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