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