| // Copyright 2024 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/glic/public/glic_enabling.h" |
| |
| #include "base/command_line.h" |
| #include "base/feature_list.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/glic/glic_pref_names.h" |
| #include "chrome/browser/glic/glic_user_status_code.h" |
| #include "chrome/browser/glic/glic_user_status_fetcher.h" |
| #include "chrome/browser/glic/host/glic.mojom.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_attributes_storage.h" |
| #include "chrome/browser/signin/identity_manager_factory.h" |
| #include "chrome/browser/ui/ui_features.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/pref_names.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/signin/public/identity_manager/account_info.h" |
| #include "components/signin/public/identity_manager/identity_manager.h" |
| #include "components/variations/service/variations_service.h" |
| |
| namespace glic { |
| |
| GlicEnabling::ProfileEnablement GlicEnabling::EnablementForProfile( |
| Profile* profile) { |
| ProfileEnablement result; |
| |
| if (!IsEnabledByFlags()) { |
| result.feature_disabled = true; |
| return result; |
| } |
| |
| if (!profile || !profile->IsRegularProfile()) { |
| result.not_regular_profile = true; |
| return result; |
| } |
| |
| // Certain checks are bypassed if --glic-dev is passed. |
| auto* command_line = base::CommandLine::ForCurrentProcess(); |
| if (!command_line->HasSwitch(::switches::kGlicDev)) { |
| if (!base::FeatureList::IsEnabled(features::kGlicRollout) && |
| !IsEligibleForGlicTieredRollout(profile)) { |
| result.not_rolled_out = true; |
| } |
| |
| signin::IdentityManager* identity_manager = |
| IdentityManagerFactory::GetForProfile(profile); |
| CHECK(identity_manager); |
| AccountInfo primary_account = |
| identity_manager->FindExtendedAccountInfoByAccountId( |
| identity_manager->GetPrimaryAccountId( |
| signin::ConsentLevel::kSignin)); |
| |
| // Not having a primary account is considered ineligible, as is kUnknown |
| // for the required account capability. |
| if (primary_account.IsEmpty() || |
| primary_account.capabilities.can_use_model_execution_features() != |
| signin::Tribool::kTrue) { |
| result.primary_account_not_capable = true; |
| } |
| } |
| |
| if (profile->GetPrefs()->GetInteger(::prefs::kGeminiSettings) != |
| static_cast<int>(glic::prefs::SettingsPolicyState::kEnabled)) { |
| result.disallowed_by_chrome_policy = true; |
| } |
| |
| if (base::FeatureList::IsEnabled(features::kGlicUserStatusCheck)) { |
| if (auto cached_user_status = |
| GlicUserStatusFetcher::GetCachedUserStatus(profile); |
| cached_user_status.has_value()) { |
| switch (cached_user_status->user_status_code) { |
| case UserStatusCode::DISABLED_BY_ADMIN: |
| result.disallowed_by_remote_admin = true; |
| break; |
| case UserStatusCode::DISABLED_OTHER: |
| result.disallowed_by_remote_other = true; |
| break; |
| case UserStatusCode::ENABLED: |
| break; |
| case UserStatusCode::SERVER_UNAVAILABLE: |
| // We never cache SERVER_UNAVAILABLE. |
| NOTREACHED(); |
| } |
| } |
| } |
| |
| if (!HasConsentedForProfile(profile)) { |
| result.not_consented = true; |
| } |
| |
| return result; |
| } |
| |
| // static |
| bool GlicEnabling::IsInRolloutLocation() { |
| auto* variations_service = g_browser_process->variations_service(); |
| return variations_service->GetStoredPermanentCountry() == "us" && |
| g_browser_process->GetApplicationLocale() == "en-US"; |
| } |
| |
| bool GlicEnabling::IsEnabledByFlags() { |
| // Check that the feature flags are enabled. |
| return base::FeatureList::IsEnabled(features::kGlic) && |
| features::HasTabSearchToolbarButton(); |
| } |
| |
| bool GlicEnabling::IsProfileEligible(const Profile* profile) { |
| // Glic is supported only in regular profiles, i.e. disable in incognito, |
| // guest, system profile, etc. |
| return IsEnabledByFlags() && profile && profile->IsRegularProfile(); |
| } |
| |
| bool GlicEnabling::IsEnabledForProfile(Profile* profile) { |
| return EnablementForProfile(profile).IsEnabled(); |
| } |
| |
| bool GlicEnabling::HasConsentedForProfile(Profile* profile) { |
| return profile->GetPrefs()->GetInteger(prefs::kGlicCompletedFre) == |
| static_cast<int>(prefs::FreStatus::kCompleted); |
| } |
| |
| bool GlicEnabling::IsEnabledAndConsentForProfile(Profile* profile) { |
| return EnablementForProfile(profile).IsEnabledAndConsented(); |
| } |
| |
| bool GlicEnabling::DidDismissForProfile(Profile* profile) { |
| return profile->GetPrefs()->GetInteger(glic::prefs::kGlicCompletedFre) == |
| static_cast<int>(prefs::FreStatus::kIncomplete); |
| } |
| |
| bool GlicEnabling::IsReadyForProfile(Profile* profile) { |
| return GetProfileReadyState(profile) == mojom::ProfileReadyState::kReady; |
| } |
| |
| mojom::ProfileReadyState GlicEnabling::GetProfileReadyState(Profile* profile) { |
| const ProfileEnablement enablement = EnablementForProfile(profile); |
| if (enablement.DisallowedByAdmin()) { |
| return mojom::ProfileReadyState::kDisabledByAdmin; |
| } |
| if (!enablement.IsEnabledAndConsented()) { |
| return mojom::ProfileReadyState::kIneligible; |
| } |
| |
| auto* command_line = base::CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(::switches::kGlicAutomation)) { |
| return mojom::ProfileReadyState::kReady; |
| } |
| |
| signin::IdentityManager* identity_manager = |
| IdentityManagerFactory::GetForProfile(profile); |
| |
| // Check that profile is not currently paused. |
| CoreAccountInfo core_account_info = |
| identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin); |
| if (core_account_info.IsEmpty()) { |
| return mojom::ProfileReadyState::kUnknownError; |
| } |
| if (identity_manager->HasAccountWithRefreshTokenInPersistentErrorState( |
| core_account_info.account_id)) { |
| return mojom::ProfileReadyState::kSignInRequired; |
| } |
| return mojom::ProfileReadyState::kReady; |
| } |
| |
| bool GlicEnabling::IsEligibleForGlicTieredRollout(Profile* profile) { |
| return base::FeatureList::IsEnabled(features::kGlicTieredRollout) && |
| profile->GetPrefs()->GetBoolean(prefs::kGlicRolloutEligibility); |
| } |
| |
| bool GlicEnabling::ShouldShowSettingsPage(Profile* profile) { |
| return EnablementForProfile(profile).ShouldShowSettingsPage(); |
| } |
| |
| void GlicEnabling::OnGlicSettingsPolicyChanged() { |
| // Update the overall enabled status as the policy has changed. |
| UpdateEnabledStatus(); |
| } |
| |
| GlicEnabling::GlicEnabling(Profile* profile, |
| ProfileAttributesStorage* profile_attributes_storage) |
| : profile_(profile), |
| profile_attributes_storage_(profile_attributes_storage) { |
| pref_registrar_.Init(profile_->GetPrefs()); |
| pref_registrar_.Add( |
| ::prefs::kGeminiSettings, |
| base::BindRepeating(&GlicEnabling::OnGlicSettingsPolicyChanged, |
| base::Unretained(this))); |
| pref_registrar_.Add(prefs::kGlicCompletedFre, |
| base::BindRepeating(&GlicEnabling::UpdateConsentStatus, |
| base::Unretained(this))); |
| if (!base::FeatureList::IsEnabled(features::kGlicRollout) && |
| base::FeatureList::IsEnabled(features::kGlicTieredRollout)) { |
| pref_registrar_.Add( |
| prefs::kGlicRolloutEligibility, |
| base::BindRepeating(&GlicEnabling::OnTieredRolloutStatusMaybeChanged, |
| base::Unretained(this))); |
| } |
| signin::IdentityManager* identity_manager = |
| IdentityManagerFactory::GetForProfile(profile); |
| CHECK(identity_manager); |
| identity_manager_observation_.Observe(identity_manager); |
| |
| if (base::FeatureList::IsEnabled(features::kGlicUserStatusCheck)) { |
| glic_user_status_fetcher_ = std::make_unique<GlicUserStatusFetcher>( |
| profile_, base::BindRepeating(&GlicEnabling::UpdateEnabledStatus, |
| base::Unretained(this))); |
| } |
| } |
| GlicEnabling::~GlicEnabling() = default; |
| |
| bool GlicEnabling::IsAllowed() { |
| return IsEnabledForProfile(profile_); |
| } |
| |
| bool GlicEnabling::HasConsented() { |
| return HasConsentedForProfile(profile_); |
| } |
| |
| base::CallbackListSubscription GlicEnabling::RegisterAllowedChanged( |
| EnableChangedCallback callback) { |
| return enable_changed_callback_list_.Add(std::move(callback)); |
| } |
| |
| base::CallbackListSubscription GlicEnabling::RegisterOnConsentChanged( |
| ConsentChangedCallback callback) { |
| return consent_changed_callback_list_.Add(std::move(callback)); |
| } |
| |
| base::CallbackListSubscription GlicEnabling::RegisterOnShowSettingsPageChanged( |
| ShowSettingsPageChangedCallback callback) { |
| return show_settings_page_changed_callback_list_.Add(std::move(callback)); |
| } |
| |
| void GlicEnabling::OnPrimaryAccountChanged( |
| const signin::PrimaryAccountChangeEvent& event_details) { |
| UpdateEnabledStatus(); |
| } |
| |
| void GlicEnabling::OnExtendedAccountInfoUpdated(const AccountInfo& info) { |
| UpdateEnabledStatus(); |
| } |
| |
| void GlicEnabling::OnExtendedAccountInfoRemoved(const AccountInfo& info) { |
| UpdateEnabledStatus(); |
| } |
| |
| void GlicEnabling::OnRefreshTokensLoaded() { |
| UpdateEnabledStatus(); |
| } |
| |
| void GlicEnabling::OnRefreshTokenRemovedForAccount( |
| const CoreAccountId& account_id) { |
| UpdateEnabledStatus(); |
| } |
| |
| void GlicEnabling::OnTieredRolloutStatusMaybeChanged() { |
| UpdateEnabledStatus(); |
| } |
| |
| void GlicEnabling::OnErrorStateOfRefreshTokenUpdatedForAccount( |
| const CoreAccountInfo& account_info, |
| const GoogleServiceAuthError& error, |
| signin_metrics::SourceForRefreshTokenOperation token_operation_source) { |
| // Check that the account info here is the same as the primary account, and |
| // ignore all events that are not about the primary account. |
| if (identity_manager_observation_.GetSource()->GetPrimaryAccountInfo( |
| signin::ConsentLevel::kSignin) != account_info) { |
| return; |
| } |
| UpdateEnabledStatus(); |
| } |
| |
| void GlicEnabling::UpdateEnabledStatus() { |
| if (ProfileAttributesEntry* entry = |
| profile_attributes_storage_->GetProfileAttributesWithPath( |
| profile_->GetPath())) { |
| entry->SetIsGlicEligible(IsAllowed()); |
| } |
| enable_changed_callback_list_.Notify(); |
| show_settings_page_changed_callback_list_.Notify(); |
| } |
| |
| void GlicEnabling::UpdateConsentStatus() { |
| consent_changed_callback_list_.Notify(); |
| show_settings_page_changed_callback_list_.Notify(); |
| } |
| |
| } // namespace glic |