xref: /aosp_15_r20/external/cronet/net/base/network_change_notifier_fuchsia.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/base/network_change_notifier_fuchsia.h"
6 
7 #include <fuchsia/net/interfaces/cpp/fidl.h>
8 #include <lib/sys/cpp/component_context.h>
9 
10 #include <algorithm>
11 #include <optional>
12 #include <utility>
13 #include <vector>
14 
15 #include "base/fuchsia/fuchsia_logging.h"
16 #include "base/fuchsia/process_context.h"
17 #include "base/logging.h"
18 #include "base/process/process.h"
19 #include "base/threading/thread_checker.h"
20 #include "base/types/expected.h"
21 #include "net/base/fuchsia/network_interface_cache.h"
22 
23 namespace net {
24 
NetworkChangeNotifierFuchsia(bool require_wlan)25 NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia(bool require_wlan)
26     : NetworkChangeNotifierFuchsia(internal::ConnectInterfacesWatcher(),
27                                    require_wlan,
28                                    /*system_dns_config_notifier=*/nullptr) {}
29 
NetworkChangeNotifierFuchsia(fuchsia::net::interfaces::WatcherHandle watcher_handle,bool require_wlan,SystemDnsConfigChangeNotifier * system_dns_config_notifier)30 NetworkChangeNotifierFuchsia::NetworkChangeNotifierFuchsia(
31     fuchsia::net::interfaces::WatcherHandle watcher_handle,
32     bool require_wlan,
33     SystemDnsConfigChangeNotifier* system_dns_config_notifier)
34     : NetworkChangeNotifier(NetworkChangeCalculatorParams(),
35                             system_dns_config_notifier),
36       cache_(require_wlan) {
37   DCHECK(watcher_handle);
38 
39   std::vector<fuchsia::net::interfaces::Properties> interfaces;
40   auto handle_or_status = internal::ReadExistingNetworkInterfacesFromNewWatcher(
41       std::move(watcher_handle), interfaces);
42   if (!handle_or_status.has_value()) {
43     ZX_LOG(ERROR, handle_or_status.error()) << "ReadExistingNetworkInterfaces";
44     base::Process::TerminateCurrentProcessImmediately(1);
45   }
46 
47   HandleCacheStatus(cache_.AddInterfaces(std::move(interfaces)));
48 
49   watcher_.set_error_handler(base::LogFidlErrorAndExitProcess(
50       FROM_HERE, "fuchsia.net.interfaces.Watcher"));
51   zx_status_t bind_status = watcher_.Bind(std::move(handle_or_status.value()));
52   ZX_CHECK(bind_status == ZX_OK, bind_status) << "Bind()";
53   watcher_->Watch(
54       fit::bind_member(this, &NetworkChangeNotifierFuchsia::OnInterfacesEvent));
55 }
56 
~NetworkChangeNotifierFuchsia()57 NetworkChangeNotifierFuchsia::~NetworkChangeNotifierFuchsia() {
58   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
59   ClearGlobalPointer();
60 }
61 
62 NetworkChangeNotifier::ConnectionType
GetCurrentConnectionType() const63 NetworkChangeNotifierFuchsia::GetCurrentConnectionType() const {
64   return cache_.GetConnectionType();
65 }
66 
67 const internal::NetworkInterfaceCache*
GetNetworkInterfaceCacheInternal() const68 NetworkChangeNotifierFuchsia::GetNetworkInterfaceCacheInternal() const {
69   return &cache_;
70 }
71 
OnInterfacesEvent(fuchsia::net::interfaces::Event event)72 void NetworkChangeNotifierFuchsia::OnInterfacesEvent(
73     fuchsia::net::interfaces::Event event) {
74   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
75 
76   // Immediately trigger the next watch, which will happen asynchronously. If
77   // event processing encounters an error it'll close the watcher channel which
78   // will cancel any pending callbacks.
79   watcher_->Watch(
80       fit::bind_member(this, &NetworkChangeNotifierFuchsia::OnInterfacesEvent));
81 
82   switch (event.Which()) {
83     case fuchsia::net::interfaces::Event::kAdded:
84       HandleCacheStatus(cache_.AddInterface(std::move(event.added())));
85       break;
86     case fuchsia::net::interfaces::Event::kRemoved:
87       HandleCacheStatus(cache_.RemoveInterface(event.removed()));
88       break;
89     case fuchsia::net::interfaces::Event::kChanged:
90       HandleCacheStatus(cache_.ChangeInterface(std::move(event.changed())));
91       break;
92     default:
93       LOG(ERROR) << "Unexpected event: " << event.Which();
94       watcher_.Unbind();
95       cache_.SetError();
96       break;
97   }
98 }
99 
HandleCacheStatus(std::optional<internal::NetworkInterfaceCache::ChangeBits> change_bits)100 void NetworkChangeNotifierFuchsia::HandleCacheStatus(
101     std::optional<internal::NetworkInterfaceCache::ChangeBits> change_bits) {
102   if (!change_bits.has_value()) {
103     watcher_.Unbind();
104     return;
105   }
106 
107   if (change_bits.value() &
108       internal::NetworkInterfaceCache::kIpAddressChanged) {
109     NotifyObserversOfIPAddressChange();
110   }
111   if (change_bits.value() &
112       internal::NetworkInterfaceCache::kConnectionTypeChanged) {
113     NotifyObserversOfConnectionTypeChange();
114   }
115 }
116 
117 namespace internal {
118 
ConnectInterfacesWatcher()119 fuchsia::net::interfaces::WatcherHandle ConnectInterfacesWatcher() {
120   fuchsia::net::interfaces::StateSyncPtr state;
121   zx_status_t status =
122       base::ComponentContextForProcess()->svc()->Connect(state.NewRequest());
123   ZX_CHECK(status == ZX_OK, status) << "Connect()";
124 
125   // GetWatcher() is a feed-forward API, so failures will be observed via
126   // peer-closed events on the returned `watcher`.
127   fuchsia::net::interfaces::WatcherHandle watcher;
128   status = state->GetWatcher(/*options=*/{}, watcher.NewRequest());
129 
130   return watcher;
131 }
132 
133 base::expected<fuchsia::net::interfaces::WatcherHandle, zx_status_t>
ReadExistingNetworkInterfacesFromNewWatcher(fuchsia::net::interfaces::WatcherHandle watcher_handle,std::vector<fuchsia::net::interfaces::Properties> & interfaces)134 ReadExistingNetworkInterfacesFromNewWatcher(
135     fuchsia::net::interfaces::WatcherHandle watcher_handle,
136     std::vector<fuchsia::net::interfaces::Properties>& interfaces) {
137   DCHECK(watcher_handle);
138 
139   fuchsia::net::interfaces::WatcherSyncPtr watcher = watcher_handle.BindSync();
140 
141   // fuchsia.net.interfaces.Watcher implements a hanging-get pattern, accepting
142   // a single Watch() call and returning an event when something changes.
143   // When a Watcher is first created, it emits a series of events describing
144   // existing interfaces, terminated by an "idle" event, before entering the
145   // normal hanging-get flow.
146   while (true) {
147     fuchsia::net::interfaces::Event event;
148     if (auto watch_status = watcher->Watch(&event); watch_status != ZX_OK) {
149       ZX_LOG(ERROR, watch_status) << "Watch() failed";
150       return base::unexpected(watch_status);
151     }
152 
153     switch (event.Which()) {
154       case fuchsia::net::interfaces::Event::Tag::kExisting:
155         interfaces.push_back(std::move(event.existing()));
156         break;
157       case fuchsia::net::interfaces::Event::Tag::kIdle:
158         // Idle means we've listed all the existing interfaces. We can stop
159         // fetching events.
160         return base::ok(watcher.Unbind());
161       default:
162         LOG(ERROR) << "Unexpected event " << event.Which();
163         return base::unexpected(ZX_ERR_BAD_STATE);
164     }
165   }
166 }
167 
168 }  // namespace internal
169 }  // namespace net
170