OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/geolocation/network_location_provider.h" | 5 #include "chrome/browser/geolocation/network_location_provider.h" |
6 | 6 |
7 #include "base/time.h" | 7 #include "base/time.h" |
8 #include "base/utf_string_conversions.h" | 8 #include "base/utf_string_conversions.h" |
9 #include "chrome/browser/geolocation/access_token_store.h" | 9 #include "chrome/browser/geolocation/access_token_store.h" |
10 | 10 |
11 namespace { | 11 namespace { |
12 // The maximum period of time we'll wait for a complete set of device data | 12 // The maximum period of time we'll wait for a complete set of device data |
13 // before sending the request. | 13 // before sending the request. |
14 const int kDataCompleteWaitPeriod = 1000 * 2; // 2 seconds | 14 const int kDataCompleteWaitPeriod = 1000 * 2; // 2 seconds |
15 | 15 |
16 // The maximum size of the cache of positions for previously requested device | 16 // The maximum size of the cache of positions for previously requested device |
17 // data. | 17 // data. |
18 const size_t kMaximumCacheSize = 10; | 18 const size_t kMaximumCacheSize = 10; |
19 } // namespace | 19 } // namespace |
20 | 20 |
21 // The PositionCache handles caching and retrieving a position returned by a | 21 // The PositionCache handles caching and retrieving a position returned by a |
22 // network location provider. It is not thread safe. It's methods are called on | 22 // network location provider. It is not thread safe. It's methods are called on |
23 // multiple threads by NetworkLocationProvider, but the timing is such that | 23 // multiple threads by NetworkLocationProvider, but the timing is such that |
24 // thread safety is not required. | 24 // thread safety is not required. |
25 class NetworkLocationProvider::PositionCache { | 25 class NetworkLocationProvider::PositionCache { |
26 public: | 26 public: |
27 // Caches the current position response for the current set of cell ID and | 27 // Caches the current position response for the current set of cell ID and |
28 // WiFi data. Returns true on success, false otherwise. | 28 // WiFi data. Returns true on success, false otherwise. |
29 bool CachePosition(const RadioData& radio_data, | 29 bool CachePosition(const GatewayData& gateway_data, |
| 30 const RadioData& radio_data, |
30 const WifiData& wifi_data, | 31 const WifiData& wifi_data, |
31 const Geoposition& position) { | 32 const Geoposition& position) { |
32 // Check that we can generate a valid key for the device data. | 33 // Check that we can generate a valid key for the device data. |
33 string16 key; | 34 string16 key; |
34 if (!MakeKey(radio_data, wifi_data, &key)) { | 35 if (!MakeKey(gateway_data, radio_data, wifi_data, &key)) { |
35 return false; | 36 return false; |
36 } | 37 } |
37 // If the cache is full, remove the oldest entry. | 38 // If the cache is full, remove the oldest entry. |
38 if (cache_.size() == kMaximumCacheSize) { | 39 if (cache_.size() == kMaximumCacheSize) { |
39 DCHECK(cache_times_.size() == kMaximumCacheSize); | 40 DCHECK(cache_times_.size() == kMaximumCacheSize); |
40 CacheTimesMap::iterator oldest_entry = cache_times_.begin(); | 41 CacheTimesMap::iterator oldest_entry = cache_times_.begin(); |
41 DCHECK(oldest_entry != cache_times_.end()); | 42 DCHECK(oldest_entry != cache_times_.end()); |
42 cache_.erase(oldest_entry->second); | 43 cache_.erase(oldest_entry->second); |
43 cache_times_.erase(oldest_entry); | 44 cache_times_.erase(oldest_entry); |
44 } | 45 } |
45 // Insert the position into the cache. | 46 // Insert the position into the cache. |
46 std::pair<CacheMap::iterator, bool> result = | 47 std::pair<CacheMap::iterator, bool> result = |
47 cache_.insert(std::make_pair(key, position)); | 48 cache_.insert(std::make_pair(key, position)); |
48 DCHECK(result.second); | 49 DCHECK(result.second); |
49 cache_times_[position.timestamp] = result.first; | 50 cache_times_[position.timestamp] = result.first; |
50 DCHECK(cache_.size() == cache_times_.size()); | 51 DCHECK(cache_.size() == cache_times_.size()); |
51 return true; | 52 return true; |
52 } | 53 } |
53 | 54 |
54 // Searches for a cached position response for the current set of cell ID and | 55 // Searches for a cached position response for the current set of cell ID and |
55 // WiFi data. Returns the cached position if available, NULL otherwise. | 56 // WiFi data. Returns the cached position if available, NULL otherwise. |
56 const Geoposition *FindPosition(const RadioData &radio_data, | 57 const Geoposition *FindPosition(const GatewayData &gateway_data, |
| 58 const RadioData &radio_data, |
57 const WifiData &wifi_data) { | 59 const WifiData &wifi_data) { |
58 string16 key; | 60 string16 key; |
59 if (!MakeKey(radio_data, wifi_data, &key)) { | 61 if (!MakeKey(gateway_data, radio_data, wifi_data, &key)) { |
60 return NULL; | 62 return NULL; |
61 } | 63 } |
62 CacheMap::const_iterator iter = cache_.find(key); | 64 CacheMap::const_iterator iter = cache_.find(key); |
63 return iter == cache_.end() ? NULL : &iter->second; | 65 return iter == cache_.end() ? NULL : &iter->second; |
64 } | 66 } |
65 | 67 |
66 // Makes the key for the map of cached positions, using a set of | 68 // Makes the key for the map of cached positions, using a set of |
67 // device data. Returns true if a good key was generated, false otherwise. | 69 // device data. Returns true if a good key was generated, false otherwise. |
68 static bool MakeKey(const RadioData& /*radio_data*/, | 70 static bool MakeKey(const GatewayData& gateway_data, |
| 71 const RadioData& /*radio_data*/, |
69 const WifiData& wifi_data, | 72 const WifiData& wifi_data, |
70 string16* key) { | 73 string16* key) { |
71 // Currently we use only the WiFi data, and base the key only on the MAC | 74 // Currently we use only the WiFi data and gateway data, and base the |
72 // addresses. | 75 // key only on the MAC addresses. |
73 // TODO(steveblock): Make use of radio_data. | 76 // TODO(steveblock): Make use of radio_data. |
74 DCHECK(key); | 77 DCHECK(key); |
75 key->clear(); | 78 key->clear(); |
76 key->reserve(wifi_data.access_point_data.size() * 19); | 79 key->reserve(wifi_data.access_point_data.size() * 19 + |
| 80 gateway_data.router_data.size() * 19); |
77 const string16 separator(ASCIIToUTF16("|")); | 81 const string16 separator(ASCIIToUTF16("|")); |
| 82 for (GatewayData::RouterDataSet::const_iterator iter = |
| 83 gateway_data.router_data.begin(); |
| 84 iter != gateway_data.router_data.end(); |
| 85 iter++) { |
| 86 *key += separator; |
| 87 *key += iter->mac_address; |
| 88 *key += separator; |
| 89 } |
78 for (WifiData::AccessPointDataSet::const_iterator iter = | 90 for (WifiData::AccessPointDataSet::const_iterator iter = |
79 wifi_data.access_point_data.begin(); | 91 wifi_data.access_point_data.begin(); |
80 iter != wifi_data.access_point_data.end(); | 92 iter != wifi_data.access_point_data.end(); |
81 iter++) { | 93 iter++) { |
82 *key += separator; | 94 *key += separator; |
83 *key += iter->mac_address; | 95 *key += iter->mac_address; |
84 *key += separator; | 96 *key += separator; |
85 } | 97 } |
86 // If the key is the empty string, return false, as we don't want to cache a | 98 // If the key is the empty string, return false, as we don't want to cache a |
87 // position for such a set of device data. | 99 // position for such a set of device data. |
(...skipping 20 matching lines...) Expand all Loading... |
108 access_token_store, context, url, access_token); | 120 access_token_store, context, url, access_token); |
109 } | 121 } |
110 | 122 |
111 // NetworkLocationProvider | 123 // NetworkLocationProvider |
112 NetworkLocationProvider::NetworkLocationProvider( | 124 NetworkLocationProvider::NetworkLocationProvider( |
113 AccessTokenStore* access_token_store, | 125 AccessTokenStore* access_token_store, |
114 URLRequestContextGetter* url_context_getter, | 126 URLRequestContextGetter* url_context_getter, |
115 const GURL& url, | 127 const GURL& url, |
116 const string16& access_token) | 128 const string16& access_token) |
117 : access_token_store_(access_token_store), | 129 : access_token_store_(access_token_store), |
| 130 gateway_data_provider_(NULL), |
118 radio_data_provider_(NULL), | 131 radio_data_provider_(NULL), |
119 wifi_data_provider_(NULL), | 132 wifi_data_provider_(NULL), |
| 133 is_gateway_data_complete_(false), |
120 is_radio_data_complete_(false), | 134 is_radio_data_complete_(false), |
121 is_wifi_data_complete_(false), | 135 is_wifi_data_complete_(false), |
122 access_token_(access_token), | 136 access_token_(access_token), |
123 is_new_data_available_(false), | 137 is_new_data_available_(false), |
124 ALLOW_THIS_IN_INITIALIZER_LIST(delayed_start_task_(this)) { | 138 ALLOW_THIS_IN_INITIALIZER_LIST(delayed_start_task_(this)) { |
125 // Create the position cache. | 139 // Create the position cache. |
126 position_cache_.reset(new PositionCache()); | 140 position_cache_.reset(new PositionCache()); |
127 | 141 |
128 request_.reset(new NetworkLocationRequest(url_context_getter, url, this)); | 142 request_.reset(new NetworkLocationRequest(url_context_getter, url, this)); |
129 } | 143 } |
130 | 144 |
131 NetworkLocationProvider::~NetworkLocationProvider() { | 145 NetworkLocationProvider::~NetworkLocationProvider() { |
132 StopProvider(); | 146 StopProvider(); |
133 } | 147 } |
134 | 148 |
135 // LocationProviderBase implementation | 149 // LocationProviderBase implementation |
136 void NetworkLocationProvider::GetPosition(Geoposition *position) { | 150 void NetworkLocationProvider::GetPosition(Geoposition *position) { |
137 DCHECK(position); | 151 DCHECK(position); |
138 *position = position_; | 152 *position = position_; |
139 } | 153 } |
140 | 154 |
141 void NetworkLocationProvider::UpdatePosition() { | 155 void NetworkLocationProvider::UpdatePosition() { |
142 // TODO(joth): When called via the public (base class) interface, this should | 156 // TODO(joth): When called via the public (base class) interface, this should |
143 // poke each data provider to get them to expedite their next scan. | 157 // poke each data provider to get them to expedite their next scan. |
144 // Whilst in the delayed start, only send request if all data is ready. | 158 // Whilst in the delayed start, only send request if all data is ready. |
145 if (delayed_start_task_.empty() || | 159 if (delayed_start_task_.empty() || |
146 (is_radio_data_complete_ && is_wifi_data_complete_)) { | 160 (is_gateway_data_complete_ && is_radio_data_complete_ && |
| 161 is_wifi_data_complete_)) { |
147 RequestPosition(); | 162 RequestPosition(); |
148 } | 163 } |
149 } | 164 } |
150 | 165 |
151 void NetworkLocationProvider::OnPermissionGranted( | 166 void NetworkLocationProvider::OnPermissionGranted( |
152 const GURL& requesting_frame) { | 167 const GURL& requesting_frame) { |
153 const bool host_was_empty = most_recent_authorized_host_.empty(); | 168 const bool host_was_empty = most_recent_authorized_host_.empty(); |
154 most_recent_authorized_host_ = requesting_frame.host(); | 169 most_recent_authorized_host_ = requesting_frame.host(); |
155 if (host_was_empty && !most_recent_authorized_host_.empty() | 170 if (host_was_empty && !most_recent_authorized_host_.empty() |
156 && IsStarted()) { | 171 && IsStarted()) { |
157 UpdatePosition(); | 172 UpdatePosition(); |
158 } | 173 } |
159 } | 174 } |
160 | 175 |
161 // DeviceDataProviderInterface::ListenerInterface implementation. | 176 // DeviceDataProviderInterface::ListenerInterface implementation. |
162 void NetworkLocationProvider::DeviceDataUpdateAvailable( | 177 void NetworkLocationProvider::DeviceDataUpdateAvailable( |
| 178 GatewayDataProvider* provider) { |
| 179 DCHECK(provider == gateway_data_provider_); |
| 180 is_gateway_data_complete_ = gateway_data_provider_->GetData(&gateway_data_); |
| 181 OnDeviceDataUpdated(); |
| 182 } |
| 183 |
| 184 void NetworkLocationProvider::DeviceDataUpdateAvailable( |
163 RadioDataProvider* provider) { | 185 RadioDataProvider* provider) { |
164 DCHECK(provider == radio_data_provider_); | 186 DCHECK(provider == radio_data_provider_); |
165 is_radio_data_complete_ = radio_data_provider_->GetData(&radio_data_); | 187 is_radio_data_complete_ = radio_data_provider_->GetData(&radio_data_); |
166 OnDeviceDataUpdated(); | 188 OnDeviceDataUpdated(); |
167 } | 189 } |
168 | 190 |
169 void NetworkLocationProvider::DeviceDataUpdateAvailable( | 191 void NetworkLocationProvider::DeviceDataUpdateAvailable( |
170 WifiDataProvider* provider) { | 192 WifiDataProvider* provider) { |
171 DCHECK(provider == wifi_data_provider_); | 193 DCHECK(provider == wifi_data_provider_); |
172 is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_); | 194 is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_); |
173 OnDeviceDataUpdated(); | 195 OnDeviceDataUpdated(); |
174 } | 196 } |
175 | 197 |
176 // NetworkLocationRequest::ListenerInterface implementation. | 198 // NetworkLocationRequest::ListenerInterface implementation. |
177 void NetworkLocationProvider::LocationResponseAvailable( | 199 void NetworkLocationProvider::LocationResponseAvailable( |
178 const Geoposition& position, | 200 const Geoposition& position, |
179 bool server_error, | 201 bool server_error, |
180 const string16& access_token, | 202 const string16& access_token, |
| 203 const GatewayData& gateway_data, |
181 const RadioData& radio_data, | 204 const RadioData& radio_data, |
182 const WifiData& wifi_data) { | 205 const WifiData& wifi_data) { |
183 DCHECK(CalledOnValidThread()); | 206 DCHECK(CalledOnValidThread()); |
184 // Record the position and update our cache. | 207 // Record the position and update our cache. |
185 position_ = position; | 208 position_ = position; |
186 if (position.IsValidFix()) { | 209 if (position.IsValidFix()) { |
187 position_cache_->CachePosition(radio_data, wifi_data, position); | 210 position_cache_->CachePosition(gateway_data, radio_data, |
| 211 wifi_data, position); |
188 } | 212 } |
189 | 213 |
190 // Record access_token if it's set. | 214 // Record access_token if it's set. |
191 if (!access_token.empty() && access_token_ != access_token) { | 215 if (!access_token.empty() && access_token_ != access_token) { |
192 access_token_ = access_token; | 216 access_token_ = access_token; |
193 access_token_store_->SaveAccessToken(request_->url(), access_token); | 217 access_token_store_->SaveAccessToken(request_->url(), access_token); |
194 } | 218 } |
195 | 219 |
196 // Let listeners know that we now have a position available. | 220 // Let listeners know that we now have a position available. |
197 UpdateListeners(); | 221 UpdateListeners(); |
198 } | 222 } |
199 | 223 |
200 bool NetworkLocationProvider::StartProvider(bool high_accuracy) { | 224 bool NetworkLocationProvider::StartProvider(bool high_accuracy) { |
201 DCHECK(CalledOnValidThread()); | 225 DCHECK(CalledOnValidThread()); |
202 if (IsStarted()) | 226 if (IsStarted()) |
203 return true; | 227 return true; |
204 DCHECK(wifi_data_provider_ == NULL); | 228 DCHECK(wifi_data_provider_ == NULL); |
205 if (!request_->url().is_valid()) { | 229 if (!request_->url().is_valid()) { |
206 LOG(WARNING) << "StartProvider() : Failed, Bad URL: " | 230 LOG(WARNING) << "StartProvider() : Failed, Bad URL: " |
207 << request_->url().possibly_invalid_spec(); | 231 << request_->url().possibly_invalid_spec(); |
208 return false; | 232 return false; |
209 } | 233 } |
210 | 234 |
211 // Get the device data providers. The first call to Register will create the | 235 // Get the device data providers. The first call to Register will create the |
212 // provider and it will be deleted by ref counting. | 236 // provider and it will be deleted by ref counting. |
| 237 gateway_data_provider_ = GatewayDataProvider::Register(this); |
213 radio_data_provider_ = RadioDataProvider::Register(this); | 238 radio_data_provider_ = RadioDataProvider::Register(this); |
214 wifi_data_provider_ = WifiDataProvider::Register(this); | 239 wifi_data_provider_ = WifiDataProvider::Register(this); |
215 | 240 |
216 MessageLoop::current()->PostDelayedTask( | 241 MessageLoop::current()->PostDelayedTask( |
217 FROM_HERE, | 242 FROM_HERE, |
218 delayed_start_task_.NewRunnableMethod( | 243 delayed_start_task_.NewRunnableMethod( |
219 &NetworkLocationProvider::RequestPosition), | 244 &NetworkLocationProvider::RequestPosition), |
220 kDataCompleteWaitPeriod); | 245 kDataCompleteWaitPeriod); |
221 // Get the device data. | 246 // Get the device data. |
| 247 is_gateway_data_complete_ = gateway_data_provider_->GetData(&gateway_data_); |
222 is_radio_data_complete_ = radio_data_provider_->GetData(&radio_data_); | 248 is_radio_data_complete_ = radio_data_provider_->GetData(&radio_data_); |
223 is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_); | 249 is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_); |
224 if (is_radio_data_complete_ || is_wifi_data_complete_) | 250 if (is_gateway_data_complete_ || is_radio_data_complete_ || |
| 251 is_wifi_data_complete_) |
225 OnDeviceDataUpdated(); | 252 OnDeviceDataUpdated(); |
226 return true; | 253 return true; |
227 } | 254 } |
228 | 255 |
229 void NetworkLocationProvider::StopProvider() { | 256 void NetworkLocationProvider::StopProvider() { |
230 DCHECK(CalledOnValidThread()); | 257 DCHECK(CalledOnValidThread()); |
231 if (IsStarted()) { | 258 if (IsStarted()) { |
| 259 gateway_data_provider_->Unregister(this); |
232 radio_data_provider_->Unregister(this); | 260 radio_data_provider_->Unregister(this); |
233 wifi_data_provider_->Unregister(this); | 261 wifi_data_provider_->Unregister(this); |
234 } | 262 } |
| 263 gateway_data_provider_ = NULL; |
235 radio_data_provider_ = NULL; | 264 radio_data_provider_ = NULL; |
236 wifi_data_provider_ = NULL; | 265 wifi_data_provider_ = NULL; |
237 delayed_start_task_.RevokeAll(); | 266 delayed_start_task_.RevokeAll(); |
238 } | 267 } |
239 | 268 |
240 // Other methods | 269 // Other methods |
241 void NetworkLocationProvider::RequestPosition() { | 270 void NetworkLocationProvider::RequestPosition() { |
242 DCHECK(CalledOnValidThread()); | 271 DCHECK(CalledOnValidThread()); |
243 if (!is_new_data_available_) | 272 if (!is_new_data_available_) |
244 return; | 273 return; |
245 | 274 |
246 const Geoposition* cached_position = | 275 const Geoposition* cached_position = |
247 position_cache_->FindPosition(radio_data_, wifi_data_); | 276 position_cache_->FindPosition(gateway_data_, radio_data_, wifi_data_); |
248 DCHECK(!device_data_updated_timestamp_.is_null()) << | 277 DCHECK(!device_data_updated_timestamp_.is_null()) << |
249 "Timestamp must be set before looking up position"; | 278 "Timestamp must be set before looking up position"; |
250 if (cached_position) { | 279 if (cached_position) { |
251 DCHECK(cached_position->IsValidFix()); | 280 DCHECK(cached_position->IsValidFix()); |
252 // Record the position and update its timestamp. | 281 // Record the position and update its timestamp. |
253 position_ = *cached_position; | 282 position_ = *cached_position; |
254 // The timestamp of a position fix is determined by the timestamp | 283 // The timestamp of a position fix is determined by the timestamp |
255 // of the source data update. (The value of position_.timestamp from | 284 // of the source data update. (The value of position_.timestamp from |
256 // the cache could be from weeks ago!) | 285 // the cache could be from weeks ago!) |
257 position_.timestamp = device_data_updated_timestamp_; | 286 position_.timestamp = device_data_updated_timestamp_; |
(...skipping 13 matching lines...) Expand all Loading... |
271 // NetworkLocationRequest for each and hold a set of pending requests. | 300 // NetworkLocationRequest for each and hold a set of pending requests. |
272 if (request_->is_request_pending()) { | 301 if (request_->is_request_pending()) { |
273 LOG(INFO) << "NetworkLocationProvider - pre-empting pending network request" | 302 LOG(INFO) << "NetworkLocationProvider - pre-empting pending network request" |
274 << "with new data. Wifi APs: " | 303 << "with new data. Wifi APs: " |
275 << wifi_data_.access_point_data.size(); | 304 << wifi_data_.access_point_data.size(); |
276 } | 305 } |
277 // The hostname sent in the request is just to give a first-order | 306 // The hostname sent in the request is just to give a first-order |
278 // approximation of usage. We do not need to guarantee that this network | 307 // approximation of usage. We do not need to guarantee that this network |
279 // request was triggered by an API call from this specific host. | 308 // request was triggered by an API call from this specific host. |
280 request_->MakeRequest(most_recent_authorized_host_, access_token_, | 309 request_->MakeRequest(most_recent_authorized_host_, access_token_, |
281 radio_data_, wifi_data_, | 310 gateway_data_, radio_data_, wifi_data_, |
282 device_data_updated_timestamp_); | 311 device_data_updated_timestamp_); |
283 } | 312 } |
284 | 313 |
285 void NetworkLocationProvider::OnDeviceDataUpdated() { | 314 void NetworkLocationProvider::OnDeviceDataUpdated() { |
286 DCHECK(CalledOnValidThread()); | 315 DCHECK(CalledOnValidThread()); |
287 device_data_updated_timestamp_ = base::Time::Now(); | 316 device_data_updated_timestamp_ = base::Time::Now(); |
288 | 317 |
289 is_new_data_available_ = is_radio_data_complete_ || is_wifi_data_complete_; | 318 is_new_data_available_ = is_gateway_data_complete_ || |
| 319 is_radio_data_complete_ || is_wifi_data_complete_; |
290 UpdatePosition(); | 320 UpdatePosition(); |
291 } | 321 } |
292 | 322 |
293 bool NetworkLocationProvider::IsStarted() const { | 323 bool NetworkLocationProvider::IsStarted() const { |
| 324 DCHECK_EQ(!!gateway_data_provider_, !!radio_data_provider_); |
294 DCHECK_EQ(!!radio_data_provider_, !!wifi_data_provider_); | 325 DCHECK_EQ(!!radio_data_provider_, !!wifi_data_provider_); |
295 return wifi_data_provider_ != NULL; | 326 return wifi_data_provider_ != NULL; |
296 } | 327 } |
OLD | NEW |