blob: f24a7271277cb9cd5a98216c6bfcff82498af1be [file] [log] [blame]
// Copyright 2019 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 "chromeos/components/sync_wifi/local_network_collector_impl.h"
#include "base/guid.h"
#include "chromeos/components/sync_wifi/network_eligibility_checker.h"
#include "chromeos/components/sync_wifi/network_identifier.h"
#include "chromeos/components/sync_wifi/network_type_conversions.h"
#include "chromeos/dbus/shill/shill_service_client.h"
#include "chromeos/network/network_event_log.h"
#include "chromeos/network/network_handler.h"
#include "chromeos/network/network_metadata_store.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/services/network_config/public/mojom/network_types.mojom-shared.h"
#include "components/device_event_log/device_event_log.h"
#include "components/sync/protocol/wifi_configuration_specifics.pb.h"
#include "dbus/object_path.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
namespace chromeos {
namespace sync_wifi {
namespace {
dbus::ObjectPath GetServicePathForGuid(const std::string& guid) {
const NetworkState* state =
NetworkHandler::Get()->network_state_handler()->GetNetworkStateFromGuid(
guid);
if (!state) {
return dbus::ObjectPath();
}
return dbus::ObjectPath(state->path());
}
} // namespace
LocalNetworkCollectorImpl::LocalNetworkCollectorImpl(
network_config::mojom::CrosNetworkConfig* cros_network_config,
SyncedNetworkMetricsLogger* metrics_recorder)
: cros_network_config_(cros_network_config),
metrics_recorder_(metrics_recorder) {
cros_network_config_->AddObserver(
cros_network_config_observer_receiver_.BindNewPipeAndPassRemote());
// Load the current list of networks.
OnNetworkStateListChanged();
}
LocalNetworkCollectorImpl::~LocalNetworkCollectorImpl() = default;
void LocalNetworkCollectorImpl::GetAllSyncableNetworks(
ProtoListCallback callback) {
if (!is_mojo_networks_loaded_) {
after_networks_are_loaded_callback_queue_.push(
base::BindOnce(&LocalNetworkCollectorImpl::GetAllSyncableNetworks,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
return;
}
std::string request_guid = InitializeRequest();
request_guid_to_list_callback_[request_guid] = std::move(callback);
int count = 0;
for (const network_config::mojom::NetworkStatePropertiesPtr& network :
mojo_networks_) {
if (!IsEligible(network)) {
continue;
}
request_guid_to_in_flight_networks_[request_guid].insert(
NetworkIdentifier::FromMojoNetwork(network));
StartGetNetworkDetails(network.get(), request_guid);
count++;
}
if (!count) {
OnRequestFinished(request_guid);
}
}
void LocalNetworkCollectorImpl::RecordZeroNetworksEligibleForSync() {
if (has_logged_zero_eligible_networks_metric_) {
return;
}
if (!is_mojo_networks_loaded_) {
after_networks_are_loaded_callback_queue_.push(base::BindOnce(
&LocalNetworkCollectorImpl::RecordZeroNetworksEligibleForSync,
weak_ptr_factory_.GetWeakPtr()));
return;
}
base::flat_set<NetworkEligibilityStatus>
network_eligible_for_sync_status_codes;
NetworkEligibilityStatus network_eligible_for_sync_status;
for (const network_config::mojom::NetworkStatePropertiesPtr& network :
mojo_networks_) {
if (!network ||
network->type != network_config::mojom::NetworkType::kWiFi) {
continue;
}
network_eligible_for_sync_status = GetNetworkEligibilityStatus(
network->guid, network->connectable,
network->type_state->get_wifi()->security, network->source,
/*log_result=*/false);
network_eligible_for_sync_status_codes.insert(
network_eligible_for_sync_status);
}
metrics_recorder_->RecordZeroNetworksEligibleForSync(
network_eligible_for_sync_status_codes);
has_logged_zero_eligible_networks_metric_ = true;
}
void LocalNetworkCollectorImpl::GetSyncableNetwork(const std::string& guid,
ProtoCallback callback) {
const network_config::mojom::NetworkStateProperties* network = nullptr;
for (const network_config::mojom::NetworkStatePropertiesPtr& n :
mojo_networks_) {
if (n->guid == guid) {
if (IsEligible(n)) {
network = n.get();
}
break;
}
}
if (!network) {
std::move(callback).Run(base::nullopt);
return;
}
std::string request_guid = InitializeRequest();
request_guid_to_single_callback_[request_guid] = std::move(callback);
StartGetNetworkDetails(network, request_guid);
}
base::Optional<NetworkIdentifier>
LocalNetworkCollectorImpl::GetNetworkIdentifierFromGuid(
const std::string& guid) {
for (const network_config::mojom::NetworkStatePropertiesPtr& network :
mojo_networks_) {
if (network->guid == guid) {
return NetworkIdentifier::FromMojoNetwork(network);
}
}
return base::nullopt;
}
void LocalNetworkCollectorImpl::SetNetworkMetadataStore(
base::WeakPtr<NetworkMetadataStore> network_metadata_store) {
network_metadata_store_ = network_metadata_store;
}
std::string LocalNetworkCollectorImpl::InitializeRequest() {
std::string request_guid = base::GenerateGUID();
request_guid_to_complete_protos_[request_guid] =
std::vector<sync_pb::WifiConfigurationSpecifics>();
request_guid_to_in_flight_networks_[request_guid] =
base::flat_set<NetworkIdentifier>();
return request_guid;
}
bool LocalNetworkCollectorImpl::IsEligible(
const network_config::mojom::NetworkStatePropertiesPtr& network) {
if (!network || network->type != network_config::mojom::NetworkType::kWiFi) {
return false;
}
const network_config::mojom::WiFiStatePropertiesPtr& wifi_properties =
network->type_state->get_wifi();
return IsEligibleForSync(network->guid, network->connectable,
wifi_properties->security, network->source,
/*log_result=*/true);
}
void LocalNetworkCollectorImpl::StartGetNetworkDetails(
const network_config::mojom::NetworkStateProperties* network,
const std::string& request_guid) {
sync_pb::WifiConfigurationSpecifics proto;
proto.set_hex_ssid(network->type_state->get_wifi()->hex_ssid);
proto.set_security_type(
SecurityTypeProtoFromMojo(network->type_state->get_wifi()->security));
base::TimeDelta timestamp =
network_metadata_store_->GetLastConnectedTimestamp(network->guid);
proto.set_last_connected_timestamp(timestamp.InMilliseconds());
cros_network_config_->GetManagedProperties(
network->guid,
base::BindOnce(&LocalNetworkCollectorImpl::OnGetManagedPropertiesResult,
weak_ptr_factory_.GetWeakPtr(), proto, request_guid));
}
void LocalNetworkCollectorImpl::OnGetManagedPropertiesResult(
sync_pb::WifiConfigurationSpecifics proto,
const std::string& request_guid,
network_config::mojom::ManagedPropertiesPtr properties) {
if (!properties) {
NET_LOG(ERROR) << "GetManagedProperties failed.";
OnNetworkFinished(NetworkIdentifier::FromProto(proto), request_guid);
return;
}
proto.set_automatically_connect(AutomaticallyConnectProtoFromMojo(
properties->type_properties->get_wifi()->auto_connect));
proto.set_is_preferred(IsPreferredProtoFromMojo(properties->priority));
if (properties->metered) {
proto.set_metered(
properties->metered->active_value
? sync_pb::
WifiConfigurationSpecifics_MeteredOption_METERED_OPTION_YES
: sync_pb::
WifiConfigurationSpecifics_MeteredOption_METERED_OPTION_NO);
}
bool is_proxy_modified =
network_metadata_store_->GetIsFieldExternallyModified(
properties->guid, shill::kProxyConfigProperty);
sync_pb::WifiConfigurationSpecifics_ProxyConfiguration proxy_config =
ProxyConfigurationProtoFromMojo(properties->proxy_settings,
/*is_unspecified=*/is_proxy_modified);
proto.mutable_proxy_configuration()->CopyFrom(proxy_config);
bool is_dns_externally_modified =
network_metadata_store_->GetIsFieldExternallyModified(
properties->guid, shill::kNameServersProperty);
if (properties->static_ip_config &&
properties->static_ip_config->name_servers &&
(properties->source == network_config::mojom::OncSource::kUser ||
!is_dns_externally_modified)) {
proto.set_dns_option(
sync_pb::WifiConfigurationSpecifics_DnsOption_DNS_OPTION_CUSTOM);
for (const std::string& nameserver :
properties->static_ip_config->name_servers->active_value) {
proto.add_custom_dns(nameserver);
}
} else if (properties->source == network_config::mojom::OncSource::kDevice &&
is_dns_externally_modified) {
proto.set_dns_option(
sync_pb::WifiConfigurationSpecifics_DnsOption_DNS_OPTION_UNSPECIFIED);
} else {
proto.set_dns_option(
sync_pb::WifiConfigurationSpecifics_DnsOption_DNS_OPTION_DEFAULT_DHCP);
}
ShillServiceClient::Get()->GetWiFiPassphrase(
GetServicePathForGuid(properties->guid),
base::BindOnce(&LocalNetworkCollectorImpl::OnGetWiFiPassphraseResult,
weak_ptr_factory_.GetWeakPtr(), proto, request_guid),
base::BindOnce(&LocalNetworkCollectorImpl::OnGetWiFiPassphraseError,
weak_ptr_factory_.GetWeakPtr(),
NetworkIdentifier::FromProto(proto), request_guid));
}
void LocalNetworkCollectorImpl::OnGetWiFiPassphraseResult(
sync_pb::WifiConfigurationSpecifics proto,
const std::string& request_guid,
const std::string& passphrase) {
proto.set_passphrase(passphrase);
NetworkIdentifier id = NetworkIdentifier::FromProto(proto);
request_guid_to_complete_protos_[request_guid].push_back(std::move(proto));
OnNetworkFinished(id, request_guid);
}
void LocalNetworkCollectorImpl::OnGetWiFiPassphraseError(
const NetworkIdentifier& id,
const std::string& request_guid,
const std::string& error_name,
const std::string& error_message) {
NET_LOG(ERROR) << "Failed to get passphrase for " << id.SerializeToString()
<< " Error: " << error_name << " Details: " << error_message;
OnNetworkFinished(id, request_guid);
}
void LocalNetworkCollectorImpl::OnNetworkFinished(
const NetworkIdentifier& id,
const std::string& request_guid) {
request_guid_to_in_flight_networks_[request_guid].erase(id);
if (request_guid_to_in_flight_networks_[request_guid].empty()) {
OnRequestFinished(request_guid);
}
}
void LocalNetworkCollectorImpl::OnRequestFinished(
const std::string& request_guid) {
DCHECK(request_guid_to_in_flight_networks_[request_guid].empty());
if (request_guid_to_single_callback_[request_guid]) {
std::vector<sync_pb::WifiConfigurationSpecifics>& list =
request_guid_to_complete_protos_[request_guid];
DCHECK(list.size() <= 1);
base::Optional<sync_pb::WifiConfigurationSpecifics> result;
if (list.size() == 1) {
result = list[0];
}
std::move(request_guid_to_single_callback_[request_guid]).Run(result);
request_guid_to_single_callback_.erase(request_guid);
} else {
ProtoListCallback callback =
std::move(request_guid_to_list_callback_[request_guid]);
DCHECK(callback);
std::move(callback).Run(request_guid_to_complete_protos_[request_guid]);
request_guid_to_list_callback_.erase(request_guid);
}
request_guid_to_complete_protos_.erase(request_guid);
request_guid_to_in_flight_networks_.erase(request_guid);
}
void LocalNetworkCollectorImpl::OnNetworkStateListChanged() {
if (!NetworkHandler::Get()
->network_state_handler()
->IsProfileNetworksLoaded()) {
is_mojo_networks_loaded_ = false;
return;
}
cros_network_config_->GetNetworkStateList(
network_config::mojom::NetworkFilter::New(
network_config::mojom::FilterType::kConfigured,
network_config::mojom::NetworkType::kWiFi,
/*limit=*/0),
base::BindOnce(&LocalNetworkCollectorImpl::OnGetNetworkList,
weak_ptr_factory_.GetWeakPtr()));
}
void LocalNetworkCollectorImpl::OnGetNetworkList(
std::vector<network_config::mojom::NetworkStatePropertiesPtr> networks) {
mojo_networks_ = std::move(networks);
is_mojo_networks_loaded_ = true;
while (!after_networks_are_loaded_callback_queue_.empty()) {
std::move(after_networks_are_loaded_callback_queue_.front()).Run();
after_networks_are_loaded_callback_queue_.pop();
}
}
} // namespace sync_wifi
} // namespace chromeos