summaryrefslogtreecommitdiffstats
path: root/net/base/network_change_notifier_mac.cc
diff options
context:
space:
mode:
Diffstat (limited to 'net/base/network_change_notifier_mac.cc')
-rw-r--r--net/base/network_change_notifier_mac.cc229
1 files changed, 229 insertions, 0 deletions
diff --git a/net/base/network_change_notifier_mac.cc b/net/base/network_change_notifier_mac.cc
new file mode 100644
index 0000000..81fe02d
--- /dev/null
+++ b/net/base/network_change_notifier_mac.cc
@@ -0,0 +1,229 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// There are three classes involved here. There's NetworkChangeNotifierMac,
+// which is the Mac specific implementation of NetworkChangeNotifier. It is the
+// class with which clients can register themselves as network change
+// observers. There's NetworkChangeNotifierThread, which is a base::Thread
+// subclass of MessageLoop::TYPE_UI (since it needs a CFRunLoop) that contains
+// the NetworkChangeNotifierImpl. NetworkChangeNotifierImpl is the object
+// that receives the actual OS X notifications and posts them to the
+// NetworkChangeNotifierMac's message loop, so that NetworkChangeNotifierMac
+// can notify all its observers.
+//
+// When NetworkChangeNotifierMac is being deleted, it will delete the
+// NetworkChangeNotifierThread, which will Stop() it and also delete the
+// NetworkChangeNotifierImpl. Therefore, NetworkChangeNotifierImpl and
+// NetworkChangeNotifierThread's lifetimes generally begin after and end before
+// NetworkChangeNotifierMac. There is an edge case where a notification task
+// gets posted to the IO thread, thereby maintaining a reference to
+// NetworkChangeNotifierImpl beyond the lifetime of NetworkChangeNotifierThread.
+// In this case, the notification is cancelled, and NetworkChangeNotifierImpl
+// will be deleted once all notification tasks that reference it have been run.
+
+#include "net/base/network_change_notifier_mac.h"
+#include <SystemConfiguration/SCDynamicStore.h>
+#include <SystemConfiguration/SCDynamicStoreKey.h>
+#include <SystemConfiguration/SCSchemaDefinitions.h>
+#include <algorithm>
+#include "base/logging.h"
+#include "base/scoped_cftyperef.h"
+#include "base/thread.h"
+
+namespace net {
+
+namespace {
+
+// NetworkChangeNotifierImpl should be created on a thread with a CFRunLoop,
+// since it requires one to pump notifications. However, it also runs some
+// methods on |notifier_loop_|, because it cannot post calls to |notifier_|
+// since NetworkChangeNotifier is not ref counted in a thread safe manner.
+class NetworkChangeNotifierImpl
+ : public base::RefCountedThreadSafe<NetworkChangeNotifierImpl> {
+ public:
+ NetworkChangeNotifierImpl(MessageLoop* notifier_loop,
+ NetworkChangeNotifierMac* notifier);
+
+ void Shutdown();
+
+ private:
+ friend class base::RefCountedThreadSafe<NetworkChangeNotifierImpl>;
+ ~NetworkChangeNotifierImpl();
+
+ static void DynamicStoreCallback(SCDynamicStoreRef /* store */,
+ CFArrayRef changed_keys,
+ void* config);
+
+ void OnNetworkConfigChange(CFArrayRef changed_keys);
+
+ // Runs on |notifier_loop_|.
+ void OnIPAddressChanged();
+
+ // Raw pointers. Note that |notifier_| _must_ outlive the
+ // NetworkChangeNotifierImpl. For lifecycle management details, read the
+ // comment at the top of the file.
+ MessageLoop* const notifier_loop_;
+ NetworkChangeNotifierMac* notifier_;
+
+ scoped_cftyperef<CFRunLoopSourceRef> source_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierImpl);
+};
+
+NetworkChangeNotifierImpl::NetworkChangeNotifierImpl(
+ MessageLoop* notifier_loop, NetworkChangeNotifierMac* notifier)
+ : notifier_loop_(notifier_loop),
+ notifier_(notifier) {
+ DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type());
+ SCDynamicStoreContext context = {
+ 0, // Version 0.
+ this, // User data.
+ NULL, // This is not reference counted. No retain function.
+ NULL, // This is not reference counted. No release function.
+ NULL, // No description for this.
+ };
+
+ // Get a reference to the dynamic store.
+ scoped_cftyperef<SCDynamicStoreRef> store(
+ SCDynamicStoreCreate(NULL /* use default allocator */,
+ CFSTR("org.chromium"),
+ DynamicStoreCallback, &context));
+
+ // Create a run loop source for the dynamic store.
+ source_.reset(SCDynamicStoreCreateRunLoopSource(
+ NULL /* use default allocator */,
+ store.get(),
+ 0 /* 0 sounds like a fine source order to me! */));
+
+ // Add the run loop source to the current run loop.
+ CFRunLoopAddSource(CFRunLoopGetCurrent(),
+ source_.get(),
+ kCFRunLoopCommonModes);
+
+ // Set up the notification keys.
+ scoped_cftyperef<CFMutableArrayRef> notification_keys(
+ CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
+
+ // Monitor interface changes.
+ scoped_cftyperef<CFStringRef> key(
+ SCDynamicStoreKeyCreateNetworkGlobalEntity(
+ NULL /* default allocator */, kSCDynamicStoreDomainState,
+ kSCEntNetInterface));
+ CFArrayAppendValue(notification_keys.get(), key.get());
+
+ // Monitor IP address changes.
+
+ key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
+ NULL /* default allocator */, kSCDynamicStoreDomainState,
+ kSCEntNetIPv4));
+ CFArrayAppendValue(notification_keys.get(), key.get());
+
+ key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
+ NULL /* default allocator */, kSCDynamicStoreDomainState,
+ kSCEntNetIPv6));
+ CFArrayAppendValue(notification_keys.get(), key.get());
+
+ // Ok, let's ask for notifications!
+ // TODO(willchan): Figure out a proper way to handle this rather than crash.
+ CHECK(SCDynamicStoreSetNotificationKeys(
+ store.get(), notification_keys.get(), NULL));
+}
+
+NetworkChangeNotifierImpl::~NetworkChangeNotifierImpl() {
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
+ source_.get(),
+ kCFRunLoopCommonModes);
+}
+
+void NetworkChangeNotifierImpl::Shutdown() {
+ CHECK(notifier_);
+ notifier_ = NULL;
+}
+
+// static
+void NetworkChangeNotifierImpl::DynamicStoreCallback(
+ SCDynamicStoreRef /* store */,
+ CFArrayRef changed_keys,
+ void* config) {
+ NetworkChangeNotifierImpl* net_config =
+ static_cast<NetworkChangeNotifierImpl*>(config);
+ net_config->OnNetworkConfigChange(changed_keys);
+}
+
+void NetworkChangeNotifierImpl::OnNetworkConfigChange(CFArrayRef changed_keys) {
+ for (CFIndex i = 0; i < CFArrayGetCount(changed_keys); ++i) {
+ CFStringRef key = static_cast<CFStringRef>(
+ CFArrayGetValueAtIndex(changed_keys, i));
+ if (CFStringHasSuffix(key, kSCEntNetIPv4) ||
+ CFStringHasSuffix(key, kSCEntNetIPv6)) {
+ notifier_loop_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &NetworkChangeNotifierImpl::OnIPAddressChanged));
+ } else if (CFStringHasSuffix(key, kSCEntNetInterface)) {
+ // TODO(willchan): Does not appear to be working. Look into this.
+ // Perhaps this isn't needed anyway.
+ } else {
+ NOTREACHED();
+ }
+ }
+}
+
+void NetworkChangeNotifierImpl::OnIPAddressChanged() {
+ // If |notifier_| doesn't exist, then that means we're shutting down, so
+ // notifications are all cancelled.
+ if (notifier_)
+ notifier_->OnIPAddressChanged();
+}
+
+class NetworkChangeNotifierThread : public base::Thread {
+ public:
+ NetworkChangeNotifierThread(MessageLoop* notifier_loop,
+ NetworkChangeNotifierMac* notifier);
+ ~NetworkChangeNotifierThread();
+
+ protected:
+ virtual void Init();
+
+ private:
+ MessageLoop* const notifier_loop_;
+ NetworkChangeNotifierMac* const notifier_;
+ scoped_refptr<NetworkChangeNotifierImpl> notifier_impl_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierThread);
+};
+
+NetworkChangeNotifierThread::NetworkChangeNotifierThread(
+ MessageLoop* notifier_loop, NetworkChangeNotifierMac* notifier)
+ : base::Thread("NetworkChangeNotifier"),
+ notifier_loop_(notifier_loop),
+ notifier_(notifier) {}
+
+NetworkChangeNotifierThread::~NetworkChangeNotifierThread() {
+ notifier_impl_->Shutdown();
+}
+
+// Note that |notifier_impl_| is initialized on the network change
+// notifier thread, not whatever thread constructs the
+// NetworkChangeNotifierThread object. This is important, because this thread
+// is the one that has a CFRunLoop.
+void NetworkChangeNotifierThread::Init() {
+ notifier_impl_ =
+ new NetworkChangeNotifierImpl(notifier_loop_, notifier_);
+}
+
+} // namespace
+
+NetworkChangeNotifierMac::NetworkChangeNotifierMac()
+ : notifier_thread_(
+ new NetworkChangeNotifierThread(MessageLoop::current(), this)) {
+ base::Thread::Options thread_options;
+ thread_options.message_loop_type = MessageLoop::TYPE_UI;
+ notifier_thread_->StartWithOptions(thread_options);
+}
+
+NetworkChangeNotifierMac::~NetworkChangeNotifierMac() {}
+
+} // namespace net