Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "chromeos/geolocation/simple_geolocation_request.h" | 5 #include "chromeos/geolocation/simple_geolocation_request.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include <string> | 10 #include <string> |
| 11 | 11 |
| 12 #include "base/json/json_reader.h" | 12 #include "base/json/json_reader.h" |
| 13 #include "base/json/json_writer.h" | |
| 13 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
| 14 #include "base/metrics/sparse_histogram.h" | 15 #include "base/metrics/sparse_histogram.h" |
| 15 #include "base/strings/string_number_conversions.h" | 16 #include "base/strings/string_number_conversions.h" |
| 16 #include "base/strings/stringprintf.h" | 17 #include "base/strings/stringprintf.h" |
| 17 #include "base/time/time.h" | 18 #include "base/time/time.h" |
| 18 #include "base/values.h" | 19 #include "base/values.h" |
| 19 #include "chromeos/geolocation/simple_geolocation_provider.h" | 20 #include "chromeos/geolocation/simple_geolocation_provider.h" |
| 21 #include "chromeos/geolocation/simple_geolocation_request_test_monitor.h" | |
| 20 #include "google_apis/google_api_keys.h" | 22 #include "google_apis/google_api_keys.h" |
| 21 #include "net/base/escape.h" | 23 #include "net/base/escape.h" |
| 22 #include "net/base/load_flags.h" | 24 #include "net/base/load_flags.h" |
| 23 #include "net/http/http_status_code.h" | 25 #include "net/http/http_status_code.h" |
| 24 #include "net/url_request/url_request_context_getter.h" | 26 #include "net/url_request/url_request_context_getter.h" |
| 25 #include "net/url_request/url_request_status.h" | 27 #include "net/url_request/url_request_status.h" |
| 26 | 28 |
| 27 // Location resolve timeout is usually 1 minute, so 2 minutes with 50 buckets | 29 // Location resolve timeout is usually 1 minute, so 2 minutes with 50 buckets |
| 28 // should be enough. | 30 // should be enough. |
| 29 #define UMA_HISTOGRAM_LOCATION_RESPONSE_TIMES(name, sample) \ | 31 #define UMA_HISTOGRAM_LOCATION_RESPONSE_TIMES(name, sample) \ |
| 30 UMA_HISTOGRAM_CUSTOM_TIMES(name, \ | 32 UMA_HISTOGRAM_CUSTOM_TIMES(name, \ |
| 31 sample, \ | 33 sample, \ |
| 32 base::TimeDelta::FromMilliseconds(10), \ | 34 base::TimeDelta::FromMilliseconds(10), \ |
| 33 base::TimeDelta::FromMinutes(2), \ | 35 base::TimeDelta::FromMinutes(2), \ |
| 34 50) | 36 50) |
| 35 | 37 |
| 36 namespace chromeos { | 38 namespace chromeos { |
| 37 | 39 |
| 38 namespace { | 40 namespace { |
| 39 | 41 |
| 40 // The full request text. (no parameters are supported by now) | 42 // The full request text. (no parameters are supported by now) |
| 41 const char kSimpleGeolocationRequestBody[] = "{\"considerIP\": \"true\"}"; | 43 const char kSimpleGeolocationRequestBody[] = "{\"considerIP\": \"true\"}"; |
| 42 | 44 |
| 45 // Request data | |
| 46 // const char kHomeMobileCountryCode[] = "homeMobileCountryCode"; | |
| 47 // const char kHomeMobileNetworkCode[] = "homeMobileNetworkCode"; | |
| 48 // const char kRadioType[] = "radioType"; | |
| 49 // const char kCarrier[] = "carrier"; | |
|
stevenjb
2016/03/22 16:39:49
Remove?
Alexander Alekseev
2016/03/23 00:04:45
Done.
| |
| 50 const char kConsiderIp[] = "considerIp"; | |
| 51 // const char kCellTowers[] = "cellTowers"; | |
| 52 const char kWifiAccessPoints[] = "wifiAccessPoints"; | |
| 53 | |
| 54 // WiFi access point objects. | |
| 55 const char kMacAddress[] = "macAddress"; | |
| 56 const char kSignalStrength[] = "signalStrength"; | |
| 57 const char kAge[] = "age"; | |
| 58 const char kChannel[] = "channel"; | |
| 59 const char kSignalToNoiseRatio[] = "signalToNoiseRatio"; | |
| 60 | |
| 43 // Response data. | 61 // Response data. |
| 44 const char kLocationString[] = "location"; | 62 const char kLocationString[] = "location"; |
| 45 const char kLatString[] = "lat"; | 63 const char kLatString[] = "lat"; |
| 46 const char kLngString[] = "lng"; | 64 const char kLngString[] = "lng"; |
| 47 const char kAccuracyString[] = "accuracy"; | 65 const char kAccuracyString[] = "accuracy"; |
| 48 // Error object and its contents. | 66 // Error object and its contents. |
| 49 const char kErrorString[] = "error"; | 67 const char kErrorString[] = "error"; |
| 50 // "errors" array in "erorr" object is ignored. | 68 // "errors" array in "erorr" object is ignored. |
| 51 const char kCodeString[] = "code"; | 69 const char kCodeString[] = "code"; |
| 52 const char kMessageString[] = "message"; | 70 const char kMessageString[] = "message"; |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 81 // in tools/metrics/histograms/histograms.xml to keep it in sync. | 99 // in tools/metrics/histograms/histograms.xml to keep it in sync. |
| 82 SIMPLE_GEOLOCATION_REQUEST_RESULT_SUCCESS = 0, | 100 SIMPLE_GEOLOCATION_REQUEST_RESULT_SUCCESS = 0, |
| 83 SIMPLE_GEOLOCATION_REQUEST_RESULT_FAILURE = 1, | 101 SIMPLE_GEOLOCATION_REQUEST_RESULT_FAILURE = 1, |
| 84 SIMPLE_GEOLOCATION_REQUEST_RESULT_SERVER_ERROR = 2, | 102 SIMPLE_GEOLOCATION_REQUEST_RESULT_SERVER_ERROR = 2, |
| 85 SIMPLE_GEOLOCATION_REQUEST_RESULT_CANCELLED = 3, | 103 SIMPLE_GEOLOCATION_REQUEST_RESULT_CANCELLED = 3, |
| 86 | 104 |
| 87 // NOTE: Add entries only immediately above this line. | 105 // NOTE: Add entries only immediately above this line. |
| 88 SIMPLE_GEOLOCATION_REQUEST_RESULT_COUNT = 4 | 106 SIMPLE_GEOLOCATION_REQUEST_RESULT_COUNT = 4 |
| 89 }; | 107 }; |
| 90 | 108 |
| 109 SimpleGeolocationRequestTestMonitor* g_test_request_hook = nullptr; | |
| 110 | |
| 91 // Too many requests (more than 1) mean there is a problem in implementation. | 111 // Too many requests (more than 1) mean there is a problem in implementation. |
| 92 void RecordUmaEvent(SimpleGeolocationRequestEvent event) { | 112 void RecordUmaEvent(SimpleGeolocationRequestEvent event) { |
| 93 UMA_HISTOGRAM_ENUMERATION("SimpleGeolocation.Request.Event", | 113 UMA_HISTOGRAM_ENUMERATION("SimpleGeolocation.Request.Event", |
| 94 event, | 114 event, |
| 95 SIMPLE_GEOLOCATION_REQUEST_EVENT_COUNT); | 115 SIMPLE_GEOLOCATION_REQUEST_EVENT_COUNT); |
| 96 } | 116 } |
| 97 | 117 |
| 98 void RecordUmaResponseCode(int code) { | 118 void RecordUmaResponseCode(int code) { |
| 99 UMA_HISTOGRAM_SPARSE_SLOWLY("SimpleGeolocation.Request.ResponseCode", code); | 119 UMA_HISTOGRAM_SPARSE_SLOWLY("SimpleGeolocation.Request.ResponseCode", code); |
| 100 } | 120 } |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 240 return true; | 260 return true; |
| 241 } | 261 } |
| 242 | 262 |
| 243 // Attempts to extract a position from the response. Detects and indicates | 263 // Attempts to extract a position from the response. Detects and indicates |
| 244 // various failure cases. | 264 // various failure cases. |
| 245 bool GetGeolocationFromResponse(bool http_success, | 265 bool GetGeolocationFromResponse(bool http_success, |
| 246 int status_code, | 266 int status_code, |
| 247 const std::string& response_body, | 267 const std::string& response_body, |
| 248 const GURL& server_url, | 268 const GURL& server_url, |
| 249 Geoposition* position) { | 269 Geoposition* position) { |
| 270 VLOG(1) << "GetGeolocationFromResponse(http_success=" << http_success | |
| 271 << ", status_code=" << status_code << "): response_body:\n" | |
| 272 << response_body; | |
| 250 | 273 |
| 251 // HttpPost can fail for a number of reasons. Most likely this is because | 274 // HttpPost can fail for a number of reasons. Most likely this is because |
| 252 // we're offline, or there was no response. | 275 // we're offline, or there was no response. |
| 253 if (!http_success) { | 276 if (!http_success) { |
| 254 PrintGeolocationError(server_url, "No response received", position); | 277 PrintGeolocationError(server_url, "No response received", position); |
| 255 RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_EMPTY); | 278 RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_EMPTY); |
| 256 return false; | 279 return false; |
| 257 } | 280 } |
| 258 if (status_code != net::HTTP_OK) { | 281 if (status_code != net::HTTP_OK) { |
| 259 std::string message = "Returned error code "; | 282 std::string message = "Returned error code "; |
| 260 message += base::IntToString(status_code); | 283 message += base::IntToString(status_code); |
| 261 PrintGeolocationError(server_url, message, position); | 284 PrintGeolocationError(server_url, message, position); |
| 262 RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_NOT_OK); | 285 RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_NOT_OK); |
| 263 return false; | 286 return false; |
| 264 } | 287 } |
| 265 | 288 |
| 266 return ParseServerResponse(server_url, response_body, position); | 289 return ParseServerResponse(server_url, response_body, position); |
| 267 } | 290 } |
| 268 | 291 |
| 269 } // namespace | 292 } // namespace |
| 270 | 293 |
| 271 SimpleGeolocationRequest::SimpleGeolocationRequest( | 294 SimpleGeolocationRequest::SimpleGeolocationRequest( |
| 272 net::URLRequestContextGetter* url_context_getter, | 295 net::URLRequestContextGetter* url_context_getter, |
| 273 const GURL& service_url, | 296 const GURL& service_url, |
| 274 base::TimeDelta timeout) | 297 base::TimeDelta timeout, |
| 298 scoped_ptr<WifiAccessPointVector> wifi_data) | |
| 275 : url_context_getter_(url_context_getter), | 299 : url_context_getter_(url_context_getter), |
| 276 service_url_(service_url), | 300 service_url_(service_url), |
| 277 retry_sleep_on_server_error_(base::TimeDelta::FromSeconds( | 301 retry_sleep_on_server_error_(base::TimeDelta::FromSeconds( |
| 278 kResolveGeolocationRetrySleepOnServerErrorSeconds)), | 302 kResolveGeolocationRetrySleepOnServerErrorSeconds)), |
| 279 retry_sleep_on_bad_response_(base::TimeDelta::FromSeconds( | 303 retry_sleep_on_bad_response_(base::TimeDelta::FromSeconds( |
| 280 kResolveGeolocationRetrySleepBadResponseSeconds)), | 304 kResolveGeolocationRetrySleepBadResponseSeconds)), |
| 281 timeout_(timeout), | 305 timeout_(timeout), |
| 282 retries_(0) { | 306 retries_(0), |
| 283 } | 307 wifi_data_(wifi_data.release()) {} |
| 284 | 308 |
| 285 SimpleGeolocationRequest::~SimpleGeolocationRequest() { | 309 SimpleGeolocationRequest::~SimpleGeolocationRequest() { |
| 286 DCHECK(thread_checker_.CalledOnValidThread()); | 310 DCHECK(thread_checker_.CalledOnValidThread()); |
| 287 | 311 |
| 288 // If callback is not empty, request is cancelled. | 312 // If callback is not empty, request is cancelled. |
| 289 if (!callback_.is_null()) { | 313 if (!callback_.is_null()) { |
| 290 RecordUmaResponseTime(base::Time::Now() - request_started_at_, false); | 314 RecordUmaResponseTime(base::Time::Now() - request_started_at_, false); |
| 291 RecordUmaResult(SIMPLE_GEOLOCATION_REQUEST_RESULT_CANCELLED, retries_); | 315 RecordUmaResult(SIMPLE_GEOLOCATION_REQUEST_RESULT_CANCELLED, retries_); |
| 292 } | 316 } |
| 317 | |
| 318 if (g_test_request_hook) | |
| 319 g_test_request_hook->OnRequestCreated(this); | |
| 320 } | |
| 321 | |
| 322 std::string SimpleGeolocationRequest::FormatRequestBody() const { | |
| 323 if (!wifi_data_) { | |
| 324 UMA_HISTOGRAM_BOOLEAN("SimpleGeolocation.Request.HasWiFiAccessPoints", | |
| 325 false); | |
| 326 return std::string(kSimpleGeolocationRequestBody); | |
| 327 } | |
| 328 | |
| 329 scoped_ptr<base::DictionaryValue> request(new base::DictionaryValue); | |
| 330 request->SetBooleanWithoutPathExpansion(kConsiderIp, true); | |
| 331 | |
| 332 base::ListValue* wifi_access_points(new base::ListValue); | |
| 333 request->SetWithoutPathExpansion(kWifiAccessPoints, wifi_access_points); | |
| 334 | |
| 335 for (const WifiAccessPoint& access_point : *wifi_data_) { | |
| 336 base::DictionaryValue* access_point_dictionary = new base::DictionaryValue; | |
| 337 wifi_access_points->Append(access_point_dictionary); | |
| 338 | |
| 339 access_point_dictionary->SetStringWithoutPathExpansion( | |
| 340 kMacAddress, access_point.mac_address); | |
| 341 access_point_dictionary->SetIntegerWithoutPathExpansion( | |
| 342 kSignalStrength, access_point.signal_strength); | |
| 343 if (!access_point.timestamp.is_null()) | |
| 344 access_point_dictionary->SetStringWithoutPathExpansion( | |
| 345 kAge, | |
| 346 base::Int64ToString( | |
| 347 (base::Time::Now() - access_point.timestamp).InMilliseconds())); | |
|
stevenjb
2016/03/22 16:39:49
{}
Alexander Alekseev
2016/03/23 00:04:45
Done.
| |
| 348 | |
| 349 access_point_dictionary->SetIntegerWithoutPathExpansion( | |
| 350 kChannel, access_point.channel); | |
| 351 access_point_dictionary->SetIntegerWithoutPathExpansion( | |
| 352 kSignalToNoiseRatio, access_point.signal_to_noise); | |
| 353 } | |
| 354 std::string result; | |
| 355 if (!base::JSONWriter::Write(*request, &result)) { | |
| 356 UMA_HISTOGRAM_BOOLEAN("SimpleGeolocation.Request.HasWiFiAccessPoints", | |
| 357 false); | |
| 358 return std::string(kSimpleGeolocationRequestBody); | |
| 359 } | |
| 360 UMA_HISTOGRAM_BOOLEAN("SimpleGeolocation.Request.HasWiFiAccessPoints", | |
| 361 wifi_data_->size()); | |
| 362 | |
| 363 return result; | |
| 293 } | 364 } |
| 294 | 365 |
| 295 void SimpleGeolocationRequest::StartRequest() { | 366 void SimpleGeolocationRequest::StartRequest() { |
| 296 DCHECK(thread_checker_.CalledOnValidThread()); | 367 DCHECK(thread_checker_.CalledOnValidThread()); |
| 297 RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_REQUEST_START); | 368 RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_REQUEST_START); |
| 298 ++retries_; | 369 ++retries_; |
| 299 | 370 |
| 371 const std::string request_body = FormatRequestBody(); | |
| 372 VLOG(1) << "SimpleGeolocationRequest::StartRequest(): request body:\n" | |
| 373 << request_body; | |
| 374 | |
| 300 url_fetcher_ = | 375 url_fetcher_ = |
| 301 net::URLFetcher::Create(request_url_, net::URLFetcher::POST, this); | 376 net::URLFetcher::Create(request_url_, net::URLFetcher::POST, this); |
| 302 url_fetcher_->SetRequestContext(url_context_getter_.get()); | 377 url_fetcher_->SetRequestContext(url_context_getter_.get()); |
| 303 url_fetcher_->SetUploadData("application/json", | 378 url_fetcher_->SetUploadData("application/json", request_body); |
| 304 std::string(kSimpleGeolocationRequestBody)); | |
| 305 url_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | | 379 url_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | |
| 306 net::LOAD_DISABLE_CACHE | | 380 net::LOAD_DISABLE_CACHE | |
| 307 net::LOAD_DO_NOT_SAVE_COOKIES | | 381 net::LOAD_DO_NOT_SAVE_COOKIES | |
| 308 net::LOAD_DO_NOT_SEND_COOKIES | | 382 net::LOAD_DO_NOT_SEND_COOKIES | |
| 309 net::LOAD_DO_NOT_SEND_AUTH_DATA); | 383 net::LOAD_DO_NOT_SEND_AUTH_DATA); |
| 384 | |
| 385 // Call test hook before | |
|
stevenjb
2016/03/22 16:39:49
s/before/first./ ?
Alexander Alekseev
2016/03/23 00:04:45
This is only half of the comment I was about to wr
| |
| 386 if (g_test_request_hook) | |
| 387 g_test_request_hook->OnStart(this); | |
| 388 | |
| 310 url_fetcher_->Start(); | 389 url_fetcher_->Start(); |
| 311 } | 390 } |
| 312 | 391 |
| 313 void SimpleGeolocationRequest::MakeRequest(const ResponseCallback& callback) { | 392 void SimpleGeolocationRequest::MakeRequest(const ResponseCallback& callback) { |
| 314 callback_ = callback; | 393 callback_ = callback; |
| 315 request_url_ = GeolocationRequestURL(service_url_); | 394 request_url_ = GeolocationRequestURL(service_url_); |
| 316 timeout_timer_.Start( | 395 timeout_timer_.Start( |
| 317 FROM_HERE, timeout_, this, &SimpleGeolocationRequest::OnTimeout); | 396 FROM_HERE, timeout_, this, &SimpleGeolocationRequest::OnTimeout); |
| 318 request_started_at_ = base::Time::Now(); | 397 request_started_at_ = base::Time::Now(); |
| 319 StartRequest(); | 398 StartRequest(); |
| 320 } | 399 } |
| 321 | 400 |
| 401 // static | |
| 402 void SimpleGeolocationRequest::SetTestMonitor( | |
| 403 SimpleGeolocationRequestTestMonitor* monitor) { | |
| 404 g_test_request_hook = monitor; | |
| 405 } | |
| 406 | |
| 407 std::string SimpleGeolocationRequest::FormatRequestBodyForTesting() const { | |
| 408 return FormatRequestBody(); | |
| 409 } | |
| 410 | |
| 322 void SimpleGeolocationRequest::Retry(bool server_error) { | 411 void SimpleGeolocationRequest::Retry(bool server_error) { |
| 323 base::TimeDelta delay(server_error ? retry_sleep_on_server_error_ | 412 base::TimeDelta delay(server_error ? retry_sleep_on_server_error_ |
| 324 : retry_sleep_on_bad_response_); | 413 : retry_sleep_on_bad_response_); |
| 325 request_scheduled_.Start( | 414 request_scheduled_.Start( |
| 326 FROM_HERE, delay, this, &SimpleGeolocationRequest::StartRequest); | 415 FROM_HERE, delay, this, &SimpleGeolocationRequest::StartRequest); |
| 327 } | 416 } |
| 328 | 417 |
| 329 void SimpleGeolocationRequest::OnURLFetchComplete( | 418 void SimpleGeolocationRequest::OnURLFetchComplete( |
| 330 const net::URLFetcher* source) { | 419 const net::URLFetcher* source) { |
| 331 DCHECK_EQ(url_fetcher_.get(), source); | 420 DCHECK_EQ(url_fetcher_.get(), source); |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 383 ? SIMPLE_GEOLOCATION_REQUEST_RESULT_SERVER_ERROR | 472 ? SIMPLE_GEOLOCATION_REQUEST_RESULT_SERVER_ERROR |
| 384 : SIMPLE_GEOLOCATION_REQUEST_RESULT_FAILURE); | 473 : SIMPLE_GEOLOCATION_REQUEST_RESULT_FAILURE); |
| 385 RecordUmaResult(result, retries_); | 474 RecordUmaResult(result, retries_); |
| 386 position_.status = Geoposition::STATUS_TIMEOUT; | 475 position_.status = Geoposition::STATUS_TIMEOUT; |
| 387 const base::TimeDelta elapsed = base::Time::Now() - request_started_at_; | 476 const base::TimeDelta elapsed = base::Time::Now() - request_started_at_; |
| 388 ReplyAndDestroySelf(elapsed, true /* server_error */); | 477 ReplyAndDestroySelf(elapsed, true /* server_error */); |
| 389 // "this" is already destroyed here. | 478 // "this" is already destroyed here. |
| 390 } | 479 } |
| 391 | 480 |
| 392 } // namespace chromeos | 481 } // namespace chromeos |
| OLD | NEW |