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 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 83 GeolocationServiceBridge::~GeolocationServiceBridge() 84 { 85 stop(); 86 stopJavaImplementation(); 87 } 88 89 bool GeolocationServiceBridge::start() 90 { 91 if (!m_javaGeolocationServiceObject) 92 return false; 93 return getJNIEnv()->CallBooleanMethod(m_javaGeolocationServiceObject, 94 javaGeolocationServiceClassMethodIDs[GeolocationServiceMethodStart]); 95 } 96 97 void GeolocationServiceBridge::stop() 98 { 99 if (!m_javaGeolocationServiceObject) 100 return; 101 getJNIEnv()->CallVoidMethod(m_javaGeolocationServiceObject, 102 javaGeolocationServiceClassMethodIDs[GeolocationServiceMethodStop]); 103 } 104 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 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 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 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 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 235 void GeolocationServiceBridge::stopJavaImplementation() 236 { 237 if (!m_javaGeolocationServiceObject) 238 return; 239 getJNIEnv()->DeleteGlobalRef(m_javaGeolocationServiceObject); 240 } 241 242 } // namespace android 243