blob: 237fcd0e6d613a0af47d11e4444c1d5b838b2e64 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/geolocation/location_arbitrator.h"
#include <map>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "content/browser/geolocation/arbitrator_dependency_factory.h"
#include "content/public/browser/access_token_store.h"
#include "googleurl/src/gurl.h"
using content::AccessTokenStore;
using content::Geoposition;
namespace {
const char* kDefaultNetworkProviderUrl =
"https://www.googleapis.com/geolocation/v1/geolocate";
GeolocationArbitratorDependencyFactory* g_dependency_factory_for_test = NULL;
} // namespace
// To avoid oscillations, set this to twice the expected update interval of a
// a GPS-type location provider (in case it misses a beat) plus a little.
const int64 GeolocationArbitrator::kFixStaleTimeoutMilliseconds =
11 * base::Time::kMillisecondsPerSecond;
GeolocationArbitrator::GeolocationArbitrator(
GeolocationArbitratorDependencyFactory* dependency_factory,
GeolocationObserver* observer)
: dependency_factory_(dependency_factory),
access_token_store_(dependency_factory->NewAccessTokenStore()),
get_time_now_(dependency_factory->GetTimeFunction()),
observer_(observer),
position_provider_(NULL),
is_permission_granted_(false) {
}
GeolocationArbitrator::~GeolocationArbitrator() {
}
GeolocationArbitrator* GeolocationArbitrator::Create(
GeolocationObserver* observer) {
GeolocationArbitratorDependencyFactory* dependency_factory =
g_dependency_factory_for_test ?
g_dependency_factory_for_test :
new DefaultGeolocationArbitratorDependencyFactory;
GeolocationArbitrator* arbitrator =
new GeolocationArbitrator(dependency_factory, observer);
g_dependency_factory_for_test = NULL;
return arbitrator;
}
GURL GeolocationArbitrator::DefaultNetworkProviderURL() {
return GURL(kDefaultNetworkProviderUrl);
}
void GeolocationArbitrator::OnPermissionGranted() {
is_permission_granted_ = true;
for (ScopedVector<LocationProviderBase>::iterator i = providers_.begin();
i != providers_.end(); ++i) {
(*i)->OnPermissionGranted();
}
}
void GeolocationArbitrator::StartProviders(
const GeolocationObserverOptions& options) {
// Stash options as OnAccessTokenStoresLoaded has not yet been called.
current_provider_options_ = options;
if (providers_.empty()) {
DCHECK(DefaultNetworkProviderURL().is_valid());
access_token_store_->LoadAccessTokens(
base::Bind(&GeolocationArbitrator::OnAccessTokenStoresLoaded,
base::Unretained(this)));
} else {
DoStartProviders();
}
}
void GeolocationArbitrator::DoStartProviders() {
for (ScopedVector<LocationProviderBase>::iterator i = providers_.begin();
i != providers_.end(); ++i) {
(*i)->StartProvider(current_provider_options_.use_high_accuracy);
}
}
void GeolocationArbitrator::StopProviders() {
providers_.clear();
}
void GeolocationArbitrator::OnAccessTokenStoresLoaded(
AccessTokenStore::AccessTokenSet access_token_set,
net::URLRequestContextGetter* context_getter) {
if (!providers_.empty()) {
// A second StartProviders() call may have arrived before the first
// completed.
return;
}
// If there are no access tokens, boot strap it with the default server URL.
if (access_token_set.empty())
access_token_set[DefaultNetworkProviderURL()];
for (AccessTokenStore::AccessTokenSet::iterator i =
access_token_set.begin();
i != access_token_set.end(); ++i) {
RegisterProvider(
dependency_factory_->NewNetworkLocationProvider(
access_token_store_.get(), context_getter,
i->first, i->second));
}
RegisterProvider(dependency_factory_->NewSystemLocationProvider());
DoStartProviders();
}
void GeolocationArbitrator::RegisterProvider(
LocationProviderBase* provider) {
if (!provider)
return;
provider->RegisterListener(this);
if (is_permission_granted_)
provider->OnPermissionGranted();
providers_.push_back(provider);
}
void GeolocationArbitrator::LocationUpdateAvailable(
LocationProviderBase* provider) {
DCHECK(provider);
Geoposition new_position;
provider->GetPosition(&new_position);
DCHECK(new_position.Validate() ||
new_position.error_code != content::Geoposition::ERROR_CODE_NONE);
if (!IsNewPositionBetter(position_, new_position,
provider == position_provider_))
return;
position_provider_ = provider;
position_ = new_position;
observer_->OnLocationUpdate(position_);
}
bool GeolocationArbitrator::IsNewPositionBetter(
const Geoposition& old_position, const Geoposition& new_position,
bool from_same_provider) const {
// Updates location_info if it's better than what we currently have,
// or if it's a newer update from the same provider.
if (!old_position.Validate()) {
// Older location wasn't locked.
return true;
}
if (new_position.Validate()) {
// New location is locked, let's check if it's any better.
if (old_position.accuracy >= new_position.accuracy) {
// Accuracy is better.
return true;
} else if (from_same_provider) {
// Same provider, fresher location.
return true;
} else if ((get_time_now_() - old_position.timestamp).InMilliseconds() >
kFixStaleTimeoutMilliseconds) {
// Existing fix is stale.
return true;
}
}
return false;
}
bool GeolocationArbitrator::HasPermissionBeenGranted() const {
return is_permission_granted_;
}
void GeolocationArbitrator::SetDependencyFactoryForTest(
GeolocationArbitratorDependencyFactory* dependency_factory) {
g_dependency_factory_for_test = dependency_factory;
}