// Copyright 2018 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 "net/base/network_change_notifier_fuchsia.h" #include #include #include #include #include "base/fuchsia/component_context.h" #include "base/optional.h" #include "base/run_loop.h" #include "net/base/network_interfaces.h" #include "net/base/network_interfaces_fuchsia.h" namespace net { namespace { using ConnectionType = NetworkChangeNotifier::ConnectionType; // Adapts a base::RepeatingCallback to a std::function object. // Useful when binding callbacks to asynchronous FIDL calls, because // it allows the caller to reference in-scope move-only objects as well as use // Chromium's ownership signifiers such as base::Passed, base::Unretained, etc. // // Note that the function takes a RepeatingCallback because it is copyable, but // in practice the callback will only be executed once by the FIDL system. template std::function WrapCallbackAsFunction( base::RepeatingCallback callback) { return [callback](Args... args) { callback.Run(std::forward(args)...); }; } } // namespace NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia() : NetworkChangeNotifierFuchsia( base::fuchsia::ComponentContext::GetDefault() ->ConnectToService()) {} NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia( fuchsia::netstack::NetstackPtr netstack) : netstack_(std::move(netstack)) { DCHECK(netstack_); netstack_.set_error_handler( [this]() { LOG(ERROR) << "Lost connection to netstack."; }); netstack_.events().OnInterfacesChanged = [this](fidl::VectorPtr interfaces) { ProcessInterfaceList(base::OnceClosure(), std::move(interfaces)); }; // Fetch the interface list synchronously, so that an initial ConnectionType // is available before we return. base::RunLoop wait_for_interfaces; netstack_->GetInterfaces([ this, quit_closure = wait_for_interfaces.QuitClosure() ](fidl::VectorPtr interfaces) { ProcessInterfaceList(quit_closure, std::move(interfaces)); }); wait_for_interfaces.Run(); } NetworkChangeNotifierFuchsia::~NetworkChangeNotifierFuchsia() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); } NetworkChangeNotifier::ConnectionType NetworkChangeNotifierFuchsia::GetCurrentConnectionType() const { ConnectionType type = static_cast( base::subtle::Acquire_Load(&cached_connection_type_)); return type; } void NetworkChangeNotifierFuchsia::ProcessInterfaceList( base::OnceClosure on_initialized_cb, fidl::VectorPtr interfaces) { netstack_->GetRouteTable(WrapCallbackAsFunction(base::BindRepeating( &NetworkChangeNotifierFuchsia::OnRouteTableReceived, base::Unretained(this), base::Passed(std::move(on_initialized_cb)), base::Passed(std::move(interfaces))))); } void NetworkChangeNotifierFuchsia::OnRouteTableReceived( base::OnceClosure on_initialized_cb, fidl::VectorPtr interfaces, fidl::VectorPtr route_table) { // Find the default interface in the routing table. auto default_route_interface = std::find_if( route_table->begin(), route_table->end(), [](const fuchsia::netstack::RouteTableEntry& rt) { return MaskPrefixLength(internal::NetAddressToIPAddress(rt.netmask)) == 0; }); // Find the default interface in the NetInterface list. const fuchsia::netstack::NetInterface* default_interface = nullptr; if (default_route_interface != route_table->end()) { for (const auto& cur_interface : *interfaces) { if (cur_interface.id == default_route_interface->nicid) { default_interface = &cur_interface; } } } base::flat_set addresses; std::string ssid; ConnectionType connection_type = CONNECTION_NONE; if (default_interface) { std::vector flattened_interfaces = internal::NetInterfaceToNetworkInterfaces(*default_interface); std::transform( flattened_interfaces.begin(), flattened_interfaces.end(), std::inserter(addresses, addresses.begin()), [](const NetworkInterface& interface) { return interface.address; }); if (!flattened_interfaces.empty()) { connection_type = flattened_interfaces.front().type; } // TODO(https://crbug.com/848355): Treat SSID changes as IP address changes. } bool connection_type_changed = false; if (connection_type != cached_connection_type_) { base::subtle::Release_Store(&cached_connection_type_, connection_type); connection_type_changed = true; } if (addresses != cached_addresses_) { std::swap(cached_addresses_, addresses); if (on_initialized_cb.is_null()) { NotifyObserversOfIPAddressChange(); } connection_type_changed = true; } if (on_initialized_cb.is_null() && connection_type_changed) { NotifyObserversOfConnectionTypeChange(); } if (!on_initialized_cb.is_null()) { std::move(on_initialized_cb).Run(); } } } // namespace net