| // Copyright 2016 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/ui/ash/system/system_tray_client_impl.h" |
| |
| #include <cstdio> |
| #include <memory> |
| #include <string_view> |
| |
| #include "ash/constants/ash_features.h" |
| #include "ash/constants/ash_switches.h" |
| #include "ash/constants/personalization_entry_point.h" |
| #include "ash/constants/web_app_id_constants.h" |
| #include "ash/public/cpp/locale_update_controller.h" |
| #include "ash/public/cpp/login_types.h" |
| #include "ash/public/cpp/new_window_delegate.h" |
| #include "ash/public/cpp/system_tray.h" |
| #include "ash/public/cpp/update_types.h" |
| #include "ash/webui/settings/public/constants/routes.mojom-forward.h" |
| #include "ash/webui/settings/public/constants/routes.mojom.h" |
| #include "ash/webui/settings/public/constants/setting.mojom.h" |
| #include "base/check_deref.h" |
| #include "base/command_line.h" |
| #include "base/i18n/time_formatting.h" |
| #include "base/logging.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/raw_ref.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/notreached.h" |
| #include "base/strings/escape.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/trace_event/trace_event.h" |
| #include "chrome/browser/apps/app_service/app_service_proxy.h" |
| #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" |
| #include "chrome/browser/apps/app_service/launch_utils.h" |
| #include "chrome/browser/ash/accessibility/accessibility_manager.h" |
| #include "chrome/browser/ash/login/help_app_launcher.h" |
| #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h" |
| #include "chrome/browser/ash/policy/core/device_cloud_policy_manager_ash.h" |
| #include "chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.h" |
| #include "chrome/browser/ash/profiles/profile_helper.h" |
| #include "chrome/browser/ash/system/system_clock.h" |
| #include "chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_metrics.h" |
| #include "chrome/browser/chromeos/extensions/vpn_provider/vpn_service_factory.h" |
| #include "chrome/browser/lifetime/application_lifetime.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" |
| #include "chrome/browser/ui/browser_navigator_params.h" |
| #include "chrome/browser/ui/chrome_pages.h" |
| #include "chrome/browser/ui/managed_ui.h" |
| #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" |
| #include "chrome/browser/ui/settings_window_manager_chromeos.h" |
| #include "chrome/browser/ui/singleton_tabs.h" |
| #include "chrome/browser/ui/webui/access_code_cast/access_code_cast_dialog.h" |
| #include "chrome/browser/ui/webui/ash/bluetooth/bluetooth_pairing_dialog.h" |
| #include "chrome/browser/ui/webui/ash/internet/internet_config_dialog.h" |
| #include "chrome/browser/ui/webui/ash/internet/internet_detail_dialog.h" |
| #include "chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.h" |
| #include "chrome/browser/ui/webui/ash/set_time/set_time_dialog.h" |
| #include "chrome/browser/upgrade_detector/upgrade_detector.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h" |
| #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h" |
| #include "chromeos/ash/components/network/network_handler.h" |
| #include "chromeos/ash/components/network/network_state.h" |
| #include "chromeos/ash/components/network/network_state_handler.h" |
| #include "chromeos/ash/components/network/network_util.h" |
| #include "chromeos/ash/components/network/onc/network_onc_utils.h" |
| #include "chromeos/ash/components/network/tether_constants.h" |
| #include "chromeos/ash/components/phonehub/util/histogram_util.h" |
| #include "chromeos/ash/experiences/settings_ui/settings_app_manager.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/services/app_service/public/cpp/app_launch_util.h" |
| #include "components/session_manager/core/session_manager.h" |
| #include "components/session_manager/core/session_manager_observer.h" |
| #include "components/user_manager/user_manager.h" |
| #include "third_party/cros_system_api/dbus/shill/dbus-constants.h" |
| #include "third_party/icu/source/i18n/unicode/timezone.h" |
| #include "ui/events/event_constants.h" |
| #include "url/gurl.h" |
| |
| using session_manager::SessionManager; |
| using session_manager::SessionState; |
| |
| namespace { |
| |
| SystemTrayClientImpl* g_system_tray_client_instance = nullptr; |
| |
| // The prefix a calendar event URL *must* have in order to be launched by the |
| // calendar web app. |
| constexpr char kOfficialCalendarUrlPrefix[] = |
| "https://calendar.google.com/calendar/"; |
| |
| void ShowSettingsSubPageForActiveUser(const std::string& sub_page) { |
| chrome::SettingsWindowManager::GetInstance()->ShowOSSettings( |
| ProfileManager::GetActiveUserProfile(), sub_page); |
| } |
| |
| // Returns the severity of a pending update. |
| ash::UpdateSeverity GetUpdateSeverity(UpgradeDetector* detector) { |
| // OS updates use UpgradeDetector's severity mapping. |
| switch (detector->upgrade_notification_stage()) { |
| case UpgradeDetector::UPGRADE_ANNOYANCE_NONE: |
| return ash::UpdateSeverity::kNone; |
| case UpgradeDetector::UPGRADE_ANNOYANCE_VERY_LOW: |
| return ash::UpdateSeverity::kVeryLow; |
| case UpgradeDetector::UPGRADE_ANNOYANCE_LOW: |
| return ash::UpdateSeverity::kLow; |
| case UpgradeDetector::UPGRADE_ANNOYANCE_ELEVATED: |
| return ash::UpdateSeverity::kElevated; |
| case UpgradeDetector::UPGRADE_ANNOYANCE_GRACE: |
| return ash::UpdateSeverity::kGrace; |
| case UpgradeDetector::UPGRADE_ANNOYANCE_HIGH: |
| return ash::UpdateSeverity::kHigh; |
| case UpgradeDetector::UPGRADE_ANNOYANCE_CRITICAL: |
| return ash::UpdateSeverity::kCritical; |
| } |
| } |
| |
| const ash::NetworkState* GetNetworkState(const std::string& network_id) { |
| if (network_id.empty()) { |
| return nullptr; |
| } |
| return ash::NetworkHandler::Get() |
| ->network_state_handler() |
| ->GetNetworkStateFromGuid(network_id); |
| } |
| |
| bool ShouldOpenCellularSetupPsimFlowOnClick(const std::string& network_id) { |
| // |kActivationStateNotActivated| is only set in physical SIM networks, |
| // checking a networks activation state is |kActivationStateNotActivated| |
| // ensures the current network is a phyical SIM network. |
| |
| const ash::NetworkState* network_state = GetNetworkState(network_id); |
| return network_state && network_state->type() == shill::kTypeCellular && |
| network_state->activation_state() == |
| shill::kActivationStateNotActivated && |
| network_state->eid().empty(); |
| } |
| |
| apps::AppServiceProxyAsh* GetActiveUserAppServiceProxyAsh() { |
| Profile* profile = ProfileManager::GetActiveUserProfile(); |
| apps::AppServiceProxyAsh* proxy = |
| apps::AppServiceProxyFactory::GetForProfile(profile); |
| return proxy; |
| } |
| |
| apps::AppRegistryCache* GetActiveUserAppRegistryCache() { |
| apps::AppServiceProxyAsh* proxy = GetActiveUserAppServiceProxyAsh(); |
| if (!proxy) { |
| return nullptr; |
| } |
| |
| return &proxy->AppRegistryCache(); |
| } |
| |
| bool IsAppInstalled(std::string app_id) { |
| apps::AppRegistryCache* reg_cache = GetActiveUserAppRegistryCache(); |
| if (!reg_cache) { |
| LOG(ERROR) << __FUNCTION__ |
| << " Failed to get active user AppRegistryCache "; |
| return false; |
| } |
| |
| bool found_app_id = false; |
| reg_cache->ForEachApp([&found_app_id, app_id](const apps::AppUpdate& update) { |
| if (update.AppId() == app_id) { |
| found_app_id = true; |
| return; |
| } |
| }); |
| |
| return found_app_id; |
| } |
| |
| void OpenInBrowser(const GURL& event_url) { |
| ShowSingletonTabOverwritingNTP(ProfileManager::GetActiveUserProfile(), |
| event_url, |
| NavigateParams::IGNORE_AND_NAVIGATE); |
| } |
| |
| ash::ManagementDeviceMode GetManagementDeviceMode( |
| policy::BrowserPolicyConnectorAsh& connector) { |
| if (!connector.IsDeviceEnterpriseManaged()) { |
| return ash::ManagementDeviceMode::kNone; |
| } |
| |
| if (connector.IsKioskEnrolled()) { |
| return ash::ManagementDeviceMode::kKioskSku; |
| } |
| |
| switch (connector.GetEnterpriseMarketSegment()) { |
| case policy::MarketSegment::UNKNOWN: |
| return ash::ManagementDeviceMode::kOther; |
| case policy::MarketSegment::ENTERPRISE: |
| return ash::ManagementDeviceMode::kChromeEnterprise; |
| case policy::MarketSegment::EDUCATION: |
| return ash::ManagementDeviceMode::kChromeEducation; |
| } |
| |
| NOTREACHED(); |
| } |
| |
| } // namespace |
| |
| class SystemTrayClientImpl::EnterpriseAccountObserver |
| : public user_manager::UserManager::UserSessionStateObserver, |
| public policy::CloudPolicyStore::Observer, |
| public session_manager::SessionManagerObserver { |
| public: |
| explicit EnterpriseAccountObserver(SystemTrayClientImpl* owner) |
| : owner_(owner) { |
| user_manager::UserManager* manager = user_manager::UserManager::Get(); |
| session_state_observation_.Observe(manager); |
| session_observation_.Observe(session_manager::SessionManager::Get()); |
| UpdateProfile(); |
| } |
| EnterpriseAccountObserver(const EnterpriseAccountObserver&) = delete; |
| EnterpriseAccountObserver& operator=(const EnterpriseAccountObserver&) = |
| delete; |
| ~EnterpriseAccountObserver() override = default; |
| |
| private: |
| const raw_ptr<SystemTrayClientImpl> owner_; |
| raw_ptr<Profile> profile_ = nullptr; |
| |
| base::ScopedObservation<user_manager::UserManager, |
| user_manager::UserManager::UserSessionStateObserver> |
| session_state_observation_{this}; |
| base::ScopedObservation<session_manager::SessionManager, |
| session_manager::SessionManagerObserver> |
| session_observation_{this}; |
| base::ScopedObservation<policy::CloudPolicyStore, |
| policy::CloudPolicyStore::Observer> |
| policy_observation_{this}; |
| |
| // user_manager::UserManager::UserSessionStateObserver: |
| void ActiveUserChanged(user_manager::User* active_user) override { |
| UpdateProfile(); |
| } |
| |
| // session_manager::SessionManagerObserver: |
| void OnSessionStateChanged() override { |
| TRACE_EVENT0("ui", |
| "SystemTrayClientImpl::EnterpriseAccountObserver::" |
| "OnSessionStateChanged"); |
| UpdateProfile(); |
| } |
| |
| // policy::CloudPolicyStore::Observer |
| void OnStoreLoaded(policy::CloudPolicyStore* store) override { |
| owner_->UpdateEnterpriseAccountDomainInfo(profile_); |
| } |
| void OnStoreError(policy::CloudPolicyStore* store) override { |
| owner_->UpdateEnterpriseAccountDomainInfo(profile_); |
| } |
| |
| void UpdateProfile() { |
| user_manager::User* user = |
| user_manager::UserManager::Get()->GetActiveUser(); |
| Profile* profile = |
| user ? ash::ProfileHelper::Get()->GetProfileByUser(user) : nullptr; |
| if (profile == profile_) { |
| return; |
| } |
| |
| policy_observation_.Reset(); |
| |
| profile_ = profile; |
| if (profile_) { |
| policy::UserCloudPolicyManagerAsh* manager = |
| profile_->GetUserCloudPolicyManagerAsh(); |
| if (manager) { |
| policy_observation_.Observe(manager->core()->store()); |
| } |
| } |
| owner_->UpdateEnterpriseAccountDomainInfo(profile_); |
| } |
| }; |
| |
| SystemTrayClientImpl::SystemTrayClientImpl( |
| ash::system::SystemClock& system_clock, |
| policy::BrowserPolicyConnectorAsh& browser_policy_connector_ash) |
| : system_clock_(system_clock), |
| browser_policy_connector_ash_(browser_policy_connector_ash), |
| system_tray_(ash::SystemTray::Get()), |
| enterprise_account_observer_( |
| std::make_unique<EnterpriseAccountObserver>(this)) { |
| // If this observes clock setting changes before ash comes up the IPCs will |
| // be queued on |system_tray_|. |
| system_clock_->AddObserver(this); |
| system_tray_->SetUse24HourClock(system_clock_->ShouldUse24HourClock()); |
| |
| // If an upgrade is available at startup then tell ash about it. |
| if (UpgradeDetector::GetInstance()->notify_upgrade()) { |
| HandleUpdateAvailable(); |
| } |
| |
| // If the device is enterprise managed then send ash the enterprise domain. |
| policy::DeviceCloudPolicyManagerAsh* policy_manager = |
| browser_policy_connector_ash_->GetDeviceCloudPolicyManager(); |
| if (policy_manager) { |
| policy_manager->core()->store()->AddObserver(this); |
| } |
| UpdateDeviceEnterpriseInfo(); |
| |
| system_tray_->SetClient(this); |
| |
| DCHECK(!g_system_tray_client_instance); |
| g_system_tray_client_instance = this; |
| UpgradeDetector::GetInstance()->AddObserver(this); |
| } |
| |
| SystemTrayClientImpl::~SystemTrayClientImpl() { |
| DCHECK_EQ(this, g_system_tray_client_instance); |
| g_system_tray_client_instance = nullptr; |
| |
| // This can happen when mocking this class in tests. |
| if (!system_tray_) { |
| return; |
| } |
| |
| system_tray_->SetClient(nullptr); |
| |
| policy::DeviceCloudPolicyManagerAsh* policy_manager = |
| browser_policy_connector_ash_->GetDeviceCloudPolicyManager(); |
| if (policy_manager) { |
| policy_manager->core()->store()->RemoveObserver(this); |
| } |
| |
| system_clock_->RemoveObserver(this); |
| UpgradeDetector::GetInstance()->RemoveObserver(this); |
| } |
| |
| // static |
| SystemTrayClientImpl* SystemTrayClientImpl::Get() { |
| return g_system_tray_client_instance; |
| } |
| |
| void SystemTrayClientImpl::SetRelaunchNotificationState( |
| const ash::RelaunchNotificationState& relaunch_notification_state) { |
| relaunch_notification_state_ = relaunch_notification_state; |
| HandleUpdateAvailable(); |
| } |
| |
| void SystemTrayClientImpl::ResetUpdateState() { |
| relaunch_notification_state_ = {}; |
| system_tray_->ResetUpdateState(); |
| } |
| |
| void SystemTrayClientImpl::SetPrimaryTrayEnabled(bool enabled) { |
| system_tray_->SetPrimaryTrayEnabled(enabled); |
| } |
| |
| void SystemTrayClientImpl::SetPrimaryTrayVisible(bool visible) { |
| system_tray_->SetPrimaryTrayVisible(visible); |
| } |
| |
| void SystemTrayClientImpl::SetPerformanceTracingIconVisible(bool visible) { |
| system_tray_->SetPerformanceTracingIconVisible(visible); |
| } |
| |
| void SystemTrayClientImpl::SetLocaleList( |
| std::vector<ash::LocaleInfo> locale_list, |
| const std::string& current_locale_iso_code) { |
| system_tray_->SetLocaleList(std::move(locale_list), current_locale_iso_code); |
| } |
| |
| void SystemTrayClientImpl::SetShowEolNotice(bool show, |
| bool eol_passed_recently) { |
| system_tray_->SetShowEolNotice(show); |
| } |
| //////////////////////////////////////////////////////////////////////////////// |
| // ash::SystemTrayClient: |
| |
| void SystemTrayClientImpl::ShowSettings(int64_t display_id) { |
| // TODO(jamescook): Use different metric for OS settings. |
| base::RecordAction(base::UserMetricsAction("ShowOptions")); |
| ash::SettingsAppManager::Get()->Open( |
| CHECK_DEREF(user_manager::UserManager::Get()->GetActiveUser()), |
| {.display_id = display_id}); |
| } |
| |
| void SystemTrayClientImpl::ShowAccountSettings() { |
| // The "Accounts" section is called "People" for historical reasons. |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kPeopleSectionPath); |
| } |
| |
| void SystemTrayClientImpl::ShowBluetoothSettings() { |
| base::RecordAction(base::UserMetricsAction("ShowBluetoothSettingsPage")); |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kBluetoothDevicesSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowBluetoothSettings(const std::string& device_id) { |
| base::RecordAction(base::UserMetricsAction("ShowBluetoothSettingsPage")); |
| ShowSettingsSubPageForActiveUser(base::StrCat( |
| {chromeos::settings::mojom::kBluetoothDeviceDetailSubpagePath, |
| "?id=", device_id})); |
| } |
| |
| void SystemTrayClientImpl::ShowBluetoothPairingDialog( |
| std::optional<std::string_view> device_address) { |
| if (ash::BluetoothPairingDialog::ShowDialog(device_address)) { |
| base::RecordAction( |
| base::UserMetricsAction("StatusArea_Bluetooth_Connect_Unknown")); |
| } |
| } |
| |
| void SystemTrayClientImpl::ShowDateSettings() { |
| base::RecordAction(base::UserMetricsAction("ShowDateOptions")); |
| // Everybody can change the time zone (even though it is a device setting). |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kSystemPreferencesSectionPath); |
| } |
| |
| void SystemTrayClientImpl::ShowSetTimeDialog() { |
| ash::SetTimeDialog::ShowDialog(); |
| } |
| |
| void SystemTrayClientImpl::ShowDisplaySettings() { |
| base::RecordAction(base::UserMetricsAction("ShowDisplayOptions")); |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kDisplaySubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowDarkModeSettings() { |
| // Record entry point metric to Personalization through Dark Mode Quick |
| // Settings/System Tray. |
| ash::personalization_app::LogPersonalizationEntryPoint( |
| ash::PersonalizationEntryPoint::kSystemTray); |
| ash::NewWindowDelegate::GetInstance()->OpenPersonalizationHub(); |
| } |
| |
| void SystemTrayClientImpl::ShowStorageSettings() { |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kStorageSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowPowerSettings() { |
| base::RecordAction(base::UserMetricsAction("Tray_ShowPowerOptions")); |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kPowerSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowPrivacyAndSecuritySettings() { |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kPrivacyAndSecuritySectionPath); |
| } |
| |
| void SystemTrayClientImpl::ShowPrivacyHubSettings() { |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kPrivacyHubSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowSpeakOnMuteDetectionSettings() { |
| ShowSettingsSubPageForActiveUser( |
| std::string(chromeos::settings::mojom::kPrivacyHubSubpagePath) + |
| "?settingId=" + |
| base::NumberToString(static_cast<int32_t>( |
| chromeos::settings::mojom::Setting::kSpeakOnMuteDetectionOnOff))); |
| } |
| |
| void SystemTrayClientImpl::ShowSmartPrivacySettings() { |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kSmartPrivacySubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowChromeSlow() { |
| chrome::ScopedTabbedBrowserDisplayer displayer( |
| ProfileManager::GetPrimaryUserProfile()); |
| chrome::ShowSlow(displayer.browser()); |
| } |
| |
| void SystemTrayClientImpl::ShowIMESettings() { |
| base::RecordAction(base::UserMetricsAction("OpenLanguageOptionsDialog")); |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kInputSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowConnectedDevicesSettings() { |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kMultiDeviceFeaturesSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowTetherNetworkSettings() { |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kMobileDataNetworksSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowWifiSyncSettings() { |
| ShowSettingsSubPageForActiveUser( |
| std::string(chromeos::settings::mojom::kMultiDeviceFeaturesSubpagePath) + |
| "?settingId=" + |
| base::NumberToString(static_cast<int32_t>( |
| chromeos::settings::mojom::Setting::kWifiSyncOnOff))); |
| } |
| |
| void SystemTrayClientImpl::ShowAboutChromeOS() { |
| // We always want to check for updates when showing the about page from the |
| // Ash UI. |
| ShowSettingsSubPageForActiveUser( |
| std::string(chromeos::settings::mojom::kAboutChromeOsSectionPath) + |
| "?checkForUpdate=true"); |
| } |
| |
| void SystemTrayClientImpl::ShowAboutChromeOSDetails() { |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kDetailedBuildInfoSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowAccessibilityHelp() { |
| ash::AccessibilityManager::ShowAccessibilityHelp(); |
| } |
| |
| void SystemTrayClientImpl::ShowAccessibilitySettings() { |
| base::RecordAction(base::UserMetricsAction("ShowAccessibilitySettings")); |
| // TODO(crbug.com/1358729): We show the old Manage Accessibility page in kiosk |
| // mode, so users can't get to other OS Settings (such as Wi-Fi, Date / Time). |
| // We plan to remove this after we add a standalone OS Accessibility page for |
| // kiosk mode, which blocks access to other OS settings. |
| ShowSettingsSubPageForActiveUser( |
| user_manager::UserManager::Get()->IsLoggedInAsAnyKioskApp() |
| ? chromeos::settings::mojom::kManageAccessibilitySubpagePath |
| : chromeos::settings::mojom::kAccessibilitySectionPath); |
| } |
| |
| void SystemTrayClientImpl::ShowColorCorrectionSettings() { |
| if (user_manager::UserManager::Get()->IsLoggedInAsAnyKioskApp()) { |
| // TODO(b/259370808): Color correction settings subpage not available in |
| // Kiosk. |
| return; |
| } |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kDisplayAndMagnificationSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowGestureEducationHelp() { |
| base::RecordAction(base::UserMetricsAction("ShowGestureEducationHelp")); |
| Profile* profile = ProfileManager::GetActiveUserProfile(); |
| if (!profile) { |
| return; |
| } |
| |
| ash::SystemAppLaunchParams params; |
| params.url = GURL(chrome::kChromeOSGestureEducationHelpURL); |
| params.launch_source = apps::LaunchSource::kFromOtherApp; |
| ash::LaunchSystemWebAppAsync(profile, ash::SystemWebAppType::HELP, params); |
| } |
| |
| void SystemTrayClientImpl::ShowPaletteHelp() { |
| ShowSingletonTab(ProfileManager::GetActiveUserProfile(), |
| GURL(chrome::kChromePaletteHelpURL)); |
| } |
| |
| void SystemTrayClientImpl::ShowPaletteSettings() { |
| base::RecordAction(base::UserMetricsAction("ShowPaletteOptions")); |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kStylusSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowEnterpriseInfo() { |
| // At the login screen, lock screen, etc. show enterprise help in a window. |
| if (SessionManager::Get()->IsUserSessionBlocked()) { |
| base::MakeRefCounted<ash::HelpAppLauncher>(/*parent_window=*/nullptr) |
| ->ShowHelpTopic(ash::HelpAppLauncher::HELP_ENTERPRISE); |
| return; |
| } |
| |
| // Otherwise show enterprise management info page. |
| chrome::ScopedTabbedBrowserDisplayer displayer( |
| ProfileManager::GetActiveUserProfile()); |
| chrome::ShowEnterpriseManagementPageInTabbedBrowser(displayer.browser()); |
| } |
| |
| void SystemTrayClientImpl::ShowNetworkConfigure(const std::string& network_id) { |
| // UI is not available at the lock screen. |
| if (SessionManager::Get()->IsScreenLocked()) { |
| return; |
| } |
| |
| DCHECK(ash::NetworkHandler::IsInitialized()); |
| const ash::NetworkState* network_state = GetNetworkState(network_id); |
| if (!network_state) { |
| LOG(ERROR) << "Network not found: " << network_id; |
| return; |
| } |
| if (network_state->type() == ash::kTypeTether && |
| !network_state->tether_has_connected_to_host()) { |
| ShowNetworkSettingsHelper(network_id, true /* show_configure */); |
| return; |
| } |
| |
| ash::InternetConfigDialog::ShowDialogForNetworkId(network_id); |
| } |
| |
| void SystemTrayClientImpl::ShowNetworkCreate(const std::string& type) { |
| if (type == ::onc::network_type::kCellular) { |
| ShowSettingsCellularSetup(/*show_psim_flow=*/false); |
| return; |
| } |
| ash::InternetConfigDialog::ShowDialogForNetworkType(type); |
| } |
| |
| void SystemTrayClientImpl::ShowSettingsCellularSetup(bool show_psim_flow) { |
| // TODO(crbug.com/40134918) Add metrics action recorder |
| std::string page = chromeos::settings::mojom::kCellularNetworksSubpagePath; |
| page += "&showCellularSetup=true"; |
| if (show_psim_flow) { |
| page += "&showPsimFlow=true"; |
| } |
| ShowSettingsSubPageForActiveUser(page); |
| } |
| |
| void SystemTrayClientImpl::ShowMobileDataSubpage() { |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kCellularNetworksSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowThirdPartyVpnCreate( |
| const std::string& extension_id) { |
| Profile* profile = ProfileManager::GetPrimaryUserProfile(); |
| |
| if (!profile) { |
| return; |
| } |
| |
| // Request that the third-party VPN provider show its "add network" dialog. |
| chromeos::VpnServiceFactory::GetForBrowserContext(profile) |
| ->SendShowAddDialogToExtension(extension_id); |
| } |
| |
| void SystemTrayClientImpl::ShowArcVpnCreate(const std::string& app_id) { |
| Profile* profile = ProfileManager::GetPrimaryUserProfile(); |
| |
| if (!profile || |
| !apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile)) { |
| return; |
| } |
| |
| apps::AppServiceProxyFactory::GetForProfile(profile)->Launch( |
| app_id, ui::EF_NONE, apps::LaunchSource::kFromParentalControls); |
| } |
| |
| void SystemTrayClientImpl::ShowSettingsSimUnlock() { |
| // TODO(crbug.com/40134918) Add metrics action recorder. |
| SessionManager* const session_manager = SessionManager::Get(); |
| DCHECK(session_manager->IsSessionStarted()); |
| DCHECK(!session_manager->IsInSecondaryLoginScreen()); |
| std::string page = chromeos::settings::mojom::kCellularNetworksSubpagePath; |
| page += "&showSimLockDialog=true"; |
| ShowSettingsSubPageForActiveUser(page); |
| } |
| |
| void SystemTrayClientImpl::ShowApnSubpage(const std::string& network_id) { |
| CHECK(ash::features::IsApnRevampEnabled()); |
| std::string page = chromeos::settings::mojom::kApnSubpagePath + |
| std::string("?guid=") + |
| base::EscapeUrlEncodedData(network_id, /*use_plus=*/true); |
| ShowSettingsSubPageForActiveUser(page); |
| } |
| |
| void SystemTrayClientImpl::ShowNetworkSettings(const std::string& network_id) { |
| ShowNetworkSettingsHelper(network_id, false /* show_configure */); |
| } |
| |
| void SystemTrayClientImpl::ShowHotspotSubpage() { |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kHotspotSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowNetworkSettingsHelper( |
| const std::string& network_id, |
| bool show_configure) { |
| SessionManager* const session_manager = SessionManager::Get(); |
| if (session_manager->IsInSecondaryLoginScreen()) { |
| return; |
| } |
| if (!session_manager->IsSessionStarted()) { |
| ash::InternetDetailDialog::ShowDialog(network_id); |
| return; |
| } |
| |
| if (ShouldOpenCellularSetupPsimFlowOnClick(network_id)) { |
| // Special case: clicking "click to activate" on a network item should open |
| // the cellular setup dialogs' pSIM flow if the network is a non-activated |
| // cellular network. |
| ShowSettingsCellularSetup(/*show_psim_flow=*/true); |
| return; |
| } |
| |
| std::string page = chromeos::settings::mojom::kNetworkSectionPath; |
| const ash::NetworkState* network_state = GetNetworkState(network_id); |
| if (!network_id.empty() && network_state) { |
| // TODO(khorimoto): Use a more general path name here. This path is named |
| // kWifi*, but it's actually a generic page. |
| page = chromeos::settings::mojom::kWifiDetailsSubpagePath; |
| page += "?guid="; |
| page += base::EscapeUrlEncodedData(network_id, true); |
| page += "&name="; |
| page += base::EscapeUrlEncodedData(network_state->name(), true); |
| page += "&type="; |
| page += base::EscapeUrlEncodedData( |
| ash::network_util::TranslateShillTypeToONC(network_state->type()), |
| true); |
| page += "&settingId="; |
| page += base::NumberToString(static_cast<int32_t>( |
| chromeos::settings::mojom::Setting::kDisconnectWifiNetwork)); |
| if (show_configure) { |
| page += "&showConfigure=true"; |
| } |
| } |
| base::RecordAction(base::UserMetricsAction("OpenInternetOptionsDialog")); |
| ShowSettingsSubPageForActiveUser(page); |
| } |
| |
| void SystemTrayClientImpl::ShowMultiDeviceSetup() { |
| ash::multidevice_setup::MultiDeviceSetupDialog::Show(); |
| } |
| |
| void SystemTrayClientImpl::ShowFirmwareUpdate() { |
| chrome::ShowFirmwareUpdatesApp(ProfileManager::GetActiveUserProfile()); |
| } |
| |
| void SystemTrayClientImpl::SetLocaleAndExit( |
| const std::string& locale_iso_code) { |
| ProfileManager::GetActiveUserProfile()->ChangeAppLocale( |
| locale_iso_code, Profile::APP_LOCALE_CHANGED_VIA_SYSTEM_TRAY); |
| chrome::AttemptUserExit(); |
| } |
| |
| void SystemTrayClientImpl::ShowAccessCodeCastingDialog( |
| AccessCodeCastDialogOpenLocation open_location) { |
| media_router::AccessCodeCastDialog::ShowForDesktopMirroring(open_location); |
| } |
| |
| void SystemTrayClientImpl::ShowCalendarEvent( |
| const std::optional<GURL>& event_url, |
| const base::Time& date, |
| bool& opened_pwa, |
| GURL& final_event_url) { |
| // Default is that we didn't open the calendar PWA. |
| opened_pwa = false; |
| |
| // Calendar URL we'll actually open, today's date by default. |
| GURL official_url(kOfficialCalendarUrlPrefix); |
| |
| // Compose the actual URL to be opened. |
| if (event_url.has_value()) { |
| // An event URL was passed in, so modify it as needed for us to pass the "in |
| // app scope" guards in WebAppLaunchProcess::Run(). See http://b/214428922 |
| GURL::Replacements replacements; |
| replacements.SetSchemeStr("https"); |
| replacements.SetHostStr("calendar.google.com"); |
| official_url = event_url->ReplaceComponents(replacements); |
| } else { |
| // No event URL provided, so fall back on opening calendar with `date`. |
| official_url = GURL(kOfficialCalendarUrlPrefix + |
| base::UnlocalizedTimeFormatWithPattern( |
| date, "'r/week/'y/M/d", icu::TimeZone::getGMT())); |
| } |
| |
| // Return the URL we actually opened. |
| final_event_url = official_url; |
| |
| // Check calendar web app installation. |
| if (!IsAppInstalled(ash::kGoogleCalendarAppId)) { |
| OpenInBrowser(official_url); |
| return; |
| } |
| |
| // Need this in order to launch the web app. |
| apps::AppServiceProxyAsh* proxy = GetActiveUserAppServiceProxyAsh(); |
| if (!proxy) { |
| LOG(ERROR) << __FUNCTION__ |
| << " failed to get active user AppServiceProxyAsh"; |
| OpenInBrowser(official_url); |
| return; |
| } |
| |
| // Launch web app. |
| proxy->LaunchAppWithUrl(ash::kGoogleCalendarAppId, ui::EF_NONE, official_url, |
| apps::LaunchSource::kFromShelf); |
| opened_pwa = true; |
| } |
| |
| // TODO(b/269075177): Reuse existing Google Meet PWA instead of opening a new |
| // one for each call to `LaunchAppWithUrl`. |
| void SystemTrayClientImpl::ShowVideoConference( |
| const GURL& video_conference_url) { |
| if (auto* profile = ProfileManager::GetActiveUserProfile()) { |
| apps::MaybeLaunchPreferredAppForUrl( |
| profile, video_conference_url, |
| apps::LaunchSource::kFromSysTrayCalendar); |
| } |
| } |
| |
| void SystemTrayClientImpl::ShowChannelInfoAdditionalDetails() { |
| base::RecordAction( |
| base::UserMetricsAction("Tray_ShowChannelInfoAdditionalDetails")); |
| ShowSettingsSubPageForActiveUser( |
| std::string(chromeos::settings::mojom::kDetailedBuildInfoSubpagePath)); |
| } |
| |
| void SystemTrayClientImpl::ShowChannelInfoGiveFeedback() { |
| ash::NewWindowDelegate::GetInstance()->OpenFeedbackPage( |
| ash::NewWindowDelegate::kFeedbackSourceChannelIndicator); |
| } |
| |
| void SystemTrayClientImpl::ShowAudioSettings() { |
| base::RecordAction(base::UserMetricsAction("ShowAudioSettingsPage")); |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kAudioSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowGraphicsTabletSettings() { |
| DCHECK(ash::features::IsPeripheralCustomizationEnabled()); |
| base::RecordAction(base::UserMetricsAction("ShowGraphicsTabletSettingsPage")); |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kGraphicsTabletSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowMouseSettings() { |
| DCHECK(ash::features::IsPeripheralCustomizationEnabled()); |
| base::RecordAction(base::UserMetricsAction("ShowMouseSettingsPage")); |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kPerDeviceMouseSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowKeyboardSettings() { |
| DCHECK(ash::features::IsWelcomeExperienceEnabled()); |
| base::RecordAction(base::UserMetricsAction("ShowKeyboardSettingsPage")); |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kPerDeviceKeyboardSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowTouchpadSettings() { |
| base::RecordAction(base::UserMetricsAction("ShowTouchpadSettingsPage")); |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kPerDeviceTouchpadSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowPointingStickSettings() { |
| DCHECK(ash::features::IsWelcomeExperienceEnabled()); |
| base::RecordAction(base::UserMetricsAction("ShowPointingStickSettingsPage")); |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kPerDevicePointingStickSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowNearbyShareSettings() { |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kNearbyShareSubpagePath); |
| } |
| |
| void SystemTrayClientImpl::ShowRemapKeysSubpage(int device_id) { |
| base::RecordAction(base::UserMetricsAction("ShowRemapKeysSettingsSubpage")); |
| ShowSettingsSubPageForActiveUser(base::StrCat({ |
| chromeos::settings::mojom::kPerDeviceKeyboardRemapKeysSubpagePath, |
| "?keyboardId=", |
| base::NumberToString(device_id), |
| })); |
| } |
| |
| void SystemTrayClientImpl::ShowYouTubeMusicPremiumPage() { |
| base::RecordAction(base::UserMetricsAction("ShowYouTubeMusicPremiumPage")); |
| |
| const GURL official_url(chrome::kYoutubeMusicPremiumURL); |
| |
| // Check YouTube Music web app installation. |
| if (!IsAppInstalled(ash::kYoutubeMusicAppId)) { |
| OpenInBrowser(official_url); |
| return; |
| } |
| |
| // Need this in order to launch the web app. |
| apps::AppServiceProxyAsh* proxy = GetActiveUserAppServiceProxyAsh(); |
| if (!proxy) { |
| LOG(ERROR) << " failed to get active user AppServiceProxyAsh"; |
| OpenInBrowser(official_url); |
| return; |
| } |
| |
| // Launch web app. |
| proxy->LaunchAppWithUrl( |
| ash::kYoutubeMusicAppId, ui::EF_NONE, official_url, |
| apps::LaunchSource::kFromFocusMode, /*window_info=*/nullptr, |
| base::BindOnce( |
| [](const GURL& url, apps::LaunchResult&& result) { |
| if (result.state != apps::LaunchResult::State::kSuccess) { |
| OpenInBrowser(url); |
| } |
| }, |
| official_url)); |
| } |
| |
| void SystemTrayClientImpl::ShowChromebookPerksYouTubePage() { |
| OpenInBrowser(GURL(chrome::kChromebookPerksYouTubePage)); |
| } |
| |
| void SystemTrayClientImpl::ShowEolInfoPage() { |
| ash::NewWindowDelegate::GetInstance()->OpenUrl( |
| GURL(chrome::kEolNotificationURL), |
| ash::NewWindowDelegate::OpenUrlFrom::kUserInteraction, |
| ash::NewWindowDelegate::Disposition::kNewForegroundTab); |
| } |
| |
| bool SystemTrayClientImpl::IsUserFeedbackEnabled() { |
| if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| ash::switches::kForceShowReleaseTrack)) { |
| // Force the release track UI to show the feedback button. |
| return true; |
| } |
| PrefService* signin_prefs = |
| ProfileManager::GetActiveUserProfile()->GetPrefs(); |
| DCHECK(signin_prefs); |
| return signin_prefs->GetBoolean(prefs::kUserFeedbackAllowed); |
| } |
| |
| void SystemTrayClientImpl::HandleUpdateAvailable() { |
| UpgradeDetector* detector = UpgradeDetector::GetInstance(); |
| if (detector->upgrade_notification_stage() == |
| UpgradeDetector::UPGRADE_ANNOYANCE_NONE) { |
| // Close any existing notifications. |
| ResetUpdateState(); |
| return; |
| } |
| |
| if (!detector->notify_upgrade()) { |
| LOG(ERROR) << "Tried to show update notification when no update available"; |
| return; |
| } |
| |
| // Show the system tray icon. |
| ash::UpdateSeverity severity = GetUpdateSeverity(detector); |
| system_tray_->ShowUpdateIcon(severity, detector->is_factory_reset_required(), |
| detector->is_rollback()); |
| |
| // Overwrite title and body. |
| system_tray_->SetRelaunchNotificationState(relaunch_notification_state_); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // chromeos::system::SystemClockObserver: |
| |
| void SystemTrayClientImpl::OnSystemClockChanged( |
| ash::system::SystemClock* clock) { |
| system_tray_->SetUse24HourClock(clock->ShouldUse24HourClock()); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // UpgradeDetector::UpgradeObserver: |
| void SystemTrayClientImpl::OnUpdateDeferred(bool use_notification) { |
| system_tray_->SetUpdateDeferred( |
| use_notification ? ash::DeferredUpdateState::kShowNotification |
| : ash::DeferredUpdateState::kShowDialog); |
| } |
| |
| void SystemTrayClientImpl::OnUpdateOverCellularAvailable() { |
| // Requests that ash show the update over cellular available icon. |
| system_tray_->SetUpdateOverCellularAvailableIconVisible(true); |
| } |
| |
| void SystemTrayClientImpl::OnUpdateOverCellularOneTimePermissionGranted() { |
| // Requests that ash hide the update over cellular available icon. |
| system_tray_->SetUpdateOverCellularAvailableIconVisible(false); |
| } |
| |
| void SystemTrayClientImpl::OnUpgradeRecommended() { |
| HandleUpdateAvailable(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // policy::CloudPolicyStore::Observer |
| void SystemTrayClientImpl::OnStoreLoaded(policy::CloudPolicyStore* store) { |
| UpdateDeviceEnterpriseInfo(); |
| } |
| |
| void SystemTrayClientImpl::OnStoreError(policy::CloudPolicyStore* store) { |
| UpdateDeviceEnterpriseInfo(); |
| } |
| |
| void SystemTrayClientImpl::UpdateDeviceEnterpriseInfo() { |
| ash::DeviceEnterpriseInfo device_enterprise_info; |
| device_enterprise_info.enterprise_domain_manager = |
| browser_policy_connector_ash_->GetEnterpriseDomainManager(); |
| device_enterprise_info.management_device_mode = |
| GetManagementDeviceMode(*browser_policy_connector_ash_); |
| if (!last_device_enterprise_info_) { |
| last_device_enterprise_info_ = |
| std::make_unique<ash::DeviceEnterpriseInfo>(); |
| } |
| |
| if (device_enterprise_info == *last_device_enterprise_info_) { |
| return; |
| } |
| |
| // Send to ash, which will add an item to the system tray. |
| system_tray_->SetDeviceEnterpriseInfo(device_enterprise_info); |
| *last_device_enterprise_info_ = device_enterprise_info; |
| } |
| |
| void SystemTrayClientImpl::UpdateEnterpriseAccountDomainInfo(Profile* profile) { |
| std::string account_manager = |
| profile ? GetAccountManagerIdentity(profile).value_or(std::string()) |
| : std::string(); |
| if (account_manager == last_enterprise_account_domain_manager_) { |
| return; |
| } |
| |
| // Send to ash, which will add an item to the system tray. |
| system_tray_->SetEnterpriseAccountDomainInfo(account_manager); |
| last_enterprise_account_domain_manager_ = account_manager; |
| } |