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