1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/geolocation/location_arbitrator_impl.h"
6
7#include <map>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "content/browser/geolocation/network_location_provider.h"
12#include "content/public/browser/access_token_store.h"
13#include "content/public/browser/content_browser_client.h"
14#include "content/public/common/content_client.h"
15#include "url/gurl.h"
16
17namespace content {
18namespace {
19
20const char* kDefaultNetworkProviderUrl =
21    "https://www.googleapis.com/geolocation/v1/geolocate";
22}  // namespace
23
24// To avoid oscillations, set this to twice the expected update interval of a
25// a GPS-type location provider (in case it misses a beat) plus a little.
26const int64 LocationArbitratorImpl::kFixStaleTimeoutMilliseconds =
27    11 * base::Time::kMillisecondsPerSecond;
28
29LocationArbitratorImpl::LocationArbitratorImpl(
30    const LocationUpdateCallback& callback)
31    : callback_(callback),
32      provider_callback_(
33          base::Bind(&LocationArbitratorImpl::LocationUpdateAvailable,
34                     base::Unretained(this))),
35      position_provider_(NULL),
36      is_permission_granted_(false),
37      is_running_(false) {
38}
39
40LocationArbitratorImpl::~LocationArbitratorImpl() {
41}
42
43GURL LocationArbitratorImpl::DefaultNetworkProviderURL() {
44  return GURL(kDefaultNetworkProviderUrl);
45}
46
47void LocationArbitratorImpl::OnPermissionGranted() {
48  is_permission_granted_ = true;
49  for (ScopedVector<LocationProvider>::iterator i = providers_.begin();
50      i != providers_.end(); ++i) {
51    (*i)->OnPermissionGranted();
52  }
53}
54
55void LocationArbitratorImpl::StartProviders(bool use_high_accuracy) {
56  // Stash options as OnAccessTokenStoresLoaded has not yet been called.
57  is_running_ = true;
58  use_high_accuracy_ = use_high_accuracy;
59  if (providers_.empty()) {
60    DCHECK(DefaultNetworkProviderURL().is_valid());
61    GetAccessTokenStore()->LoadAccessTokens(
62        base::Bind(&LocationArbitratorImpl::OnAccessTokenStoresLoaded,
63                   base::Unretained(this)));
64  } else {
65    DoStartProviders();
66  }
67}
68
69void LocationArbitratorImpl::DoStartProviders() {
70  for (ScopedVector<LocationProvider>::iterator i = providers_.begin();
71       i != providers_.end(); ++i) {
72    (*i)->StartProvider(use_high_accuracy_);
73  }
74}
75
76void LocationArbitratorImpl::StopProviders() {
77  // Reset the reference location state (provider+position)
78  // so that future starts use fresh locations from
79  // the newly constructed providers.
80  position_provider_ = NULL;
81  position_ = Geoposition();
82
83  providers_.clear();
84  is_running_ = false;
85}
86
87void LocationArbitratorImpl::OnAccessTokenStoresLoaded(
88    AccessTokenStore::AccessTokenSet access_token_set,
89    net::URLRequestContextGetter* context_getter) {
90  if (!is_running_ || !providers_.empty()) {
91    // A second StartProviders() call may have arrived before the first
92    // completed.
93    return;
94  }
95  // If there are no access tokens, boot strap it with the default server URL.
96  if (access_token_set.empty())
97    access_token_set[DefaultNetworkProviderURL()];
98  for (AccessTokenStore::AccessTokenSet::iterator i =
99           access_token_set.begin();
100      i != access_token_set.end(); ++i) {
101    RegisterProvider(
102        NewNetworkLocationProvider(
103            GetAccessTokenStore(), context_getter,
104            i->first, i->second));
105  }
106
107  LocationProvider* provider =
108      GetContentClient()->browser()->OverrideSystemLocationProvider();
109  if (!provider)
110    provider = NewSystemLocationProvider();
111  RegisterProvider(provider);
112  DoStartProviders();
113}
114
115void LocationArbitratorImpl::RegisterProvider(
116    LocationProvider* provider) {
117  if (!provider)
118    return;
119  provider->SetUpdateCallback(provider_callback_);
120  if (is_permission_granted_)
121    provider->OnPermissionGranted();
122  providers_.push_back(provider);
123}
124
125void LocationArbitratorImpl::LocationUpdateAvailable(
126    const LocationProvider* provider,
127    const Geoposition& new_position) {
128  DCHECK(new_position.Validate() ||
129         new_position.error_code != Geoposition::ERROR_CODE_NONE);
130  if (!IsNewPositionBetter(position_, new_position,
131                           provider == position_provider_))
132    return;
133  position_provider_ = provider;
134  position_ = new_position;
135  callback_.Run(position_);
136}
137
138AccessTokenStore* LocationArbitratorImpl::NewAccessTokenStore() {
139  return GetContentClient()->browser()->CreateAccessTokenStore();
140}
141
142AccessTokenStore* LocationArbitratorImpl::GetAccessTokenStore() {
143  if (!access_token_store_.get())
144    access_token_store_ = NewAccessTokenStore();
145  return access_token_store_.get();
146}
147
148LocationProvider* LocationArbitratorImpl::NewNetworkLocationProvider(
149    AccessTokenStore* access_token_store,
150    net::URLRequestContextGetter* context,
151    const GURL& url,
152    const base::string16& access_token) {
153#if defined(OS_ANDROID)
154  // Android uses its own SystemLocationProvider.
155  return NULL;
156#else
157  return new NetworkLocationProvider(access_token_store, context, url,
158                                     access_token);
159#endif
160}
161
162LocationProvider* LocationArbitratorImpl::NewSystemLocationProvider() {
163#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
164  return NULL;
165#else
166  return content::NewSystemLocationProvider();
167#endif
168}
169
170base::Time LocationArbitratorImpl::GetTimeNow() const {
171  return base::Time::Now();
172}
173
174bool LocationArbitratorImpl::IsNewPositionBetter(
175    const Geoposition& old_position, const Geoposition& new_position,
176    bool from_same_provider) const {
177  // Updates location_info if it's better than what we currently have,
178  // or if it's a newer update from the same provider.
179  if (!old_position.Validate()) {
180    // Older location wasn't locked.
181    return true;
182  }
183  if (new_position.Validate()) {
184    // New location is locked, let's check if it's any better.
185    if (old_position.accuracy >= new_position.accuracy) {
186      // Accuracy is better.
187      return true;
188    } else if (from_same_provider) {
189      // Same provider, fresher location.
190      return true;
191    } else if ((GetTimeNow() - old_position.timestamp).InMilliseconds() >
192               kFixStaleTimeoutMilliseconds) {
193      // Existing fix is stale.
194      return true;
195    }
196  }
197  return false;
198}
199
200bool LocationArbitratorImpl::HasPermissionBeenGranted() const {
201  return is_permission_granted_;
202}
203
204}  // namespace content
205