AU: Resume interrupted update attempts.

BUG=7390,7520
TEST=unit tests

Change-Id: I9baf72aa444dd855409f865f03fb665e91f8d03d

Review URL: http://codereview.chromium.org/3620013
diff --git a/delta_performer.cc b/delta_performer.cc
index 10031eb..0eccb09 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -198,7 +198,10 @@
                                       manifest_metadata_size_))
         << "Unable to save the manifest metadata size.";
     manifest_valid_ = true;
-    block_size_ = manifest_.block_size();
+    if (!PrimeUpdateState()) {
+      LOG(ERROR) << "Unable to prime the update state.";
+      return -EINVAL;
+    }
   }
   ssize_t total_operations = manifest_.install_operations_size() +
       manifest_.kernel_install_operations_size();
@@ -225,7 +228,7 @@
     // update.
     if (!IsIdempotentOperation(op)) {
       Terminator::set_exit_blocked(true);
-      ResetUpdateProgress(prefs_);
+      ResetUpdateProgress(prefs_, true);
     }
     if (op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
         op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
@@ -281,8 +284,8 @@
 
   // Since we delete data off the beginning of the buffer as we use it,
   // the data we need should be exactly at the beginning of the buffer.
-  CHECK_EQ(buffer_offset_, operation.data_offset());
-  CHECK_GE(buffer_.size(), operation.data_length());
+  TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset());
+  TEST_AND_RETURN_FALSE(buffer_.size() >= operation.data_length());
 
   // Extract the signature message if it's in this operation.
   ExtractSignatureMessage(operation);
@@ -405,8 +408,8 @@
     bool is_kernel_partition) {
   // Since we delete data off the beginning of the buffer as we use it,
   // the data we need should be exactly at the beginning of the buffer.
-  CHECK_EQ(buffer_offset_, operation.data_offset());
-  CHECK_GE(buffer_.size(), operation.data_length());
+  TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset());
+  TEST_AND_RETURN_FALSE(buffer_.size() >= operation.data_length());
 
   string input_positions;
   TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.src_extents(),
@@ -571,9 +574,16 @@
   return true;
 }
 
-bool DeltaPerformer::ResetUpdateProgress(PrefsInterface* prefs) {
+bool DeltaPerformer::ResetUpdateProgress(PrefsInterface* prefs, bool quick) {
   TEST_AND_RETURN_FALSE(prefs->SetInt64(kPrefsUpdateStateNextOperation,
                                         kUpdateStateOperationInvalid));
+  if (!quick) {
+    prefs->SetString(kPrefsUpdateCheckResponseHash, "");
+    prefs->SetInt64(kPrefsUpdateStateNextDataOffset, -1);
+    prefs->SetString(kPrefsUpdateStateSHA256Context, "");
+    prefs->SetString(kPrefsUpdateStateSignedSHA256Context, "");
+    prefs->SetInt64(kPrefsManifestMetadataSize, -1);
+  }
   return true;
 }
 
@@ -581,7 +591,7 @@
   Terminator::set_exit_blocked(true);
   if (last_updated_buffer_offset_ != buffer_offset_) {
     // Resets the progress in case we die in the middle of the state update.
-    ResetUpdateProgress(prefs_);
+    ResetUpdateProgress(prefs_, true);
     TEST_AND_RETURN_FALSE(
         prefs_->SetString(kPrefsUpdateStateSHA256Context,
                           hash_calculator_.GetContext()));
@@ -594,4 +604,43 @@
   return true;
 }
 
+bool DeltaPerformer::PrimeUpdateState() {
+  CHECK(manifest_valid_);
+  block_size_ = manifest_.block_size();
+
+  int64_t next_operation = kUpdateStateOperationInvalid;
+  if (!prefs_->GetInt64(kPrefsUpdateStateNextOperation, &next_operation) ||
+      next_operation == kUpdateStateOperationInvalid ||
+      next_operation <= 0) {
+    // Initiating a new update, no more state needs to be initialized.
+    return true;
+  }
+  next_operation_num_ = next_operation;
+
+  // Resuming an update -- load the rest of the update state.
+  int64_t next_data_offset = -1;
+  TEST_AND_RETURN_FALSE(prefs_->GetInt64(kPrefsUpdateStateNextDataOffset,
+                                         &next_data_offset) &&
+                        next_data_offset >= 0);
+  buffer_offset_ = next_data_offset;
+
+  // The signed hash context may be empty if the interrupted update didn't reach
+  // the signature blob.
+  prefs_->GetString(kPrefsUpdateStateSignedSHA256Context,
+                    &signed_hash_context_);
+
+  string hash_context;
+  TEST_AND_RETURN_FALSE(prefs_->GetString(kPrefsUpdateStateSHA256Context,
+                                          &hash_context) &&
+                        hash_calculator_.SetContext(hash_context));
+
+  int64_t manifest_metadata_size = 0;
+  TEST_AND_RETURN_FALSE(prefs_->GetInt64(kPrefsManifestMetadataSize,
+                                         &manifest_metadata_size) &&
+                        manifest_metadata_size > 0);
+  manifest_metadata_size_ = manifest_metadata_size;
+
+  return true;
+}
+
 }  // namespace chromeos_update_engine
