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 "GeolocationServiceBridge.h"
28
29 #include "WebViewCore.h"
30
31 #include <GeolocationError.h>
32 #include <GeolocationPosition.h>
33 #include <JNIHelp.h>
34
35 using JSC::Bindings::getJNIEnv;
36 using WebCore::GeolocationError;
37 using WebCore::GeolocationPosition;
38
39 namespace android {
40
41 static const char* javaGeolocationServiceClassName = "android/webkit/GeolocationService";
42 enum javaGeolocationServiceClassMethods {
43 GeolocationServiceMethodInit = 0,
44 GeolocationServiceMethodStart,
45 GeolocationServiceMethodStop,
46 GeolocationServiceMethodSetEnableGps,
47 GeolocationServiceMethodCount,
48 };
49 static jmethodID javaGeolocationServiceClassMethodIDs[GeolocationServiceMethodCount];
50
51 static const JNINativeMethod javaGeolocationServiceClassNativeMethods[] = {
52 { "nativeNewLocationAvailable", "(JLandroid/location/Location;)V",
53 (void*) GeolocationServiceBridge::newLocationAvailable },
54 { "nativeNewErrorAvailable", "(JLjava/lang/String;)V",
55 (void*) GeolocationServiceBridge::newErrorAvailable }
56 };
57
58 static const char *javaLocationClassName = "android/location/Location";
59 enum javaLocationClassMethods {
60 LocationMethodGetLatitude = 0,
61 LocationMethodGetLongitude,
62 LocationMethodHasAltitude,
63 LocationMethodGetAltitude,
64 LocationMethodHasAccuracy,
65 LocationMethodGetAccuracy,
66 LocationMethodHasBearing,
67 LocationMethodGetBearing,
68 LocationMethodHasSpeed,
69 LocationMethodGetSpeed,
70 LocationMethodGetTime,
71 LocationMethodCount,
72 };
73 static jmethodID javaLocationClassMethodIDs[LocationMethodCount];
74
GeolocationServiceBridge(Listener * listener,WebViewCore * webViewCore)75 GeolocationServiceBridge::GeolocationServiceBridge(Listener* listener, WebViewCore* webViewCore)
76 : m_listener(listener)
77 , m_javaGeolocationServiceObject(0)
78 {
79 ASSERT(m_listener);
80 startJavaImplementation(webViewCore);
81 }
82
~GeolocationServiceBridge()83 GeolocationServiceBridge::~GeolocationServiceBridge()
84 {
85 stop();
86 stopJavaImplementation();
87 }
88
start()89 bool GeolocationServiceBridge::start()
90 {
91 if (!m_javaGeolocationServiceObject)
92 return false;
93 return getJNIEnv()->CallBooleanMethod(m_javaGeolocationServiceObject,
94 javaGeolocationServiceClassMethodIDs[GeolocationServiceMethodStart]);
95 }
96
stop()97 void GeolocationServiceBridge::stop()
98 {
99 if (!m_javaGeolocationServiceObject)
100 return;
101 getJNIEnv()->CallVoidMethod(m_javaGeolocationServiceObject,
102 javaGeolocationServiceClassMethodIDs[GeolocationServiceMethodStop]);
103 }
104
setEnableGps(bool enable)105 void GeolocationServiceBridge::setEnableGps(bool enable)
106 {
107 if (!m_javaGeolocationServiceObject)
108 return;
109 getJNIEnv()->CallVoidMethod(m_javaGeolocationServiceObject,
110 javaGeolocationServiceClassMethodIDs[GeolocationServiceMethodSetEnableGps],
111 enable);
112 }
113
newLocationAvailable(JNIEnv * env,jclass,jlong nativeObject,jobject location)114 void GeolocationServiceBridge::newLocationAvailable(JNIEnv* env, jclass, jlong nativeObject, jobject location)
115 {
116 ASSERT(nativeObject);
117 ASSERT(location);
118 GeolocationServiceBridge* object = reinterpret_cast<GeolocationServiceBridge*>(nativeObject);
119 object->m_listener->newPositionAvailable(toGeolocationPosition(env, location));
120 }
121
newErrorAvailable(JNIEnv * env,jclass,jlong nativeObject,jstring message)122 void GeolocationServiceBridge::newErrorAvailable(JNIEnv* env, jclass, jlong nativeObject, jstring message)
123 {
124 GeolocationServiceBridge* object = reinterpret_cast<GeolocationServiceBridge*>(nativeObject);
125 RefPtr<GeolocationError> error = GeolocationError::create(GeolocationError::PositionUnavailable, jstringToWtfString(env, message));
126 object->m_listener->newErrorAvailable(error.release());
127 }
128
toGeolocationPosition(JNIEnv * env,const jobject & location)129 PassRefPtr<GeolocationPosition> GeolocationServiceBridge::toGeolocationPosition(JNIEnv *env, const jobject &location)
130 {
131 // Altitude is optional and may not be supplied.
132 bool hasAltitude =
133 env->CallBooleanMethod(location, javaLocationClassMethodIDs[LocationMethodHasAltitude]);
134 double Altitude =
135 hasAltitude ?
136 env->CallDoubleMethod(location, javaLocationClassMethodIDs[LocationMethodGetAltitude]) :
137 0.0;
138 // Accuracy is required, but is not supplied by the emulator.
139 double Accuracy =
140 env->CallBooleanMethod(location, javaLocationClassMethodIDs[LocationMethodHasAccuracy]) ?
141 env->CallFloatMethod(location, javaLocationClassMethodIDs[LocationMethodGetAccuracy]) :
142 0.0;
143 // heading is optional and may not be supplied.
144 bool hasHeading =
145 env->CallBooleanMethod(location, javaLocationClassMethodIDs[LocationMethodHasBearing]);
146 double heading =
147 hasHeading ?
148 env->CallFloatMethod(location, javaLocationClassMethodIDs[LocationMethodGetBearing]) :
149 0.0;
150 // speed is optional and may not be supplied.
151 bool hasSpeed =
152 env->CallBooleanMethod(location, javaLocationClassMethodIDs[LocationMethodHasSpeed]);
153 double speed =
154 hasSpeed ?
155 env->CallFloatMethod(location, javaLocationClassMethodIDs[LocationMethodGetSpeed]) :
156 0.0;
157
158 return GeolocationPosition::create(
159 env->CallLongMethod(location, javaLocationClassMethodIDs[LocationMethodGetTime]) / 1000.0,
160 env->CallDoubleMethod(location, javaLocationClassMethodIDs[LocationMethodGetLatitude]),
161 env->CallDoubleMethod(location, javaLocationClassMethodIDs[LocationMethodGetLongitude]),
162 Accuracy,
163 hasAltitude, Altitude,
164 false, 0.0, // AltitudeAccuracy not provided.
165 hasHeading, heading,
166 hasSpeed, speed);
167 }
168
startJavaImplementation(WebViewCore * webViewCore)169 void GeolocationServiceBridge::startJavaImplementation(WebViewCore* webViewCore)
170 {
171 JNIEnv* env = getJNIEnv();
172
173 // Get the Java GeolocationService class.
174 jclass javaGeolocationServiceClass = env->FindClass(javaGeolocationServiceClassName);
175 ASSERT(javaGeolocationServiceClass);
176
177 // Set up the methods we wish to call on the Java GeolocationService class.
178 javaGeolocationServiceClassMethodIDs[GeolocationServiceMethodInit] =
179 env->GetMethodID(javaGeolocationServiceClass, "<init>", "(Landroid/content/Context;J)V");
180 javaGeolocationServiceClassMethodIDs[GeolocationServiceMethodStart] =
181 env->GetMethodID(javaGeolocationServiceClass, "start", "()Z");
182 javaGeolocationServiceClassMethodIDs[GeolocationServiceMethodStop] =
183 env->GetMethodID(javaGeolocationServiceClass, "stop", "()V");
184 javaGeolocationServiceClassMethodIDs[GeolocationServiceMethodSetEnableGps] =
185 env->GetMethodID(javaGeolocationServiceClass, "setEnableGps", "(Z)V");
186
187 // Create the Java GeolocationService object.
188 jobject context = webViewCore->getContext();
189 if (!context)
190 return;
191 jlong nativeObject = reinterpret_cast<jlong>(this);
192 jobject object = env->NewObject(javaGeolocationServiceClass,
193 javaGeolocationServiceClassMethodIDs[GeolocationServiceMethodInit],
194 context,
195 nativeObject);
196
197 m_javaGeolocationServiceObject = getJNIEnv()->NewGlobalRef(object);
198 ASSERT(m_javaGeolocationServiceObject);
199
200 // Register to handle calls to native methods of the Java GeolocationService
201 // object. We register once only.
202 static int registered = jniRegisterNativeMethods(env,
203 javaGeolocationServiceClassName,
204 javaGeolocationServiceClassNativeMethods,
205 NELEM(javaGeolocationServiceClassNativeMethods));
206 ASSERT(registered == JNI_OK);
207
208 // Set up the methods we wish to call on the Java Location class.
209 jclass javaLocationClass = env->FindClass(javaLocationClassName);
210 ASSERT(javaLocationClass);
211 javaLocationClassMethodIDs[LocationMethodGetLatitude] =
212 env->GetMethodID(javaLocationClass, "getLatitude", "()D");
213 javaLocationClassMethodIDs[LocationMethodGetLongitude] =
214 env->GetMethodID(javaLocationClass, "getLongitude", "()D");
215 javaLocationClassMethodIDs[LocationMethodHasAltitude] =
216 env->GetMethodID(javaLocationClass, "hasAltitude", "()Z");
217 javaLocationClassMethodIDs[LocationMethodGetAltitude] =
218 env->GetMethodID(javaLocationClass, "getAltitude", "()D");
219 javaLocationClassMethodIDs[LocationMethodHasAccuracy] =
220 env->GetMethodID(javaLocationClass, "hasAccuracy", "()Z");
221 javaLocationClassMethodIDs[LocationMethodGetAccuracy] =
222 env->GetMethodID(javaLocationClass, "getAccuracy", "()F");
223 javaLocationClassMethodIDs[LocationMethodHasBearing] =
224 env->GetMethodID(javaLocationClass, "hasBearing", "()Z");
225 javaLocationClassMethodIDs[LocationMethodGetBearing] =
226 env->GetMethodID(javaLocationClass, "getBearing", "()F");
227 javaLocationClassMethodIDs[LocationMethodHasSpeed] =
228 env->GetMethodID(javaLocationClass, "hasSpeed", "()Z");
229 javaLocationClassMethodIDs[LocationMethodGetSpeed] =
230 env->GetMethodID(javaLocationClass, "getSpeed", "()F");
231 javaLocationClassMethodIDs[LocationMethodGetTime] =
232 env->GetMethodID(javaLocationClass, "getTime", "()J");
233 }
234
stopJavaImplementation()235 void GeolocationServiceBridge::stopJavaImplementation()
236 {
237 if (!m_javaGeolocationServiceObject)
238 return;
239 getJNIEnv()->DeleteGlobalRef(m_javaGeolocationServiceObject);
240 }
241
242 } // namespace android
243