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"
134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/metrics/sparse_histogram.h"
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_number_conversions.h"
15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/browser/geolocation/location_arbitrator_impl.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/common/geoposition.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "google_apis/google_api_keys.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/escape.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/load_flags.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_fetcher.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_context_getter.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_status.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace content {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kAccessTokenString[] = "accessToken";
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kLocationString[] = "location";
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kLatitudeString[] = "lat";
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kLongitudeString[] = "lng";
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kAccuracyString[] = "accuracy";
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)enum NetworkLocationRequestEvent {
364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // NOTE: Do not renumber these as that would confuse interpretation of
374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // previously logged data. When making changes, also update the enum list
384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // in tools/metrics/histograms/histograms.xml to keep it in sync.
394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  NETWORK_LOCATION_REQUEST_EVENT_REQUEST_START = 0,
404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  NETWORK_LOCATION_REQUEST_EVENT_REQUEST_CANCEL = 1,
414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_SUCCESS = 2,
424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_NOT_OK = 3,
434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_EMPTY = 4,
444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_MALFORMED = 5,
454e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_INVALID_FIX = 6,
464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // NOTE: Add entries only immediately above this line.
484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  NETWORK_LOCATION_REQUEST_EVENT_COUNT = 7
494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)};
504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void RecordUmaEvent(NetworkLocationRequestEvent event) {
524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("Geolocation.NetworkLocationRequest.Event",
534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      event, NETWORK_LOCATION_REQUEST_EVENT_COUNT);
544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void RecordUmaResponseCode(int code) {
574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  UMA_HISTOGRAM_SPARSE_SLOWLY("Geolocation.NetworkLocationRequest.ResponseCode",
584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      code);
594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
618bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void RecordUmaAccessPoints(int count) {
628bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  const int min = 0;
631e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  const int max = 20;
641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  const int buckets = 21;
658bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  UMA_HISTOGRAM_CUSTOM_COUNTS("Geolocation.NetworkLocationRequest.AccessPoints",
668bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      count, min, max, buckets);
678bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
688bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Local functions
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Creates the request url to send to the server.
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)GURL FormRequestURL(const GURL& url);
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FormUploadData(const WifiData& wifi_data,
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    const base::Time& timestamp,
75a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                    const base::string16& access_token,
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    std::string* upload_data);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Attempts to extract a position from the response. Detects and indicates
794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// various failure cases.
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GetLocationFromResponse(bool http_post_result,
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             int status_code,
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const std::string& response_body,
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const base::Time& timestamp,
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const GURL& server_url,
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             Geoposition* position,
86a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                             base::string16* access_token);
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Parses the server response body. Returns true if parsing was successful.
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Sets |*position| to the parsed location if a valid fix was received,
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// otherwise leaves it unchanged.
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ParseServerResponse(const std::string& response_body,
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         const base::Time& timestamp,
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         Geoposition* position,
94a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                         base::string16* access_token);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AddWifiData(const WifiData& wifi_data,
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 int age_milliseconds,
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 base::DictionaryValue* request);
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int NetworkLocationRequest::url_fetcher_id_for_tests = 0;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NetworkLocationRequest::NetworkLocationRequest(
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    net::URLRequestContextGetter* context,
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const GURL& url,
1053551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    LocationResponseCallback callback)
1066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    : url_context_(context), location_response_callback_(callback), url_(url) {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NetworkLocationRequest::~NetworkLocationRequest() {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
112a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool NetworkLocationRequest::MakeRequest(const base::string16& access_token,
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         const WifiData& wifi_data,
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         const base::Time& timestamp) {
1154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_REQUEST_START);
1168bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  RecordUmaAccessPoints(wifi_data.access_point_data.size());
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (url_fetcher_ != NULL) {
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DVLOG(1) << "NetworkLocationRequest : Cancelling pending request";
1194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_REQUEST_CANCEL);
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url_fetcher_.reset();
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wifi_data_ = wifi_data;
1236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  wifi_data_timestamp_ = timestamp;
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GURL request_url = FormRequestURL(url_);
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_fetcher_.reset(net::URLFetcher::Create(
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      url_fetcher_id_for_tests, request_url, net::URLFetcher::POST, this));
128868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  url_fetcher_->SetRequestContext(url_context_.get());
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string upload_data;
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FormUploadData(wifi_data, timestamp, access_token, &upload_data);
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_fetcher_->SetUploadData("application/json", upload_data);
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_fetcher_->SetLoadFlags(
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES |
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::LOAD_DO_NOT_SEND_AUTH_DATA);
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  request_start_time_ = base::TimeTicks::Now();
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_fetcher_->Start();
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NetworkLocationRequest::OnURLFetchComplete(
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const net::URLFetcher* source) {
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(url_fetcher_.get(), source);
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net::URLRequestStatus status = source->GetStatus();
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int response_code = source->GetResponseCode();
1484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  RecordUmaResponseCode(response_code);
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Geoposition position;
151a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 access_token;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string data;
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  source->GetResponseAsString(&data);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetLocationFromResponse(status.is_success(),
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          response_code,
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          data,
1576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                          wifi_data_timestamp_,
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          source->GetURL(),
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          &position,
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          &access_token);
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const bool server_error =
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !status.is_success() || (response_code >= 500 && response_code < 600);
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  url_fetcher_.reset();
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!server_error) {
1666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const base::TimeDelta request_time =
1676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        base::TimeTicks::Now() - request_start_time_;
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UMA_HISTOGRAM_CUSTOM_TIMES(
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "Net.Wifi.LbsLatency",
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        request_time,
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::TimeDelta::FromMilliseconds(1),
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::TimeDelta::FromSeconds(10),
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        100);
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1773551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  DVLOG(1) << "NetworkLocationRequest::OnURLFetchComplete() : run callback.";
1786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  location_response_callback_.Run(
1796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      position, server_error, access_token, wifi_data_);
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Local functions.
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct AccessPointLess {
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool operator()(const AccessPointData* ap1,
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  const AccessPointData* ap2) const {
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ap2->radio_signal_strength < ap1->radio_signal_strength;
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)GURL FormRequestURL(const GURL& url) {
19358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (url == LocationArbitratorImpl::DefaultNetworkProviderURL()) {
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string api_key = google_apis::GetAPIKey();
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!api_key.empty()) {
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string query(url.query());
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!query.empty())
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        query += "&";
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      query += "key=" + net::EscapeQueryParamValue(api_key, true);
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GURL::Replacements replacements;
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      replacements.SetQueryStr(query);
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return url.ReplaceComponents(replacements);
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return url;
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FormUploadData(const WifiData& wifi_data,
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    const base::Time& timestamp,
210a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                    const base::string16& access_token,
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    std::string* upload_data) {
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int age = kint32min;  // Invalid so AddInteger() will ignore.
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!timestamp.is_null()) {
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Convert absolute timestamps into a relative age.
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int64 delta_ms = (base::Time::Now() - timestamp).InMilliseconds();
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (delta_ms >= 0 && delta_ms < kint32max)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      age = static_cast<int>(delta_ms);
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::DictionaryValue request;
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddWifiData(wifi_data, age, &request);
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!access_token.empty())
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request.SetString(kAccessTokenString, access_token);
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::JSONWriter::Write(&request, upload_data);
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AddString(const std::string& property_name, const std::string& value,
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               base::DictionaryValue* dict) {
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(dict);
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!value.empty())
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dict->SetString(property_name, value);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AddInteger(const std::string& property_name, int value,
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                base::DictionaryValue* dict) {
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(dict);
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value != kint32min)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dict->SetInteger(property_name, value);
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void AddWifiData(const WifiData& wifi_data,
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 int age_milliseconds,
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 base::DictionaryValue* request) {
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(request);
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (wifi_data.access_point_data.empty())
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typedef std::multiset<const AccessPointData*, AccessPointLess> AccessPointSet;
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AccessPointSet access_points_by_signal_strength;
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (WifiData::AccessPointDataSet::const_iterator iter =
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       wifi_data.access_point_data.begin();
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != wifi_data.access_point_data.end();
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       ++iter) {
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    access_points_by_signal_strength.insert(&(*iter));
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::ListValue* wifi_access_point_list = new base::ListValue();
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (AccessPointSet::iterator iter =
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      access_points_by_signal_strength.begin();
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      iter != access_points_by_signal_strength.end();
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++iter) {
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::DictionaryValue* wifi_dict = new base::DictionaryValue();
2655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    AddString("macAddress", base::UTF16ToUTF8((*iter)->mac_address), wifi_dict);
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddInteger("signalStrength", (*iter)->radio_signal_strength, wifi_dict);
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddInteger("age", age_milliseconds, wifi_dict);
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddInteger("channel", (*iter)->channel, wifi_dict);
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddInteger("signalToNoiseRatio", (*iter)->signal_to_noise, wifi_dict);
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    wifi_access_point_list->Append(wifi_dict);
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request->Set("wifiAccessPoints", wifi_access_point_list);
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FormatPositionError(const GURL& server_url,
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         const std::string& message,
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         Geoposition* position) {
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    position->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    position->error_message = "Network location provider at '";
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    position->error_message += server_url.GetOrigin().spec();
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    position->error_message += "' : ";
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    position->error_message += message;
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    position->error_message += ".";
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VLOG(1) << "NetworkLocationRequest::GetLocationFromResponse() : "
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            << position->error_message;
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GetLocationFromResponse(bool http_post_result,
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             int status_code,
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const std::string& response_body,
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const base::Time& timestamp,
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const GURL& server_url,
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             Geoposition* position,
294a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                             base::string16* access_token) {
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(position);
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(access_token);
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // HttpPost can fail for a number of reasons. Most likely this is because
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // we're offline, or there was no response.
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!http_post_result) {
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FormatPositionError(server_url, "No response received", position);
3024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_EMPTY);
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (status_code != 200) {  // HTTP OK.
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string message = "Returned error code ";
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    message += base::IntToString(status_code);
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FormatPositionError(server_url, message, position);
3094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_NOT_OK);
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
312424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // We use the timestamp from the wifi data that was used to generate
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // this position fix.
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ParseServerResponse(response_body, timestamp, position, access_token)) {
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We failed to parse the repsonse.
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FormatPositionError(server_url, "Response was malformed", position);
3174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_MALFORMED);
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The response was successfully parsed, but it may not be a valid
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // position fix.
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!position->Validate()) {
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FormatPositionError(server_url,
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        "Did not provide a good position fix", position);
3254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_INVALID_FIX);
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_SUCCESS);
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Numeric values without a decimal point have type integer and IsDouble() will
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// return false. This is convenience function for detecting integer or floating
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// point numeric values. Note that isIntegral() includes boolean values, which
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// is not what we want.
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool GetAsDouble(const base::DictionaryValue& object,
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 const std::string& property_name,
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 double* out) {
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(out);
3397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  const base::Value* value = NULL;
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!object.Get(property_name, &value))
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int value_as_int;
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(value);
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value->GetAsInteger(&value_as_int)) {
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *out = value_as_int;
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return value->GetAsDouble(out);
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ParseServerResponse(const std::string& response_body,
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         const base::Time& timestamp,
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         Geoposition* position,
354a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                         base::string16* access_token) {
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(position);
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!position->Validate());
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(position->error_code == Geoposition::ERROR_CODE_NONE);
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(access_token);
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!timestamp.is_null());
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (response_body.empty()) {
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "ParseServerResponse() : Response was empty.";
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DVLOG(1) << "ParseServerResponse() : Parsing response " << response_body;
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Parse the response, ignoring comments.
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string error_msg;
3697d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  scoped_ptr<base::Value> response_value(base::JSONReader::ReadAndReturnError(
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_body, base::JSON_PARSE_RFC, NULL, &error_msg));
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (response_value == NULL) {
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "ParseServerResponse() : JSONReader failed : "
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << error_msg;
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3777d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (!response_value->IsType(base::Value::TYPE_DICTIONARY)) {
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VLOG(1) << "ParseServerResponse() : Unexpected response type "
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            << response_value->GetType();
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const base::DictionaryValue* response_object =
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<base::DictionaryValue*>(response_value.get());
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the access token, if any.
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  response_object->GetString(kAccessTokenString, access_token);
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the location
3897d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  const base::Value* location_value = NULL;
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!response_object->Get(kLocationString, &location_value)) {
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VLOG(1) << "ParseServerResponse() : Missing location attribute.";
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // GLS returns a response with no location property to represent
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // no fix available; return true to indicate successful parse.
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(location_value);
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3987d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  if (!location_value->IsType(base::Value::TYPE_DICTIONARY)) {
3997d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if (!location_value->IsType(base::Value::TYPE_NULL)) {
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      VLOG(1) << "ParseServerResponse() : Unexpected location type "
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              << location_value->GetType();
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If the network provider was unable to provide a position fix, it should
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // return a HTTP 200, with "location" : null. Otherwise it's an error.
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;  // Successfully parsed response containing no fix.
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const base::DictionaryValue* location_object =
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<const base::DictionaryValue*>(location_value);
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // latitude and longitude fields are always required.
412116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  double latitude = 0;
413116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  double longitude = 0;
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetAsDouble(*location_object, kLatitudeString, &latitude) ||
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !GetAsDouble(*location_object, kLongitudeString, &longitude)) {
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VLOG(1) << "ParseServerResponse() : location lacks lat and/or long.";
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // All error paths covered: now start actually modifying postion.
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  position->latitude = latitude;
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  position->longitude = longitude;
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  position->timestamp = timestamp;
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Other fields are optional.
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetAsDouble(*response_object, kAccuracyString, &position->accuracy);
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace content
433