#include "services/device/geolocation/location_arbitrator.h"
#include <map>
#include <memory>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/task/single_thread_task_runner.h"
#include "build/build_config.h"
#include "services/device/geolocation/network_location_provider.h"
#include "services/device/geolocation/wifi_polling_policy.h"
#include "services/device/public/cpp/geolocation/geoposition.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace device {
const base::TimeDelta LocationArbitrator::kFixStaleTimeoutTimeDelta =
base::Seconds(11);
LocationArbitrator::LocationArbitrator(
const CustomLocationProviderCallback& custom_location_provider_getter,
GeolocationManager* geolocation_manager,
const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner,
const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
const std::string& api_key,
std::unique_ptr<PositionCache> position_cache)
: custom_location_provider_getter_(custom_location_provider_getter),
geolocation_manager_(geolocation_manager),
main_task_runner_(main_task_runner),
url_loader_factory_(url_loader_factory),
api_key_(api_key),
position_cache_(std::move(position_cache)) {}
LocationArbitrator::~LocationArbitrator() {
WifiPollingPolicy::Shutdown();
}
bool LocationArbitrator::HasPermissionBeenGrantedForTest() const {
return is_permission_granted_;
}
void LocationArbitrator::OnPermissionGranted() {
is_permission_granted_ = true;
for (const auto& provider : providers_)
provider->OnPermissionGranted();
}
void LocationArbitrator::StartProvider(bool enable_high_accuracy) {
is_running_ = true;
enable_high_accuracy_ = enable_high_accuracy;
if (providers_.empty()) {
RegisterProviders();
}
DoStartProviders();
}
void LocationArbitrator::DoStartProviders() {
if (providers_.empty()) {
arbitrator_update_callback_.Run(
this, mojom::GeopositionResult::NewError(mojom::GeopositionError::New(
mojom::GeopositionErrorCode::kPositionUnavailable, "", "")));
return;
}
for (const auto& provider : providers_) {
provider->StartProvider(enable_high_accuracy_);
}
}
void LocationArbitrator::StopProvider() {
#if BUILDFLAG(IS_OHOS)
base::AutoLock lock(lock_);
#endif
position_provider_ = nullptr;
result_.reset();
providers_.clear();
is_running_ = false;
}
void LocationArbitrator::RegisterProvider(
std::unique_ptr<LocationProvider> provider) {
if (!provider)
return;
provider->SetUpdateCallback(base::BindRepeating(
&LocationArbitrator::OnLocationUpdate, base::Unretained(this)));
if (is_permission_granted_)
provider->OnPermissionGranted();
providers_.push_back(std::move(provider));
}
void LocationArbitrator::RegisterProviders() {
if (custom_location_provider_getter_) {
auto custom_provider = custom_location_provider_getter_.Run();
if (custom_provider) {
RegisterProvider(std::move(custom_provider));
return;
}
}
auto system_provider = NewSystemLocationProvider();
if (system_provider) {
RegisterProvider(std::move(system_provider));
return;
}
if (url_loader_factory_)
RegisterProvider(NewNetworkLocationProvider(url_loader_factory_, api_key_));
}
void LocationArbitrator::OnLocationUpdate(
const LocationProvider* provider,
mojom::GeopositionResultPtr new_result) {
#if BUILDFLAG(IS_OHOS)
base::AutoLock lock(lock_);
if (!is_running_) {
return;
}
#endif
DCHECK(new_result);
DCHECK(new_result->is_error() ||
new_result->is_position() &&
ValidateGeoposition(*new_result->get_position()));
if (result_ && !IsNewPositionBetter(*result_, *new_result,
provider == position_provider_)) {
return;
}
position_provider_ = provider;
result_ = std::move(new_result);
arbitrator_update_callback_.Run(this, result_.Clone());
}
const mojom::GeopositionResult* LocationArbitrator::GetPosition() {
return result_.get();
}
void LocationArbitrator::SetUpdateCallback(
const LocationProviderUpdateCallback& callback) {
DCHECK(!callback.is_null());
arbitrator_update_callback_ = callback;
}
std::unique_ptr<LocationProvider>
LocationArbitrator::NewNetworkLocationProvider(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const std::string& api_key) {
DCHECK(url_loader_factory);
#if BUILDFLAG(IS_ANDROID)
return nullptr;
#else
return std::make_unique<NetworkLocationProvider>(
std::move(url_loader_factory), geolocation_manager_, main_task_runner_,
api_key, position_cache_.get());
#endif
}
std::unique_ptr<LocationProvider>
LocationArbitrator::NewSystemLocationProvider() {
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
return nullptr;
#else
return device::NewSystemLocationProvider(main_task_runner_,
geolocation_manager_);
#endif
}
base::Time LocationArbitrator::GetTimeNow() const {
return base::Time::Now();
}
bool LocationArbitrator::IsNewPositionBetter(
const mojom::GeopositionResult& old_result,
const mojom::GeopositionResult& new_result,
bool from_same_provider) const {
if (old_result.is_error() || !old_result.get_position()) {
return true;
}
const mojom::Geoposition& old_position = *old_result.get_position();
if (new_result.is_position()) {
const mojom::Geoposition& new_position = *new_result.get_position();
if (old_position.accuracy >= new_position.accuracy) {
return true;
} else if (from_same_provider) {
return true;
} else if (GetTimeNow() - old_position.timestamp >
kFixStaleTimeoutTimeDelta) {
return true;
}
}
return false;
}
}