| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // 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.h" |
| |
| #include "ash/public/cpp/shell_window_ids.h" |
| #include "ash/public/interfaces/constants.mojom.h" |
| #include "ash/shell.h" |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/metrics/user_metrics.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/browser_process_platform_part.h" |
| #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" |
| #include "chrome/browser/chromeos/login/help_app_launcher.h" |
| #include "chrome/browser/chromeos/options/network_config_view.h" |
| #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" |
| #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h" |
| #include "chrome/browser/chromeos/profiles/profile_helper.h" |
| #include "chrome/browser/chromeos/set_time_dialog.h" |
| #include "chrome/browser/chromeos/system/system_clock.h" |
| #include "chrome/browser/lifetime/termination_notification.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/ui/app_list/arc/arc_app_utils.h" |
| #include "chrome/browser/ui/ash/ash_util.h" |
| #include "chrome/browser/ui/chrome_pages.h" |
| #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" |
| #include "chrome/browser/ui/singleton_tabs.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/upgrade_detector.h" |
| #include "chrome/common/url_constants.h" |
| #include "chromeos/chromeos_switches.h" |
| #include "chromeos/dbus/dbus_thread_manager.h" |
| #include "chromeos/dbus/session_manager_client.h" |
| #include "chromeos/network/network_handler.h" |
| #include "chromeos/network/network_state.h" |
| #include "chromeos/network/network_state_handler.h" |
| #include "chromeos/network/network_util.h" |
| #include "chromeos/network/tether_constants.h" |
| #include "components/arc/arc_bridge_service.h" |
| #include "components/arc/arc_service_manager.h" |
| #include "components/arc/common/net.mojom.h" |
| #include "components/arc/connection_holder.h" |
| #include "components/session_manager/core/session_manager.h" |
| #include "components/user_manager/user_manager.h" |
| #include "content/public/common/service_manager_connection.h" |
| #include "device/bluetooth/bluetooth_device.h" |
| #include "extensions/browser/api/vpn_provider/vpn_service.h" |
| #include "extensions/browser/api/vpn_provider/vpn_service_factory.h" |
| #include "net/base/escape.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "services/ui/public/cpp/property_type_converters.h" |
| #include "services/ui/public/interfaces/window_manager.mojom.h" |
| #include "third_party/cros_system_api/dbus/shill/dbus-constants.h" |
| #include "ui/events/event_constants.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/views/window/dialog_delegate.h" |
| |
| using chromeos::DBusThreadManager; |
| using chromeos::UpdateEngineClient; |
| using session_manager::SessionState; |
| using session_manager::SessionManager; |
| using views::Widget; |
| |
| namespace { |
| |
| SystemTrayClient* g_system_tray_client_instance = nullptr; |
| |
| void ShowSettingsSubPageForActiveUser(const std::string& sub_page) { |
| chrome::ShowSettingsSubPageForProfile(ProfileManager::GetActiveUserProfile(), |
| sub_page); |
| } |
| |
| // Returns the severity of a pending Chrome / Chrome OS update. |
| ash::mojom::UpdateSeverity GetUpdateSeverity(UpgradeDetector* detector) { |
| switch (detector->upgrade_notification_stage()) { |
| case UpgradeDetector::UPGRADE_ANNOYANCE_NONE: |
| return ash::mojom::UpdateSeverity::NONE; |
| case UpgradeDetector::UPGRADE_ANNOYANCE_LOW: |
| return ash::mojom::UpdateSeverity::LOW; |
| case UpgradeDetector::UPGRADE_ANNOYANCE_ELEVATED: |
| return ash::mojom::UpdateSeverity::ELEVATED; |
| case UpgradeDetector::UPGRADE_ANNOYANCE_HIGH: |
| return ash::mojom::UpdateSeverity::HIGH; |
| case UpgradeDetector::UPGRADE_ANNOYANCE_CRITICAL: |
| return ash::mojom::UpdateSeverity::CRITICAL; |
| } |
| NOTREACHED(); |
| return ash::mojom::UpdateSeverity::CRITICAL; |
| } |
| |
| const chromeos::NetworkState* GetNetworkState(const std::string& network_id) { |
| if (network_id.empty()) |
| return nullptr; |
| return chromeos::NetworkHandler::Get() |
| ->network_state_handler() |
| ->GetNetworkStateFromGuid(network_id); |
| } |
| |
| bool IsArcVpn(const std::string& network_id) { |
| const chromeos::NetworkState* network_state = GetNetworkState(network_id); |
| return network_state && network_state->type() == shill::kTypeVPN && |
| network_state->vpn_provider_type() == shill::kProviderArcVpn; |
| } |
| |
| } // namespace |
| |
| SystemTrayClient::SystemTrayClient() : binding_(this) { |
| content::ServiceManagerConnection::GetForProcess() |
| ->GetConnector() |
| ->BindInterface(ash::mojom::kServiceName, &system_tray_); |
| // Register this object as the client interface implementation. |
| ash::mojom::SystemTrayClientPtr client; |
| binding_.Bind(mojo::MakeRequest(&client)); |
| system_tray_->SetClient(std::move(client)); |
| |
| // If this observes clock setting changes before ash comes up the IPCs will |
| // be queued on |system_tray_|. |
| g_browser_process->platform_part()->GetSystemClock()->AddObserver(this); |
| |
| // 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::BrowserPolicyConnectorChromeOS* policy_connector = |
| g_browser_process->platform_part()->browser_policy_connector_chromeos(); |
| policy::DeviceCloudPolicyManagerChromeOS* policy_manager = |
| policy_connector->GetDeviceCloudPolicyManager(); |
| if (policy_manager) |
| policy_manager->core()->store()->AddObserver(this); |
| UpdateEnterpriseDisplayDomain(); |
| |
| DCHECK(!g_system_tray_client_instance); |
| g_system_tray_client_instance = this; |
| UpgradeDetector::GetInstance()->AddObserver(this); |
| } |
| |
| SystemTrayClient::~SystemTrayClient() { |
| DCHECK_EQ(this, g_system_tray_client_instance); |
| g_system_tray_client_instance = nullptr; |
| |
| policy::BrowserPolicyConnectorChromeOS* connector = |
| g_browser_process->platform_part()->browser_policy_connector_chromeos(); |
| policy::DeviceCloudPolicyManagerChromeOS* 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 |
| SystemTrayClient* SystemTrayClient::Get() { |
| return g_system_tray_client_instance; |
| } |
| |
| // static |
| int SystemTrayClient::GetDialogParentContainerId() { |
| return SessionManager::Get()->session_state() == SessionState::ACTIVE |
| ? ash::kShellWindowId_SystemModalContainer |
| : ash::kShellWindowId_LockSystemModalContainer; |
| } |
| |
| // static |
| Widget* SystemTrayClient::CreateUnownedDialogWidget( |
| views::WidgetDelegate* widget_delegate) { |
| DCHECK(widget_delegate); |
| Widget::InitParams params = views::DialogDelegate::GetDialogWidgetInitParams( |
| widget_delegate, nullptr, nullptr, gfx::Rect()); |
| // Place the dialog in the appropriate modal dialog container, either above |
| // or below the lock screen, based on the login state. |
| int container_id = GetDialogParentContainerId(); |
| if (ash_util::IsRunningInMash()) { |
| using ui::mojom::WindowManager; |
| params.mus_properties[WindowManager::kContainerId_InitProperty] = |
| mojo::ConvertTo<std::vector<uint8_t>>(container_id); |
| } else { |
| params.parent = ash::Shell::GetContainer( |
| ash::Shell::GetRootWindowForNewWindows(), container_id); |
| } |
| Widget* widget = new Widget; // Owned by native widget. |
| widget->Init(params); |
| return widget; |
| } |
| |
| void SystemTrayClient::SetFlashUpdateAvailable() { |
| flash_update_available_ = true; |
| HandleUpdateAvailable(); |
| } |
| |
| void SystemTrayClient::SetPrimaryTrayEnabled(bool enabled) { |
| system_tray_->SetPrimaryTrayEnabled(enabled); |
| } |
| |
| void SystemTrayClient::SetPrimaryTrayVisible(bool visible) { |
| system_tray_->SetPrimaryTrayVisible(visible); |
| } |
| |
| void SystemTrayClient::SetPerformanceTracingIconVisible(bool visible) { |
| system_tray_->SetPerformanceTracingIconVisible(visible); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ash::mojom::SystemTrayClient: |
| |
| void SystemTrayClient::ShowSettings() { |
| ShowSettingsSubPageForActiveUser(std::string()); |
| } |
| |
| void SystemTrayClient::ShowBluetoothSettings() { |
| base::RecordAction(base::UserMetricsAction("ShowBluetoothSettingsPage")); |
| ShowSettingsSubPageForActiveUser(chrome::kBluetoothSubPage); |
| } |
| |
| void SystemTrayClient::ShowBluetoothPairingDialog( |
| const std::string& address, |
| const base::string16& name_for_display, |
| bool paired, |
| bool connected) { |
| std::string canonical_address = |
| device::BluetoothDevice::CanonicalizeAddress(address); |
| if (canonical_address.empty()) // Address was invalid. |
| return; |
| |
| base::RecordAction( |
| base::UserMetricsAction("StatusArea_Bluetooth_Connect_Unknown")); |
| chromeos::BluetoothPairingDialog::ShowDialog( |
| canonical_address, name_for_display, paired, connected); |
| } |
| |
| void SystemTrayClient::ShowDateSettings() { |
| base::RecordAction(base::UserMetricsAction("ShowDateOptions")); |
| // Everybody can change the time zone (even though it is a device setting). |
| chrome::ShowSettingsSubPageForProfile(ProfileManager::GetActiveUserProfile(), |
| chrome::kDateTimeSubPage); |
| } |
| |
| void SystemTrayClient::ShowSetTimeDialog() { |
| chromeos::SetTimeDialog::ShowDialogInContainer(GetDialogParentContainerId()); |
| } |
| |
| void SystemTrayClient::ShowDisplaySettings() { |
| base::RecordAction(base::UserMetricsAction("ShowDisplayOptions")); |
| ShowSettingsSubPageForActiveUser(chrome::kDisplaySubPage); |
| } |
| |
| void SystemTrayClient::ShowPowerSettings() { |
| base::RecordAction(base::UserMetricsAction("Tray_ShowPowerOptions")); |
| ShowSettingsSubPageForActiveUser(chrome::kPowerSubPage); |
| } |
| |
| void SystemTrayClient::ShowChromeSlow() { |
| chrome::ScopedTabbedBrowserDisplayer displayer( |
| ProfileManager::GetPrimaryUserProfile()); |
| chrome::ShowSlow(displayer.browser()); |
| } |
| |
| void SystemTrayClient::ShowIMESettings() { |
| base::RecordAction(base::UserMetricsAction("OpenLanguageOptionsDialog")); |
| ShowSettingsSubPageForActiveUser(chrome::kLanguageOptionsSubPage); |
| } |
| |
| void SystemTrayClient::ShowAboutChromeOS() { |
| // We always want to check for updates when showing the about page from the |
| // Ash UI. |
| ShowSettingsSubPageForActiveUser(std::string(chrome::kHelpSubPage) + |
| "?checkForUpdate=true"); |
| } |
| |
| void SystemTrayClient::ShowHelp() { |
| chrome::ShowHelpForProfile(ProfileManager::GetActiveUserProfile(), |
| chrome::HELP_SOURCE_MENU); |
| } |
| |
| void SystemTrayClient::ShowAccessibilityHelp() { |
| chrome::ScopedTabbedBrowserDisplayer displayer( |
| ProfileManager::GetActiveUserProfile()); |
| chromeos::AccessibilityManager::ShowAccessibilityHelp(displayer.browser()); |
| } |
| |
| void SystemTrayClient::ShowAccessibilitySettings() { |
| base::RecordAction(base::UserMetricsAction("ShowAccessibilitySettings")); |
| ShowSettingsSubPageForActiveUser(chrome::kAccessibilitySubPage); |
| } |
| |
| void SystemTrayClient::ShowPaletteHelp() { |
| chrome::ScopedTabbedBrowserDisplayer displayer( |
| ProfileManager::GetActiveUserProfile()); |
| ShowSingletonTab(displayer.browser(), GURL(chrome::kChromePaletteHelpURL)); |
| } |
| |
| void SystemTrayClient::ShowPaletteSettings() { |
| base::RecordAction(base::UserMetricsAction("ShowPaletteOptions")); |
| ShowSettingsSubPageForActiveUser(chrome::kStylusSubPage); |
| } |
| |
| void SystemTrayClient::ShowPublicAccountInfo() { |
| chrome::ScopedTabbedBrowserDisplayer displayer( |
| ProfileManager::GetActiveUserProfile()); |
| chrome::ShowPolicy(displayer.browser()); |
| } |
| |
| void SystemTrayClient::ShowEnterpriseInfo() { |
| // At the login screen, lock screen, etc. show enterprise help in a window. |
| if (SessionManager::Get()->IsUserSessionBlocked()) { |
| scoped_refptr<chromeos::HelpAppLauncher> help_app( |
| new chromeos::HelpAppLauncher(nullptr /* parent_window */)); |
| help_app->ShowHelpTopic(chromeos::HelpAppLauncher::HELP_ENTERPRISE); |
| return; |
| } |
| |
| // Otherwise show enterprise help in a browser tab. |
| chrome::ScopedTabbedBrowserDisplayer displayer( |
| ProfileManager::GetActiveUserProfile()); |
| ShowSingletonTab(displayer.browser(), GURL(chrome::kLearnMoreEnterpriseURL)); |
| } |
| |
| void SystemTrayClient::ShowNetworkConfigure(const std::string& network_id) { |
| // UI is not available at the lock screen. |
| if (SessionManager::Get()->IsScreenLocked()) |
| return; |
| |
| DCHECK(chromeos::NetworkHandler::IsInitialized()); |
| const chromeos::NetworkState* network_state = GetNetworkState(network_id); |
| if (!network_state) { |
| LOG(ERROR) << "Network not found: " << network_id; |
| return; |
| } |
| if (network_state->type() == chromeos::kTypeTether && |
| !network_state->tether_has_connected_to_host()) { |
| ShowNetworkSettingsHelper(network_id, true /* show_configure */); |
| return; |
| } |
| |
| if (chromeos::switches::IsNetworkSettingsConfigEnabled()) |
| chromeos::InternetConfigDialog::ShowDialogForNetworkId(network_id); |
| else |
| chromeos::NetworkConfigView::ShowForNetworkId(network_id); |
| } |
| |
| void SystemTrayClient::ShowNetworkCreate(const std::string& type) { |
| if (type == shill::kTypeCellular) { |
| const chromeos::NetworkState* cellular = |
| chromeos::NetworkHandler::Get() |
| ->network_state_handler() |
| ->FirstNetworkByType(chromeos::NetworkTypePattern::Primitive(type)); |
| std::string network_id = cellular ? cellular->guid() : ""; |
| ShowNetworkSettingsHelper(network_id, false /* show_configure */); |
| return; |
| } |
| if (chromeos::switches::IsNetworkSettingsConfigEnabled()) { |
| // TODO(stevenjb): Pass ONC type to ShowNetworkCreate once NetworkConfigView |
| // is deprecated. |
| std::string onc_type = |
| chromeos::network_util::TranslateShillTypeToONC(type); |
| chromeos::InternetConfigDialog::ShowDialogForNetworkType(onc_type); |
| } else { |
| chromeos::NetworkConfigView::ShowForType(type); |
| } |
| } |
| |
| void SystemTrayClient::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 SystemTrayClient::ShowArcVpnCreate(const std::string& app_id) { |
| Profile* profile = ProfileManager::GetPrimaryUserProfile(); |
| |
| if (!profile) |
| return; |
| |
| arc::LaunchApp(profile, app_id, ui::EF_NONE); |
| } |
| |
| void SystemTrayClient::ShowNetworkSettings(const std::string& network_id) { |
| ShowNetworkSettingsHelper(network_id, false /* show_configure */); |
| } |
| |
| void SystemTrayClient::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 (IsArcVpn(network_id)) { |
| // Special case: clicking on a connected ARCVPN will ask Android to |
| // show the settings dialog. |
| auto* net_instance = ARC_GET_INSTANCE_FOR_METHOD( |
| arc::ArcServiceManager::Get()->arc_bridge_service()->net(), |
| ConfigureAndroidVpn); |
| if (!net_instance) { |
| LOG(ERROR) << "User requested VPN configuration but API is unavailable"; |
| return; |
| } |
| net_instance->ConfigureAndroidVpn(); |
| return; |
| } |
| |
| std::string page = chrome::kInternetSubPage; |
| if (!network_id.empty()) { |
| page = chrome::kNetworkDetailSubPage; |
| page += "?guid="; |
| page += net::EscapeUrlEncodedData(network_id, true); |
| if (show_configure) |
| page += "&showConfigure=true"; |
| } |
| base::RecordAction(base::UserMetricsAction("OpenInternetOptionsDialog")); |
| ShowSettingsSubPageForActiveUser(page); |
| } |
| |
| void SystemTrayClient::ShowMultiDeviceSetup() { |
| chromeos::multidevice_setup::MultiDeviceSetupDialog::Show(); |
| } |
| |
| void SystemTrayClient::RequestRestartForUpdate() { |
| // Flash updates on Chrome OS require device reboot. |
| const browser_shutdown::RebootPolicy reboot_policy = |
| flash_update_available_ ? browser_shutdown::RebootPolicy::kForceReboot |
| : browser_shutdown::RebootPolicy::kOptionalReboot; |
| |
| browser_shutdown::NotifyAndTerminate(true /* fast_path */, reboot_policy); |
| } |
| |
| void SystemTrayClient::HandleUpdateAvailable() { |
| // Show an update icon for Chrome updates and Flash component updates. |
| UpgradeDetector* detector = UpgradeDetector::GetInstance(); |
| bool update_available = detector->notify_upgrade() || flash_update_available_; |
| DCHECK(update_available); |
| if (!update_available) |
| return; |
| |
| // Get the Chrome update severity. |
| ash::mojom::UpdateSeverity severity = GetUpdateSeverity(detector); |
| |
| // Flash updates are low severity unless the Chrome severity is higher. |
| if (flash_update_available_) |
| severity = std::max(severity, ash::mojom::UpdateSeverity::LOW); |
| |
| // Show a string specific to updating flash player if there is no system |
| // update. |
| ash::mojom::UpdateType update_type = detector->notify_upgrade() |
| ? ash::mojom::UpdateType::SYSTEM |
| : ash::mojom::UpdateType::FLASH; |
| |
| system_tray_->ShowUpdateIcon(severity, detector->is_factory_reset_required(), |
| update_type); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // chromeos::system::SystemClockObserver: |
| |
| void SystemTrayClient::OnSystemClockChanged( |
| chromeos::system::SystemClock* clock) { |
| system_tray_->SetUse24HourClock(clock->ShouldUse24HourClock()); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // UpgradeDetector::UpgradeObserver: |
| void SystemTrayClient::OnUpdateOverCellularAvailable() { |
| // Requests that ash show the update over cellular available icon. |
| system_tray_->SetUpdateOverCellularAvailableIconVisible(true); |
| } |
| |
| void SystemTrayClient::OnUpdateOverCellularOneTimePermissionGranted() { |
| // Requests that ash hide the update over cellular available icon. |
| system_tray_->SetUpdateOverCellularAvailableIconVisible(false); |
| } |
| |
| void SystemTrayClient::OnUpgradeRecommended() { |
| HandleUpdateAvailable(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // policy::CloudPolicyStore::Observer |
| void SystemTrayClient::OnStoreLoaded(policy::CloudPolicyStore* store) { |
| UpdateEnterpriseDisplayDomain(); |
| } |
| |
| void SystemTrayClient::OnStoreError(policy::CloudPolicyStore* store) { |
| UpdateEnterpriseDisplayDomain(); |
| } |
| |
| void SystemTrayClient::UpdateEnterpriseDisplayDomain() { |
| policy::BrowserPolicyConnectorChromeOS* connector = |
| g_browser_process->platform_part()->browser_policy_connector_chromeos(); |
| const std::string enterprise_display_domain = |
| connector->GetEnterpriseDisplayDomain(); |
| const bool active_directory_managed = connector->IsActiveDirectoryManaged(); |
| if (enterprise_display_domain == last_enterprise_display_domain_ && |
| active_directory_managed == last_active_directory_managed_) { |
| return; |
| } |
| // Send to ash, which will add an item to the system tray. |
| system_tray_->SetEnterpriseDisplayDomain(enterprise_display_domain, |
| active_directory_managed); |
| last_enterprise_display_domain_ = enterprise_display_domain; |
| last_active_directory_managed_ = active_directory_managed; |
| } |