| OLD | NEW |
| 1 // Copyright 2008, Google Inc. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // Redistribution and use in source and binary forms, with or without | 3 // found in the LICENSE file. |
| 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 | 4 |
| 26 // TODO(joth): port to chromium | 5 #include "chrome/browser/geolocation/network_location_provider.h" |
| 27 #if 0 | |
| 28 | 6 |
| 29 #include "gears/geolocation/network_location_provider.h" | 7 #include "base/time.h" |
| 30 | 8 |
| 31 #include "gears/base/common/base_class.h" | 9 namespace { |
| 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 | 10 // The maximum period of time we'll wait for a complete set of device data |
| 37 // before sending the request. | 11 // before sending the request. |
| 38 static const int kDataCompleteWaitPeriod = 1000 * 2; // 2 seconds | 12 const int kDataCompleteWaitPeriod = 1000 * 2; // 2 seconds |
| 39 | 13 |
| 40 // The maximum size of the cache of positions for previously requested device | 14 // The maximum size of the cache of positions for previously requested device |
| 41 // data. | 15 // data. |
| 42 static const size_t kMaximumCacheSize = 10; | 16 const size_t kMaximumCacheSize = 10; |
| 43 | 17 |
| 18 // TODO(joth): Share, or remove usage by porting callers to Time et al. |
| 19 int64 GetCurrentTimeMillis() { |
| 20 return static_cast<int64>(base::Time::Now().ToDoubleT() * 1000); |
| 21 } |
| 22 } // namespace |
| 44 | 23 |
| 45 // The PositionCache handles caching and retrieving a position returned by a | 24 // 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 | 25 // 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 | 26 // multiple threads by NetworkLocationProvider, but the timing is such that |
| 48 // thread safety is not required. | 27 // thread safety is not required. |
| 49 class PositionCache { | 28 class NetworkLocationProvider::PositionCache { |
| 50 public: | 29 public: |
| 51 // Caches the current position response for the current set of cell ID and | 30 // Caches the current position response for the current set of cell ID and |
| 52 // WiFi data. Returns true on success, false otherwise. | 31 // WiFi data. Returns true on success, false otherwise. |
| 53 bool CachePosition(const RadioData &radio_data, | 32 bool CachePosition(const RadioData& radio_data, |
| 54 const WifiData &wifi_data, | 33 const WifiData& wifi_data, |
| 55 const Position &position) { | 34 const Position& position) { |
| 56 // Check that we can generate a valid key for the device data. | 35 // Check that we can generate a valid key for the device data. |
| 57 std::string16 key; | 36 string16 key; |
| 58 if (!MakeKey(radio_data, wifi_data, &key)) { | 37 if (!MakeKey(radio_data, wifi_data, &key)) { |
| 59 return false; | 38 return false; |
| 60 } | 39 } |
| 61 // If the cache is full, remove the oldest entry. | 40 // If the cache is full, remove the oldest entry. |
| 62 if (cache_.size() == kMaximumCacheSize) { | 41 if (cache_.size() == kMaximumCacheSize) { |
| 63 assert(cache_times_.size() == kMaximumCacheSize); | 42 DCHECK(cache_times_.size() == kMaximumCacheSize); |
| 64 CacheTimesMap::iterator oldest_entry = cache_times_.begin(); | 43 CacheTimesMap::iterator oldest_entry = cache_times_.begin(); |
| 65 assert(oldest_entry != cache_times_.end()); | 44 DCHECK(oldest_entry != cache_times_.end()); |
| 66 cache_.erase(oldest_entry->second); | 45 cache_.erase(oldest_entry->second); |
| 67 cache_times_.erase(oldest_entry); | 46 cache_times_.erase(oldest_entry); |
| 68 } | 47 } |
| 69 // Insert the position into the cache. | 48 // Insert the position into the cache. |
| 70 std::pair<CacheMap::iterator, bool> result = | 49 std::pair<CacheMap::iterator, bool> result = |
| 71 cache_.insert(std::make_pair(key, position)); | 50 cache_.insert(std::make_pair(key, position)); |
| 72 assert(result.second); | 51 DCHECK(result.second); |
| 73 cache_times_[position.timestamp] = result.first; | 52 cache_times_[position.timestamp] = result.first; |
| 74 assert(cache_.size() == cache_times_.size()); | 53 DCHECK(cache_.size() == cache_times_.size()); |
| 75 return true; | 54 return true; |
| 76 } | 55 } |
| 77 | 56 |
| 78 // Searches for a cached position response for the current set of cell ID and | 57 // 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. | 58 // WiFi data. Returns the cached position if available, NULL otherwise. |
| 80 const Position *FindPosition(const RadioData &radio_data, | 59 const Position *FindPosition(const RadioData &radio_data, |
| 81 const WifiData &wifi_data) { | 60 const WifiData &wifi_data) { |
| 82 std::string16 key; | 61 string16 key; |
| 83 if (!MakeKey(radio_data, wifi_data, &key)) { | 62 if (!MakeKey(radio_data, wifi_data, &key)) { |
| 84 return NULL; | 63 return NULL; |
| 85 } | 64 } |
| 86 CacheMap::const_iterator iter = cache_.find(key); | 65 CacheMap::const_iterator iter = cache_.find(key); |
| 87 return iter == cache_.end() ? NULL : &iter->second; | 66 return iter == cache_.end() ? NULL : &iter->second; |
| 88 } | 67 } |
| 89 | 68 |
| 90 // Makes the key for the map of cached positions, using a set of | 69 // 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. | 70 // device data. Returns true if a good key was generated, false otherwise. |
| 92 static bool MakeKey(const RadioData& /*radio_data*/, | 71 static bool MakeKey(const RadioData& /*radio_data*/, |
| 93 const WifiData &wifi_data, | 72 const WifiData& wifi_data, |
| 94 std::string16 *key) { | 73 string16* key) { |
| 95 // Currently we use only the WiFi data, and base the key only on the MAC | 74 // Currently we use only the WiFi data, and base the key only on the MAC |
| 96 // addresses. | 75 // addresses. |
| 97 // TODO(steveblock): Make use of radio_data. | 76 // TODO(steveblock): Make use of radio_data. |
| 77 DCHECK(key); |
| 98 key->clear(); | 78 key->clear(); |
| 79 key->reserve(wifi_data.access_point_data.size() * 19); |
| 80 const string16 separator(ASCIIToUTF16("|")); |
| 99 for (WifiData::AccessPointDataSet::const_iterator iter = | 81 for (WifiData::AccessPointDataSet::const_iterator iter = |
| 100 wifi_data.access_point_data.begin(); | 82 wifi_data.access_point_data.begin(); |
| 101 iter != wifi_data.access_point_data.begin(); | 83 iter != wifi_data.access_point_data.begin(); |
| 102 iter++) { | 84 iter++) { |
| 103 key->append(STRING16(L"|") + iter->mac_address + STRING16(L"|")); | 85 *key += separator; |
| 86 *key += iter->mac_address; |
| 87 *key += separator; |
| 104 } | 88 } |
| 105 // If the key is the empty string, return false, as we don't want to cache a | 89 // 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. | 90 // position for such a set of device data. |
| 107 return !key->empty(); | 91 return !key->empty(); |
| 108 } | 92 } |
| 109 | 93 |
| 110 private: | 94 private: |
| 111 // The cache of positions. This is stored using two maps. One map is keyed on | 95 // 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 | 96 // a string that represents a set of device data, the other is keyed on the |
| 113 // timestamp of the position. | 97 // timestamp of the position. |
| 114 typedef std::map<std::string16, Position> CacheMap; | 98 typedef std::map<string16, Position> CacheMap; |
| 115 CacheMap cache_; | 99 CacheMap cache_; |
| 116 typedef std::map<int64, CacheMap::iterator> CacheTimesMap; | 100 typedef std::map<int64, CacheMap::iterator> CacheTimesMap; |
| 117 CacheTimesMap cache_times_; | 101 CacheTimesMap cache_times_; |
| 118 }; | 102 }; |
| 119 | 103 |
| 120 | |
| 121 // NetworkLocationProvider factory function | 104 // NetworkLocationProvider factory function |
| 122 LocationProviderBase *NewNetworkLocationProvider( | 105 LocationProviderBase* NewNetworkLocationProvider( |
| 123 BrowsingContext *browsing_context, | 106 LocationProviderBase::AccessTokenStore* access_token_store, |
| 124 const std::string16 &url, | 107 URLRequestContextGetter* context, |
| 125 const std::string16 &host_name, | 108 const GURL& url, |
| 126 const std::string16 &language) { | 109 const string16& host_name) { |
| 127 return new NetworkLocationProvider(browsing_context, url, host_name, | 110 return new NetworkLocationProvider(access_token_store, context, |
| 128 language); | 111 url, host_name); |
| 129 } | 112 } |
| 130 | |
| 131 | 113 |
| 132 // NetworkLocationProvider | 114 // NetworkLocationProvider |
| 133 NetworkLocationProvider::NetworkLocationProvider( | 115 NetworkLocationProvider::NetworkLocationProvider( |
| 134 BrowsingContext *browsing_context, | 116 AccessTokenStore* access_token_store, |
| 135 const std::string16 &url, | 117 URLRequestContextGetter* url_context_getter, |
| 136 const std::string16 &host_name, | 118 const GURL& url, |
| 137 const std::string16 &language) | 119 const string16& host_name) |
| 138 : request_(NULL), | 120 : access_token_store_(access_token_store), |
| 139 url_(url), | 121 radio_data_provider_(NULL), |
| 140 host_name_(host_name), | 122 wifi_data_provider_(NULL), |
| 141 request_address_(false), | 123 is_radio_data_complete_(false), |
| 142 request_address_from_last_request_(false), | 124 is_wifi_data_complete_(false), |
| 143 address_language_(language), | 125 device_data_updated_timestamp_(kint64min), |
| 144 is_shutting_down_(false), | |
| 145 is_new_data_available_(false), | 126 is_new_data_available_(false), |
| 146 is_new_listener_waiting_(false), | 127 ALLOW_THIS_IN_INITIALIZER_LIST(delayed_start_task_(this)) { |
| 147 browsing_context_(browsing_context) { | 128 // Create the position cache. |
| 148 // TODO(steveblock): Consider allowing multiple values for "address_language" | 129 position_cache_.reset(new PositionCache()); |
| 149 // in the network protocol to allow better sharing of network location | 130 |
| 150 // providers. | 131 request_.reset(new NetworkLocationRequest(url_context_getter, url, |
| 132 host_name, this)); |
| 133 } |
| 134 |
| 135 NetworkLocationProvider::~NetworkLocationProvider() { |
| 136 if (radio_data_provider_) |
| 137 radio_data_provider_->Unregister(this); |
| 138 if (wifi_data_provider_) |
| 139 wifi_data_provider_->Unregister(this); |
| 140 } |
| 141 |
| 142 // LocationProviderBase implementation |
| 143 void NetworkLocationProvider::GetPosition(Position *position) { |
| 144 DCHECK(position); |
| 145 AutoLock lock(position_mutex_); |
| 146 *position = position_; |
| 147 } |
| 148 |
| 149 void NetworkLocationProvider::UpdatePosition() { |
| 150 if (is_new_data_available_) { |
| 151 RequestPosition(); |
| 152 } |
| 153 } |
| 154 |
| 155 // DeviceDataProviderInterface::ListenerInterface implementation. |
| 156 void NetworkLocationProvider::DeviceDataUpdateAvailable( |
| 157 RadioDataProvider* provider) { |
| 158 { |
| 159 AutoLock lock(data_mutex_); |
| 160 DCHECK(provider == radio_data_provider_); |
| 161 is_radio_data_complete_ = radio_data_provider_->GetData(&radio_data_); |
| 162 } |
| 163 OnDeviceDataUpdated(); |
| 164 } |
| 165 |
| 166 void NetworkLocationProvider::DeviceDataUpdateAvailable( |
| 167 WifiDataProvider* provider) { |
| 168 { |
| 169 AutoLock lock(data_mutex_); |
| 170 DCHECK(provider == wifi_data_provider_); |
| 171 is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_); |
| 172 } |
| 173 OnDeviceDataUpdated(); |
| 174 } |
| 175 |
| 176 // NetworkLocationRequest::ListenerInterface implementation. |
| 177 void NetworkLocationProvider::LocationResponseAvailable( |
| 178 const Position& position, |
| 179 bool server_error, |
| 180 const string16& access_token) { |
| 181 CheckRunningInClientLoop(); |
| 182 // Record the position and update our cache. |
| 183 { |
| 184 AutoLock position_lock(position_mutex_); |
| 185 position_ = position; |
| 186 } |
| 187 if (position.IsValidFix()) { |
| 188 AutoLock lock(data_mutex_); |
| 189 position_cache_->CachePosition(radio_data_, wifi_data_, position); |
| 190 } |
| 191 |
| 192 // Record access_token if it's set. |
| 193 if (!access_token.empty() && access_token_ != access_token) { |
| 194 access_token_ = access_token; |
| 195 access_token_store_->SetAccessToken(request_->url(), access_token); |
| 196 } |
| 197 |
| 198 // If new data arrived whilst request was pending reissue the request. |
| 199 UpdatePosition(); |
| 200 // Let listeners know that we now have a position available. |
| 201 UpdateListeners(); |
| 202 } |
| 203 |
| 204 bool NetworkLocationProvider::StartProvider() { |
| 205 CheckRunningInClientLoop(); |
| 206 DCHECK(radio_data_provider_ == NULL); |
| 207 DCHECK(wifi_data_provider_ == NULL); |
| 208 if (!request_->url().is_valid()) { |
| 209 LOG(WARNING) << "StartProvider() : Failed, Bad URL: " |
| 210 << request_->url().possibly_invalid_spec(); |
| 211 return false; |
| 212 } |
| 151 | 213 |
| 152 // Get the device data providers. The first call to Register will create the | 214 // Get the device data providers. The first call to Register will create the |
| 153 // provider and it will be deleted by ref counting. | 215 // provider and it will be deleted by ref counting. |
| 154 radio_data_provider_ = RadioDataProvider::Register(this); | 216 radio_data_provider_ = RadioDataProvider::Register(this); |
| 155 wifi_data_provider_ = WifiDataProvider::Register(this); | 217 wifi_data_provider_ = WifiDataProvider::Register(this); |
| 156 | 218 |
| 157 AccessTokenManager::GetInstance()->Register(url_); | 219 MessageLoop::current()->PostDelayedTask( |
| 158 | 220 FROM_HERE, |
| 159 // Create the position cache. | 221 delayed_start_task_.NewRunnableMethod( |
| 160 position_cache_.reset(new PositionCache()); | 222 &NetworkLocationProvider::RequestPosition), |
| 161 | 223 kDataCompleteWaitPeriod); |
| 162 // Start the worker thread | 224 { |
| 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. | 225 // Get the device data. |
| 317 data_mutex_.Lock(); | 226 AutoLock lock(data_mutex_); |
| 318 is_radio_data_complete_ = radio_data_provider_->GetData(&radio_data_); | 227 is_radio_data_complete_ = radio_data_provider_->GetData(&radio_data_); |
| 319 is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_); | 228 is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_); |
| 320 timestamp_ = GetCurrentTimeMillis(); | 229 } |
| 321 | 230 if (is_radio_data_complete_ || is_wifi_data_complete_) |
| 322 // For the first request, wait for a certain maximum time period to get as | 231 OnDeviceDataUpdated(); |
| 323 // much device data as possible. | 232 return true; |
| 324 int64 start_time = timestamp_; | 233 } |
| 325 while (true) { | 234 |
| 326 if (is_radio_data_complete_ && is_wifi_data_complete_) { | 235 // Other methods |
| 327 data_mutex_.Unlock(); | 236 void NetworkLocationProvider::RequestPosition() { |
| 328 break; | 237 CheckRunningInClientLoop(); |
| 238 |
| 239 delayed_start_task_.RevokeAll(); |
| 240 const Position* cached_position; |
| 241 { |
| 242 AutoLock lock(data_mutex_); |
| 243 cached_position = position_cache_->FindPosition(radio_data_, wifi_data_); |
| 244 } |
| 245 DCHECK_NE(device_data_updated_timestamp_, kint64min) << |
| 246 "Timestamp must be set before looking up position"; |
| 247 if (cached_position) { |
| 248 DCHECK(cached_position->IsValidFix()); |
| 249 // Record the position and update its timestamp. |
| 250 { |
| 251 AutoLock lock(position_mutex_); |
| 252 position_ = *cached_position; |
| 253 // The timestamp of a position fix is determined by the timestamp |
| 254 // of the source data update. (The value of position_.timestamp from |
| 255 // the cache could be from weeks ago!) |
| 256 position_.timestamp = device_data_updated_timestamp_; |
| 329 } | 257 } |
| 330 data_mutex_.Unlock(); | 258 is_new_data_available_ = false; |
| 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. | 259 // Let listeners know that we now have a position available. |
| 483 UpdateListeners(); | 260 UpdateListeners(); |
| 484 return true; | 261 return; |
| 485 } | 262 } |
| 486 | 263 |
| 487 assert(request_); | 264 DCHECK(request_ != NULL); |
| 488 is_last_request_complete_ = false; | 265 |
| 489 MutexLock data_lock(&data_mutex_); | 266 // TODO(joth): Consider timing out any pending request. |
| 490 return request_->MakeRequest(access_token, | 267 if (request_->is_request_pending()) |
| 491 radio_data_, | 268 return; |
| 492 wifi_data_, | 269 |
| 493 request_address_, | 270 is_new_data_available_ = false; |
| 494 address_language_, | 271 |
| 495 kBadLatLng, // We don't have a position to pass | 272 if (access_token_.empty()) |
| 496 kBadLatLng, // to the server. | 273 access_token_store_->GetAccessToken(request_->url(), &access_token_); |
| 497 timestamp_); | 274 |
| 498 } | 275 AutoLock data_lock(data_mutex_); |
| 499 | 276 request_->MakeRequest(access_token_, radio_data_, wifi_data_, |
| 500 void NetworkLocationProvider::DeviceDataUpdateAvailableImpl() { | 277 device_data_updated_timestamp_); |
| 501 timestamp_ = GetCurrentTimeMillis(); | 278 } |
| 502 | 279 |
| 503 // Signal to the worker thread that new data is available. | 280 void NetworkLocationProvider::OnDeviceDataUpdated() { |
| 504 is_new_data_available_ = true; | 281 if (MessageLoop::current() != client_loop()) { |
| 505 thread_notification_event_.Signal(); | 282 client_loop()->PostTask(FROM_HERE, |
| 506 } | 283 NewRunnableMethod(this, &NetworkLocationProvider::OnDeviceDataUpdated)); |
| 507 | 284 return; |
| 508 #endif // if 0 | 285 } |
| 286 device_data_updated_timestamp_ = GetCurrentTimeMillis(); |
| 287 |
| 288 is_new_data_available_ = is_radio_data_complete_ || is_radio_data_complete_; |
| 289 if (delayed_start_task_.empty() || |
| 290 (is_radio_data_complete_ && is_radio_data_complete_)) { |
| 291 UpdatePosition(); |
| 292 } |
| 293 } |
| OLD | NEW |