This source file includes following definitions.
- CalculateReachability
- CalculateConnectionType
- DnsConfigServiceThread
- DnsConfigServiceThread
- Init
- CleanUp
- dns_config_service_thread_
- NetworkChangeCalculatorParamsMac
- GetCurrentConnectionType
- Init
- StartReachabilityNotifications
- SetDynamicStoreNotificationKeys
- OnNetworkConfigChange
- SetInitialConnectionType
- StartReachabilityNotifications
- SetDynamicStoreNotificationKeys
- OnNetworkConfigChange
- ReachabilityCallback
#include "net/base/network_change_notifier_mac.h"
#include <netinet/in.h>
#include <resolv.h>
#include "base/basictypes.h"
#include "base/threading/thread.h"
#include "net/dns/dns_config_service.h"
namespace net {
static bool CalculateReachability(SCNetworkConnectionFlags flags) {
bool reachable = flags & kSCNetworkFlagsReachable;
bool connection_required = flags & kSCNetworkFlagsConnectionRequired;
return reachable && !connection_required;
}
NetworkChangeNotifier::ConnectionType CalculateConnectionType(
SCNetworkConnectionFlags flags) {
bool reachable = CalculateReachability(flags);
if (reachable) {
#if defined(OS_IOS)
return (flags & kSCNetworkReachabilityFlagsIsWWAN) ?
NetworkChangeNotifier::CONNECTION_3G :
NetworkChangeNotifier::CONNECTION_WIFI;
#else
return NetworkChangeNotifier::CONNECTION_UNKNOWN;
#endif
} else {
return NetworkChangeNotifier::CONNECTION_NONE;
}
}
class NetworkChangeNotifierMac::DnsConfigServiceThread : public base::Thread {
public:
DnsConfigServiceThread() : base::Thread("DnsConfigService") {}
virtual ~DnsConfigServiceThread() {
Stop();
}
virtual void Init() OVERRIDE {
service_ = DnsConfigService::CreateSystemService();
service_->WatchConfig(base::Bind(&NetworkChangeNotifier::SetDnsConfig));
}
virtual void CleanUp() OVERRIDE {
service_.reset();
}
private:
scoped_ptr<DnsConfigService> service_;
DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceThread);
};
NetworkChangeNotifierMac::NetworkChangeNotifierMac()
: NetworkChangeNotifier(NetworkChangeCalculatorParamsMac()),
connection_type_(CONNECTION_UNKNOWN),
connection_type_initialized_(false),
initial_connection_type_cv_(&connection_type_lock_),
forwarder_(this),
dns_config_service_thread_(new DnsConfigServiceThread()) {
config_watcher_.reset(new NetworkConfigWatcherMac(&forwarder_));
dns_config_service_thread_->StartWithOptions(
base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
}
NetworkChangeNotifierMac::~NetworkChangeNotifierMac() {
config_watcher_.reset();
if (reachability_.get() && run_loop_.get()) {
SCNetworkReachabilityUnscheduleFromRunLoop(reachability_.get(),
run_loop_.get(),
kCFRunLoopCommonModes);
}
}
NetworkChangeNotifier::NetworkChangeCalculatorParams
NetworkChangeNotifierMac::NetworkChangeCalculatorParamsMac() {
NetworkChangeCalculatorParams params;
params.ip_address_offline_delay_ = base::TimeDelta::FromMilliseconds(500);
params.ip_address_online_delay_ = base::TimeDelta::FromMilliseconds(500);
params.connection_type_offline_delay_ =
base::TimeDelta::FromMilliseconds(1000);
params.connection_type_online_delay_ = base::TimeDelta::FromMilliseconds(500);
return params;
}
NetworkChangeNotifier::ConnectionType
NetworkChangeNotifierMac::GetCurrentConnectionType() const {
base::AutoLock lock(connection_type_lock_);
while (!connection_type_initialized_) {
initial_connection_type_cv_.Wait();
}
return connection_type_;
}
void NetworkChangeNotifierMac::Forwarder::Init() {
net_config_watcher_->SetInitialConnectionType();
}
void NetworkChangeNotifierMac::Forwarder::StartReachabilityNotifications() {
net_config_watcher_->StartReachabilityNotifications();
}
void NetworkChangeNotifierMac::Forwarder::SetDynamicStoreNotificationKeys(
SCDynamicStoreRef store) {
net_config_watcher_->SetDynamicStoreNotificationKeys(store);
}
void NetworkChangeNotifierMac::Forwarder::OnNetworkConfigChange(
CFArrayRef changed_keys) {
net_config_watcher_->OnNetworkConfigChange(changed_keys);
}
void NetworkChangeNotifierMac::SetInitialConnectionType() {
struct sockaddr_in addr = {0};
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
reachability_.reset(SCNetworkReachabilityCreateWithAddress(
kCFAllocatorDefault, reinterpret_cast<struct sockaddr*>(&addr)));
SCNetworkConnectionFlags flags;
ConnectionType connection_type = CONNECTION_UNKNOWN;
if (SCNetworkReachabilityGetFlags(reachability_, &flags)) {
connection_type = CalculateConnectionType(flags);
} else {
LOG(ERROR) << "Could not get initial network connection type,"
<< "assuming online.";
}
{
base::AutoLock lock(connection_type_lock_);
connection_type_ = connection_type;
connection_type_initialized_ = true;
initial_connection_type_cv_.Signal();
}
}
void NetworkChangeNotifierMac::StartReachabilityNotifications() {
run_loop_.reset(CFRunLoopGetCurrent());
CFRetain(run_loop_.get());
DCHECK(reachability_);
SCNetworkReachabilityContext reachability_context = {
0,
this,
NULL,
NULL,
NULL
};
if (!SCNetworkReachabilitySetCallback(
reachability_,
&NetworkChangeNotifierMac::ReachabilityCallback,
&reachability_context)) {
LOG(DFATAL) << "Could not set network reachability callback";
reachability_.reset();
} else if (!SCNetworkReachabilityScheduleWithRunLoop(reachability_,
run_loop_,
kCFRunLoopCommonModes)) {
LOG(DFATAL) << "Could not schedule network reachability on run loop";
reachability_.reset();
}
}
void NetworkChangeNotifierMac::SetDynamicStoreNotificationKeys(
SCDynamicStoreRef store) {
#if defined(OS_IOS)
NOTREACHED();
#else
base::ScopedCFTypeRef<CFMutableArrayRef> notification_keys(
CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
base::ScopedCFTypeRef<CFStringRef> key(
SCDynamicStoreKeyCreateNetworkGlobalEntity(
NULL, kSCDynamicStoreDomainState, kSCEntNetInterface));
CFArrayAppendValue(notification_keys.get(), key.get());
key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4));
CFArrayAppendValue(notification_keys.get(), key.get());
key.reset(SCDynamicStoreKeyCreateNetworkGlobalEntity(
NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6));
CFArrayAppendValue(notification_keys.get(), key.get());
bool ret = SCDynamicStoreSetNotificationKeys(
store, notification_keys.get(), NULL);
CHECK(ret);
#endif
}
void NetworkChangeNotifierMac::OnNetworkConfigChange(CFArrayRef changed_keys) {
#if defined(OS_IOS)
NOTREACHED();
#else
DCHECK_EQ(run_loop_.get(), CFRunLoopGetCurrent());
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)) {
NotifyObserversOfIPAddressChange();
return;
}
if (CFStringHasSuffix(key, kSCEntNetInterface)) {
} else {
NOTREACHED();
}
}
#endif
}
void NetworkChangeNotifierMac::ReachabilityCallback(
SCNetworkReachabilityRef target,
SCNetworkConnectionFlags flags,
void* notifier) {
NetworkChangeNotifierMac* notifier_mac =
static_cast<NetworkChangeNotifierMac*>(notifier);
DCHECK_EQ(notifier_mac->run_loop_.get(), CFRunLoopGetCurrent());
ConnectionType new_type = CalculateConnectionType(flags);
ConnectionType old_type;
{
base::AutoLock lock(notifier_mac->connection_type_lock_);
old_type = notifier_mac->connection_type_;
notifier_mac->connection_type_ = new_type;
}
if (old_type != new_type)
NotifyObserversOfConnectionTypeChange();
#if defined(OS_IOS)
if (new_type != CONNECTION_NONE)
NotifyObserversOfIPAddressChange();
#endif
}
}