15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/network_change_notifier_mac.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <netinet/in.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <resolv.h>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/threading/thread.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/dns/dns_config_service.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net {
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static bool CalculateReachability(SCNetworkConnectionFlags flags) {
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool reachable = flags & kSCNetworkFlagsReachable;
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool connection_required = flags & kSCNetworkFlagsConnectionRequired;
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return reachable && !connection_required;
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NetworkChangeNotifier::ConnectionType CalculateConnectionType(
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SCNetworkConnectionFlags flags) {
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool reachable = CalculateReachability(flags);
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (reachable) {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_IOS)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return (flags & kSCNetworkReachabilityFlagsIsWWAN) ?
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NetworkChangeNotifier::CONNECTION_3G :
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NetworkChangeNotifier::CONNECTION_WIFI;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(droger): Get something more detailed than CONNECTION_UNKNOWN.
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // http://crbug.com/112937
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NetworkChangeNotifier::CONNECTION_UNKNOWN;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif  // defined(OS_IOS)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NetworkChangeNotifier::CONNECTION_NONE;
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Thread on which we can run DnsConfigService, which requires a TYPE_IO
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// message loop.
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class NetworkChangeNotifierMac::DnsConfigServiceThread : public base::Thread {
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DnsConfigServiceThread() : base::Thread("DnsConfigService") {}
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~DnsConfigServiceThread() {
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Stop();
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void Init() OVERRIDE {
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    service_ = DnsConfigService::CreateSystemService();
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    service_->WatchConfig(base::Bind(&NetworkChangeNotifier::SetDnsConfig));
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void CleanUp() OVERRIDE {
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    service_.reset();
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<DnsConfigService> service_;
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceThread);
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NetworkChangeNotifierMac::NetworkChangeNotifierMac()
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : NetworkChangeNotifier(NetworkChangeCalculatorParamsMac()),
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      connection_type_(CONNECTION_UNKNOWN),
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      connection_type_initialized_(false),
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      initial_connection_type_cv_(&connection_type_lock_),
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      forwarder_(this),
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dns_config_service_thread_(new DnsConfigServiceThread()) {
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Must be initialized after the rest of this object, as it may call back into
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // SetInitialConnectionType().
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  config_watcher_.reset(new NetworkConfigWatcherMac(&forwarder_));
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dns_config_service_thread_->StartWithOptions(
7690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NetworkChangeNotifierMac::~NetworkChangeNotifierMac() {
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Delete the ConfigWatcher to join the notifier thread, ensuring that
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // StartReachabilityNotifications() has an opportunity to run to completion.
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  config_watcher_.reset();
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now that StartReachabilityNotifications() has either run to completion or
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // never run at all, unschedule reachability_ if it was previously scheduled.
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (reachability_.get() && run_loop_.get()) {
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SCNetworkReachabilityUnscheduleFromRunLoop(reachability_.get(),
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                               run_loop_.get(),
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                               kCFRunLoopCommonModes);
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)NetworkChangeNotifier::NetworkChangeCalculatorParams
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)NetworkChangeNotifierMac::NetworkChangeCalculatorParamsMac() {
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NetworkChangeCalculatorParams params;
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Delay values arrived at by simple experimentation and adjusted so as to
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // produce a single signal when switching between network connections.
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  params.ip_address_offline_delay_ = base::TimeDelta::FromMilliseconds(500);
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  params.ip_address_online_delay_ = base::TimeDelta::FromMilliseconds(500);
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  params.connection_type_offline_delay_ =
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::TimeDelta::FromMilliseconds(1000);
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  params.connection_type_online_delay_ = base::TimeDelta::FromMilliseconds(500);
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return params;
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NetworkChangeNotifier::ConnectionType
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NetworkChangeNotifierMac::GetCurrentConnectionType() const {
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::AutoLock lock(connection_type_lock_);
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Make sure the initial connection type is set before returning.
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (!connection_type_initialized_) {
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    initial_connection_type_cv_.Wait();
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return connection_type_;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NetworkChangeNotifierMac::Forwarder::Init()  {
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net_config_watcher_->SetInitialConnectionType();
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NetworkChangeNotifierMac::Forwarder::StartReachabilityNotifications() {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net_config_watcher_->StartReachabilityNotifications();
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NetworkChangeNotifierMac::Forwarder::SetDynamicStoreNotificationKeys(
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SCDynamicStoreRef store)  {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net_config_watcher_->SetDynamicStoreNotificationKeys(store);
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NetworkChangeNotifierMac::Forwarder::OnNetworkConfigChange(
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CFArrayRef changed_keys)  {
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net_config_watcher_->OnNetworkConfigChange(changed_keys);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NetworkChangeNotifierMac::SetInitialConnectionType() {
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Called on notifier thread.
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Try to reach 0.0.0.0. This is the approach taken by Firefox:
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // http://mxr.mozilla.org/mozilla2.0/source/netwerk/system/mac/nsNetworkLinkService.mm
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // From my (adamk) testing on Snow Leopard, 0.0.0.0
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // seems to be reachable if any network connection is available.
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  struct sockaddr_in addr = {0};
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  addr.sin_len = sizeof(addr);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  addr.sin_family = AF_INET;
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  reachability_.reset(SCNetworkReachabilityCreateWithAddress(
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kCFAllocatorDefault, reinterpret_cast<struct sockaddr*>(&addr)));
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SCNetworkConnectionFlags flags;
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ConnectionType connection_type = CONNECTION_UNKNOWN;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (SCNetworkReachabilityGetFlags(reachability_, &flags)) {
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    connection_type = CalculateConnectionType(flags);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Could not get initial network connection type,"
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               << "assuming online.";
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  {
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::AutoLock lock(connection_type_lock_);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    connection_type_ = connection_type;
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    connection_type_initialized_ = true;
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    initial_connection_type_cv_.Signal();
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NetworkChangeNotifierMac::StartReachabilityNotifications() {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Called on notifier thread.
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  run_loop_.reset(CFRunLoopGetCurrent());
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CFRetain(run_loop_.get());
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(reachability_);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SCNetworkReachabilityContext reachability_context = {
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    0,     // version
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this,  // user data
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NULL,  // retain
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NULL,  // release
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NULL   // description
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!SCNetworkReachabilitySetCallback(
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          reachability_,
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          &NetworkChangeNotifierMac::ReachabilityCallback,
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          &reachability_context)) {
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(DFATAL) << "Could not set network reachability callback";
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    reachability_.reset();
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (!SCNetworkReachabilityScheduleWithRunLoop(reachability_,
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       run_loop_,
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       kCFRunLoopCommonModes)) {
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(DFATAL) << "Could not schedule network reachability on run loop";
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    reachability_.reset();
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NetworkChangeNotifierMac::SetDynamicStoreNotificationKeys(
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SCDynamicStoreRef store) {
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_IOS)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // SCDynamicStore API does not exist on iOS.
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTREACHED();
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
199eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::ScopedCFTypeRef<CFMutableArrayRef> notification_keys(
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
201eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::ScopedCFTypeRef<CFStringRef> key(
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SCDynamicStoreKeyCreateNetworkGlobalEntity(
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          NULL, kSCDynamicStoreDomainState, kSCEntNetInterface));
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CFArrayAppendValue(notification_keys.get(), key.get());
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4));
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CFArrayAppendValue(notification_keys.get(), key.get());
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6));
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CFArrayAppendValue(notification_keys.get(), key.get());
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the notification keys.  This starts us receiving notifications.
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool ret = SCDynamicStoreSetNotificationKeys(
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      store, notification_keys.get(), NULL);
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(willchan): Figure out a proper way to handle this rather than crash.
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(ret);
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif  // defined(OS_IOS)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NetworkChangeNotifierMac::OnNetworkConfigChange(CFArrayRef changed_keys) {
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_IOS)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // SCDynamicStore API does not exist on iOS.
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTREACHED();
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(run_loop_.get(), CFRunLoopGetCurrent());
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (CFIndex i = 0; i < CFArrayGetCount(changed_keys); ++i) {
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CFStringRef key = static_cast<CFStringRef>(
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        CFArrayGetValueAtIndex(changed_keys, i));
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (CFStringHasSuffix(key, kSCEntNetIPv4) ||
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        CFStringHasSuffix(key, kSCEntNetIPv6)) {
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NotifyObserversOfIPAddressChange();
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (CFStringHasSuffix(key, kSCEntNetInterface)) {
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(willchan): Does not appear to be working.  Look into this.
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Perhaps this isn't needed anyway.
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif  // defined(OS_IOS)
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NetworkChangeNotifierMac::ReachabilityCallback(
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SCNetworkReachabilityRef target,
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SCNetworkConnectionFlags flags,
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    void* notifier) {
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NetworkChangeNotifierMac* notifier_mac =
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<NetworkChangeNotifierMac*>(notifier);
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(notifier_mac->run_loop_.get(), CFRunLoopGetCurrent());
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ConnectionType new_type = CalculateConnectionType(flags);
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ConnectionType old_type;
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  {
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::AutoLock lock(notifier_mac->connection_type_lock_);
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    old_type = notifier_mac->connection_type_;
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    notifier_mac->connection_type_ = new_type;
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (old_type != new_type)
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NotifyObserversOfConnectionTypeChange();
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_IOS)
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // On iOS, the SCDynamicStore API does not exist, and we use the reachability
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // API to detect IP address changes instead.
268c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  NotifyObserversOfIPAddressChange();
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif  // defined(OS_IOS)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace net
273