Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(130)

Side by Side Diff: net/base/network_change_notifier_linux.cc

Issue 11359141: Use Netlink instead of NetworkManager (via D-bus) to monitor network (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address szym's second round of unittest comments Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 //
5 // This implementation of NetworkChangeNotifier's offline state detection
6 // depends on D-Bus and NetworkManager, and is known to work on at least
7 // GNOME version 2.30. If D-Bus or NetworkManager are unavailable, this
8 // implementation will always behave as if it is online.
9 4
10 #include "net/base/network_change_notifier_linux.h" 5 #include "net/base/network_change_notifier_linux.h"
11 6
12 #include <resolv.h>
13
14 #include "base/bind.h" 7 #include "base/bind.h"
15 #include "base/bind_helpers.h"
16 #include "base/callback.h"
17 #include "base/compiler_specific.h" 8 #include "base/compiler_specific.h"
18 #include "base/memory/weak_ptr.h"
19 #include "base/synchronization/lock.h"
20 #include "base/synchronization/waitable_event.h"
21 #include "base/threading/platform_thread.h"
22 #include "base/threading/thread.h" 9 #include "base/threading/thread.h"
23 #include "base/threading/thread_restrictions.h"
24 #include "dbus/bus.h"
25 #include "dbus/message.h"
26 #include "dbus/object_proxy.h"
27 #include "net/base/address_tracker_linux.h" 10 #include "net/base/address_tracker_linux.h"
28 #include "net/base/net_errors.h"
29 #include "net/dns/dns_config_service.h" 11 #include "net/dns/dns_config_service.h"
30 12
31 namespace net { 13 namespace net {
32 14
33 namespace {
34
35 const char kNetworkManagerServiceName[] = "org.freedesktop.NetworkManager";
36 const char kNetworkManagerPath[] = "/org/freedesktop/NetworkManager";
37 const char kNetworkManagerInterface[] = "org.freedesktop.NetworkManager";
38
39 // http://projects.gnome.org/NetworkManager/developers/spec-08.html#type-NM_STAT E
40 enum {
41 NM_LEGACY_STATE_UNKNOWN = 0,
42 NM_LEGACY_STATE_ASLEEP = 1,
43 NM_LEGACY_STATE_CONNECTING = 2,
44 NM_LEGACY_STATE_CONNECTED = 3,
45 NM_LEGACY_STATE_DISCONNECTED = 4
46 };
47
48 // http://projects.gnome.org/NetworkManager/developers/migrating-to-09/spec.html #type-NM_STATE
49 enum {
50 NM_STATE_UNKNOWN = 0,
51 NM_STATE_ASLEEP = 10,
52 NM_STATE_DISCONNECTED = 20,
53 NM_STATE_DISCONNECTING = 30,
54 NM_STATE_CONNECTING = 40,
55 NM_STATE_CONNECTED_LOCAL = 50,
56 NM_STATE_CONNECTED_SITE = 60,
57 NM_STATE_CONNECTED_GLOBAL = 70
58 };
59
60 } // namespace
61
62 // A wrapper around NetworkManager's D-Bus API.
63 class NetworkManagerApi {
64 public:
65 NetworkManagerApi(const base::Closure& notification_callback, dbus::Bus* bus)
66 : is_offline_(false),
67 offline_state_initialized_(true /*manual_reset*/, false),
68 notification_callback_(notification_callback),
69 helper_thread_id_(base::kInvalidThreadId),
70 ALLOW_THIS_IN_INITIALIZER_LIST(ptr_factory_(this)),
71 system_bus_(bus) { }
72
73 ~NetworkManagerApi() { }
74
75 // Should be called on a helper thread which must be of type IO.
76 void Init();
77
78 // Must be called by the helper thread's CleanUp() method.
79 void CleanUp();
80
81 // Implementation of NetworkChangeNotifierLinux::GetCurrentConnectionType().
82 // Safe to call from any thread, but will block until Init() has completed.
83 NetworkChangeNotifier::ConnectionType GetCurrentConnectionType();
84
85 private:
86 // Callbacks for D-Bus API.
87 void OnInitialResponse(dbus::Response* response) {
88 HandleResponse(response);
89 offline_state_initialized_.Signal();
90 }
91
92 void OnSignaled(dbus::Signal* signal);
93
94 void OnConnected(const std::string&, const std::string&, bool success) {
95 if (!success) {
96 DLOG(WARNING) << "Failed to set up offline state detection";
97 offline_state_initialized_.Signal();
98 }
99 }
100
101 // Helper for OnInitialResponse.
102 void HandleResponse(dbus::Response* response);
103
104 // Converts a NetworkManager state uint to a bool.
105 static bool StateIsOffline(uint32 state);
106
107 bool is_offline_;
108 base::Lock is_offline_lock_;
109 base::WaitableEvent offline_state_initialized_;
110
111 base::Closure notification_callback_;
112
113 base::PlatformThreadId helper_thread_id_;
114
115 base::WeakPtrFactory<NetworkManagerApi> ptr_factory_;
116
117 scoped_refptr<dbus::Bus> system_bus_;
118
119 DISALLOW_COPY_AND_ASSIGN(NetworkManagerApi);
120 };
121
122 void NetworkManagerApi::Init() {
123 // D-Bus requires an IO MessageLoop.
124 DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_IO);
125 helper_thread_id_ = base::PlatformThread::CurrentId();
126
127 if (!system_bus_) {
128 dbus::Bus::Options options;
129 options.bus_type = dbus::Bus::SYSTEM;
130 options.connection_type = dbus::Bus::PRIVATE;
131 system_bus_ = new dbus::Bus(options);
132 }
133
134 // Ignore ServiceUnknown errors to avoid log spam: http://crbug.com/109696.
135 dbus::ObjectProxy* proxy = system_bus_->GetObjectProxyWithOptions(
136 kNetworkManagerServiceName, dbus::ObjectPath(kNetworkManagerPath),
137 dbus::ObjectProxy::IGNORE_SERVICE_UNKNOWN_ERRORS);
138
139 // Get the initial state asynchronously.
140 dbus::MethodCall method_call(DBUS_INTERFACE_PROPERTIES, "Get");
141 dbus::MessageWriter builder(&method_call);
142 builder.AppendString(kNetworkManagerInterface);
143 builder.AppendString("State");
144 proxy->CallMethod(
145 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
146 base::Bind(&NetworkManagerApi::OnInitialResponse,
147 ptr_factory_.GetWeakPtr()));
148
149 // And sign up for notifications.
150 proxy->ConnectToSignal(
151 kNetworkManagerInterface,
152 "StateChanged",
153 base::Bind(&NetworkManagerApi::OnSignaled, ptr_factory_.GetWeakPtr()),
154 base::Bind(&NetworkManagerApi::OnConnected, ptr_factory_.GetWeakPtr()));
155 }
156
157 void NetworkManagerApi::CleanUp() {
158 DCHECK_EQ(helper_thread_id_, base::PlatformThread::CurrentId());
159 ptr_factory_.InvalidateWeakPtrs();
160 }
161
162 void NetworkManagerApi::HandleResponse(dbus::Response* response) {
163 DCHECK_EQ(helper_thread_id_, base::PlatformThread::CurrentId());
164 if (!response) {
165 DVLOG(1) << "No response received for initial state request";
166 return;
167 }
168 dbus::MessageReader reader(response);
169 uint32 state = 0;
170 if (!reader.PopVariantOfUint32(&state)) {
171 DLOG(WARNING) << "Unexpected response for NetworkManager State request: "
172 << response->ToString();
173 return;
174 }
175 {
176 base::AutoLock lock(is_offline_lock_);
177 is_offline_ = StateIsOffline(state);
178 }
179 }
180
181 void NetworkManagerApi::OnSignaled(dbus::Signal* signal) {
182 DCHECK_EQ(helper_thread_id_, base::PlatformThread::CurrentId());
183 dbus::MessageReader reader(signal);
184 uint32 state = 0;
185 if (!reader.PopUint32(&state)) {
186 DLOG(WARNING) << "Unexpected signal for NetworkManager StateChanged: "
187 << signal->ToString();
188 return;
189 }
190 bool new_is_offline = StateIsOffline(state);
191 {
192 base::AutoLock lock(is_offline_lock_);
193 if (is_offline_ != new_is_offline)
194 is_offline_ = new_is_offline;
195 else
196 return;
197 }
198 notification_callback_.Run();
199 }
200
201 bool NetworkManagerApi::StateIsOffline(uint32 state) {
202 switch (state) {
203 case NM_LEGACY_STATE_CONNECTED:
204 case NM_STATE_CONNECTED_SITE:
205 case NM_STATE_CONNECTED_GLOBAL:
206 // Definitely connected
207 return false;
208 case NM_LEGACY_STATE_DISCONNECTED:
209 case NM_STATE_DISCONNECTED:
210 // Definitely disconnected
211 return true;
212 case NM_STATE_CONNECTED_LOCAL:
213 // Local networking only; I'm treating this as offline (keybuk)
214 return true;
215 case NM_LEGACY_STATE_CONNECTING:
216 case NM_STATE_DISCONNECTING:
217 case NM_STATE_CONNECTING:
218 // In-flight change to connection status currently underway
219 return true;
220 case NM_LEGACY_STATE_ASLEEP:
221 case NM_STATE_ASLEEP:
222 // Networking disabled or no devices on system
223 return true;
224 default:
225 // Unknown status
226 return false;
227 }
228 }
229
230 NetworkChangeNotifier::ConnectionType
231 NetworkManagerApi::GetCurrentConnectionType() {
232 // http://crbug.com/125097
233 base::ThreadRestrictions::ScopedAllowWait allow_wait;
234 offline_state_initialized_.Wait();
235 base::AutoLock lock(is_offline_lock_);
236 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN.
237 return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE :
238 NetworkChangeNotifier::CONNECTION_UNKNOWN;
239 }
240
241 class NetworkChangeNotifierLinux::Thread : public base::Thread { 15 class NetworkChangeNotifierLinux::Thread : public base::Thread {
242 public: 16 public:
243 explicit Thread(dbus::Bus* bus); 17 Thread();
244 virtual ~Thread(); 18 virtual ~Thread();
245 19
246 // Plumbing for NetworkChangeNotifier::GetCurrentConnectionType. 20 // Plumbing for NetworkChangeNotifier::GetCurrentConnectionType.
247 // Safe to call from any thread. 21 // Safe to call from any thread.
248 NetworkChangeNotifier::ConnectionType GetCurrentConnectionType() { 22 NetworkChangeNotifier::ConnectionType GetCurrentConnectionType() {
249 return network_manager_api_.GetCurrentConnectionType(); 23 return address_tracker_.GetCurrentConnectionType();
250 } 24 }
251 25
252 const internal::AddressTrackerLinux* address_tracker() const { 26 const internal::AddressTrackerLinux* address_tracker() const {
253 return &address_tracker_; 27 return &address_tracker_;
254 } 28 }
255 29
256 protected: 30 protected:
257 // base::Thread 31 // base::Thread
258 virtual void Init() OVERRIDE; 32 virtual void Init() OVERRIDE;
259 virtual void CleanUp() OVERRIDE; 33 virtual void CleanUp() OVERRIDE;
260 34
261 private: 35 private:
262 // Used to detect online/offline state changes.
263 NetworkManagerApi network_manager_api_;
264
265 scoped_ptr<DnsConfigService> dns_config_service_; 36 scoped_ptr<DnsConfigService> dns_config_service_;
37 // Used to detect online/offline state and IP address changes.
266 internal::AddressTrackerLinux address_tracker_; 38 internal::AddressTrackerLinux address_tracker_;
267 39
268 DISALLOW_COPY_AND_ASSIGN(Thread); 40 DISALLOW_COPY_AND_ASSIGN(Thread);
269 }; 41 };
270 42
271 NetworkChangeNotifierLinux::Thread::Thread(dbus::Bus* bus) 43 NetworkChangeNotifierLinux::Thread::Thread()
272 : base::Thread("NetworkChangeNotifier"), 44 : base::Thread("NetworkChangeNotifier"),
273 network_manager_api_(
274 base::Bind(&NetworkChangeNotifier::
275 NotifyObserversOfConnectionTypeChange),
276 bus),
277 address_tracker_( 45 address_tracker_(
278 base::Bind(&NetworkChangeNotifier:: 46 base::Bind(&NetworkChangeNotifier::
279 NotifyObserversOfIPAddressChange)) { 47 NotifyObserversOfIPAddressChange),
48 base::Bind(&NetworkChangeNotifier::
49 NotifyObserversOfConnectionTypeChange)) {
280 } 50 }
281 51
282 NetworkChangeNotifierLinux::Thread::~Thread() { 52 NetworkChangeNotifierLinux::Thread::~Thread() {
283 DCHECK(!Thread::IsRunning()); 53 DCHECK(!Thread::IsRunning());
284 } 54 }
285 55
286 void NetworkChangeNotifierLinux::Thread::Init() { 56 void NetworkChangeNotifierLinux::Thread::Init() {
287 network_manager_api_.Init();
288 address_tracker_.Init(); 57 address_tracker_.Init();
289 dns_config_service_ = DnsConfigService::CreateSystemService(); 58 dns_config_service_ = DnsConfigService::CreateSystemService();
290 dns_config_service_->WatchConfig( 59 dns_config_service_->WatchConfig(
291 base::Bind(&NetworkChangeNotifier::SetDnsConfig)); 60 base::Bind(&NetworkChangeNotifier::SetDnsConfig));
292 } 61 }
293 62
294 void NetworkChangeNotifierLinux::Thread::CleanUp() { 63 void NetworkChangeNotifierLinux::Thread::CleanUp() {
295 network_manager_api_.CleanUp();
296 dns_config_service_.reset(); 64 dns_config_service_.reset();
297 } 65 }
298 66
299 NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::Create() { 67 NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::Create() {
300 return new NetworkChangeNotifierLinux(NULL); 68 return new NetworkChangeNotifierLinux();
301 } 69 }
302 70
303 NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::CreateForTest( 71 NetworkChangeNotifierLinux::NetworkChangeNotifierLinux()
304 dbus::Bus* bus) { 72 : notifier_thread_(new Thread()) {
305 return new NetworkChangeNotifierLinux(bus);
306 }
307
308 NetworkChangeNotifierLinux::NetworkChangeNotifierLinux(dbus::Bus* bus)
309 : notifier_thread_(new Thread(bus)) {
310 // We create this notifier thread because the notification implementation 73 // We create this notifier thread because the notification implementation
311 // needs a MessageLoopForIO, and there's no guarantee that 74 // needs a MessageLoopForIO, and there's no guarantee that
312 // MessageLoop::current() meets that criterion. 75 // MessageLoop::current() meets that criterion.
313 base::Thread::Options thread_options(MessageLoop::TYPE_IO, 0); 76 base::Thread::Options thread_options(MessageLoop::TYPE_IO, 0);
314 notifier_thread_->StartWithOptions(thread_options); 77 notifier_thread_->StartWithOptions(thread_options);
315 } 78 }
316 79
317 NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() { 80 NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() {
318 // Stopping from here allows us to sanity- check that the notifier 81 // Stopping from here allows us to sanity- check that the notifier
319 // thread shut down properly. 82 // thread shut down properly.
320 notifier_thread_->Stop(); 83 notifier_thread_->Stop();
321 } 84 }
322 85
323 NetworkChangeNotifier::ConnectionType 86 NetworkChangeNotifier::ConnectionType
324 NetworkChangeNotifierLinux::GetCurrentConnectionType() const { 87 NetworkChangeNotifierLinux::GetCurrentConnectionType() const {
325 return notifier_thread_->GetCurrentConnectionType(); 88 return notifier_thread_->GetCurrentConnectionType();
326 } 89 }
327 90
328 const internal::AddressTrackerLinux* 91 const internal::AddressTrackerLinux*
329 NetworkChangeNotifierLinux::GetAddressTrackerInternal() const { 92 NetworkChangeNotifierLinux::GetAddressTrackerInternal() const {
330 return notifier_thread_->address_tracker(); 93 return notifier_thread_->address_tracker();
331 } 94 }
332 95
333 } // namespace net 96 } // namespace net
OLDNEW
« no previous file with comments | « net/base/network_change_notifier_linux.h ('k') | net/base/network_change_notifier_linux_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698