| OLD | NEW |
| 1 // Copyright 2008, Google Inc. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // Redistribution and use in source and binary forms, with or without | 3 // found in the LICENSE file. |
| 4 // modification, are permitted provided that the following conditions are met: | 4 |
| 5 // | 5 #include "chrome/browser/geolocation/network_location_request.h" |
| 6 // 1. Redistributions of source code must retain the above copyright notice, | 6 |
| 7 // this list of conditions and the following disclaimer. | 7 #include "base/json/json_reader.h" |
| 8 // 2. Redistributions in binary form must reproduce the above copyright notice, | 8 #include "base/json/json_writer.h" |
| 9 // this list of conditions and the following disclaimer in the documentation | 9 #include "base/string_util.h" |
| 10 // and/or other materials provided with the distribution. | 10 #include "base/values.h" |
| 11 // 3. Neither the name of Google Inc. nor the names of its contributors may be | 11 #include "chrome/browser/geolocation/geoposition.h" |
| 12 // used to endorse or promote products derived from this software without | 12 #include "chrome/browser/net/url_request_context_getter.h" |
| 13 // specific prior written permission. | 13 #include "net/url_request/url_request_status.h" |
| 14 // | 14 |
| 15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | 15 namespace { |
| 16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | 16 const char* const kMimeApplicationJson = "application/json"; |
| 17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | 17 |
| 18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 18 // See http://code.google.com/apis/gears/geolocation_network_protocol.html |
| 19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 19 const char* kGeoLocationNetworkProtocolVersion = "1.1.0"; |
| 20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | 20 |
| 21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | 21 const wchar_t* kAccessTokenString = L"access_token"; |
| 22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | 22 const wchar_t* kLocationString = L"location"; |
| 23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | 23 const wchar_t* kLatitudeString = L"latitude"; |
| 24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 24 const wchar_t* kLongitudeString = L"longitude"; |
| 25 | 25 const wchar_t* kAltitudeString = L"altitude"; |
| 26 // TODO(joth): port to chromium | 26 const wchar_t* kAccuracyString = L"accuracy"; |
| 27 #if 0 | 27 const wchar_t* kAltitudeAccuracyString = L"altitude_accuracy"; |
| 28 | 28 |
| 29 #include "gears/geolocation/network_location_request.h" | |
| 30 | |
| 31 #include "gears/blob/blob_utils.h" | |
| 32 #include "gears/blob/buffer_blob.h" | |
| 33 #include "gears/localserver/common/http_constants.h" | |
| 34 #include "third_party/jsoncpp/reader.h" | |
| 35 #include "third_party/jsoncpp/value.h" | |
| 36 #include "third_party/jsoncpp/writer.h" | |
| 37 | |
| 38 static const char *kGearsNetworkLocationProtocolVersion = "1.1.0"; | |
| 39 | |
| 40 static const char *kAccessTokenString = "access_token"; | |
| 41 static const char *kLatitudeString = "latitude"; | |
| 42 static const char *kLongitudeString = "longitude"; | |
| 43 static const char *kAltitudeString = "altitude"; | |
| 44 static const char *kAccuracyString = "accuracy"; | |
| 45 static const char *kAltitudeAccuracyString = "altitude_accuracy"; | |
| 46 // Note that the corresponding JavaScript Position property is 'gearsAddress'. | |
| 47 static const char *kAddressString = "address"; | |
| 48 static const char *kStreetNumberString = "street_number"; | |
| 49 static const char *kStreetString = "street"; | |
| 50 static const char *kPremisesString = "premises"; | |
| 51 static const char *kCityString = "city"; | |
| 52 static const char *kCountyString = "county"; | |
| 53 static const char *kRegionString = "region"; | |
| 54 static const char *kCountryString = "country"; | |
| 55 static const char *kCountryCodeString = "country_code"; | |
| 56 static const char *kPostalCodeString = "postal_code"; | |
| 57 | |
| 58 // Local functions | 29 // Local functions |
| 59 static const char16* RadioTypeToString(RadioType type); | 30 // Creates the request payload to send to the server. |
| 31 bool FormRequestBody(const string16& host_name, |
| 32 const string16& access_token, |
| 33 const RadioData& radio_data, |
| 34 const WifiData& wifi_data, |
| 35 std::string* data); |
| 36 // Parsers the server response. |
| 37 void GetLocationFromResponse(bool http_post_result, |
| 38 int status_code, |
| 39 const std::string& response_body, |
| 40 int64 timestamp, |
| 41 const GURL& server_url, |
| 42 Position* position, |
| 43 string16* access_token); |
| 44 |
| 45 const char* RadioTypeToString(RadioType type); |
| 60 // Adds a string if it's valid to the JSON object. | 46 // Adds a string if it's valid to the JSON object. |
| 61 static void AddString(const std::string &property_name, | 47 void AddString(const std::wstring& property_name, |
| 62 const std::string16 &value, | 48 const string16& value, |
| 63 Json::Value *object); | 49 DictionaryValue* object); |
| 64 // Adds an integer if it's valid to the JSON object. | 50 // Adds an integer if it's valid to the JSON object. |
| 65 static void AddInteger(const std::string &property_name, | 51 void AddInteger(const std::wstring& property_name, |
| 66 const int &value, | 52 int value, |
| 67 Json::Value *object); | 53 DictionaryValue* object); |
| 68 // Returns true if the value is a valid angle. | |
| 69 static bool IsValidAngle(const double &value); | |
| 70 // Parses the server response body. Returns true if parsing was successful. | 54 // Parses the server response body. Returns true if parsing was successful. |
| 71 static bool ParseServerResponse(const std::string &response_body, | 55 bool ParseServerResponse(const std::string& response_body, |
| 72 int64 timestamp, | 56 int64 timestamp, |
| 73 bool is_reverse_geocode, | 57 Position* position, |
| 74 Position *position, | 58 string16* access_token); |
| 75 std::string16 *access_token); | 59 void AddRadioData(const RadioData& radio_data, DictionaryValue* body_object); |
| 76 static void AddRadioData(const RadioData &radio_data, Json::Value *body_object); | 60 void AddWifiData(const WifiData& wifi_data, DictionaryValue* body_object); |
| 77 static void AddWifiData(const WifiData &wifi_data, Json::Value *body_object); | 61 } // namespace |
| 78 | 62 |
| 79 // static | 63 NetworkLocationRequest::NetworkLocationRequest(URLRequestContextGetter* context, |
| 80 NetworkLocationRequest *NetworkLocationRequest::Create( | 64 const GURL& url, |
| 81 BrowsingContext *browsing_context, | 65 const string16& host_name, |
| 82 const std::string16 &url, | 66 ListenerInterface* listener) |
| 83 const std::string16 &host_name, | 67 : url_context_(context), timestamp_(kint64min), listener_(listener), |
| 84 ListenerInterface *listener) { | 68 url_(url), host_name_(host_name) { |
| 85 scoped_ptr<NetworkLocationRequest> request( | 69 // DCHECK(url_context_); |
| 86 new NetworkLocationRequest(browsing_context, url, host_name, listener)); | 70 DCHECK(listener); |
| 87 assert(request.get()); | 71 } |
| 88 if (!request.get() || !request->Init() || !request->Start()) { | 72 |
| 89 return NULL; | 73 NetworkLocationRequest::~NetworkLocationRequest() { |
| 90 } | 74 } |
| 91 return request.release(); | 75 |
| 92 } | 76 bool NetworkLocationRequest::MakeRequest(const string16& access_token, |
| 93 | 77 const RadioData& radio_data, |
| 94 NetworkLocationRequest::NetworkLocationRequest( | 78 const WifiData& wifi_data, |
| 95 BrowsingContext *browsing_context, | |
| 96 const std::string16 &url, | |
| 97 const std::string16 &host_name, | |
| 98 ListenerInterface *listener) | |
| 99 : AsyncTask(browsing_context), | |
| 100 listener_(listener), | |
| 101 url_(url), | |
| 102 host_name_(host_name), | |
| 103 is_shutting_down_(false) { | |
| 104 } | |
| 105 | |
| 106 bool NetworkLocationRequest::MakeRequest(const std::string16 &access_token, | |
| 107 const RadioData &radio_data, | |
| 108 const WifiData &wifi_data, | |
| 109 bool request_address, | |
| 110 const std::string16 &address_language, | |
| 111 double latitude, | |
| 112 double longitude, | |
| 113 int64 timestamp) { | 79 int64 timestamp) { |
| 114 is_reverse_geocode_ = request_address && | 80 if (url_fetcher_ != NULL) { |
| 115 IsValidAngle(latitude) && | 81 DLOG(INFO) << "NetworkLocationRequest : Cancelling pending request"; |
| 116 IsValidAngle(longitude); | 82 url_fetcher_.reset(); |
| 83 } |
| 84 std::string post_body; |
| 117 if (!FormRequestBody(host_name_, access_token, radio_data, wifi_data, | 85 if (!FormRequestBody(host_name_, access_token, radio_data, wifi_data, |
| 118 request_address, address_language, latitude, longitude, | 86 &post_body)) { |
| 119 is_reverse_geocode_, &post_body_)) { | |
| 120 return false; | 87 return false; |
| 121 } | 88 } |
| 122 timestamp_ = timestamp; | 89 timestamp_ = timestamp; |
| 123 | 90 |
| 124 thread_event_.Signal(); | 91 url_fetcher_.reset(URLFetcher::Create( |
| 92 host_name_.size(), // Used for testing |
| 93 url_, URLFetcher::POST, this)); |
| 94 url_fetcher_->set_upload_data(kMimeApplicationJson, post_body); |
| 95 url_fetcher_->set_request_context(url_context_); |
| 96 url_fetcher_->Start(); |
| 125 return true; | 97 return true; |
| 126 } | 98 } |
| 127 | 99 |
| 128 // AsyncTask implementation. | 100 void NetworkLocationRequest::OnURLFetchComplete(const URLFetcher* source, |
| 129 void NetworkLocationRequest::Run() { | 101 const GURL& url, |
| 130 while (true) { | 102 const URLRequestStatus& status, |
| 131 thread_event_.Wait(); | 103 int response_code, |
| 132 if (is_shutting_down_) { | 104 const ResponseCookies& cookies, |
| 133 break; | 105 const std::string& data) { |
| 134 } | 106 DCHECK_EQ(url_fetcher_.get(), source); |
| 135 MakeRequestImpl(); | 107 DCHECK(url_.possibly_invalid_spec() == url.possibly_invalid_spec()); |
| 136 } | 108 |
| 137 } | 109 Position position; |
| 138 | 110 string16 access_token; |
| 139 void NetworkLocationRequest::MakeRequestImpl() { | 111 GetLocationFromResponse(status.is_success(), response_code, data, |
| 140 WebCacheDB::PayloadInfo payload; | 112 timestamp_, url, &position, &access_token); |
| 141 // TODO(andreip): remove this once WebCacheDB::PayloadInfo.data is a Blob. | 113 const bool server_error = |
| 142 scoped_refptr<BlobInterface> payload_data; | 114 !status.is_success() || (response_code >= 500 && response_code < 600); |
| 143 bool result = HttpPost(url_.c_str(), | 115 url_fetcher_.reset(); |
| 144 false, // Not capturing, so follow redirects | 116 |
| 145 NULL, // reason_header_value | 117 DCHECK(listener_); |
| 146 HttpConstants::kMimeApplicationJson, // Content-Type | 118 DLOG(INFO) << "NetworkLocationRequest::Run() : " |
| 147 NULL, // mod_since_date | 119 "Calling listener with position.\n"; |
| 148 NULL, // required_cookie | 120 listener_->LocationResponseAvailable(position, server_error, access_token); |
| 149 true, // disable_browser_cookies | 121 } |
| 150 post_body_.get(), | 122 |
| 151 &payload, | 123 // Local functions. |
| 152 &payload_data, | 124 namespace { |
| 153 NULL, // was_redirected | 125 |
| 154 NULL, // full_redirect_url | 126 bool FormRequestBody(const string16& host_name, |
| 155 NULL); // error_message | 127 const string16& access_token, |
| 156 | 128 const RadioData& radio_data, |
| 157 MutexLock lock(&is_processing_response_mutex_); | 129 const WifiData& wifi_data, |
| 158 // is_aborted_ may be true even if HttpPost succeeded. | 130 std::string* data) { |
| 159 if (is_aborted_) { | 131 DCHECK(data); |
| 160 LOG(("NetworkLocationRequest::Run() : HttpPost request was cancelled.\n")); | 132 |
| 161 return; | 133 DictionaryValue body_object; |
| 162 } | |
| 163 | |
| 164 if (listener_) { | |
| 165 Position position; | |
| 166 std::string response_body; | |
| 167 if (result) { | |
| 168 // If HttpPost succeeded, payload_data is guaranteed to be non-NULL. | |
| 169 assert(payload_data.get()); | |
| 170 if (!payload_data->Length() || | |
| 171 !BlobToString(payload_data.get(), &response_body)) { | |
| 172 LOG(("NetworkLocationRequest::Run() : Failed to get response body.\n")); | |
| 173 } | |
| 174 } | |
| 175 std::string16 access_token; | |
| 176 GetLocationFromResponse(result, payload.status_code, response_body, | |
| 177 timestamp_, url_, is_reverse_geocode_, | |
| 178 &position, &access_token); | |
| 179 | |
| 180 LOG(("NetworkLocationRequest::Run() : Calling listener with position.\n")); | |
| 181 bool server_error = | |
| 182 !result || (payload.status_code >= 500 && payload.status_code < 600); | |
| 183 listener_->LocationResponseAvailable(position, server_error, access_token); | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 // static | |
| 188 bool NetworkLocationRequest::FormRequestBody( | |
| 189 const std::string16 &host_name, | |
| 190 const std::string16 &access_token, | |
| 191 const RadioData &radio_data, | |
| 192 const WifiData &wifi_data, | |
| 193 bool request_address, | |
| 194 std::string16 address_language, | |
| 195 double latitude, | |
| 196 double longitude, | |
| 197 bool is_reverse_geocode, | |
| 198 scoped_refptr<BlobInterface> *blob) { | |
| 199 assert(blob); | |
| 200 Json::Value body_object; | |
| 201 assert(body_object.isObject()); | |
| 202 // Version and host are required. | 134 // Version and host are required. |
| 203 if (host_name.empty()) { | 135 if (host_name.empty()) { |
| 204 return false; | 136 return false; |
| 205 } | 137 } |
| 206 body_object["version"] = Json::Value(kGearsNetworkLocationProtocolVersion); | 138 body_object.SetString(L"version", kGeoLocationNetworkProtocolVersion); |
| 207 AddString("host", host_name, &body_object); | 139 AddString(L"host", host_name, &body_object); |
| 208 | 140 |
| 209 AddString("access_token", access_token, &body_object); | 141 AddString(L"access_token", access_token, &body_object); |
| 210 | 142 |
| 211 body_object["request_address"] = request_address; | 143 body_object.SetBoolean(L"request_address", false); |
| 212 AddString("address_language", address_language, &body_object); | 144 |
| 213 | 145 AddRadioData(radio_data, &body_object); |
| 214 if (is_reverse_geocode) { | 146 AddWifiData(wifi_data, &body_object); |
| 215 assert(request_address); | 147 |
| 216 assert(IsValidAngle(latitude) && IsValidAngle(longitude)); | 148 // TODO(joth): Do we need to mess with locales? |
| 217 Json::Value location; | |
| 218 location["latitude"] = Json::Value(latitude); | |
| 219 location["longitude"] = Json::Value(longitude); | |
| 220 body_object["location"] = location; | |
| 221 } else { | |
| 222 AddRadioData(radio_data, &body_object); | |
| 223 AddWifiData(wifi_data, &body_object); | |
| 224 } | |
| 225 | |
| 226 Json::FastWriter writer; | |
| 227 // We always use the platform independent 'C' locale when writing the JSON | 149 // We always use the platform independent 'C' locale when writing the JSON |
| 228 // request, irrespective of the browser's locale. This avoids the need for | 150 // request, irrespective of the browser's locale. This avoids the need for |
| 229 // the network location provider to determine the locale of the request and | 151 // the network location provider to determine the locale of the request and |
| 230 // parse the JSON accordingly. | 152 // parse the JSON accordingly. |
| 231 #ifdef OS_WINCE | 153 // char* current_locale = setlocale(LC_NUMERIC, "C"); |
| 232 // WinCE does not support setlocale. | 154 |
| 233 #else | 155 base::JSONWriter::Write(&body_object, false, data); |
| 234 char *current_locale = setlocale(LC_NUMERIC, "C"); | 156 |
| 235 #endif | 157 // setlocale(LC_NUMERIC, current_locale); |
| 236 std::string body_string = writer.write(body_object); | 158 |
| 237 #ifdef OS_WINCE | 159 DLOG(INFO) << "NetworkLocationRequest::FormRequestBody(): Formed body " |
| 238 // WinCE does not support setlocale. | 160 << data << ".\n"; |
| 239 #else | |
| 240 setlocale(LC_NUMERIC, current_locale); | |
| 241 #endif | |
| 242 LOG(("NetworkLocationRequest::FormRequestBody(): Formed body %s.\n", | |
| 243 body_string.c_str())); | |
| 244 | |
| 245 blob->reset(new BufferBlob(body_string.c_str(), body_string.size())); | |
| 246 return true; | 161 return true; |
| 247 } | 162 } |
| 248 | 163 |
| 249 // static | 164 void FormatPositionError(const GURL& server_url, |
| 250 void NetworkLocationRequest::GetLocationFromResponse( | 165 const std::wstring& message, |
| 251 bool http_post_result, | 166 Position* position) { |
| 252 int status_code, | 167 position->error_code = Position::ERROR_CODE_POSITION_UNAVAILABLE; |
| 253 const std::string &response_body, | 168 position->error_message = L"Network location provider at '"; |
| 254 int64 timestamp, | 169 position->error_message += ASCIIToWide(server_url.possibly_invalid_spec()); |
| 255 const std::string16 &server_url, | 170 position->error_message += L"' : "; |
| 256 bool is_reverse_geocode, | 171 position->error_message += message; |
| 257 Position *position, | 172 position->error_message += L"."; |
| 258 std::string16 *access_token) { | 173 LOG(INFO) << "NetworkLocationRequest::GetLocationFromResponse() : " |
| 259 assert(position); | 174 << position->error_message; |
| 260 assert(access_token); | 175 } |
| 176 |
| 177 void GetLocationFromResponse(bool http_post_result, |
| 178 int status_code, |
| 179 const std::string& response_body, |
| 180 int64 timestamp, |
| 181 const GURL& server_url, |
| 182 Position* position, |
| 183 string16* access_token) { |
| 184 DCHECK(position); |
| 185 DCHECK(access_token); |
| 261 | 186 |
| 262 // HttpPost can fail for a number of reasons. Most likely this is because | 187 // HttpPost can fail for a number of reasons. Most likely this is because |
| 263 // we're offline, or there was no response. | 188 // we're offline, or there was no response. |
| 264 if (!http_post_result) { | 189 if (!http_post_result) { |
| 265 LOG(("NetworkLocationRequest::GetLocationFromResponse() : HttpPost request " | 190 FormatPositionError(server_url, L"No response received", position); |
| 266 "failed.\n")); | 191 return; |
| 267 position->error_code = Position::ERROR_CODE_POSITION_UNAVAILABLE; | 192 } |
| 268 position->error_message = STRING16(L"No response from network provider " | 193 if (status_code != 200) { // XXX is '200' in a constant? Can't see it |
| 269 L"at "); | |
| 270 position->error_message += server_url.c_str(); | |
| 271 position->error_message += STRING16(L"."); | |
| 272 } else if (status_code == HttpConstants::HTTP_OK) { | |
| 273 // We use the timestamp from the device data that was used to generate | |
| 274 // this position fix. | |
| 275 if (ParseServerResponse(response_body, timestamp, is_reverse_geocode, | |
| 276 position, access_token)) { | |
| 277 // The response was successfully parsed, but it may not be a valid | |
| 278 // position fix. | |
| 279 if (!position->IsGoodFix()) { | |
| 280 position->error_code = Position::ERROR_CODE_POSITION_UNAVAILABLE; | |
| 281 position->error_message = STRING16(L"Network provider at "); | |
| 282 position->error_message += server_url.c_str(); | |
| 283 position->error_message += STRING16(L" did not provide a good position " | |
| 284 L"fix."); | |
| 285 } | |
| 286 } else { | |
| 287 // We failed to parse the repsonse. | |
| 288 LOG(("NetworkLocationRequest::GetLocationFromResponse() : Response " | |
| 289 "malformed.\n")); | |
| 290 position->error_code = Position::ERROR_CODE_POSITION_UNAVAILABLE; | |
| 291 position->error_message = STRING16(L"Response from network provider at "); | |
| 292 position->error_message += server_url.c_str(); | |
| 293 position->error_message += STRING16(L" was malformed."); | |
| 294 } | |
| 295 } else { | |
| 296 // The response was bad. | 194 // The response was bad. |
| 297 LOG(("NetworkLocationRequest::GetLocationFromResponse() : HttpPost " | 195 std::wstring message = L"Returned error code "; |
| 298 "response was bad.\n")); | 196 message += IntToWString(status_code); |
| 299 position->error_code = Position::ERROR_CODE_POSITION_UNAVAILABLE; | 197 FormatPositionError(server_url, message, position); |
| 300 position->error_message = STRING16(L"Network provider at "); | 198 return; |
| 301 position->error_message += server_url.c_str(); | 199 } |
| 302 position->error_message += STRING16(L" returned error code "); | 200 // We use the timestamp from the device data that was used to generate |
| 303 position->error_message += IntegerToString16(status_code); | 201 // this position fix. |
| 304 position->error_message += STRING16(L"."); | 202 if (!ParseServerResponse(response_body, timestamp, position, access_token)) { |
| 305 } | 203 // We failed to parse the repsonse. |
| 306 } | 204 FormatPositionError(server_url, L"Response was malformed", position); |
| 307 | 205 return; |
| 308 void NetworkLocationRequest::StopThreadAndDelete() { | 206 } |
| 309 // The FF implementation of AsyncTask::Abort() delivers a message to the UI | 207 // The response was successfully parsed, but it may not be a valid |
| 310 // thread to cancel the request. So if we call this method on the UI thread, | 208 // position fix. |
| 311 // we must return to the OS before the call to Abort() will take effect. In | 209 if (!position->IsValidFix()) { |
| 312 // particular, we can't call Abort() then block here waiting for HttpPost to | 210 FormatPositionError(server_url, |
| 313 // return. | 211 L"Did not provide a good position fix", position); |
| 314 is_shutting_down_ = true; | 212 return; |
| 315 thread_event_.Signal(); | 213 } |
| 316 is_processing_response_mutex_.Lock(); | 214 } |
| 317 Abort(); | 215 |
| 318 is_processing_response_mutex_.Unlock(); | 216 const char* RadioTypeToString(RadioType type) { |
| 319 DeleteWhenDone(); | |
| 320 } | |
| 321 | |
| 322 // Local functions. | |
| 323 | |
| 324 static const char16* RadioTypeToString(RadioType type) { | |
| 325 switch (type) { | 217 switch (type) { |
| 326 case RADIO_TYPE_UNKNOWN: | 218 case RADIO_TYPE_UNKNOWN: |
| 327 return STRING16(L"unknown"); | 219 break; |
| 328 case RADIO_TYPE_GSM: | 220 case RADIO_TYPE_GSM: |
| 329 return STRING16(L"gsm"); | 221 return "gsm"; |
| 330 case RADIO_TYPE_CDMA: | 222 case RADIO_TYPE_CDMA: |
| 331 return STRING16(L"cdma"); | 223 return "cdma"; |
| 332 case RADIO_TYPE_WCDMA: | 224 case RADIO_TYPE_WCDMA: |
| 333 return STRING16(L"wcdma"); | 225 return "wcdma"; |
| 334 default: | 226 default: |
| 335 assert(false); | 227 LOG(DFATAL) << "Bad RadioType"; |
| 336 } | 228 } |
| 337 return NULL; | 229 return "unknown"; |
| 338 } | 230 } |
| 339 | 231 |
| 340 static void AddString(const std::string &property_name, | 232 void AddString(const std::wstring& property_name, |
| 341 const std::string16 &value, | 233 const string16& value, |
| 342 Json::Value *object) { | 234 DictionaryValue* object) { |
| 343 assert(object); | 235 DCHECK(object); |
| 344 assert(object->isObject()); | |
| 345 if (!value.empty()) { | 236 if (!value.empty()) { |
| 346 std::string value_utf8; | 237 object->SetStringFromUTF16(property_name, value); |
| 347 if (String16ToUTF8(value.c_str(), value.size(), &value_utf8)) { | 238 } |
| 348 (*object)[property_name] = Json::Value(value_utf8); | 239 } |
| 349 } | 240 |
| 350 } | 241 void AddInteger(const std::wstring& property_name, |
| 351 } | 242 int value, |
| 352 | 243 DictionaryValue* object) { |
| 353 static void AddInteger(const std::string &property_name, | 244 DCHECK(object); |
| 354 const int &value, | |
| 355 Json::Value *object) { | |
| 356 assert(object); | |
| 357 assert(object->isObject()); | |
| 358 if (kint32min != value) { | 245 if (kint32min != value) { |
| 359 (*object)[property_name] = Json::Value(value); | 246 object->SetInteger(property_name, value); |
| 360 } | 247 } |
| 361 } | |
| 362 | |
| 363 static bool IsValidAngle(const double &value) { | |
| 364 return value >= -180.0 && value <= 180.0; | |
| 365 } | 248 } |
| 366 | 249 |
| 367 // Numeric values without a decimal point have type integer and IsDouble() will | 250 // Numeric values without a decimal point have type integer and IsDouble() will |
| 368 // return false. This is convenience function for detecting integer or floating | 251 // return false. This is convenience function for detecting integer or floating |
| 369 // point numeric values. Note that isIntegral() includes boolean values, which | 252 // point numeric values. Note that isIntegral() includes boolean values, which |
| 370 // is not what we want. | 253 // is not what we want. |
| 371 static bool IsDoubleOrInt(const Json::Value &object, | 254 bool GetAsDouble(const DictionaryValue& object, |
| 372 const std::string &property_name) { | 255 const std::wstring& property_name, |
| 373 return object[property_name].isDouble() || object[property_name].isInt(); | 256 double* out) { |
| 374 } | 257 DCHECK(out); |
| 375 | 258 Value* value = NULL; |
| 376 // The JsValue::asXXX() methods return zero if a property isn't specified. For | 259 if (!object.Get(property_name, &value)) |
| 377 // our purposes, zero is a valid value, so we have to test for existence. | 260 return false; |
| 378 | 261 int value_as_int; |
| 379 // Gets a double if it's present. | 262 DCHECK(value); |
| 380 static bool GetAsDouble(const Json::Value &object, | 263 if (value->GetAsInteger(&value_as_int)) { |
| 381 const std::string &property_name, | 264 *out = value_as_int; |
| 382 double *out) { | 265 return true; |
| 383 assert(out); | 266 } |
| 384 if (!IsDoubleOrInt(object, property_name)) { | 267 return value->GetAsReal(out); |
| 385 return false; | 268 } |
| 386 } | 269 |
| 387 *out = object[property_name].asDouble(); | 270 bool ParseServerResponse(const std::string& response_body, |
| 271 int64 timestamp, |
| 272 Position* position, |
| 273 string16* access_token) { |
| 274 DCHECK(position); |
| 275 DCHECK(access_token); |
| 276 DCHECK(timestamp != kint64min); |
| 277 |
| 278 if (response_body.empty()) { |
| 279 LOG(WARNING) << "ParseServerResponse() : Response was empty.\n"; |
| 280 return false; |
| 281 } |
| 282 DLOG(INFO) << "ParseServerResponse() : Parsing response " |
| 283 << response_body << ".\n"; |
| 284 |
| 285 // Parse the response, ignoring comments. |
| 286 // TODO(joth): Gears version stated: The JSON reponse from the network |
| 287 // location provider should always use the 'C' locale. |
| 288 // Chromium JSON parser works in UTF8 so hopefully we can ignore setlocale? |
| 289 |
| 290 // char* current_locale = setlocale(LC_NUMERIC, "C"); |
| 291 std::string error_msg; |
| 292 scoped_ptr<Value> response_value(base::JSONReader::ReadAndReturnError( |
| 293 response_body, false, &error_msg)); |
| 294 |
| 295 // setlocale(LC_NUMERIC, current_locale); |
| 296 |
| 297 if (response_value == NULL) { |
| 298 LOG(WARNING) << "ParseServerResponse() : JSONReader failed : " |
| 299 << error_msg << ".\n"; |
| 300 return false; |
| 301 } |
| 302 |
| 303 if (!response_value->IsType(Value::TYPE_DICTIONARY)) { |
| 304 LOG(INFO) << "ParseServerResponse() : Unexpected resopnse type " |
| 305 << response_value->GetType() << ".\n"; |
| 306 return false; |
| 307 } |
| 308 const DictionaryValue* response_object = |
| 309 static_cast<DictionaryValue*>(response_value.get()); |
| 310 |
| 311 // Get the access token, if any. |
| 312 response_object->GetStringAsUTF16(kAccessTokenString, access_token); |
| 313 |
| 314 // Get the location |
| 315 DictionaryValue* location_object; |
| 316 if (!response_object->GetDictionary(kLocationString, &location_object)) { |
| 317 Value* value = NULL; |
| 318 // If the network provider was unable to provide a position fix, it should |
| 319 // return a 200 with location == null. Otherwise it's an error. |
| 320 return response_object->Get(kLocationString, &value) |
| 321 && value && value->IsType(Value::TYPE_NULL); |
| 322 } |
| 323 DCHECK(location_object); |
| 324 |
| 325 // latitude and longitude fields are always required. |
| 326 double latitude, longitude; |
| 327 if (!GetAsDouble(*location_object, kLatitudeString, &latitude) || |
| 328 !GetAsDouble(*location_object, kLongitudeString, &longitude)) { |
| 329 return false; |
| 330 } |
| 331 // All error paths covered: now start actually modifying postion. |
| 332 position->latitude = latitude; |
| 333 position->longitude = longitude; |
| 334 position->timestamp = timestamp; |
| 335 |
| 336 // Other fields are optional. |
| 337 GetAsDouble(*location_object, kAccuracyString, &position->accuracy); |
| 338 GetAsDouble(*location_object, kAltitudeString, &position->altitude); |
| 339 GetAsDouble(*location_object, kAltitudeAccuracyString, |
| 340 &position->altitude_accuracy); |
| 341 |
| 388 return true; | 342 return true; |
| 389 } | 343 } |
| 390 | 344 |
| 391 // Gets a string if it's present. | 345 void AddRadioData(const RadioData& radio_data, DictionaryValue* body_object) { |
| 392 static bool GetAsString(const Json::Value &object, | 346 DCHECK(body_object); |
| 393 const std::string &property_name, | 347 |
| 394 std::string16 *out) { | 348 AddInteger(L"home_mobile_country_code", radio_data.home_mobile_country_code, |
| 395 assert(out); | |
| 396 if (!object[property_name].isString()) { | |
| 397 return false; | |
| 398 } | |
| 399 std::string out_utf8 = object[property_name].asString(); | |
| 400 return UTF8ToString16(out_utf8.c_str(), out_utf8.size(), out); | |
| 401 } | |
| 402 | |
| 403 static bool ParseServerResponse(const std::string &response_body, | |
| 404 int64 timestamp, | |
| 405 bool is_reverse_geocode, | |
| 406 Position *position, | |
| 407 std::string16 *access_token) { | |
| 408 assert(position); | |
| 409 assert(access_token); | |
| 410 | |
| 411 if (response_body.empty()) { | |
| 412 LOG(("ParseServerResponse() : Response was empty.\n")); | |
| 413 return false; | |
| 414 } | |
| 415 LOG(("ParseServerResponse() : Parsing response %s.\n", | |
| 416 response_body.c_str())); | |
| 417 | |
| 418 // Parse the response, ignoring comments. The JSON reposne from the network | |
| 419 // location provider should always use the 'C' locale. | |
| 420 Json::Reader reader; | |
| 421 Json::Value response_object; | |
| 422 #ifdef OS_WINCE | |
| 423 // WinCE does not support setlocale. | |
| 424 #else | |
| 425 char *current_locale = setlocale(LC_NUMERIC, "C"); | |
| 426 #endif | |
| 427 bool res = reader.parse(response_body, response_object, false); | |
| 428 #ifdef OS_WINCE | |
| 429 // WinCE does not support setlocale. | |
| 430 #else | |
| 431 setlocale(LC_NUMERIC, current_locale); | |
| 432 #endif | |
| 433 if (!res) { | |
| 434 LOG(("ParseServerResponse() : Failed to parse response : %s.\n", | |
| 435 reader.getFormatedErrorMessages().c_str())); | |
| 436 return false; | |
| 437 } | |
| 438 | |
| 439 if (!response_object.isObject()) { | |
| 440 LOG(("ParseServerResponse() : Unexpected response type: %d.\n", | |
| 441 response_object.type())); | |
| 442 return false; | |
| 443 } | |
| 444 | |
| 445 // Get the access token. | |
| 446 GetAsString(response_object, kAccessTokenString, access_token); | |
| 447 | |
| 448 // Get the location | |
| 449 Json::Value location = response_object["location"]; | |
| 450 | |
| 451 // If the network provider was unable to provide a position fix, it should | |
| 452 // return a 200 with location == null. | |
| 453 if (location.type() == Json::nullValue) { | |
| 454 LOG(("ParseServerResponse() : Location is null.\n")); | |
| 455 return true; | |
| 456 } | |
| 457 | |
| 458 // If location is not null, it must be an object. | |
| 459 if (!location.isObject()) { | |
| 460 return false; | |
| 461 } | |
| 462 | |
| 463 // latitude and longitude fields are always required. | |
| 464 if (!GetAsDouble(location, kLatitudeString, &position->latitude) || | |
| 465 !GetAsDouble(location, kLongitudeString, &position->longitude)) { | |
| 466 return false; | |
| 467 } | |
| 468 | |
| 469 // If it's not a reverse geocode request, the accuracy field is also required. | |
| 470 if (is_reverse_geocode) { | |
| 471 position->accuracy = 0.0; | |
| 472 } else { | |
| 473 if (!GetAsDouble(location, kAccuracyString, &position->accuracy)) { | |
| 474 return false; | |
| 475 } | |
| 476 } | |
| 477 | |
| 478 // Other fields are optional. | |
| 479 GetAsDouble(location, kAltitudeString, &position->altitude); | |
| 480 GetAsDouble(location, kAltitudeAccuracyString, &position->altitude_accuracy); | |
| 481 Json::Value address = location[kAddressString]; | |
| 482 if (address.isObject()) { | |
| 483 GetAsString(address, kStreetNumberString, &position->address.street_number); | |
| 484 GetAsString(address, kStreetString, &position->address.street); | |
| 485 GetAsString(address, kPremisesString, &position->address.premises); | |
| 486 GetAsString(address, kCityString, &position->address.city); | |
| 487 GetAsString(address, kCountyString, &position->address.county); | |
| 488 GetAsString(address, kRegionString, &position->address.region); | |
| 489 GetAsString(address, kCountryString, &position->address.country); | |
| 490 GetAsString(address, kCountryCodeString, &position->address.country_code); | |
| 491 GetAsString(address, kPostalCodeString, &position->address.postal_code); | |
| 492 } | |
| 493 | |
| 494 position->timestamp = timestamp; | |
| 495 return true; | |
| 496 } | |
| 497 | |
| 498 static void AddRadioData(const RadioData &radio_data, | |
| 499 Json::Value *body_object) { | |
| 500 assert(body_object); | |
| 501 | |
| 502 AddInteger("home_mobile_country_code", radio_data.home_mobile_country_code, | |
| 503 body_object); | 349 body_object); |
| 504 AddInteger("home_mobile_network_code", radio_data.home_mobile_network_code, | 350 AddInteger(L"home_mobile_network_code", radio_data.home_mobile_network_code, |
| 505 body_object); | 351 body_object); |
| 506 AddString("radio_type", RadioTypeToString(radio_data.radio_type), | 352 AddString(L"radio_type", |
| 353 ASCIIToUTF16(RadioTypeToString(radio_data.radio_type)), |
| 507 body_object); | 354 body_object); |
| 508 AddString("carrier", radio_data.carrier, body_object); | 355 AddString(L"carrier", radio_data.carrier, body_object); |
| 509 | 356 |
| 510 Json::Value cell_towers; | 357 const int num_cell_towers = static_cast<int>(radio_data.cell_data.size()); |
| 511 assert(cell_towers.isArray()); | 358 if (num_cell_towers == 0) { |
| 512 int num_cell_towers = static_cast<int>(radio_data.cell_data.size()); | 359 return; |
| 360 } |
| 361 ListValue* cell_towers = new ListValue; |
| 513 for (int i = 0; i < num_cell_towers; ++i) { | 362 for (int i = 0; i < num_cell_towers; ++i) { |
| 514 Json::Value cell_tower; | 363 DictionaryValue* cell_tower = new DictionaryValue; |
| 515 assert(cell_tower.isObject()); | 364 AddInteger(L"cell_id", radio_data.cell_data[i].cell_id, cell_tower); |
| 516 AddInteger("cell_id", radio_data.cell_data[i].cell_id, &cell_tower); | 365 AddInteger(L"location_area_code", |
| 517 AddInteger("location_area_code", radio_data.cell_data[i].location_area_code, | 366 radio_data.cell_data[i].location_area_code, cell_tower); |
| 518 &cell_tower); | 367 AddInteger(L"mobile_country_code", |
| 519 AddInteger("mobile_country_code", | 368 radio_data.cell_data[i].mobile_country_code, cell_tower); |
| 520 radio_data.cell_data[i].mobile_country_code, &cell_tower); | 369 AddInteger(L"mobile_network_code", |
| 521 AddInteger("mobile_network_code", | 370 radio_data.cell_data[i].mobile_network_code, cell_tower); |
| 522 radio_data.cell_data[i].mobile_network_code, &cell_tower); | 371 AddInteger(L"age", radio_data.cell_data[i].age, cell_tower); |
| 523 AddInteger("age", radio_data.cell_data[i].age, &cell_tower); | 372 AddInteger(L"signal_strength", |
| 524 AddInteger("signal_strength", radio_data.cell_data[i].radio_signal_strength, | 373 radio_data.cell_data[i].radio_signal_strength, cell_tower); |
| 525 &cell_tower); | 374 AddInteger(L"timing_advance", radio_data.cell_data[i].timing_advance, |
| 526 AddInteger("timing_advance", radio_data.cell_data[i].timing_advance, | 375 cell_tower); |
| 527 &cell_tower); | 376 cell_towers->Append(cell_tower); |
| 528 cell_towers[i] = cell_tower; | 377 } |
| 529 } | 378 body_object->Set(L"cell_towers", cell_towers); |
| 530 if (num_cell_towers > 0) { | 379 } |
| 531 (*body_object)["cell_towers"] = cell_towers; | 380 |
| 532 } | 381 void AddWifiData(const WifiData& wifi_data, DictionaryValue* body_object) { |
| 533 } | 382 DCHECK(body_object); |
| 534 | |
| 535 static void AddWifiData(const WifiData &wifi_data, Json::Value *body_object) { | |
| 536 assert(body_object); | |
| 537 | 383 |
| 538 if (wifi_data.access_point_data.empty()) { | 384 if (wifi_data.access_point_data.empty()) { |
| 539 return; | 385 return; |
| 540 } | 386 } |
| 541 | 387 |
| 542 Json::Value wifi_towers; | 388 ListValue* wifi_towers = new ListValue; |
| 543 assert(wifi_towers.isArray()); | |
| 544 for (WifiData::AccessPointDataSet::const_iterator iter = | 389 for (WifiData::AccessPointDataSet::const_iterator iter = |
| 545 wifi_data.access_point_data.begin(); | 390 wifi_data.access_point_data.begin(); |
| 546 iter != wifi_data.access_point_data.end(); | 391 iter != wifi_data.access_point_data.end(); |
| 547 iter++) { | 392 iter++) { |
| 548 Json::Value wifi_tower; | 393 DictionaryValue* wifi_tower = new DictionaryValue; |
| 549 assert(wifi_tower.isObject()); | 394 AddString(L"mac_address", iter->mac_address, wifi_tower); |
| 550 AddString("mac_address", iter->mac_address, &wifi_tower); | 395 AddInteger(L"signal_strength", iter->radio_signal_strength, wifi_tower); |
| 551 AddInteger("signal_strength", iter->radio_signal_strength, &wifi_tower); | 396 AddInteger(L"age", iter->age, wifi_tower); |
| 552 AddInteger("age", iter->age, &wifi_tower); | 397 AddInteger(L"channel", iter->channel, wifi_tower); |
| 553 AddInteger("channel", iter->channel, &wifi_tower); | 398 AddInteger(L"signal_to_noise", iter->signal_to_noise, wifi_tower); |
| 554 AddInteger("signal_to_noise", iter->signal_to_noise, &wifi_tower); | 399 AddString(L"ssid", iter->ssid, wifi_tower); |
| 555 AddString("ssid", iter->ssid, &wifi_tower); | 400 wifi_towers->Append(wifi_tower); |
| 556 wifi_towers.append(wifi_tower); | 401 } |
| 557 } | 402 body_object->Set(L"wifi_towers", wifi_towers); |
| 558 (*body_object)["wifi_towers"] = wifi_towers; | 403 } |
| 559 } | 404 } // namespace |
| 560 | |
| 561 #endif // if 0 | |
| OLD | NEW |