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
updateState()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
dynamicStoreCallback(SCDynamicStoreRef,CFArrayRef,void * info)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
networkStateChangeTimerFired(Timer<NetworkStateNotifier> *)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
NetworkStateNotifier()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