diff --git a/delta_performer.h b/delta_performer.h
index 5425ea5..051d605 100644
--- a/delta_performer.h
+++ b/delta_performer.h
@@ -83,8 +83,10 @@
                               std::string update_check_response_hash);
 
   // Resets the persistent update progress state to indicate that an update
-  // can't be resumed. Returns true on success, false otherwise.
-  static bool ResetUpdateProgress(PrefsInterface* prefs);
+  // can't be resumed. Performs a quick update-in-progress reset if |quick| is
+  // true, otherwise resets all progress-related update state. Returns true on
+  // success, false otherwise.
+  static bool ResetUpdateProgress(PrefsInterface* prefs, bool quick);
 
  private:
   // Returns true if enough of the delta file has been passed via Write()
@@ -120,6 +122,11 @@
   // update attempt to be resumed after reboot.
   bool CheckpointUpdateProgress();
 
+  // Primes the required update state. Returns true if the update state was
+  // successfully initialized to a saved resume state or if the update is a new
+  // update. Returns false otherwise.
+  bool PrimeUpdateState();
+
   // Update Engine preference store.
   PrefsInterface* prefs_;
 
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index 5565c88..07fd8cc 100755
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -259,6 +259,8 @@
                               manifest_metadata_size)).WillOnce(Return(true));
   EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextOperation, _))
       .WillRepeatedly(Return(true));
+  EXPECT_CALL(prefs, GetInt64(kPrefsUpdateStateNextOperation, _))
+      .WillOnce(Return(false));
   EXPECT_CALL(prefs, SetInt64(kPrefsUpdateStateNextDataOffset, _))
       .WillRepeatedly(Return(true));
   EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSHA256Context, _))
diff --git a/download_action.h b/download_action.h
index 62dd29a..08035f0 100644
--- a/download_action.h
+++ b/download_action.h
@@ -93,6 +93,8 @@
     delegate_ = delegate;
   }
 
+  HttpFetcher* http_fetcher() { return http_fetcher_.get(); }
+
  private:
   // The InstallPlan passed in
   InstallPlan install_plan_;
diff --git a/filesystem_copier_action.cc b/filesystem_copier_action.cc
index 388e0e7..5628ec3 100755
--- a/filesystem_copier_action.cc
+++ b/filesystem_copier_action.cc
@@ -1,21 +1,25 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "update_engine/filesystem_copier_action.h"
+
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <stdlib.h>
+
 #include <algorithm>
+#include <cstdlib>
 #include <map>
 #include <string>
 #include <vector>
+
 #include <gio/gio.h>
 #include <gio/gunixinputstream.h>
 #include <gio/gunixoutputstream.h>
 #include <glib.h>
+
 #include "update_engine/filesystem_iterator.h"
 #include "update_engine/subprocess.h"
 #include "update_engine/utils.h"
@@ -41,7 +45,7 @@
   }
   install_plan_ = GetInputObject();
 
