1 /* 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "NetworkStateNotifier.h" 28 29 #include <SystemConfiguration/SystemConfiguration.h> 30 31 #ifdef BUILDING_ON_TIGER 32 // This function is available on Tiger, but not declared in the CFRunLoop.h header on Tiger. 33 extern "C" CFRunLoopRef CFRunLoopGetMain(); 34 #endif 35 36 namespace WebCore { 37 38 static const double StateChangeTimerInterval = 2.0; 39 40 void NetworkStateNotifier::updateState() 41 { 42 // Assume that we're offline until proven otherwise. 43 m_isOnLine = false; 44 45 RetainPtr<CFStringRef> str(AdoptCF, SCDynamicStoreKeyCreateNetworkInterface(0, kSCDynamicStoreDomainState)); 46 47 RetainPtr<CFPropertyListRef> propertyList(AdoptCF, SCDynamicStoreCopyValue(m_store.get(), str.get())); 48 49 if (!propertyList) 50 return; 51 52 if (CFGetTypeID(propertyList.get()) != CFDictionaryGetTypeID()) 53 return; 54 55 CFArrayRef netInterfaces = (CFArrayRef)CFDictionaryGetValue((CFDictionaryRef)propertyList.get(), kSCDynamicStorePropNetInterfaces); 56 if (CFGetTypeID(netInterfaces) != CFArrayGetTypeID()) 57 return; 58 59 for (CFIndex i = 0; i < CFArrayGetCount(netInterfaces); i++) { 60 CFStringRef interface = (CFStringRef)CFArrayGetValueAtIndex(netInterfaces, i); 61 if (CFGetTypeID(interface) != CFStringGetTypeID()) 62 continue; 63 64 // Ignore the loopback interface. 65 if (CFStringFind(interface, CFSTR("lo"), kCFCompareAnchored).location != kCFNotFound) 66 continue; 67 68 RetainPtr<CFStringRef> key(AdoptCF, SCDynamicStoreKeyCreateNetworkInterfaceEntity(0, kSCDynamicStoreDomainState, interface, kSCEntNetIPv4)); 69 70 RetainPtr<CFArrayRef> keyList(AdoptCF, SCDynamicStoreCopyKeyList(m_store.get(), key.get())); 71 72 if (keyList && CFArrayGetCount(keyList.get())) { 73 m_isOnLine = true; 74 break; 75 } 76 } 77 } 78 79 void NetworkStateNotifier::dynamicStoreCallback(SCDynamicStoreRef, CFArrayRef, void* info) 80 { 81 NetworkStateNotifier* notifier = static_cast<NetworkStateNotifier*>(info); 82 83 // Calling updateState() could be expensive so we schedule a timer that will do it 84 // when things have cooled down. 85 notifier->m_networkStateChangeTimer.startOneShot(StateChangeTimerInterval); 86 } 87 88 void NetworkStateNotifier::networkStateChangeTimerFired(Timer<NetworkStateNotifier>*) 89 { 90 bool oldOnLine = m_isOnLine; 91 92 updateState(); 93 94 if (m_isOnLine == oldOnLine) 95 return; 96 97 if (m_networkStateChangedFunction) 98 m_networkStateChangedFunction(); 99 } 100 101 NetworkStateNotifier::NetworkStateNotifier() 102 : m_isOnLine(false) 103 , m_networkStateChangedFunction(0) 104 , m_networkStateChangeTimer(this, &NetworkStateNotifier::networkStateChangeTimerFired) 105 { 106 SCDynamicStoreContext context = { 0, this, 0, 0, 0 }; 107 108 m_store.adoptCF(SCDynamicStoreCreate(0, CFSTR("com.apple.WebCore"), dynamicStoreCallback, &context)); 109 if (!m_store) 110 return; 111 112 RetainPtr<CFRunLoopSourceRef> configSource = SCDynamicStoreCreateRunLoopSource(0, m_store.get(), 0); 113 if (!configSource) 114 return; 115 116 CFRunLoopAddSource(CFRunLoopGetMain(), configSource.get(), kCFRunLoopCommonModes); 117 118 RetainPtr<CFMutableArrayRef> keys(AdoptCF, CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks)); 119 RetainPtr<CFMutableArrayRef> patterns(AdoptCF, CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks)); 120 121 RetainPtr<CFStringRef> key; 122 RetainPtr<CFStringRef> pattern; 123 124 key.adoptCF(SCDynamicStoreKeyCreateNetworkGlobalEntity(0, kSCDynamicStoreDomainState, kSCEntNetIPv4)); 125 CFArrayAppendValue(keys.get(), key.get()); 126 127 pattern.adoptCF(SCDynamicStoreKeyCreateNetworkInterfaceEntity(0, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4)); 128 CFArrayAppendValue(patterns.get(), pattern.get()); 129 130 key.adoptCF(SCDynamicStoreKeyCreateNetworkGlobalEntity(0, kSCDynamicStoreDomainState, kSCEntNetDNS)); 131 CFArrayAppendValue(keys.get(), key.get()); 132 133 SCDynamicStoreSetNotificationKeys(m_store.get(), keys.get(), patterns.get()); 134 135 updateState(); 136 } 137 138 } 139