blob: 985ef6a8733d8dccd6d9b9072858448526a009f3 [file] [log] [blame]
// Copyright 2015 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 "components/arc/net/arc_net_host_impl.h"
#include <memory>
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "chromeos/login/login_state.h"
#include "chromeos/network/managed_network_configuration_handler.h"
#include "chromeos/network/network_connection_handler.h"
#include "chromeos/network/network_handler.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/network/network_type_pattern.h"
#include "chromeos/network/network_util.h"
#include "chromeos/network/onc/onc_utils.h"
#include "components/arc/arc_bridge_service.h"
namespace {
const int kGetNetworksListLimit = 100;
chromeos::NetworkStateHandler* GetStateHandler() {
return chromeos::NetworkHandler::Get()->network_state_handler();
}
chromeos::ManagedNetworkConfigurationHandler* GetManagedConfigurationHandler() {
return chromeos::NetworkHandler::Get()
->managed_network_configuration_handler();
}
chromeos::NetworkConnectionHandler* GetNetworkConnectionHandler() {
return chromeos::NetworkHandler::Get()->network_connection_handler();
}
bool IsDeviceOwner() {
// Check whether the logged-in Chrome OS user is allowed to add or
// remove WiFi networks.
return chromeos::LoginState::Get()->GetLoggedInUserType() ==
chromeos::LoginState::LOGGED_IN_USER_OWNER;
}
std::string GetStringFromOncDictionary(const base::DictionaryValue* dict,
const char* key,
bool required) {
std::string value;
dict->GetString(key, &value);
if (required && value.empty())
NOTREACHED();
return value;
}
arc::mojom::SecurityType TranslateONCWifiSecurityType(
const base::DictionaryValue* dict) {
std::string type = GetStringFromOncDictionary(dict, onc::wifi::kSecurity,
true /* required */);
if (type == onc::wifi::kWEP_PSK)
return arc::mojom::SecurityType::WEP_PSK;
else if (type == onc::wifi::kWEP_8021X)
return arc::mojom::SecurityType::WEP_8021X;
else if (type == onc::wifi::kWPA_PSK)
return arc::mojom::SecurityType::WPA_PSK;
else if (type == onc::wifi::kWPA_EAP)
return arc::mojom::SecurityType::WPA_EAP;
else
return arc::mojom::SecurityType::NONE;
}
arc::mojom::WiFiPtr TranslateONCWifi(const base::DictionaryValue* dict) {
arc::mojom::WiFiPtr wifi = arc::mojom::WiFi::New();
// Optional; defaults to 0.
dict->GetInteger(onc::wifi::kFrequency, &wifi->frequency);
wifi->bssid =
GetStringFromOncDictionary(dict, onc::wifi::kBSSID, false /* required */);
wifi->hex_ssid = GetStringFromOncDictionary(dict, onc::wifi::kHexSSID,
true /* required */);
// Optional; defaults to false.
dict->GetBoolean(onc::wifi::kHiddenSSID, &wifi->hidden_ssid);
wifi->security = TranslateONCWifiSecurityType(dict);
// Optional; defaults to 0.
dict->GetInteger(onc::wifi::kSignalStrength, &wifi->signal_strength);
return wifi;
}
mojo::Array<mojo::String> TranslateStringArray(const base::ListValue* list) {
mojo::Array<mojo::String> strings = mojo::Array<mojo::String>::New(0);
for (size_t i = 0; i < list->GetSize(); i++) {
std::string value;
list->GetString(i, &value);
DCHECK(!value.empty());
strings.push_back(static_cast<mojo::String>(value));
}
return strings;
}
mojo::Array<arc::mojom::IPConfigurationPtr> TranslateONCIPConfigs(
const base::ListValue* list) {
mojo::Array<arc::mojom::IPConfigurationPtr> configs =
mojo::Array<arc::mojom::IPConfigurationPtr>::New(0);
for (size_t i = 0; i < list->GetSize(); i++) {
const base::DictionaryValue* ip_dict = nullptr;
arc::mojom::IPConfigurationPtr configuration =
arc::mojom::IPConfiguration::New();
list->GetDictionary(i, &ip_dict);
DCHECK(ip_dict);
configuration->gateway = GetStringFromOncDictionary(
ip_dict, onc::ipconfig::kGateway, true /* required */);
configuration->ip_address = GetStringFromOncDictionary(
ip_dict, onc::ipconfig::kIPAddress, true /* required */);
const base::ListValue* dns_list;
if (!ip_dict->GetList(onc::ipconfig::kNameServers, &dns_list))
NOTREACHED();
configuration->name_servers = TranslateStringArray(dns_list);
if (!ip_dict->GetInteger(onc::ipconfig::kRoutingPrefix,
&configuration->routing_prefix)) {
NOTREACHED();
}
std::string type = GetStringFromOncDictionary(ip_dict, onc::ipconfig::kType,
true /* required */);
configuration->type = type == onc::ipconfig::kIPv6
? arc::mojom::IPAddressType::IPV6
: arc::mojom::IPAddressType::IPV4;
configuration->web_proxy_auto_discovery_url = GetStringFromOncDictionary(
ip_dict, onc::ipconfig::kWebProxyAutoDiscoveryUrl, true /* required */);
configs.push_back(std::move(configuration));
}
return configs;
}
arc::mojom::ConnectionStateType TranslateONCConnectionState(
const base::DictionaryValue* dict) {
std::string connection_state = GetStringFromOncDictionary(
dict, onc::network_config::kConnectionState, true /* required */);
if (connection_state == onc::connection_state::kConnected)
return arc::mojom::ConnectionStateType::CONNECTED;
else if (connection_state == onc::connection_state::kConnecting)
return arc::mojom::ConnectionStateType::CONNECTING;
else if (connection_state == onc::connection_state::kNotConnected)
return arc::mojom::ConnectionStateType::NOT_CONNECTED;
NOTREACHED();
return arc::mojom::ConnectionStateType::NOT_CONNECTED;
}
void TranslateONCNetworkTypeDetails(const base::DictionaryValue* dict,
arc::mojom::NetworkConfiguration* mojo) {
std::string type = GetStringFromOncDictionary(
dict, onc::network_config::kType, true /* required */);
if (type == onc::network_type::kCellular) {
mojo->type = arc::mojom::NetworkType::CELLULAR;
} else if (type == onc::network_type::kEthernet) {
mojo->type = arc::mojom::NetworkType::ETHERNET;
} else if (type == onc::network_type::kVPN) {
mojo->type = arc::mojom::NetworkType::VPN;
} else if (type == onc::network_type::kWiFi) {
mojo->type = arc::mojom::NetworkType::WIFI;
const base::DictionaryValue* wifi_dict = nullptr;
dict->GetDictionary(onc::network_config::kWiFi, &wifi_dict);
DCHECK(wifi_dict);
mojo->wifi = TranslateONCWifi(wifi_dict);
} else if (type == onc::network_type::kWimax) {
mojo->type = arc::mojom::NetworkType::WIMAX;
} else {
NOTREACHED();
}
}
arc::mojom::NetworkConfigurationPtr TranslateONCConfiguration(
const base::DictionaryValue* dict) {
arc::mojom::NetworkConfigurationPtr mojo =
arc::mojom::NetworkConfiguration::New();
mojo->connection_state = TranslateONCConnectionState(dict);
mojo->guid = GetStringFromOncDictionary(dict, onc::network_config::kGUID,
true /* required */);
const base::ListValue* ip_config_list = nullptr;
if (dict->GetList(onc::network_config::kIPConfigs, &ip_config_list)) {
DCHECK(ip_config_list);
mojo->ip_configs = TranslateONCIPConfigs(ip_config_list);
}
mojo->guid = GetStringFromOncDictionary(dict, onc::network_config::kGUID,
true /* required */);
mojo->mac_address = GetStringFromOncDictionary(
dict, onc::network_config::kMacAddress, true /* required */);
TranslateONCNetworkTypeDetails(dict, mojo.get());
return mojo;
}
void CreateNetworkSuccessCallback(
const arc::mojom::NetHost::CreateNetworkCallback& mojo_callback,
const std::string& service_path,
const std::string& guid) {
VLOG(1) << "CreateNetworkSuccessCallback";
mojo_callback.Run(guid);
}
void CreateNetworkFailureCallback(
const arc::mojom::NetHost::CreateNetworkCallback& mojo_callback,
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data) {
VLOG(1) << "CreateNetworkFailureCallback: " << error_name;
mojo_callback.Run("");
}
void ForgetNetworkSuccessCallback(
const arc::mojom::NetHost::ForgetNetworkCallback& mojo_callback) {
mojo_callback.Run(arc::mojom::NetworkResult::SUCCESS);
}
void ForgetNetworkFailureCallback(
const arc::mojom::NetHost::ForgetNetworkCallback& mojo_callback,
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data) {
VLOG(1) << "ForgetNetworkFailureCallback: " << error_name;
mojo_callback.Run(arc::mojom::NetworkResult::FAILURE);
}
void StartConnectSuccessCallback(
const arc::mojom::NetHost::StartConnectCallback& mojo_callback) {
mojo_callback.Run(arc::mojom::NetworkResult::SUCCESS);
}
void StartConnectFailureCallback(
const arc::mojom::NetHost::StartConnectCallback& mojo_callback,
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data) {
VLOG(1) << "StartConnectFailureCallback: " << error_name;
mojo_callback.Run(arc::mojom::NetworkResult::FAILURE);
}
void StartDisconnectSuccessCallback(
const arc::mojom::NetHost::StartDisconnectCallback& mojo_callback) {
mojo_callback.Run(arc::mojom::NetworkResult::SUCCESS);
}
void StartDisconnectFailureCallback(
const arc::mojom::NetHost::StartDisconnectCallback& mojo_callback,
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data) {
VLOG(1) << "StartDisconnectFailureCallback: " << error_name;
mojo_callback.Run(arc::mojom::NetworkResult::FAILURE);
}
void GetDefaultNetworkSuccessCallback(
const arc::ArcNetHostImpl::GetDefaultNetworkCallback& callback,
const std::string& service_path,
const base::DictionaryValue& dictionary) {
// TODO(cernekee): Figure out how to query Chrome for the default physical
// service if a VPN is connected, rather than just reporting the
// default logical service in both fields.
callback.Run(TranslateONCConfiguration(&dictionary),
TranslateONCConfiguration(&dictionary));
}
void GetDefaultNetworkFailureCallback(
const arc::ArcNetHostImpl::GetDefaultNetworkCallback& callback,
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data) {
LOG(ERROR) << "Failed to query default logical network";
callback.Run(nullptr, nullptr);
}
void DefaultNetworkFailureCallback(
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data) {
LOG(ERROR) << "Failed to query default logical network";
}
} // namespace
namespace arc {
ArcNetHostImpl::ArcNetHostImpl(ArcBridgeService* bridge_service)
: ArcService(bridge_service), binding_(this), weak_factory_(this) {
arc_bridge_service()->AddObserver(this);
GetStateHandler()->AddObserver(this, FROM_HERE);
}
ArcNetHostImpl::~ArcNetHostImpl() {
DCHECK(thread_checker_.CalledOnValidThread());
arc_bridge_service()->RemoveObserver(this);
if (chromeos::NetworkHandler::IsInitialized()) {
GetStateHandler()->RemoveObserver(this, FROM_HERE);
}
}
void ArcNetHostImpl::OnNetInstanceReady() {
DCHECK(thread_checker_.CalledOnValidThread());
mojom::NetHostPtr host;
binding_.Bind(GetProxy(&host));
arc_bridge_service()->net_instance()->Init(std::move(host));
}
void ArcNetHostImpl::GetNetworksDeprecated(
bool configured_only,
bool visible_only,
const GetNetworksDeprecatedCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
if (configured_only && visible_only) {
VLOG(1) << "Illegal arguments - both configured and visible networks "
"requested.";
return;
}
mojom::GetNetworksRequestType type =
mojom::GetNetworksRequestType::CONFIGURED_ONLY;
if (visible_only) {
type = mojom::GetNetworksRequestType::VISIBLE_ONLY;
}
GetNetworks(type, callback);
}
void ArcNetHostImpl::GetNetworks(mojom::GetNetworksRequestType type,
const GetNetworksCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
mojom::NetworkDataPtr data = mojom::NetworkData::New();
bool configured_only = true;
bool visible_only = false;
if (type == mojom::GetNetworksRequestType::VISIBLE_ONLY) {
configured_only = false;
visible_only = true;
}
// Retrieve list of nearby wifi networks
chromeos::NetworkTypePattern network_pattern =
chromeos::onc::NetworkTypePatternFromOncType(onc::network_type::kWiFi);
std::unique_ptr<base::ListValue> network_properties_list =
chromeos::network_util::TranslateNetworkListToONC(
network_pattern, configured_only, visible_only,
kGetNetworksListLimit);
// Extract info for each network and add it to the list.
// Even if there's no WiFi, an empty (size=0) list must be returned and not a
// null one. The explicitly sized New() constructor ensures the non-null
// property.
mojo::Array<mojom::WifiConfigurationPtr> networks =
mojo::Array<mojom::WifiConfigurationPtr>::New(0);
for (const auto& value : *network_properties_list) {
mojom::WifiConfigurationPtr wc = mojom::WifiConfiguration::New();
base::DictionaryValue* network_dict = nullptr;
value->GetAsDictionary(&network_dict);
DCHECK(network_dict);
// kName is a post-processed version of kHexSSID.
std::string tmp;
network_dict->GetString(onc::network_config::kName, &tmp);
DCHECK(!tmp.empty());
wc->ssid = tmp;
tmp.clear();
network_dict->GetString(onc::network_config::kGUID, &tmp);
DCHECK(!tmp.empty());
wc->guid = tmp;
base::DictionaryValue* wifi_dict = nullptr;
network_dict->GetDictionary(onc::network_config::kWiFi, &wifi_dict);
DCHECK(wifi_dict);
if (!wifi_dict->GetInteger(onc::wifi::kFrequency, &wc->frequency))
wc->frequency = 0;
if (!wifi_dict->GetInteger(onc::wifi::kSignalStrength,
&wc->signal_strength))
wc->signal_strength = 0;
if (!wifi_dict->GetString(onc::wifi::kSecurity, &tmp))
NOTREACHED();
DCHECK(!tmp.empty());
wc->security = tmp;
if (!wifi_dict->GetString(onc::wifi::kBSSID, &tmp))
NOTREACHED();
DCHECK(!tmp.empty());
wc->bssid = tmp;
mojom::VisibleNetworkDetailsPtr details =
mojom::VisibleNetworkDetails::New();
details->frequency = wc->frequency;
details->signal_strength = wc->signal_strength;
details->bssid = wc->bssid;
wc->details = mojom::NetworkDetails::New();
wc->details->set_visible(std::move(details));
networks.push_back(std::move(wc));
}
data->networks = std::move(networks);
callback.Run(std::move(data));
}
void ArcNetHostImpl::CreateNetwork(mojom::WifiConfigurationPtr cfg,
const CreateNetworkCallback& callback) {
if (!IsDeviceOwner()) {
callback.Run("");
return;
}
std::unique_ptr<base::DictionaryValue> properties(new base::DictionaryValue);
std::unique_ptr<base::DictionaryValue> wifi_dict(new base::DictionaryValue);
if (cfg->hexssid.is_null() || !cfg->details) {
callback.Run("");
return;
}
mojom::ConfiguredNetworkDetailsPtr details =
std::move(cfg->details->get_configured());
if (!details) {
callback.Run("");
return;
}
properties->SetStringWithoutPathExpansion(onc::network_config::kType,
onc::network_config::kWiFi);
wifi_dict->SetStringWithoutPathExpansion(onc::wifi::kHexSSID, cfg->hexssid);
wifi_dict->SetBooleanWithoutPathExpansion(onc::wifi::kAutoConnect,
details->autoconnect);
if (cfg->security.get().empty()) {
wifi_dict->SetStringWithoutPathExpansion(onc::wifi::kSecurity,
onc::wifi::kSecurityNone);
} else {
wifi_dict->SetStringWithoutPathExpansion(onc::wifi::kSecurity,
cfg->security);
if (!details->passphrase.is_null()) {
wifi_dict->SetStringWithoutPathExpansion(onc::wifi::kPassphrase,
details->passphrase);
}
}
properties->SetWithoutPathExpansion(onc::network_config::kWiFi,
std::move(wifi_dict));
std::string user_id_hash = chromeos::LoginState::Get()->primary_user_hash();
GetManagedConfigurationHandler()->CreateConfiguration(
user_id_hash, *properties,
base::Bind(&CreateNetworkSuccessCallback, callback),
base::Bind(&CreateNetworkFailureCallback, callback));
}
void ArcNetHostImpl::ForgetNetwork(const mojo::String& guid,
const ForgetNetworkCallback& callback) {
if (!IsDeviceOwner()) {
callback.Run(mojom::NetworkResult::FAILURE);
return;
}
const chromeos::NetworkState* network =
GetStateHandler()->GetNetworkStateFromGuid(guid);
if (!network) {
callback.Run(mojom::NetworkResult::FAILURE);
return;
}
GetManagedConfigurationHandler()->RemoveConfiguration(
network->path(), base::Bind(&ForgetNetworkSuccessCallback, callback),
base::Bind(&ForgetNetworkFailureCallback, callback));
}
void ArcNetHostImpl::StartConnect(const mojo::String& guid,
const StartConnectCallback& callback) {
const chromeos::NetworkState* network =
GetStateHandler()->GetNetworkStateFromGuid(guid);
if (!network) {
callback.Run(mojom::NetworkResult::FAILURE);
return;
}
GetNetworkConnectionHandler()->ConnectToNetwork(
network->path(), base::Bind(&StartConnectSuccessCallback, callback),
base::Bind(&StartConnectFailureCallback, callback), false);
}
void ArcNetHostImpl::StartDisconnect(const mojo::String& guid,
const StartDisconnectCallback& callback) {
const chromeos::NetworkState* network =
GetStateHandler()->GetNetworkStateFromGuid(guid);
if (!network) {
callback.Run(mojom::NetworkResult::FAILURE);
return;
}
GetNetworkConnectionHandler()->DisconnectNetwork(
network->path(), base::Bind(&StartDisconnectSuccessCallback, callback),
base::Bind(&StartDisconnectFailureCallback, callback));
}
void ArcNetHostImpl::GetWifiEnabledState(
const GetWifiEnabledStateCallback& callback) {
bool is_enabled = GetStateHandler()->IsTechnologyEnabled(
chromeos::NetworkTypePattern::WiFi());
callback.Run(is_enabled);
}
void ArcNetHostImpl::SetWifiEnabledState(
bool is_enabled,
const SetWifiEnabledStateCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
chromeos::NetworkStateHandler::TechnologyState state =
GetStateHandler()->GetTechnologyState(
chromeos::NetworkTypePattern::WiFi());
// WiFi can't be enabled or disabled in these states.
if ((state == chromeos::NetworkStateHandler::TECHNOLOGY_PROHIBITED) ||
(state == chromeos::NetworkStateHandler::TECHNOLOGY_UNINITIALIZED) ||
(state == chromeos::NetworkStateHandler::TECHNOLOGY_UNAVAILABLE)) {
VLOG(1) << "SetWifiEnabledState failed due to WiFi state: " << state;
callback.Run(false);
} else {
GetStateHandler()->SetTechnologyEnabled(
chromeos::NetworkTypePattern::WiFi(), is_enabled,
chromeos::network_handler::ErrorCallback());
callback.Run(true);
}
}
void ArcNetHostImpl::StartScan() {
GetStateHandler()->RequestScan();
}
void ArcNetHostImpl::ScanCompleted(const chromeos::DeviceState* /*unused*/) {
if (!arc_bridge_service()->net_instance()) {
VLOG(2) << "NetInstance not ready yet";
return;
}
if (arc_bridge_service()->net_version() < 1) {
VLOG(1) << "NetInstance does not support ScanCompleted.";
return;
}
arc_bridge_service()->net_instance()->ScanCompleted();
}
void ArcNetHostImpl::GetDefaultNetwork(
const GetDefaultNetworkCallback& callback) {
const chromeos::NetworkState* default_network =
GetStateHandler()->DefaultNetwork();
if (!default_network) {
VLOG(1) << "GetDefaultNetwork: no default network";
callback.Run(nullptr, nullptr);
return;
}
VLOG(1) << "GetDefaultNetwork: default network is "
<< default_network->path();
std::string user_id_hash = chromeos::LoginState::Get()->primary_user_hash();
GetManagedConfigurationHandler()->GetProperties(
user_id_hash, default_network->path(),
base::Bind(&GetDefaultNetworkSuccessCallback, callback),
base::Bind(&GetDefaultNetworkFailureCallback, callback));
}
void ArcNetHostImpl::DefaultNetworkSuccessCallback(
const std::string& service_path,
const base::DictionaryValue& dictionary) {
arc_bridge_service()->net_instance()->DefaultNetworkChanged(
TranslateONCConfiguration(&dictionary),
TranslateONCConfiguration(&dictionary));
}
void ArcNetHostImpl::DefaultNetworkChanged(
const chromeos::NetworkState* network) {
if (arc_bridge_service()->net_version() < 2) {
VLOG(1) << "ArcBridgeService does not support DefaultNetworkChanged.";
return;
}
if (!network) {
VLOG(1) << "No default network";
arc_bridge_service()->net_instance()->DefaultNetworkChanged(nullptr,
nullptr);
return;
}
VLOG(1) << "New default network: " << network->path();
std::string user_id_hash = chromeos::LoginState::Get()->primary_user_hash();
GetManagedConfigurationHandler()->GetProperties(
user_id_hash, network->path(),
base::Bind(&arc::ArcNetHostImpl::DefaultNetworkSuccessCallback,
weak_factory_.GetWeakPtr()),
base::Bind(&DefaultNetworkFailureCallback));
}
void ArcNetHostImpl::OnShuttingDown() {
GetStateHandler()->RemoveObserver(this, FROM_HERE);
}
} // namespace arc