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