| 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 |