network_change_notifier_mac.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
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_change_notifier_mac.h" 6 7#include <SystemConfiguration/SCDynamicStoreKey.h> 8#include <SystemConfiguration/SCSchemaDefinitions.h> 9#include <algorithm> 10 11#include "base/thread.h" 12 13// We only post tasks to a child thread we own, so we don't need refcounting. 14DISABLE_RUNNABLE_METHOD_REFCOUNT(net::NetworkChangeNotifierMac); 15 16namespace net { 17 18NetworkChangeNotifierMac::NetworkChangeNotifierMac() 19 : notifier_thread_(new base::Thread("NetworkChangeNotifier")) { 20 // We create this notifier thread because the notification implementation 21 // needs a thread with a CFRunLoop, and there's no guarantee that 22 // MessageLoop::current() meets that criterion. 23 base::Thread::Options thread_options(MessageLoop::TYPE_UI, 0); 24 notifier_thread_->StartWithOptions(thread_options); 25 // TODO(willchan): Look to see if there's a better signal for when it's ok to 26 // initialize this, rather than just delaying it by a fixed time. 27 const int kNotifierThreadInitializationDelayMS = 1000; 28 notifier_thread_->message_loop()->PostDelayedTask(FROM_HERE, 29 NewRunnableMethod(this, &NetworkChangeNotifierMac::Init), 30 kNotifierThreadInitializationDelayMS); 31} 32 33NetworkChangeNotifierMac::~NetworkChangeNotifierMac() { 34 // We don't need to explicitly Stop(), but doing so allows us to sanity- 35 // check that the notifier thread shut down properly. 36 notifier_thread_->Stop(); 37 DCHECK(run_loop_source_ == NULL); 38} 39 40// static 41void NetworkChangeNotifierMac::DynamicStoreCallback( 42 SCDynamicStoreRef /* store */, 43 CFArrayRef changed_keys, 44 void* config) { 45 NetworkChangeNotifierMac* net_config = 46 static_cast<NetworkChangeNotifierMac*>(config); 47 net_config->OnNetworkConfigChange(changed_keys); 48} 49 50void NetworkChangeNotifierMac::WillDestroyCurrentMessageLoop() { 51 DCHECK(notifier_thread_ != NULL); 52 // We can't check the notifier_thread_'s message_loop(), as it's now 0. 53 // DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current()); 54 55 DCHECK(run_loop_source_ != NULL); 56 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), run_loop_source_.get(), 57 kCFRunLoopCommonModes); 58 run_loop_source_.reset(); 59} 60 61void NetworkChangeNotifierMac::Init() { 62 DCHECK(notifier_thread_ != NULL); 63 DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current()); 64 65 // Add a run loop source for a dynamic store to the current run loop. 66 SCDynamicStoreContext context = { 67 0, // Version 0. 68 this, // User data. 69 NULL, // This is not reference counted. No retain function. 70 NULL, // This is not reference counted. No release function. 71 NULL, // No description for this. 72 }; 73 scoped_cftyperef<SCDynamicStoreRef> store(SCDynamicStoreCreate( 74 NULL, CFSTR("org.chromium"), DynamicStoreCallback, &context)); 75 run_loop_source_.reset(SCDynamicStoreCreateRunLoopSource( 76 NULL, store.get(), 0)); 77 CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source_.get(), 78 kCFRunLoopCommonModes); 79 80 // Set up notifications for interface and IP address changes. 81 scoped_cftyperef<CFMutableArrayRef> notification_keys( 82 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks)); 83 scoped_cftyperef<CFStringRef> key(SCDynamicStoreKeyCreateNetworkGlobalEntity( 84 NULL, kSCDynamicStoreDomainState, kSCEntNetInterface)); 85 CFArrayAppendValue(notification_keys.get(), key.get()); 86 key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity( 87 NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4)); 88 CFArrayAppendValue(notification_keys.get(), key.get()); 89 key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity( 90 NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6)); 91 CFArrayAppendValue(notification_keys.get(), key.get()); 92 93 // Set the notification keys. This starts us receiving notifications. 94 bool ret = SCDynamicStoreSetNotificationKeys( 95 store.get(), notification_keys.get(), NULL); 96 // TODO(willchan): Figure out a proper way to handle this rather than crash. 97 CHECK(ret); 98 99 MessageLoop::current()->AddDestructionObserver(this); 100} 101 102void NetworkChangeNotifierMac::OnNetworkConfigChange(CFArrayRef changed_keys) { 103 DCHECK(notifier_thread_ != NULL); 104 DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current()); 105 106 for (CFIndex i = 0; i < CFArrayGetCount(changed_keys); ++i) { 107 CFStringRef key = static_cast<CFStringRef>( 108 CFArrayGetValueAtIndex(changed_keys, i)); 109 if (CFStringHasSuffix(key, kSCEntNetIPv4) || 110 CFStringHasSuffix(key, kSCEntNetIPv6)) { 111 NotifyObserversOfIPAddressChange(); 112 return; 113 } 114 if (CFStringHasSuffix(key, kSCEntNetInterface)) { 115 // TODO(willchan): Does not appear to be working. Look into this. 116 // Perhaps this isn't needed anyway. 117 } else { 118 NOTREACHED(); 119 } 120 } 121} 122 123} // namespace net 124