-  if (install_plan_.is_full_update) {
+  if (install_plan_.is_full_update || install_plan_.is_resume) {
     // No copy needed. Done!
     if (HasOutputPipe())
       SetOutputObject(install_plan_);
diff --git a/filesystem_copier_action_unittest.cc b/filesystem_copier_action_unittest.cc
index f82097e..d9305ff 100644
--- a/filesystem_copier_action_unittest.cc
+++ b/filesystem_copier_action_unittest.cc
@@ -249,6 +249,32 @@
   EXPECT_EQ(kUrl, collector_action.object().download_url);
 }
 
+TEST_F(FilesystemCopierActionTest, ResumeTest) {
+  ActionProcessor processor;
+  FilesystemCopierActionTest2Delegate delegate;
+
+  processor.set_delegate(&delegate);
+
+  ObjectFeederAction<InstallPlan> feeder_action;
+  const char* kUrl = "http://some/url";
+  InstallPlan install_plan(false, true, kUrl, 0, "", "", "");
+  feeder_action.set_obj(install_plan);
+  FilesystemCopierAction copier_action(false);
+  ObjectCollectorAction<InstallPlan> collector_action;
+
+  BondActions(&feeder_action, &copier_action);
+  BondActions(&copier_action, &collector_action);
+
+  processor.EnqueueAction(&feeder_action);
+  processor.EnqueueAction(&copier_action);
+  processor.EnqueueAction(&collector_action);
+  processor.StartProcessing();
+  EXPECT_FALSE(processor.IsRunning());
+  EXPECT_TRUE(delegate.ran_);
+  EXPECT_EQ(kActionCodeSuccess, delegate.code_);
+  EXPECT_EQ(kUrl, collector_action.object().download_url);
+}
+
 TEST_F(FilesystemCopierActionTest, NonExistentDriveTest) {
   ActionProcessor processor;
   FilesystemCopierActionTest2Delegate delegate;
diff --git a/generate_delta_main.cc b/generate_delta_main.cc
index 8c5d6a9..a113e15 100644
--- a/generate_delta_main.cc
+++ b/generate_delta_main.cc
@@ -91,6 +91,7 @@
       CHECK_EQ(performer.Write(&buf[0], bytes_read), bytes_read);
     }
     CHECK_EQ(performer.Close(), 0);
+    DeltaPerformer::ResetUpdateProgress(&prefs, false);
     LOG(INFO) << "done applying delta.";
     return 0;
   }
diff --git a/multi_http_fetcher.h b/multi_http_fetcher.h
index cee7de0..1972347 100644
--- a/multi_http_fetcher.h
+++ b/multi_http_fetcher.h
@@ -40,7 +40,7 @@
       (*it)->set_delegate(this);
     }
   }
-  
+
   void SetOffset(off_t offset) {}  // for now, doesn't support this
 
   // Begins the transfer to the specified URL.
@@ -100,17 +100,18 @@
     if (delegate_)
       delegate_->TransferComplete(this, successful);
   }
-   
+
   void StartTransfer() {
     if (current_index_ >= ranges_.size()) {
       return;
     }
-    LOG(INFO) << "Starting a transfer";
+    LOG(INFO) << "Starting a transfer @" << ranges_[current_index_].first << "("
+              << ranges_[current_index_].second << ")";
     bytes_received_this_fetcher_ = 0;
     fetchers_[current_index_]->SetOffset(ranges_[current_index_].first);
     fetchers_[current_index_]->BeginTransfer(url_);
   }
-  
+
   void ReceivedBytes(HttpFetcher* fetcher,
                      const char* bytes,
                      int length) {
@@ -148,7 +149,7 @@
       SendTransferComplete(fetcher, true);
       return;
     }
-    
+
     if (ranges_[current_index_].second < 0) {
       // We're done with the current operation
       current_index_++;
@@ -160,7 +161,7 @@
       }
       return;
     }
-    
+
     if (bytes_received_this_fetcher_ < ranges_[current_index_].second) {
       LOG(WARNING) << "Received insufficient bytes from fetcher. "
                    << "Ending early";
@@ -170,13 +171,13 @@
       LOG(INFO) << "Got spurious TransferComplete. Ingoring.";
     }
   }
-  
+
   // If true, do not send any more data or TransferComplete to the delegate.
   bool sent_transfer_complete_;  
-  
+
   RangesVect ranges_;
   std::vector<std::tr1::shared_ptr<BaseHttpFetcher> > fetchers_;
