Skip to content

Commit

Permalink
[CrOS Hotspot] Refactor out hotspot capabilities to its own class
Browse files Browse the repository at this point in the history
This CL refactors out hotspot capabilities related logic into a new
class: HotspotCapabilitiesProvider. The motivation for doing that is
to avoid circular dependency that will be introduced if the
HotspotStateHandler also needs to own an instance of HotspotController.

Bug: b/239477916
Change-Id: I175216f19c1842b300f926d3767bbbb26d1b186a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4163737
Reviewed-by: Azeem Arshad <azeemarshad@chromium.org>
Commit-Queue: Jason Zhang <jiajunz@google.com>
Reviewed-by: Regan Hsu <hsuregan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1096970}
  • Loading branch information
Jason Zhang authored and Chromium LUCI CQ committed Jan 25, 2023
1 parent a178ab4 commit f206bcb
Show file tree
Hide file tree
Showing 17 changed files with 778 additions and 529 deletions.
3 changes: 3 additions & 0 deletions chromeos/ash/components/network/BUILD.gn
Expand Up @@ -99,6 +99,8 @@ component("network") {
"hidden_network_handler.h",
"hotspot_allowed_flag_handler.cc",
"hotspot_allowed_flag_handler.h",
"hotspot_capabilities_provider.cc",
"hotspot_capabilities_provider.h",
"hotspot_controller.cc",
"hotspot_controller.h",
"hotspot_state_handler.cc",
Expand Down Expand Up @@ -303,6 +305,7 @@ source_set("unit_tests") {
"geolocation_handler_unittest.cc",
"hidden_network_handler_unittest.cc",
"hotspot_allowed_flag_handler_unittest.cc",
"hotspot_capabilities_provider_unittest.cc",
"hotspot_controller_unittest.cc",
"hotspot_state_handler_unittest.cc",
"managed_cellular_pref_handler_unittest.cc",
Expand Down
283 changes: 283 additions & 0 deletions chromeos/ash/components/network/hotspot_capabilities_provider.cc
@@ -0,0 +1,283 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/components/network/hotspot_capabilities_provider.h"

#include "base/containers/contains.h"
#include "chromeos/ash/components/dbus/shill/shill_manager_client.h"
#include "chromeos/ash/components/network/hotspot_util.h"
#include "chromeos/ash/components/network/network_event_log.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"

namespace ash {

namespace {

// Convert the base::Value::List type of |allowed_security_modes_in_shill| to
// the corresponding mojom enum and update the value to the
// |allowed_security_modes|.
void UpdateAllowedSecurityList(
std::vector<hotspot_config::mojom::WiFiSecurityMode>&
allowed_security_modes,
const base::Value::List& allowed_security_modes_in_shill) {
allowed_security_modes.clear();
for (const base::Value& allowed_security : allowed_security_modes_in_shill) {
allowed_security_modes.push_back(
ShillSecurityToMojom(allowed_security.GetString()));
}
}

bool IsDisallowedByPlatformCapabilities(
hotspot_config::mojom::HotspotAllowStatus allow_status) {
using HotspotAllowStatus = hotspot_config::mojom::HotspotAllowStatus;
return allow_status == HotspotAllowStatus::kDisallowedNoCellularUpstream ||
allow_status == HotspotAllowStatus::kDisallowedNoWiFiDownstream ||
allow_status == HotspotAllowStatus::kDisallowedNoWiFiSecurityModes;
}

} // namespace

HotspotCapabilitiesProvider::HotspotCapabilities::HotspotCapabilities(
const hotspot_config::mojom::HotspotAllowStatus allow_status)
: allow_status(allow_status) {}

HotspotCapabilitiesProvider::HotspotCapabilities::~HotspotCapabilities() =
default;

HotspotCapabilitiesProvider::HotspotCapabilitiesProvider() = default;

HotspotCapabilitiesProvider::~HotspotCapabilitiesProvider() {
ResetNetworkStateHandler();

if (ShillManagerClient::Get()) {
ShillManagerClient::Get()->RemovePropertyChangedObserver(this);
}
}

void HotspotCapabilitiesProvider::Init(
NetworkStateHandler* network_state_handler) {
network_state_handler_ = network_state_handler;
network_state_handler_observer_.Observe(network_state_handler_);

// Add as an observer here so that new hotspot state updated after this call
// are recognized.
ShillManagerClient::Get()->AddPropertyChangedObserver(this);
ShillManagerClient::Get()->GetProperties(
base::BindOnce(&HotspotCapabilitiesProvider::OnManagerProperties,
weak_ptr_factory_.GetWeakPtr()));
}

void HotspotCapabilitiesProvider::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}

void HotspotCapabilitiesProvider::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}

bool HotspotCapabilitiesProvider::HasObserver(Observer* observer) const {
return observer_list_.HasObserver(observer);
}

const HotspotCapabilitiesProvider::HotspotCapabilities&
HotspotCapabilitiesProvider::GetHotspotCapabilities() const {
return hotspot_capabilities_;
}

void HotspotCapabilitiesProvider::OnPropertyChanged(const std::string& key,
const base::Value& value) {
if (key == shill::kTetheringCapabilitiesProperty) {
UpdateHotspotCapabilities(value.GetDict());
}
}

// The hotspot capabilities is re-calculated when a cellular network connection
// state is changed.
void HotspotCapabilitiesProvider::NetworkConnectionStateChanged(
const NetworkState* network) {
using HotspotAllowStatus = hotspot_config::mojom::HotspotAllowStatus;
// Only check the Cellular connectivity as the upstream technology
if (!network->Matches(NetworkTypePattern::Cellular())) {
return;
}

// Exit early if the platform capabilities doesn't support hotspot.
if (IsDisallowedByPlatformCapabilities(hotspot_capabilities_.allow_status)) {
return;
}

if (!network->IsConnectingOrConnected()) {
// The cellular network got disconnected.
SetHotspotAllowStatus(HotspotAllowStatus::kDisallowedNoMobileData);
return;
}

if (network->IsConnectedState()) {
ShillManagerClient::Get()->CheckTetheringReadiness(
base::BindOnce(&HotspotCapabilitiesProvider::OnCheckReadinessSuccess,
weak_ptr_factory_.GetWeakPtr(), base::DoNothing()),
base::BindOnce(&HotspotCapabilitiesProvider::OnCheckReadinessFailure,
weak_ptr_factory_.GetWeakPtr(), base::DoNothing()));
}
}

void HotspotCapabilitiesProvider::OnShuttingDown() {
ResetNetworkStateHandler();
}

void HotspotCapabilitiesProvider::ResetNetworkStateHandler() {
if (!network_state_handler_) {
return;
}
network_state_handler_observer_.Reset();
network_state_handler_ = nullptr;
}

void HotspotCapabilitiesProvider::OnManagerProperties(
absl::optional<base::Value> properties) {
if (!properties) {
NET_LOG(ERROR)
<< "HotspotCapabilitiesProvider: Failed to get manager properties.";
return;
}

const base::Value::Dict* capabilities =
properties->GetDict().FindDict(shill::kTetheringCapabilitiesProperty);
if (!capabilities) {
NET_LOG(EVENT) << "HotspotCapabilitiesProvider: No dict value for: "
<< shill::kTetheringCapabilitiesProperty;
} else {
UpdateHotspotCapabilities(*capabilities);
}
}

void HotspotCapabilitiesProvider::UpdateHotspotCapabilities(
const base::Value::Dict& capabilities) {
using HotspotAllowStatus = hotspot_config::mojom::HotspotAllowStatus;

const base::Value::List* upstream_technologies =
capabilities.FindList(shill::kTetheringCapUpstreamProperty);
if (!upstream_technologies) {
NET_LOG(ERROR) << "No list value for: "
<< shill::kTetheringCapUpstreamProperty << " in "
<< shill::kTetheringCapabilitiesProperty;
SetHotspotAllowStatus(HotspotAllowStatus::kDisallowedNoCellularUpstream);
return;
}

if (!base::Contains(*upstream_technologies, shill::kTypeCellular)) {
SetHotspotAllowStatus(HotspotAllowStatus::kDisallowedNoCellularUpstream);
return;
}

const base::Value::List* downstream_technologies =
capabilities.FindList(shill::kTetheringCapDownstreamProperty);
if (!downstream_technologies) {
NET_LOG(ERROR) << "No list value for: "
<< shill::kTetheringCapDownstreamProperty << " in "
<< shill::kTetheringCapabilitiesProperty;
SetHotspotAllowStatus(HotspotAllowStatus::kDisallowedNoWiFiDownstream);
return;
}

if (!base::Contains(*downstream_technologies, shill::kTypeWifi)) {
SetHotspotAllowStatus(HotspotAllowStatus::kDisallowedNoWiFiDownstream);
return;
}

// Update allowed security modes for WiFi downstream
const base::Value::List* allowed_security_modes_in_shill =
capabilities.FindList(shill::kTetheringCapSecurityProperty);
if (!allowed_security_modes_in_shill) {
NET_LOG(ERROR) << "No list value for: "
<< shill::kTetheringCapSecurityProperty << " in "
<< shill::kTetheringCapabilitiesProperty;
SetHotspotAllowStatus(HotspotAllowStatus::kDisallowedNoWiFiSecurityModes);
return;
}

UpdateAllowedSecurityList(hotspot_capabilities_.allowed_security_modes,
*allowed_security_modes_in_shill);
if (hotspot_capabilities_.allowed_security_modes.empty()) {
SetHotspotAllowStatus(HotspotAllowStatus::kDisallowedNoWiFiSecurityModes);
return;
}

// Check if there's a connected cellular network
const NetworkState* connected_cellular_network =
network_state_handler_->ConnectedNetworkByType(
NetworkTypePattern::Cellular());
if (!connected_cellular_network) {
SetHotspotAllowStatus(HotspotAllowStatus::kDisallowedNoMobileData);
return;
}

CheckTetheringReadiness(base::DoNothing());
}

void HotspotCapabilitiesProvider::CheckTetheringReadiness(
CheckTetheringReadinessCallback callback) {
auto callback_split = base::SplitOnceCallback(std::move(callback));
ShillManagerClient::Get()->CheckTetheringReadiness(
base::BindOnce(&HotspotCapabilitiesProvider::OnCheckReadinessSuccess,
weak_ptr_factory_.GetWeakPtr(),
std::move(callback_split.first)),
base::BindOnce(&HotspotCapabilitiesProvider::OnCheckReadinessFailure,
weak_ptr_factory_.GetWeakPtr(),
std::move(callback_split.second)));
}

void HotspotCapabilitiesProvider::OnCheckReadinessSuccess(
CheckTetheringReadinessCallback callback,
const std::string& result) {
using HotspotAllowStatus = hotspot_config::mojom::HotspotAllowStatus;

if (result == shill::kTetheringReadinessReady) {
SetHotspotAllowStatus(HotspotAllowStatus::kAllowed);
std::move(callback).Run(CheckTetheringReadinessResult::kReady);
return;
}
if (result == shill::kTetheringReadinessUpstreamNetworkNotAvailable) {
SetHotspotAllowStatus(HotspotAllowStatus::kDisallowedReadinessCheckFail);
std::move(callback).Run(
CheckTetheringReadinessResult::kUpstreamNetworkNotAvailable);
return;
}
if (result == shill::kTetheringReadinessNotAllowed) {
SetHotspotAllowStatus(HotspotAllowStatus::kDisallowedReadinessCheckFail);
std::move(callback).Run(CheckTetheringReadinessResult::kNotAllowed);
return;
}
NET_LOG(ERROR) << "Unexpected check tethering readiness result: " << result;
std::move(callback).Run(CheckTetheringReadinessResult::kNotAllowed);
}

void HotspotCapabilitiesProvider::OnCheckReadinessFailure(
CheckTetheringReadinessCallback callback,
const std::string& error_name,
const std::string& error_message) {
NET_LOG(ERROR) << "Check tethering readiness failed, error name: "
<< error_name << ", message: " << error_message;
SetHotspotAllowStatus(
hotspot_config::mojom::HotspotAllowStatus::kDisallowedReadinessCheckFail);
std::move(callback).Run(CheckTetheringReadinessResult::kShillOperationFailed);
}

void HotspotCapabilitiesProvider::SetHotspotAllowStatus(
hotspot_config::mojom::HotspotAllowStatus new_allow_status) {
if (hotspot_capabilities_.allow_status == new_allow_status) {
return;
}

hotspot_capabilities_.allow_status = new_allow_status;
NotifyHotspotCapabilitiesChanged();
}

void HotspotCapabilitiesProvider::NotifyHotspotCapabilitiesChanged() {
for (auto& observer : observer_list_) {
observer.OnHotspotCapabilitiesChanged();
}
}

} // namespace ash

0 comments on commit f206bcb

Please sign in to comment.