Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(158)

Side by Side Diff: device/geolocation/network_location_request.cc

Issue 2192683003: Revert of Reland: Geolocation: move from content/browser to device/ (patchset #2 id:20001 of https:… (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@2810
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « device/geolocation/network_location_request.h ('k') | device/geolocation/wifi_data.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 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
5 #include "device/geolocation/network_location_request.h"
6
7 #include <stdint.h>
8
9 #include <limits>
10 #include <set>
11 #include <string>
12
13 #include "base/json/json_reader.h"
14 #include "base/json/json_writer.h"
15 #include "base/metrics/histogram.h"
16 #include "base/metrics/sparse_histogram.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/values.h"
20 #include "device/geolocation/geoposition.h"
21 #include "device/geolocation/location_arbitrator_impl.h"
22 #include "google_apis/google_api_keys.h"
23 #include "net/base/escape.h"
24 #include "net/base/load_flags.h"
25 #include "net/url_request/url_fetcher.h"
26 #include "net/url_request/url_request_context_getter.h"
27 #include "net/url_request/url_request_status.h"
28
29 namespace device {
30 namespace {
31
32 const char kAccessTokenString[] = "accessToken";
33 const char kLocationString[] = "location";
34 const char kLatitudeString[] = "lat";
35 const char kLongitudeString[] = "lng";
36 const char kAccuracyString[] = "accuracy";
37
38 enum NetworkLocationRequestEvent {
39 // NOTE: Do not renumber these as that would confuse interpretation of
40 // previously logged data. When making changes, also update the enum list
41 // in tools/metrics/histograms/histograms.xml to keep it in sync.
42 NETWORK_LOCATION_REQUEST_EVENT_REQUEST_START = 0,
43 NETWORK_LOCATION_REQUEST_EVENT_REQUEST_CANCEL = 1,
44 NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_SUCCESS = 2,
45 NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_NOT_OK = 3,
46 NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_EMPTY = 4,
47 NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_MALFORMED = 5,
48 NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_INVALID_FIX = 6,
49
50 // NOTE: Add entries only immediately above this line.
51 NETWORK_LOCATION_REQUEST_EVENT_COUNT = 7
52 };
53
54 void RecordUmaEvent(NetworkLocationRequestEvent event) {
55 UMA_HISTOGRAM_ENUMERATION("Geolocation.NetworkLocationRequest.Event",
56 event, NETWORK_LOCATION_REQUEST_EVENT_COUNT);
57 }
58
59 void RecordUmaResponseCode(int code) {
60 UMA_HISTOGRAM_SPARSE_SLOWLY("Geolocation.NetworkLocationRequest.ResponseCode",
61 code);
62 }
63
64 void RecordUmaAccessPoints(int count) {
65 const int min = 0;
66 const int max = 20;
67 const int buckets = 21;
68 UMA_HISTOGRAM_CUSTOM_COUNTS("Geolocation.NetworkLocationRequest.AccessPoints",
69 count, min, max, buckets);
70 }
71
72 // Local functions
73 // Creates the request url to send to the server.
74 GURL FormRequestURL(const GURL& url);
75
76 void FormUploadData(const WifiData& wifi_data,
77 const base::Time& wifi_timestamp,
78 const base::string16& access_token,
79 std::string* upload_data);
80
81 // Attempts to extract a position from the response. Detects and indicates
82 // various failure cases.
83 void GetLocationFromResponse(bool http_post_result,
84 int status_code,
85 const std::string& response_body,
86 const base::Time& wifi_timestamp,
87 const GURL& server_url,
88 Geoposition* position,
89 base::string16* access_token);
90
91 // Parses the server response body. Returns true if parsing was successful.
92 // Sets |*position| to the parsed location if a valid fix was received,
93 // otherwise leaves it unchanged.
94 bool ParseServerResponse(const std::string& response_body,
95 const base::Time& wifi_timestamp,
96 Geoposition* position,
97 base::string16* access_token);
98 void AddWifiData(const WifiData& wifi_data,
99 int age_milliseconds,
100 base::DictionaryValue* request);
101 } // namespace
102
103 int NetworkLocationRequest::url_fetcher_id_for_tests = 0;
104
105 NetworkLocationRequest::NetworkLocationRequest(
106 const scoped_refptr<net::URLRequestContextGetter>& context,
107 const GURL& url,
108 LocationResponseCallback callback)
109 : url_context_(context), location_response_callback_(callback), url_(url) {
110 }
111
112 NetworkLocationRequest::~NetworkLocationRequest() {
113 }
114
115 bool NetworkLocationRequest::MakeRequest(const base::string16& access_token,
116 const WifiData& wifi_data,
117 const base::Time& wifi_timestamp) {
118 RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_REQUEST_START);
119 RecordUmaAccessPoints(wifi_data.access_point_data.size());
120 if (url_fetcher_ != NULL) {
121 DVLOG(1) << "NetworkLocationRequest : Cancelling pending request";
122 RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_REQUEST_CANCEL);
123 url_fetcher_.reset();
124 }
125 wifi_data_ = wifi_data;
126 wifi_timestamp_ = wifi_timestamp;
127
128 GURL request_url = FormRequestURL(url_);
129 url_fetcher_ = net::URLFetcher::Create(url_fetcher_id_for_tests, request_url,
130 net::URLFetcher::POST, this);
131 url_fetcher_->SetRequestContext(url_context_.get());
132 std::string upload_data;
133 FormUploadData(wifi_data, wifi_timestamp, access_token, &upload_data);
134 url_fetcher_->SetUploadData("application/json", upload_data);
135 url_fetcher_->SetLoadFlags(
136 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
137 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES |
138 net::LOAD_DO_NOT_SEND_AUTH_DATA);
139
140 request_start_time_ = base::TimeTicks::Now();
141 url_fetcher_->Start();
142 return true;
143 }
144
145 void NetworkLocationRequest::OnURLFetchComplete(
146 const net::URLFetcher* source) {
147 DCHECK_EQ(url_fetcher_.get(), source);
148
149 net::URLRequestStatus status = source->GetStatus();
150 int response_code = source->GetResponseCode();
151 RecordUmaResponseCode(response_code);
152
153 Geoposition position;
154 base::string16 access_token;
155 std::string data;
156 source->GetResponseAsString(&data);
157 GetLocationFromResponse(status.is_success(), response_code, data,
158 wifi_timestamp_, source->GetURL(), &position,
159 &access_token);
160 const bool server_error =
161 !status.is_success() || (response_code >= 500 && response_code < 600);
162 url_fetcher_.reset();
163
164 if (!server_error) {
165 const base::TimeDelta request_time =
166 base::TimeTicks::Now() - request_start_time_;
167
168 UMA_HISTOGRAM_CUSTOM_TIMES(
169 "Net.Wifi.LbsLatency",
170 request_time,
171 base::TimeDelta::FromMilliseconds(1),
172 base::TimeDelta::FromSeconds(10),
173 100);
174 }
175
176 DVLOG(1) << "NetworkLocationRequest::OnURLFetchComplete() : run callback.";
177 location_response_callback_.Run(
178 position, server_error, access_token, wifi_data_);
179 }
180
181 // Local functions.
182 namespace {
183
184 struct AccessPointLess {
185 bool operator()(const AccessPointData* ap1,
186 const AccessPointData* ap2) const {
187 return ap2->radio_signal_strength < ap1->radio_signal_strength;
188 }
189 };
190
191 GURL FormRequestURL(const GURL& url) {
192 if (url == LocationArbitratorImpl::DefaultNetworkProviderURL()) {
193 std::string api_key = google_apis::GetAPIKey();
194 if (!api_key.empty()) {
195 std::string query(url.query());
196 if (!query.empty())
197 query += "&";
198 query += "key=" + net::EscapeQueryParamValue(api_key, true);
199 GURL::Replacements replacements;
200 replacements.SetQueryStr(query);
201 return url.ReplaceComponents(replacements);
202 }
203 }
204 return url;
205 }
206
207 void FormUploadData(const WifiData& wifi_data,
208 const base::Time& wifi_timestamp,
209 const base::string16& access_token,
210 std::string* upload_data) {
211 int age = std::numeric_limits<int32_t>::min(); // Invalid so AddInteger()
212 // will ignore.
213 if (!wifi_timestamp.is_null()) {
214 // Convert absolute timestamps into a relative age.
215 int64_t delta_ms = (base::Time::Now() - wifi_timestamp).InMilliseconds();
216 if (delta_ms >= 0 && delta_ms < std::numeric_limits<int32_t>::max())
217 age = static_cast<int>(delta_ms);
218 }
219
220 base::DictionaryValue request;
221 AddWifiData(wifi_data, age, &request);
222 if (!access_token.empty())
223 request.SetString(kAccessTokenString, access_token);
224 base::JSONWriter::Write(request, upload_data);
225 }
226
227 void AddString(const std::string& property_name, const std::string& value,
228 base::DictionaryValue* dict) {
229 DCHECK(dict);
230 if (!value.empty())
231 dict->SetString(property_name, value);
232 }
233
234 void AddInteger(const std::string& property_name, int value,
235 base::DictionaryValue* dict) {
236 DCHECK(dict);
237 if (value != std::numeric_limits<int32_t>::min())
238 dict->SetInteger(property_name, value);
239 }
240
241 void AddWifiData(const WifiData& wifi_data,
242 int age_milliseconds,
243 base::DictionaryValue* request) {
244 DCHECK(request);
245
246 if (wifi_data.access_point_data.empty())
247 return;
248
249 typedef std::multiset<const AccessPointData*, AccessPointLess> AccessPointSet;
250 AccessPointSet access_points_by_signal_strength;
251
252 for (const auto& ap_data : wifi_data.access_point_data)
253 access_points_by_signal_strength.insert(&ap_data);
254
255 base::ListValue* wifi_access_point_list = new base::ListValue();
256 for (auto* ap_data : access_points_by_signal_strength) {
257 base::DictionaryValue* wifi_dict = new base::DictionaryValue();
258 AddString("macAddress", base::UTF16ToUTF8(ap_data->mac_address), wifi_dict);
259 AddInteger("signalStrength", ap_data->radio_signal_strength, wifi_dict);
260 AddInteger("age", age_milliseconds, wifi_dict);
261 AddInteger("channel", ap_data->channel, wifi_dict);
262 AddInteger("signalToNoiseRatio", ap_data->signal_to_noise, wifi_dict);
263 wifi_access_point_list->Append(wifi_dict);
264 }
265 request->Set("wifiAccessPoints", wifi_access_point_list);
266 }
267
268 void FormatPositionError(const GURL& server_url,
269 const std::string& message,
270 Geoposition* position) {
271 position->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE;
272 position->error_message = "Network location provider at '";
273 position->error_message += server_url.GetOrigin().spec();
274 position->error_message += "' : ";
275 position->error_message += message;
276 position->error_message += ".";
277 VLOG(1) << "NetworkLocationRequest::GetLocationFromResponse() : "
278 << position->error_message;
279 }
280
281 void GetLocationFromResponse(bool http_post_result,
282 int status_code,
283 const std::string& response_body,
284 const base::Time& wifi_timestamp,
285 const GURL& server_url,
286 Geoposition* position,
287 base::string16* access_token) {
288 DCHECK(position);
289 DCHECK(access_token);
290
291 // HttpPost can fail for a number of reasons. Most likely this is because
292 // we're offline, or there was no response.
293 if (!http_post_result) {
294 FormatPositionError(server_url, "No response received", position);
295 RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_EMPTY);
296 return;
297 }
298 if (status_code != 200) { // HTTP OK.
299 std::string message = "Returned error code ";
300 message += base::IntToString(status_code);
301 FormatPositionError(server_url, message, position);
302 RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_NOT_OK);
303 return;
304 }
305 // We use the timestamp from the wifi data that was used to generate
306 // this position fix.
307 if (!ParseServerResponse(response_body, wifi_timestamp, position,
308 access_token)) {
309 // We failed to parse the repsonse.
310 FormatPositionError(server_url, "Response was malformed", position);
311 RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_MALFORMED);
312 return;
313 }
314 // The response was successfully parsed, but it may not be a valid
315 // position fix.
316 if (!position->Validate()) {
317 FormatPositionError(server_url,
318 "Did not provide a good position fix", position);
319 RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_INVALID_FIX);
320 return;
321 }
322 RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_SUCCESS);
323 }
324
325 // Numeric values without a decimal point have type integer and IsDouble() will
326 // return false. This is convenience function for detecting integer or floating
327 // point numeric values. Note that isIntegral() includes boolean values, which
328 // is not what we want.
329 bool GetAsDouble(const base::DictionaryValue& object,
330 const std::string& property_name,
331 double* out) {
332 DCHECK(out);
333 const base::Value* value = NULL;
334 if (!object.Get(property_name, &value))
335 return false;
336 int value_as_int;
337 DCHECK(value);
338 if (value->GetAsInteger(&value_as_int)) {
339 *out = value_as_int;
340 return true;
341 }
342 return value->GetAsDouble(out);
343 }
344
345 bool ParseServerResponse(const std::string& response_body,
346 const base::Time& wifi_timestamp,
347 Geoposition* position,
348 base::string16* access_token) {
349 DCHECK(position);
350 DCHECK(!position->Validate());
351 DCHECK(position->error_code == Geoposition::ERROR_CODE_NONE);
352 DCHECK(access_token);
353 DCHECK(!wifi_timestamp.is_null());
354
355 if (response_body.empty()) {
356 LOG(WARNING) << "ParseServerResponse() : Response was empty.";
357 return false;
358 }
359 DVLOG(1) << "ParseServerResponse() : Parsing response " << response_body;
360
361 // Parse the response, ignoring comments.
362 std::string error_msg;
363 std::unique_ptr<base::Value> response_value =
364 base::JSONReader::ReadAndReturnError(response_body, base::JSON_PARSE_RFC,
365 NULL, &error_msg);
366 if (response_value == NULL) {
367 LOG(WARNING) << "ParseServerResponse() : JSONReader failed : "
368 << error_msg;
369 return false;
370 }
371
372 if (!response_value->IsType(base::Value::TYPE_DICTIONARY)) {
373 VLOG(1) << "ParseServerResponse() : Unexpected response type "
374 << response_value->GetType();
375 return false;
376 }
377 const base::DictionaryValue* response_object =
378 static_cast<base::DictionaryValue*>(response_value.get());
379
380 // Get the access token, if any.
381 response_object->GetString(kAccessTokenString, access_token);
382
383 // Get the location
384 const base::Value* location_value = NULL;
385 if (!response_object->Get(kLocationString, &location_value)) {
386 VLOG(1) << "ParseServerResponse() : Missing location attribute.";
387 // GLS returns a response with no location property to represent
388 // no fix available; return true to indicate successful parse.
389 return true;
390 }
391 DCHECK(location_value);
392
393 if (!location_value->IsType(base::Value::TYPE_DICTIONARY)) {
394 if (!location_value->IsType(base::Value::TYPE_NULL)) {
395 VLOG(1) << "ParseServerResponse() : Unexpected location type "
396 << location_value->GetType();
397 // If the network provider was unable to provide a position fix, it should
398 // return a HTTP 200, with "location" : null. Otherwise it's an error.
399 return false;
400 }
401 return true; // Successfully parsed response containing no fix.
402 }
403 const base::DictionaryValue* location_object =
404 static_cast<const base::DictionaryValue*>(location_value);
405
406 // latitude and longitude fields are always required.
407 double latitude = 0;
408 double longitude = 0;
409 if (!GetAsDouble(*location_object, kLatitudeString, &latitude) ||
410 !GetAsDouble(*location_object, kLongitudeString, &longitude)) {
411 VLOG(1) << "ParseServerResponse() : location lacks lat and/or long.";
412 return false;
413 }
414 // All error paths covered: now start actually modifying postion.
415 position->latitude = latitude;
416 position->longitude = longitude;
417 position->timestamp = wifi_timestamp;
418
419 // Other fields are optional.
420 GetAsDouble(*response_object, kAccuracyString, &position->accuracy);
421
422 return true;
423 }
424
425 } // namespace
426
427 } // namespace device
OLDNEW
« no previous file with comments | « device/geolocation/network_location_request.h ('k') | device/geolocation/wifi_data.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698