update_engine-DownloadAction(一)
通过update_engine-整体结构(一),(二),(三)对update_engine整体的运行机制有了一定的认识之后。开始逐个分析重要的Action。先从DownloadAction开始分析。
开始分析DownloadAction
src/update_engine/payload_consumer/download_action.cc
DownloadAction::DownloadAction(PrefsInterface* prefs,
BootControlInterface* boot_control,
HardwareInterface* hardware,
SystemState* system_state,
HttpFetcher* http_fetcher)
: prefs_(prefs),
boot_control_(boot_control),
hardware_(hardware),
system_state_(system_state),
http_fetcher_(new MultiRangeHttpFetcher(http_fetcher)), //MultiRangeHttpFetcher也继承了HttpFetcher,实现了HttpFetcherDelegate
writer_(nullptr), code_(ErrorCode::kSuccess), delegate_(nullptr), p2p_sharing_fd_(-), p2p_visible_(true) {}
在构造方法中,system_state为nullptr。
接着看PerformAction()
void DownloadAction::PerformAction() {
http_fetcher_->set_delegate(this); //http_fetcher_是MultiRangeHttpFetcher // Get the InstallPlan and read it
CHECK(HasInputObject()); //检查是否有输入管道
install_plan_ = GetInputObject(); //获取InstallPlanAction输出的install_plan_
install_plan_.Dump(); bytes_received_ = ;
bytes_total_ = ;
for (const auto& payload : install_plan_.payloads)
bytes_total_ += payload.size; //计算payload的总大小 if (install_plan_.is_resume) { //检查是否进行恢复更新
int64_t payload_index = ;
if (prefs_->GetInt64(kPrefsUpdateStatePayloadIndex, &payload_index) && //获取需要继续更新的payload的索引
static_cast<size_t>(payload_index) < install_plan_.payloads.size()) {
// Save the index for the resume payload before downloading any previous
// payload, otherwise it will be overwritten.
resume_payload_index_ = payload_index;
for (int i = ; i < payload_index; i++)
install_plan_.payloads[i].already_applied = true; //获取到了索引后说明在该索引之前的都已经应用过了
}
}
// TODO(senj): check that install plan has at least one payload.
if (!payload_) //payload_为InstallPlan::Payload
payload_ = &install_plan_.payloads[]; //默认的payload_ LOG(INFO) << "Marking new slot as unbootable"; //将target_slot标记为unboot状态
if (!boot_control_->MarkSlotUnbootable(install_plan_.target_slot)) {
LOG(WARNING) << "Unable to mark new slot "
<< BootControlInterface::SlotName(install_plan_.target_slot)
<< ". Proceeding with the update anyway.";
} StartDownloading(); //开始下载
}
在PerformAction()中首先是获取InstallPlan,对resume_payload_index_,payload_进行恢复,之后设置target_slot为unboot,最后开始downloading。
void DownloadAction::StartDownloading() {
download_active_ = true;
http_fetcher_->ClearRanges();
if (install_plan_.is_resume && //判断是否需要恢复更新
payload_ == &install_plan_.payloads[resume_payload_index_]) {
// Resuming an update so fetch the update manifest metadata first.
int64_t manifest_metadata_size = ;
int64_t manifest_signature_size = ;
prefs_->GetInt64(kPrefsManifestMetadataSize, &manifest_metadata_size);
prefs_->GetInt64(kPrefsManifestSignatureSize, &manifest_signature_size);
http_fetcher_->AddRange(base_offset_,
manifest_metadata_size + manifest_signature_size);
// If there're remaining unprocessed data blobs, fetch them. Be careful not
// to request data beyond the end of the payload to avoid 416 HTTP response
// error codes.
int64_t next_data_offset = ;
prefs_->GetInt64(kPrefsUpdateStateNextDataOffset, &next_data_offset);
uint64_t resume_offset =
manifest_metadata_size + manifest_signature_size + next_data_offset;
if (!payload_->size) {
http_fetcher_->AddRange(base_offset_ + resume_offset);
} else if (resume_offset < payload_->size) {
http_fetcher_->AddRange(base_offset_ + resume_offset,
payload_->size - resume_offset);
}
} else {
if (payload_->size) { //如果payload->size不为0
http_fetcher_->AddRange(base_offset_, payload_->size); //设置下载数据的offset和length。
} else {
// If no payload size is passed we assume we read until the end of the
// stream.
http_fetcher_->AddRange(base_offset_); //设置下载数据的offset
}
} if (writer_ && writer_ != delta_performer_.get()) { //对writer_进行初始化,writer_比较重要后面会进行详细的介绍
LOG(INFO) << "Using writer for test.";
} else {
delta_performer_.reset(new DeltaPerformer(
prefs_, boot_control_, hardware_, delegate_, &install_plan_, payload_));
writer_ = delta_performer_.get();
}
if (system_state_ != nullptr) { //在这里system_state为nullptr后面就不在进行分析
const PayloadStateInterface* payload_state = system_state_->payload_state();
string file_id = utils::CalculateP2PFileId(payload_->hash, payload_->size);
if (payload_state->GetUsingP2PForSharing()) {
// If we're sharing the update, store the file_id to convey
// that we should write to the file.
p2p_file_id_ = file_id;
LOG(INFO) << "p2p file id: " << p2p_file_id_;
} else {
// Even if we're not sharing the update, it could be that
// there's a partial file from a previous attempt with the same
// hash. If this is the case, we NEED to clean it up otherwise
// we're essentially timing out other peers downloading from us
// (since we're never going to complete the file).
FilePath path = system_state_->p2p_manager()->FileGetPath(file_id);
if (!path.empty()) {
if (unlink(path.value().c_str()) != ) {
PLOG(ERROR) << "Error deleting p2p file " << path.value();
} else {
LOG(INFO) << "Deleting partial p2p file " << path.value()
<< " since we're not using p2p to share.";
}
}
} // Tweak timeouts on the HTTP fetcher if we're downloading from a
// local peer.
if (payload_state->GetUsingP2PForDownloading() &&
payload_state->GetP2PUrl() == install_plan_.download_url) {
LOG(INFO) << "Tweaking HTTP fetcher since we're downloading via p2p";
http_fetcher_->set_low_speed_limit(kDownloadP2PLowSpeedLimitBps,
kDownloadP2PLowSpeedTimeSeconds);
http_fetcher_->set_max_retry_count(kDownloadP2PMaxRetryCount);
http_fetcher_->set_connect_timeout(kDownloadP2PConnectTimeoutSeconds);
}
} http_fetcher_->BeginTransfer(install_plan_.download_url); //开始进行下载
}
先不分析恢复更新这部分的操作,可以看到里面的操作几乎都是在获取中断更新时保存的数据。所以当我们明白了所保存的数据都有什么意义的时候,也就明白了这部分的操作,我们需要从头开始分析。之后是system_state_其实是nullptr所以这部分暂时不进行分析。这样排除完之后,我们需要关注的就是http_fetcher_和writer_,先对这两个类进行一个简单的分析。先看MultiRangeHttpFetcher,下面是部分的代码
src/system/update_engine/common/multi_range_http_fetcher.h
class MultiRangeHttpFetcher : public HttpFetcher, public HttpFetcherDelegate {
public:
// Takes ownership of the passed in fetcher.
explicit MultiRangeHttpFetcher(HttpFetcher* base_fetcher)
: HttpFetcher(base_fetcher->proxy_resolver()),
base_fetcher_(base_fetcher),
base_fetcher_active_(false),
pending_transfer_ended_(false),
terminating_(false),
current_index_(),
bytes_received_this_range_() {}
~MultiRangeHttpFetcher() override {} void ClearRanges() { ranges_.clear(); } void AddRange(off_t offset, size_t size) {
CHECK_GT(size, static_cast<size_t>());
ranges_.push_back(Range(offset, size));
} void AddRange(off_t offset) {
ranges_.push_back(Range(offset));
} private:
class Range {
public:
Range(off_t offset, size_t length) : offset_(offset), length_(length) {}
explicit Range(off_t offset) : offset_(offset), length_() {} inline off_t offset() const { return offset_; }
inline size_t length() const { return length_; } inline bool HasLength() const { return (length_ > ); } std::string ToString() const; private:
off_t offset_;
size_t length_;
};
};
可以看到MultiRangeHttpFetcher继承了HttpFetcher,实现了 HttpFetcherDelegate并且还保存了一个base_fetcher_。在它的内部还定义了一个内部类Range,主要就是表示所要下载数据的偏移量和长度。
而DeltaPerformer继承自FileWriter,同时重写了Writer方法,这个方法比较重要,可以说是升级的核心方法,在后面会做详细的介绍。
接着看http_fetcher_->BeginTransfer(install_plan_.download_url)。注意是 MultiRangeHttpFetcher的BeginTransfer方法。
src/system/update_engine/common/multi_range_http_fetcher.cc
void MultiRangeHttpFetcher::BeginTransfer(const std::string& url) {
CHECK(!base_fetcher_active_) << "BeginTransfer but already active.";
CHECK(!pending_transfer_ended_) << "BeginTransfer but pending.";
CHECK(!terminating_) << "BeginTransfer but terminating."; if (ranges_.empty()) {
// Note that after the callback returns this object may be destroyed.
if (delegate_)
delegate_->TransferComplete(this, true); //DownloadAction的TransferComplete
return;
}
url_ = url;
current_index_ = ;
bytes_received_this_range_ = ;
LOG(INFO) << "starting first transfer";
base_fetcher_->set_delegate(this); //为FileFetcher设置delegate
StartTransfer();
} // State change: Stopped or Downloading -> Downloading
void MultiRangeHttpFetcher::StartTransfer() {
if (current_index_ >= ranges_.size()) {
return;
} Range range = ranges_[current_index_];
LOG(INFO) << "starting transfer of range " << range.ToString(); bytes_received_this_range_ = ;
base_fetcher_->SetOffset(range.offset());
if (range.HasLength())
base_fetcher_->SetLength(range.length());
else
base_fetcher_->UnsetLength();
if (delegate_)
delegate_->SeekToOffset(range.offset());
base_fetcher_active_ = true;
base_fetcher_->BeginTransfer(url_);
}
在 BeginTransfer这个方法中:
1. 判断ranges是否为空,如果为空则认为已经下载完成回调DownloadAction的TransferComplete
2. 为base_fetcher_设置delegate
3. 调用StartTransfer()
在StartTransger()中,根据current_index_获取到Range,再设置base_fetcher_的offset和length,之后调用base_fetcher_->BeginTransfer(url_);开始进行正式的下载。那么FileFetcher的BeginTransfer都做了些什么?
src/system/update_engine/common/file_fether.cc
void FileFetcher::BeginTransfer(const string& url) {
CHECK(!transfer_in_progress_); if (!SupportedUrl(url)) { //检查是否是file:///协议
LOG(ERROR) << "Unsupported file URL: " << url;
// No HTTP error code when the URL is not supported.
http_response_code_ = ;
CleanUp();
if (delegate_)
delegate_->TransferComplete(this, false); //delgate_是MultiRangeHttpFetcher
return;
} string file_path = url.substr(strlen("file://"));
stream_ =
brillo::FileStream::Open(base::FilePath(file_path), //打开file_path指向的升级文件
brillo::Stream::AccessMode::READ,
brillo::FileStream::Disposition::OPEN_EXISTING,
nullptr); if (!stream_) {
LOG(ERROR) << "Couldn't open " << file_path;
http_response_code_ = kHttpResponseNotFound;
CleanUp();
if (delegate_)
delegate_->TransferComplete(this, false);
return;
}
http_response_code_ = kHttpResponseOk; if (offset_) //设置读取的位置
stream_->SetPosition(offset_, nullptr);
bytes_copied_ = ; //已经copy的字节,也就是下载了多少字节了
transfer_in_progress_ = true;
ScheduleRead();
} void FileFetcher::ScheduleRead() {
if (transfer_paused_ || ongoing_read_ || !transfer_in_progress_)
return; buffer_.resize(kReadBufferSize); //设置buffer_缓存区的大小
size_t bytes_to_read = buffer_.size(); //设置读取数据的数量
if (data_length_ >= ) {
bytes_to_read = std::min(static_cast<uint64_t>(bytes_to_read),
data_length_ - bytes_copied_); //剩下的数据量,bytes_to_read哪一个小哪一个就是将要读取的数据量
} if (!bytes_to_read) { //没有可读取的数据了,说明已经下载完了
OnReadDoneCallback();
return;
} ongoing_read_ = stream_->ReadAsync( //开始下载数据
buffer_.data(),
bytes_to_read,
base::Bind(&FileFetcher::OnReadDoneCallback, base::Unretained(this)),
base::Bind(&FileFetcher::OnReadErrorCallback, base::Unretained(this)),
nullptr); if (!ongoing_read_) {
LOG(ERROR) << "Unable to schedule an asynchronous read from the stream.";
CleanUp();
if (delegate_)
delegate_->TransferComplete(this, false);
}
} void FileFetcher::OnReadDoneCallback(size_t bytes_read) {
ongoing_read_ = false;
if (bytes_read == ) { //判读数据是否已经被下载完成了
CleanUp();
if (delegate_)
delegate_->TransferComplete(this, true);
} else {
bytes_copied_ += bytes_read;
if (delegate_)
delegate_->ReceivedBytes(this, buffer_.data(), bytes_read); //调用MultiRangeHttpFetcher的ReceivedBytes
ScheduleRead();
}
}
这几个方法比较简单,主要就是打开文件,下载数据就是把数据保存到buffer_中,通过回调向外传递数据直到下载完成。
接下来看MultiRangeHttpFetcher的ReceivedBytes
void MultiRangeHttpFetcher::ReceivedBytes(HttpFetcher* fetcher,
const void* bytes,
size_t length) {
CHECK_LT(current_index_, ranges_.size());
CHECK_EQ(fetcher, base_fetcher_.get());
CHECK(!pending_transfer_ended_);
size_t next_size = length;
Range range = ranges_[current_index_];
if (range.HasLength()) {
next_size = std::min(next_size,
range.length() - bytes_received_this_range_); //获取接收到数据的长度
}
LOG_IF(WARNING, next_size <= ) << "Asked to write length <= 0";
if (delegate_) {
delegate_->ReceivedBytes(this, bytes, next_size); //delegate_是DownloadAction
}
bytes_received_this_range_ += length; //更新已经接收到数据的长度
if (range.HasLength() && bytes_received_this_range_ >= range.length()) { //如果已经下载完了
// Terminates the current fetcher. Waits for its TransferTerminated
// callback before starting the next range so that we don't end up
// signalling the delegate that the whole multi-transfer is complete
// before all fetchers are really done and cleaned up.
pending_transfer_ended_ = true;
LOG(INFO) << "terminating transfer";
fetcher->TerminateTransfer();
}
}
这个方法其实主要就是向外传递,由DownloadAction的ReceivedBytes来进行执行。
void DownloadAction::ReceivedBytes(HttpFetcher* fetcher,
const void* bytes,
size_t length) {
// Note that bytes_received_ is the current offset.
if (!p2p_file_id_.empty()) {
WriteToP2PFile(bytes, length, bytes_received_);
} bytes_received_ += length;
if (delegate_ && download_active_) {
delegate_->BytesReceived(length, bytes_received_, bytes_total_); //delegate_是UpdateAttempterAndroid
}
if (writer_ && !writer_->Write(bytes, length, &code_)) {
if (code_ != ErrorCode::kSuccess) {
LOG(ERROR) << "Error " << utils::ErrorCodeToString(code_) << " (" << code_
<< ") in DeltaPerformer's Write method when "
<< "processing the received payload -- Terminating processing";
}
// Delete p2p file, if applicable.
if (!p2p_file_id_.empty())
CloseP2PSharingFd(true);
// Don't tell the action processor that the action is complete until we get
// the TransferTerminated callback. Otherwise, this and the HTTP fetcher
// objects may get destroyed before all callbacks are complete.
TerminateProcessing();
return;
} // Call p2p_manager_->FileMakeVisible() when we've successfully
// verified the manifest!
if (!p2p_visible_ && system_state_ && delta_performer_.get() &&
delta_performer_->IsManifestValid()) {
LOG(INFO) << "Manifest has been validated. Making p2p file visible.";
system_state_->p2p_manager()->FileMakeVisible(p2p_file_id_);
p2p_visible_ = true;
}
}
p2p_file_id_是在system_state不为nullptr时为其进行初始化,所以这里的p2p_file_id_和system_state相关的操作目前不用关心。在这个方法中其实主要就是向外继续传递BytesReceived,传递给了UpdateAttempterAndroid。在UpdateAttempterAndroid中的BytesReceived操作也比较简单主要就是更新了下载数据的进度。在更新完成进度之后就会调用DeltaPerformer的Write方法。该方法比较重要,在update_engine-DownloadAction(二)将会进行单独的介绍。
update_engine-DownloadAction(一)的更多相关文章
- JavaWeb -- Struts1 多文件上传与下载 DownloadAction, DispatchAction
1. 多文件上传与下载 上传下载jsp: <%@ page language="java" import="java.util.*" pageEncodi ...
- update_engine-FilesystemVerifierAction和PostinstallRunnerAction
在介绍完了DownloadAction之后,还剩下FilesystemVerifierAction和PostinstallRunnerAction,下面开始对其进行分析. FilesystemVeri ...
- update_engine-DownloadAction(二)
在update_engine-DownloadAction(一)中对DownloadAction介绍到了DeltaPerformer的Write方法.下面开始介绍Write方法. src/system ...
- update_engine-整体结构(三)
在update_engine-整体结构(二)中分析到了Action,那么我们接着继续分析. 首先来看一下BuildUpdateActons(...)这个方法. src/system/update_en ...
- update_engine-整体结构(二)
在update_engine-整体结构(一)中分析UpdateEngineDaemon::OnInit()的整体情况.下面先分析在该方法中涉及的DaemonStateAndroid和BinderUpd ...
- 11、Struts2 的文件上传和下载
文件上传 表单准备 要想使用 HTML 表单上传一个或多个文件 须把 HTML 表单的 enctype 属性设置为 multipart/form-data 须把 HTML 表单的method 属性设置 ...
- Struts2学习笔记--使用Response下载文件和Struts2的StreamResult文件下载
使用Response下载文件,servlet中的文件下载是通过流来实现的 我在webRoot文件夹下新建了一个文件夹from,里边放了一张图片,这里就以下载这张图片为例:download.jsp很 ...
- Structs框架
一.准备工作及实例 1.解压struts-2.1.6-all.zip(structs网上下载) apps目录:struts2自带的例子程序 docs目录:官方文档. lib 目录:存放所有jar文件. ...
- struts 文件下载
=============================struts 文件下载 ================================== 步骤一: JSP页面 <a href=& ...
随机推荐
- JavaScript里面的arguments到底是个啥?
类数组对象:arguments 总所周知,js是一门相当灵活的语言.当我们在js中在调用一个函数的时候,我们经常会给这个函数传递一些参数,js把传入到这个函数的全部参数存储在一个叫做arguments ...
- 马凯军201771010116《面向对象与程序设计Java》
实验十八 总复习 实验时间 2018-12-30 1.实验目的与要求 (1) 综合掌握java基本程序结构: (2) 综合掌握java面向对象程序设计特点: (3) 综合掌握java GUI 程序设 ...
- 十分钟搞定 pandas
原文:http://pandas.pydata.org/pandas-docs/stable/10min.html 译者:ChaoSimple 校对:飞龙 官方网站上<10 Minutes to ...
- String与StringBuilder 区别
string 是不可变字符串.一旦创建不可修改,使用Insert.Remove.PadLeft.Replace.Splict等,返回都是新的字符串对象. StringBuilder 是可变字符串,大多 ...
- python学习笔记——(一)基础设置
python的学习,今天就开始上开发环境Pycharm,这样以后在调试,使用和学习起来就方便很多. 我用的是JetBrains PyCharm Community Edition 2018.1.1 x ...
- oracle 修改服务端字符集编码
进入服务端的sqlplus命令界面 SELECT * FROM V$NLS_PARAMETERS; 可以查看参数的值. 解决字符集编码 NLS_CHARACTERSET 办法: UPDATE PROP ...
- jquery 上滑加载更多
$(document).ready(function() { var totalPage = {$totalPage};//总页数 var page = {$page}; //起始页 var page ...
- css尺寸(大小)属性
尺寸属性:用来控制元素大小的属性,单位为长度单位. 尺寸属性的使用场景 当使用相对长度单位定义尺寸时,元素的大小跟随窗口大小变化. 为保证元素的正常显示,需要设定元素的最大.最小长度. 手机端开发时需 ...
- VIM:Found a swap file by the name
在linux下用vi或vim打开Test.java文件时 [root@localhost tmp]# vi Test.java出现了如下信息: E325: ATTENTION Found a s ...
- 科学计算库Numpy(1)
Numpy 一,数据结构 数据类型: ndarray import numpy world_alchol = numpy.genfromtxt('world_alchol.txt',delimiter ...