| 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\"}"; |
| 44 |
| 45 // Request data |
| 46 const char kConsiderIp[] = "considerIp"; |
| 47 const char kWifiAccessPoints[] = "wifiAccessPoints"; |
| 48 |
| 49 // WiFi access point objects. |
| 50 const char kMacAddress[] = "macAddress"; |
| 51 const char kSignalStrength[] = "signalStrength"; |
| 52 const char kAge[] = "age"; |
| 53 const char kChannel[] = "channel"; |
| 54 const char kSignalToNoiseRatio[] = "signalToNoiseRatio"; |
| 42 | 55 |
| 43 // Response data. | 56 // Response data. |
| 44 const char kLocationString[] = "location"; | 57 const char kLocationString[] = "location"; |
| 45 const char kLatString[] = "lat"; | 58 const char kLatString[] = "lat"; |
| 46 const char kLngString[] = "lng"; | 59 const char kLngString[] = "lng"; |
| 47 const char kAccuracyString[] = "accuracy"; | 60 const char kAccuracyString[] = "accuracy"; |
| 48 // Error object and its contents. | 61 // Error object and its contents. |
| 49 const char kErrorString[] = "error"; | 62 const char kErrorString[] = "error"; |
| 50 // "errors" array in "erorr" object is ignored. | 63 // "errors" array in "erorr" object is ignored. |
| 51 const char kCodeString[] = "code"; | 64 const char kCodeString[] = "code"; |
| (...skipping 29 matching lines...) Expand all Loading... |
| 81 // in tools/metrics/histograms/histograms.xml to keep it in sync. | 94 // in tools/metrics/histograms/histograms.xml to keep it in sync. |
| 82 SIMPLE_GEOLOCATION_REQUEST_RESULT_SUCCESS = 0, | 95 SIMPLE_GEOLOCATION_REQUEST_RESULT_SUCCESS = 0, |
| 83 SIMPLE_GEOLOCATION_REQUEST_RESULT_FAILURE = 1, | 96 SIMPLE_GEOLOCATION_REQUEST_RESULT_FAILURE = 1, |
| 84 SIMPLE_GEOLOCATION_REQUEST_RESULT_SERVER_ERROR = 2, | 97 SIMPLE_GEOLOCATION_REQUEST_RESULT_SERVER_ERROR = 2, |
| 85 SIMPLE_GEOLOCATION_REQUEST_RESULT_CANCELLED = 3, | 98 SIMPLE_GEOLOCATION_REQUEST_RESULT_CANCELLED = 3, |
| 86 | 99 |
| 87 // NOTE: Add entries only immediately above this line. | 100 // NOTE: Add entries only immediately above this line. |
| 88 SIMPLE_GEOLOCATION_REQUEST_RESULT_COUNT = 4 | 101 SIMPLE_GEOLOCATION_REQUEST_RESULT_COUNT = 4 |
| 89 }; | 102 }; |
| 90 | 103 |
| 104 SimpleGeolocationRequestTestMonitor* g_test_request_hook = nullptr; |
| 105 |
| 91 // Too many requests (more than 1) mean there is a problem in implementation. | 106 // Too many requests (more than 1) mean there is a problem in implementation. |
| 92 void RecordUmaEvent(SimpleGeolocationRequestEvent event) { | 107 void RecordUmaEvent(SimpleGeolocationRequestEvent event) { |
| 93 UMA_HISTOGRAM_ENUMERATION("SimpleGeolocation.Request.Event", | 108 UMA_HISTOGRAM_ENUMERATION("SimpleGeolocation.Request.Event", |
| 94 event, | 109 event, |
| 95 SIMPLE_GEOLOCATION_REQUEST_EVENT_COUNT); | 110 SIMPLE_GEOLOCATION_REQUEST_EVENT_COUNT); |
| 96 } | 111 } |
| 97 | 112 |
| 98 void RecordUmaResponseCode(int code) { | 113 void RecordUmaResponseCode(int code) { |
| 99 UMA_HISTOGRAM_SPARSE_SLOWLY("SimpleGeolocation.Request.ResponseCode", code); | 114 UMA_HISTOGRAM_SPARSE_SLOWLY("SimpleGeolocation.Request.ResponseCode", code); |
| 100 } | 115 } |
| (...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 240 return true; | 255 return true; |
| 241 } | 256 } |
| 242 | 257 |
| 243 // Attempts to extract a position from the response. Detects and indicates | 258 // Attempts to extract a position from the response. Detects and indicates |
| 244 // various failure cases. | 259 // various failure cases. |
| 245 bool GetGeolocationFromResponse(bool http_success, | 260 bool GetGeolocationFromResponse(bool http_success, |
| 246 int status_code, | 261 int status_code, |
| 247 const std::string& response_body, | 262 const std::string& response_body, |
| 248 const GURL& server_url, | 263 const GURL& server_url, |
| 249 Geoposition* position) { | 264 Geoposition* position) { |
| 265 VLOG(1) << "GetGeolocationFromResponse(http_success=" << http_success |
| 266 << ", status_code=" << status_code << "): response_body:\n" |
| 267 << response_body; |
| 250 | 268 |
| 251 // HttpPost can fail for a number of reasons. Most likely this is because | 269 // HttpPost can fail for a number of reasons. Most likely this is because |
| 252 // we're offline, or there was no response. | 270 // we're offline, or there was no response. |
| 253 if (!http_success) { | 271 if (!http_success) { |
| 254 PrintGeolocationError(server_url, "No response received", position); | 272 PrintGeolocationError(server_url, "No response received", position); |
| 255 RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_EMPTY); | 273 RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_EMPTY); |
| 256 return false; | 274 return false; |
| 257 } | 275 } |
| 258 if (status_code != net::HTTP_OK) { | 276 if (status_code != net::HTTP_OK) { |
| 259 std::string message = "Returned error code "; | 277 std::string message = "Returned error code "; |
| 260 message += base::IntToString(status_code); | 278 message += base::IntToString(status_code); |
| 261 PrintGeolocationError(server_url, message, position); | 279 PrintGeolocationError(server_url, message, position); |
| 262 RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_NOT_OK); | 280 RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_NOT_OK); |
| 263 return false; | 281 return false; |
| 264 } | 282 } |
| 265 | 283 |
| 266 return ParseServerResponse(server_url, response_body, position); | 284 return ParseServerResponse(server_url, response_body, position); |
| 267 } | 285 } |
| 268 | 286 |
| 287 void ReportUmaHasWiFiAccessPoints(bool value) { |
| 288 UMA_HISTOGRAM_BOOLEAN("SimpleGeolocation.Request.HasWiFiAccessPoints", value); |
| 289 } |
| 290 |
| 269 } // namespace | 291 } // namespace |
| 270 | 292 |
| 271 SimpleGeolocationRequest::SimpleGeolocationRequest( | 293 SimpleGeolocationRequest::SimpleGeolocationRequest( |
| 272 net::URLRequestContextGetter* url_context_getter, | 294 net::URLRequestContextGetter* url_context_getter, |
| 273 const GURL& service_url, | 295 const GURL& service_url, |
| 274 base::TimeDelta timeout) | 296 base::TimeDelta timeout, |
| 297 scoped_ptr<WifiAccessPointVector> wifi_data) |
| 275 : url_context_getter_(url_context_getter), | 298 : url_context_getter_(url_context_getter), |
| 276 service_url_(service_url), | 299 service_url_(service_url), |
| 277 retry_sleep_on_server_error_(base::TimeDelta::FromSeconds( | 300 retry_sleep_on_server_error_(base::TimeDelta::FromSeconds( |
| 278 kResolveGeolocationRetrySleepOnServerErrorSeconds)), | 301 kResolveGeolocationRetrySleepOnServerErrorSeconds)), |
| 279 retry_sleep_on_bad_response_(base::TimeDelta::FromSeconds( | 302 retry_sleep_on_bad_response_(base::TimeDelta::FromSeconds( |
| 280 kResolveGeolocationRetrySleepBadResponseSeconds)), | 303 kResolveGeolocationRetrySleepBadResponseSeconds)), |
| 281 timeout_(timeout), | 304 timeout_(timeout), |
| 282 retries_(0) { | 305 retries_(0), |
| 283 } | 306 wifi_data_(wifi_data.release()) {} |
| 284 | 307 |
| 285 SimpleGeolocationRequest::~SimpleGeolocationRequest() { | 308 SimpleGeolocationRequest::~SimpleGeolocationRequest() { |
| 286 DCHECK(thread_checker_.CalledOnValidThread()); | 309 DCHECK(thread_checker_.CalledOnValidThread()); |
| 287 | 310 |
| 288 // If callback is not empty, request is cancelled. | 311 // If callback is not empty, request is cancelled. |
| 289 if (!callback_.is_null()) { | 312 if (!callback_.is_null()) { |
| 290 RecordUmaResponseTime(base::Time::Now() - request_started_at_, false); | 313 RecordUmaResponseTime(base::Time::Now() - request_started_at_, false); |
| 291 RecordUmaResult(SIMPLE_GEOLOCATION_REQUEST_RESULT_CANCELLED, retries_); | 314 RecordUmaResult(SIMPLE_GEOLOCATION_REQUEST_RESULT_CANCELLED, retries_); |
| 292 } | 315 } |
| 316 |
| 317 if (g_test_request_hook) |
| 318 g_test_request_hook->OnRequestCreated(this); |
| 319 } |
| 320 |
| 321 std::string SimpleGeolocationRequest::FormatRequestBody() const { |
| 322 if (!wifi_data_) { |
| 323 ReportUmaHasWiFiAccessPoints(false); |
| 324 return std::string(kSimpleGeolocationRequestBody); |
| 325 } |
| 326 |
| 327 scoped_ptr<base::DictionaryValue> request(new base::DictionaryValue); |
| 328 request->SetBooleanWithoutPathExpansion(kConsiderIp, true); |
| 329 |
| 330 base::ListValue* wifi_access_points(new base::ListValue); |
| 331 request->SetWithoutPathExpansion(kWifiAccessPoints, wifi_access_points); |
| 332 |
| 333 for (const WifiAccessPoint& access_point : *wifi_data_) { |
| 334 base::DictionaryValue* access_point_dictionary = new base::DictionaryValue; |
| 335 wifi_access_points->Append(access_point_dictionary); |
| 336 |
| 337 access_point_dictionary->SetStringWithoutPathExpansion( |
| 338 kMacAddress, access_point.mac_address); |
| 339 access_point_dictionary->SetIntegerWithoutPathExpansion( |
| 340 kSignalStrength, access_point.signal_strength); |
| 341 if (!access_point.timestamp.is_null()) { |
| 342 access_point_dictionary->SetStringWithoutPathExpansion( |
| 343 kAge, |
| 344 base::Int64ToString( |
| 345 (base::Time::Now() - access_point.timestamp).InMilliseconds())); |
| 346 } |
| 347 |
| 348 access_point_dictionary->SetIntegerWithoutPathExpansion( |
| 349 kChannel, access_point.channel); |
| 350 access_point_dictionary->SetIntegerWithoutPathExpansion( |
| 351 kSignalToNoiseRatio, access_point.signal_to_noise); |
| 352 } |
| 353 std::string result; |
| 354 if (!base::JSONWriter::Write(*request, &result)) { |
| 355 ReportUmaHasWiFiAccessPoints(false); |
| 356 return std::string(kSimpleGeolocationRequestBody); |
| 357 } |
| 358 ReportUmaHasWiFiAccessPoints(wifi_data_->size()); |
| 359 |
| 360 return result; |
| 293 } | 361 } |
| 294 | 362 |
| 295 void SimpleGeolocationRequest::StartRequest() { | 363 void SimpleGeolocationRequest::StartRequest() { |
| 296 DCHECK(thread_checker_.CalledOnValidThread()); | 364 DCHECK(thread_checker_.CalledOnValidThread()); |
| 297 RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_REQUEST_START); | 365 RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_REQUEST_START); |
| 298 ++retries_; | 366 ++retries_; |
| 299 | 367 |
| 368 const std::string request_body = FormatRequestBody(); |
| 369 VLOG(1) << "SimpleGeolocationRequest::StartRequest(): request body:\n" |
| 370 << request_body; |
| 371 |
| 300 url_fetcher_ = | 372 url_fetcher_ = |
| 301 net::URLFetcher::Create(request_url_, net::URLFetcher::POST, this); | 373 net::URLFetcher::Create(request_url_, net::URLFetcher::POST, this); |
| 302 url_fetcher_->SetRequestContext(url_context_getter_.get()); | 374 url_fetcher_->SetRequestContext(url_context_getter_.get()); |
| 303 url_fetcher_->SetUploadData("application/json", | 375 url_fetcher_->SetUploadData("application/json", request_body); |
| 304 std::string(kSimpleGeolocationRequestBody)); | |
| 305 url_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | | 376 url_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | |
| 306 net::LOAD_DISABLE_CACHE | | 377 net::LOAD_DISABLE_CACHE | |
| 307 net::LOAD_DO_NOT_SAVE_COOKIES | | 378 net::LOAD_DO_NOT_SAVE_COOKIES | |
| 308 net::LOAD_DO_NOT_SEND_COOKIES | | 379 net::LOAD_DO_NOT_SEND_COOKIES | |
| 309 net::LOAD_DO_NOT_SEND_AUTH_DATA); | 380 net::LOAD_DO_NOT_SEND_AUTH_DATA); |
| 381 |
| 382 // Call test hook before asynchronous request actually starts. |
| 383 if (g_test_request_hook) |
| 384 g_test_request_hook->OnStart(this); |
| 385 |
| 310 url_fetcher_->Start(); | 386 url_fetcher_->Start(); |
| 311 } | 387 } |
| 312 | 388 |
| 313 void SimpleGeolocationRequest::MakeRequest(const ResponseCallback& callback) { | 389 void SimpleGeolocationRequest::MakeRequest(const ResponseCallback& callback) { |
| 314 callback_ = callback; | 390 callback_ = callback; |
| 315 request_url_ = GeolocationRequestURL(service_url_); | 391 request_url_ = GeolocationRequestURL(service_url_); |
| 316 timeout_timer_.Start( | 392 timeout_timer_.Start( |
| 317 FROM_HERE, timeout_, this, &SimpleGeolocationRequest::OnTimeout); | 393 FROM_HERE, timeout_, this, &SimpleGeolocationRequest::OnTimeout); |
| 318 request_started_at_ = base::Time::Now(); | 394 request_started_at_ = base::Time::Now(); |
| 319 StartRequest(); | 395 StartRequest(); |
| 320 } | 396 } |
| 321 | 397 |
| 398 // static |
| 399 void SimpleGeolocationRequest::SetTestMonitor( |
| 400 SimpleGeolocationRequestTestMonitor* monitor) { |
| 401 g_test_request_hook = monitor; |
| 402 } |
| 403 |
| 404 std::string SimpleGeolocationRequest::FormatRequestBodyForTesting() const { |
| 405 return FormatRequestBody(); |
| 406 } |
| 407 |
| 322 void SimpleGeolocationRequest::Retry(bool server_error) { | 408 void SimpleGeolocationRequest::Retry(bool server_error) { |
| 323 base::TimeDelta delay(server_error ? retry_sleep_on_server_error_ | 409 base::TimeDelta delay(server_error ? retry_sleep_on_server_error_ |
| 324 : retry_sleep_on_bad_response_); | 410 : retry_sleep_on_bad_response_); |
| 325 request_scheduled_.Start( | 411 request_scheduled_.Start( |
| 326 FROM_HERE, delay, this, &SimpleGeolocationRequest::StartRequest); | 412 FROM_HERE, delay, this, &SimpleGeolocationRequest::StartRequest); |
| 327 } | 413 } |
| 328 | 414 |
| 329 void SimpleGeolocationRequest::OnURLFetchComplete( | 415 void SimpleGeolocationRequest::OnURLFetchComplete( |
| 330 const net::URLFetcher* source) { | 416 const net::URLFetcher* source) { |
| 331 DCHECK_EQ(url_fetcher_.get(), source); | 417 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 | 469 ? SIMPLE_GEOLOCATION_REQUEST_RESULT_SERVER_ERROR |
| 384 : SIMPLE_GEOLOCATION_REQUEST_RESULT_FAILURE); | 470 : SIMPLE_GEOLOCATION_REQUEST_RESULT_FAILURE); |
| 385 RecordUmaResult(result, retries_); | 471 RecordUmaResult(result, retries_); |
| 386 position_.status = Geoposition::STATUS_TIMEOUT; | 472 position_.status = Geoposition::STATUS_TIMEOUT; |
| 387 const base::TimeDelta elapsed = base::Time::Now() - request_started_at_; | 473 const base::TimeDelta elapsed = base::Time::Now() - request_started_at_; |
| 388 ReplyAndDestroySelf(elapsed, true /* server_error */); | 474 ReplyAndDestroySelf(elapsed, true /* server_error */); |
| 389 // "this" is already destroyed here. | 475 // "this" is already destroyed here. |
| 390 } | 476 } |
| 391 | 477 |
| 392 } // namespace chromeos | 478 } // namespace chromeos |
| OLD | NEW |