Android P update_engine分析--升级核心DeltaPerformer的分析

Posted Give.Me.Five

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android P update_engine分析--升级核心DeltaPerformer的分析相关的知识,希望对你有一定的参考价值。

前面几篇分析了update_engine的启动update_engine_client的使用boot_control的AB切换,以及update_engine的4个Action,但始终没有看到核心的部分,如何将payload.bin如何下载到目标分区的。之前一直以为是在PostinstallRunnerAction的做的,后面发现错了,升级包里根本没有Postinstall脚本。重新分析之后发现,核心的升级操作都在DownloadAction 里做的,从而牵扯出一个核心的升级类DeltaPerformer。那我们还是从我们之前分析漏了的DownloadAction内容里开始。

Writer的初始化和使用

在DownloadAction的StartDonwloading中,已经初始化了Delta_performer_和wrter_。

void DownloadAction::StartDownloading() 
...
  if (writer_ && writer_ != delta_performer_.get()) 
    LOG(INFO) << "Using writer for test.";
   else 
    //初始化delta_performer_ 和 writer_
    delta_performer_.reset(new DeltaPerformer(prefs_,
                                              boot_control_,
                                              hardware_,
                                              delegate_,
                                              &install_plan_,
                                              payload_,
                                              is_interactive_));
    writer_ = delta_performer_.get();
  
  ...
   //开始下载
   http_fetcher_->BeginTransfer(install_plan_.download_url);


void UpdateAttempterandroid::BuildUpdateActions(const string& url) 
...
//初始化HtttpFetcher,使用FileFetcher 还是LibcurlHttpFetcher
  HttpFetcher* download_fetcher = nullptr;
  if (FileFetcher::SupportedUrl(url)) 
    DLOG(INFO) << "Using FileFetcher for file URL.";
    download_fetcher = new FileFetcher();
   else 
#ifdef _UE_SIDELOAD
    LOG(FATAL) << "Unsupported sideload URI: " << url;
#else
    LibcurlHttpFetcher* libcurl_fetcher =
        new LibcurlHttpFetcher(&proxy_resolver_, hardware_);
    libcurl_fetcher->set_server_to_check(ServerToCheck::kDownload);
    download_fetcher = libcurl_fetcher;
#endif  // _UE_SIDELOAD
  
  shared_ptr<DownloadAction> download_action(
      new DownloadAction(prefs_,
                         boot_control_,
                         hardware_,
                         nullptr,           // system_state, not used.
                         download_fetcher,  // passes ownership
                         true /* is_interactive */));
 ...

bool FileFetcher::SupportedUrl(const string& url) 
  // check url是不是以file:///开头
  return base::StartsWith(
      url, "file:///", base::CompareCase::INSENSITIVE_ASCII);

因为我们升级更多的是将升级包下载后,使用U盘或SD卡在安装,调用的方式一般使用file:///开头的,所以download_fetcher使用的是FileFetcher类。同步download_action的初始化也是用FileFetcher初始化,那我们继续分析FileFetcher.BeginTransfer(…)。

FileFetcher传输


// Begins the transfer, which must not have already been started.
void FileFetcher::BeginTransfer(const string& url) 
  CHECK(!transfer_in_progress_);
 //再次确认是不是以file:///开头的
  if (!SupportedUrl(url)) 
    LOG(ERROR) << "Unsupported file URL: " << url;
    // No HTTP error code when the URL is not supported.
    http_response_code_ = 0;
    CleanUp();
    if (delegate_)
      delegate_->TransferComplete(this, false);
    return;
  