-  
+
   RangesVect::size_type current_index_;  // index into ranges_, fetchers_
   off_t bytes_received_this_fetcher_;
 
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
index d3142a7..5159733 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -32,7 +32,7 @@
   install_plan_.is_resume =
       DeltaPerformer::CanResumeUpdate(prefs_, response.hash);
   if (!install_plan_.is_resume) {
-    LOG_IF(WARNING, !DeltaPerformer::ResetUpdateProgress(prefs_))
+    LOG_IF(WARNING, !DeltaPerformer::ResetUpdateProgress(prefs_, false))
         << "Unable to reset the update progress.";
     LOG_IF(WARNING, !prefs_->SetString(kPrefsUpdateCheckResponseHash,
                                        response.hash))
diff --git a/update_attempter.cc b/update_attempter.cc
index 2db91a6..f408d82 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -10,8 +10,8 @@
 #endif  // _POSIX_C_SOURCE
 #include <time.h>
 
-#include <tr1/memory>
 #include <string>
+#include <tr1/memory>
 #include <vector>
 
 #include <glib.h>
@@ -21,6 +21,7 @@
 #include "update_engine/download_action.h"
 #include "update_engine/filesystem_copier_action.h"
 #include "update_engine/libcurl_http_fetcher.h"
+#include "update_engine/multi_http_fetcher.h"
 #include "update_engine/omaha_request_action.h"
 #include "update_engine/omaha_request_params.h"
 #include "update_engine/omaha_response_handler_action.h"
@@ -31,6 +32,7 @@
 
 using base::TimeDelta;
 using base::TimeTicks;
+using std::make_pair;
 using std::tr1::shared_ptr;
 using std::string;
 using std::vector;
@@ -153,7 +155,7 @@
                                  OmahaEvent::kTypeUpdateDownloadStarted),
                              new LibcurlHttpFetcher));
   shared_ptr<DownloadAction> download_action(
-      new DownloadAction(prefs_, new LibcurlHttpFetcher));
+      new DownloadAction(prefs_, new MultiHttpFetcher<LibcurlHttpFetcher>));
   shared_ptr<OmahaRequestAction> download_finished_action(
       new OmahaRequestAction(prefs_,
                              omaha_request_params_,
@@ -174,6 +176,7 @@
 
   download_action->set_delegate(this);
   response_handler_action_ = response_handler_action;
+  download_action_ = download_action;
 
   actions_.push_back(shared_ptr<AbstractAction>(update_check_action));
   actions_.push_back(shared_ptr<AbstractAction>(response_handler_action));
@@ -254,9 +257,10 @@
   }
 
   if (code == kActionCodeSuccess) {
-    SetStatusAndNotify(UPDATE_STATUS_UPDATED_NEED_REBOOT);
     utils::WriteFile(kUpdateCompletedMarker, "", 0);
     prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
+    DeltaPerformer::ResetUpdateProgress(prefs_, false);
+    SetStatusAndNotify(UPDATE_STATUS_UPDATED_NEED_REBOOT);
 
     // Report the time it took to update the system.
     int64_t update_time = time(NULL) - last_checked_time_;
@@ -327,20 +331,18 @@
   }
   // Find out which action completed.
   if (type == OmahaResponseHandlerAction::StaticType()) {
-    // Note that the status will be updated to DOWNLOADING when some
-    // bytes get actually downloaded from the server and the
-    // BytesReceived callback is invoked. This avoids notifying the
-    // user that a download has started in cases when the server and
-    // the client are unable to initiate the download.
-    OmahaResponseHandlerAction* omaha_response_handler_action =
-        dynamic_cast<OmahaResponseHandlerAction*>(action);
-    CHECK(omaha_response_handler_action);
-    const InstallPlan& plan = omaha_response_handler_action->install_plan();
+    // Note that the status will be updated to DOWNLOADING when some bytes get
+    // actually downloaded from the server and the BytesReceived callback is
+    // invoked. This avoids notifying the user that a download has started in
+    // cases when the server and the client are unable to initiate the download.
+    CHECK(action == response_handler_action_.get());
+    const InstallPlan& plan = response_handler_action_->install_plan();
     last_checked_time_ = time(NULL);
     // TODO(adlr): put version in InstallPlan
     new_version_ = "0.0.0.0";
     new_size_ = plan.size;
     is_full_update_ = plan.is_full_update;
+    SetupDownload();
     SetupPriorityManagement();
   } else if (type == DownloadAction::StaticType()) {
     SetStatusAndNotify(UPDATE_STATUS_FINALIZING);
@@ -517,6 +519,11 @@
 
 void UpdateAttempter::MarkDeltaUpdateFailure() {
   CHECK(!is_full_update_);
+  // If a delta update fails after the downloading phase, don't try to resume it
+  // the next time.
+  if (status_ > UPDATE_STATUS_DOWNLOADING) {
+    DeltaPerformer::ResetUpdateProgress(prefs_, false);
+  }
   int64_t delta_failures;
   if (!prefs_->GetInt64(kPrefsDeltaUpdateFailures, &delta_failures) ||
       delta_failures < 0) {
@@ -525,4 +532,22 @@
   prefs_->SetInt64(kPrefsDeltaUpdateFailures, ++delta_failures);
 }
 
+void UpdateAttempter::SetupDownload() {
+  MultiHttpFetcher<LibcurlHttpFetcher>* fetcher =
+      dynamic_cast<MultiHttpFetcher<LibcurlHttpFetcher>*>(
+          download_action_->http_fetcher());
+  MultiHttpFetcher<LibcurlHttpFetcher>::RangesVect ranges;
+  if (response_handler_action_->install_plan().is_resume) {
+    int64_t manifest_metadata_size = 0;
+    prefs_->GetInt64(kPrefsManifestMetadataSize, &manifest_metadata_size);
+    int64_t next_data_offset = 0;
+    prefs_->GetInt64(kPrefsUpdateStateNextDataOffset, &next_data_offset);
+    ranges.push_back(make_pair(0, manifest_metadata_size));
+    ranges.push_back(make_pair(manifest_metadata_size + next_data_offset, -1));
+  } else {
+    ranges.push_back(make_pair(0, -1));
+  }
+  fetcher->set_ranges(ranges);
+}
+
 }  // namespace chromeos_update_engine
diff --git a/update_attempter.h b/update_attempter.h
index 05f3dcb..db05c7b 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -116,33 +116,36 @@
   // over dbus.
   void SetStatusAndNotify(UpdateStatus status);
 
-  // Creates an error event object in |error_event_| to be included in
-  // an OmahaRequestAction once the current action processor is done.
+  // Sets up the download parameters after receiving the update check response.
+  void SetupDownload();
+
+  // Creates an error event object in |error_event_| to be included in an
+  // OmahaRequestAction once the current action processor is done.
   void CreatePendingErrorEvent(AbstractAction* action, ActionExitCode code);
 
-  // If there's a pending error event allocated in |error_event_|,
-  // schedules an OmahaRequestAction with that event in the current
-  // processor, clears the pending event, updates the status and
-  // returns true. Returns false otherwise.
+  // If there's a pending error event allocated in |error_event_|, schedules an
+  // OmahaRequestAction with that event in the current processor, clears the
+  // pending event, updates the status and returns true. Returns false
+  // otherwise.
   bool ScheduleErrorEventAction();
 
-  // Sets the process priority to |priority| and updates |priority_|
-  // if the new |priority| is different than the current |priority_|,
-  // otherwise simply returns.
+  // Sets the process priority to |priority| and updates |priority_| if the new
+  // |priority| is different than the current |priority_|, otherwise simply
+  // returns.
   void SetPriority(utils::ProcessPriority priority);
 
-  // Set the process priority to low and sets up timeout events to
-  // increase the priority gradually to high.
+  // Set the process priority to low and sets up timeout events to increase the
+  // priority gradually to high.
   void SetupPriorityManagement();
 
-  // Resets the process priority to normal and destroys any scheduled
-  // timeout sources.
+  // Resets the process priority to normal and destroys any scheduled timeout
+  // sources.
   void CleanupPriorityManagement();
 
-  // The process priority timeout source callback increases the
-  // current priority by one step (low goes to normal, normal goes to
-  // high). Returns true if the callback must be invoked again after a
-  // timeout, or false if GLib can destroy this timeout source.
+  // The process priority timeout source callback increases the current priority
+  // by one step (low goes to normal, normal goes to high). Returns true if the
+  // callback must be invoked again after a timeout, or false if GLib can
+  // destroy this timeout source.
   static gboolean StaticManagePriorityCallback(gpointer data);
   bool ManagePriorityCallback();
 
@@ -154,9 +157,9 @@
   // update can be tried when needed.
   void MarkDeltaUpdateFailure();
 
-  // Last status notification timestamp used for throttling. Use
-  // monotonic TimeTicks to ensure that notifications are sent even if
-  // the system clock is set back in the middle of an update.
+  // Last status notification timestamp used for throttling. Use monotonic
+  // TimeTicks to ensure that notifications are sent even if the system clock is
+  // set back in the middle of an update.
   base::TimeTicks last_notify_time_;
 
   std::vector<std::tr1::shared_ptr<AbstractAction> > actions_;
@@ -166,9 +169,12 @@
   // dbus service.
   UpdateEngineService* dbus_service_;
 
-  // pointer to the OmahaResponseHandlerAction in the actions_ vector;
+  // Pointer to the OmahaResponseHandlerAction in the actions_ vector.
   std::tr1::shared_ptr<OmahaResponseHandlerAction> response_handler_action_;
 
+  // Pointer to the DownloadAction in the actions_ vector.
+  std::tr1::shared_ptr<DownloadAction> download_action_;
+
   // Pointer to the preferences store interface.
   PrefsInterface* prefs_;