blob: 642e2eb67514019f3270fffe59a114b65e9be704 [file] [log] [blame]
// 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/locale_update_controller.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/weak_ptr.h"
#include "base/metrics/user_metrics.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/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/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/application_lifetime.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/chrome_pages.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/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/upgrade_detector/upgrade_detector.h"
#include "chrome/browser/web_applications/components/web_app_id_constants.h"
#include "chrome/common/url_constants.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/constants/chromeos_switches.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/session_manager/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/onc/onc_utils.h"
#include "chromeos/network/tether_constants.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/metrics/arc_metrics_constants.h"
#include "components/arc/mojom/net.mojom.h"
#include "components/arc/session/arc_bridge_service.h"
#include "components/arc/session/connection_holder.h"
#include "components/session_manager/core/session_manager.h"
#include "components/user_manager/user_manager.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 "third_party/cros_system_api/dbus/shill/dbus-constants.h"
#include "ui/events/event_constants.h"
using chromeos::DBusThreadManager;
using chromeos::UpdateEngineClient;
using session_manager::SessionManager;
using session_manager::SessionState;
namespace {
SystemTrayClient* g_system_tray_client_instance = nullptr;
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_HIGH:
return ash::UpdateSeverity::kHigh;
case UpgradeDetector::UPGRADE_ANNOYANCE_CRITICAL:
return ash::UpdateSeverity::kCritical;
}
}
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->GetVpnProviderType() == shill::kProviderArcVpn;
}
} // namespace
SystemTrayClient::SystemTrayClient()
: system_tray_(ash::SystemTray::Get()),
update_notification_style_(ash::NotificationStyle::kDefault) {
// 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::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);
UpdateEnterpriseDomainInfo();
system_tray_->SetClient(this);
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;
system_tray_->SetClient(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;
}
void SystemTrayClient::SetUpdateNotificationState(
ash::NotificationStyle style,
const base::string16& notification_title,
const base::string16& notification_body) {
update_notification_style_ = style;
update_notification_title_ = notification_title;
update_notification_body_ = notification_body;
HandleUpdateAvailable(ash::UpdateType::kSystem);
}
void SystemTrayClient::SetLacrosUpdateAvailable() {
HandleUpdateAvailable(ash::UpdateType::kLacros);
}
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);
}
void SystemTrayClient::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::mojom::SystemTrayClient:
void SystemTrayClient::ShowSettings() {
// TODO(jamescook): Use different metric for OS settings.
base::RecordAction(base::UserMetricsAction("ShowOptions"));
chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
ProfileManager::GetActiveUserProfile());
}
void SystemTrayClient::ShowBluetoothSettings() {
base::RecordAction(base::UserMetricsAction("ShowBluetoothSettingsPage"));
ShowSettingsSubPageForActiveUser(
chromeos::settings::mojom::kBluetoothDevicesSubpagePath);
}
void SystemTrayClient::ShowBluetoothPairingDialog(
const std::string& address,
const base::string16& name_for_display,
bool paired,
bool connected) {
if (chromeos::BluetoothPairingDialog::ShowDialog(address, name_for_display,
paired, connected)) {
base::RecordAction(
base::UserMetricsAction("StatusArea_Bluetooth_Connect_Unknown"));
}
}
void SystemTrayClient::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 SystemTrayClient::ShowSetTimeDialog() {
chromeos::SetTimeDialog::ShowDialog();
}
void SystemTrayClient::ShowDisplaySettings() {
base::RecordAction(base::UserMetricsAction("ShowDisplayOptions"));
ShowSettingsSubPageForActiveUser(
chromeos::settings::mojom::kDisplaySubpagePath);
}
void SystemTrayClient::ShowPowerSettings() {
base::RecordAction(base::UserMetricsAction("Tray_ShowPowerOptions"));
ShowSettingsSubPageForActiveUser(
chromeos::settings::mojom::kPowerSubpagePath);
}
void SystemTrayClient::ShowChromeSlow() {
chrome::ScopedTabbedBrowserDisplayer displayer(
ProfileManager::GetPrimaryUserProfile());
chrome::ShowSlow(displayer.browser());
}
void SystemTrayClient::ShowIMESettings() {
base::RecordAction(base::UserMetricsAction("OpenLanguageOptionsDialog"));
const std::string path =
base::FeatureList::IsEnabled(
::chromeos::features::kLanguageSettingsUpdate)
? chromeos::settings::mojom::kInputSubpagePath
: chromeos::settings::mojom::kLanguagesAndInputDetailsSubpagePath;
ShowSettingsSubPageForActiveUser(path);
}
void SystemTrayClient::ShowConnectedDevicesSettings() {
ShowSettingsSubPageForActiveUser(
chromeos::settings::mojom::kMultiDeviceFeaturesSubpagePath);
}
void SystemTrayClient::ShowTetherNetworkSettings() {
ShowSettingsSubPageForActiveUser(
chromeos::settings::mojom::kMobileDataNetworksSubpagePath);
}
void SystemTrayClient::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 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(
chromeos::settings::mojom::kManageAccessibilitySubpagePath);
}
void SystemTrayClient::ShowGestureEducationHelp() {
base::RecordAction(base::UserMetricsAction("ShowGestureEducationHelp"));
Profile* profile = ProfileManager::GetActiveUserProfile();
if (!profile)
return;
apps::AppServiceProxy* proxy =
apps::AppServiceProxyFactory::GetForProfileRedirectInIncognito(profile);
proxy->LaunchAppWithUrl(web_app::kHelpAppId, ui::EventFlags::EF_NONE,
GURL(chrome::kChromeOSGestureEducationHelpURL),
apps::mojom::LaunchSource::kFromOtherApp,
display::kDefaultDisplayId);
}
void SystemTrayClient::ShowPaletteHelp() {
chrome::ScopedTabbedBrowserDisplayer displayer(
ProfileManager::GetActiveUserProfile());
ShowSingletonTab(displayer.browser(), GURL(chrome::kChromePaletteHelpURL));
}
void SystemTrayClient::ShowPaletteSettings() {
base::RecordAction(base::UserMetricsAction("ShowPaletteOptions"));
ShowSettingsSubPageForActiveUser(
chromeos::settings::mojom::kStylusSubpagePath);
}
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 management info page.
chrome::ScopedTabbedBrowserDisplayer displayer(
ProfileManager::GetActiveUserProfile());
chrome::ShowEnterpriseManagementPageInTabbedBrowser(displayer.browser());
}
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;
}
chromeos::InternetConfigDialog::ShowDialogForNetworkId(network_id);
}
void SystemTrayClient::ShowNetworkCreate(const std::string& type) {
if (type == ::onc::network_type::kCellular) {
const chromeos::NetworkState* cellular =
chromeos::NetworkHandler::Get()
->network_state_handler()
->FirstNetworkByType(
chromeos::onc::NetworkTypePatternFromOncType(type));
std::string network_id = cellular ? cellular->guid() : "";
ShowNetworkSettingsHelper(network_id, false /* show_configure */);
return;
}
chromeos::InternetConfigDialog::ShowDialogForNetworkType(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,
arc::UserInteractionType::APP_STARTED_FROM_SETTINGS);
}
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 = chromeos::settings::mojom::kNetworkSectionPath;
const chromeos::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 += net::EscapeUrlEncodedData(network_id, true);
page += "&name=";
page += net::EscapeUrlEncodedData(network_state->name(), true);
page += "&type=";
page += net::EscapeUrlEncodedData(
chromeos::network_util::TranslateShillTypeToONC(network_state->type()),
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() {
browser_shutdown::NotifyAndTerminate(/*fast_path=*/true);
}
void SystemTrayClient::SetLocaleAndExit(const std::string& locale_iso_code) {
ProfileManager::GetActiveUserProfile()->ChangeAppLocale(
locale_iso_code, Profile::APP_LOCALE_CHANGED_VIA_SYSTEM_TRAY);
chrome::AttemptUserExit();
}
void SystemTrayClient::HandleUpdateAvailable(ash::UpdateType update_type) {
UpgradeDetector* detector = UpgradeDetector::GetInstance();
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_->SetUpdateNotificationState(update_notification_style_,
update_notification_title_,
update_notification_body_);
}
}
////////////////////////////////////////////////////////////////////////////////
// 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(ash::UpdateType::kSystem);
}
////////////////////////////////////////////////////////////////////////////////
// policy::CloudPolicyStore::Observer
void SystemTrayClient::OnStoreLoaded(policy::CloudPolicyStore* store) {
UpdateEnterpriseDomainInfo();
}
void SystemTrayClient::OnStoreError(policy::CloudPolicyStore* store) {
UpdateEnterpriseDomainInfo();
}
void SystemTrayClient::UpdateEnterpriseDomainInfo() {
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
const std::string enterprise_domain_manager =
connector->GetEnterpriseDomainManager();
const bool active_directory_managed = connector->IsActiveDirectoryManaged();
if (enterprise_domain_manager == last_enterprise_domain_manager_ &&
active_directory_managed == last_active_directory_managed_) {
return;
}
// Send to ash, which will add an item to the system tray.
system_tray_->SetEnterpriseDomainInfo(enterprise_domain_manager,
active_directory_managed);
last_enterprise_domain_manager_ = enterprise_domain_manager;
last_active_directory_managed_ = active_directory_managed;
}