1/* 2 * Copyright (C) 2014 Samsung Electronics. All rights reserved. 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 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. 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 APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "GeolocationProviderGeoclue.h" 28 29#if ENABLE(GEOLOCATION) && USE(GEOCLUE2) 30 31#include <wtf/gobject/GUniquePtr.h> 32#include <wtf/gobject/GlibUtilities.h> 33#include <wtf/text/CString.h> 34 35const char* gGeoclueBusName = "org.freedesktop.GeoClue2"; 36const char* gGeoclueManagerPath = "/org/freedesktop/GeoClue2/Manager"; 37 38using namespace WebCore; 39 40typedef enum { 41 GeoclueAccuracyLevelCountry = 1, 42 GeoclueAccuracyLevelCity = 4, 43 GeoclueAccuracyLevelStreet = 6, 44 GeoclueAccuracyLevelExact = 8, 45} GeoclueAccuracyLevel; 46 47GeolocationProviderGeoclue::GeolocationProviderGeoclue(GeolocationProviderGeoclueClient* client) 48 : m_client(client) 49 , m_latitude(0) 50 , m_longitude(0) 51 , m_altitude(0) 52 , m_accuracy(0) 53 , m_altitudeAccuracy(0) 54 , m_timestamp(0) 55 , m_enableHighAccuracy(false) 56 , m_isUpdating(false) 57{ 58 ASSERT(m_client); 59} 60 61GeolocationProviderGeoclue::~GeolocationProviderGeoclue() 62{ 63 stopUpdating(); 64} 65 66void GeolocationProviderGeoclue::startUpdating() 67{ 68 m_isUpdating = true; 69 70 if (!m_managerProxy) { 71 geoclue_manager_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, gGeoclueBusName, gGeoclueManagerPath, nullptr, 72 reinterpret_cast<GAsyncReadyCallback>(createGeoclueManagerProxyCallback), this); 73 return; 74 } 75 76 geoclue_manager_call_get_client(m_managerProxy.get(), nullptr, reinterpret_cast<GAsyncReadyCallback>(getGeoclueClientCallback), this); 77} 78 79void GeolocationProviderGeoclue::stopUpdating() 80{ 81 if (m_clientProxy) { 82 geoclue_client_call_stop(m_clientProxy.get(), nullptr, nullptr, nullptr); 83 g_signal_handlers_disconnect_by_func(m_clientProxy.get(), reinterpret_cast<gpointer>(locationUpdatedCallback), this); 84 m_clientProxy = nullptr; 85 } 86 m_isUpdating = false; 87} 88 89void GeolocationProviderGeoclue::setEnableHighAccuracy(bool enable) 90{ 91 if (m_enableHighAccuracy == enable) 92 return; 93 94 m_enableHighAccuracy = enable; 95 96 // If we're already updating we should report the new requirements in order 97 // to change to a more suitable provider if needed. If not, return. 98 if (!m_isUpdating) 99 return; 100 101 updateClientRequirements(); 102} 103 104void GeolocationProviderGeoclue::createGeoclueManagerProxyCallback(GObject*, GAsyncResult* result, GeolocationProviderGeoclue* provider) 105{ 106 GUniqueOutPtr<GError> error; 107 provider->m_managerProxy = adoptGRef(geoclue_manager_proxy_new_for_bus_finish(result, &error.outPtr())); 108 if (error) { 109 provider->errorOccurred(error->message); 110 return; 111 } 112 113 geoclue_manager_call_get_client(provider->m_managerProxy.get(), nullptr, reinterpret_cast<GAsyncReadyCallback>(getGeoclueClientCallback), provider); 114} 115 116void GeolocationProviderGeoclue::getGeoclueClientCallback(GObject* sourceObject, GAsyncResult* result, GeolocationProviderGeoclue* provider) 117{ 118 GUniqueOutPtr<GError> error; 119 GUniqueOutPtr<gchar> path; 120 if (!geoclue_manager_call_get_client_finish(GEOCLUE_MANAGER(sourceObject), &path.outPtr(), result, &error.outPtr())) { 121 provider->errorOccurred(error->message); 122 return; 123 } 124 125 geoclue_client_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, gGeoclueBusName, path.get(), nullptr, 126 reinterpret_cast<GAsyncReadyCallback>(createGeoclueClientProxyCallback), provider); 127} 128 129void GeolocationProviderGeoclue::createGeoclueClientProxyCallback(GObject*, GAsyncResult* result, GeolocationProviderGeoclue* provider) 130{ 131 GUniqueOutPtr<GError> error; 132 provider->m_clientProxy = adoptGRef(geoclue_client_proxy_new_for_bus_finish(result, &error.outPtr())); 133 if (error) { 134 provider->errorOccurred(error->message); 135 return; 136 } 137 138 // Geoclue2 requires the client to provide a desktop ID for security 139 // reasons, which should identify the application requesting the location. 140 // FIXME: We provide the program name as the desktop ID for now but, in an ideal world, 141 // we should provide a proper "application ID" (normally the name of a .desktop file). 142 // https://bugs.webkit.org/show_bug.cgi?id=129879 143 geoclue_client_set_desktop_id(provider->m_clientProxy.get(), g_get_prgname()); 144 145 provider->startGeoclueClient(); 146} 147 148void GeolocationProviderGeoclue::startClientCallback(GObject* sourceObject, GAsyncResult* result, GeolocationProviderGeoclue* provider) 149{ 150 GUniqueOutPtr<GError> error; 151 if (!geoclue_client_call_start_finish(GEOCLUE_CLIENT(sourceObject), result, &error.outPtr())) 152 static_cast<GeolocationProviderGeoclue*>(provider)->errorOccurred(error->message); 153} 154 155void GeolocationProviderGeoclue::locationUpdatedCallback(GeoclueClient*, const gchar*, const gchar* newPath, GeolocationProviderGeoclue* provider) 156{ 157 geoclue_location_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, gGeoclueBusName, newPath, nullptr, 158 reinterpret_cast<GAsyncReadyCallback>(createLocationProxyCallback), provider); 159} 160 161void GeolocationProviderGeoclue::createLocationProxyCallback(GObject*, GAsyncResult* result, GeolocationProviderGeoclue* provider) 162{ 163 GUniqueOutPtr<GError> error; 164 GRefPtr<GeoclueLocation> locationProxy = adoptGRef(geoclue_location_proxy_new_for_bus_finish(result, &error.outPtr())); 165 if (error) { 166 provider->errorOccurred(error->message); 167 return; 168 } 169 provider->updateLocation(locationProxy.get()); 170} 171 172void GeolocationProviderGeoclue::startGeoclueClient() 173{ 174 // Set the requirement for the client. 175 updateClientRequirements(); 176 177 g_signal_connect(m_clientProxy.get(), "location-updated", G_CALLBACK(locationUpdatedCallback), this); 178 geoclue_client_call_start(m_clientProxy.get(), nullptr, reinterpret_cast<GAsyncReadyCallback>(startClientCallback), this); 179} 180 181void GeolocationProviderGeoclue::updateLocation(GeoclueLocation* locationProxy) 182{ 183 GTimeVal timeValue; 184 g_get_current_time(&timeValue); 185 m_timestamp = timeValue.tv_sec; 186 m_latitude = geoclue_location_get_latitude(locationProxy); 187 m_longitude = geoclue_location_get_longitude(locationProxy); 188 m_accuracy = geoclue_location_get_accuracy(locationProxy); 189 m_client->notifyPositionChanged(m_timestamp, m_latitude, m_longitude, m_altitude, m_accuracy, m_altitudeAccuracy); 190} 191 192void GeolocationProviderGeoclue::errorOccurred(const char* message) 193{ 194 m_isUpdating = false; 195 m_client->notifyErrorOccurred(message); 196} 197 198void GeolocationProviderGeoclue::updateClientRequirements() 199{ 200 if (!m_clientProxy) 201 return; 202 203 GeoclueAccuracyLevel accuracyLevel = m_enableHighAccuracy ? GeoclueAccuracyLevelExact : GeoclueAccuracyLevelCity; 204 geoclue_client_set_requested_accuracy_level(m_clientProxy.get(), accuracyLevel); 205} 206 207#endif // ENABLE(GEOLOCATION) && USE(GEOCLUE2) 208