blob: 4a16220240a4223f6d919ba9a72c439587527cd8 [file] [log] [blame]
/*
* Copyright (C) 2014 Samsung Electronics. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "GeolocationProviderGeoclue.h"
#if ENABLE(GEOLOCATION)
#include <wtf/glib/GLibUtilities.h>
#include <wtf/glib/GUniquePtr.h>
#include <wtf/text/CString.h>
const char* gGeoclueBusName = "org.freedesktop.GeoClue2";
const char* gGeoclueManagerPath = "/org/freedesktop/GeoClue2/Manager";
using namespace WebCore;
typedef enum {
GeoclueAccuracyLevelCountry = 1,
GeoclueAccuracyLevelCity = 4,
GeoclueAccuracyLevelStreet = 6,
GeoclueAccuracyLevelExact = 8,
} GeoclueAccuracyLevel;
GeolocationProviderGeoclue::GeolocationProviderGeoclue(GeolocationProviderGeoclueClient* client)
: m_client(client)
, m_latitude(0)
, m_longitude(0)
, m_altitude(0)
, m_accuracy(0)
, m_altitudeAccuracy(0)
, m_timestamp(0)
, m_enableHighAccuracy(false)
, m_isUpdating(false)
{
ASSERT(m_client);
}
GeolocationProviderGeoclue::~GeolocationProviderGeoclue()
{
stopUpdating();
}
void GeolocationProviderGeoclue::startUpdating()
{
m_isUpdating = true;
if (!m_managerProxy) {
geoclue_manager_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, gGeoclueBusName, gGeoclueManagerPath, nullptr,
reinterpret_cast<GAsyncReadyCallback>(createGeoclueManagerProxyCallback), this);
return;
}
geoclue_manager_call_get_client(m_managerProxy.get(), nullptr, reinterpret_cast<GAsyncReadyCallback>(getGeoclueClientCallback), this);
}
void GeolocationProviderGeoclue::stopUpdating()
{
if (m_clientProxy) {
geoclue_client_call_stop(m_clientProxy.get(), nullptr, nullptr, nullptr);
g_signal_handlers_disconnect_by_func(m_clientProxy.get(), reinterpret_cast<gpointer>(locationUpdatedCallback), this);
m_clientProxy = nullptr;
}
m_isUpdating = false;
}
void GeolocationProviderGeoclue::setEnableHighAccuracy(bool enable)
{
if (m_enableHighAccuracy == enable)
return;
m_enableHighAccuracy = enable;
// If we're already updating we should report the new requirements in order
// to change to a more suitable provider if needed. If not, return.
if (!m_isUpdating)
return;
updateClientRequirements();
}
void GeolocationProviderGeoclue::createGeoclueManagerProxyCallback(GObject*, GAsyncResult* result, GeolocationProviderGeoclue* provider)
{
GUniqueOutPtr<GError> error;
provider->m_managerProxy = adoptGRef(geoclue_manager_proxy_new_for_bus_finish(result, &error.outPtr()));
if (error) {
provider->errorOccurred(error->message);
return;
}
geoclue_manager_call_get_client(provider->m_managerProxy.get(), nullptr, reinterpret_cast<GAsyncReadyCallback>(getGeoclueClientCallback), provider);
}
void GeolocationProviderGeoclue::getGeoclueClientCallback(GObject* sourceObject, GAsyncResult* result, GeolocationProviderGeoclue* provider)
{
GUniqueOutPtr<GError> error;
GUniqueOutPtr<gchar> path;
if (!geoclue_manager_call_get_client_finish(GEOCLUE_MANAGER(sourceObject), &path.outPtr(), result, &error.outPtr())) {
provider->errorOccurred(error->message);
return;
}
geoclue_client_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, gGeoclueBusName, path.get(), nullptr,
reinterpret_cast<GAsyncReadyCallback>(createGeoclueClientProxyCallback), provider);
}
void GeolocationProviderGeoclue::createGeoclueClientProxyCallback(GObject*, GAsyncResult* result, GeolocationProviderGeoclue* provider)
{
GUniqueOutPtr<GError> error;
provider->m_clientProxy = adoptGRef(geoclue_client_proxy_new_for_bus_finish(result, &error.outPtr()));
if (error) {
provider->errorOccurred(error->message);
return;
}
// Geoclue2 requires the client to provide a desktop ID for security
// reasons, which should identify the application requesting the location.
// We use the application ID configured for the default GApplication, and
// also fallback to our old behavior of using g_get_prgname().
const char* appId = nullptr;
GApplication* defaultApp = g_application_get_default();
if (defaultApp)
appId = g_application_get_application_id(defaultApp);
if (!appId)
appId = g_get_prgname();
geoclue_client_set_desktop_id(provider->m_clientProxy.get(), appId);
provider->startGeoclueClient();
}
void GeolocationProviderGeoclue::startClientCallback(GObject* sourceObject, GAsyncResult* result, GeolocationProviderGeoclue* provider)
{
GUniqueOutPtr<GError> error;
if (!geoclue_client_call_start_finish(GEOCLUE_CLIENT(sourceObject), result, &error.outPtr()))
static_cast<GeolocationProviderGeoclue*>(provider)->errorOccurred(error->message);
}
void GeolocationProviderGeoclue::locationUpdatedCallback(GeoclueClient*, const gchar*, const gchar* newPath, GeolocationProviderGeoclue* provider)
{
geoclue_location_proxy_new_for_bus(G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, gGeoclueBusName, newPath, nullptr,
reinterpret_cast<GAsyncReadyCallback>(createLocationProxyCallback), provider);
}
void GeolocationProviderGeoclue::createLocationProxyCallback(GObject*, GAsyncResult* result, GeolocationProviderGeoclue* provider)
{
GUniqueOutPtr<GError> error;
GRefPtr<GeoclueLocation> locationProxy = adoptGRef(geoclue_location_proxy_new_for_bus_finish(result, &error.outPtr()));
if (error) {
provider->errorOccurred(error->message);
return;
}
provider->updateLocation(locationProxy.get());
}
void GeolocationProviderGeoclue::startGeoclueClient()
{
// Set the requirement for the client.
updateClientRequirements();
g_signal_connect(m_clientProxy.get(), "location-updated", G_CALLBACK(locationUpdatedCallback), this);
geoclue_client_call_start(m_clientProxy.get(), nullptr, reinterpret_cast<GAsyncReadyCallback>(startClientCallback), this);
}
void GeolocationProviderGeoclue::updateLocation(GeoclueLocation* locationProxy)
{
GTimeVal timeValue;
g_get_current_time(&timeValue);
m_timestamp = timeValue.tv_sec;
m_latitude = geoclue_location_get_latitude(locationProxy);
m_longitude = geoclue_location_get_longitude(locationProxy);
m_accuracy = geoclue_location_get_accuracy(locationProxy);
m_client->notifyPositionChanged(m_timestamp, m_latitude, m_longitude, m_altitude, m_accuracy, m_altitudeAccuracy);
}
void GeolocationProviderGeoclue::errorOccurred(const char* message)
{
m_isUpdating = false;
m_client->notifyErrorOccurred(message);
}
void GeolocationProviderGeoclue::updateClientRequirements()
{
if (!m_clientProxy)
return;
GeoclueAccuracyLevel accuracyLevel = m_enableHighAccuracy ? GeoclueAccuracyLevelExact : GeoclueAccuracyLevelCity;
geoclue_client_set_requested_accuracy_level(m_clientProxy.get(), accuracyLevel);
}
#endif // ENABLE(GEOLOCATION)