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 17 namespace content { 18 namespace { 19 20 const 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. 26 const int64 LocationArbitratorImpl::kFixStaleTimeoutMilliseconds = 27 11 * base::Time::kMillisecondsPerSecond; 28 29 LocationArbitratorImpl::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 40 LocationArbitratorImpl::~LocationArbitratorImpl() { 41 } 42 43 GURL LocationArbitratorImpl::DefaultNetworkProviderURL() { 44 return GURL(kDefaultNetworkProviderUrl); 45 } 46 47 void 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 55 void 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 69 void LocationArbitratorImpl::DoStartProviders() { 70 for (ScopedVector<LocationProvider>::iterator i = providers_.begin(); 71 i != providers_.end(); ++i) { 72 (*i)->StartProvider(use_high_accuracy_); 73 } 74 } 75 76 void 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 87 void 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 115 void 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 125 void 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 138 AccessTokenStore* LocationArbitratorImpl::NewAccessTokenStore() { 139 return GetContentClient()->browser()->CreateAccessTokenStore(); 140 } 141 142 AccessTokenStore* LocationArbitratorImpl::GetAccessTokenStore() { 143 if (!access_token_store_.get()) 144 access_token_store_ = NewAccessTokenStore(); 145 return access_token_store_.get(); 146 } 147 148 LocationProvider* 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 162 LocationProvider* 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 170 base::Time LocationArbitratorImpl::GetTimeNow() const { 171 return base::Time::Now(); 172 } 173 174 bool 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 200 bool LocationArbitratorImpl::HasPermissionBeenGranted() const { 201 return is_permission_granted_; 202 } 203 204 } // namespace content 205