| // |
| // Copyright (C) 2012 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| #include "update_engine/omaha_request_action.h" |
| |
| #include <inttypes.h> |
| |
| #include <map> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #include <base/bind.h> |
| #include <base/logging.h> |
| #include <base/rand_util.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/string_split.h> |
| #include <base/strings/stringprintf.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/time/time.h> |
| #include <expat.h> |
| #include <metrics/metrics_library.h> |
| |
| #include "update_engine/common/action_pipe.h" |
| #include "update_engine/common/constants.h" |
| #include "update_engine/common/hardware_interface.h" |
| #include "update_engine/common/hash_calculator.h" |
| #include "update_engine/common/platform_constants.h" |
| #include "update_engine/common/prefs_interface.h" |
| #include "update_engine/common/utils.h" |
| #include "update_engine/connection_manager_interface.h" |
| #include "update_engine/metrics.h" |
| #include "update_engine/metrics_utils.h" |
| #include "update_engine/p2p_manager.h" |
| #include "update_engine/payload_state_interface.h" |
| #include "update_engine/payload_properties_request_action.h" |
| |
| #define HAS_KEY(x, y) ((x).find((y)) != (x).end()) |
| |
| namespace chromeos_update_engine { |
| |
| using std::string; |
| using std::pair; |
| using std::vector; |
| |
| PayloadPropertiesRequestAction::PayloadPropertiesRequestAction( |
| SystemState* system_state, |
| PayloadProperties *properties, |
| HttpFetcher *http_fetcher) |
| : system_state_(system_state), |
| properties_(properties), |
| http_fetcher_(http_fetcher) |
| { |
| } |
| |
| PayloadPropertiesRequestAction::~PayloadPropertiesRequestAction() {} |
| |
| void PayloadPropertiesRequestAction::PerformAction() { |
| http_fetcher_->set_delegate(this); |
| |
| // we support only one URL for now |
| CHECK(properties_->payload_urls.size() == 1); |
| |
| LOG(INFO) << "URL: " << properties_->payload_urls[0]; |
| http_fetcher_->BeginTransfer(properties_->payload_urls[0]); |
| } |
| |
| void PayloadPropertiesRequestAction::TerminateProcessing() { |
| http_fetcher_->TerminateTransfer(); |
| } |
| |
| // We just store the response in the buffer. Once we've received all bytes, |
| // we'll look in the buffer and decide what to do. |
| void PayloadPropertiesRequestAction::ReceivedBytes(HttpFetcher *fetcher, |
| const void* bytes, |
| size_t length) { |
| const uint8_t* byte_ptr = reinterpret_cast<const uint8_t*>(bytes); |
| response_buffer_.insert(response_buffer_.end(), byte_ptr, byte_ptr + length); |
| } |
| |
| // If the transfer was successful, this uses expat to parse the response |
| // and fill in the appropriate fields of the output object. Also, notifies |
| // the processor that we're done. |
| void PayloadPropertiesRequestAction::TransferComplete(HttpFetcher *fetcher, |
| bool successful) { |
| ScopedActionCompleter completer(processor_, this); |
| |
| if (!successful) { |
| LOG(ERROR) << "Payload properties network transfer failed."; |
| int code = GetHTTPResponseCode(); |
| // Makes sure we send sane error values. |
| if (code < 0 || code >= 1000) { |
| code = 999; |
| } |
| completer.set_code(static_cast<ErrorCode>( |
| static_cast<int>(ErrorCode::kHTTPResponseBase) + code)); |
| return; |
| } |
| |
| auto prop_end = std::find(response_buffer_.begin(), response_buffer_.end(), 0); |
| vector<pair<string, string>> kv_pairs; |
| std::map<string, string> kv_map; |
| if (!base::SplitStringIntoKeyValuePairs(string(response_buffer_.begin(), prop_end), |
| ' ', '\n', &kv_pairs)) |
| { |
| LOG(ERROR) << "Can't parse Payload properties"; |
| return; |
| } |
| std::copy(kv_pairs.begin(), kv_pairs.end(), |
| std::inserter(kv_map, kv_map.begin())); |
| |
| if (!HAS_KEY(kv_map, "FILE_SIZE:") || |
| !base::StringToInt64(kv_map.at("FILE_SIZE:"), &properties_->size)) |
| { |
| LOG(ERROR) << "Can't get FILE_SIZE from Payload properties: "; |
| return; |
| } |
| if (!HAS_KEY(kv_map, "METADATA_SIZE:") || |
| !base::StringToInt64(kv_map.at("METADATA_SIZE:"), &properties_->metadata_size)) |
| { |
| LOG(ERROR) << "Can't get METADATA_SIZE from Payload properties: "; |
| return; |
| } |
| if (!HAS_KEY(kv_map, "FILE_HASH:")) |
| { |
| LOG(ERROR) << "Can't get FILE_HASH from Payload properties: "; |
| return; |
| } |
| properties_->hash = kv_map["FILE_HASH:"]; |
| if (!HAS_KEY(kv_map, "Larry-app-version:")) |
| { |
| LOG(ERROR) << "Can't get Larry-app-version from Payload properties: "; |
| return; |
| } |
| properties_->version = kv_map["Larry-app-version:"]; |
| |
| properties_->update_exists = true; |
| |
| system_state_->payload_state()->SetResponse(*properties_); |
| |
| if (HasOutputPipe()) |
| SetOutputObject(*properties_); |
| completer.set_code(ErrorCode::kSuccess); |
| |
| // TODO add more properties to be able to recover update after crash or reboot |
| } |
| |
| void PayloadPropertiesRequestAction::ActionCompleted(ErrorCode code) |
| { |
| metrics::CheckResult result = metrics::CheckResult::kUnset; |
| metrics::CheckReaction reaction = metrics::CheckReaction::kUnset; |
| metrics::DownloadErrorCode download_error_code = |
| metrics::DownloadErrorCode::kUnset; |
| |
| // Regular update attempt. |
| switch (code) { |
| case ErrorCode::kSuccess: |
| // OK, we parsed the response successfully but that does |
| // necessarily mean that an update is available. |
| if (HasOutputPipe()) { |
| result = metrics::CheckResult::kUpdateAvailable; |
| reaction = metrics::CheckReaction::kUpdating; |
| } else { |
| result = metrics::CheckResult::kNoUpdateAvailable; |
| } |
| break; |
| |
| default: |
| // We report two flavors of errors, "Download errors" and "Parsing |
| // error". Try to convert to the former and if that doesn't work |
| // we know it's the latter. |
| metrics::DownloadErrorCode tmp_error = |
| metrics_utils::GetDownloadErrorCode(code); |
| if (tmp_error != metrics::DownloadErrorCode::kInputMalformed) { |
| result = metrics::CheckResult::kDownloadError; |
| download_error_code = tmp_error; |
| } else { |
| result = metrics::CheckResult::kParsingError; |
| } |
| break; |
| } |
| |
| metrics::ReportUpdateCheckMetrics(system_state_, |
| result, reaction, download_error_code); |
| } |
| |
| } // namespace chromeos_update_engine |