Home | History | Annotate | Download | only in base
      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