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