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 41using WebCore::Geolocation; 42using WebCore::GeolocationError; 43using WebCore::GeolocationPosition; 44using WebCore::Timer; 45 46using namespace std; 47 48namespace { 49 50bool 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 64bool isPositionMoreAccurate(GeolocationPosition* position1, GeolocationPosition* position2) 65{ 66 return position2->accuracy() < position1->accuracy(); 67} 68 69bool 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 78namespace android { 79 80GeolocationClientImpl::GeolocationClientImpl(WebViewCore* webViewCore) 81 : m_webViewCore(webViewCore) 82 , m_timer(this, &GeolocationClientImpl::timerFired) 83 , m_isSuspended(false) 84 , m_useGps(false) 85{ 86} 87 88GeolocationClientImpl::~GeolocationClientImpl() 89{ 90} 91 92void GeolocationClientImpl::geolocationDestroyed() 93{ 94 // Lifetime is managed by GeolocationManager. 95} 96 97void 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 121void 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 135void 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 143GeolocationPosition* GeolocationClientImpl::lastPosition() 144{ 145 return m_lastPosition.get(); 146} 147 148void GeolocationClientImpl::requestPermission(Geolocation* geolocation) 149{ 150 permissions()->queryPermissionState(geolocation->frame()); 151} 152 153void 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. 160void 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 174void 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 182void GeolocationClientImpl::suspend() 183{ 184 m_isSuspended = true; 185 if (m_javaBridge) 186 m_javaBridge->stop(); 187} 188 189void GeolocationClientImpl::resume() 190{ 191 m_isSuspended = false; 192 if (m_javaBridge) 193 m_javaBridge->start(); 194} 195 196void GeolocationClientImpl::resetTemporaryPermissionStates() 197{ 198 permissions()->resetTemporaryPermissionStates(); 199} 200 201void GeolocationClientImpl::providePermissionState(String origin, bool allow, bool remember) 202{ 203 permissions()->providePermissionState(origin, allow, remember); 204} 205 206GeolocationPermissions* GeolocationClientImpl::permissions() const 207{ 208 if (!m_permissions) 209 m_permissions = new GeolocationPermissions(m_webViewCore); 210 return m_permissions.get(); 211} 212 213void 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