• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
create(GeolocationServiceClient * client)55 GeolocationService* GeolocationServiceAndroid::create(GeolocationServiceClient* client)
56 {
57     return new GeolocationServiceAndroid(client);
58 }
59 
60 GeolocationService::FactoryFunction* GeolocationService::s_factoryFunction = &GeolocationServiceAndroid::create;
61 
GeolocationServiceAndroid(GeolocationServiceClient * client)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
startUpdating(PositionOptions * options,bool suspend)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 
stopUpdating()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 
suspend()128 void GeolocationServiceAndroid::suspend()
129 {
130     if (m_javaBridge)
131         m_javaBridge->stop();
132 }
133 
resume()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.
newPositionAvailable(PassRefPtr<Geoposition> 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 
newErrorAvailable(PassRefPtr<PositionError> error)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 
timerFired(Timer<GeolocationServiceAndroid> * timer)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 
isPositionMovement(Geoposition * position1,Geoposition * position2)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 
isPositionMoreAccurate(Geoposition * position1,Geoposition * position2)190 bool GeolocationServiceAndroid::isPositionMoreAccurate(Geoposition* position1, Geoposition* position2)
191 {
192     ASSERT(position1 && position2);
193     return position2->coords()->accuracy() < position1->coords()->accuracy();
194 }
195 
isPositionMoreTimely(Geoposition * position1,Geoposition * position2)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