Update expectations after WebKit roll.
[chromium-blink-merge.git] / net / base / network_change_notifier_mac.cc
blob2dd1b4d694e99d8bc17134e77b721e4ccddf2d3d
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // There are three classes involved here. There's NetworkChangeNotifierMac,
6 // which is the Mac specific implementation of NetworkChangeNotifier. It is the
7 // class with which clients can register themselves as network change
8 // observers. There's NetworkChangeNotifierThread, which is a base::Thread
9 // subclass of MessageLoop::TYPE_UI (since it needs a CFRunLoop) that contains
10 // the NetworkChangeNotifierImpl. NetworkChangeNotifierImpl is the object
11 // that receives the actual OS X notifications and posts them to the
12 // NetworkChangeNotifierMac's message loop, so that NetworkChangeNotifierMac
13 // can notify all its observers.
15 // When NetworkChangeNotifierMac is being deleted, it will delete the
16 // NetworkChangeNotifierThread, which will Stop() it and also delete the
17 // NetworkChangeNotifierImpl. Therefore, NetworkChangeNotifierImpl and
18 // NetworkChangeNotifierThread's lifetimes generally begin after and end before
19 // NetworkChangeNotifierMac. There is an edge case where a notification task
20 // gets posted to the IO thread, thereby maintaining a reference to
21 // NetworkChangeNotifierImpl beyond the lifetime of NetworkChangeNotifierThread.
22 // In this case, the notification is cancelled, and NetworkChangeNotifierImpl
23 // will be deleted once all notification tasks that reference it have been run.
25 #include "net/base/network_change_notifier_mac.h"
26 #include <SystemConfiguration/SCDynamicStore.h>
27 #include <SystemConfiguration/SCDynamicStoreKey.h>
28 #include <SystemConfiguration/SCSchemaDefinitions.h>
29 #include <algorithm>
30 #include "base/logging.h"
31 #include "base/message_loop.h"
32 #include "base/scoped_cftyperef.h"
33 #include "base/thread.h"
35 namespace net {
37 namespace {
39 // NetworkChangeNotifierImpl should be created on a thread with a CFRunLoop,
40 // since it requires one to pump notifications. However, it also runs some
41 // methods on |notifier_loop_|, because it cannot post calls to |notifier_|
42 // since NetworkChangeNotifier is not ref counted in a thread safe manner.
43 class NetworkChangeNotifierImpl
44 : public base::RefCountedThreadSafe<NetworkChangeNotifierImpl> {
45 public:
46 NetworkChangeNotifierImpl(MessageLoop* notifier_loop,
47 NetworkChangeNotifierMac* notifier);
49 void Shutdown();
51 private:
52 friend class base::RefCountedThreadSafe<NetworkChangeNotifierImpl>;
53 ~NetworkChangeNotifierImpl();
55 static void DynamicStoreCallback(SCDynamicStoreRef /* store */,
56 CFArrayRef changed_keys,
57 void* config);
59 void OnNetworkConfigChange(CFArrayRef changed_keys);
61 // Runs on |notifier_loop_|.
62 void OnIPAddressChanged();
64 // Raw pointers. Note that |notifier_| _must_ outlive the
65 // NetworkChangeNotifierImpl. For lifecycle management details, read the
66 // comment at the top of the file.
67 MessageLoop* const notifier_loop_;
68 NetworkChangeNotifierMac* notifier_;
70 scoped_cftyperef<CFRunLoopSourceRef> source_;
72 DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierImpl);
75 NetworkChangeNotifierImpl::NetworkChangeNotifierImpl(
76 MessageLoop* notifier_loop, NetworkChangeNotifierMac* notifier)
77 : notifier_loop_(notifier_loop),
78 notifier_(notifier) {
79 DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type());
80 SCDynamicStoreContext context = {
81 0, // Version 0.
82 this, // User data.
83 NULL, // This is not reference counted. No retain function.
84 NULL, // This is not reference counted. No release function.
85 NULL, // No description for this.
88 // Get a reference to the dynamic store.
89 scoped_cftyperef<SCDynamicStoreRef> store(
90 SCDynamicStoreCreate(NULL /* use default allocator */,
91 CFSTR("org.chromium"),
92 DynamicStoreCallback, &context));
94 // Create a run loop source for the dynamic store.
95 source_.reset(SCDynamicStoreCreateRunLoopSource(
96 NULL /* use default allocator */,
97 store.get(),
98 0 /* 0 sounds like a fine source order to me! */));
100 // Add the run loop source to the current run loop.
101 CFRunLoopAddSource(CFRunLoopGetCurrent(),
102 source_.get(),
103 kCFRunLoopCommonModes);
105 // Set up the notification keys.
106 scoped_cftyperef<CFMutableArrayRef> notification_keys(
107 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
109 // Monitor interface changes.
110 scoped_cftyperef<CFStringRef> key(
111 SCDynamicStoreKeyCreateNetworkGlobalEntity(
112 NULL /* default allocator */, kSCDynamicStoreDomainState,
113 kSCEntNetInterface));
114 CFArrayAppendValue(notification_keys.get(), key.get());
116 // Monitor IP address changes.
118 key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
119 NULL /* default allocator */, kSCDynamicStoreDomainState,
120 kSCEntNetIPv4));
121 CFArrayAppendValue(notification_keys.get(), key.get());
123 key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
124 NULL /* default allocator */, kSCDynamicStoreDomainState,
125 kSCEntNetIPv6));
126 CFArrayAppendValue(notification_keys.get(), key.get());
128 // Ok, let's ask for notifications!
129 // TODO(willchan): Figure out a proper way to handle this rather than crash.
130 CHECK(SCDynamicStoreSetNotificationKeys(
131 store.get(), notification_keys.get(), NULL));
134 NetworkChangeNotifierImpl::~NetworkChangeNotifierImpl() {
135 CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
136 source_.get(),
137 kCFRunLoopCommonModes);
140 void NetworkChangeNotifierImpl::Shutdown() {
141 CHECK(notifier_);
142 notifier_ = NULL;
145 // static
146 void NetworkChangeNotifierImpl::DynamicStoreCallback(
147 SCDynamicStoreRef /* store */,
148 CFArrayRef changed_keys,
149 void* config) {
150 NetworkChangeNotifierImpl* net_config =
151 static_cast<NetworkChangeNotifierImpl*>(config);
152 net_config->OnNetworkConfigChange(changed_keys);
155 void NetworkChangeNotifierImpl::OnNetworkConfigChange(CFArrayRef changed_keys) {
156 for (CFIndex i = 0; i < CFArrayGetCount(changed_keys); ++i) {
157 CFStringRef key = static_cast<CFStringRef>(
158 CFArrayGetValueAtIndex(changed_keys, i));
159 if (CFStringHasSuffix(key, kSCEntNetIPv4) ||
160 CFStringHasSuffix(key, kSCEntNetIPv6)) {
161 notifier_loop_->PostTask(
162 FROM_HERE,
163 NewRunnableMethod(
164 this,
165 &NetworkChangeNotifierImpl::OnIPAddressChanged));
166 } else if (CFStringHasSuffix(key, kSCEntNetInterface)) {
167 // TODO(willchan): Does not appear to be working. Look into this.
168 // Perhaps this isn't needed anyway.
169 } else {
170 NOTREACHED();
175 void NetworkChangeNotifierImpl::OnIPAddressChanged() {
176 // If |notifier_| doesn't exist, then that means we're shutting down, so
177 // notifications are all cancelled.
178 if (notifier_)
179 notifier_->OnIPAddressChanged();
182 class NetworkChangeNotifierThread : public base::Thread {
183 public:
184 NetworkChangeNotifierThread(MessageLoop* notifier_loop,
185 NetworkChangeNotifierMac* notifier);
186 ~NetworkChangeNotifierThread();
188 protected:
189 virtual void Init();
191 private:
192 MessageLoop* const notifier_loop_;
193 NetworkChangeNotifierMac* const notifier_;
194 scoped_refptr<NetworkChangeNotifierImpl> notifier_impl_;
196 DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierThread);
199 NetworkChangeNotifierThread::NetworkChangeNotifierThread(
200 MessageLoop* notifier_loop, NetworkChangeNotifierMac* notifier)
201 : base::Thread("NetworkChangeNotifier"),
202 notifier_loop_(notifier_loop),
203 notifier_(notifier) {}
205 NetworkChangeNotifierThread::~NetworkChangeNotifierThread() {
206 notifier_impl_->Shutdown();
207 Stop();
210 // Note that |notifier_impl_| is initialized on the network change
211 // notifier thread, not whatever thread constructs the
212 // NetworkChangeNotifierThread object. This is important, because this thread
213 // is the one that has a CFRunLoop.
214 void NetworkChangeNotifierThread::Init() {
215 notifier_impl_ =
216 new NetworkChangeNotifierImpl(notifier_loop_, notifier_);
219 } // namespace
221 NetworkChangeNotifierMac::NetworkChangeNotifierMac()
222 : notifier_thread_(NULL),
223 method_factory_(this) {
224 // TODO(willchan): Look to see if there's a better signal for when it's ok to
225 // initialize this, rather than just delaying it by a fixed time.
226 const int kNotifierThreadInitializationDelayMS = 1000;
227 MessageLoop* loop = MessageLoop::current();
228 loop->PostDelayedTask(
229 FROM_HERE,
230 method_factory_.NewRunnableMethod(
231 &NetworkChangeNotifierMac::InitializeNotifierThread, loop),
232 kNotifierThreadInitializationDelayMS);
235 NetworkChangeNotifierMac::~NetworkChangeNotifierMac() {}
237 void NetworkChangeNotifierMac::InitializeNotifierThread(MessageLoop* loop) {
238 notifier_thread_.reset(new NetworkChangeNotifierThread(loop, this));
239 base::Thread::Options thread_options;
240 thread_options.message_loop_type = MessageLoop::TYPE_UI;
241 notifier_thread_->StartWithOptions(thread_options);
244 } // namespace net