1 /* 2 * Copyright 2009, 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 "GeolocationServiceAndroid.h" 28 29 #include "Geolocation.h" 30 #include "GeolocationServiceBridge.h" 31 #include "Geoposition.h" 32 #include "PositionError.h" 33 #include "PositionOptions.h" 34 35 #if PLATFORM(ANDROID) 36 // Required for sim-eng build 37 #include <math.h> 38 #endif 39 #include <wtf/CurrentTime.h> 40 41 using JSC::Bindings::getJNIEnv; 42 using namespace std; 43 44 namespace WebCore { 45 46 // GeolocationServiceAndroid is the Android implmentation of Geolocation 47 // service. Each object of this class owns an object of type 48 // GeolocationServiceBridge, which in turn owns a Java GeolocationService 49 // object. Therefore, there is a 1:1 mapping between Geolocation, 50 // GeolocationServiceAndroid, GeolocationServiceBridge and Java 51 // GeolocationService objects. In the case where multiple Geolocation objects 52 // exist simultaneously, the corresponsing Java GeolocationService objects all 53 // register with the platform location service. It is the platform service that 54 // handles making sure that updates are passed to all Geolocation objects. 55 GeolocationService* GeolocationServiceAndroid::create(GeolocationServiceClient* client) 56 { 57 return new GeolocationServiceAndroid(client); 58 } 59 60 GeolocationService::FactoryFunction* GeolocationService::s_factoryFunction = &GeolocationServiceAndroid::create; 61 62 GeolocationServiceAndroid::GeolocationServiceAndroid(GeolocationServiceClient* client) 63 : GeolocationService(client) 64 , m_timer(this, &GeolocationServiceAndroid::timerFired) 65 , m_javaBridge(0) 66 { 67 } 68 69 // ANDROID 70 // TODO: Upstream to webkit.org. See https://bugs.webkit.org/show_bug.cgi?id=34082 71 bool GeolocationServiceAndroid::startUpdating(PositionOptions* options, bool suspend) 72 { 73 // ANDROID 74 // This is an ugly hack. A correct fix would require a change to WebCore, 75 // but this isn't worth the effort as we're in the process of switching to a 76 // client-based implementation. See https://bugs.webkit.org/show_bug.cgi?id=40373 77 Frame* frame = reinterpret_cast<Geolocation*>(geolocationServiceClient())->frame(); 78 if (!frame) 79 return false; 80 81 // This method is called every time a new watch or one-shot position request 82 // is started. If we already have a position or an error, call back 83 // immediately. 84 if (m_lastPosition || m_lastError) { 85 ASSERT(m_javaBridge); 86 m_timer.startOneShot(0); 87 } 88 89 // Lazilly create the Java object. 90 bool haveJavaBridge = m_javaBridge; 91 if (!haveJavaBridge) 92 m_javaBridge.set(new GeolocationServiceBridge(this, frame)); 93 ASSERT(m_javaBridge); 94 95 // On Android, high power == GPS. Set whether to use GPS before we start the 96 // implementation. 97 ASSERT(options); 98 if (options->enableHighAccuracy()) 99 m_javaBridge->setEnableGps(true); 100 101 // We need only start the service when it's first created. 102 if (!haveJavaBridge) { 103 // If the browser is paused, don't start the service. It will be started 104 // when we get the call to resume. 105 // ANDROID 106 // TODO: Upstream to webkit.org. See https://bugs.webkit.org/show_bug.cgi?id=34082 107 if (!suspend) 108 return m_javaBridge->start(); 109 } 110 111 return true; 112 } 113 114 void GeolocationServiceAndroid::stopUpdating() 115 { 116 // Called when the Geolocation object has no watches or one shots in 117 // progress. This may be called repeatedly. 118 m_javaBridge.clear(); 119 // Reset last position and error to make sure that we always try to get a 120 // new position from the system service when a request is first made. 121 m_lastPosition = 0; 122 m_lastError = 0; 123 // remove the pending timer 124 if (m_timer.isActive()) 125 m_timer.stop(); 126 } 127 128 void GeolocationServiceAndroid::suspend() 129 { 130 if (m_javaBridge) 131 m_javaBridge->stop(); 132 } 133 134 void GeolocationServiceAndroid::resume() 135 { 136 if (m_javaBridge) 137 m_javaBridge->start(); 138 } 139 140 // Note that there is no guarantee that subsequent calls to this method offer a 141 // more accurate or updated position. 142 void GeolocationServiceAndroid::newPositionAvailable(PassRefPtr<Geoposition> position) 143 { 144 ASSERT(position); 145 if (!m_lastPosition 146 || isPositionMovement(m_lastPosition.get(), position.get()) 147 || isPositionMoreAccurate(m_lastPosition.get(), position.get()) 148 || isPositionMoreTimely(m_lastPosition.get(), position.get())) { 149 m_lastPosition = position; 150 // Remove the last error. 151 m_lastError = 0; 152 positionChanged(); 153 } 154 } 155 156 void GeolocationServiceAndroid::newErrorAvailable(PassRefPtr<PositionError> error) 157 { 158 ASSERT(error); 159 // We leave the last position 160 m_lastError = error; 161 errorOccurred(); 162 } 163 164 void GeolocationServiceAndroid::timerFired(Timer<GeolocationServiceAndroid>* timer) 165 { 166 ASSERT(&m_timer == timer); 167 ASSERT(m_lastPosition || m_lastError); 168 if (m_lastPosition) 169 positionChanged(); 170 else if (m_lastError) 171 errorOccurred(); 172 } 173 174 bool GeolocationServiceAndroid::isPositionMovement(Geoposition* position1, Geoposition* position2) 175 { 176 ASSERT(position1 && position2); 177 // For the small distances in which we are likely concerned, it's reasonable 178 // to approximate the distance between the two positions as the sum of the 179 // differences in latitude and longitude. 180 double delta = fabs(position1->coords()->latitude() - position2->coords()->latitude()) 181 + fabs(position1->coords()->longitude() - position2->coords()->longitude()); 182 // Approximate conversion from degrees of arc to metres. 183 delta *= 60 * 1852; 184 // The threshold is when the distance between the two positions exceeds the 185 // worse (larger) of the two accuracies. 186 int maxAccuracy = max(position1->coords()->accuracy(), position2->coords()->accuracy()); 187 return delta > maxAccuracy; 188 } 189 190 bool GeolocationServiceAndroid::isPositionMoreAccurate(Geoposition* position1, Geoposition* position2) 191 { 192 ASSERT(position1 && position2); 193 return position2->coords()->accuracy() < position1->coords()->accuracy(); 194 } 195 196 bool GeolocationServiceAndroid::isPositionMoreTimely(Geoposition* position1, Geoposition* position2) 197 { 198 ASSERT(position1 && position2); 199 DOMTimeStamp currentTime = convertSecondsToDOMTimeStamp(WTF::currentTime()); 200 DOMTimeStamp maximumAge = convertSecondsToDOMTimeStamp(10 * 60); // 10 minutes 201 return currentTime - position1->timestamp() > maximumAge; 202 } 203 204 } // namespace WebCore 205