blob: cf0aa6cf7465ddbac01490d9bde7abbf8a0881ca [file] [log] [blame]
//
// 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