| // 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_tray_client_impl.h" |
| |
| #include <cstdio> |
| #include <memory> |
| |
| #include "ash/constants/ash_features.h" |
| #include "ash/constants/personalization_entry_point.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 "base/command_line.h" |
| #include "base/logging.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/stringprintf.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/crosapi/browser_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/set_time_dialog.h" |
| #include "chrome/browser/ash/system/system_clock.h" |
| #include "chrome/browser/ash/web_applications/personalization_app/personalization_app_metrics.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/browser_process_platform_part.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/chromeos/bluetooth_pairing_dialog.h" |
| #include "chrome/browser/ui/webui/chromeos/internet_config_dialog.h" |
| #include "chrome/browser/ui/webui/chromeos/internet_detail_dialog.h" |
| #include "chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.h" |
| #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h" |
| #include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h" |
| #include "chrome/browser/upgrade_detector/upgrade_detector.h" |
| #include "chrome/browser/web_applications/web_app_id_constants.h" |
| #include "chrome/common/channel_info.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.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 "components/prefs/pref_service.h" |
| #include "components/services/app_service/public/cpp/app_launch_util.h" |
| #include "components/services/app_service/public/cpp/features.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 "ui/accessibility/accessibility_features.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. |
| const 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(ash::UpdateType update_type, |
| UpgradeDetector* detector) { |
| // Lacros is always "low", which is the same severity OS updates start with. |
| if (update_type == ash::UpdateType::kLacros) |
| return ash::UpdateSeverity::kLow; |
| |
| // 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) { |
| if (crosapi::browser_util::IsLacrosPrimaryBrowser()) { |
| auto* browser_manager = crosapi::BrowserManager::Get(); |
| browser_manager->SwitchToTab( |
| event_url, |
| /*path_behavior=*/NavigateParams::IGNORE_AND_NAVIGATE); |
| return; |
| } |
| |
| // Lacros is not the primary browser, so use this workaround. |
| chrome::ScopedTabbedBrowserDisplayer displayer( |
| ProfileManager::GetActiveUserProfile()); |
| NavigateParams params( |
| GetSingletonTabNavigateParams(displayer.browser(), event_url)); |
| params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE; |
| ShowSingletonTabOverwritingNTP(displayer.browser(), ¶ms); |
| } |
| |
| 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(); |
| return ash::ManagementDeviceMode::kOther; |
| } |
| |
| } // 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: |
| SystemTrayClientImpl* const owner_; |
| Profile* profile_ = nullptr; |
| |
| base::ScopedObservation< |
| user_manager::UserManager, |
| user_manager::UserManager::UserSessionStateObserver, |
| &user_manager::UserManager::AddSessionStateObserver, |
| &user_manager::UserManager::RemoveSessionStateObserver> |
| 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 { 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() |
| : 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_|. |
| chromeos::system::SystemClock* clock = |
| g_browser_process->platform_part()->GetSystemClock(); |
| clock->AddObserver(this); |
| system_tray_->SetUse24HourClock(clock->ShouldUse24HourClock()); |
| |
| // If an upgrade is available at startup then tell ash about it. |
| if (UpgradeDetector::GetInstance()->notify_upgrade()) |
| HandleUpdateAvailable(ash::UpdateType::kSystem); |
| |
| // If the device is enterprise managed then send ash the enterprise domain. |
| policy::BrowserPolicyConnectorAsh* policy_connector = |
| g_browser_process->platform_part()->browser_policy_connector_ash(); |
| policy::DeviceCloudPolicyManagerAsh* policy_manager = |
| policy_connector->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::BrowserPolicyConnectorAsh* connector = |
| g_browser_process->platform_part()->browser_policy_connector_ash(); |
| policy::DeviceCloudPolicyManagerAsh* policy_manager = |
| connector->GetDeviceCloudPolicyManager(); |
| if (policy_manager) |
| policy_manager->core()->store()->RemoveObserver(this); |
| |
| g_browser_process->platform_part()->GetSystemClock()->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(ash::UpdateType::kSystem); |
| } |
| |
| void SystemTrayClientImpl::ResetUpdateState() { |
| relaunch_notification_state_ = {}; |
| system_tray_->ResetUpdateState(); |
| } |
| |
| void SystemTrayClientImpl::SetLacrosUpdateAvailable() { |
| HandleUpdateAvailable(ash::UpdateType::kLacros); |
| } |
| |
| 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); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ash::SystemTrayClient: |
| |
| void SystemTrayClientImpl::ShowSettings(int64_t display_id) { |
| // TODO(jamescook): Use different metric for OS settings. |
| base::RecordAction(base::UserMetricsAction("ShowOptions")); |
| chrome::SettingsWindowManager::GetInstance()->ShowOSSettings( |
| ProfileManager::GetActiveUserProfile(), display_id); |
| } |
| |
| 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( |
| absl::optional<base::StringPiece> device_address) { |
| if (chromeos::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::kDateAndTimeSectionPath); |
| } |
| |
| 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::GetPrimary()->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::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. |
| bool is_kiosk = user_manager::UserManager::Get()->IsLoggedInAsAnyKioskApp(); |
| if (!is_kiosk && ::features::IsAccessibilityOSSettingsVisibilityEnabled()) { |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kAccessibilitySectionPath); |
| } else { |
| ShowSettingsSubPageForActiveUser( |
| chromeos::settings::mojom::kManageAccessibilitySubpagePath); |
| } |
| } |
| |
| 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() { |
| if (crosapi::browser_util::IsLacrosPrimaryBrowser()) { |
| crosapi::BrowserManager::Get()->SwitchToTab( |
| GURL(chrome::kChromePaletteHelpURL), |
| /*path_behavior=*/NavigateParams::RESPECT); |
| return; |
| } |
| |
| chrome::ScopedTabbedBrowserDisplayer displayer( |
| ProfileManager::GetActiveUserProfile()); |
| ShowSingletonTab(displayer.browser(), 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. |
| if (crosapi::browser_util::IsLacrosPrimaryBrowser()) { |
| crosapi::BrowserManager::Get()->SwitchToTab( |
| GURL(chrome::kChromeUIManagementURL), |
| /*path_behavior=*/NavigateParams::RESPECT); |
| return; |
| } |
| |
| 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; |
| } |
| |
| chromeos::InternetConfigDialog::ShowDialogForNetworkId(network_id); |
| } |
| |
| void SystemTrayClientImpl::ShowNetworkCreate(const std::string& type) { |
| if (type == ::onc::network_type::kCellular) { |
| ShowSettingsCellularSetup(/*show_psim_flow=*/false); |
| return; |
| } |
| chromeos::InternetConfigDialog::ShowDialogForNetworkType(type); |
| } |
| |
| void SystemTrayClientImpl::ShowSettingsCellularSetup(bool show_psim_flow) { |
| // TODO(crbug.com/1093185) 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::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; |
| } |
| |
| if (base::FeatureList::IsEnabled(apps::kAppServiceLaunchWithoutMojom)) { |
| apps::AppServiceProxyFactory::GetForProfile(profile)->Launch( |
| app_id, ui::EF_NONE, apps::LaunchSource::kFromParentalControls); |
| } else { |
| apps::AppServiceProxyFactory::GetForProfile(profile)->Launch( |
| app_id, ui::EF_NONE, apps::mojom::LaunchSource::kFromParentalControls); |
| } |
| } |
| |
| void SystemTrayClientImpl::ShowSettingsSimUnlock() { |
| // TODO(https://crbug.com/1093185) 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::ShowNetworkSettings(const std::string& network_id) { |
| ShowNetworkSettingsHelper(network_id, false /* show_configure */); |
| } |
| |
| 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()) { |
| chromeos::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() { |
| chromeos::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 absl::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`. |
| std::string calendar_url_str = kOfficialCalendarUrlPrefix; |
| base::Time::Exploded date_exp; |
| date.UTCExplode(&date_exp); |
| std::string date_url = |
| base::StringPrintf("r/week/%d/%d/%d", date_exp.year, date_exp.month, |
| date_exp.day_of_month); |
| calendar_url_str.append(date_url); |
| official_url = GURL(calendar_url_str); |
| } |
| |
| // Return the URL we actually opened. |
| final_event_url = official_url; |
| |
| // Check calendar web app installation. |
| if (!IsAppInstalled(web_app::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. |
| if (base::FeatureList::IsEnabled(apps::kAppServiceLaunchWithoutMojom)) { |
| proxy->LaunchAppWithUrl( |
| web_app::kGoogleCalendarAppId, |
| apps::GetEventFlags(WindowOpenDisposition::NEW_WINDOW, |
| /*prefer_container=*/true), |
| official_url, apps::LaunchSource::kFromShelf); |
| } else { |
| proxy->LaunchAppWithUrl( |
| web_app::kGoogleCalendarAppId, |
| apps::GetEventFlags(WindowOpenDisposition::NEW_WINDOW, |
| /*prefer_container=*/true), |
| official_url, apps::mojom::LaunchSource::kFromShelf); |
| } |
| opened_pwa = true; |
| } |
| |
| 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); |
| } |
| |
| bool SystemTrayClientImpl::IsUserFeedbackEnabled() { |
| PrefService* signin_prefs = |
| ProfileManager::GetActiveUserProfile()->GetPrefs(); |
| DCHECK(signin_prefs); |
| return signin_prefs->GetBoolean(prefs::kUserFeedbackAllowed); |
| } |
| |
| SystemTrayClientImpl::SystemTrayClientImpl(SystemTrayClientImpl* mock_instance) |
| : system_tray_(nullptr) { |
| DCHECK(!g_system_tray_client_instance); |
| g_system_tray_client_instance = mock_instance; |
| } |
| |
| void SystemTrayClientImpl::HandleUpdateAvailable(ash::UpdateType update_type) { |
| UpgradeDetector* detector = UpgradeDetector::GetInstance(); |
| if (detector->upgrade_notification_stage() == |
| UpgradeDetector::UPGRADE_ANNOYANCE_NONE) { |
| // Close any existing notifications. |
| ResetUpdateState(); |
| return; |
| } |
| |
| if (update_type == ash::UpdateType::kSystem && !detector->notify_upgrade()) { |
| LOG(ERROR) << "Tried to show update notification when no update available"; |
| return; |
| } |
| |
| // Show the system tray icon. |
| ash::UpdateSeverity severity = GetUpdateSeverity(update_type, detector); |
| system_tray_->ShowUpdateIcon(severity, detector->is_factory_reset_required(), |
| detector->is_rollback(), update_type); |
| |
| // Only overwrite title and body for system updates. |
| if (update_type == ash::UpdateType::kSystem) |
| system_tray_->SetRelaunchNotificationState(relaunch_notification_state_); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // chromeos::system::SystemClockObserver: |
| |
| void SystemTrayClientImpl::OnSystemClockChanged( |
| chromeos::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(ash::UpdateType::kSystem); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // policy::CloudPolicyStore::Observer |
| void SystemTrayClientImpl::OnStoreLoaded(policy::CloudPolicyStore* store) { |
| UpdateDeviceEnterpriseInfo(); |
| } |
| |
| void SystemTrayClientImpl::OnStoreError(policy::CloudPolicyStore* store) { |
| UpdateDeviceEnterpriseInfo(); |
| } |
| |
| void SystemTrayClientImpl::UpdateDeviceEnterpriseInfo() { |
| policy::BrowserPolicyConnectorAsh* connector = |
| g_browser_process->platform_part()->browser_policy_connector_ash(); |
| ash::DeviceEnterpriseInfo device_enterprise_info; |
| device_enterprise_info.enterprise_domain_manager = |
| connector->GetEnterpriseDomainManager(); |
| device_enterprise_info.active_directory_managed = |
| connector->IsActiveDirectoryManaged(); |
| device_enterprise_info.management_device_mode = |
| GetManagementDeviceMode(connector); |
| 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 |
| ? chrome::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; |
| } |