| // Copyright 2022 The Chromium Authors |
| // 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_detailed_view_controller.h" |
| |
| #include <memory> |
| |
| #include "ash/constants/ash_features.h" |
| #include "ash/public/cpp/bluetooth_config_service.h" |
| #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/model/system_tray_model.h" |
| #include "ash/system/network/network_detailed_network_view.h" |
| #include "ash/system/network/network_list_view_controller.h" |
| #include "ash/system/network/network_utils.h" |
| #include "ash/system/network/tray_network_state_model.h" |
| #include "ash/system/tray/detailed_view_delegate.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/metrics/user_metrics.h" |
| #include "chromeos/ash/components/network/network_connect.h" |
| #include "chromeos/services/network_config/public/cpp/cros_network_config_util.h" |
| #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/views/view.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| using ::base::UserMetricsAction; |
| using bluetooth_config::mojom::BluetoothSystemPropertiesPtr; |
| using bluetooth_config::mojom::BluetoothSystemState; |
| using ::chromeos::network_config::NetworkTypeMatchesType; |
| using ::chromeos::network_config::mojom::ActivationStateType; |
| using ::chromeos::network_config::mojom::CellularStateProperties; |
| using ::chromeos::network_config::mojom::ConnectionStateType; |
| using ::chromeos::network_config::mojom::DeviceStateType; |
| using ::chromeos::network_config::mojom::NetworkStateProperties; |
| using ::chromeos::network_config::mojom::NetworkStatePropertiesPtr; |
| using ::chromeos::network_config::mojom::NetworkType; |
| using ::chromeos::network_config::mojom::PortalState; |
| |
| 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(); |
| } |
| |
| bool IsNetworkBehindPortalOrProxy(PortalState portalState) { |
| return portalState == PortalState::kPortal || |
| portalState == PortalState::kPortalSuspected; |
| } |
| |
| bool IsNetworkConnectable(const NetworkStatePropertiesPtr& network_properties) { |
| // The network must not already be connected to be able to be connected to. |
| if (network_properties->connection_state != |
| ConnectionStateType::kNotConnected) { |
| return false; |
| } |
| |
| if (NetworkTypeMatchesType(network_properties->type, |
| NetworkType::kCellular)) { |
| // Cellular networks must be activated, uninhibited, and have an unlocked |
| // SIM to be able to be connected to. |
| const CellularStateProperties* cellular = |
| network_properties->type_state->get_cellular().get(); |
| |
| if (cellular->activation_state == ActivationStateType::kNotActivated && |
| !cellular->eid.empty()) { |
| return false; |
| } |
| |
| if (cellular->activation_state == ActivationStateType::kActivated) { |
| return true; |
| } |
| } |
| |
| // The network can be connected to if the network is connectable. |
| if (network_properties->connectable) { |
| return true; |
| } |
| |
| // Network can be connected to if the active user is the primary user and the |
| // network is configurable. |
| if (!IsSecondaryUser() && |
| NetworkTypeIsConfigurable(network_properties->type)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| } // namespace |
| |
| NetworkDetailedViewController::NetworkDetailedViewController( |
| UnifiedSystemTrayController* tray_controller) |
| : model_(Shell::Get()->system_tray_model()->network_state_model()), |
| detailed_view_delegate_( |
| std::make_unique<DetailedViewDelegate>(tray_controller)) { |
| GetBluetoothConfigService( |
| remote_cros_bluetooth_config_.BindNewPipeAndPassReceiver()); |
| remote_cros_bluetooth_config_->ObserveSystemProperties( |
| cros_system_properties_observer_receiver_.BindNewPipeAndPassRemote()); |
| } |
| |
| NetworkDetailedViewController::~NetworkDetailedViewController() = default; |
| |
| std::unique_ptr<views::View> NetworkDetailedViewController::CreateView() { |
| DCHECK(!network_detailed_view_); |
| std::unique_ptr<NetworkDetailedNetworkView> view = |
| NetworkDetailedNetworkView::Factory::Create(detailed_view_delegate_.get(), |
| /*delegate=*/this); |
| network_detailed_view_ = view.get(); |
| network_list_view_controller_ = |
| NetworkListViewController::Factory::Create(view.get()); |
| |
| // `view` is not a views::View, so we must GetAsView(). |
| return base::WrapUnique(view.release()->GetAsView()); |
| } |
| |
| std::u16string NetworkDetailedViewController::GetAccessibleName() const { |
| return l10n_util::GetStringUTF16( |
| IDS_ASH_QUICK_SETTINGS_BUBBLE_NETWORK_SETTINGS_ACCESSIBLE_DESCRIPTION); |
| } |
| |
| void NetworkDetailedViewController::OnNetworkListItemSelected( |
| const NetworkStatePropertiesPtr& network) { |
| if (Shell::Get()->session_controller()->login_status() == |
| LoginStatus::LOCKED) { |
| return; |
| } |
| |
| 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; |
| } |
| // It is not possible to unlock the carrier locked device by entering the |
| // pin on UI as unlock flow is triggered by simLock server |
| if (network->type_state->get_cellular()->sim_lock_type == "network-pin") { |
| return; |
| } |
| Shell::Get()->system_tray_model()->client()->ShowSettingsSimUnlock(); |
| return; |
| } |
| |
| // If user is logged in, the network is connected, and the network is in a |
| // portal or proxy state, the user is shown the portal signin. We do not |
| // show portal sign in for user not logged in because it is the only way for |
| // the user to get to the network details page. |
| if (Shell::Get()->session_controller()->login_status() != |
| LoginStatus::NOT_LOGGED_IN && |
| chromeos::network_config::StateIsConnected(network->connection_state) && |
| IsNetworkBehindPortalOrProxy(network->portal_state)) { |
| NetworkConnect::Get()->ShowPortalSignin( |
| network->guid, NetworkConnect::Source::kQuickSettings); |
| return; |
| } |
| |
| if (IsNetworkConnectable(network)) { |
| base::RecordAction( |
| UserMetricsAction("StatusArea_Network_ConnectConfigured")); |
| NetworkConnect::Get()->ConnectToNetworkId(network->guid); |
| return; |
| } |
| } |
| |
| // If the network is no longer available or not connectable or configurable, |
| // show the Settings UI. |
| base::RecordAction(UserMetricsAction("StatusArea_Network_ConnectionDetails")); |
| Shell::Get()->system_tray_model()->client()->ShowNetworkSettings( |
| network ? network->guid : std::string()); |
| } |
| |
| void NetworkDetailedViewController::OnMobileToggleClicked(bool new_state) { |
| const DeviceStateType cellular_state = |
| model_->GetDeviceState(NetworkType::kCellular); |
| |
| // When Cellular is available, the toggle controls Cellular enabled state. |
| if (cellular_state != DeviceStateType::kUnavailable) { |
| model_->SetNetworkTypeEnabledState(NetworkType::kCellular, new_state); |
| return; |
| } |
| |
| if (features::IsInstantHotspotRebrandEnabled()) { |
| return; |
| } |
| |
| const DeviceStateType tether_state = |
| model_->GetDeviceState(NetworkType::kTether); |
| |
| DCHECK(tether_state != DeviceStateType::kUnavailable); |
| |
| // If Tether is available but uninitialized, we expect Bluetooth to be off. |
| // Enable Bluetooth so that Tether will be initialized. |
| if (tether_state == DeviceStateType::kUninitialized) { |
| if (new_state && |
| (bluetooth_system_state_ == BluetoothSystemState::kDisabled || |
| bluetooth_system_state_ == BluetoothSystemState::kDisabling)) { |
| remote_cros_bluetooth_config_->SetBluetoothEnabledState(true); |
| waiting_to_initialize_bluetooth_ = true; |
| } |
| return; |
| } |
| |
| // Otherwise the toggle controls the Tether enabled state. |
| model_->SetNetworkTypeEnabledState(NetworkType::kTether, new_state); |
| } |
| |
| void NetworkDetailedViewController::OnWifiToggleClicked(bool new_state) { |
| model_->SetNetworkTypeEnabledState(NetworkType::kWiFi, new_state); |
| } |
| |
| void NetworkDetailedViewController::OnPropertiesUpdated( |
| BluetoothSystemPropertiesPtr properties) { |
| bluetooth_system_state_ = properties->system_state; |
| |
| // We enabled Bluetooth so Tether is now initialized, but it was not |
| // enabled so enable it. |
| if (waiting_to_initialize_bluetooth_ && |
| bluetooth_system_state_ == BluetoothSystemState::kEnabled) { |
| waiting_to_initialize_bluetooth_ = false; |
| model_->SetNetworkTypeEnabledState(NetworkType::kTether, |
| /*enabled=*/true); |
| } |
| } |
| |
| void NetworkDetailedViewController::ShutDown() { |
| network_list_view_controller_.reset(); |
| } |
| |
| } // namespace ash |