Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(359)

Side by Side Diff: chrome/browser/geolocation/network_location_provider.cc

Issue 555148: Add more gears geolocaiton files into chromium: locaiton provider, network lo... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 10 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright 2008, Google Inc.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
5 //
6 // 1. Redistributions of source code must retain the above copyright notice,
7 // this list of conditions and the following disclaimer.
8 // 2. Redistributions in binary form must reproduce the above copyright notice,
9 // this list of conditions and the following disclaimer in the documentation
10 // and/or other materials provided with the distribution.
11 // 3. Neither the name of Google Inc. nor the names of its contributors may be
12 // used to endorse or promote products derived from this software without
13 // specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26 // TODO(joth): port to chromium
27 #if 0
28
29 #include "gears/geolocation/network_location_provider.h"
30
31 #include "gears/base/common/base_class.h"
32 #include "gears/base/common/stopwatch.h" // For GetCurrentTimeMillis
33 #include "gears/geolocation/access_token_manager.h"
34 #include "gears/geolocation/backoff_manager.h"
35
36 // The maximum period of time we'll wait for a complete set of device data
37 // before sending the request.
38 static const int kDataCompleteWaitPeriod = 1000 * 2; // 2 seconds
39
40 // The maximum size of the cache of positions for previously requested device
41 // data.
42 static const size_t kMaximumCacheSize = 10;
43
44
45 // The PositionCache handles caching and retrieving a position returned by a
46 // network location provider. It is not thread safe. It's methods are called on
47 // multiple threads by NetworkLocationProvider, but the timing is such that
48 // thread safety is not required.
49 class PositionCache {
50 public:
51 // Caches the current position response for the current set of cell ID and
52 // WiFi data. Returns true on success, false otherwise.
53 bool CachePosition(const RadioData &radio_data,
54 const WifiData &wifi_data,
55 const Position &position) {
56 // Check that we can generate a valid key for the device data.
57 std::string16 key;
58 if (!MakeKey(radio_data, wifi_data, &key)) {
59 return false;
60 }
61 // If the cache is full, remove the oldest entry.
62 if (cache_.size() == kMaximumCacheSize) {
63 assert(cache_times_.size() == kMaximumCacheSize);
64 CacheTimesMap::iterator oldest_entry = cache_times_.begin();
65 assert(oldest_entry != cache_times_.end());
66 cache_.erase(oldest_entry->second);
67 cache_times_.erase(oldest_entry);
68 }
69 // Insert the position into the cache.
70 std::pair<CacheMap::iterator, bool> result =
71 cache_.insert(std::make_pair(key, position));
72 assert(result.second);
73 cache_times_[position.timestamp] = result.first;
74 assert(cache_.size() == cache_times_.size());
75 return true;
76 }
77
78 // Searches for a cached position response for the current set of cell ID and
79 // WiFi data. Returns the cached position if available, NULL otherwise.
80 const Position *FindPosition(const RadioData &radio_data,
81 const WifiData &wifi_data) {
82 std::string16 key;
83 if (!MakeKey(radio_data, wifi_data, &key)) {
84 return NULL;
85 }
86 CacheMap::const_iterator iter = cache_.find(key);
87 return iter == cache_.end() ? NULL : &iter->second;
88 }
89
90 // Makes the key for the map of cached positions, using a set of
91 // device data. Returns true if a good key was generated, false otherwise.
92 static bool MakeKey(const RadioData& /*radio_data*/,
93 const WifiData &wifi_data,
94 std::string16 *key) {
95 // Currently we use only the WiFi data, and base the key only on the MAC
96 // addresses.
97 // TODO(steveblock): Make use of radio_data.
98 key->clear();
99 for (WifiData::AccessPointDataSet::const_iterator iter =
100 wifi_data.access_point_data.begin();
101 iter != wifi_data.access_point_data.begin();
102 iter++) {
103 key->append(STRING16(L"|") + iter->mac_address + STRING16(L"|"));
104 }
105 // If the key is the empty string, return false, as we don't want to cache a
106 // position for such a set of device data.
107 return !key->empty();
108 }
109
110 private:
111 // The cache of positions. This is stored using two maps. One map is keyed on
112 // a string that represents a set of device data, the other is keyed on the
113 // timestamp of the position.
114 typedef std::map<std::string16, Position> CacheMap;
115 CacheMap cache_;
116 typedef std::map<int64, CacheMap::iterator> CacheTimesMap;
117 CacheTimesMap cache_times_;
118 };
119
120
121 // NetworkLocationProvider factory function
122 LocationProviderBase *NewNetworkLocationProvider(
123 BrowsingContext *browsing_context,
124 const std::string16 &url,
125 const std::string16 &host_name,
126 const std::string16 &language) {
127 return new NetworkLocationProvider(browsing_context, url, host_name,
128 language);
129 }
130
131
132 // NetworkLocationProvider
133 NetworkLocationProvider::NetworkLocationProvider(
134 BrowsingContext *browsing_context,
135 const std::string16 &url,
136 const std::string16 &host_name,
137 const std::string16 &language)
138 : request_(NULL),
139 url_(url),
140 host_name_(host_name),
141 request_address_(false),
142 request_address_from_last_request_(false),
143 address_language_(language),
144 is_shutting_down_(false),
145 is_new_data_available_(false),
146 is_new_listener_waiting_(false),
147 browsing_context_(browsing_context) {
148 // TODO(steveblock): Consider allowing multiple values for "address_language"
149 // in the network protocol to allow better sharing of network location
150 // providers.
151
152 // Get the device data providers. The first call to Register will create the
153 // provider and it will be deleted by ref counting.
154 radio_data_provider_ = RadioDataProvider::Register(this);
155 wifi_data_provider_ = WifiDataProvider::Register(this);
156
157 AccessTokenManager::GetInstance()->Register(url_);
158
159 // Create the position cache.
160 position_cache_.reset(new PositionCache());
161
162 // Start the worker thread
163 if (!Start()) {
164 // This should never happen.
165 LOG(("Could not start the NLR"));
166 assert(false);
167 }
168 }
169
170 NetworkLocationProvider::~NetworkLocationProvider() {
171 // Shut down the worker thread
172 is_shutting_down_ = true;
173 thread_notification_event_.Signal();
174 Join();
175
176 // Must keep the request around until our worker thread has stopped.
177 if (request_) {
178 request_->StopThreadAndDelete();
179 request_ = NULL;
180 }
181
182 radio_data_provider_->Unregister(this);
183 wifi_data_provider_->Unregister(this);
184
185 AccessTokenManager::GetInstance()->Unregister();
186 }
187
188 void NetworkLocationProvider::RegisterListener(
189 LocationProviderBase::ListenerInterface *listener,
190 bool request_address) {
191 // Determine whether this listener requires an address when the last request
192 // does not.
193 bool new_listener_requires_address =
194 !request_address_from_last_request_ && request_address;
195
196 // Update whether or not we need to request an address.
197 request_address_ |= request_address;
198
199 // If we now need to request an address when we did not before, we don't add
200 // the listener. This is because if a request is currently in progress, we
201 // don't want the new listener to be called back with a position without an
202 // address. We add the listener when we make the next request.
203 if (new_listener_requires_address) {
204 MutexLock lock(&new_listeners_requiring_address_mutex_);
205 new_listeners_requiring_address_.insert(listener);
206 } else {
207 LocationProviderBase::RegisterListener(listener, request_address);
208 }
209
210 // Signal to the worker thread that there is a new listener.
211 is_new_listener_waiting_ = true;
212 thread_notification_event_.Signal();
213 }
214
215 void NetworkLocationProvider::UnregisterListener(
216 LocationProviderBase::ListenerInterface *listener) {
217 assert(listener);
218
219 // First try removing the listener from the set of new listeners waiting for
220 // an address. Otherwise, try the regular listeners.
221 MutexLock new_listeners_lock(&new_listeners_requiring_address_mutex_);
222 ListenerSet::iterator iter = new_listeners_requiring_address_.find(listener);
223 if (iter != new_listeners_requiring_address_.end()) {
224 new_listeners_requiring_address_.erase(iter);
225 } else {
226 LocationProviderBase::UnregisterListener(listener);
227 }
228
229 // Update whether or not we need to request an address.
230 if (request_address_) {
231 if (!new_listeners_requiring_address_.empty()) {
232 return;
233 }
234 MutexLock listeners_lock(GetListenersMutex());
235 ListenerMap *listeners = GetListeners();
236 for (ListenerMap::const_iterator iter = listeners->begin();
237 iter != listeners->end();
238 iter++) {
239 if (iter->second.first == true) {
240 return;
241 }
242 }
243 request_address_ = false;
244 }
245 }
246
247 // LocationProviderBase implementation
248 void NetworkLocationProvider::GetPosition(Position *position) {
249 assert(position);
250 MutexLock lock(&position_mutex_);
251 *position = position_;
252 }
253
254 // DeviceDataProviderInterface::ListenerInterface implementation.
255 void NetworkLocationProvider::DeviceDataUpdateAvailable(
256 RadioDataProvider *provider) {
257 MutexLock lock(&data_mutex_);
258 assert(provider == radio_data_provider_);
259 is_radio_data_complete_ = radio_data_provider_->GetData(&radio_data_);
260
261 DeviceDataUpdateAvailableImpl();
262 }
263
264 void NetworkLocationProvider::DeviceDataUpdateAvailable(
265 WifiDataProvider *provider) {
266 assert(provider == wifi_data_provider_);
267 MutexLock lock(&data_mutex_);
268 is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_);
269
270 DeviceDataUpdateAvailableImpl();
271 }
272
273 // NetworkLocationRequest::ListenerInterface implementation.
274 void NetworkLocationProvider::LocationResponseAvailable(
275 const Position &position,
276 bool server_error,
277 const std::string16 &access_token) {
278 // Record the position and update our cache.
279 position_mutex_.Lock();
280 position_ = position;
281 if (position_.IsGoodFix()) {
282 MutexLock lock(&data_mutex_);
283 position_cache_->CachePosition(radio_data_, wifi_data_, position_);
284 }
285 position_mutex_.Unlock();
286
287 // Record access_token if it's set.
288 if (!access_token.empty()) {
289 AccessTokenManager::GetInstance()->SetToken(url_, access_token);
290 }
291
292 // Get earliest time for next request.
293 earliest_next_request_time_ = BackoffManager::ReportResponse(url_,
294 server_error);
295
296 // Signal to the worker thread that this request has completed.
297 is_last_request_complete_ = true;
298 thread_notification_event_.Signal();
299
300 // Let listeners know that we now have a position available.
301 UpdateListeners();
302 }
303
304 // Thread implementation
305 void NetworkLocationProvider::Run() {
306 // Create the network request object. We must do this on the same thread from
307 // which we'll call Start().
308 request_ = NetworkLocationRequest::Create(browsing_context_, url_,
309 host_name_, this);
310 if (!request_) {
311 LOG(("Failed to create NetworkLocationRequest object.\n"));
312 assert(false);
313 return;
314 }
315
316 // Get the device data.
317 data_mutex_.Lock();
318 is_radio_data_complete_ = radio_data_provider_->GetData(&radio_data_);
319 is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_);
320 timestamp_ = GetCurrentTimeMillis();
321
322 // For the first request, wait for a certain maximum time period to get as
323 // much device data as possible.
324 int64 start_time = timestamp_;
325 while (true) {
326 if (is_radio_data_complete_ && is_wifi_data_complete_) {
327 data_mutex_.Unlock();
328 break;
329 }
330 data_mutex_.Unlock();
331
332 int64 elapsed_time = GetCurrentTimeMillis() - start_time;
333 int timeout = kDataCompleteWaitPeriod - static_cast<int>(elapsed_time);
334 if (timeout <= 0) {
335 break;
336 }
337 if (!thread_notification_event_.WaitWithTimeout(timeout)) {
338 // Quit waiting if we time out.
339 break;
340 }
341 // Terminate the thread if requested.
342 if (is_shutting_down_) {
343 return;
344 }
345 // The event should be due to new device data or a new listener.
346 assert(is_new_data_available_ || is_new_listener_waiting_);
347 // If we have new data available, inform listeners of movement.
348 if (is_new_data_available_) {
349 InformListenersOfMovement();
350 }
351 // Lock the data mutex to test is_radio_data_complete_ and
352 // is_wifi_data_complete_ on the next loop.
353 data_mutex_.Lock();
354 }
355
356 earliest_next_request_time_ = 0;
357 MakeRequest();
358
359 // Loop continually, making requests whenever new data becomes available,
360 // subject to the minimum interval.
361 //
362 // This loop is structured such that we don't require mutex locks to
363 // synchronise changes to is_new_data_available_ etc with signals on
364 // thread_notification_event_. Note that if we get a signal before we wait,
365 // the wait will proceed immediately, so we don't miss signals.
366 int64 remaining_time = 1;
367 while (!is_shutting_down_) {
368 if (remaining_time > 0) {
369 remaining_time = earliest_next_request_time_ - GetCurrentTimeMillis();
370 }
371
372 // If the minimum time period has not yet elapsed, set the timeout such
373 // that the wait expires when the period has elapsed.
374 if (remaining_time > 0) {
375 thread_notification_event_.WaitWithTimeout(
376 static_cast<int>(remaining_time));
377 } else {
378 thread_notification_event_.Wait();
379 }
380
381 // Update remaining time now we've woken up. Note that it can never
382 // transition from <= 0 to > 0.
383 if (remaining_time > 0) {
384 remaining_time = earliest_next_request_time_ - GetCurrentTimeMillis();
385 }
386
387 bool make_request = false;
388 if (is_new_listener_waiting_) {
389 // A new listener has just registered with this provider. If new data is
390 // available, force a new request now, unless a request is already in
391 // progress. If not, update listeners with the last known position,
392 // provided we have one.
393 if (is_new_data_available_) {
394 if (is_last_request_complete_) {
395 make_request = true;
396 }
397 } else {
398 // Before the first network request completes, position_ may not be
399 // valid, so we do not update the listeners. They will be updated once
400 // the network request completes.
401 if (position_.IsInitialized()) {
402 // Update listeners with the last known position.
403 UpdateListeners();
404 }
405 }
406 is_new_listener_waiting_ = false;
407 }
408
409 // If a new listener has now registered such that we now require an address,
410 // we make a new request once the current request completes.
411 new_listeners_requiring_address_mutex_.Lock();
412 if (!new_listeners_requiring_address_.empty()) {
413 if (is_last_request_complete_) {
414 make_request = true;
415 }
416 }
417 new_listeners_requiring_address_mutex_.Unlock();
418
419 // If the thread is not shutting down, we have new data, the last request
420 // has completed, and the minimum time has elapsed, make the next request.
421 if (!is_shutting_down_ &&
422 is_new_data_available_ &&
423 is_last_request_complete_ &&
424 remaining_time <= 0) {
425 make_request = true;
426 }
427
428 // If we have new data available, inform listeners of movement.
429 if (is_new_data_available_) {
430 InformListenersOfMovement();
431 }
432
433 // TODO(steveblock): If the request does not complete within some maximum
434 // time period, we should kill it and start a new request.
435 if (make_request) {
436 MakeRequest();
437 remaining_time = 1;
438 }
439 }
440 }
441
442 // Other methods
443
444 bool NetworkLocationProvider::MakeRequest() {
445 // If we have new listeners waiting for an address, request_address_
446 // must be true.
447 assert(new_listeners_requiring_address_.empty() || request_address_);
448
449 // Move the new listeners waiting for an address to the list of listeners.
450 MutexLock lock(&new_listeners_requiring_address_mutex_);
451 for (ListenerSet::const_iterator iter =
452 new_listeners_requiring_address_.begin();
453 iter != new_listeners_requiring_address_.end();
454 iter++) {
455 LocationProviderBase::RegisterListener(*iter, true);
456 }
457 new_listeners_requiring_address_.clear();
458
459 request_address_from_last_request_ = request_address_;
460
461 BackoffManager::ReportRequest(url_);
462
463 std::string16 access_token;
464 AccessTokenManager::GetInstance()->GetToken(url_, &access_token);
465
466 // Reset flags
467 is_new_data_available_ = false;
468 is_new_listener_waiting_ = false;
469
470 data_mutex_.Lock();
471 const Position *cached_position =
472 position_cache_->FindPosition(radio_data_, wifi_data_);
473 data_mutex_.Unlock();
474 if (cached_position) {
475 assert(cached_position->IsGoodFix());
476 // Record the position and update its timestamp.
477 position_mutex_.Lock();
478 position_ = *cached_position;
479 position_.timestamp = timestamp_;
480 position_mutex_.Unlock();
481
482 // Let listeners know that we now have a position available.
483 UpdateListeners();
484 return true;
485 }
486
487 assert(request_);
488 is_last_request_complete_ = false;
489 MutexLock data_lock(&data_mutex_);
490 return request_->MakeRequest(access_token,
491 radio_data_,
492 wifi_data_,
493 request_address_,
494 address_language_,
495 kBadLatLng, // We don't have a position to pass
496 kBadLatLng, // to the server.
497 timestamp_);
498 }
499
500 void NetworkLocationProvider::DeviceDataUpdateAvailableImpl() {
501 timestamp_ = GetCurrentTimeMillis();
502
503 // Signal to the worker thread that new data is available.
504 is_new_data_available_ = true;
505 thread_notification_event_.Signal();
506 }
507
508 #endif // if 0
OLDNEW
« no previous file with comments | « chrome/browser/geolocation/network_location_provider.h ('k') | chrome/browser/geolocation/network_location_request.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698