123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- // Copyright (c) 2012 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 "ash/system/network/network_state_list_detailed_view.h"
- #include <algorithm>
- #include "ash/public/cpp/system_tray_client.h"
- #include "ash/session/session_controller_impl.h"
- #include "ash/shell.h"
- #include "ash/strings/grit/ash_strings.h"
- #include "ash/system/machine_learning/user_settings_event_logger.h"
- #include "ash/system/model/system_tray_model.h"
- #include "ash/system/network/network_utils.h"
- #include "ash/system/network/tray_network_state_model.h"
- #include "ash/system/tray/system_menu_button.h"
- #include "ash/system/tray/tri_view.h"
- #include "base/bind.h"
- #include "base/metrics/user_metrics.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/time/time.h"
- #include "chromeos/ash/components/network/network_connect.h"
- #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
- #include "net/base/ip_address.h"
- #include "third_party/cros_system_api/dbus/service_constants.h"
- #include "ui/accessibility/ax_enums.mojom.h"
- #include "ui/base/l10n/l10n_util.h"
- #include "ui/views/bubble/bubble_dialog_delegate_view.h"
- #include "ui/views/controls/button/button.h"
- #include "ui/views/controls/label.h"
- #include "ui/views/layout/fill_layout.h"
- #include "ui/views/layout/layout_manager.h"
- #include "ui/views/widget/widget.h"
- namespace ash {
- namespace {
- using base::UserMetricsAction;
- using chromeos::network_config::mojom::ActivationStateType;
- using chromeos::network_config::mojom::ConnectionStateType;
- using chromeos::network_config::mojom::DeviceStateProperties;
- using chromeos::network_config::mojom::DeviceStateType;
- using chromeos::network_config::mojom::NetworkStateProperties;
- using chromeos::network_config::mojom::NetworkStatePropertiesPtr;
- using chromeos::network_config::mojom::NetworkType;
- // Delay between scan requests.
- constexpr int kRequestScanDelaySeconds = 10;
- // 00:00:00:00:00:00 is provided when a device MAC address cannot be retrieved.
- constexpr char kMissingMacAddress[] = "00:00:00:00:00:00";
- // This margin value is used throughout the bubble:
- // - margins inside the border
- // - horizontal spacing between bubble border and parent bubble border
- // - distance between top of this bubble's border and the bottom of the anchor
- // view (horizontal rule).
- constexpr int kBubbleMargin = 8;
- // Elevation used for the bubble shadow effect (tiny).
- constexpr int kBubbleShadowElevation = 2;
- bool IsSecondaryUser() {
- SessionControllerImpl* session_controller =
- Shell::Get()->session_controller();
- return session_controller->IsActiveUserSessionStarted() &&
- !session_controller->IsUserPrimary();
- }
- bool NetworkTypeIsConfigurable(NetworkType type) {
- switch (type) {
- case NetworkType::kVPN:
- case NetworkType::kWiFi:
- return true;
- case NetworkType::kAll:
- case NetworkType::kCellular:
- case NetworkType::kEthernet:
- case NetworkType::kMobile:
- case NetworkType::kTether:
- case NetworkType::kWireless:
- return false;
- }
- NOTREACHED();
- return false;
- }
- void LogUserNetworkEvent(const NetworkStateProperties& network) {
- auto* const logger = ml::UserSettingsEventLogger::Get();
- if (logger) {
- logger->LogNetworkUkmEvent(network);
- }
- }
- } // namespace
- bool CanNetworkConnect(
- chromeos::network_config::mojom::ConnectionStateType connection_state,
- chromeos::network_config::mojom::NetworkType type,
- chromeos::network_config::mojom::ActivationStateType activation_state,
- bool connectable,
- std::string sim_eid) {
- // Network can be connected to if the network is not connected and:
- // * The network is connectable or
- // * The active user is primary and the network is configurable or
- // * The network is cellular and activated
- if (connection_state != ConnectionStateType::kNotConnected) {
- return false;
- }
- // Network cannot be connected to if it is an unactivated eSIM network.
- if (type == NetworkType::kCellular &&
- activation_state == ActivationStateType::kNotActivated &&
- !sim_eid.empty()) {
- return false;
- }
- if (connectable) {
- return true;
- }
- if (!IsSecondaryUser() && NetworkTypeIsConfigurable(type)) {
- return true;
- }
- if (type == NetworkType::kCellular &&
- activation_state == ActivationStateType::kActivated) {
- return true;
- }
- return false;
- }
- // A bubble which displays network info.
- class NetworkStateListDetailedView::InfoBubble
- : public views::BubbleDialogDelegateView {
- public:
- InfoBubble(views::View* anchor,
- views::View* content,
- NetworkStateListDetailedView* detailed_view)
- : views::BubbleDialogDelegateView(anchor, views::BubbleBorder::TOP_RIGHT),
- detailed_view_(detailed_view) {
- SetButtons(ui::DIALOG_BUTTON_NONE);
- set_margins(gfx::Insets(kBubbleMargin));
- SetArrow(views::BubbleBorder::NONE);
- set_shadow(views::BubbleBorder::NO_SHADOW);
- SetNotifyEnterExitOnChild(true);
- SetLayoutManager(std::make_unique<views::FillLayout>());
- AddChildView(content);
- }
- InfoBubble(const InfoBubble&) = delete;
- InfoBubble& operator=(const InfoBubble&) = delete;
- ~InfoBubble() override {
- // The detailed view can be destructed before info bubble is destructed.
- // Call OnInfoBubbleDestroyed only if the detailed view is live.
- if (detailed_view_)
- detailed_view_->OnInfoBubbleDestroyed();
- }
- void OnNetworkStateListDetailedViewIsDeleting() { detailed_view_ = nullptr; }
- private:
- // View:
- gfx::Size CalculatePreferredSize() const override {
- // This bubble should be inset by kBubbleMargin on both left and right
- // relative to the parent bubble.
- const gfx::Size anchor_size = GetAnchorView()->size();
- int contents_width =
- anchor_size.width() - 2 * kBubbleMargin - margins().width();
- return gfx::Size(contents_width, GetHeightForWidth(contents_width));
- }
- void OnMouseExited(const ui::MouseEvent& event) override {
- // Like the user switching bubble/menu, hide the bubble when the mouse
- // exits.
- if (detailed_view_)
- detailed_view_->ResetInfoBubble();
- }
- void OnBeforeBubbleWidgetInit(views::Widget::InitParams* params,
- views::Widget* widget) const override {
- params->shadow_type = views::Widget::InitParams::ShadowType::kDrop;
- params->shadow_elevation = kBubbleShadowElevation;
- params->name = "NetworkStateListDetailedView::InfoBubble";
- }
- // Not owned.
- NetworkStateListDetailedView* detailed_view_;
- };
- //------------------------------------------------------------------------------
- // NetworkStateListDetailedView
- NetworkStateListDetailedView::NetworkStateListDetailedView(
- DetailedViewDelegate* delegate,
- ListType list_type,
- LoginStatus login)
- : TrayDetailedView(delegate),
- list_type_(list_type),
- login_(login),
- model_(Shell::Get()->system_tray_model()->network_state_model()),
- info_button_(nullptr),
- settings_button_(nullptr),
- info_bubble_(nullptr) {
- RecordDetailedViewSection(DetailedViewSection::kDetailedSection);
- OverrideProgressBarAccessibleName(l10n_util::GetStringUTF16(
- IDS_ASH_STATUS_TRAY_NETWORK_PROGRESS_ACCESSIBLE_NAME));
- }
- NetworkStateListDetailedView::~NetworkStateListDetailedView() {
- model_->RemoveObserver(this);
- if (info_bubble_)
- info_bubble_->OnNetworkStateListDetailedViewIsDeleting();
- ResetInfoBubble();
- }
- void NetworkStateListDetailedView::ToggleInfoBubbleForTesting() {
- ToggleInfoBubble();
- }
- const char* NetworkStateListDetailedView::GetClassName() const {
- return "NetworkStateListDetailedView";
- }
- void NetworkStateListDetailedView::Init() {
- CreateScrollableList();
- CreateTitleRow(list_type_ == ListType::LIST_TYPE_NETWORK
- ? IDS_ASH_STATUS_TRAY_NETWORK
- : IDS_ASH_STATUS_TRAY_VPN);
- model_->AddObserver(this);
- Update();
- if (list_type_ == LIST_TYPE_NETWORK && IsWifiEnabled())
- ScanAndStartTimer();
- }
- void NetworkStateListDetailedView::Update() {
- UpdateNetworkList();
- UpdateHeaderButtons();
- UpdateScanningBar();
- Layout();
- }
- void NetworkStateListDetailedView::ActiveNetworkStateChanged() {
- Update();
- }
- void NetworkStateListDetailedView::NetworkListChanged() {
- Update();
- }
- void NetworkStateListDetailedView::HandleViewClicked(views::View* view) {
- if (login_ == LoginStatus::LOCKED)
- return;
- std::string guid;
- if (!IsNetworkEntry(view, &guid))
- return;
- model_->cros_network_config()->GetNetworkState(
- guid, base::BindOnce(&NetworkStateListDetailedView::HandleViewClickedImpl,
- weak_ptr_factory_.GetWeakPtr()));
- }
- void NetworkStateListDetailedView::HandleViewClickedImpl(
- NetworkStatePropertiesPtr network) {
- if (network) {
- // If the network is locked and is cellular show SIM unlock dialog in OS
- // Settings.
- if (network->type == NetworkType::kCellular &&
- network->type_state->get_cellular()->sim_locked) {
- if (!Shell::Get()->session_controller()->ShouldEnableSettings()) {
- return;
- }
- RecordNetworkRowClickedAction(
- NetworkRowClickedAction::kOpenSimUnlockDialog);
- Shell::Get()->system_tray_model()->client()->ShowSettingsSimUnlock();
- return;
- }
- if (CanNetworkConnect(
- network->connection_state, network->type,
- network->type == NetworkType::kCellular
- ? network->type_state->get_cellular()->activation_state
- : ActivationStateType::kUnknown,
- network->connectable,
- network->type == NetworkType::kCellular
- ? network->type_state->get_cellular()->eid
- : "")) {
- base::RecordAction(
- list_type_ == LIST_TYPE_VPN
- ? UserMetricsAction("StatusArea_VPN_ConnectToNetwork")
- : UserMetricsAction("StatusArea_Network_ConnectConfigured"));
- LogUserNetworkEvent(*network.get());
- if (list_type_ == LIST_TYPE_NETWORK) {
- RecordNetworkRowClickedAction(
- NetworkRowClickedAction::kConnectToNetwork);
- }
- NetworkConnect::Get()->ConnectToNetworkId(network->guid);
- return;
- }
- }
- // If the network is no longer available or not connectable or configurable,
- // show the Settings UI.
- base::RecordAction(
- list_type_ == LIST_TYPE_VPN
- ? UserMetricsAction("StatusArea_VPN_ConnectionDetails")
- : UserMetricsAction("StatusArea_Network_ConnectionDetails"));
- if (list_type_ == LIST_TYPE_NETWORK) {
- RecordNetworkRowClickedAction(
- NetworkRowClickedAction::kOpenNetworkSettingsPage);
- }
- Shell::Get()->system_tray_model()->client()->ShowNetworkSettings(
- network ? network->guid : std::string());
- }
- void NetworkStateListDetailedView::CreateExtraTitleRowButtons() {
- if (login_ == LoginStatus::LOCKED)
- return;
- DCHECK(!info_button_);
- tri_view()->SetContainerVisible(TriView::Container::END, true);
- info_button_ = CreateInfoButton(
- base::BindRepeating(&NetworkStateListDetailedView::ToggleInfoBubble,
- base::Unretained(this)),
- IDS_ASH_STATUS_TRAY_NETWORK_INFO);
- tri_view()->AddView(TriView::Container::END, info_button_);
- DCHECK(!settings_button_);
- settings_button_ = CreateSettingsButton(
- base::BindRepeating(&NetworkStateListDetailedView::ShowSettings,
- base::Unretained(this)),
- IDS_ASH_STATUS_TRAY_NETWORK_SETTINGS);
- tri_view()->AddView(TriView::Container::END, settings_button_);
- }
- void NetworkStateListDetailedView::ShowSettings() {
- base::RecordAction(list_type_ == LIST_TYPE_VPN
- ? UserMetricsAction("StatusArea_VPN_Settings")
- : UserMetricsAction("StatusArea_Network_Settings"));
- const std::string guid = model_->default_network()
- ? model_->default_network()->guid
- : std::string();
- base::RecordAction(base::UserMetricsAction(
- "ChromeOS.SystemTray.Network.SettingsButtonPressed"));
- // Showing network settings window may close the bubble (and destroy this
- // view). Explicitly request bubble closure here, before showing network
- // settings.
- CloseBubble(); // Deletes |this|.
- SystemTrayClient* system_tray_client =
- Shell::Get()->system_tray_model()->client();
- if (system_tray_client)
- system_tray_client->ShowNetworkSettings(guid);
- }
- void NetworkStateListDetailedView::UpdateHeaderButtons() {
- if (settings_button_) {
- if (login_ == LoginStatus::NOT_LOGGED_IN) {
- // When not logged in, only enable the settings button if there is a
- // default (i.e. connected or connecting) network to show settings for.
- settings_button_->SetEnabled(model_->default_network());
- } else {
- // Otherwise, enable if showing settings is allowed. There are situations
- // (supervised user creation flow) when the session is started but UI flow
- // continues within login UI, i.e., no browser window is yet available.
- settings_button_->SetEnabled(
- Shell::Get()->session_controller()->ShouldEnableSettings());
- }
- }
- }
- void NetworkStateListDetailedView::UpdateScanningBar() {
- if (list_type_ != LIST_TYPE_NETWORK)
- return;
- bool is_wifi_enabled = IsWifiEnabled();
- if (is_wifi_enabled && !network_scan_repeating_timer_.IsRunning())
- ScanAndStartTimer();
- if (!is_wifi_enabled && network_scan_repeating_timer_.IsRunning())
- network_scan_repeating_timer_.Stop();
- bool scanning_bar_visible = false;
- if (is_wifi_enabled) {
- const DeviceStateProperties* wifi = model_->GetDevice(NetworkType::kWiFi);
- const DeviceStateProperties* tether =
- model_->GetDevice(NetworkType::kTether);
- scanning_bar_visible =
- (wifi && wifi->scanning) || (tether && tether->scanning);
- }
- ShowProgress(-1, scanning_bar_visible);
- }
- void NetworkStateListDetailedView::ToggleInfoBubble() {
- if (ResetInfoBubble())
- return;
- info_bubble_ = new InfoBubble(tri_view(), CreateNetworkInfoView(), this);
- views::BubbleDialogDelegateView::CreateBubble(info_bubble_)->Show();
- info_bubble_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert, false);
- }
- bool NetworkStateListDetailedView::ResetInfoBubble() {
- if (!info_bubble_)
- return false;
- info_bubble_->GetWidget()->Close();
- return true;
- }
- void NetworkStateListDetailedView::OnInfoBubbleDestroyed() {
- info_bubble_ = nullptr;
- // Widget of info bubble is activated while info bubble is shown. To move
- // focus back to the widget of this view, activate it again here.
- GetWidget()->Activate();
- }
- views::View* NetworkStateListDetailedView::CreateNetworkInfoView() {
- std::string ipv4_address, ipv6_address;
- const NetworkStateProperties* network = model_->default_network();
- const DeviceStateProperties* device =
- network ? model_->GetDevice(network->type) : nullptr;
- if (device) {
- if (device->ipv4_address)
- ipv4_address = device->ipv4_address->ToString();
- if (device->ipv6_address)
- ipv6_address = device->ipv6_address->ToString();
- }
- std::string ethernet_address, wifi_address, cellular_address;
- if (list_type_ == LIST_TYPE_NETWORK) {
- const DeviceStateProperties* ethernet =
- model_->GetDevice(NetworkType::kEthernet);
- if (ethernet && ethernet->mac_address)
- ethernet_address = *ethernet->mac_address;
- const DeviceStateProperties* wifi = model_->GetDevice(NetworkType::kWiFi);
- if (wifi && wifi->mac_address)
- wifi_address = *wifi->mac_address;
- const DeviceStateProperties* cellular =
- model_->GetDevice(NetworkType::kCellular);
- if (cellular && cellular->mac_address)
- cellular_address = *cellular->mac_address;
- }
- std::u16string bubble_text;
- auto maybe_add_mac_address = [&bubble_text](const std::string& address,
- int ids) {
- if (address.empty() || address == kMissingMacAddress)
- return;
- if (!bubble_text.empty())
- bubble_text += u"\n";
- bubble_text += l10n_util::GetStringFUTF16(ids, base::UTF8ToUTF16(address));
- };
- maybe_add_mac_address(ipv4_address, IDS_ASH_STATUS_TRAY_IP_ADDRESS);
- maybe_add_mac_address(ipv6_address, IDS_ASH_STATUS_TRAY_IPV6_ADDRESS);
- maybe_add_mac_address(ethernet_address, IDS_ASH_STATUS_TRAY_ETHERNET_ADDRESS);
- maybe_add_mac_address(wifi_address, IDS_ASH_STATUS_TRAY_WIFI_ADDRESS);
- maybe_add_mac_address(cellular_address, IDS_ASH_STATUS_TRAY_CELLULAR_ADDRESS);
- // Avoid an empty bubble in the unlikely event that there is no network
- // information at all.
- if (bubble_text.empty())
- bubble_text = l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NO_NETWORKS);
- auto* label = new views::Label(bubble_text);
- label->SetMultiLine(true);
- label->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD);
- label->SetSelectable(true);
- return label;
- }
- void NetworkStateListDetailedView::ScanAndStartTimer() {
- CallRequestScan();
- network_scan_repeating_timer_.Start(
- FROM_HERE, base::Seconds(kRequestScanDelaySeconds), this,
- &NetworkStateListDetailedView::CallRequestScan);
- }
- void NetworkStateListDetailedView::CallRequestScan() {
- if (!IsWifiEnabled())
- return;
- VLOG(1) << "Requesting Network Scan.";
- model_->cros_network_config()->RequestNetworkScan(NetworkType::kWiFi);
- model_->cros_network_config()->RequestNetworkScan(NetworkType::kTether);
- }
- bool NetworkStateListDetailedView::IsWifiEnabled() {
- return model_->GetDeviceState(NetworkType::kWiFi) ==
- DeviceStateType::kEnabled;
- }
- } // namespace ash
|