1120 lines
38 KiB
C++
1120 lines
38 KiB
C++
/*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* Author: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
|
|
*/
|
|
|
|
#include "GeoclueLocationProvider.h"
|
|
|
|
#include <gio/gio.h>
|
|
#include <glib.h>
|
|
#include "mozilla/FloatingPoint.h"
|
|
#include "mozilla/GRefPtr.h"
|
|
#include "mozilla/GUniquePtr.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "mozilla/ScopeExit.h"
|
|
#include "mozilla/StaticPrefs_geo.h"
|
|
#include "mozilla/UniquePtrExtensions.h"
|
|
#include "mozilla/WeakPtr.h"
|
|
#include "mozilla/XREAppData.h"
|
|
#include "mozilla/dom/GeolocationPosition.h"
|
|
#include "mozilla/dom/GeolocationPositionErrorBinding.h"
|
|
#include "mozilla/glean/DomGeolocationMetrics.h"
|
|
#include "MLSFallback.h"
|
|
#include "nsAppRunner.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsIDOMGeoPosition.h"
|
|
#include "nsINamed.h"
|
|
#include "nsITimer.h"
|
|
#include "nsStringFwd.h"
|
|
#include "prtime.h"
|
|
|
|
namespace mozilla::dom {
|
|
|
|
static LazyLogModule gGCLocationLog("GeoclueLocation");
|
|
|
|
#define GCL_LOG(level, ...) \
|
|
MOZ_LOG(gGCLocationLog, mozilla::LogLevel::level, (__VA_ARGS__))
|
|
|
|
static const char* const kGeoclueBusName = "org.freedesktop.GeoClue2";
|
|
static const char* const kGCManagerPath = "/org/freedesktop/GeoClue2/Manager";
|
|
static const char* const kGCManagerInterface =
|
|
"org.freedesktop.GeoClue2.Manager";
|
|
static const char* const kGCClientInterface = "org.freedesktop.GeoClue2.Client";
|
|
static const char* const kGCLocationInterface =
|
|
"org.freedesktop.GeoClue2.Location";
|
|
static const char* const kDBPropertySetMethod =
|
|
"org.freedesktop.DBus.Properties.Set";
|
|
|
|
/*
|
|
* Minimum altitude reported as valid (in meters),
|
|
* https://en.wikipedia.org/wiki/List_of_places_on_land_with_elevations_below_sea_level
|
|
* says that lowest land in the world is at -430 m, so let's use -500 m here.
|
|
*/
|
|
static const double kGCMinAlt = -500;
|
|
|
|
/*
|
|
* Matches "enum GClueAccuracyLevel" values, see:
|
|
* https://www.freedesktop.org/software/geoclue/docs/geoclue-gclue-enums.html#GClueAccuracyLevel
|
|
*/
|
|
enum class GCAccuracyLevel {
|
|
None = 0,
|
|
Country = 1,
|
|
City = 4,
|
|
Neighborhood = 5,
|
|
Street = 6,
|
|
Exact = 8,
|
|
};
|
|
|
|
/*
|
|
* Whether to reuse D-Bus proxies between uses of this provider.
|
|
* Usually a good thing, can be disabled for debug purposes.
|
|
*/
|
|
static const bool kGCReuseDBusProxy = true;
|
|
|
|
class GCLocProviderPriv final : public nsIGeolocationProvider,
|
|
public SupportsWeakPtr {
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIGEOLOCATIONPROVIDER
|
|
|
|
GCLocProviderPriv();
|
|
|
|
private:
|
|
enum class Accuracy { Unset, Low, High };
|
|
// States:
|
|
// Uninit: The default / initial state, with no client proxy yet.
|
|
// Initing: Takes care of establishing the client connection (GetClient /
|
|
// ConnectClient / SetDesktopID).
|
|
// SettingAccuracy: Does SetAccuracy operation, knows it should just go idle
|
|
// after finishing it.
|
|
// SettingAccuracyForStart: Does SetAccuracy operation, knows it then needs
|
|
// to do a Start operation after finishing it.
|
|
// Idle: Fully initialized, but not running state (quiescent).
|
|
// Starting: Starts the client by calling the Start D-Bus method.
|
|
// Started: Normal running state.
|
|
// Stopping: Stops the client by calling the Stop D-Bus method, knows it
|
|
// should just go idle after finishing it.
|
|
// StoppingForRestart: Stops the client by calling the Stop D-Bus method as
|
|
// a part of a Stop -> Start sequence (with possibly
|
|
// an accuracy update between these method calls).
|
|
//
|
|
// Valid state transitions are:
|
|
// (any state) -> Uninit: Transition when a D-Bus call failed or
|
|
// provided invalid data.
|
|
//
|
|
// Watch() startup path:
|
|
// Uninit -> Initing: Transition after getting the very first Watch()
|
|
// request
|
|
// or any such request while not having the client proxy.
|
|
// Initing -> SettingAccuracyForStart: Transition after getting a successful
|
|
// SetDesktopID response.
|
|
// SettingAccuracyForStart -> Starting: Transition after getting a
|
|
// successful
|
|
// SetAccuracy response.
|
|
// Idle -> Starting: Transition after getting a Watch() request while in
|
|
// fully
|
|
// initialized, but not running state.
|
|
// SettingAccuracy -> SettingAccuracyForStart: Transition after getting a
|
|
// Watch()
|
|
// request in the middle of
|
|
// setting accuracy during idle
|
|
// status.
|
|
// Stopping -> StoppingForRestart: Transition after getting a Watch()
|
|
// request
|
|
// in the middle of doing a Stop D-Bus call
|
|
// for idle status.
|
|
// StoppingForRestart -> Starting: Transition after getting a successful
|
|
// Stop response as a part of a Stop ->
|
|
// Start sequence while the previously set
|
|
// accuracy is still correct.
|
|
// StoppingForRestart -> SettingAccuracyForStart: Transition after getting
|
|
// a successful Stop response
|
|
// as a part of a Stop ->
|
|
// Start sequence but the set
|
|
// accuracy needs updating.
|
|
// Starting -> Started: Transition after getting a successful Start
|
|
// response.
|
|
//
|
|
// Shutdown() path:
|
|
// (any state) -> Uninit: Transition when not reusing the client proxy for
|
|
// any reason.
|
|
// Started -> Stopping: Transition from normal running state when reusing
|
|
// the client proxy.
|
|
// SettingAccuracyForStart -> SettingAccuracy: Transition when doing
|
|
// a shutdown in the middle of
|
|
// setting accuracy for a start
|
|
// when reusing the client
|
|
// proxy.
|
|
// SettingAccuracy -> Idle: Transition after getting a successful
|
|
// SetAccuracy
|
|
// response.
|
|
// StoppingForRestart -> Stopping: Transition when doing shutdown
|
|
// in the middle of a Stop -> Start sequence
|
|
// when reusing the client proxy.
|
|
// Stopping -> Idle: Transition after getting a successful Stop response.
|
|
//
|
|
// SetHighAccuracy() path:
|
|
// Started -> StoppingForRestart: Transition when accuracy needs updating
|
|
// on a running client.
|
|
// (the rest of the flow in StoppingForRestart state is the same as when
|
|
// being in this state in the Watch() startup path)
|
|
enum class ClientState {
|
|
Uninit,
|
|
Initing,
|
|
SettingAccuracy,
|
|
SettingAccuracyForStart,
|
|
Idle,
|
|
Starting,
|
|
Started,
|
|
Stopping,
|
|
StoppingForRestart
|
|
};
|
|
|
|
~GCLocProviderPriv();
|
|
|
|
static bool AlwaysHighAccuracy();
|
|
|
|
void SetState(ClientState aNewState, const char* aNewStateStr);
|
|
|
|
void Update(nsIDOMGeoPosition* aPosition);
|
|
MOZ_CAN_RUN_SCRIPT void NotifyError(int aError);
|
|
MOZ_CAN_RUN_SCRIPT void DBusProxyError(const GError* aGError,
|
|
bool aResetManager = false);
|
|
|
|
MOZ_CAN_RUN_SCRIPT static void GetClientResponse(GDBusProxy* aProxy,
|
|
GAsyncResult* aResult,
|
|
gpointer aUserData);
|
|
void ConnectClient(const gchar* aClientPath);
|
|
MOZ_CAN_RUN_SCRIPT static void ConnectClientResponse(GObject* aObject,
|
|
GAsyncResult* aResult,
|
|
gpointer aUserData);
|
|
void SetDesktopID();
|
|
MOZ_CAN_RUN_SCRIPT static void SetDesktopIDResponse(GDBusProxy* aProxy,
|
|
GAsyncResult* aResult,
|
|
gpointer aUserData);
|
|
void SetAccuracy();
|
|
MOZ_CAN_RUN_SCRIPT static void SetAccuracyResponse(GDBusProxy* aProxy,
|
|
GAsyncResult* aResult,
|
|
gpointer aUserData);
|
|
void StartClient();
|
|
MOZ_CAN_RUN_SCRIPT static void StartClientResponse(GDBusProxy* aProxy,
|
|
GAsyncResult* aResult,
|
|
gpointer aUserData);
|
|
void StopClient(bool aForRestart);
|
|
MOZ_CAN_RUN_SCRIPT static void StopClientResponse(GDBusProxy* aProxy,
|
|
GAsyncResult* aResult,
|
|
gpointer aUserData);
|
|
void StopClientNoWait();
|
|
void MaybeRestartForAccuracy();
|
|
|
|
MOZ_CAN_RUN_SCRIPT static void GCManagerOwnerNotify(GObject* aObject,
|
|
GParamSpec* aPSpec,
|
|
gpointer aUserData);
|
|
static void GCClientSignal(GDBusProxy* aProxy, gchar* aSenderName,
|
|
gchar* aSignalName, GVariant* aParameters,
|
|
gpointer aUserData);
|
|
void ConnectLocation(const gchar* aLocationPath);
|
|
static bool GetLocationProperty(GDBusProxy* aProxyLocation,
|
|
const gchar* aName, double* aOut);
|
|
static void ConnectLocationResponse(GObject* aObject, GAsyncResult* aResult,
|
|
gpointer aUserData);
|
|
|
|
void StartLastPositionTimer();
|
|
void StopPositionTimer();
|
|
void UpdateLastPosition();
|
|
|
|
void StartMLSFallbackTimerIfNeeded();
|
|
void StopMLSFallbackTimer();
|
|
void MLSFallbackTimerFired();
|
|
|
|
bool InDBusCall();
|
|
bool InDBusStoppingCall();
|
|
bool InDBusStoppedCall();
|
|
|
|
void DeleteManager();
|
|
void DoShutdown(bool aDeleteClient, bool aDeleteManager);
|
|
void DoShutdownClearCallback(bool aDestroying);
|
|
|
|
nsresult FallbackToMLS(MLSFallback::FallbackReason aReason);
|
|
void StopMLSFallback();
|
|
|
|
void WatchStart();
|
|
|
|
Accuracy mAccuracyWanted = Accuracy::Unset;
|
|
Accuracy mAccuracySet = Accuracy::Unset;
|
|
RefPtr<GDBusProxy> mProxyManager;
|
|
RefPtr<GDBusProxy> mProxyClient;
|
|
RefPtr<GCancellable> mCancellable;
|
|
nsCOMPtr<nsIGeolocationUpdate> mCallback;
|
|
ClientState mClientState = ClientState::Uninit;
|
|
RefPtr<nsIDOMGeoPosition> mLastPosition;
|
|
RefPtr<nsITimer> mLastPositionTimer;
|
|
RefPtr<nsITimer> mMLSFallbackTimer;
|
|
RefPtr<MLSFallback> mMLSFallback;
|
|
};
|
|
|
|
class GCLocWeakCallback final : public nsITimerCallback, public nsINamed {
|
|
using Method = void (GCLocProviderPriv::*)();
|
|
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSITIMERCALLBACK
|
|
|
|
explicit GCLocWeakCallback(GCLocProviderPriv* aParent, const char* aName,
|
|
Method aMethod)
|
|
: mParent(aParent), mName(aName), mMethod(aMethod) {}
|
|
|
|
NS_IMETHOD GetName(nsACString& aName) override {
|
|
aName = mName;
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
~GCLocWeakCallback() = default;
|
|
WeakPtr<GCLocProviderPriv> mParent;
|
|
const char* mName = nullptr;
|
|
Method mMethod = nullptr;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(GCLocWeakCallback, nsITimerCallback, nsINamed)
|
|
|
|
NS_IMETHODIMP
|
|
GCLocWeakCallback::Notify(nsITimer* aTimer) {
|
|
if (RefPtr parent = mParent.get()) {
|
|
(parent->*mMethod)();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
//
|
|
// GCLocProviderPriv
|
|
//
|
|
|
|
#define GCLP_SETSTATE(this, state) this->SetState(ClientState::state, #state)
|
|
|
|
GCLocProviderPriv::GCLocProviderPriv() {
|
|
if (AlwaysHighAccuracy()) {
|
|
mAccuracyWanted = Accuracy::High;
|
|
} else {
|
|
mAccuracyWanted = Accuracy::Low;
|
|
}
|
|
}
|
|
|
|
GCLocProviderPriv::~GCLocProviderPriv() { DoShutdownClearCallback(true); }
|
|
|
|
bool GCLocProviderPriv::AlwaysHighAccuracy() {
|
|
return StaticPrefs::geo_provider_geoclue_always_high_accuracy();
|
|
}
|
|
|
|
void GCLocProviderPriv::SetState(ClientState aNewState,
|
|
const char* aNewStateStr) {
|
|
if (mClientState == aNewState) {
|
|
return;
|
|
}
|
|
|
|
GCL_LOG(Debug, "changing state to %s", aNewStateStr);
|
|
mClientState = aNewState;
|
|
}
|
|
|
|
void GCLocProviderPriv::Update(nsIDOMGeoPosition* aPosition) {
|
|
if (!mCallback) {
|
|
return;
|
|
}
|
|
|
|
mCallback->Update(aPosition);
|
|
}
|
|
|
|
void GCLocProviderPriv::UpdateLastPosition() {
|
|
MOZ_DIAGNOSTIC_ASSERT(mLastPosition, "No last position to update");
|
|
if (mMLSFallbackTimer) {
|
|
// We are not going to wait for MLS fallback anymore
|
|
glean::geolocation::fallback
|
|
.EnumGet(glean::geolocation::FallbackLabel::eNone)
|
|
.Add();
|
|
}
|
|
StopPositionTimer();
|
|
StopMLSFallbackTimer();
|
|
Update(mLastPosition);
|
|
}
|
|
|
|
nsresult GCLocProviderPriv::FallbackToMLS(MLSFallback::FallbackReason aReason) {
|
|
GCL_LOG(Debug, "trying to fall back to MLS");
|
|
StopMLSFallback();
|
|
|
|
RefPtr fallback = new MLSFallback(0);
|
|
MOZ_TRY(fallback->Startup(mCallback, aReason));
|
|
|
|
GCL_LOG(Debug, "Started up MLS fallback");
|
|
mMLSFallback = std::move(fallback);
|
|
return NS_OK;
|
|
}
|
|
|
|
void GCLocProviderPriv::StopMLSFallback() {
|
|
if (!mMLSFallback) {
|
|
return;
|
|
}
|
|
GCL_LOG(Debug, "Clearing MLS fallback");
|
|
if (mMLSFallback) {
|
|
mMLSFallback->Shutdown(MLSFallback::ShutdownReason::ProviderShutdown);
|
|
mMLSFallback = nullptr;
|
|
}
|
|
}
|
|
|
|
void GCLocProviderPriv::NotifyError(int aError) {
|
|
if (!mCallback) {
|
|
return;
|
|
}
|
|
|
|
// We errored out, try to fall back to MLS.
|
|
if (NS_SUCCEEDED(FallbackToMLS(MLSFallback::FallbackReason::Error))) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr callback = mCallback;
|
|
callback->NotifyError(aError);
|
|
}
|
|
|
|
void GCLocProviderPriv::DBusProxyError(const GError* aGError,
|
|
bool aResetManager) {
|
|
// that G_DBUS_ERROR below is actually a function call, not a constant
|
|
GQuark gdbusDomain = G_DBUS_ERROR;
|
|
int error = GeolocationPositionError_Binding::POSITION_UNAVAILABLE;
|
|
if (aGError) {
|
|
if (g_error_matches(aGError, gdbusDomain, G_DBUS_ERROR_TIMEOUT) ||
|
|
g_error_matches(aGError, gdbusDomain, G_DBUS_ERROR_TIMED_OUT)) {
|
|
error = GeolocationPositionError_Binding::TIMEOUT;
|
|
} else if (g_error_matches(aGError, gdbusDomain,
|
|
G_DBUS_ERROR_LIMITS_EXCEEDED) ||
|
|
g_error_matches(aGError, gdbusDomain,
|
|
G_DBUS_ERROR_ACCESS_DENIED) ||
|
|
g_error_matches(aGError, gdbusDomain,
|
|
G_DBUS_ERROR_AUTH_FAILED)) {
|
|
error = GeolocationPositionError_Binding::PERMISSION_DENIED;
|
|
}
|
|
}
|
|
|
|
DoShutdown(true, aResetManager);
|
|
NotifyError(error);
|
|
}
|
|
|
|
void GCLocProviderPriv::GetClientResponse(GDBusProxy* aProxy,
|
|
GAsyncResult* aResult,
|
|
gpointer aUserData) {
|
|
GUniquePtr<GError> error;
|
|
RefPtr<GVariant> variant = dont_AddRef(
|
|
g_dbus_proxy_call_finish(aProxy, aResult, getter_Transfers(error)));
|
|
if (!variant) {
|
|
GCL_LOG(Error, "Failed to get client: %s\n", error->message);
|
|
// if cancelled |self| might no longer be there
|
|
if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
|
RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
|
|
self->DBusProxyError(error.get(), true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
|
|
MOZ_DIAGNOSTIC_ASSERT(self->mClientState == ClientState::Initing,
|
|
"Client in a wrong state");
|
|
|
|
auto signalError = MakeScopeExit([&]() MOZ_CAN_RUN_SCRIPT_BOUNDARY {
|
|
self->DBusProxyError(nullptr, true);
|
|
});
|
|
|
|
if (!g_variant_is_of_type(variant, G_VARIANT_TYPE_TUPLE)) {
|
|
GCL_LOG(Error, "Unexpected get client call return type: %s\n",
|
|
g_variant_get_type_string(variant));
|
|
return;
|
|
}
|
|
|
|
if (g_variant_n_children(variant) < 1) {
|
|
GCL_LOG(Error,
|
|
"Not enough params in get client call return: %" G_GSIZE_FORMAT
|
|
"\n",
|
|
g_variant_n_children(variant));
|
|
return;
|
|
}
|
|
|
|
variant = dont_AddRef(g_variant_get_child_value(variant, 0));
|
|
if (!g_variant_is_of_type(variant, G_VARIANT_TYPE_OBJECT_PATH)) {
|
|
GCL_LOG(Error, "Unexpected get client call return type inside tuple: %s\n",
|
|
g_variant_get_type_string(variant));
|
|
return;
|
|
}
|
|
|
|
const gchar* clientPath = g_variant_get_string(variant, nullptr);
|
|
GCL_LOG(Debug, "Client path: %s\n", clientPath);
|
|
|
|
signalError.release();
|
|
self->ConnectClient(clientPath);
|
|
}
|
|
|
|
void GCLocProviderPriv::ConnectClient(const gchar* aClientPath) {
|
|
MOZ_DIAGNOSTIC_ASSERT(mClientState == ClientState::Initing,
|
|
"Client in a wrong state");
|
|
MOZ_ASSERT(mCancellable, "Watch() wasn't successfully called");
|
|
g_dbus_proxy_new_for_bus(
|
|
G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, nullptr, kGeoclueBusName,
|
|
aClientPath, kGCClientInterface, mCancellable,
|
|
reinterpret_cast<GAsyncReadyCallback>(ConnectClientResponse), this);
|
|
}
|
|
|
|
void GCLocProviderPriv::ConnectClientResponse(GObject* aObject,
|
|
GAsyncResult* aResult,
|
|
gpointer aUserData) {
|
|
GUniquePtr<GError> error;
|
|
RefPtr<GDBusProxy> proxyClient =
|
|
dont_AddRef(g_dbus_proxy_new_finish(aResult, getter_Transfers(error)));
|
|
if (!proxyClient) {
|
|
// if cancelled |self| might no longer be there
|
|
if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
|
GCL_LOG(Error, "Failed to connect to client: %s\n", error->message);
|
|
RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
|
|
self->DBusProxyError(error.get());
|
|
}
|
|
return;
|
|
}
|
|
RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
|
|
self->mProxyClient = std::move(proxyClient);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(self->mClientState == ClientState::Initing,
|
|
"Client in a wrong state");
|
|
|
|
GCL_LOG(Info, "Client interface connected\n");
|
|
|
|
g_signal_connect(self->mProxyClient, "g-signal", G_CALLBACK(GCClientSignal),
|
|
self);
|
|
self->SetDesktopID();
|
|
}
|
|
|
|
void GCLocProviderPriv::SetDesktopID() {
|
|
MOZ_DIAGNOSTIC_ASSERT(mClientState == ClientState::Initing,
|
|
"Client in a wrong state");
|
|
MOZ_DIAGNOSTIC_ASSERT(mProxyClient && mCancellable,
|
|
"Watch() wasn't successfully called");
|
|
|
|
nsAutoCString appName;
|
|
gAppData->GetDBusAppName(appName);
|
|
g_dbus_proxy_call(mProxyClient, kDBPropertySetMethod,
|
|
g_variant_new("(ssv)", kGCClientInterface, "DesktopId",
|
|
g_variant_new_string(appName.get())),
|
|
G_DBUS_CALL_FLAGS_NONE, -1, mCancellable,
|
|
reinterpret_cast<GAsyncReadyCallback>(SetDesktopIDResponse),
|
|
this);
|
|
}
|
|
|
|
void GCLocProviderPriv::SetDesktopIDResponse(GDBusProxy* aProxy,
|
|
GAsyncResult* aResult,
|
|
gpointer aUserData) {
|
|
GUniquePtr<GError> error;
|
|
|
|
RefPtr<GVariant> variant = dont_AddRef(
|
|
g_dbus_proxy_call_finish(aProxy, aResult, getter_Transfers(error)));
|
|
if (!variant) {
|
|
// if cancelled |self| might no longer be there
|
|
if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
|
GCL_LOG(Error, "Failed to set DesktopId: %s\n", error->message);
|
|
RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
|
|
self->DBusProxyError(error.get());
|
|
}
|
|
return;
|
|
}
|
|
|
|
RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
|
|
MOZ_DIAGNOSTIC_ASSERT(self->mClientState == ClientState::Initing,
|
|
"Client in a wrong state");
|
|
|
|
GCLP_SETSTATE(self, Idle);
|
|
self->SetAccuracy();
|
|
}
|
|
|
|
void GCLocProviderPriv::SetAccuracy() {
|
|
MOZ_DIAGNOSTIC_ASSERT(mClientState == ClientState::Idle,
|
|
"Client in a wrong state");
|
|
MOZ_DIAGNOSTIC_ASSERT(mProxyClient && mCancellable,
|
|
"Watch() wasn't successfully called");
|
|
MOZ_ASSERT(mAccuracyWanted != Accuracy::Unset, "Invalid accuracy");
|
|
|
|
guint32 accuracy;
|
|
if (mAccuracyWanted == Accuracy::High) {
|
|
accuracy = (guint32)GCAccuracyLevel::Exact;
|
|
} else {
|
|
accuracy = (guint32)GCAccuracyLevel::City;
|
|
}
|
|
|
|
mAccuracySet = mAccuracyWanted;
|
|
GCLP_SETSTATE(this, SettingAccuracyForStart);
|
|
g_dbus_proxy_call(
|
|
mProxyClient, kDBPropertySetMethod,
|
|
g_variant_new("(ssv)", kGCClientInterface, "RequestedAccuracyLevel",
|
|
g_variant_new_uint32(accuracy)),
|
|
G_DBUS_CALL_FLAGS_NONE, -1, mCancellable,
|
|
reinterpret_cast<GAsyncReadyCallback>(SetAccuracyResponse), this);
|
|
}
|
|
|
|
void GCLocProviderPriv::SetAccuracyResponse(GDBusProxy* aProxy,
|
|
GAsyncResult* aResult,
|
|
gpointer aUserData) {
|
|
GUniquePtr<GError> error;
|
|
RefPtr<GVariant> variant = dont_AddRef(
|
|
g_dbus_proxy_call_finish(aProxy, aResult, getter_Transfers(error)));
|
|
if (!variant) {
|
|
// if cancelled |self| might no longer be there
|
|
if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
|
GCL_LOG(Error, "Failed to set requested accuracy level: %s\n",
|
|
error->message);
|
|
RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
|
|
self->DBusProxyError(error.get());
|
|
}
|
|
return;
|
|
}
|
|
|
|
RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
|
|
MOZ_DIAGNOSTIC_ASSERT(
|
|
self->mClientState == ClientState::SettingAccuracyForStart ||
|
|
self->mClientState == ClientState::SettingAccuracy,
|
|
"Client in a wrong state");
|
|
bool wantStart = self->mClientState == ClientState::SettingAccuracyForStart;
|
|
GCLP_SETSTATE(self, Idle);
|
|
|
|
if (wantStart) {
|
|
self->StartClient();
|
|
}
|
|
}
|
|
|
|
void GCLocProviderPriv::StartClient() {
|
|
MOZ_DIAGNOSTIC_ASSERT(mClientState == ClientState::Idle,
|
|
"Client in a wrong state");
|
|
MOZ_DIAGNOSTIC_ASSERT(mProxyClient && mCancellable,
|
|
"Watch() wasn't successfully called");
|
|
GCLP_SETSTATE(this, Starting);
|
|
g_dbus_proxy_call(
|
|
mProxyClient, "Start", nullptr, G_DBUS_CALL_FLAGS_NONE, -1, mCancellable,
|
|
reinterpret_cast<GAsyncReadyCallback>(StartClientResponse), this);
|
|
}
|
|
|
|
void GCLocProviderPriv::StartClientResponse(GDBusProxy* aProxy,
|
|
GAsyncResult* aResult,
|
|
gpointer aUserData) {
|
|
GUniquePtr<GError> error;
|
|
|
|
RefPtr<GVariant> variant = dont_AddRef(
|
|
g_dbus_proxy_call_finish(aProxy, aResult, getter_Transfers(error)));
|
|
if (!variant) {
|
|
// if cancelled |self| might no longer be there
|
|
if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
|
GCL_LOG(Error, "Failed to start client: %s\n", error->message);
|
|
RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
|
|
/*
|
|
* A workaround for
|
|
* https://gitlab.freedesktop.org/geoclue/geoclue/-/issues/143 We need to
|
|
* get a new client instance once the agent finally connects to the
|
|
* Geoclue service, otherwise every Start request on the old client
|
|
* interface will be denied. We need to reconnect to the Manager interface
|
|
* to achieve this since otherwise GetClient call will simply return the
|
|
* old client instance.
|
|
*/
|
|
bool resetManager = g_error_matches(error.get(), G_DBUS_ERROR,
|
|
G_DBUS_ERROR_ACCESS_DENIED);
|
|
self->DBusProxyError(error.get(), resetManager);
|
|
}
|
|
return;
|
|
}
|
|
|
|
RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
|
|
MOZ_DIAGNOSTIC_ASSERT(self->mClientState == ClientState::Starting,
|
|
"Client in a wrong state");
|
|
GCLP_SETSTATE(self, Started);
|
|
// If we're started, and we don't get any location update in a reasonable
|
|
// amount of time, we fallback to MLS.
|
|
self->StartMLSFallbackTimerIfNeeded();
|
|
self->MaybeRestartForAccuracy();
|
|
}
|
|
|
|
void GCLocProviderPriv::StopClient(bool aForRestart) {
|
|
MOZ_DIAGNOSTIC_ASSERT(mClientState == ClientState::Started,
|
|
"Client in a wrong state");
|
|
MOZ_DIAGNOSTIC_ASSERT(mProxyClient && mCancellable,
|
|
"Watch() wasn't successfully called");
|
|
|
|
if (aForRestart) {
|
|
GCLP_SETSTATE(this, StoppingForRestart);
|
|
} else {
|
|
GCLP_SETSTATE(this, Stopping);
|
|
}
|
|
|
|
g_dbus_proxy_call(
|
|
mProxyClient, "Stop", nullptr, G_DBUS_CALL_FLAGS_NONE, -1, mCancellable,
|
|
reinterpret_cast<GAsyncReadyCallback>(StopClientResponse), this);
|
|
}
|
|
|
|
void GCLocProviderPriv::StopClientResponse(GDBusProxy* aProxy,
|
|
GAsyncResult* aResult,
|
|
gpointer aUserData) {
|
|
GUniquePtr<GError> error;
|
|
RefPtr<GVariant> variant = dont_AddRef(
|
|
g_dbus_proxy_call_finish(aProxy, aResult, getter_Transfers(error)));
|
|
if (!variant) {
|
|
// if cancelled |self| might no longer be there
|
|
if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
|
GCL_LOG(Error, "Failed to stop client: %s\n", error->message);
|
|
RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
|
|
self->DBusProxyError(error.get());
|
|
}
|
|
return;
|
|
}
|
|
|
|
RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
|
|
MOZ_DIAGNOSTIC_ASSERT(self->InDBusStoppingCall(), "Client in a wrong state");
|
|
bool wantRestart = self->mClientState == ClientState::StoppingForRestart;
|
|
GCLP_SETSTATE(self, Idle);
|
|
|
|
if (!wantRestart) {
|
|
return;
|
|
}
|
|
|
|
if (self->mAccuracyWanted != self->mAccuracySet) {
|
|
self->SetAccuracy();
|
|
} else {
|
|
self->StartClient();
|
|
}
|
|
}
|
|
|
|
void GCLocProviderPriv::StopClientNoWait() {
|
|
MOZ_DIAGNOSTIC_ASSERT(mProxyClient, "Watch() wasn't successfully called");
|
|
g_dbus_proxy_call(mProxyClient, "Stop", nullptr, G_DBUS_CALL_FLAGS_NONE, -1,
|
|
nullptr, nullptr, nullptr);
|
|
}
|
|
|
|
void GCLocProviderPriv::MaybeRestartForAccuracy() {
|
|
if (mAccuracyWanted == mAccuracySet) {
|
|
return;
|
|
}
|
|
|
|
if (mClientState != ClientState::Started) {
|
|
return;
|
|
}
|
|
|
|
// Setting a new accuracy requires restarting the client
|
|
StopClient(true);
|
|
}
|
|
|
|
void GCLocProviderPriv::GCManagerOwnerNotify(GObject* aObject,
|
|
GParamSpec* aPSpec,
|
|
gpointer aUserData) {
|
|
RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
|
|
GUniquePtr<gchar> managerOwner(
|
|
g_dbus_proxy_get_name_owner(self->mProxyManager));
|
|
if (!managerOwner) {
|
|
GCL_LOG(Info, "The Manager interface has lost its owner\n");
|
|
self->DBusProxyError(nullptr, true);
|
|
}
|
|
}
|
|
|
|
void GCLocProviderPriv::GCClientSignal(GDBusProxy* aProxy, gchar* aSenderName,
|
|
gchar* aSignalName,
|
|
GVariant* aParameters,
|
|
gpointer aUserData) {
|
|
GCL_LOG(Info, "%s: %s (%s)\n", __PRETTY_FUNCTION__, aSignalName,
|
|
GUniquePtr<gchar>(g_variant_print(aParameters, TRUE)).get());
|
|
|
|
if (g_strcmp0(aSignalName, "LocationUpdated")) {
|
|
return;
|
|
}
|
|
|
|
if (!g_variant_is_of_type(aParameters, G_VARIANT_TYPE_TUPLE)) {
|
|
GCL_LOG(Error, "Unexpected location updated signal params type: %s\n",
|
|
g_variant_get_type_string(aParameters));
|
|
return;
|
|
}
|
|
|
|
if (g_variant_n_children(aParameters) < 2) {
|
|
GCL_LOG(Error,
|
|
"Not enough params in location updated signal: %" G_GSIZE_FORMAT
|
|
"\n",
|
|
g_variant_n_children(aParameters));
|
|
return;
|
|
}
|
|
|
|
RefPtr<GVariant> variant =
|
|
dont_AddRef(g_variant_get_child_value(aParameters, 1));
|
|
if (!g_variant_is_of_type(variant, G_VARIANT_TYPE_OBJECT_PATH)) {
|
|
GCL_LOG(Error,
|
|
"Unexpected location updated signal new location path type: %s\n",
|
|
g_variant_get_type_string(variant));
|
|
return;
|
|
}
|
|
|
|
RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
|
|
const gchar* locationPath = g_variant_get_string(variant, nullptr);
|
|
GCL_LOG(Verbose, "New location path: %s\n", locationPath);
|
|
self->ConnectLocation(locationPath);
|
|
}
|
|
|
|
void GCLocProviderPriv::ConnectLocation(const gchar* aLocationPath) {
|
|
MOZ_ASSERT(mCancellable, "Startup() wasn't successfully called");
|
|
g_dbus_proxy_new_for_bus(
|
|
G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, nullptr, kGeoclueBusName,
|
|
aLocationPath, kGCLocationInterface, mCancellable,
|
|
reinterpret_cast<GAsyncReadyCallback>(ConnectLocationResponse), this);
|
|
}
|
|
|
|
bool GCLocProviderPriv::GetLocationProperty(GDBusProxy* aProxyLocation,
|
|
const gchar* aName, double* aOut) {
|
|
RefPtr<GVariant> property =
|
|
dont_AddRef(g_dbus_proxy_get_cached_property(aProxyLocation, aName));
|
|
if (!g_variant_is_of_type(property, G_VARIANT_TYPE_DOUBLE)) {
|
|
GCL_LOG(Error, "Unexpected location property %s type: %s\n", aName,
|
|
g_variant_get_type_string(property));
|
|
return false;
|
|
}
|
|
|
|
*aOut = g_variant_get_double(property);
|
|
return true;
|
|
}
|
|
|
|
void GCLocProviderPriv::ConnectLocationResponse(GObject* aObject,
|
|
GAsyncResult* aResult,
|
|
gpointer aUserData) {
|
|
GUniquePtr<GError> error;
|
|
RefPtr<GDBusProxy> proxyLocation =
|
|
dont_AddRef(g_dbus_proxy_new_finish(aResult, getter_Transfers(error)));
|
|
if (!proxyLocation) {
|
|
if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
|
GCL_LOG(Warning, "Failed to connect to location: %s\n", error->message);
|
|
}
|
|
return;
|
|
}
|
|
|
|
RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
|
|
/*
|
|
* nsGeoPositionCoords will convert NaNs to null for optional properties of
|
|
* the JavaScript Coordinates object.
|
|
*/
|
|
double lat = UnspecifiedNaN<double>();
|
|
double lon = UnspecifiedNaN<double>();
|
|
double alt = UnspecifiedNaN<double>();
|
|
double hError = UnspecifiedNaN<double>();
|
|
const double vError = UnspecifiedNaN<double>();
|
|
double heading = UnspecifiedNaN<double>();
|
|
double speed = UnspecifiedNaN<double>();
|
|
struct {
|
|
const gchar* name;
|
|
double* out;
|
|
} props[] = {
|
|
{"Latitude", &lat}, {"Longitude", &lon}, {"Altitude", &alt},
|
|
{"Accuracy", &hError}, {"Heading", &heading}, {"Speed", &speed},
|
|
};
|
|
|
|
for (auto& prop : props) {
|
|
if (!GetLocationProperty(proxyLocation, prop.name, prop.out)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (alt < kGCMinAlt) {
|
|
alt = UnspecifiedNaN<double>();
|
|
}
|
|
if (speed < 0) {
|
|
speed = UnspecifiedNaN<double>();
|
|
}
|
|
if (heading < 0 || std::isnan(speed) || speed == 0) {
|
|
heading = UnspecifiedNaN<double>();
|
|
}
|
|
|
|
GCL_LOG(Info, "New location: %f %f +-%fm @ %gm; hdg %f spd %fm/s\n", lat, lon,
|
|
hError, alt, heading, speed);
|
|
|
|
self->mLastPosition =
|
|
new nsGeoPosition(lat, lon, alt, hError, vError, heading, speed,
|
|
PR_Now() / PR_USEC_PER_MSEC);
|
|
self->UpdateLastPosition();
|
|
}
|
|
|
|
void GCLocProviderPriv::StartLastPositionTimer() {
|
|
MOZ_DIAGNOSTIC_ASSERT(mLastPosition, "no last position to report");
|
|
|
|
StopPositionTimer();
|
|
|
|
RefPtr timerCallback = new GCLocWeakCallback(
|
|
this, "UpdateLastPosition", &GCLocProviderPriv::UpdateLastPosition);
|
|
NS_NewTimerWithCallback(getter_AddRefs(mLastPositionTimer), timerCallback,
|
|
1000, nsITimer::TYPE_ONE_SHOT);
|
|
}
|
|
|
|
void GCLocProviderPriv::StopPositionTimer() {
|
|
if (!mLastPositionTimer) {
|
|
return;
|
|
}
|
|
|
|
mLastPositionTimer->Cancel();
|
|
mLastPositionTimer = nullptr;
|
|
}
|
|
|
|
void GCLocProviderPriv::StartMLSFallbackTimerIfNeeded() {
|
|
StopMLSFallbackTimer();
|
|
if (mLastPosition) {
|
|
// If we already have a location we're good.
|
|
return;
|
|
}
|
|
|
|
uint32_t delay = StaticPrefs::geo_provider_geoclue_mls_fallback_timeout_ms();
|
|
if (!delay) {
|
|
return;
|
|
}
|
|
|
|
RefPtr timerCallback = new GCLocWeakCallback(
|
|
this, "MLSFallbackTimerFired", &GCLocProviderPriv::MLSFallbackTimerFired);
|
|
NS_NewTimerWithCallback(getter_AddRefs(mMLSFallbackTimer), timerCallback,
|
|
delay, nsITimer::TYPE_ONE_SHOT);
|
|
}
|
|
|
|
void GCLocProviderPriv::StopMLSFallbackTimer() {
|
|
if (!mMLSFallbackTimer) {
|
|
return;
|
|
}
|
|
mMLSFallbackTimer->Cancel();
|
|
mMLSFallbackTimer = nullptr;
|
|
}
|
|
|
|
void GCLocProviderPriv::MLSFallbackTimerFired() {
|
|
mMLSFallbackTimer = nullptr;
|
|
|
|
if (mMLSFallback || mLastPosition || mClientState != ClientState::Started) {
|
|
return;
|
|
}
|
|
|
|
GCL_LOG(Info,
|
|
"Didn't get a location in a reasonable amount of time, trying to "
|
|
"fall back to MLS");
|
|
FallbackToMLS(MLSFallback::FallbackReason::Timeout);
|
|
}
|
|
|
|
// Did we made some D-Bus call and are still waiting for its response?
|
|
bool GCLocProviderPriv::InDBusCall() {
|
|
return mClientState == ClientState::Initing ||
|
|
mClientState == ClientState::SettingAccuracy ||
|
|
mClientState == ClientState::SettingAccuracyForStart ||
|
|
mClientState == ClientState::Starting ||
|
|
mClientState == ClientState::Stopping ||
|
|
mClientState == ClientState::StoppingForRestart;
|
|
}
|
|
|
|
bool GCLocProviderPriv::InDBusStoppingCall() {
|
|
return mClientState == ClientState::Stopping ||
|
|
mClientState == ClientState::StoppingForRestart;
|
|
}
|
|
|
|
/*
|
|
* Did we made some D-Bus call while stopped and
|
|
* are still waiting for its response?
|
|
*/
|
|
bool GCLocProviderPriv::InDBusStoppedCall() {
|
|
return mClientState == ClientState::SettingAccuracy ||
|
|
mClientState == ClientState::SettingAccuracyForStart;
|
|
}
|
|
|
|
void GCLocProviderPriv::DeleteManager() {
|
|
if (!mProxyManager) {
|
|
return;
|
|
}
|
|
|
|
g_signal_handlers_disconnect_matched(mProxyManager, G_SIGNAL_MATCH_DATA, 0, 0,
|
|
nullptr, nullptr, this);
|
|
mProxyManager = nullptr;
|
|
}
|
|
|
|
void GCLocProviderPriv::DoShutdown(bool aDeleteClient, bool aDeleteManager) {
|
|
MOZ_DIAGNOSTIC_ASSERT(
|
|
!aDeleteManager || aDeleteClient,
|
|
"deleting manager proxy requires deleting client one, too");
|
|
|
|
// Invalidate the cached last position
|
|
StopPositionTimer();
|
|
StopMLSFallbackTimer();
|
|
mLastPosition = nullptr;
|
|
|
|
/*
|
|
* Do we need to delete the D-Bus proxy (or proxies)?
|
|
* Either because that's what our caller wanted, or because we are set to
|
|
* never reuse them, or because we are in a middle of some D-Bus call while
|
|
* having the service running (and so not being able to issue an immediate
|
|
* Stop call).
|
|
*/
|
|
if (aDeleteClient || !kGCReuseDBusProxy ||
|
|
(InDBusCall() && !InDBusStoppingCall() && !InDBusStoppedCall())) {
|
|
if (mClientState == ClientState::Started) {
|
|
StopClientNoWait();
|
|
GCLP_SETSTATE(this, Idle);
|
|
}
|
|
if (mProxyClient) {
|
|
g_signal_handlers_disconnect_matched(mProxyClient, G_SIGNAL_MATCH_DATA, 0,
|
|
0, nullptr, nullptr, this);
|
|
}
|
|
if (mCancellable) {
|
|
g_cancellable_cancel(mCancellable);
|
|
mCancellable = nullptr;
|
|
}
|
|
mProxyClient = nullptr;
|
|
|
|
if (aDeleteManager || !kGCReuseDBusProxy) {
|
|
DeleteManager();
|
|
}
|
|
|
|
GCLP_SETSTATE(this, Uninit);
|
|
} else if (mClientState == ClientState::Started) {
|
|
StopClient(false);
|
|
} else if (mClientState == ClientState::SettingAccuracyForStart) {
|
|
GCLP_SETSTATE(this, SettingAccuracy);
|
|
} else if (mClientState == ClientState::StoppingForRestart) {
|
|
GCLP_SETSTATE(this, Stopping);
|
|
}
|
|
}
|
|
|
|
void GCLocProviderPriv::DoShutdownClearCallback(bool aDestroying) {
|
|
mCallback = nullptr;
|
|
StopMLSFallback();
|
|
DoShutdown(aDestroying, aDestroying);
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(GCLocProviderPriv, nsIGeolocationProvider)
|
|
|
|
// nsIGeolocationProvider
|
|
//
|
|
|
|
/*
|
|
* The Startup() method should only succeed if Geoclue is available on D-Bus
|
|
* so it can be used for determining whether to continue with this geolocation
|
|
* provider in Geolocation.cpp
|
|
*/
|
|
NS_IMETHODIMP
|
|
GCLocProviderPriv::Startup() {
|
|
if (mProxyManager) {
|
|
return NS_OK;
|
|
}
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mClientState == ClientState::Uninit,
|
|
"Client in a initialized state but no manager");
|
|
|
|
GUniquePtr<GError> error;
|
|
mProxyManager = dont_AddRef(g_dbus_proxy_new_for_bus_sync(
|
|
G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, nullptr, kGeoclueBusName,
|
|
kGCManagerPath, kGCManagerInterface, nullptr, getter_Transfers(error)));
|
|
if (!mProxyManager) {
|
|
GCL_LOG(Info, "Cannot connect to the Manager interface: %s\n",
|
|
error->message);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
g_signal_connect(mProxyManager, "notify::g-name-owner",
|
|
G_CALLBACK(GCManagerOwnerNotify), this);
|
|
|
|
GUniquePtr<gchar> managerOwner(g_dbus_proxy_get_name_owner(mProxyManager));
|
|
if (!managerOwner) {
|
|
GCL_LOG(Info, "The Manager interface has no owner\n");
|
|
DeleteManager();
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
GCL_LOG(Info, "Manager interface connected successfully\n");
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void GCLocProviderPriv::WatchStart() {
|
|
if (mClientState == ClientState::Idle) {
|
|
StartClient();
|
|
} else if (mClientState == ClientState::Started) {
|
|
if (mLastPosition && !mLastPositionTimer) {
|
|
GCL_LOG(Verbose,
|
|
"Will report the existing position if new one doesn't come up\n");
|
|
StartLastPositionTimer();
|
|
}
|
|
} else if (mClientState == ClientState::SettingAccuracy) {
|
|
GCLP_SETSTATE(this, SettingAccuracyForStart);
|
|
} else if (mClientState == ClientState::Stopping) {
|
|
GCLP_SETSTATE(this, StoppingForRestart);
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GCLocProviderPriv::Watch(nsIGeolocationUpdate* aCallback) {
|
|
mCallback = aCallback;
|
|
|
|
if (!mCancellable) {
|
|
mCancellable = dont_AddRef(g_cancellable_new());
|
|
}
|
|
|
|
if (mClientState != ClientState::Uninit) {
|
|
WatchStart();
|
|
return NS_OK;
|
|
}
|
|
|
|
if (!mProxyManager) {
|
|
GCL_LOG(Debug, "watch request falling back to MLS");
|
|
return FallbackToMLS(MLSFallback::FallbackReason::Error);
|
|
}
|
|
|
|
StopMLSFallback();
|
|
|
|
GCLP_SETSTATE(this, Initing);
|
|
g_dbus_proxy_call(mProxyManager, "GetClient", nullptr, G_DBUS_CALL_FLAGS_NONE,
|
|
-1, mCancellable,
|
|
reinterpret_cast<GAsyncReadyCallback>(GetClientResponse),
|
|
this);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GCLocProviderPriv::Shutdown() {
|
|
DoShutdownClearCallback(false);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GCLocProviderPriv::SetHighAccuracy(bool aHigh) {
|
|
GCL_LOG(Verbose, "Want %s accuracy\n", aHigh ? "high" : "low");
|
|
if (!aHigh && AlwaysHighAccuracy()) {
|
|
GCL_LOG(Verbose, "Forcing high accuracy due to pref\n");
|
|
aHigh = true;
|
|
}
|
|
|
|
mAccuracyWanted = aHigh ? Accuracy::High : Accuracy::Low;
|
|
MaybeRestartForAccuracy();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
GeoclueLocationProvider::GeoclueLocationProvider() {
|
|
mPriv = new GCLocProviderPriv;
|
|
}
|
|
|
|
// nsISupports
|
|
//
|
|
|
|
NS_IMPL_ISUPPORTS(GeoclueLocationProvider, nsIGeolocationProvider)
|
|
|
|
// nsIGeolocationProvider
|
|
//
|
|
|
|
NS_IMETHODIMP
|
|
GeoclueLocationProvider::Startup() { return mPriv->Startup(); }
|
|
|
|
NS_IMETHODIMP
|
|
GeoclueLocationProvider::Watch(nsIGeolocationUpdate* aCallback) {
|
|
return mPriv->Watch(aCallback);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
GeoclueLocationProvider::Shutdown() { return mPriv->Shutdown(); }
|
|
|
|
NS_IMETHODIMP
|
|
GeoclueLocationProvider::SetHighAccuracy(bool aHigh) {
|
|
return mPriv->SetHighAccuracy(aHigh);
|
|
}
|
|
|
|
} // namespace mozilla::dom
|