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(). 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 53 NetworkConfigWatcherMacThread::NetworkConfigWatcherMacThread( 54 NetworkConfigWatcherMac::Delegate* delegate) 55 : base::Thread("NetworkConfigWatcher"), 56 delegate_(delegate), 57 weak_factory_(this) {} 58 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 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 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 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 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 130 NetworkConfigWatcherMac::~NetworkConfigWatcherMac() {} 131 132 } // namespace net 133