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

Side by Side Diff: chrome/browser/geolocation/network_location_request.cc

Issue 6591034: Move core pieces of geolocation from chrome to content.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: fix Linux build Created 9 years, 9 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 "chrome/browser/geolocation/network_location_request.h"
6
7 #include "base/json/json_reader.h"
8 #include "base/json/json_writer.h"
9 #include "base/string_number_conversions.h"
10 #include "base/utf_string_conversions.h"
11 #include "base/values.h"
12 #include "chrome/common/geoposition.h"
13 #include "chrome/common/net/url_request_context_getter.h"
14 #include "net/base/load_flags.h"
15 #include "net/url_request/url_request_status.h"
16
17 namespace {
18 const char kMimeApplicationJson[] = "application/json";
19
20 // See http://code.google.com/apis/gears/geolocation_network_protocol.html
21 const char kGeoLocationNetworkProtocolVersion[] = "1.1.0";
22
23 const char kAccessTokenString[] = "access_token";
24 const char kLocationString[] = "location";
25 const char kLatitudeString[] = "latitude";
26 const char kLongitudeString[] = "longitude";
27 const char kAltitudeString[] = "altitude";
28 const char kAccuracyString[] = "accuracy";
29 const char kAltitudeAccuracyString[] = "altitude_accuracy";
30
31 // Local functions
32 // Creates the request payload to send to the server.
33 void FormRequestBody(const std::string& host_name,
34 const string16& access_token,
35 const GatewayData& gateway_data,
36 const RadioData& radio_data,
37 const WifiData& wifi_data,
38 const base::Time& timestamp,
39 std::string* data);
40 // Parsers the server response.
41 void GetLocationFromResponse(bool http_post_result,
42 int status_code,
43 const std::string& response_body,
44 const base::Time& timestamp,
45 const GURL& server_url,
46 Geoposition* position,
47 string16* access_token);
48
49 const char* RadioTypeToString(RadioType type);
50 // Adds a string if it's valid to the JSON object.
51 void AddString(const std::string& property_name,
52 const string16& value,
53 DictionaryValue* object);
54 // Adds an integer if it's valid to the JSON object.
55 void AddInteger(const std::string& property_name,
56 int value,
57 DictionaryValue* object);
58 // Parses the server response body. Returns true if parsing was successful.
59 // Sets |*position| to the parsed location if a valid fix was received,
60 // otherwise leaves it unchanged (i.e. IsInitialized() == false).
61 bool ParseServerResponse(const std::string& response_body,
62 const base::Time& timestamp,
63 Geoposition* position,
64 string16* access_token);
65 void AddGatewayData(const GatewayData& gateway_data,
66 int age_milliseconds,
67 DictionaryValue* body_object);
68 void AddRadioData(const RadioData& radio_data,
69 int age_milliseconds,
70 DictionaryValue* body_object);
71 void AddWifiData(const WifiData& wifi_data,
72 int age_milliseconds,
73 DictionaryValue* body_object);
74 } // namespace
75
76 int NetworkLocationRequest::url_fetcher_id_for_tests = 0;
77
78 NetworkLocationRequest::NetworkLocationRequest(URLRequestContextGetter* context,
79 const GURL& url,
80 ListenerInterface* listener)
81 : url_context_(context), listener_(listener),
82 url_(url) {
83 DCHECK(listener);
84 }
85
86 NetworkLocationRequest::~NetworkLocationRequest() {
87 }
88
89 bool NetworkLocationRequest::MakeRequest(const std::string& host_name,
90 const string16& access_token,
91 const GatewayData& gateway_data,
92 const RadioData& radio_data,
93 const WifiData& wifi_data,
94 const base::Time& timestamp) {
95 if (url_fetcher_ != NULL) {
96 DVLOG(1) << "NetworkLocationRequest : Cancelling pending request";
97 url_fetcher_.reset();
98 }
99 gateway_data_ = gateway_data;
100 radio_data_ = radio_data;
101 wifi_data_ = wifi_data;
102 timestamp_ = timestamp;
103 std::string post_body;
104 FormRequestBody(host_name, access_token, gateway_data, radio_data_,
105 wifi_data_, timestamp_, &post_body);
106
107 url_fetcher_.reset(URLFetcher::Create(
108 url_fetcher_id_for_tests, url_, URLFetcher::POST, this));
109 url_fetcher_->set_upload_data(kMimeApplicationJson, post_body);
110 url_fetcher_->set_request_context(url_context_);
111 url_fetcher_->set_load_flags(
112 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
113 net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES |
114 net::LOAD_DO_NOT_SEND_AUTH_DATA);
115 url_fetcher_->Start();
116 return true;
117 }
118
119 void NetworkLocationRequest::OnURLFetchComplete(
120 const URLFetcher* source,
121 const GURL& url,
122 const net::URLRequestStatus& status,
123 int response_code,
124 const ResponseCookies& cookies,
125 const std::string& data) {
126 DCHECK_EQ(url_fetcher_.get(), source);
127 DCHECK(url_.possibly_invalid_spec() == url.possibly_invalid_spec());
128
129 Geoposition position;
130 string16 access_token;
131 GetLocationFromResponse(status.is_success(), response_code, data,
132 timestamp_, url, &position, &access_token);
133 const bool server_error =
134 !status.is_success() || (response_code >= 500 && response_code < 600);
135 url_fetcher_.reset();
136
137 DCHECK(listener_);
138 DVLOG(1) << "NetworkLocationRequest::Run() : Calling listener with position.";
139 listener_->LocationResponseAvailable(position, server_error, access_token,
140 gateway_data_, radio_data_, wifi_data_);
141 }
142
143 // Local functions.
144 namespace {
145
146 void FormRequestBody(const std::string& host_name,
147 const string16& access_token,
148 const GatewayData& gateway_data,
149 const RadioData& radio_data,
150 const WifiData& wifi_data,
151 const base::Time& timestamp,
152 std::string* data) {
153 DCHECK(data);
154
155 DictionaryValue body_object;
156 // Version and host are required.
157 COMPILE_ASSERT(sizeof(kGeoLocationNetworkProtocolVersion) > 1,
158 must_include_valid_version);
159 DCHECK(!host_name.empty());
160 body_object.SetString("version", kGeoLocationNetworkProtocolVersion);
161 body_object.SetString("host", host_name);
162
163 AddString("access_token", access_token, &body_object);
164
165 body_object.SetBoolean("request_address", false);
166
167 int age = kint32min; // Invalid so AddInteger() will ignore.
168 if (!timestamp.is_null()) {
169 // Convert absolute timestamps into a relative age.
170 int64 delta_ms = (base::Time::Now() - timestamp).InMilliseconds();
171 if (delta_ms >= 0 && delta_ms < kint32max)
172 age = static_cast<int>(delta_ms);
173 }
174 AddRadioData(radio_data, age, &body_object);
175 AddWifiData(wifi_data, age, &body_object);
176 AddGatewayData(gateway_data, age, &body_object);
177
178 base::JSONWriter::Write(&body_object, false, data);
179 DVLOG(1) << "NetworkLocationRequest::FormRequestBody(): Formed body " << *data
180 << ".";
181 }
182
183 void FormatPositionError(const GURL& server_url,
184 const std::string& message,
185 Geoposition* position) {
186 position->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE;
187 position->error_message = "Network location provider at '";
188 position->error_message += server_url.possibly_invalid_spec();
189 position->error_message += "' : ";
190 position->error_message += message;
191 position->error_message += ".";
192 VLOG(1) << "NetworkLocationRequest::GetLocationFromResponse() : "
193 << position->error_message;
194 }
195
196 void GetLocationFromResponse(bool http_post_result,
197 int status_code,
198 const std::string& response_body,
199 const base::Time& timestamp,
200 const GURL& server_url,
201 Geoposition* position,
202 string16* access_token) {
203 DCHECK(position);
204 DCHECK(access_token);
205
206 // HttpPost can fail for a number of reasons. Most likely this is because
207 // we're offline, or there was no response.
208 if (!http_post_result) {
209 FormatPositionError(server_url, "No response received", position);
210 return;
211 }
212 if (status_code != 200) { // HTTP OK.
213 std::string message = "Returned error code ";
214 message += base::IntToString(status_code);
215 FormatPositionError(server_url, message, position);
216 return;
217 }
218 // We use the timestamp from the device data that was used to generate
219 // this position fix.
220 if (!ParseServerResponse(response_body, timestamp, position, access_token)) {
221 // We failed to parse the repsonse.
222 FormatPositionError(server_url, "Response was malformed", position);
223 return;
224 }
225 // The response was successfully parsed, but it may not be a valid
226 // position fix.
227 if (!position->IsValidFix()) {
228 FormatPositionError(server_url,
229 "Did not provide a good position fix", position);
230 return;
231 }
232 }
233
234 const char* RadioTypeToString(RadioType type) {
235 switch (type) {
236 case RADIO_TYPE_UNKNOWN:
237 break;
238 case RADIO_TYPE_GSM:
239 return "gsm";
240 case RADIO_TYPE_CDMA:
241 return "cdma";
242 case RADIO_TYPE_WCDMA:
243 return "wcdma";
244 default:
245 LOG(DFATAL) << "Bad RadioType";
246 }
247 return "unknown";
248 }
249
250 void AddString(const std::string& property_name,
251 const string16& value,
252 DictionaryValue* object) {
253 DCHECK(object);
254 if (!value.empty()) {
255 object->SetString(property_name, value);
256 }
257 }
258
259 void AddInteger(const std::string& property_name,
260 int value,
261 DictionaryValue* object) {
262 DCHECK(object);
263 if (kint32min != value) {
264 object->SetInteger(property_name, value);
265 }
266 }
267
268 // Numeric values without a decimal point have type integer and IsDouble() will
269 // return false. This is convenience function for detecting integer or floating
270 // point numeric values. Note that isIntegral() includes boolean values, which
271 // is not what we want.
272 bool GetAsDouble(const DictionaryValue& object,
273 const std::string& property_name,
274 double* out) {
275 DCHECK(out);
276 Value* value = NULL;
277 if (!object.Get(property_name, &value))
278 return false;
279 int value_as_int;
280 DCHECK(value);
281 if (value->GetAsInteger(&value_as_int)) {
282 *out = value_as_int;
283 return true;
284 }
285 return value->GetAsDouble(out);
286 }
287
288 bool ParseServerResponse(const std::string& response_body,
289 const base::Time& timestamp,
290 Geoposition* position,
291 string16* access_token) {
292 DCHECK(position);
293 DCHECK(!position->IsInitialized());
294 DCHECK(access_token);
295 DCHECK(!timestamp.is_null());
296
297 if (response_body.empty()) {
298 LOG(WARNING) << "ParseServerResponse() : Response was empty.";
299 return false;
300 }
301 DVLOG(1) << "ParseServerResponse() : Parsing response " << response_body;
302
303 // Parse the response, ignoring comments.
304 std::string error_msg;
305 scoped_ptr<Value> response_value(base::JSONReader::ReadAndReturnError(
306 response_body, false, NULL, &error_msg));
307 if (response_value == NULL) {
308 LOG(WARNING) << "ParseServerResponse() : JSONReader failed : "
309 << error_msg;
310 return false;
311 }
312
313 if (!response_value->IsType(Value::TYPE_DICTIONARY)) {
314 VLOG(1) << "ParseServerResponse() : Unexpected resopnse type "
315 << response_value->GetType();
316 return false;
317 }
318 const DictionaryValue* response_object =
319 static_cast<DictionaryValue*>(response_value.get());
320
321 // Get the access token, if any.
322 response_object->GetString(kAccessTokenString, access_token);
323
324 // Get the location
325 Value* location_value = NULL;
326 if (!response_object->Get(kLocationString, &location_value)) {
327 VLOG(1) << "ParseServerResponse() : Missing location attribute.";
328 // GLS returns a response with no location property to represent
329 // no fix available; return true to indicate successful parse.
330 return true;
331 }
332 DCHECK(location_value);
333
334 if (!location_value->IsType(Value::TYPE_DICTIONARY)) {
335 if (!location_value->IsType(Value::TYPE_NULL)) {
336 VLOG(1) << "ParseServerResponse() : Unexpected location type "
337 << location_value->GetType();
338 // If the network provider was unable to provide a position fix, it should
339 // return a HTTP 200, with "location" : null. Otherwise it's an error.
340 return false;
341 }
342 return true; // Successfully parsed response containing no fix.
343 }
344 DictionaryValue* location_object =
345 static_cast<DictionaryValue*>(location_value);
346
347 // latitude and longitude fields are always required.
348 double latitude, longitude;
349 if (!GetAsDouble(*location_object, kLatitudeString, &latitude) ||
350 !GetAsDouble(*location_object, kLongitudeString, &longitude)) {
351 VLOG(1) << "ParseServerResponse() : location lacks lat and/or long.";
352 return false;
353 }
354 // All error paths covered: now start actually modifying postion.
355 position->latitude = latitude;
356 position->longitude = longitude;
357 position->timestamp = timestamp;
358
359 // Other fields are optional.
360 GetAsDouble(*location_object, kAccuracyString, &position->accuracy);
361 GetAsDouble(*location_object, kAltitudeString, &position->altitude);
362 GetAsDouble(*location_object, kAltitudeAccuracyString,
363 &position->altitude_accuracy);
364
365 return true;
366 }
367
368 void AddRadioData(const RadioData& radio_data,
369 int age_milliseconds,
370 DictionaryValue* body_object) {
371 DCHECK(body_object);
372
373 AddInteger("home_mobile_country_code", radio_data.home_mobile_country_code,
374 body_object);
375 AddInteger("home_mobile_network_code", radio_data.home_mobile_network_code,
376 body_object);
377 AddString("radio_type",
378 ASCIIToUTF16(RadioTypeToString(radio_data.radio_type)),
379 body_object);
380 AddString("carrier", radio_data.carrier, body_object);
381
382 const int num_cell_towers = static_cast<int>(radio_data.cell_data.size());
383 if (num_cell_towers == 0) {
384 return;
385 }
386 ListValue* cell_towers = new ListValue;
387 for (int i = 0; i < num_cell_towers; ++i) {
388 DictionaryValue* cell_tower = new DictionaryValue;
389 AddInteger("cell_id", radio_data.cell_data[i].cell_id, cell_tower);
390 AddInteger("location_area_code",
391 radio_data.cell_data[i].location_area_code, cell_tower);
392 AddInteger("mobile_country_code",
393 radio_data.cell_data[i].mobile_country_code, cell_tower);
394 AddInteger("mobile_network_code",
395 radio_data.cell_data[i].mobile_network_code, cell_tower);
396 AddInteger("age", age_milliseconds, cell_tower);
397 AddInteger("signal_strength",
398 radio_data.cell_data[i].radio_signal_strength, cell_tower);
399 AddInteger("timing_advance", radio_data.cell_data[i].timing_advance,
400 cell_tower);
401 cell_towers->Append(cell_tower);
402 }
403 body_object->Set("cell_towers", cell_towers);
404 }
405
406 void AddWifiData(const WifiData& wifi_data,
407 int age_milliseconds,
408 DictionaryValue* body_object) {
409 DCHECK(body_object);
410
411 if (wifi_data.access_point_data.empty()) {
412 return;
413 }
414
415 ListValue* wifi_towers = new ListValue;
416 for (WifiData::AccessPointDataSet::const_iterator iter =
417 wifi_data.access_point_data.begin();
418 iter != wifi_data.access_point_data.end();
419 iter++) {
420 DictionaryValue* wifi_tower = new DictionaryValue;
421 AddString("mac_address", iter->mac_address, wifi_tower);
422 AddInteger("signal_strength", iter->radio_signal_strength, wifi_tower);
423 AddInteger("age", age_milliseconds, wifi_tower);
424 AddInteger("channel", iter->channel, wifi_tower);
425 AddInteger("signal_to_noise", iter->signal_to_noise, wifi_tower);
426 AddString("ssid", iter->ssid, wifi_tower);
427 wifi_towers->Append(wifi_tower);
428 }
429 body_object->Set("wifi_towers", wifi_towers);
430 }
431
432 void AddGatewayData(const GatewayData& gateway_data,
433 int age_milliseconds,
434 DictionaryValue* body_object) {
435 DCHECK(body_object);
436
437 if (gateway_data.router_data.empty()) {
438 return;
439 }
440
441 ListValue* gateways = new ListValue;
442 for (GatewayData::RouterDataSet::const_iterator iter =
443 gateway_data.router_data.begin();
444 iter != gateway_data.router_data.end();
445 iter++) {
446 DictionaryValue* gateway = new DictionaryValue;
447 AddString("mac_address", iter->mac_address, gateway);
448 gateways->Append(gateway);
449 }
450 body_object->Set("gateways", gateways);
451 }
452 } // namespace
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698