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