| // Copyright (c) 2012 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. |
| |
| #include "net/base/network_config_watcher_mac.h" |
| |
| #include <algorithm> |
| |
| #include "base/bind.h" |
| #include "base/compiler_specific.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/message_loop.h" |
| #include "base/threading/thread.h" |
| #include "base/threading/thread_restrictions.h" |
| |
| namespace net { |
| |
| namespace { |
| |
| #if !defined(OS_IOS) |
| // Called back by OS. Calls OnNetworkConfigChange(). |
| void DynamicStoreCallback(SCDynamicStoreRef /* store */, |
| CFArrayRef changed_keys, |
| void* config_delegate) { |
| NetworkConfigWatcherMac::Delegate* net_config_delegate = |
| static_cast<NetworkConfigWatcherMac::Delegate*>(config_delegate); |
| net_config_delegate->OnNetworkConfigChange(changed_keys); |
| } |
| #endif // !defined(OS_IOS) |
| |
| class NetworkConfigWatcherMacThread : public base::Thread { |
| public: |
| NetworkConfigWatcherMacThread(NetworkConfigWatcherMac::Delegate* delegate); |
| virtual ~NetworkConfigWatcherMacThread(); |
| |
| protected: |
| // base::Thread |
| virtual void Init() OVERRIDE; |
| virtual void CleanUp() OVERRIDE; |
| |
| private: |
| // The SystemConfiguration calls in this function can lead to contention early |
| // on, so we invoke this function later on in startup to keep it fast. |
| void InitNotifications(); |
| |
| base::mac::ScopedCFTypeRef<CFRunLoopSourceRef> run_loop_source_; |
| NetworkConfigWatcherMac::Delegate* const delegate_; |
| base::WeakPtrFactory<NetworkConfigWatcherMacThread> weak_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(NetworkConfigWatcherMacThread); |
| }; |
| |
| NetworkConfigWatcherMacThread::NetworkConfigWatcherMacThread( |
| NetworkConfigWatcherMac::Delegate* delegate) |
| : base::Thread("NetworkConfigWatcher"), |
| delegate_(delegate), |
| ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {} |
| |
| NetworkConfigWatcherMacThread::~NetworkConfigWatcherMacThread() { |
| // Allow IO because Stop() calls PlatformThread::Join(), which is a blocking |
| // operation. This is expected during shutdown. |
| base::ThreadRestrictions::ScopedAllowIO allow_io; |
| |
| Stop(); |
| } |
| |
| void NetworkConfigWatcherMacThread::Init() { |
| // Disallow IO to make sure NetworkConfigWatcherMacThread's helper thread does |
| // not perform blocking operations. |
| base::ThreadRestrictions::SetIOAllowed(false); |
| |
| delegate_->Init(); |
| |
| // TODO(willchan): Look to see if there's a better signal for when it's ok to |
| // initialize this, rather than just delaying it by a fixed time. |
| const base::TimeDelta kInitializationDelay = base::TimeDelta::FromSeconds(1); |
| message_loop()->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&NetworkConfigWatcherMacThread::InitNotifications, |
| weak_factory_.GetWeakPtr()), |
| kInitializationDelay); |
| } |
| |
| void NetworkConfigWatcherMacThread::CleanUp() { |
| if (!run_loop_source_.get()) |
| return; |
| |
| CFRunLoopRemoveSource(CFRunLoopGetCurrent(), run_loop_source_.get(), |
| kCFRunLoopCommonModes); |
| run_loop_source_.reset(); |
| } |
| |
| void NetworkConfigWatcherMacThread::InitNotifications() { |
| #if !defined(OS_IOS) |
| // SCDynamicStore API does not exist on iOS. |
| // Add a run loop source for a dynamic store to the current run loop. |
| SCDynamicStoreContext context = { |
| 0, // Version 0. |
| delegate_, // 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. |
| }; |
| base::mac::ScopedCFTypeRef<SCDynamicStoreRef> store(SCDynamicStoreCreate( |
| NULL, CFSTR("org.chromium"), DynamicStoreCallback, &context)); |
| run_loop_source_.reset(SCDynamicStoreCreateRunLoopSource( |
| NULL, store.get(), 0)); |
| CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source_.get(), |
| kCFRunLoopCommonModes); |
| #endif // !defined(OS_IOS) |
| |
| // Set up notifications for interface and IP address changes. |
| delegate_->StartReachabilityNotifications(); |
| #if !defined(OS_IOS) |
| delegate_->SetDynamicStoreNotificationKeys(store.get()); |
| #endif // !defined(OS_IOS) |
| } |
| |
| } // namespace |
| |
| NetworkConfigWatcherMac::NetworkConfigWatcherMac(Delegate* delegate) |
| : notifier_thread_(new NetworkConfigWatcherMacThread(delegate)) { |
| // We create this notifier thread because the notification implementation |
| // needs a thread with a CFRunLoop, and there's no guarantee that |
| // MessageLoop::current() meets that criterion. |
| base::Thread::Options thread_options(MessageLoop::TYPE_UI, 0); |
| notifier_thread_->StartWithOptions(thread_options); |
| } |
| |
| NetworkConfigWatcherMac::~NetworkConfigWatcherMac() {} |
| |
| } // namespace net |