1 /* 2 * Copyright 2012, The Android Open Source Project 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 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "GeolocationClientImpl.h" 28 29 #include <Frame.h> 30 #include <Page.h> 31 #include <GeolocationController.h> 32 #include <GeolocationError.h> 33 #include <GeolocationPosition.h> 34 #include <WebViewCore.h> 35 #if PLATFORM(ANDROID) 36 // Required for sim-eng build 37 #include <math.h> 38 #endif 39 #include <wtf/CurrentTime.h> 40 41 using WebCore::Geolocation; 42 using WebCore::GeolocationError; 43 using WebCore::GeolocationPosition; 44 using WebCore::Timer; 45 46 using namespace std; 47 48 namespace { 49 50 bool isPositionMovement(GeolocationPosition* position1, GeolocationPosition* position2) 51 { 52 // For the small distances in which we are likely concerned, it's reasonable 53 // to approximate the distance between the two positions as the sum of the 54 // differences in latitude and longitude. 55 double delta = fabs(position1->latitude() - position2->latitude()) + fabs(position1->longitude() - position2->longitude()); 56 // Approximate conversion from degrees of arc to metres. 57 delta *= 60 * 1852; 58 // The threshold is when the distance between the two positions exceeds the 59 // worse (larger) of the two accuracies. 60 int maxAccuracy = max(position1->accuracy(), position2->accuracy()); 61 return delta > maxAccuracy; 62 } 63 64 bool isPositionMoreAccurate(GeolocationPosition* position1, GeolocationPosition* position2) 65 { 66 return position2->accuracy() < position1->accuracy(); 67 } 68 69 bool isPositionMoreTimely(GeolocationPosition* position1) 70 { 71 double currentTime = WTF::currentTime(); 72 double maximumAge = 10 * 60; // 10 minutes 73 return currentTime - position1->timestamp() > maximumAge; 74 } 75 76 } // anonymous namespace 77 78 namespace android { 79 80 GeolocationClientImpl::GeolocationClientImpl(WebViewCore* webViewCore) 81 : m_webViewCore(webViewCore) 82 , m_timer(this, &GeolocationClientImpl::timerFired) 83 , m_isSuspended(false) 84 , m_useGps(false) 85 { 86 } 87 88 GeolocationClientImpl::~GeolocationClientImpl() 89 { 90 } 91 92 void GeolocationClientImpl::geolocationDestroyed() 93 { 94 // Lifetime is managed by GeolocationManager. 95 } 96 97 void GeolocationClientImpl::startUpdating() 98 { 99 // This method is called every time a new watch or one-shot position request 100 // is started. If we already have a position or an error, call back 101 // immediately. 102 if (m_lastPosition || m_lastError) { 103 m_timer.startOneShot(0); 104 } 105 106 // Lazilly create the Java object. 107 bool haveJavaBridge = m_javaBridge; 108 if (!haveJavaBridge) 109 m_javaBridge.set(new GeolocationServiceBridge(this, m_webViewCore)); 110 ASSERT(m_javaBridge); 111 112 // Set whether to use GPS before we start the implementation. 113 m_javaBridge->setEnableGps(m_useGps); 114 115 // If we're suspended, don't start the service. It will be started when we 116 // get the call to resume(). 117 if (!haveJavaBridge && !m_isSuspended) 118 m_javaBridge->start(); 119 } 120 121 void GeolocationClientImpl::stopUpdating() 122 { 123 // TODO: It would be good to re-use the Java bridge object. 124 m_javaBridge.clear(); 125 m_useGps = false; 126 // Reset last position and error to make sure that we always try to get a 127 // new position from the client when a request is first made. 128 m_lastPosition = 0; 129 m_lastError = 0; 130 131 if (m_timer.isActive()) 132 m_timer.stop(); 133 } 134 135 void GeolocationClientImpl::setEnableHighAccuracy(bool enableHighAccuracy) 136 { 137 // On Android, high power == GPS. 138 m_useGps = enableHighAccuracy; 139 if (m_javaBridge) 140 m_javaBridge->setEnableGps(m_useGps); 141 } 142 143 GeolocationPosition* GeolocationClientImpl::lastPosition() 144 { 145 return m_lastPosition.get(); 146 } 147 148 void GeolocationClientImpl::requestPermission(Geolocation* geolocation) 149 { 150 permissions()->queryPermissionState(geolocation->frame()); 151 } 152 153 void GeolocationClientImpl::cancelPermissionRequest(Geolocation* geolocation) 154 { 155 permissions()->cancelPermissionStateQuery(geolocation->frame()); 156 } 157 158 // Note that there is no guarantee that subsequent calls to this method offer a 159 // more accurate or updated position. 160 void GeolocationClientImpl::newPositionAvailable(PassRefPtr<GeolocationPosition> position) 161 { 162 ASSERT(position); 163 if (!m_lastPosition 164 || isPositionMovement(m_lastPosition.get(), position.get()) 165 || isPositionMoreAccurate(m_lastPosition.get(), position.get()) 166 || isPositionMoreTimely(m_lastPosition.get())) { 167 m_lastPosition = position; 168 // Remove the last error. 169 m_lastError = 0; 170 m_webViewCore->mainFrame()->page()->geolocationController()->positionChanged(m_lastPosition.get()); 171 } 172 } 173 174 void GeolocationClientImpl::newErrorAvailable(PassRefPtr<WebCore::GeolocationError> error) 175 { 176 ASSERT(error); 177 // We leave the last position 178 m_lastError = error; 179 m_webViewCore->mainFrame()->page()->geolocationController()->errorOccurred(m_lastError.get()); 180 } 181 182 void GeolocationClientImpl::suspend() 183 { 184 m_isSuspended = true; 185 if (m_javaBridge) 186 m_javaBridge->stop(); 187 } 188 189 void GeolocationClientImpl::resume() 190 { 191 m_isSuspended = false; 192 if (m_javaBridge) 193 m_javaBridge->start(); 194 } 195 196 void GeolocationClientImpl::resetTemporaryPermissionStates() 197 { 198 permissions()->resetTemporaryPermissionStates(); 199 } 200 201 void GeolocationClientImpl::providePermissionState(String origin, bool allow, bool remember) 202 { 203 permissions()->providePermissionState(origin, allow, remember); 204 } 205 206 GeolocationPermissions* GeolocationClientImpl::permissions() const 207 { 208 if (!m_permissions) 209 m_permissions = new GeolocationPermissions(m_webViewCore); 210 return m_permissions.get(); 211 } 212 213 void GeolocationClientImpl::timerFired(Timer<GeolocationClientImpl>* timer) 214 { 215 ASSERT(&m_timer == timer); 216 ASSERT(m_lastPosition || m_lastError); 217 if (m_lastPosition) 218 m_webViewCore->mainFrame()->page()->geolocationController()->positionChanged(m_lastPosition.get()); 219 else 220 m_webViewCore->mainFrame()->page()->geolocationController()->errorOccurred(m_lastError.get()); 221 } 222 223 } // namespace android 224