//从指定路径使用文件流以只读的方式打开
  string file_path = url.substr(strlen("file://"));
  stream_ =
      brillo::FileStream::Open(base::FilePath(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;
//如果有设置offset,就从指定offset开始
  if (offset_)
    stream_->SetPosition(offset_, nullptr);
  bytes_copied_ = 0;
  transfer_in_progress_ = true;
  ScheduleRead();


void FileFetcher::ScheduleRead() 
//检查状态
  if (transfer_paused_ || ongoing_read_ || !transfer_in_progress_)
    return;
//设置此次buffer读取的大小,默认为16K
  buffer_.resize(kReadBufferSize);
  size_t bytes_to_read = buffer_.size();
  if (data_length_ >= 0) 
    bytes_to_read = std::min(static_cast<uint64_t>(bytes_to_read),
                             data_length_ - bytes_copied_);
  
//检查bytes_to_read是否合法
  if (!bytes_to_read) 
    OnReadDoneCallback(0);
    return;
  
//从文件流中开始读取bytes_to_read字节,同时设置读取完成回调和读取出错回调
  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);
  



使用文件流的方式打开升级包文件,使用buffer作为下载的接收缓存,然后开始下载,每次默认下载16K,下载完成之后就会回调onReadDoneCallback。


void FileFetcher::OnReadDoneCallback(size_t bytes_read) 
  ongoing_read_ = false;
  //如果读取数据为0,清除下载现场,同时完成传输
  if (bytes_read == 0) 
    CleanUp();
    if (delegate_)
      delegate_->TransferComplete(this, true);
   else 
  //如果有设置delegate回调,就使用回调,调用ReceivedBytes处理buffer数据
    bytes_copied_ += bytes_read;
    if (delegate_)
      delegate_->ReceivedBytes(this, buffer_.data(), bytes_read);
    ScheduleRead();
  

void DownloadAction::PerformAction() 
  http_fetcher_->set_delegate(this);
  ...

在DownloadAction的PerformAction中,已经设置了delegate回调,将DownloadAction对象本身做为回调delegate设置给FileFetcher。上面的ReceivedBytes调用就是调用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;
  uint64_t bytes_downloaded_total =
      bytes_received_previous_payloads_ + bytes_received_;
  //updateAttermpterAndroid也设置了downloadAction的delegate_,同时也会回调updateAttermpterAndroid的BytesReceived
  if (delegate_ && download_active_) 
    delegate_->BytesReceived(length, bytes_downloaded_total, bytes_total_);
  
  //调用DeltaPerformer的Write,这个是重点
  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";
    
  ....

//DownloadAction 回调UpdateAttermpterAndroid的BytesReceived,上报进度
void UpdateAttempterAndroid::BytesReceived(uint64_t bytes_progressed,
                                           uint64_t bytes_received,
                                           uint64_t total) 
  double progress = 0;
  if (total)
    progress = static_cast<double>(bytes_received) / static_cast<double>(total);
  if (status_ != UpdateStatus::DOWNLOADING || bytes_received == total) 
    download_progress_ = progress;
    SetStatusAndNotify(UpdateStatus::DOWNLOADING);
   else 
    //更新进度,同时将进度上报给上层调用者
    ProgressUpdate(progress);
  

  // 更新进度到sharePerferences的文件中
  int64_t current_bytes_downloaded =
      metrics_utils::GetPersistedValue(kPrefsCurrentBytesDownloaded, prefs_);
  int64_t total_bytes_downloaded =
      metrics_utils::GetPersistedValue(kPrefsTotalBytesDownloaded, prefs_);
  prefs_->SetInt64(kPrefsCurrentBytesDownloaded,
                   current_bytes_downloaded + bytes_progressed);
  prefs_->SetInt64(kPrefsTotalBytesDownloaded,
                   total_bytes_downloaded + bytes_progressed);


DownloadAction的BytesReceived 一方面回调给UpdateAttempterAndroid的BytesReceived,将升级进度上报给上层调用者,同时将数据传给DeltaPerformer的write。 那这个函数是不是实际的升级动作呢,我们继续往下分析:


// Wrapper around write. Returns true if all requested bytes
// were written, or false on any error, regardless of progress
// and stores an action exit code in |error|.
bool DeltaPerformer::Write(const void* bytes, size_t count, ErrorCode *error) 
  *error = ErrorCode::kSuccess;
  const char* c_bytes = reinterpret_cast<const char*>(bytes);

  //更新下载的总数
  total_bytes_received_ += count;
  UpdateOverallProgress(false, "Completed ");

  //当manifest_vaild 为false,即无效时,如果manifest和metadata 还没有下载,就先下载manifest和metadata数据
  while (!manifest_valid_) 
    // Read data up to the needed limit; this is either maximium payload header
    // size, or the full metadata size (once it becomes known).
    const bool do_read_header = !IsHeaderParsed();
    //拷贝数据到Buffer中去
    CopyDataToBuffer(&c_bytes, &count,
                     (do_read_header ? kMaxPayloadHeaderSize :
                      metadata_size_ + metadata_signature_size_));
   //解析metadata,并并检查是否有错
    MetadataParseResult result = ParsePayloadMetadata(buffer_, error);
    if (result == MetadataParseResult::kError)
      return false;
    if (result == MetadataParseResult::kInsufficientData) 
      // If we just processed the header, make an attempt on the manifest.
      if (do_read_header && IsHeaderParsed())
        continue;

      return true;
    

    // 检查Manifest是否有效
    if ((*error = ValidateManifest()) != ErrorCode::kSuccess)
      return false;
    manifest_valid_ = true;

    //清除buffer数据
    DiscardBuffer(false, metadata_size_);

    // This populates |partitions_| and the |install_plan.partitions| with the
    // list of partitions from the manifest.
    if (!ParseManifestPartitions(error))
      return false;

    // |install_plan.partitions| was filled in, nothing need to be done here if
    // the payload was already applied, returns false to terminate http fetcher,
    // but keep |error| as ErrorCode::kSuccess.
    if (payload_->already_applied)
      return false;

    num_total_operations_ = 0;
    for (const auto& partition : partitions_) 
      num_total_operations_ += partition.operations_size();
      acc_num_operations_.push_back(num_total_operations_);
    

    LOG_IF(WARNING, !prefs_->SetInt64(kPrefsManifestMetadataSize,
                                      metadata_size_))
        << "Unable to save the manifest metadata size.";
    LOG_IF(WARNING, !prefs_->SetInt64(kPrefsManifestSignatureSize,
                                      metadata_signature_size_))
        << "Unable to save the manifest signature size.";

    if (!PrimeUpdateState()) 
      *error = ErrorCode::kDownloadStateInitializationError;
      LOG(ERROR) << "Unable to prime the update state.";
      return false;
    

    if (!OpenCurrentPartition()) 
      *error = ErrorCode::kInstallDeviceOpenError;
      return false;
    

    if (next_operation_num_ > 0)
      UpdateOverallProgress(true, "Resuming after ");
    LOG(INFO) << "Starting to apply update payload operations";
  
//开始真实下载分区里的数据
  while (next_operation_num_ < num_total_operations_) 
    // 检查是否应该退出下载
    if (download_delegate_ && download_delegate_->ShouldCancel(error))
      return false;

    //检查是否超过当前分区大小限制.
    while (next_operation_num_ >= acc_num_operations_[current_partition_]) 
      CloseCurrentPartition();
      current_partition_++;
      if (!OpenCurrentPartition()) 
        *error = ErrorCode::kInstallDeviceOpenError;
        return false;
      
    
    //计算当前需要操作的分区的块数
    const size_t partition_operation_num = next_operation_num_ - (
        current_partition_ ? acc_num_operations_[current_partition_ - 1] : 0);
    //将当前需要操作的分区块的参数封装到installOperation中
    const InstallOperation& op =
        partitions_[current_partition_].operations(partition_operation_num);
    //将需要下载的数据拷贝到buffet_中
    CopyDataToBuffer(&c_bytes, &count, op.data_length());

    // 检查是否接收到了下次操作的全部数据
    if (!CanPerformInstallOperation(op))
      return true;

    // 检查metadata的签名是否为空,如果是空,则无效
    if (!payload_->metadata_signature.empty()) 
      // Note: Validate must be called only if CanPerformInstallOperation is
      // called. Otherwise, we might be failing operations before even if there
      // isn't sufficient data to compute the proper hash.
      *error = ValidateOperationHash(op);
      if (*error != ErrorCode::kSuccess) 
        if (install_plan_->hash_checks_mandatory) 
          LOG(ERROR) << "Mandatory operation hash check failed";
          return false;
        

        // For non-mandatory cases, just send a UMA stat.
        LOG(WARNING) << "Ignoring operation validation errors";
        *error = ErrorCode::kSuccess;
      
    

    // Makes sure we unblock exit when this operation completes.
    ScopedTerminatorExitUnblocker exit_unblocker =
        ScopedTerminatorExitUnblocker();  // Avoids a compiler unused var bug.

    base::TimeTicks op_start_time = base::TimeTicks::Now();

    bool op_result;
    //根据操作的不同类型做不同的操作处理,我们使用比较多得多的是升级替代
    switch (op.type()) 
      case InstallOperation::REPLACE:
      case InstallOperation::REPLACE_BZ:
      case InstallOperation::REPLACE_XZ:
       //执行替代的相关操作
        op_result = PerformReplaceOperation(op);
        OP_DURATION_HISTOGRAM("REPLACE", op_start_time);
        break;
      case InstallOperation::ZERO:
      case InstallOperation::DISCARD:
        op_result = PerformZeroOrDiscardOperation(op);
        OP_DURATION_HISTOGRAM("ZERO_OR_DISCARD", op_start_time);
        break;
      case InstallOperation::MOVE:
        op_result = PerformMoveOperation(op);
        OP_DURATION_HISTOGRAM("MOVE", op_start_time);
        break;
      case InstallOperation::BSDIFF:
        op_result = PerformBsdiffOperation(op);
        OP_DURATION_HISTOGRAM("BSDIFF", op_start_time);
        break;
      case InstallOperation::SOURCE_COPY:
        op_result = PerformSourceCopyOperation(op, error);
        OP_DURATION_HISTOGRAM("SOURCE_COPY", op_start_time);
        break;
      case InstallOperation::SOURCE_BSDIFF:
      case InstallOperation::BROTLI_BSDIFF:
        op_result = PerformSourceBsdiffOperation(op, error);
        OP_DURATION_HISTOGRAM("SOURCE_BSDIFF", op_start_time);
        break;
      case InstallOperation::PUFFDIFF:
        op_result = PerformPuffDiffOperation(op, error);
        OP_DURATION_HISTOGRAM("PUFFDIFF",

以上是关于Android P update_engine分析--升级核心DeltaPerformer的分析的主要内容,如果未能解决你的问题,请参考以下文章

Android P update_engine分析--升级核心DeltaPerformer的分析

Android P update_engine分析--升级核心DeltaPerformer的分析

Android P update_engine分析--升级核心DeltaPerformer的分析

Android P update_engine分析--升级核心DeltaPerformer的分析

Android P update_engine分析-- PostinstallRunnerAction的工作

Android P update_engine分析-- PostinstallRunnerAction的工作