blob: 0da8a37df794b0e630eac025fc697b7c4104b623 [file] [log] [blame]
// Copyright 2023 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/ash/growth/campaigns_manager_client_impl.h"
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <variant>
#include "ash/constants/ash_switches.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/no_destructor.h"
#include "base/strings/string_number_conversions.h"
#include "base/version.h"
#include "chrome/browser/ash/growth/install_web_app_action_performer.h"
#include "chrome/browser/ash/growth/metrics.h"
#include "chrome/browser/ash/growth/open_url_action_performer.h"
#include "chrome/browser/ash/growth/show_notification_action_performer.h"
#include "chrome/browser/ash/growth/show_nudge_action_performer.h"
#include "chrome/browser/ash/login/demo_mode/demo_components.h"
#include "chrome/browser/ash/login/demo_mode/demo_mode_dimensions.h"
#include "chrome/browser/ash/login/demo_mode/demo_session.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/component_updater/cros_component_manager.h"
#include "chrome/browser/feature_engagement/tracker_factory.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chromeos/ash/components/growth/campaigns_constants.h"
#include "chromeos/ash/components/growth/campaigns_manager.h"
#include "chromeos/ash/components/growth/growth_metrics.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "components/feature_engagement/public/tracker.h"
#include "components/variations/synthetic_trials.h"
namespace {
inline constexpr char kCampaignComponentName[] = "growth-campaigns";
// The synthetic trial name prefix for growth experiment. Formatted as
// `CrOSGrowthStudy{studyId}`, where `studyId` is an integer. For non
// experimental campaigns, `studyId` will be empty.
inline constexpr char kGrowthStudyName[] = "CrOSGrowthStudy";
// The synthetical trial group name for growth experiment. The campaign id
// will be unique for different groups.
inline constexpr char kGrowthGroupName[] = "CampaignId";
Profile* GetProfile() {
return ProfileManager::GetActiveUserProfile();
}
} // namespace
CampaignsManagerClientImpl::CampaignsManagerClientImpl() {
// `show_nudge_performer_observation_` is used in `campaigns_manager_` ctor,
// so it needs to be initialized first.
campaigns_manager_ = std::make_unique<growth::CampaignsManager>(
/*client=*/this, g_browser_process->local_state());
}
CampaignsManagerClientImpl::~CampaignsManagerClientImpl() = default;
void CampaignsManagerClientImpl::LoadCampaignsComponent(
growth::CampaignComponentLoadedCallback callback) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(ash::switches::kGrowthCampaignsPath)) {
std::move(callback).Run(base::FilePath(command_line->GetSwitchValueASCII(
ash::switches::kGrowthCampaignsPath)));
return;
}
// Loads campaigns component.
auto cros_component_manager =
g_browser_process->platform_part()->cros_component_manager();
CHECK(cros_component_manager);
cros_component_manager->Load(
kCampaignComponentName,
component_updater::CrOSComponentManager::MountPolicy::kMount,
component_updater::CrOSComponentManager::UpdatePolicy::kDontForce,
base::BindOnce(&CampaignsManagerClientImpl::OnComponentDownloaded,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
bool CampaignsManagerClientImpl::IsDeviceInDemoMode() const {
return ash::DemoSession::IsDeviceInDemoMode();
}
bool CampaignsManagerClientImpl::IsCloudGamingDevice() const {
return ash::demo_mode::IsCloudGamingDevice();
}
bool CampaignsManagerClientImpl::IsFeatureAwareDevice() const {
return ash::demo_mode::IsFeatureAwareDevice();
}
const std::string& CampaignsManagerClientImpl::GetApplicationLocale() const {
return g_browser_process->GetApplicationLocale();
}
const base::Version& CampaignsManagerClientImpl::GetDemoModeAppVersion() const {
auto* demo_session = ash::DemoSession::Get();
CHECK(demo_session);
const auto& version = demo_session->components()->app_component_version();
if (!version.has_value()) {
growth::RecordCampaignsManagerError(
growth::CampaignsManagerError::kDemoModeAppVersionUnavailable);
static const base::NoDestructor<base::Version> empty_version;
return *empty_version;
}
return version.value();
}
growth::ActionMap CampaignsManagerClientImpl::GetCampaignsActions() {
growth::ActionMap action_map;
action_map.emplace(
make_pair(growth::ActionType::kInstallWebApp,
std::make_unique<InstallWebAppActionPerformer>()));
action_map.emplace(make_pair(growth::ActionType::kOpenUrl,
std::make_unique<OpenUrlActionPerformer>()));
std::unique_ptr<ShowNudgeActionPerformer> show_nudge_performer =
std::make_unique<ShowNudgeActionPerformer>();
show_nudge_performer_observation_.Observe(show_nudge_performer.get());
action_map.emplace(make_pair(growth::ActionType::kShowNudge,
std::move(show_nudge_performer)));
std::unique_ptr<ShowNotificationActionPerformer> show_notification_performer =
std::make_unique<ShowNotificationActionPerformer>();
show_notification_performer_observation_.Observe(
show_notification_performer.get());
action_map.emplace(make_pair(growth::ActionType::kShowNotification,
std::move(show_notification_performer)));
return action_map;
}
void CampaignsManagerClientImpl::RegisterSyntheticFieldTrial(
const std::optional<int> study_id,
const int campaign_id) const {
// If `study_id` is not null, appends it to the end of `trial_name`.
std::string trial_name(kGrowthStudyName);
if (study_id) {
base::StringAppendF(&trial_name, "%d", *study_id);
}
std::string group_name(kGrowthGroupName);
base::StringAppendF(&group_name, "%d", campaign_id);
ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(trial_name,
group_name);
}
void CampaignsManagerClientImpl::NotifyEvent(const std::string& event_name) {
auto* tracker =
feature_engagement::TrackerFactory::GetForBrowserContext(GetProfile());
if (!tracker || !tracker->IsInitialized()) {
LOG(ERROR) << "Feature Engagement tracer is not available";
return;
}
tracker->NotifyEvent(event_name);
}
void CampaignsManagerClientImpl::ClearConfig(
const std::map<std::string, std::string>& params) {
auto* tracker =
feature_engagement::TrackerFactory::GetForBrowserContext(GetProfile());
if (!tracker || !tracker->IsInitialized()) {
LOG(ERROR) << "Feature Engagement tracer is not available";
return;
}
UpdateConfig(params);
tracker->ClearEventData(feature_engagement::kIPHGrowthFramework);
}
bool CampaignsManagerClientImpl::WouldTriggerHelpUI(
const std::map<std::string, std::string>& params) {
auto* tracker =
feature_engagement::TrackerFactory::GetForBrowserContext(GetProfile());
if (!tracker || !tracker->IsInitialized()) {
LOG(ERROR) << "Feature Engagement tracer is not available";
return false;
}
UpdateConfig(params);
return tracker->WouldTriggerHelpUI(feature_engagement::kIPHGrowthFramework);
}
signin::IdentityManager* CampaignsManagerClientImpl::GetIdentityManager()
const {
return IdentityManagerFactory::GetForProfile(GetProfile());
}
void CampaignsManagerClientImpl::OnReadyToLogImpression(int campaign_id) {
RecordImpression(campaign_id);
campaigns_manager_->NotifyEventForTargeting(
growth::CampaignEvent::kImpression, base::NumberToString(campaign_id));
}
void CampaignsManagerClientImpl::OnDismissed(int campaign_id) {
RecordDismissed(campaign_id);
}
void CampaignsManagerClientImpl::OnButtonPressed(int campaign_id,
CampaignButtonId button_id,
bool should_mark_dismissed) {
RecordButtonPressed(campaign_id, button_id);
if (!should_mark_dismissed) {
return;
}
// Notify `kDismissed` event to the Feature Engagement framework. This event
// will be stored and could be used later.
switch (button_id) {
case CampaignButtonId::kPrimary:
case CampaignButtonId::kSecondary:
// Primary and Secondary button press will treated as user dismissal.
campaigns_manager_->NotifyEventForTargeting(
growth::CampaignEvent::kDismissed, base::NumberToString(campaign_id));
break;
case CampaignButtonId::kOthers:
break;
}
}
void CampaignsManagerClientImpl::OnComponentDownloaded(
growth::CampaignComponentLoadedCallback loaded_callback,
component_updater::CrOSComponentManager::Error error,
const base::FilePath& path) {
if (error != component_updater::CrOSComponentManager::Error::NONE) {
std::move(loaded_callback).Run(std::nullopt);
return;
}
std::move(loaded_callback).Run(path);
}
void CampaignsManagerClientImpl::UpdateConfig(
const std::map<std::string, std::string>& params) {
auto* tracker =
feature_engagement::TrackerFactory::GetForBrowserContext(GetProfile());
if (!tracker || !tracker->IsInitialized()) {
LOG(ERROR) << "Feature Engagement tracer is not available";
return;
}
config_provider_.SetConfig(params);
tracker->UpdateConfig(feature_engagement::kIPHGrowthFramework,
&config_provider_);
}