1effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Copyright 2014 The Chromium Authors. All rights reserved. 2effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// found in the LICENSE file. 4effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 5effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "chrome/browser/chromeos/timezone/timezone_request.h" 6effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 7effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include <string> 8effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 9effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/json/json_reader.h" 10effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/metrics/histogram.h" 11effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/metrics/sparse_histogram.h" 12effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/strings/string_number_conversions.h" 13effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/strings/stringprintf.h" 14effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/time/time.h" 15effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "base/values.h" 160529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch#include "chrome/browser/chromeos/geolocation/geoposition.h" 17effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "google_apis/google_api_keys.h" 18effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "net/base/escape.h" 19effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "net/base/load_flags.h" 20effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "net/http/http_status_code.h" 21effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "net/url_request/url_fetcher.h" 22effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "net/url_request/url_request_context_getter.h" 23effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "net/url_request/url_request_status.h" 24effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 25effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochnamespace chromeos { 26effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 27effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochnamespace { 28effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 29effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char kDefaultTimezoneProviderUrl[] = 30effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "https://maps.googleapis.com/maps/api/timezone/json?"; 31effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 32effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char kKeyString[] = "key"; 33effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Language parameter is unsupported for now. 34effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// const char kLanguageString[] = "language"; 35effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char kLocationString[] = "location"; 36effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char kSensorString[] = "sensor"; 37effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char kTimestampString[] = "timestamp"; 38effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 39effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char kDstOffsetString[] = "dstOffset"; 40effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char kRawOffsetString[] = "rawOffset"; 41effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char kTimeZoneIdString[] = "timeZoneId"; 42effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char kTimeZoneNameString[] = "timeZoneName"; 43effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char kStatusString[] = "status"; 44effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst char kErrorMessageString[] = "error_message"; 45effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 46effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Sleep between timezone request retry on HTTP error. 47effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst unsigned int kResolveTimeZoneRetrySleepOnServerErrorSeconds = 5; 48effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 49effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Sleep between timezone request retry on bad server response. 50effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst unsigned int kResolveTimeZoneRetrySleepBadResponseSeconds = 10; 51effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 52effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochstruct StatusString2Enum { 53effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch const char* string; 54effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TimeZoneResponseData::Status value; 55effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}; 56effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 57effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst StatusString2Enum statusString2Enum[] = { 58effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch {"OK", TimeZoneResponseData::OK}, 59effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch {"INVALID_REQUEST", TimeZoneResponseData::INVALID_REQUEST}, 60effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch {"OVER_QUERY_LIMIT", TimeZoneResponseData::OVER_QUERY_LIMIT}, 61effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch {"REQUEST_DENIED", TimeZoneResponseData::REQUEST_DENIED}, 62effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch {"UNKNOWN_ERROR", TimeZoneResponseData::UNKNOWN_ERROR}, 63effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch {"ZERO_RESULTS", TimeZoneResponseData::ZERO_RESULTS}, }; 64effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 65effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochenum TimeZoneRequestEvent { 66effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // NOTE: Do not renumber these as that would confuse interpretation of 67effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // previously logged data. When making changes, also update the enum list 68effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // in tools/metrics/histograms/histograms.xml to keep it in sync. 69effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TIMEZONE_REQUEST_EVENT_REQUEST_START = 0, 70effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TIMEZONE_REQUEST_EVENT_RESPONSE_SUCCESS = 1, 71effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TIMEZONE_REQUEST_EVENT_RESPONSE_NOT_OK = 2, 72effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TIMEZONE_REQUEST_EVENT_RESPONSE_EMPTY = 3, 73effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TIMEZONE_REQUEST_EVENT_RESPONSE_MALFORMED = 4, 74effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 75effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // NOTE: Add entries only immediately above this line. 76effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TIMEZONE_REQUEST_EVENT_COUNT = 5 77effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}; 78effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 79effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochenum TimeZoneRequestResult { 80effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // NOTE: Do not renumber these as that would confuse interpretation of 81effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // previously logged data. When making changes, also update the enum list 82effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // in tools/metrics/histograms/histograms.xml to keep it in sync. 83effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TIMEZONE_REQUEST_RESULT_SUCCESS = 0, 84effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TIMEZONE_REQUEST_RESULT_FAILURE = 1, 85effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TIMEZONE_REQUEST_RESULT_SERVER_ERROR = 2, 86effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TIMEZONE_REQUEST_RESULT_CANCELLED = 3, 87effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 88effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // NOTE: Add entries only immediately above this line. 89effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TIMEZONE_REQUEST_RESULT_COUNT = 4 90effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}; 91effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 92effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Too many requests (more than 1) mean there is a problem in implementation. 93effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid RecordUmaEvent(TimeZoneRequestEvent event) { 94effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch UMA_HISTOGRAM_ENUMERATION( 95effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "TimeZone.TimeZoneRequest.Event", event, TIMEZONE_REQUEST_EVENT_COUNT); 96effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 97effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 98effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid RecordUmaResponseCode(int code) { 99effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch UMA_HISTOGRAM_SPARSE_SLOWLY("TimeZone.TimeZoneRequest.ResponseCode", code); 100effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 101effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 102effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Slow timezone resolve leads to bad user experience. 103effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid RecordUmaResponseTime(base::TimeDelta elapsed, bool success) { 104effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (success) { 105effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch UMA_HISTOGRAM_TIMES("TimeZone.TimeZoneRequest.ResponseSuccessTime", 106effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch elapsed); 107effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } else { 108effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch UMA_HISTOGRAM_TIMES("TimeZone.TimeZoneRequest.ResponseFailureTime", 109effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch elapsed); 110effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 111effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 112effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 113effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid RecordUmaResult(TimeZoneRequestResult result, unsigned retries) { 114effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch UMA_HISTOGRAM_ENUMERATION( 115effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "TimeZone.TimeZoneRequest.Result", result, TIMEZONE_REQUEST_RESULT_COUNT); 116effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch UMA_HISTOGRAM_SPARSE_SLOWLY("TimeZone.TimeZoneRequest.Retries", retries); 117effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 118effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 119effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Creates the request url to send to the server. 120effb81e5f8246d0db0270817048dc992db66e9fbBen MurdochGURL TimeZoneRequestURL(const GURL& url, 1210529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch const Geoposition& geoposition, 122effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch bool sensor) { 123effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch std::string query(url.query()); 124effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch query += base::StringPrintf( 125effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "%s=%f,%f", kLocationString, geoposition.latitude, geoposition.longitude); 126effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (url == DefaultTimezoneProviderURL()) { 127effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch std::string api_key = google_apis::GetAPIKey(); 128effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!api_key.empty()) { 129effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch query += "&"; 130effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch query += kKeyString; 131effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch query += "="; 132effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch query += net::EscapeQueryParamValue(api_key, true); 133effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 134effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 135effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!geoposition.timestamp.is_null()) { 136effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch query += base::StringPrintf( 137effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "&%s=%ld", kTimestampString, geoposition.timestamp.ToTimeT()); 138effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 139effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch query += "&"; 140effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch query += kSensorString; 141effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch query += "="; 142effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch query += (sensor ? "true" : "false"); 143effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 144effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch GURL::Replacements replacements; 145effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch replacements.SetQueryStr(query); 146effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return url.ReplaceComponents(replacements); 147effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 148effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 149effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid PrintTimeZoneError(const GURL& server_url, 150effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch const std::string& message, 151effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TimeZoneResponseData* timezone) { 152effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch timezone->status = TimeZoneResponseData::REQUEST_ERROR; 153effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch timezone->error_message = 154effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch base::StringPrintf("TimeZone provider at '%s' : %s.", 155effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch server_url.GetOrigin().spec().c_str(), 156effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch message.c_str()); 157effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch LOG(WARNING) << "TimeZoneRequest::GetTimeZoneFromResponse() : " 158effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch << timezone->error_message; 159effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 160effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 161effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Parses the server response body. Returns true if parsing was successful. 162effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Sets |*timezone| to the parsed TimeZone if a valid timezone was received, 163effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// otherwise leaves it unchanged. 164effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochbool ParseServerResponse(const GURL& server_url, 165effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch const std::string& response_body, 166effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TimeZoneResponseData* timezone) { 167effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch DCHECK(timezone); 168effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 169effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (response_body.empty()) { 170effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch PrintTimeZoneError(server_url, "Server returned empty response", timezone); 171effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaEvent(TIMEZONE_REQUEST_EVENT_RESPONSE_EMPTY); 172effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return false; 173effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 174effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch VLOG(1) << "TimeZoneRequest::ParseServerResponse() : Parsing response " 175effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch << response_body; 176effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 177effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // Parse the response, ignoring comments. 178effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch std::string error_msg; 179effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch scoped_ptr<base::Value> response_value(base::JSONReader::ReadAndReturnError( 180effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch response_body, base::JSON_PARSE_RFC, NULL, &error_msg)); 181effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (response_value == NULL) { 182effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch PrintTimeZoneError(server_url, "JSONReader failed: " + error_msg, timezone); 183effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaEvent(TIMEZONE_REQUEST_EVENT_RESPONSE_MALFORMED); 184effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return false; 185effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 186effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 187effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch const base::DictionaryValue* response_object = NULL; 188effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!response_value->GetAsDictionary(&response_object)) { 189effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch PrintTimeZoneError(server_url, 190effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "Unexpected response type : " + 191effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch base::StringPrintf("%u", response_value->GetType()), 192effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch timezone); 193effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaEvent(TIMEZONE_REQUEST_EVENT_RESPONSE_MALFORMED); 194effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return false; 195effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 196effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 197effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch std::string status; 198effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 199effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!response_object->GetStringWithoutPathExpansion(kStatusString, &status)) { 200effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch PrintTimeZoneError(server_url, "Missing status attribute.", timezone); 201effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaEvent(TIMEZONE_REQUEST_EVENT_RESPONSE_MALFORMED); 202effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return false; 203effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 204effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 205effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch bool found = false; 206effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch for (size_t i = 0; i < arraysize(statusString2Enum); ++i) { 207effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (status != statusString2Enum[i].string) 208effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch continue; 209effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 210effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch timezone->status = statusString2Enum[i].value; 211effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch found = true; 212effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch break; 213effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 214effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 215effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!found) { 216effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch PrintTimeZoneError( 217effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch server_url, "Bad status attribute value: '" + status + "'", timezone); 218effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaEvent(TIMEZONE_REQUEST_EVENT_RESPONSE_MALFORMED); 219effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return false; 220effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 221effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 222effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch const bool status_ok = (timezone->status == TimeZoneResponseData::OK); 223effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 224effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!response_object->GetDoubleWithoutPathExpansion(kDstOffsetString, 225effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch &timezone->dstOffset) && 226effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch status_ok) { 227effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch PrintTimeZoneError(server_url, "Missing dstOffset attribute.", timezone); 228effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaEvent(TIMEZONE_REQUEST_EVENT_RESPONSE_MALFORMED); 229effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return false; 230effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 231effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 232effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!response_object->GetDoubleWithoutPathExpansion(kRawOffsetString, 233effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch &timezone->rawOffset) && 234effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch status_ok) { 235effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch PrintTimeZoneError(server_url, "Missing rawOffset attribute.", timezone); 236effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaEvent(TIMEZONE_REQUEST_EVENT_RESPONSE_MALFORMED); 237effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return false; 238effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 239effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 240effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!response_object->GetStringWithoutPathExpansion(kTimeZoneIdString, 241effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch &timezone->timeZoneId) && 242effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch status_ok) { 243effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch PrintTimeZoneError(server_url, "Missing timeZoneId attribute.", timezone); 244effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaEvent(TIMEZONE_REQUEST_EVENT_RESPONSE_MALFORMED); 245effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return false; 246effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 247effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 248effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!response_object->GetStringWithoutPathExpansion( 249effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch kTimeZoneNameString, &timezone->timeZoneName) && 250effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch status_ok) { 251effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch PrintTimeZoneError(server_url, "Missing timeZoneName attribute.", timezone); 252effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaEvent(TIMEZONE_REQUEST_EVENT_RESPONSE_MALFORMED); 253effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return false; 254effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 255effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 256effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // "error_message" field is optional. Ignore result. 257effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch response_object->GetStringWithoutPathExpansion(kErrorMessageString, 258effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch &timezone->error_message); 259effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 260effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return true; 261effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 262effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 263effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Attempts to extract a position from the response. Detects and indicates 264effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// various failure cases. 265effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochscoped_ptr<TimeZoneResponseData> GetTimeZoneFromResponse( 266effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch bool http_success, 267effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch int status_code, 268effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch const std::string& response_body, 269effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch const GURL& server_url) { 270effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch scoped_ptr<TimeZoneResponseData> timezone(new TimeZoneResponseData); 271effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 272effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // HttpPost can fail for a number of reasons. Most likely this is because 273effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // we're offline, or there was no response. 274effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!http_success) { 275effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch PrintTimeZoneError(server_url, "No response received", timezone.get()); 276effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaEvent(TIMEZONE_REQUEST_EVENT_RESPONSE_EMPTY); 277effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return timezone.Pass(); 278effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 279effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (status_code != net::HTTP_OK) { 280effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch std::string message = "Returned error code "; 281effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch message += base::IntToString(status_code); 282effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch PrintTimeZoneError(server_url, message, timezone.get()); 283effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaEvent(TIMEZONE_REQUEST_EVENT_RESPONSE_NOT_OK); 284effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return timezone.Pass(); 285effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 286effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 287effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!ParseServerResponse(server_url, response_body, timezone.get())) 288effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return timezone.Pass(); 289effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 290effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaEvent(TIMEZONE_REQUEST_EVENT_RESPONSE_SUCCESS); 291effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return timezone.Pass(); 292effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 293effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 294effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} // namespace 295effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 296effb81e5f8246d0db0270817048dc992db66e9fbBen MurdochTimeZoneResponseData::TimeZoneResponseData() 297effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch : dstOffset(0), rawOffset(0), status(ZERO_RESULTS) { 298effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 299effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 300effb81e5f8246d0db0270817048dc992db66e9fbBen MurdochGURL DefaultTimezoneProviderURL() { 301effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return GURL(kDefaultTimezoneProviderUrl); 302effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 303effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 304effb81e5f8246d0db0270817048dc992db66e9fbBen MurdochTimeZoneRequest::TimeZoneRequest( 305effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch net::URLRequestContextGetter* url_context_getter, 306effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch const GURL& service_url, 3070529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch const Geoposition& geoposition, 308effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch bool sensor, 309effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch base::TimeDelta retry_timeout) 310effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch : url_context_getter_(url_context_getter), 311effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch service_url_(service_url), 312effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch geoposition_(geoposition), 313effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch sensor_(sensor), 314effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch retry_timeout_abs_(base::Time::Now() + retry_timeout), 315cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) retry_sleep_on_server_error_(base::TimeDelta::FromSeconds( 316cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) kResolveTimeZoneRetrySleepOnServerErrorSeconds)), 317cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) retry_sleep_on_bad_response_(base::TimeDelta::FromSeconds( 318cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) kResolveTimeZoneRetrySleepBadResponseSeconds)), 319effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch retries_(0) { 320effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 321effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 322effb81e5f8246d0db0270817048dc992db66e9fbBen MurdochTimeZoneRequest::~TimeZoneRequest() { 323effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch DCHECK(thread_checker_.CalledOnValidThread()); 324effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 325effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // If callback is not empty, request is cancelled. 326effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!callback_.is_null()) { 327effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaResponseTime(base::Time::Now() - request_started_at_, false); 328effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaResult(TIMEZONE_REQUEST_RESULT_CANCELLED, retries_); 329effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 330effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 331effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 332effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid TimeZoneRequest::StartRequest() { 333effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch DCHECK(thread_checker_.CalledOnValidThread()); 334effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaEvent(TIMEZONE_REQUEST_EVENT_REQUEST_START); 335effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch request_started_at_ = base::Time::Now(); 336effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch ++retries_; 337effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 338effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch url_fetcher_.reset( 339effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch net::URLFetcher::Create(request_url_, net::URLFetcher::GET, this)); 340effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch url_fetcher_->SetRequestContext(url_context_getter_); 341effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch url_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | 342effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch net::LOAD_DISABLE_CACHE | 343effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch net::LOAD_DO_NOT_SAVE_COOKIES | 344effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch net::LOAD_DO_NOT_SEND_COOKIES | 345effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch net::LOAD_DO_NOT_SEND_AUTH_DATA); 346effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch url_fetcher_->Start(); 347effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 348effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 349effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid TimeZoneRequest::MakeRequest(TimeZoneResponseCallback callback) { 350effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch callback_ = callback; 351effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch request_url_ = 352effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TimeZoneRequestURL(service_url_, geoposition_, false /* sensor */); 353effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch StartRequest(); 354effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 355effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 356effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid TimeZoneRequest::Retry(bool server_error) { 357cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const base::TimeDelta delay(server_error ? retry_sleep_on_server_error_ 358cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) : retry_sleep_on_bad_response_); 359effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch timezone_request_scheduled_.Start( 360effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch FROM_HERE, delay, this, &TimeZoneRequest::StartRequest); 361effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 362effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 363effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid TimeZoneRequest::OnURLFetchComplete(const net::URLFetcher* source) { 364effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch DCHECK_EQ(url_fetcher_.get(), source); 365effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 366effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch net::URLRequestStatus status = source->GetStatus(); 367effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch int response_code = source->GetResponseCode(); 368effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaResponseCode(response_code); 369effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 370effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch std::string data; 371effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch source->GetResponseAsString(&data); 372effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch scoped_ptr<TimeZoneResponseData> timezone = GetTimeZoneFromResponse( 373effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch status.is_success(), response_code, data, source->GetURL()); 374effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch const bool server_error = 375effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch !status.is_success() || (response_code >= 500 && response_code < 600); 376effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch url_fetcher_.reset(); 377effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 378effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch DVLOG(1) << "TimeZoneRequest::OnURLFetchComplete(): timezone={" 379effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch << timezone->ToStringForDebug() << "}"; 380effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 381effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch const base::Time now = base::Time::Now(); 382effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch const bool retry_timeout = (now >= retry_timeout_abs_); 383effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 384effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch const bool success = (timezone->status == TimeZoneResponseData::OK); 385effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch if (!success && !retry_timeout) { 386effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch Retry(server_error); 387effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return; 388effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch } 389effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaResponseTime(base::Time::Now() - request_started_at_, success); 390effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 391effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch const TimeZoneRequestResult result = 392effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch (server_error ? TIMEZONE_REQUEST_RESULT_SERVER_ERROR 393effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch : (success ? TIMEZONE_REQUEST_RESULT_SUCCESS 394effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch : TIMEZONE_REQUEST_RESULT_FAILURE)); 395effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch RecordUmaResult(result, retries_); 396effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 397effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch TimeZoneResponseCallback callback = callback_; 398effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 399effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // Empty callback is used to identify "completed or not yet started request". 400effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch callback_.Reset(); 401effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 402effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // callback.Run() usually destroys TimeZoneRequest, because this is the way 403effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // callback is implemented in TimeZoneProvider. 404effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch callback.Run(timezone.Pass(), server_error); 405effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch // "this" is already destroyed here. 406effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} 407effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 408effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochstd::string TimeZoneResponseData::ToStringForDebug() const { 409effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch static const char* const status2string[] = { 410effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "OK", 411effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "INVALID_REQUEST", 412effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "OVER_QUERY_LIMIT", 413effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "REQUEST_DENIED", 414effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "UNKNOWN_ERROR", 415effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "ZERO_RESULTS", 416effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "REQUEST_ERROR" 417effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch }; 418effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 419effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch return base::StringPrintf( 420effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "dstOffset=%f, rawOffset=%f, timeZoneId='%s', timeZoneName='%s', " 421effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch "error_message='%s', status=%u (%s)", 422effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch dstOffset, 423effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch rawOffset, 424effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch timeZoneId.c_str(), 425effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch timeZoneName.c_str(), 426effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch error_message.c_str(), 427effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch (unsigned)status, 428effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch (status < arraysize(status2string) ? status2string[status] : "unknown")); 429cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 430effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch 431effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch} // namespace chromeos 432