Home | History | Annotate | Download | only in android
      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