| OLD | NEW |
| (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 |
| OLD | NEW |