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