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 "GeolocationServiceAndroid.h"
     28 
     29 #include "Geolocation.h"
     30 #include "GeolocationServiceBridge.h"
     31 #include "Geoposition.h"
     32 #include "PositionError.h"
     33 #include "PositionOptions.h"
     34 
     35 #if PLATFORM(ANDROID)
     36 // Required for sim-eng build
     37 #include <math.h>
     38 #endif
     39 #include <wtf/CurrentTime.h>
     40 
     41 using JSC::Bindings::getJNIEnv;
     42 using namespace std;
     43 
     44 namespace WebCore {
     45 
     46 // GeolocationServiceAndroid is the Android implmentation of Geolocation
     47 // service. Each object of this class owns an object of type
     48 // GeolocationServiceBridge, which in turn owns a Java GeolocationService
     49 // object. Therefore, there is a 1:1 mapping between Geolocation,
     50 // GeolocationServiceAndroid, GeolocationServiceBridge and Java
     51 // GeolocationService objects. In the case where multiple Geolocation objects
     52 // exist simultaneously, the corresponsing Java GeolocationService objects all
     53 // register with the platform location service. It is the platform service that
     54 // handles making sure that updates are passed to all Geolocation objects.
     55 GeolocationService* GeolocationServiceAndroid::create(GeolocationServiceClient* client)
     56 {
     57     return new GeolocationServiceAndroid(client);
     58 }
     59 
     60 GeolocationService::FactoryFunction* GeolocationService::s_factoryFunction = &GeolocationServiceAndroid::create;
     61 
     62 GeolocationServiceAndroid::GeolocationServiceAndroid(GeolocationServiceClient* client)
     63     : GeolocationService(client)
     64     , m_timer(this, &GeolocationServiceAndroid::timerFired)
     65     , m_javaBridge(0)
     66 {
     67 }
     68 
     69 // ANDROID
     70 // TODO: Upstream to webkit.org. See https://bugs.webkit.org/show_bug.cgi?id=34082
     71 bool GeolocationServiceAndroid::startUpdating(PositionOptions* options, bool suspend)
     72 {
     73     // ANDROID
     74     // This is an ugly hack. A correct fix would require a change to WebCore,
     75     // but this isn't worth the effort as we're in the process of switching to a
     76     // client-based implementation. See https://bugs.webkit.org/show_bug.cgi?id=40373
     77     Frame* frame = reinterpret_cast<Geolocation*>(geolocationServiceClient())->frame();
     78     if (!frame)
     79         return false;
     80 
     81     // This method is called every time a new watch or one-shot position request
     82     // is started. If we already have a position or an error, call back
     83     // immediately.
     84     if (m_lastPosition || m_lastError) {
     85         ASSERT(m_javaBridge);
     86         m_timer.startOneShot(0);
     87     }
     88 
     89     // Lazilly create the Java object.
     90     bool haveJavaBridge = m_javaBridge;
     91     if (!haveJavaBridge)
     92         m_javaBridge.set(new GeolocationServiceBridge(this, frame));
     93     ASSERT(m_javaBridge);
     94 
     95     // On Android, high power == GPS. Set whether to use GPS before we start the
     96     // implementation.
     97     ASSERT(options);
     98     if (options->enableHighAccuracy())
     99         m_javaBridge->setEnableGps(true);
    100 
    101     // We need only start the service when it's first created.
    102     if (!haveJavaBridge) {
    103         // If the browser is paused, don't start the service. It will be started
    104         // when we get the call to resume.
    105         // ANDROID
    106         // TODO: Upstream to webkit.org. See https://bugs.webkit.org/show_bug.cgi?id=34082
    107         if (!suspend)
    108             return m_javaBridge->start();
    109     }
    110 
    111     return true;
    112 }
    113 
    114 void GeolocationServiceAndroid::stopUpdating()
    115 {
    116     // Called when the Geolocation object has no watches or one shots in
    117     // progress. This may be called repeatedly.
    118     m_javaBridge.clear();
    119     // Reset last position and error to make sure that we always try to get a
    120     // new position from the system service when a request is first made.
    121     m_lastPosition = 0;
    122     m_lastError = 0;
    123     // remove the pending timer
    124     if (m_timer.isActive())
    125         m_timer.stop();
    126 }
    127 
    128 void GeolocationServiceAndroid::suspend()
    129 {
    130     if (m_javaBridge)
    131         m_javaBridge->stop();
    132 }
    133 
    134 void GeolocationServiceAndroid::resume()
    135 {
    136     if (m_javaBridge)
    137         m_javaBridge->start();
    138 }
    139 
    140 // Note that there is no guarantee that subsequent calls to this method offer a
    141 // more accurate or updated position.
    142 void GeolocationServiceAndroid::newPositionAvailable(PassRefPtr<Geoposition> position)
    143 {
    144     ASSERT(position);
    145     if (!m_lastPosition
    146         || isPositionMovement(m_lastPosition.get(), position.get())
    147         || isPositionMoreAccurate(m_lastPosition.get(), position.get())
    148         || isPositionMoreTimely(m_lastPosition.get(), position.get())) {
    149         m_lastPosition = position;
    150         // Remove the last error.
    151         m_lastError = 0;
    152         positionChanged();
    153     }
    154 }
    155 
    156 void GeolocationServiceAndroid::newErrorAvailable(PassRefPtr<PositionError> error)
    157 {
    158     ASSERT(error);
    159     // We leave the last position
    160     m_lastError = error;
    161     errorOccurred();
    162 }
    163 
    164 void GeolocationServiceAndroid::timerFired(Timer<GeolocationServiceAndroid>* timer)
    165 {
    166     ASSERT(&m_timer == timer);
    167     ASSERT(m_lastPosition || m_lastError);
    168     if (m_lastPosition)
    169         positionChanged();
    170     else if (m_lastError)
    171         errorOccurred();
    172 }
    173 
    174 bool GeolocationServiceAndroid::isPositionMovement(Geoposition* position1, Geoposition* position2)
    175 {
    176     ASSERT(position1 && position2);
    177     // For the small distances in which we are likely concerned, it's reasonable
    178     // to approximate the distance between the two positions as the sum of the
    179     // differences in latitude and longitude.
    180     double delta = fabs(position1->coords()->latitude() - position2->coords()->latitude())
    181         + fabs(position1->coords()->longitude() - position2->coords()->longitude());
    182     // Approximate conversion from degrees of arc to metres.
    183     delta *= 60 * 1852;
    184     // The threshold is when the distance between the two positions exceeds the
    185     // worse (larger) of the two accuracies.
    186     int maxAccuracy = max(position1->coords()->accuracy(), position2->coords()->accuracy());
    187     return delta > maxAccuracy;
    188 }
    189 
    190 bool GeolocationServiceAndroid::isPositionMoreAccurate(Geoposition* position1, Geoposition* position2)
    191 {
    192     ASSERT(position1 && position2);
    193     return position2->coords()->accuracy() < position1->coords()->accuracy();
    194 }
    195 
    196 bool GeolocationServiceAndroid::isPositionMoreTimely(Geoposition* position1, Geoposition* position2)
    197 {
    198     ASSERT(position1 && position2);
    199     DOMTimeStamp currentTime = convertSecondsToDOMTimeStamp(WTF::currentTime());
    200     DOMTimeStamp maximumAge = convertSecondsToDOMTimeStamp(10 * 60); // 10 minutes
    201     return currentTime - position1->timestamp() > maximumAge;
    202 }
    203 
    204 } // namespace WebCore
    205