15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/browser/geolocation/network_location_request.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <set>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/json/json_reader.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/json/json_writer.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/metrics/histogram.h"
13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_number_conversions.h"
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/browser/geolocation/location_arbitrator_impl.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/common/geoposition.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "google_apis/google_api_keys.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/escape.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/load_flags.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_fetcher.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_context_getter.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_status.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace content {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const size_t kMaxRequestLength = 2048;
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kAccessTokenString[] = "accessToken";
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kLocationString[] = "location";
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kLatitudeString[] = "lat";
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kLongitudeString[] = "lng";
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kAccuracyString[] = "accuracy";
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kStatusString[] = "status";
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kStatusOKString[] = "OK";
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Local functions
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Creates the request url to send to the server.
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)GURL FormRequestURL(const GURL& url);
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FormUploadData(const WifiData& wifi_data,
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    const base::Time& timestamp,
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    const string16& access_token,
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    std::string* upload_data);
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Parsers the server response.
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GetLocationFromResponse(bool http_post_result,
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             int status_code,
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const std::string& response_body,
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const base::Time& timestamp,
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const GURL& server_url,
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             Geoposition* position,
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             string16* access_token);
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Parses the server response body. Returns true if parsing was successful.
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Sets |*position| to the parsed location if a valid fix was received,
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// otherwise leaves it unchanged.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ParseServerResponse(const std::string& response_body,
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         const base::Time& timestamp,
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         Geoposition* position,
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         string16* access_token);
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AddWifiData(const WifiData& wifi_data,
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 int age_milliseconds,
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 base::DictionaryValue* request);
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int NetworkLocationRequest::url_fetcher_id_for_tests = 0;
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NetworkLocationRequest::NetworkLocationRequest(
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    net::URLRequestContextGetter* context,
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const GURL& url,
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ListenerInterface* listener)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        : url_context_(context), listener_(listener),
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          url_(url) {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(listener);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NetworkLocationRequest::~NetworkLocationRequest() {
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool NetworkLocationRequest::MakeRequest(const string16& access_token,
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         const WifiData& wifi_data,
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         const base::Time& timestamp) {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (url_fetcher_ != NULL) {
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DVLOG(1) << "NetworkLocationRequest : Cancelling pending request";
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url_fetcher_.reset();
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wifi_data_ = wifi_data;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  timestamp_ = timestamp;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GURL request_url = FormRequestURL(url_);
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_fetcher_.reset(net::URLFetcher::Create(
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      url_fetcher_id_for_tests, request_url, net::URLFetcher::POST, this));
95868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  url_fetcher_->SetRequestContext(url_context_.get());
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string upload_data;
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FormUploadData(wifi_data, timestamp, access_token, &upload_data);
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_fetcher_->SetUploadData("application/json", upload_data);
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_fetcher_->SetLoadFlags(
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES |
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::LOAD_DO_NOT_SEND_AUTH_DATA);
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  start_time_ = base::TimeTicks::Now();
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_fetcher_->Start();
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NetworkLocationRequest::OnURLFetchComplete(
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const net::URLFetcher* source) {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(url_fetcher_.get(), source);
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net::URLRequestStatus status = source->GetStatus();
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int response_code = source->GetResponseCode();
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Geoposition position;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 access_token;
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string data;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  source->GetResponseAsString(&data);
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetLocationFromResponse(status.is_success(),
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          response_code,
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          data,
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          timestamp_,
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          source->GetURL(),
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          &position,
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          &access_token);
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const bool server_error =
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !status.is_success() || (response_code >= 500 && response_code < 600);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_fetcher_.reset();
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!server_error) {
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const base::TimeDelta request_time = base::TimeTicks::Now() - start_time_;
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UMA_HISTOGRAM_CUSTOM_TIMES(
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "Net.Wifi.LbsLatency",
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        request_time,
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::TimeDelta::FromMilliseconds(1),
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::TimeDelta::FromSeconds(10),
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        100);
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(listener_);
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DVLOG(1) << "NetworkLocationRequest::Run() : Calling listener with position.";
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  listener_->LocationResponseAvailable(position, server_error, access_token,
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       wifi_data_);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Local functions.
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct AccessPointLess {
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool operator()(const AccessPointData* ap1,
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  const AccessPointData* ap2) const {
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ap2->radio_signal_strength < ap1->radio_signal_strength;
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)GURL FormRequestURL(const GURL& url) {
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (url == GeolocationArbitratorImpl::DefaultNetworkProviderURL()) {
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string api_key = google_apis::GetAPIKey();
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!api_key.empty()) {
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string query(url.query());
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!query.empty())
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        query += "&";
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      query += "key=" + net::EscapeQueryParamValue(api_key, true);
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GURL::Replacements replacements;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      replacements.SetQueryStr(query);
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return url.ReplaceComponents(replacements);
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return url;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FormUploadData(const WifiData& wifi_data,
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    const base::Time& timestamp,
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    const string16& access_token,
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    std::string* upload_data) {
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int age = kint32min;  // Invalid so AddInteger() will ignore.
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!timestamp.is_null()) {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Convert absolute timestamps into a relative age.
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int64 delta_ms = (base::Time::Now() - timestamp).InMilliseconds();
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (delta_ms >= 0 && delta_ms < kint32max)
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      age = static_cast<int>(delta_ms);
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::DictionaryValue request;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddWifiData(wifi_data, age, &request);
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!access_token.empty())
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request.SetString(kAccessTokenString, access_token);
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::JSONWriter::Write(&request, upload_data);
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AddString(const std::string& property_name, const std::string& value,
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               base::DictionaryValue* dict) {
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(dict);
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!value.empty())
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dict->SetString(property_name, value);
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AddInteger(const std::string& property_name, int value,
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                base::DictionaryValue* dict) {
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(dict);
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value != kint32min)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dict->SetInteger(property_name, value);
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AddWifiData(const WifiData& wifi_data,
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 int age_milliseconds,
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 base::DictionaryValue* request) {
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(request);
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (wifi_data.access_point_data.empty())
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typedef std::multiset<const AccessPointData*, AccessPointLess> AccessPointSet;
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AccessPointSet access_points_by_signal_strength;
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (WifiData::AccessPointDataSet::const_iterator iter =
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       wifi_data.access_point_data.begin();
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != wifi_data.access_point_data.end();
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++iter) {
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    access_points_by_signal_strength.insert(&(*iter));
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::ListValue* wifi_access_point_list = new base::ListValue();
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (AccessPointSet::iterator iter =
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      access_points_by_signal_strength.begin();
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      iter != access_points_by_signal_strength.end();
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++iter) {
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::DictionaryValue* wifi_dict = new base::DictionaryValue();
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddString("macAddress", UTF16ToUTF8((*iter)->mac_address), wifi_dict);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddInteger("signalStrength", (*iter)->radio_signal_strength, wifi_dict);
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddInteger("age", age_milliseconds, wifi_dict);
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddInteger("channel", (*iter)->channel, wifi_dict);
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddInteger("signalToNoiseRatio", (*iter)->signal_to_noise, wifi_dict);
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    wifi_access_point_list->Append(wifi_dict);
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request->Set("wifiAccessPoints", wifi_access_point_list);
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FormatPositionError(const GURL& server_url,
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         const std::string& message,
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         Geoposition* position) {
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    position->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE;
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    position->error_message = "Network location provider at '";
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    position->error_message += server_url.GetOrigin().spec();
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    position->error_message += "' : ";
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    position->error_message += message;
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    position->error_message += ".";
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VLOG(1) << "NetworkLocationRequest::GetLocationFromResponse() : "
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            << position->error_message;
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GetLocationFromResponse(bool http_post_result,
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             int status_code,
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const std::string& response_body,
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const base::Time& timestamp,
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const GURL& server_url,
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             Geoposition* position,
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             string16* access_token) {
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(position);
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(access_token);
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // HttpPost can fail for a number of reasons. Most likely this is because
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // we're offline, or there was no response.
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!http_post_result) {
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FormatPositionError(server_url, "No response received", position);
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (status_code != 200) {  // HTTP OK.
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string message = "Returned error code ";
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    message += base::IntToString(status_code);
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FormatPositionError(server_url, message, position);
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We use the timestamp from the device data that was used to generate
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // this position fix.
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ParseServerResponse(response_body, timestamp, position, access_token)) {
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We failed to parse the repsonse.
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FormatPositionError(server_url, "Response was malformed", position);
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The response was successfully parsed, but it may not be a valid
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // position fix.
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!position->Validate()) {
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FormatPositionError(server_url,
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        "Did not provide a good position fix", position);
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Numeric values without a decimal point have type integer and IsDouble() will
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// return false. This is convenience function for detecting integer or floating
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// point numeric values. Note that isIntegral() includes boolean values, which
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// is not what we want.
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetAsDouble(const base::DictionaryValue& object,
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 const std::string& property_name,
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 double* out) {
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(out);
3007d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  const base::Value* value = NULL;
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!object.Get(property_name, &value))
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int value_as_int;
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(value);
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value->GetAsInteger(&value_as_int)) {
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *out = value_as_int;
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return value->GetAsDouble(out);
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ParseServerResponse(const std::string& response_body,
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         const base::Time& timestamp,
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         Geoposition* position,
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         string16* access_token) {
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(position);
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!position->Validate());
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(position->error_code == Geoposition::ERROR_CODE_NONE);
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(access_token);
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!timestamp.is_null());
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (response_body.empty()) {
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "ParseServerResponse() : Response was empty.";
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DVLOG(1) << "ParseServerResponse() : Parsing response " << response_body;
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Parse the response, ignoring comments.
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string error_msg;
3307d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  scoped_ptr<base::Value> response_value(base::JSONReader::ReadAndReturnError(
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_body, base::JSON_PARSE_RFC, NULL, &error_msg));
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (response_value == NULL) {
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "ParseServerResponse() : JSONReader failed : "
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << error_msg;
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3387d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (!response_value->IsType(base::Value::TYPE_DICTIONARY)) {
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VLOG(1) << "ParseServerResponse() : Unexpected response type "
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            << response_value->GetType();
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const base::DictionaryValue* response_object =
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<base::DictionaryValue*>(response_value.get());
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the access token, if any.
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  response_object->GetString(kAccessTokenString, access_token);
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the location
3507d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  const base::Value* location_value = NULL;
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!response_object->Get(kLocationString, &location_value)) {
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VLOG(1) << "ParseServerResponse() : Missing location attribute.";
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // GLS returns a response with no location property to represent
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // no fix available; return true to indicate successful parse.
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(location_value);
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3597d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (!location_value->IsType(base::Value::TYPE_DICTIONARY)) {
3607d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if (!location_value->IsType(base::Value::TYPE_NULL)) {
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      VLOG(1) << "ParseServerResponse() : Unexpected location type "
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              << location_value->GetType();
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If the network provider was unable to provide a position fix, it should
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // return a HTTP 200, with "location" : null. Otherwise it's an error.
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;  // Successfully parsed response containing no fix.
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const base::DictionaryValue* location_object =
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<const base::DictionaryValue*>(location_value);
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // latitude and longitude fields are always required.
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double latitude, longitude;
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetAsDouble(*location_object, kLatitudeString, &latitude) ||
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !GetAsDouble(*location_object, kLongitudeString, &longitude)) {
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VLOG(1) << "ParseServerResponse() : location lacks lat and/or long.";
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // All error paths covered: now start actually modifying postion.
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  position->latitude = latitude;
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  position->longitude = longitude;
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  position->timestamp = timestamp;
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Other fields are optional.
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetAsDouble(*response_object, kAccuracyString, &position->accuracy);
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace content
393