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(). 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 51 NetworkConfigWatcherMacThread::NetworkConfigWatcherMacThread( 52 NetworkConfigWatcherMac::Delegate* delegate) 53 : base::Thread("NetworkConfigWatcher"), 54 delegate_(delegate), 55 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {} 56 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 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 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 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 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 120 NetworkConfigWatcherMac::~NetworkConfigWatcherMac() {} 121 122 } // namespace net 123