simple_geolocation_request.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  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
  3. // found in the LICENSE file.
  4. #include "ash/components/geolocation/simple_geolocation_request.h"
  5. #include <stddef.h>
  6. #include <algorithm>
  7. #include <memory>
  8. #include <string>
  9. #include <utility>
  10. #include "ash/components/geolocation/simple_geolocation_provider.h"
  11. #include "ash/components/geolocation/simple_geolocation_request_test_monitor.h"
  12. #include "base/bind.h"
  13. #include "base/json/json_reader.h"
  14. #include "base/json/json_writer.h"
  15. #include "base/metrics/histogram_functions.h"
  16. #include "base/metrics/histogram_macros.h"
  17. #include "base/strings/escape.h"
  18. #include "base/strings/string_number_conversions.h"
  19. #include "base/strings/stringprintf.h"
  20. #include "base/time/time.h"
  21. #include "base/values.h"
  22. #include "google_apis/google_api_keys.h"
  23. #include "net/base/load_flags.h"
  24. #include "net/http/http_status_code.h"
  25. #include "services/network/public/cpp/resource_request.h"
  26. #include "services/network/public/cpp/shared_url_loader_factory.h"
  27. #include "services/network/public/cpp/simple_url_loader.h"
  28. #include "services/network/public/mojom/url_response_head.mojom.h"
  29. // Location resolve timeout is usually 1 minute, so 2 minutes with 50 buckets
  30. // should be enough.
  31. #define UMA_HISTOGRAM_LOCATION_RESPONSE_TIMES(name, sample) \
  32. UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, base::Milliseconds(10), \
  33. base::Minutes(2), 50)
  34. namespace ash {
  35. namespace {
  36. // Used if sending location signals (WiFi APs, cell towers, etc) is disabled.
  37. constexpr char kSimpleGeolocationRequestBody[] = "{\"considerIp\": \"true\"}";
  38. // Geolocation request field keys:
  39. // Top-level request data fields.
  40. constexpr char kConsiderIp[] = "considerIp";
  41. constexpr char kWifiAccessPoints[] = "wifiAccessPoints";
  42. constexpr char kCellTowers[] = "cellTowers";
  43. // Shared Wifi and Cell Tower objects.
  44. constexpr char kAge[] = "age";
  45. constexpr char kSignalStrength[] = "signalStrength";
  46. // WiFi access point objects.
  47. constexpr char kMacAddress[] = "macAddress";
  48. constexpr char kChannel[] = "channel";
  49. constexpr char kSignalToNoiseRatio[] = "signalToNoiseRatio";
  50. // Cell tower objects.
  51. constexpr char kCellId[] = "cellId";
  52. constexpr char kLocationAreaCode[] = "locationAreaCode";
  53. constexpr char kMobileCountryCode[] = "mobileCountryCode";
  54. constexpr char kMobileNetworkCode[] = "mobileNetworkCode";
  55. // Geolocation response field keys:
  56. constexpr char kLocationString[] = "location";
  57. constexpr char kLatString[] = "lat";
  58. constexpr char kLngString[] = "lng";
  59. constexpr char kAccuracyString[] = "accuracy";
  60. // Error object and its contents.
  61. constexpr char kErrorString[] = "error";
  62. // "errors" array in "error" object is ignored.
  63. constexpr char kCodeString[] = "code";
  64. constexpr char kMessageString[] = "message";
  65. // We are using "sparse" histograms for the number of retry attempts,
  66. // so we need to explicitly limit maximum value (in case something goes wrong).
  67. const size_t kMaxRetriesValueInHistograms = 20;
  68. // Sleep between geolocation request retry on HTTP error.
  69. const unsigned int kResolveGeolocationRetrySleepOnServerErrorSeconds = 5;
  70. // Sleep between geolocation request retry on bad server response.
  71. const unsigned int kResolveGeolocationRetrySleepBadResponseSeconds = 10;
  72. enum SimpleGeolocationRequestEvent {
  73. // NOTE: Do not renumber these as that would confuse interpretation of
  74. // previously logged data. When making changes, also update the enum list
  75. // in tools/metrics/histograms/histograms.xml to keep it in sync.
  76. SIMPLE_GEOLOCATION_REQUEST_EVENT_REQUEST_START = 0,
  77. SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_SUCCESS = 1,
  78. SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_NOT_OK = 2,
  79. SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_EMPTY = 3,
  80. SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_MALFORMED = 4,
  81. // NOTE: Add entries only immediately above this line.
  82. SIMPLE_GEOLOCATION_REQUEST_EVENT_COUNT = 5
  83. };
  84. enum SimpleGeolocationRequestResult {
  85. // NOTE: Do not renumber these as that would confuse interpretation of
  86. // previously logged data. When making changes, also update the enum list
  87. // in tools/metrics/histograms/histograms.xml to keep it in sync.
  88. SIMPLE_GEOLOCATION_REQUEST_RESULT_SUCCESS = 0,
  89. SIMPLE_GEOLOCATION_REQUEST_RESULT_FAILURE = 1,
  90. SIMPLE_GEOLOCATION_REQUEST_RESULT_SERVER_ERROR = 2,
  91. SIMPLE_GEOLOCATION_REQUEST_RESULT_CANCELLED = 3,
  92. // NOTE: Add entries only immediately above this line.
  93. SIMPLE_GEOLOCATION_REQUEST_RESULT_COUNT = 4
  94. };
  95. SimpleGeolocationRequestTestMonitor* g_test_request_hook = nullptr;
  96. // Too many requests (more than 1) mean there is a problem in implementation.
  97. void RecordUmaEvent(SimpleGeolocationRequestEvent event) {
  98. UMA_HISTOGRAM_ENUMERATION("SimpleGeolocation.Request.Event",
  99. event,
  100. SIMPLE_GEOLOCATION_REQUEST_EVENT_COUNT);
  101. }
  102. void RecordUmaResponseCode(int code) {
  103. base::UmaHistogramSparse("SimpleGeolocation.Request.ResponseCode", code);
  104. }
  105. // Slow geolocation resolve leads to bad user experience.
  106. void RecordUmaResponseTime(base::TimeDelta elapsed, bool success) {
  107. if (success) {
  108. UMA_HISTOGRAM_LOCATION_RESPONSE_TIMES(
  109. "SimpleGeolocation.Request.ResponseSuccessTime", elapsed);
  110. } else {
  111. UMA_HISTOGRAM_LOCATION_RESPONSE_TIMES(
  112. "SimpleGeolocation.Request.ResponseFailureTime", elapsed);
  113. }
  114. }
  115. void RecordUmaResult(SimpleGeolocationRequestResult result, size_t retries) {
  116. UMA_HISTOGRAM_ENUMERATION("SimpleGeolocation.Request.Result",
  117. result,
  118. SIMPLE_GEOLOCATION_REQUEST_RESULT_COUNT);
  119. base::UmaHistogramSparse("SimpleGeolocation.Request.Retries",
  120. std::min(retries, kMaxRetriesValueInHistograms));
  121. }
  122. // Creates the request url to send to the server.
  123. GURL GeolocationRequestURL(const GURL& url) {
  124. if (url != SimpleGeolocationProvider::DefaultGeolocationProviderURL())
  125. return url;
  126. std::string api_key = google_apis::GetAPIKey();
  127. if (api_key.empty())
  128. return url;
  129. std::string query(url.query());
  130. if (!query.empty())
  131. query += "&";
  132. query += "key=" + base::EscapeQueryParamValue(api_key, true);
  133. GURL::Replacements replacements;
  134. replacements.SetQueryStr(query);
  135. return url.ReplaceComponents(replacements);
  136. }
  137. void PrintGeolocationError(const GURL& server_url,
  138. const std::string& message,
  139. Geoposition* position) {
  140. position->status = Geoposition::STATUS_SERVER_ERROR;
  141. position->error_message = base::StringPrintf(
  142. "SimpleGeolocation provider at '%s' : %s.",
  143. server_url.DeprecatedGetOriginAsURL().spec().c_str(), message.c_str());
  144. VLOG(1) << "SimpleGeolocationRequest::GetGeolocationFromResponse() : "
  145. << position->error_message;
  146. }
  147. // Parses the server response body. Returns true if parsing was successful.
  148. // Sets |*position| to the parsed Geolocation if a valid position was received,
  149. // otherwise leaves it unchanged.
  150. bool ParseServerResponse(const GURL& server_url,
  151. const std::string& response_body,
  152. Geoposition* position) {
  153. DCHECK(position);
  154. if (response_body.empty()) {
  155. PrintGeolocationError(
  156. server_url, "Server returned empty response", position);
  157. RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_EMPTY);
  158. return false;
  159. }
  160. VLOG(1) << "SimpleGeolocationRequest::ParseServerResponse() : "
  161. "Parsing response '" << response_body << "'";
  162. // Parse the response, ignoring comments.
  163. auto response_result =
  164. base::JSONReader::ReadAndReturnValueWithError(response_body);
  165. if (!response_result.has_value()) {
  166. PrintGeolocationError(
  167. server_url, "JSONReader failed: " + response_result.error().message,
  168. position);
  169. RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_MALFORMED);
  170. return false;
  171. }
  172. base::Value response_value = std::move(*response_result);
  173. if (!response_value.is_dict()) {
  174. PrintGeolocationError(
  175. server_url,
  176. "Unexpected response type : " +
  177. base::StringPrintf(
  178. "%u", static_cast<unsigned int>(response_value.type())),
  179. position);
  180. RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_MALFORMED);
  181. return false;
  182. }
  183. base::Value* error_object = response_value.FindDictKey(kErrorString);
  184. base::Value* location_object = response_value.FindDictKey(kLocationString);
  185. position->timestamp = base::Time::Now();
  186. if (error_object) {
  187. std::string* error_message = error_object->FindStringKey(kMessageString);
  188. if (!error_message) {
  189. position->error_message = "Server returned error without message.";
  190. } else {
  191. position->error_message = *error_message;
  192. }
  193. // Ignore result (code defaults to zero).
  194. position->error_code =
  195. error_object->FindIntKey(kCodeString).value_or(position->error_code);
  196. } else {
  197. position->error_message.erase();
  198. }
  199. if (location_object) {
  200. absl::optional<double> latitude =
  201. location_object->FindDoubleKey(kLatString);
  202. if (!latitude) {
  203. PrintGeolocationError(server_url, "Missing 'lat' attribute.", position);
  204. RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_MALFORMED);
  205. return false;
  206. }
  207. position->latitude = latitude.value();
  208. absl::optional<double> longitude =
  209. location_object->FindDoubleKey(kLngString);
  210. if (!longitude) {
  211. PrintGeolocationError(server_url, "Missing 'lon' attribute.", position);
  212. RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_MALFORMED);
  213. return false;
  214. }
  215. position->longitude = longitude.value();
  216. absl::optional<double> accuracy =
  217. response_value.FindDoubleKey(kAccuracyString);
  218. if (!accuracy) {
  219. PrintGeolocationError(
  220. server_url, "Missing 'accuracy' attribute.", position);
  221. RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_MALFORMED);
  222. return false;
  223. }
  224. position->accuracy = accuracy.value();
  225. }
  226. if (error_object) {
  227. position->status = Geoposition::STATUS_SERVER_ERROR;
  228. return false;
  229. }
  230. // Empty response is STATUS_OK but not Valid().
  231. position->status = Geoposition::STATUS_OK;
  232. return true;
  233. }
  234. // Attempts to extract a position from the response. Detects and indicates
  235. // various failure cases.
  236. bool GetGeolocationFromResponse(bool http_success,
  237. int status_code,
  238. const std::string& response_body,
  239. const GURL& server_url,
  240. Geoposition* position) {
  241. VLOG(1) << "GetGeolocationFromResponse(http_success=" << http_success
  242. << ", status_code=" << status_code << "): response_body:\n"
  243. << response_body;
  244. // HttpPost can fail for a number of reasons. Most likely this is because
  245. // we're offline, or there was no response.
  246. if (!http_success) {
  247. PrintGeolocationError(server_url, "No response received", position);
  248. RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_EMPTY);
  249. return false;
  250. }
  251. if (status_code != net::HTTP_OK) {
  252. std::string message = "Returned error code ";
  253. message += base::NumberToString(status_code);
  254. PrintGeolocationError(server_url, message, position);
  255. RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_RESPONSE_NOT_OK);
  256. return false;
  257. }
  258. return ParseServerResponse(server_url, response_body, position);
  259. }
  260. void ReportUmaHasWiFiAccessPoints(bool value) {
  261. UMA_HISTOGRAM_BOOLEAN("SimpleGeolocation.Request.HasWiFiAccessPoints", value);
  262. }
  263. void ReportUmaHasCellTowers(bool value) {
  264. UMA_HISTOGRAM_BOOLEAN("SimpleGeolocation.Request.HasCellTowers", value);
  265. }
  266. // Helpers to reformat data into dictionaries for conversion to request JSON
  267. base::Value::Dict CreateAccessPointDictionary(
  268. const WifiAccessPoint& access_point) {
  269. base::Value::Dict access_point_dictionary;
  270. access_point_dictionary.Set(kMacAddress, access_point.mac_address);
  271. access_point_dictionary.Set(kSignalStrength, access_point.signal_strength);
  272. if (!access_point.timestamp.is_null()) {
  273. access_point_dictionary.Set(
  274. kAge,
  275. base::NumberToString(
  276. (base::Time::Now() - access_point.timestamp).InMilliseconds()));
  277. }
  278. access_point_dictionary.Set(kChannel, access_point.channel);
  279. access_point_dictionary.Set(kSignalToNoiseRatio,
  280. access_point.signal_to_noise);
  281. return access_point_dictionary;
  282. }
  283. base::Value::Dict CreateCellTowerDictionary(const CellTower& cell_tower) {
  284. base::Value::Dict cell_tower_dictionary;
  285. cell_tower_dictionary.Set(kCellId, cell_tower.ci);
  286. cell_tower_dictionary.Set(kLocationAreaCode, cell_tower.lac);
  287. cell_tower_dictionary.Set(kMobileCountryCode, cell_tower.mcc);
  288. cell_tower_dictionary.Set(kMobileNetworkCode, cell_tower.mnc);
  289. if (!cell_tower.timestamp.is_null()) {
  290. cell_tower_dictionary.Set(
  291. kAge, base::NumberToString(
  292. (base::Time::Now() - cell_tower.timestamp).InMilliseconds()));
  293. }
  294. return cell_tower_dictionary;
  295. }
  296. } // namespace
  297. SimpleGeolocationRequest::SimpleGeolocationRequest(
  298. scoped_refptr<network::SharedURLLoaderFactory> factory,
  299. const GURL& service_url,
  300. base::TimeDelta timeout,
  301. std::unique_ptr<WifiAccessPointVector> wifi_data,
  302. std::unique_ptr<CellTowerVector> cell_tower_data)
  303. : shared_url_loader_factory_(std::move(factory)),
  304. service_url_(service_url),
  305. retry_sleep_on_server_error_(
  306. base::Seconds(kResolveGeolocationRetrySleepOnServerErrorSeconds)),
  307. retry_sleep_on_bad_response_(
  308. base::Seconds(kResolveGeolocationRetrySleepBadResponseSeconds)),
  309. timeout_(timeout),
  310. retries_(0),
  311. wifi_data_(wifi_data.release()),
  312. cell_tower_data_(cell_tower_data.release()) {}
  313. SimpleGeolocationRequest::~SimpleGeolocationRequest() {
  314. DCHECK(thread_checker_.CalledOnValidThread());
  315. // If callback is not empty, request is cancelled.
  316. if (callback_) {
  317. RecordUmaResponseTime(base::Time::Now() - request_started_at_, false);
  318. RecordUmaResult(SIMPLE_GEOLOCATION_REQUEST_RESULT_CANCELLED, retries_);
  319. }
  320. if (g_test_request_hook)
  321. g_test_request_hook->OnRequestCreated(this);
  322. }
  323. std::string SimpleGeolocationRequest::FormatRequestBody() const {
  324. if (!wifi_data_)
  325. ReportUmaHasWiFiAccessPoints(false);
  326. if (!cell_tower_data_)
  327. ReportUmaHasCellTowers(false);
  328. if (!cell_tower_data_ && !wifi_data_)
  329. return std::string(kSimpleGeolocationRequestBody);
  330. base::Value::Dict request;
  331. request.Set(kConsiderIp, true);
  332. if (wifi_data_) {
  333. base::Value::List wifi_access_points;
  334. for (const WifiAccessPoint& access_point : *wifi_data_) {
  335. wifi_access_points.Append(CreateAccessPointDictionary(access_point));
  336. }
  337. request.Set(kWifiAccessPoints, std::move(wifi_access_points));
  338. }
  339. if (cell_tower_data_) {
  340. base::Value::List cell_towers;
  341. for (const CellTower& cell_tower : *cell_tower_data_) {
  342. cell_towers.Append(CreateCellTowerDictionary(cell_tower));
  343. }
  344. request.Set(kCellTowers, std::move(cell_towers));
  345. }
  346. std::string result;
  347. if (!base::JSONWriter::Write(request, &result)) {
  348. // If there's no data for a network type, we will have already reported
  349. // false above
  350. if (wifi_data_)
  351. ReportUmaHasWiFiAccessPoints(false);
  352. if (cell_tower_data_)
  353. ReportUmaHasCellTowers(false);
  354. return std::string(kSimpleGeolocationRequestBody);
  355. }
  356. if (wifi_data_)
  357. ReportUmaHasWiFiAccessPoints(wifi_data_->size());
  358. if (cell_tower_data_)
  359. ReportUmaHasCellTowers(cell_tower_data_->size());
  360. return result;
  361. }
  362. void SimpleGeolocationRequest::StartRequest() {
  363. DCHECK(thread_checker_.CalledOnValidThread());
  364. RecordUmaEvent(SIMPLE_GEOLOCATION_REQUEST_EVENT_REQUEST_START);
  365. ++retries_;
  366. const std::string request_body = FormatRequestBody();
  367. VLOG(1) << "SimpleGeolocationRequest::StartRequest(): request body:\n"
  368. << request_body;
  369. auto request = std::make_unique<network::ResourceRequest>();
  370. request->url = request_url_;
  371. request->method = "POST";
  372. request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
  373. request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  374. simple_url_loader_ = network::SimpleURLLoader::Create(
  375. std::move(request), NO_TRAFFIC_ANNOTATION_YET);
  376. simple_url_loader_->AttachStringForUpload(request_body, "application/json");
  377. // Call test hook before asynchronous request actually starts.
  378. if (g_test_request_hook)
  379. g_test_request_hook->OnStart(this);
  380. simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
  381. shared_url_loader_factory_.get(),
  382. base::BindOnce(&SimpleGeolocationRequest::OnSimpleURLLoaderComplete,
  383. base::Unretained(this)));
  384. }
  385. void SimpleGeolocationRequest::MakeRequest(ResponseCallback callback) {
  386. callback_ = std::move(callback);
  387. request_url_ = GeolocationRequestURL(service_url_);
  388. timeout_timer_.Start(
  389. FROM_HERE, timeout_, this, &SimpleGeolocationRequest::OnTimeout);
  390. request_started_at_ = base::Time::Now();
  391. StartRequest();
  392. }
  393. // static
  394. void SimpleGeolocationRequest::SetTestMonitor(
  395. SimpleGeolocationRequestTestMonitor* monitor) {
  396. g_test_request_hook = monitor;
  397. }
  398. std::string SimpleGeolocationRequest::FormatRequestBodyForTesting() const {
  399. return FormatRequestBody();
  400. }
  401. void SimpleGeolocationRequest::Retry(bool server_error) {
  402. base::TimeDelta delay(server_error ? retry_sleep_on_server_error_
  403. : retry_sleep_on_bad_response_);
  404. request_scheduled_.Start(
  405. FROM_HERE, delay, this, &SimpleGeolocationRequest::StartRequest);
  406. }
  407. void SimpleGeolocationRequest::OnSimpleURLLoaderComplete(
  408. std::unique_ptr<std::string> response_body) {
  409. bool is_success = !!response_body;
  410. int response_code = -1;
  411. if (simple_url_loader_->ResponseInfo() &&
  412. simple_url_loader_->ResponseInfo()->headers) {
  413. response_code =
  414. simple_url_loader_->ResponseInfo()->headers->response_code();
  415. }
  416. RecordUmaResponseCode(response_code);
  417. const bool parse_success = GetGeolocationFromResponse(
  418. is_success, response_code, response_body ? *response_body : std::string(),
  419. simple_url_loader_->GetFinalURL(), &position_);
  420. // Note that SimpleURLLoader doesn't return a body for non-2xx
  421. // responses by default.
  422. const bool server_error =
  423. (!is_success && (response_code == -1 || response_code / 100 == 2)) ||
  424. (response_code >= 500 && response_code < 600);
  425. const bool success = parse_success && position_.Valid();
  426. simple_url_loader_.reset();
  427. DVLOG(1)
  428. << "SimpleGeolocationRequest::OnSimpleURLLoaderComplete(): position={"
  429. << position_.ToString() << "}";
  430. if (!success) {
  431. Retry(server_error);
  432. return;
  433. }
  434. const base::TimeDelta elapsed = base::Time::Now() - request_started_at_;
  435. RecordUmaResponseTime(elapsed, success);
  436. RecordUmaResult(SIMPLE_GEOLOCATION_REQUEST_RESULT_SUCCESS, retries_);
  437. ReplyAndDestroySelf(elapsed, server_error);
  438. // "this" is already destroyed here.
  439. }
  440. void SimpleGeolocationRequest::ReplyAndDestroySelf(
  441. const base::TimeDelta elapsed,
  442. bool server_error) {
  443. simple_url_loader_.reset();
  444. timeout_timer_.Stop();
  445. request_scheduled_.Stop();
  446. ResponseCallback callback = std::move(callback_);
  447. // Empty callback is used to identify "completed or not yet started request".
  448. callback_.Reset();
  449. // callback.Run() usually destroys SimpleGeolocationRequest, because this is
  450. // the way callback is implemented in GeolocationProvider.
  451. std::move(callback).Run(position_, server_error, elapsed);
  452. // "this" is already destroyed here.
  453. }
  454. void SimpleGeolocationRequest::OnTimeout() {
  455. const SimpleGeolocationRequestResult result =
  456. (position_.status == Geoposition::STATUS_SERVER_ERROR
  457. ? SIMPLE_GEOLOCATION_REQUEST_RESULT_SERVER_ERROR
  458. : SIMPLE_GEOLOCATION_REQUEST_RESULT_FAILURE);
  459. RecordUmaResult(result, retries_);
  460. position_.status = Geoposition::STATUS_TIMEOUT;
  461. const base::TimeDelta elapsed = base::Time::Now() - request_started_at_;
  462. ReplyAndDestroySelf(elapsed, true /* server_error */);
  463. // "this" is already destroyed here.
  464. }
  465. } // namespace ash