1 // Copyright (c) 2010 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.
4
5 #include "net/base/network_config_watcher_mac.h"
6
7 #include <SystemConfiguration/SCDynamicStoreKey.h>
8 #include <SystemConfiguration/SCSchemaDefinitions.h>
9 #include <algorithm>
10
11 #include "base/compiler_specific.h"
12 #include "base/threading/thread.h"
13 #include "base/threading/thread_restrictions.h"
14 #include "base/mac/scoped_cftyperef.h"
15
16 namespace net {
17
18 namespace {
19
20 // Called back by OS. Calls OnNetworkConfigChange().
DynamicStoreCallback(SCDynamicStoreRef,CFArrayRef changed_keys,void * config_delegate)21 void DynamicStoreCallback(SCDynamicStoreRef /* store */,
22 CFArrayRef changed_keys,
23 void* config_delegate) {
24 NetworkConfigWatcherMac::Delegate* net_config_delegate =
25 static_cast<NetworkConfigWatcherMac::Delegate*>(config_delegate);
26 net_config_delegate->OnNetworkConfigChange(changed_keys);
27 }
28
29 class NetworkConfigWatcherMacThread : public base::Thread {
30 public:
31 NetworkConfigWatcherMacThread(NetworkConfigWatcherMac::Delegate* delegate);
32 virtual ~NetworkConfigWatcherMacThread();
33
34 protected:
35 // base::Thread
36 virtual void Init();
37 virtual void CleanUp();
38
39 private:
40 // The SystemConfiguration calls in this function can lead to contention early
41 // on, so we invoke this function later on in startup to keep it fast.
42 void InitNotifications();
43
44 base::mac::ScopedCFTypeRef<CFRunLoopSourceRef> run_loop_source_;
45 NetworkConfigWatcherMac::Delegate* const delegate_;
46 ScopedRunnableMethodFactory<NetworkConfigWatcherMacThread> method_factory_;
47
48 DISALLOW_COPY_AND_ASSIGN(NetworkConfigWatcherMacThread);
49 };
50
NetworkConfigWatcherMacThread(NetworkConfigWatcherMac::Delegate * delegate)51 NetworkConfigWatcherMacThread::NetworkConfigWatcherMacThread(
52 NetworkConfigWatcherMac::Delegate* delegate)
53 : base::Thread("NetworkConfigWatcher"),
54 delegate_(delegate),
55 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {}
56
~NetworkConfigWatcherMacThread()57 NetworkConfigWatcherMacThread::~NetworkConfigWatcherMacThread() {
58 // Allow IO because Stop() calls PlatformThread::Join(), which is a blocking
59 // operation. This is expected during shutdown.
60 base::ThreadRestrictions::ScopedAllowIO allow_io;
61
62 Stop();
63 }
64
Init()65 void NetworkConfigWatcherMacThread::Init() {
66 // Disallow IO to make sure NetworkConfigWatcherMacThread's helper thread does
67 // not perform blocking operations.
68 base::ThreadRestrictions::SetIOAllowed(false);
69
70 // TODO(willchan): Look to see if there's a better signal for when it's ok to
71 // initialize this, rather than just delaying it by a fixed time.
72 const int kInitializationDelayMS = 1000;
73 message_loop()->PostDelayedTask(
74 FROM_HERE,
75 method_factory_.NewRunnableMethod(
76 &NetworkConfigWatcherMacThread::InitNotifications),
77 kInitializationDelayMS);
78 }
79
CleanUp()80 void NetworkConfigWatcherMacThread::CleanUp() {
81 if (!run_loop_source_.get())
82 return;
83
84 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), run_loop_source_.get(),
85 kCFRunLoopCommonModes);
86 run_loop_source_.reset();
87 }
88
InitNotifications()89 void NetworkConfigWatcherMacThread::InitNotifications() {
90 // Add a run loop source for a dynamic store to the current run loop.
91 SCDynamicStoreContext context = {
92 0, // Version 0.
93 delegate_, // User data.
94 NULL, // This is not reference counted. No retain function.
95 NULL, // This is not reference counted. No release function.
96 NULL, // No description for this.
97 };
98 base::mac::ScopedCFTypeRef<SCDynamicStoreRef> store(SCDynamicStoreCreate(
99 NULL, CFSTR("org.chromium"), DynamicStoreCallback, &context));
100 run_loop_source_.reset(SCDynamicStoreCreateRunLoopSource(
101 NULL, store.get(), 0));
102 CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source_.get(),
103 kCFRunLoopCommonModes);
104
105 // Set up notifications for interface and IP address changes.
106 delegate_->SetDynamicStoreNotificationKeys(store.get());
107 }
108
109 } // namespace
110
NetworkConfigWatcherMac(Delegate * delegate)111 NetworkConfigWatcherMac::NetworkConfigWatcherMac(Delegate* delegate)
112 : notifier_thread_(new NetworkConfigWatcherMacThread(delegate)) {
113 // We create this notifier thread because the notification implementation
114 // needs a thread with a CFRunLoop, and there's no guarantee that
115 // MessageLoop::current() meets that criterion.
116 base::Thread::Options thread_options(MessageLoop::TYPE_UI, 0);
117 notifier_thread_->StartWithOptions(thread_options);
118 }
119
~NetworkConfigWatcherMac()120 NetworkConfigWatcherMac::~NetworkConfigWatcherMac() {}
121
122 } // namespace net
123