p2p: Use p2p for updates

This is the main patch for enabling use of p2p for consuming and/or
sharing updates via p2p. Refer to the ddoc and other documentation for
how this works.

BUG=chromium:260426,chromium:273110
TEST=New unit tests + unit tests pass + manual testing
Change-Id: I6bc3bddae1e041ccc176969a651396e8e89cb3f0
Reviewed-on: https://chromium-review.googlesource.com/64829
Reviewed-by: David Zeuthen <[email protected]>
Commit-Queue: David Zeuthen <[email protected]>
Tested-by: David Zeuthen <[email protected]>
diff --git a/constants.cc b/constants.cc
index 93dd1e3..ffcd112 100644
--- a/constants.cc
+++ b/constants.cc
@@ -20,6 +20,8 @@
 
 const char kSystemRebootedMarkerFile[] = "/tmp/update_engine_update_recorded";
 
+const char kP2PAllowInteractiveMarkerFile[] =
+    "/mnt/stateful_partition/p2p-allow-interactive";
 
 // Constants defining keys for the persisted state of update engine.
 const char kPrefsBackoffExpiryTime[] = "backoff-expiry-time";
diff --git a/constants.h b/constants.h
index e114956..f3e7450 100644
--- a/constants.h
+++ b/constants.h
@@ -26,6 +26,10 @@
 // Path to the stateful partition on the root filesystem.
 extern const char kStatefulPartition[];
 
+// Path to the marker file to allow p2p on interactive update attempts.
+// See utils::IsP2PAllowedForInteractiveChecks().
+extern const char kP2PAllowInteractiveMarkerFile[];
+
 // Constants related to preferences.
 extern const char kPrefsBackoffExpiryTime[];
 extern const char kPrefsCertificateReportToSendDownload[];
@@ -92,12 +96,16 @@
   kNumPayloadTypes
 } PayloadType;
 
-// Maximum number of attempts using p2p.
+// Maximum number of times we'll allow using p2p for the same update payload.
 const int kMaxP2PAttempts = 10;
 
-// Maximum wallclock time we allow attempting to update using p2p -
-// two days.
-const int kMaxP2PAttemptTimeSeconds = 2*24*60*60;
+// Maximum wallclock time we allow attempting to update using p2p for
+// the same update payload - two days.
+const int kMaxP2PAttemptTimeSeconds = 2 * 24 * 60 * 60;
+
+// The maximum amount of time to spend waiting for p2p-client(1) to
+// return while waiting in line to use the LAN - six hours.
+const int kMaxP2PNetworkWaitTimeSeconds = 6 * 60 * 60;
 
 // The maximum number of payload files to keep in /var/cache/p2p.
 const int kMaxP2PFilesToKeep = 3;
diff --git a/delta_performer.cc b/delta_performer.cc
index 0a33811..68161d2 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -488,6 +488,10 @@
   return true;
 }
 
+bool DeltaPerformer::IsManifestValid() {
+  return manifest_valid_;
+}
+
 bool DeltaPerformer::CanPerformInstallOperation(
     const chromeos_update_engine::DeltaArchiveManifest_InstallOperation&
     operation) {
diff --git a/delta_performer.h b/delta_performer.h
index 33e39d7..7943d7a 100644
--- a/delta_performer.h
+++ b/delta_performer.h
@@ -97,6 +97,9 @@
   // Closes both 'path' given to Open() and the kernel path.
   int Close();
 
+  // Returns |true| only if the manifest has been processed and it's valid.
+  bool IsManifestValid();
+
   // Verifies the downloaded payload against the signed hash included in the
   // payload, against the update check hash (which is in base64 format)  and
   // size using the public key and returns kErrorCodeSuccess on success, an
diff --git a/download_action.cc b/download_action.cc
index da6b1fc..17d4538 100644
--- a/download_action.cc
+++ b/download_action.cc
@@ -8,12 +8,19 @@
 #include <string>
 #include <vector>
 #include <glib.h>
+
+#include <base/file_path.h>
+#include <base/stringprintf.h>
+
 #include "update_engine/action_pipe.h"
+#include "update_engine/p2p_manager.h"
 #include "update_engine/subprocess.h"
 
 using std::min;
 using std::string;
 using std::vector;
+using base::FilePath;
+using base::StringPrintf;
 
 namespace chromeos_update_engine {
 
@@ -29,10 +36,118 @@
       writer_(NULL),
       code_(kErrorCodeSuccess),
       delegate_(NULL),
-      bytes_received_(0) {}
+      bytes_received_(0),
+      p2p_sharing_fd_(-1),
+      p2p_visible_(true) {}
 
 DownloadAction::~DownloadAction() {}
 
+void DownloadAction::CloseP2PSharingFd(bool delete_p2p_file) {
+  if (p2p_sharing_fd_ != -1) {
+    if (close(p2p_sharing_fd_) != 0) {
+      PLOG(ERROR) << "Error closing p2p sharing fd";
+    }
+    p2p_sharing_fd_ = -1;
+  }
+
+  if (delete_p2p_file) {
+    FilePath path = system_state_->p2p_manager()->FileGetPath(p2p_file_id_);
+    if (unlink(path.value().c_str()) != 0) {
+      PLOG(ERROR) << "Error deleting p2p file " << path.value();
+    } else {
+      LOG(INFO) << "Deleted p2p file " << path.value();
+    }
+  }
+
+  // Don't use p2p from this point onwards.
+  p2p_file_id_.clear();
+}
+
+bool DownloadAction::SetupP2PSharingFd() {
+  P2PManager *p2p_manager = system_state_->p2p_manager();
+
+  if (!p2p_manager->FileShare(p2p_file_id_, install_plan_.payload_size)) {
+    LOG(ERROR) << "Unable to share file via p2p";
+    CloseP2PSharingFd(true); // delete p2p file
+    return false;
+  }
+
+  // File has already been created (and allocated, xattrs been
+  // populated etc.) by FileShare() so just open it for writing.
+  FilePath path = p2p_manager->FileGetPath(p2p_file_id_);
+  p2p_sharing_fd_ = open(path.value().c_str(), O_WRONLY);
+  if (p2p_sharing_fd_ == -1) {
+    PLOG(ERROR) << "Error opening file " << path.value();
+    CloseP2PSharingFd(true); // Delete p2p file.
+    return false;
+  }
+
+  // Ensure file to share is world-readable, otherwise
+  // p2p-server and p2p-http-server can't access it.
+  //
+  // (Q: Why doesn't the file have mode 0644 already? A: Because
+  // the process-wide umask is set to 0700 in main.cc.)
+  if (fchmod(p2p_sharing_fd_, 0644) != 0) {
+    PLOG(ERROR) << "Error setting mode 0644 on " << path.value();
+    CloseP2PSharingFd(true); // Delete p2p file.
+    return false;
+  }
+
+  // All good.
+  LOG(INFO) << "Writing payload contents to " << path.value();
+  p2p_manager->FileGetVisible(p2p_file_id_, &p2p_visible_);
+  return true;
+}
+
+void DownloadAction::WriteToP2PFile(const char *data,
+                                    size_t length,
+                                    off_t file_offset) {
+  if (p2p_sharing_fd_ == -1) {
+    if (!SetupP2PSharingFd())
+      return;
+  }
+
+  // Check that the file is at least |file_offset| bytes long - if
+  // it's not something is wrong and we must immediately delete the
+  // file to avoid propagating this problem to other peers.
+  //
+  // How can this happen? It could be that we're resuming an update
+  // after a system crash... in this case, it could be that
+  //
+  //  1. the p2p file didn't get properly synced to stable storage; or
+  //  2. the file was deleted at bootup (it's in /var/cache after all); or
+  //  3. other reasons
+  struct stat statbuf;
+  if (fstat(p2p_sharing_fd_, &statbuf) != 0) {
+    PLOG(ERROR) << "Error getting file status for p2p file";
+    CloseP2PSharingFd(true); // Delete p2p file.
+    return;
+  }
+  if (statbuf.st_size < file_offset) {
+    LOG(ERROR) << "Wanting to write to file offset " << file_offset
+               << " but existing p2p file is only " << statbuf.st_size
+               << " bytes.";
+    CloseP2PSharingFd(true); // Delete p2p file.
+    return;
+  }
+
+  off_t cur_file_offset = lseek(p2p_sharing_fd_, file_offset, SEEK_SET);
+  if (cur_file_offset != static_cast<off_t>(file_offset)) {
+    PLOG(ERROR) << "Error seeking to position "
+                << file_offset << " in p2p file";
+    CloseP2PSharingFd(true); // Delete p2p file.
+  } else {
+    // OK, seeking worked, now write the data
+    ssize_t bytes_written = write(p2p_sharing_fd_, data, length);
+    if (bytes_written != static_cast<ssize_t>(length)) {
+      PLOG(ERROR) << "Error writing "
+                  << length << " bytes at file offset "
+                  << file_offset << " in p2p file";
+      CloseP2PSharingFd(true); // Delete p2p file.
+    }
+  }
+}
+
 void DownloadAction::PerformAction() {
   http_fetcher_->set_delegate(this);
 
@@ -72,6 +187,33 @@
   if (delegate_) {
     delegate_->SetDownloadStatus(true);  // Set to active.
   }
+
+  if (system_state_ != NULL) {
+    string file_id = utils::CalculateP2PFileId(install_plan_.payload_hash,
+                                               install_plan_.payload_size);
+    if (system_state_->request_params()->use_p2p_for_sharing()) {
+      // 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()) != 0) {
+          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.";
+        }
+      }
+    }
+  }
+
   http_fetcher_->BeginTransfer(install_plan_.download_url);
 }
 
@@ -83,6 +225,7 @@
   if (delegate_) {
     delegate_->SetDownloadStatus(false);  // Set to inactive.
   }
+  CloseP2PSharingFd(false); // Keep p2p file.
   // Terminates the transfer. The action is terminated, if necessary, when the
   // TransferTerminated callback is received.
   http_fetcher_->TerminateTransfer();
@@ -95,6 +238,11 @@
 void DownloadAction::ReceivedBytes(HttpFetcher *fetcher,
                                    const char* bytes,
                                    int length) {
+  // Note that bytes_received_ is the current offset.
+  if (!p2p_file_id_.empty()) {
+    WriteToP2PFile(bytes, length, bytes_received_);
+  }
+
   bytes_received_ += length;
   if (delegate_)
     delegate_->BytesReceived(bytes_received_, install_plan_.payload_size);
@@ -107,6 +255,15 @@
     TerminateProcessing();
     return;
   }
+
+  // Call p2p_manager_->FileMakeVisible() when we've successfully
+  // verified the manifest!
+  if (!p2p_visible_ &&
+      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;
+  }
 }
 
 void DownloadAction::TransferComplete(HttpFetcher *fetcher, bool successful) {
diff --git a/download_action.h b/download_action.h
index 020aa9c..e6099f9 100644
--- a/download_action.h
+++ b/download_action.h
@@ -81,7 +81,29 @@
 
   HttpFetcher* http_fetcher() { return http_fetcher_.get(); }
 
+  // Returns the p2p file id for the file being written or the empty
+  // string if we're not writing to a p2p file.
+  std::string p2p_file_id() { return p2p_file_id_; };
+
  private:
+  // Closes the file descriptor for the p2p file being written and
+  // clears |p2p_file_id_| to indicate that we're no longer sharing
+  // the file. If |delete_p2p_file| is True, also deletes the file.
+  // If there is no p2p file descriptor, this method does nothing.
+  void CloseP2PSharingFd(bool delete_p2p_file);
+
+  // Starts sharing the p2p file. Must be called before
+  // WriteToP2PFile(). Returns True if this worked.
+  bool SetupP2PSharingFd();
+
+  // Writes |length| bytes of payload from |data| into |file_offset|
+  // of the p2p file. Also does sanity checks; for example ensures we
+  // don't end up with a file with holes in it.
+  //
+  // This method does nothing if SetupP2PSharingFd() hasn't been
+  // called or if CloseP2PSharingFd() has been called.
+  void WriteToP2PFile(const char *data, size_t length, off_t file_offset);
+
   // The InstallPlan passed in
   InstallPlan install_plan_;
 
@@ -108,6 +130,17 @@
   DownloadActionDelegate* delegate_;
   uint64_t bytes_received_;
 
+  // The file-id for the file we're sharing or the empty string
+  // if we're not using p2p to share.
+  std::string p2p_file_id_;
+
+  // The file descriptor for the p2p file used for caching the payload or -1
+  // if we're not using p2p to share.
+  int p2p_sharing_fd_;
+
+  // Set to |false| if p2p file is not visible.
+  bool p2p_visible_;
+
   DISALLOW_COPY_AND_ASSIGN(DownloadAction);
 };
 
diff --git a/download_action_unittest.cc b/download_action_unittest.cc
index e937d71..c53b36c 100644
--- a/download_action_unittest.cc
+++ b/download_action_unittest.cc
@@ -2,17 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <string>
-#include <vector>
-
 #include <glib.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <base/file_path.h>
+#include <base/file_util.h>
+#include <base/stringprintf.h>
+
 #include "update_engine/action_pipe.h"
 #include "update_engine/download_action.h"
 #include "update_engine/mock_http_fetcher.h"
+#include "update_engine/mock_system_state.h"
 #include "update_engine/omaha_hash_calculator.h"
+#include "update_engine/fake_p2p_manager_configuration.h"
 #include "update_engine/prefs_mock.h"
 #include "update_engine/test_utils.h"
 #include "update_engine/utils.h"
@@ -24,6 +31,10 @@
 using testing::_;
 using testing::AtLeast;
 using testing::InSequence;
+using base::FilePath;
+using base::StringPrintf;
+using file_util::WriteFile;
+using file_util::ReadFileToString;
 
 class DownloadActionTest : public ::testing::Test { };
 
@@ -417,4 +428,201 @@
   g_main_loop_unref(loop);
 }
 
+gboolean StartProcessorInRunLoopForP2P(gpointer user_data) {
+  ActionProcessor* processor = reinterpret_cast<ActionProcessor*>(user_data);
+  processor->StartProcessing();
+  return FALSE;
+}
+
+// Test fixture for P2P tests.
+class P2PDownloadActionTest : public testing::Test {
+protected:
+  P2PDownloadActionTest()
+    : loop_(NULL),
+      start_at_offset_(0) {}
+
+  virtual ~P2PDownloadActionTest() {}
+
+  // Derived from testing::Test.
+  virtual void SetUp() {
+    loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+  }
+
+  // Derived from testing::Test.
+  virtual void TearDown() {
+    if (loop_ != NULL)
+      g_main_loop_unref(loop_);
+  }
+
+  // To be called by tests to setup the download. The
+  // |starting_offset| parameter is for where to resume.
+  void SetupDownload(off_t starting_offset) {
+    start_at_offset_ = starting_offset;
+    // Prepare data 10 kB of data.
+    data_.clear();
+    for (unsigned int i = 0; i < 10 * 1000; i++)
+      data_ += 'a' + (i % 25);
+
+    // Setup p2p.
+    FakeP2PManagerConfiguration *test_conf = new FakeP2PManagerConfiguration();
+    p2p_manager_.reset(P2PManager::Construct(test_conf, NULL, "cros_au", 3));
+    mock_system_state_.set_p2p_manager(p2p_manager_.get());
+  }
+
+  // To be called by tests to perform the download. The
+  // |use_p2p_to_share| parameter is used to indicate whether the
+  // payload should be shared via p2p.
+  void StartDownload(bool use_p2p_to_share) {
+    mock_system_state_.request_params()->set_use_p2p_for_sharing(
+        use_p2p_to_share);
+
+    ScopedTempFile output_temp_file;
+    TestDirectFileWriter writer;
+    InstallPlan install_plan(false,
+                             false,
+                             "",
+                             data_.length(),
+                             "1234hash",
+                             0,
+                             "",
+                             output_temp_file.GetPath(),
+                             "");
+    ObjectFeederAction<InstallPlan> feeder_action;
+    feeder_action.set_obj(install_plan);
+    PrefsMock prefs;
+    http_fetcher_ = new MockHttpFetcher(data_.c_str(),
+                                        data_.length(),
+                                        NULL);
+    // Note that DownloadAction takes ownership of the passed in HttpFetcher.
+    download_action_.reset(new DownloadAction(&prefs, &mock_system_state_,
+                                              http_fetcher_));
+    download_action_->SetTestFileWriter(&writer);
+    BondActions(&feeder_action, download_action_.get());
+    DownloadActionTestProcessorDelegate delegate(kErrorCodeSuccess);
+    delegate.loop_ = loop_;
+    delegate.expected_data_ = vector<char>(data_.begin() + start_at_offset_,
+                                           data_.end());
+    delegate.path_ = output_temp_file.GetPath();
+    processor_.set_delegate(&delegate);
+    processor_.EnqueueAction(&feeder_action);
+    processor_.EnqueueAction(download_action_.get());
+
+    g_timeout_add(0, &StartProcessorInRunLoopForP2P, this);
+    g_main_loop_run(loop_);
+  }
+
+  // The DownloadAction instance under test.
+  scoped_ptr<DownloadAction> download_action_;
+
+  // The HttpFetcher used in the test.
+  MockHttpFetcher* http_fetcher_;
+
+  // The P2PManager used in the test.
+  scoped_ptr<P2PManager> p2p_manager_;
+
+  // The ActionProcessor used for running the actions.
+  ActionProcessor processor_;
+
+  // A fake system state.
+  MockSystemState mock_system_state_;
+
+  // The data being downloaded.
+  string data_;
+
+private:
+  // Callback used in StartDownload() method.
+  static gboolean StartProcessorInRunLoopForP2P(gpointer user_data) {
+    class P2PDownloadActionTest *test =
+        reinterpret_cast<P2PDownloadActionTest*>(user_data);
+    test->processor_.StartProcessing();
+    test->http_fetcher_->SetOffset(test->start_at_offset_);
+    return FALSE;
+  }
+
+  // Mainloop used to make StartDownload() synchronous.
+  GMainLoop *loop_;
+
+  // The requested starting offset passed to SetupDownload().
+  off_t start_at_offset_;
+};
+
+TEST_F(P2PDownloadActionTest, IsWrittenTo) {
+  SetupDownload(0);    // starting_offset
+  StartDownload(true); // use_p2p_to_share
+
+  // Check the p2p file and its content matches what was sent.
+  string file_id = download_action_->p2p_file_id();
+  EXPECT_NE(file_id, "");
+  EXPECT_EQ(p2p_manager_->FileGetSize(file_id), data_.length());
+  EXPECT_EQ(p2p_manager_->FileGetExpectedSize(file_id), data_.length());
+  string p2p_file_contents;
+  EXPECT_TRUE(ReadFileToString(p2p_manager_->FileGetPath(file_id),
+                               &p2p_file_contents));
+  EXPECT_EQ(data_, p2p_file_contents);
+}
+
+TEST_F(P2PDownloadActionTest, DeleteIfHoleExists) {
+  SetupDownload(1000); // starting_offset
+  StartDownload(true); // use_p2p_to_share
+
+  // DownloadAction should convey that the file is not being shared.
+  // and that we don't have any p2p files.
+  EXPECT_EQ(download_action_->p2p_file_id(), "");
+  EXPECT_EQ(p2p_manager_->CountSharedFiles(), 0);
+}
+
+TEST_F(P2PDownloadActionTest, CanAppend) {
+  SetupDownload(1000); // starting_offset
+
+  // Prepare the file with existing data before starting to write to
+  // it via DownloadAction.
+  string file_id = utils::CalculateP2PFileId("1234hash", data_.length());
+  ASSERT_TRUE(p2p_manager_->FileShare(file_id, data_.length()));
+  string existing_data;
+  for (unsigned int i = 0; i < 1000; i++)
+    existing_data += '0' + (i % 10);
+  ASSERT_EQ(WriteFile(p2p_manager_->FileGetPath(file_id), existing_data.c_str(),
+                      1000), 1000);
+
+  StartDownload(true); // use_p2p_to_share
+
+  // DownloadAction should convey the same file_id and the file should
+  // have the expected size.
+  EXPECT_EQ(download_action_->p2p_file_id(), file_id);
+  EXPECT_EQ(p2p_manager_->FileGetSize(file_id), data_.length());
+  EXPECT_EQ(p2p_manager_->FileGetExpectedSize(file_id), data_.length());
+  string p2p_file_contents;
+  // Check that the first 1000 bytes wasn't touched and that we
+  // appended the remaining as appropriate.
+  EXPECT_TRUE(ReadFileToString(p2p_manager_->FileGetPath(file_id),
+                               &p2p_file_contents));
+  EXPECT_EQ(existing_data, p2p_file_contents.substr(0, 1000));
+  EXPECT_EQ(data_.substr(1000), p2p_file_contents.substr(1000));
+}
+
+
+TEST_F(P2PDownloadActionTest, DeletePartialP2PFileIfResumingWithoutP2P) {
+  SetupDownload(1000); // starting_offset
+
+  // Prepare the file with all existing data before starting to write
+  // to it via DownloadAction.
+  string file_id = utils::CalculateP2PFileId("1234hash", data_.length());
+  ASSERT_TRUE(p2p_manager_->FileShare(file_id, data_.length()));
+  string existing_data;
+  for (unsigned int i = 0; i < 1000; i++)
+    existing_data += '0' + (i % 10);
+  ASSERT_EQ(WriteFile(p2p_manager_->FileGetPath(file_id), existing_data.c_str(),
+                      1000), 1000);
+
+  // Check that the file is there.
+  EXPECT_EQ(p2p_manager_->FileGetSize(file_id), 1000);
+  EXPECT_EQ(p2p_manager_->CountSharedFiles(), 1);
+
+  StartDownload(false); // use_p2p_to_share
+
+  // DownloadAction should have deleted the p2p file. Check that it's gone.
+  EXPECT_EQ(p2p_manager_->FileGetSize(file_id), -1);
+  EXPECT_EQ(p2p_manager_->CountSharedFiles(), 0);
+}
+
 }  // namespace chromeos_update_engine
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index 0e984c0..1c11a4b 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -9,6 +9,7 @@
 #include <sstream>
 #include <string>
 
+#include <base/bind.h>
 #include <base/logging.h>
 #include <base/rand_util.h>
 #include <base/string_number_conversions.h>
@@ -20,7 +21,9 @@
 
 #include "update_engine/action_pipe.h"
 #include "update_engine/constants.h"
+#include "update_engine/omaha_hash_calculator.h"
 #include "update_engine/omaha_request_params.h"
+#include "update_engine/p2p_manager.h"
 #include "update_engine/payload_state_interface.h"
 #include "update_engine/prefs_interface.h"
 #include "update_engine/utils.h"
@@ -47,6 +50,8 @@
 // Deprecated: "NeedsAdmin"
 static const char* kTagPrompt = "Prompt";
 static const char* kTagSha256 = "sha256";
+static const char* kTagDisableP2PForDownloading = "DisableP2PForDownloading";
+static const char* kTagDisableP2PForSharing = "DisableP2PForSharing";
 
 namespace {
 
@@ -490,10 +495,6 @@
   if (!ParseParams(doc, output_object, completer))
     return false;
 
-  output_object->update_exists = true;
-  SetOutputObject(*output_object);
-  completer->set_code(kErrorCodeSuccess);
-
   return true;
 }
 
@@ -693,6 +694,10 @@
   output_object->deadline = XmlGetProperty(pie_action_node, kTagDeadline);
   output_object->max_days_to_scatter =
       ParseInt(XmlGetProperty(pie_action_node, kTagMaxDaysToScatter));
+  output_object->disable_p2p_for_downloading =
+      (XmlGetProperty(pie_action_node, kTagDisableP2PForDownloading) == "true");
+  output_object->disable_p2p_for_sharing =
+      (XmlGetProperty(pie_action_node, kTagDisableP2PForSharing) == "true");
 
   string max = XmlGetProperty(pie_action_node, kTagMaxFailureCountPerUrl);
   if (!base::StringToUint(max, &output_object->max_failure_count_per_url))
@@ -771,6 +776,8 @@
   OmahaResponse output_object;
   if (!ParseResponse(doc.get(), &output_object, &completer))
     return;
+  output_object.update_exists = true;
+  SetOutputObject(output_object);
 
   if (params_->update_disabled()) {
     LOG(INFO) << "Ignoring Omaha updates as updates are disabled by policy.";
@@ -786,11 +793,16 @@
     return;
   }
 
-  if (ShouldDeferDownload(&output_object)) {
-    output_object.update_exists = false;
-    LOG(INFO) << "Ignoring Omaha updates as updates are deferred by policy.";
-    completer.set_code(kErrorCodeOmahaUpdateDeferredPerPolicy);
-    return;
+  // If Omaha says to disable p2p, respect that
+  if (output_object.disable_p2p_for_downloading) {
+    LOG(INFO) << "Forcibly disabling use of p2p for downloading as "
+              << "requested by Omaha.";
+    params_->set_use_p2p_for_downloading(false);
+  }
+  if (output_object.disable_p2p_for_sharing) {
+    LOG(INFO) << "Forcibly disabling use of p2p for sharing as "
+              << "requested by Omaha.";
+    params_->set_use_p2p_for_sharing(false);
   }
 
   // Update the payload state with the current response. The payload state
@@ -802,13 +814,100 @@
   PayloadStateInterface* payload_state = system_state_->payload_state();
   payload_state->SetResponse(output_object);
 
-  if (payload_state->ShouldBackoffDownload()) {
+  // It could be we've already exceeded the deadline for when p2p is
+  // allowed or that we've tried too many times with p2p. Check that.
+  if (params_->use_p2p_for_downloading()) {
+    payload_state->P2PNewAttempt();
+    if (!payload_state->P2PAttemptAllowed()) {
+      LOG(INFO) << "Forcibly disabling use of p2p for downloading because "
+                << "of previous failures when using p2p.";
+      params_->set_use_p2p_for_downloading(false);
+    }
+  }
+
+  // From here on, we'll complete stuff in CompleteProcessing() so
+  // disable |completer| since we'll create a new one in that
+  // function.
+  completer.set_should_complete(false);
+
+  // If we're allowed to use p2p for downloading we do not pay
+  // attention to wall-clock-based waiting if the URL is indeed
+  // available via p2p. Therefore, check if the file is available via
+  // p2p before deferring...
+  if (params_->use_p2p_for_downloading()) {
+    LookupPayloadViaP2P(output_object);
+  } else {
+    CompleteProcessing();
+  }
+}
+
+void OmahaRequestAction::CompleteProcessing() {
+  ScopedActionCompleter completer(processor_, this);
+  OmahaResponse& output_object = const_cast<OmahaResponse&>(GetOutputObject());
+  PayloadStateInterface* payload_state = system_state_->payload_state();
+
+  if (ShouldDeferDownload(&output_object)) {
     output_object.update_exists = false;
-    LOG(INFO) << "Ignoring Omaha updates in order to backoff our retry "
-                 "attempts";
-    completer.set_code(kErrorCodeOmahaUpdateDeferredForBackoff);
+    LOG(INFO) << "Ignoring Omaha updates as updates are deferred by policy.";
+    completer.set_code(kErrorCodeOmahaUpdateDeferredPerPolicy);
     return;
   }
+
+  // Only back off if we're not using p2p
+  if (params_->use_p2p_for_downloading() && !params_->p2p_url().empty()) {
+    LOG(INFO) << "Payload backoff logic is disabled because download "
+              << "will happen from local peer (via p2p).";
+  } else {
+    if (payload_state->ShouldBackoffDownload()) {
+      output_object.update_exists = false;
+      LOG(INFO) << "Ignoring Omaha updates in order to backoff our retry "
+                << "attempts";
+      completer.set_code(kErrorCodeOmahaUpdateDeferredForBackoff);
+      return;
+    }
+  }
+
+  completer.set_code(kErrorCodeSuccess);
+}
+
+void OmahaRequestAction::OnLookupPayloadViaP2PCompleted(const string& url) {
+  LOG(INFO) << "Lookup complete, p2p-client returned URL '" << url << "'";
+  if (!url.empty()) {
+    params_->set_p2p_url(url);
+  } else {
+    LOG(INFO) << "Forcibly disabling use of p2p for downloading "
+              << "because no suitable peer could be found.";
+    params_->set_use_p2p_for_downloading(false);
+  }
+  CompleteProcessing();
+}
+
+void OmahaRequestAction::LookupPayloadViaP2P(const OmahaResponse& response) {
+  // The kPrefsUpdateStateNextDataOffset state variable tracks the
+  // offset into the payload of the last completed operation if we're
+  // in the middle of an update. As such, p2p is only useful if the
+  // peer actually has that many bytes.
+  size_t minimum_size = 0;
+  int64_t next_data_offset = -1;
+  if (system_state_ != NULL &&
+      system_state_->prefs()->GetInt64(kPrefsUpdateStateNextDataOffset,
+                                       &next_data_offset)) {
+    if (next_data_offset > 0) {
+      minimum_size = next_data_offset;
+    }
+  }
+
+  string file_id = utils::CalculateP2PFileId(response.hash, response.size);
+  if (system_state_->p2p_manager() != NULL) {
+    LOG(INFO) << "Checking if payload is available via p2p, file_id="
+              << file_id << " minimum_size=" << minimum_size;
+    system_state_->p2p_manager()->LookupUrlForFile(
+        file_id,
+        minimum_size,
+        TimeDelta::FromHours(kMaxP2PNetworkWaitTimeSeconds),
+        base::Bind(&OmahaRequestAction::OnLookupPayloadViaP2PCompleted,
+                   base::Unretained(this)));
+  }
 }
 
 bool OmahaRequestAction::ShouldDeferDownload(OmahaResponse* output_object) {
@@ -817,6 +916,16 @@
     return false;
   }
 
+  // If we're using p2p to download _and_ we have a p2p URL, we never
+  // defer the download. This is because the download will always
+  // happen from a peer on the LAN and we've been waiting in line for
+  // our turn.
+  if (params_->use_p2p_for_downloading() && !params_->p2p_url().empty()) {
+    LOG(INFO) << "Download not deferred because download "
+              << "will happen from a local peer (via p2p).";
+    return false;
+  }
+
   // We should defer the downloads only if we've first satisfied the
   // wall-clock-based-waiting period and then the update-check-based waiting
   // period, if required.
diff --git a/omaha_request_action.h b/omaha_request_action.h
index 7a4d3f2..4e1bc7d 100644
--- a/omaha_request_action.h
+++ b/omaha_request_action.h
@@ -203,6 +203,16 @@
                    OmahaResponse* output_object,
                    ScopedActionCompleter* completer);
 
+  // Called by TransferComplete() to complete processing, either
+  // asynchronously after looking up resources via p2p or directly.
+  void CompleteProcessing();
+
+  // Helper to asynchronously look up payload on the LAN.
+  void LookupPayloadViaP2P(const OmahaResponse& response);
+
+  // Callback used by LookupPayloadViaP2P().
+  void OnLookupPayloadViaP2PCompleted(const std::string& url);
+
   // Global system context.
   SystemState* system_state_;
 
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index cb305f7..428a286 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -58,8 +58,10 @@
     false,  // delta okay
     false,  // interactive
     "http://url",
-    false, // update_disabled
-    ""); // target_version_prefix);
+    false,  // update_disabled
+    "",     // target_version_prefix
+    false,  // use_p2p_for_downloading
+    false); // use_p2p_for_sharing
 
 string GetNoUpdateResponse(const string& app_id) {
   return string(
@@ -79,7 +81,9 @@
                           const string& needsadmin,
                           const string& size,
                           const string& deadline,
-                          const string& max_days_to_scatter) {
+                          const string& max_days_to_scatter,
+                          bool disable_p2p_for_downloading,
+                          bool disable_p2p_for_sharing) {
   string response =
       "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
       "protocol=\"3.0\">"
@@ -99,6 +103,9 @@
       "sha256=\"" + hash + "\" "
       "needsadmin=\"" + needsadmin + "\" " +
       (deadline.empty() ? "" : ("deadline=\"" + deadline + "\" ")) +
+      (disable_p2p_for_downloading ?
+          "DisableP2PForDownloading=\"true\" " : "") +
+      (disable_p2p_for_sharing ? "DisableP2PForSharing=\"true\" " : "") +
       "/></actions></manifest></updatecheck></app></response>";
   LOG(INFO) << "Response = " << response;
   return response;
@@ -124,7 +131,9 @@
                             needsadmin,
                             size,
                             deadline,
-                            "7");
+                            "7",
+                            false,  // disable_p2p_for_downloading
+                            false); // disable_p2p_for sharing
 }
 
 class OmahaRequestActionTestProcessorDelegate : public ActionProcessorDelegate {
@@ -196,12 +205,16 @@
 
 // Returns true iff an output response was obtained from the
 // OmahaRequestAction. |prefs| may be NULL, in which case a local PrefsMock is
-// used. out_response may be NULL. If |fail_http_response_code| is non-negative,
+// used. |payload_state| may be NULL, in which case a local mock is used.
+// |p2p_manager| may be NULL, in which case a local mock is used.
+// out_response may be NULL. If |fail_http_response_code| is non-negative,
 // the transfer will fail with that code. |ping_only| is passed through to the
 // OmahaRequestAction constructor. out_post_data may be null; if non-null, the
 // post-data received by the mock HttpFetcher is returned.
 bool TestUpdateCheck(PrefsInterface* prefs,
-                     OmahaRequestParams params,
+                     PayloadStateInterface *payload_state,
+                     P2PManager *p2p_manager,
+                     OmahaRequestParams& params,
                      const string& http_response,
                      int fail_http_response_code,
                      bool ping_only,
@@ -218,6 +231,10 @@
   MockSystemState mock_system_state;
   if (prefs)
     mock_system_state.set_prefs(prefs);
+  if (payload_state)
+    mock_system_state.set_payload_state(payload_state);
+  if (p2p_manager)
+    mock_system_state.set_p2p_manager(p2p_manager);
   mock_system_state.set_request_params(&params);
   OmahaRequestAction action(&mock_system_state,
                             NULL,
@@ -276,6 +293,8 @@
   OmahaResponse response;
   ASSERT_TRUE(
       TestUpdateCheck(NULL,  // prefs
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       GetNoUpdateResponse(OmahaRequestParams::kAppId),
                       -1,
@@ -290,6 +309,8 @@
   OmahaResponse response;
   ASSERT_TRUE(
       TestUpdateCheck(NULL,  // prefs
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       GetUpdateResponse(OmahaRequestParams::kAppId,
                                         "1.2.3.4",  // version
@@ -323,6 +344,8 @@
   params.set_update_disabled(true);
   ASSERT_FALSE(
       TestUpdateCheck(NULL,  // prefs
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       params,
                       GetUpdateResponse(OmahaRequestParams::kAppId,
                                         "1.2.3.4",  // version
@@ -348,6 +371,8 @@
   params.set_update_disabled(true);
   ASSERT_TRUE(
       TestUpdateCheck(NULL,  // prefs
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       params,
                       GetNoUpdateResponse(OmahaRequestParams::kAppId),
                       -1,
@@ -376,6 +401,8 @@
 
   ASSERT_FALSE(
       TestUpdateCheck(&prefs,  // prefs
+                      NULL,    // payload_state
+                      NULL,    // p2p_manager
                       params,
                       GetUpdateResponse2(OmahaRequestParams::kAppId,
                                          "1.2.3.4",  // version
@@ -387,7 +414,9 @@
                                          "false",  // needs admin
                                          "123",  // size
                                          "",  // deadline
-                                         "7"), // max days to scatter
+                                         "7", // max days to scatter
+                                         false,  // disable_p2p_for_downloading
+                                         false), // disable_p2p_for sharing
                       -1,
                       false,  // ping_only
                       kErrorCodeOmahaUpdateDeferredPerPolicy,
@@ -399,6 +428,8 @@
   params.set_interactive(true);
   ASSERT_TRUE(
       TestUpdateCheck(&prefs,  // prefs
+                      NULL,    // payload_state
+                      NULL,    // p2p_manager
                       params,
                       GetUpdateResponse2(OmahaRequestParams::kAppId,
                                          "1.2.3.4",  // version
@@ -410,7 +441,9 @@
                                          "false",  // needs admin
                                          "123",  // size
                                          "",  // deadline
-                                         "7"), // max days to scatter
+                                         "7", // max days to scatter
+                                         false,  // disable_p2p_for_downloading
+                                         false), // disable_p2p_for sharing
                       -1,
                       false,  // ping_only
                       kErrorCodeSuccess,
@@ -440,6 +473,8 @@
 
   ASSERT_TRUE(
       TestUpdateCheck(&prefs,  // prefs
+                      NULL,    // payload_state
+                      NULL,    // p2p_manager
                       params,
                       GetUpdateResponse2(OmahaRequestParams::kAppId,
                                          "1.2.3.4",  // version
@@ -451,7 +486,9 @@
                                          "false",  // needs admin
                                          "123",  // size
                                          "",  // deadline
-                                         "7"), // max days to scatter
+                                         "7", // max days to scatter
+                                         false,  // disable_p2p_for_downloading
+                                         false), // disable_p2p_for sharing
                       -1,
                       false,  // ping_only
                       kErrorCodeSuccess,
@@ -481,6 +518,8 @@
 
   ASSERT_TRUE(
       TestUpdateCheck(&prefs,  // prefs
+                      NULL,    // payload_state
+                      NULL,    // p2p_manager
                       params,
                       GetUpdateResponse2(OmahaRequestParams::kAppId,
                                          "1.2.3.4",  // version
@@ -492,7 +531,9 @@
                                          "false",  // needs admin
                                          "123",  // size
                                          "",  // deadline
-                                         "0"), // max days to scatter
+                                         "0", // max days to scatter
+                                         false,  // disable_p2p_for_downloading
+                                         false), // disable_p2p_for sharing
                       -1,
                       false,  // ping_only
                       kErrorCodeSuccess,
@@ -523,6 +564,8 @@
 
   ASSERT_TRUE(TestUpdateCheck(
                       &prefs,  // prefs
+                      NULL,    // payload_state
+                      NULL,    // p2p_manager
                       params,
                       GetUpdateResponse2(OmahaRequestParams::kAppId,
                                          "1.2.3.4",  // version
@@ -534,7 +577,9 @@
                                          "false",  // needs admin
                                          "123",  // size
                                          "",  // deadline
-                                         "7"), // max days to scatter
+                                         "7", // max days to scatter
+                                         false,  // disable_p2p_for_downloading
+                                         false), // disable_p2p_for sharing
                       -1,
                       false,  // ping_only
                       kErrorCodeSuccess,
@@ -568,6 +613,8 @@
 
   ASSERT_FALSE(TestUpdateCheck(
                       &prefs,  // prefs
+                      NULL,    // payload_state
+                      NULL,    // p2p_manager
                       params,
                       GetUpdateResponse2(OmahaRequestParams::kAppId,
                                          "1.2.3.4",  // version
@@ -579,7 +626,9 @@
                                          "false",  // needs admin
                                          "123",  // size
                                          "",  // deadline
-                                         "7"), // max days to scatter
+                                         "7", // max days to scatter
+                                         false,  // disable_p2p_for_downloading
+                                         false), // disable_p2p_for sharing
                       -1,
                       false,  // ping_only
                       kErrorCodeOmahaUpdateDeferredPerPolicy,
@@ -595,6 +644,8 @@
   params.set_interactive(true);
   ASSERT_TRUE(
       TestUpdateCheck(&prefs,  // prefs
+                      NULL,    // payload_state
+                      NULL,    // p2p_manager
                       params,
                       GetUpdateResponse2(OmahaRequestParams::kAppId,
                                          "1.2.3.4",  // version
@@ -606,7 +657,9 @@
                                          "false",  // needs admin
                                          "123",  // size
                                          "",  // deadline
-                                         "7"), // max days to scatter
+                                         "7", // max days to scatter
+                                         false,  // disable_p2p_for_downloading
+                                         false), // disable_p2p_for sharing
                       -1,
                       false,  // ping_only
                       kErrorCodeSuccess,
@@ -638,6 +691,8 @@
 
   ASSERT_FALSE(TestUpdateCheck(
                       &prefs,  // prefs
+                      NULL,    // payload_state
+                      NULL,    // p2p_manager
                       params,
                       GetUpdateResponse2(OmahaRequestParams::kAppId,
                                          "1.2.3.4",  // version
@@ -649,7 +704,9 @@
                                          "false",  // needs admin
                                          "123",  // size
                                          "",  // deadline
-                                         "7"), // max days to scatter
+                                         "7", // max days to scatter
+                                         false,  // disable_p2p_for_downloading
+                                         false), // disable_p2p_for sharing
                       -1,
                       false,  // ping_only
                       kErrorCodeOmahaUpdateDeferredPerPolicy,
@@ -667,6 +724,8 @@
   params.set_interactive(true);
   ASSERT_TRUE(
       TestUpdateCheck(&prefs,  // prefs
+                      NULL,    // payload_state
+                      NULL,    // p2p_manager
                       params,
                       GetUpdateResponse2(OmahaRequestParams::kAppId,
                                          "1.2.3.4",  // version
@@ -678,7 +737,9 @@
                                          "false",  // needs admin
                                          "123",  // size
                                          "",  // deadline
-                                         "7"), // max days to scatter
+                                         "7", // max days to scatter
+                                         false,  // disable_p2p_for_downloading
+                                         false), // disable_p2p_for sharing
                       -1,
                       false,  // ping_only
                       kErrorCodeSuccess,
@@ -716,6 +777,8 @@
   OmahaResponse response;
   ASSERT_FALSE(
       TestUpdateCheck(NULL,  // prefs
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       "invalid xml>",
                       -1,
@@ -730,6 +793,8 @@
   OmahaResponse response;
   ASSERT_FALSE(
       TestUpdateCheck(NULL,  // prefs
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       "",
                       -1,
@@ -744,6 +809,8 @@
   OmahaResponse response;
   ASSERT_FALSE(TestUpdateCheck(
       NULL,  // prefs
+      NULL,  // payload_state
+      NULL,  // p2p_manager
       kDefaultTestParams,
       "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
       "<daystart elapsed_seconds=\"100\"/>"
@@ -762,6 +829,8 @@
   OmahaResponse response;
   ASSERT_FALSE(TestUpdateCheck(
       NULL,  // prefs
+      NULL,  // payload_state
+      NULL,  // p2p_manager
       kDefaultTestParams,
       "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
       "<daystart elapsed_seconds=\"100\"/>"
@@ -780,6 +849,8 @@
   OmahaResponse response;
   ASSERT_FALSE(TestUpdateCheck(
       NULL,  // prefs
+      NULL,  // payload_state
+      NULL,  // p2p_manager
       kDefaultTestParams,
       "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\">"
       "<daystart elapsed_seconds=\"100\"/>"
@@ -816,6 +887,8 @@
 
   OmahaResponse response;
   ASSERT_TRUE(TestUpdateCheck(NULL,  // prefs
+                              NULL,  // payload_state
+                              NULL,  // p2p_manager
                               kDefaultTestParams,
                               input_response,
                               -1,
@@ -901,11 +974,15 @@
                             false,  // delta okay
                             false,  // interactive
                             "http://url",
-                            false,   // update_disabled
-                            "");  // target_version_prefix
+                            false,  // update_disabled
+                            "",     // target_version_prefix
+                            false,  // use_p2p_for_downloading
+                            false); // use_p2p_for_sharing
   OmahaResponse response;
   ASSERT_FALSE(
       TestUpdateCheck(NULL,  // prefs
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       params,
                       "invalid xml>",
                       -1,
@@ -929,6 +1006,8 @@
   OmahaResponse response;
   ASSERT_TRUE(
       TestUpdateCheck(NULL,  // prefs
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       GetUpdateResponse(OmahaRequestParams::kAppId,
                                         "1.2.3.4",  // version
@@ -955,6 +1034,8 @@
   OmahaResponse response;
   ASSERT_TRUE(
       TestUpdateCheck(NULL,  // prefs
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       GetUpdateResponse(OmahaRequestParams::kAppId,
                                         "1.2.3.4",  // version
@@ -983,6 +1064,8 @@
       .WillOnce(DoAll(SetArgumentPointee<1>(string("")), Return(true)));
   EXPECT_CALL(prefs, SetString(kPrefsPreviousVersion, _)).Times(1);
   ASSERT_FALSE(TestUpdateCheck(&prefs,
+                               NULL,  // payload_state
+                               NULL,  // p2p_manager
                                kDefaultTestParams,
                                "invalid xml>",
                                -1,
@@ -1014,6 +1097,8 @@
   OmahaRequestParams params = kDefaultTestParams;
   params.set_update_disabled(true);
   ASSERT_FALSE(TestUpdateCheck(&prefs,
+                               NULL,  // payload_state
+                               NULL,  // p2p_manager
                                params,
                                "invalid xml>",
                                -1,
@@ -1119,9 +1204,13 @@
                               delta_okay,
                               false,  // interactive
                               "http://url",
-                              false, // update_disabled
-                              "");   // target_version_prefix
+                              false,  // update_disabled
+                              "",     // target_version_prefix
+                              false,  // use_p2p_for_downloading
+                              false); // use_p2p_for_sharing
     ASSERT_FALSE(TestUpdateCheck(NULL,  // prefs
+                                 NULL,  // payload_state
+                                 NULL,  // p2p_manager
                                  params,
                                  "invalid xml>",
                                  -1,
@@ -1155,12 +1244,16 @@
                               "OEM MODEL REV 1234",
                               "ChromeOSFirmware.1.0",
                               "EC100",
-                              true,  // delta_okay
+                              true,   // delta_okay
                               interactive,
                               "http://url",
-                              false, // update_disabled
-                              "");   // target_version_prefix
+                              false,  // update_disabled
+                              "",     // target_version_prefix
+                              false,  // use_p2p_for_downloading
+                              false); // use_p2p_for_sharing
     ASSERT_FALSE(TestUpdateCheck(NULL,  // prefs
+                                 NULL,  // payload_state
+                                 NULL,  // p2p_manager
                                  params,
                                  "invalid xml>",
                                  -1,
@@ -1211,6 +1304,8 @@
     vector<char> post_data;
     ASSERT_TRUE(
         TestUpdateCheck(&prefs,
+                        NULL,  // payload_state
+                        NULL,  // p2p_manager
                         kDefaultTestParams,
                         GetNoUpdateResponse(OmahaRequestParams::kAppId),
                         -1,
@@ -1243,6 +1338,8 @@
   vector<char> post_data;
   ASSERT_TRUE(
       TestUpdateCheck(&prefs,
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       GetNoUpdateResponse(OmahaRequestParams::kAppId),
                       -1,
@@ -1267,6 +1364,8 @@
   vector<char> post_data;
   ASSERT_TRUE(
       TestUpdateCheck(&prefs,
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       GetNoUpdateResponse(OmahaRequestParams::kAppId),
                       -1,
@@ -1292,6 +1391,8 @@
   vector<char> post_data;
   ASSERT_TRUE(
       TestUpdateCheck(&prefs,
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       GetNoUpdateResponse(OmahaRequestParams::kAppId),
                       -1,
@@ -1316,6 +1417,8 @@
   vector<char> post_data;
   EXPECT_TRUE(
       TestUpdateCheck(&prefs,
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       GetNoUpdateResponse(OmahaRequestParams::kAppId),
                       -1,
@@ -1341,6 +1444,8 @@
   vector<char> post_data;
   ASSERT_TRUE(
       TestUpdateCheck(&prefs,
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
                       "protocol=\"3.0\"><daystart elapsed_seconds=\"100\"/>"
@@ -1373,6 +1478,8 @@
       .WillOnce(Return(true));
   ASSERT_TRUE(
       TestUpdateCheck(&prefs,
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
                       "protocol=\"3.0\"><daystart elapsed_seconds=\"200\"/>"
@@ -1391,6 +1498,8 @@
   EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _)).Times(0);
   ASSERT_TRUE(
       TestUpdateCheck(&prefs,
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
                       "protocol=\"3.0\"><daystart blah=\"200\"/>"
@@ -1409,6 +1518,8 @@
   EXPECT_CALL(prefs, SetInt64(kPrefsLastRollCallPingDay, _)).Times(0);
   ASSERT_TRUE(
       TestUpdateCheck(&prefs,
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response "
                       "protocol=\"3.0\"><daystart elapsed_seconds=\"x\"/>"
@@ -1424,6 +1535,8 @@
 TEST(OmahaRequestActionTest, NoUniqueIDTest) {
   vector<char> post_data;
   ASSERT_FALSE(TestUpdateCheck(NULL,  // prefs
+                               NULL,  // payload_state
+                               NULL,  // p2p_manager
                                kDefaultTestParams,
                                "invalid xml>",
                                -1,
@@ -1441,6 +1554,8 @@
   OmahaResponse response;
   ASSERT_FALSE(
       TestUpdateCheck(NULL,  // prefs
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       "",
                       501,
@@ -1456,6 +1571,8 @@
   OmahaResponse response;
   ASSERT_FALSE(
       TestUpdateCheck(NULL,  // prefs
+                      NULL,  // payload_state
+                      NULL,  // p2p_manager
                       kDefaultTestParams,
                       "",
                       1500,
@@ -1485,6 +1602,8 @@
 
   ASSERT_FALSE(TestUpdateCheck(
                       &prefs,  // prefs
+                      NULL,    // payload_state
+                      NULL,    // p2p_manager
                       params,
                       GetUpdateResponse2(OmahaRequestParams::kAppId,
                                          "1.2.3.4",  // version
@@ -1496,7 +1615,9 @@
                                          "false",  // needs admin
                                          "123",  // size
                                          "",  // deadline
-                                         "7"), // max days to scatter
+                                         "7", // max days to scatter
+                                         false,  // disable_p2p_for_downloading
+                                         false), // disable_p2p_for sharing
                       -1,
                       false,  // ping_only
                       kErrorCodeOmahaUpdateDeferredPerPolicy,
@@ -1512,6 +1633,8 @@
   params.set_interactive(true);
   ASSERT_TRUE(
       TestUpdateCheck(&prefs,  // prefs
+                      NULL,    // payload_state
+                      NULL,    // p2p_manager
                       params,
                       GetUpdateResponse2(OmahaRequestParams::kAppId,
                                          "1.2.3.4",  // version
@@ -1523,7 +1646,9 @@
                                          "false",  // needs admin
                                          "123",  // size
                                          "",  // deadline
-                                         "7"), // max days to scatter
+                                         "7", // max days to scatter
+                                         false,  // disable_p2p_for_downloading
+                                         false), // disable_p2p_for sharing
                       -1,
                       false,  // ping_only
                       kErrorCodeSuccess,
@@ -1555,6 +1680,8 @@
   ASSERT_TRUE(prefs.SetInt64(kPrefsUpdateFirstSeenAt, t1.ToInternalValue()));
   ASSERT_TRUE(TestUpdateCheck(
                       &prefs,  // prefs
+                      NULL,    // payload_state
+                      NULL,    // p2p_manager
                       params,
                       GetUpdateResponse2(OmahaRequestParams::kAppId,
                                          "1.2.3.4",  // version
@@ -1566,7 +1693,9 @@
                                          "false",  // needs admin
                                          "123",  // size
                                          "",  // deadline
-                                         "7"), // max days to scatter
+                                         "7", // max days to scatter
+                                         false,  // disable_p2p_for_downloading
+                                         false), // disable_p2p_for sharing
                       -1,
                       false,  // ping_only
                       kErrorCodeSuccess,
@@ -1610,6 +1739,8 @@
   EXPECT_TRUE(params.to_more_stable_channel());
   EXPECT_TRUE(params.is_powerwash_allowed());
   ASSERT_FALSE(TestUpdateCheck(&prefs,
+                               NULL,    // payload_state
+                               NULL,    // p2p_manager
                                params,
                                "invalid xml>",
                                -1,
@@ -1655,6 +1786,8 @@
   EXPECT_FALSE(params.to_more_stable_channel());
   EXPECT_FALSE(params.is_powerwash_allowed());
   ASSERT_FALSE(TestUpdateCheck(&prefs,
+                               NULL,    // payload_state
+                               NULL,    // p2p_manager
                                params,
                                "invalid xml>",
                                -1,
@@ -1673,4 +1806,145 @@
   ASSERT_TRUE(utils::RecursiveUnlinkDir(test_dir));
 }
 
+void P2PTest(bool initial_allow_p2p_for_downloading,
+             bool initial_allow_p2p_for_sharing,
+             bool omaha_disable_p2p_for_downloading,
+             bool omaha_disable_p2p_for_sharing,
+             bool payload_state_allow_p2p_attempt,
+             bool expect_p2p_client_lookup,
+             const string& p2p_client_result_url,
+             bool expected_allow_p2p_for_downloading,
+             bool expected_allow_p2p_for_sharing,
+             const string& expected_p2p_url) {
+  OmahaResponse response;
+  OmahaRequestParams request_params = kDefaultTestParams;
+  request_params.set_use_p2p_for_downloading(initial_allow_p2p_for_downloading);
+  request_params.set_use_p2p_for_sharing(initial_allow_p2p_for_sharing);
+
+  MockPayloadState mock_payload_state;
+  EXPECT_CALL(mock_payload_state, P2PAttemptAllowed())
+      .WillRepeatedly(Return(payload_state_allow_p2p_attempt));
+  MockP2PManager mock_p2p_manager;
+  mock_p2p_manager.fake().SetLookupUrlForFileResult(p2p_client_result_url);
+
+  EXPECT_CALL(mock_p2p_manager, LookupUrlForFile(_, _, _, _))
+      .Times(expect_p2p_client_lookup ? 1 : 0);
+
+  ASSERT_TRUE(
+      TestUpdateCheck(NULL,  // prefs
+                      &mock_payload_state,
+                      &mock_p2p_manager,
+                      request_params,
+                      GetUpdateResponse2(OmahaRequestParams::kAppId,
+                                         "1.2.3.4",  // version
+                                         "http://more/info",
+                                         "true",  // prompt
+                                         "http://code/base/",  // dl url
+                                         "file.signed", // file name
+                                         "HASH1234=",  // checksum
+                                         "false",  // needs admin
+                                         "123",  // size
+                                         "",  // deadline
+                                         "7", // max days to scatter
+                                         omaha_disable_p2p_for_downloading,
+                                         omaha_disable_p2p_for_sharing),
+                      -1,
+                      false,  // ping_only
+                      kErrorCodeSuccess,
+                      &response,
+                      NULL));
+  EXPECT_TRUE(response.update_exists);
+
+  EXPECT_EQ(response.disable_p2p_for_downloading,
+            omaha_disable_p2p_for_downloading);
+  EXPECT_EQ(response.disable_p2p_for_sharing,
+            omaha_disable_p2p_for_sharing);
+
+  EXPECT_EQ(request_params.use_p2p_for_downloading(),
+            expected_allow_p2p_for_downloading);
+
+  EXPECT_EQ(request_params.use_p2p_for_sharing(),
+            expected_allow_p2p_for_sharing);
+
+  EXPECT_EQ(request_params.p2p_url(), expected_p2p_url);
+}
+
+TEST(OmahaRequestActionTest, P2PWithPeer) {
+  P2PTest(true,                  // initial_allow_p2p_for_downloading
+          true,                  // initial_allow_p2p_for_sharing
+          false,                 // omaha_disable_p2p_for_downloading
+          false,                 // omaha_disable_p2p_for_sharing
+          true,                  // payload_state_allow_p2p_attempt
+          true,                  // expect_p2p_client_lookup
+          "http://1.3.5.7/p2p",  // p2p_client_result_url
+          true,                  // expected_allow_p2p_for_downloading
+          true,                  // expected_allow_p2p_for_sharing
+          "http://1.3.5.7/p2p"); // expected_p2p_url
+}
+
+TEST(OmahaRequestActionTest, P2PWithoutPeer) {
+  P2PTest(true,                  // initial_allow_p2p_for_downloading
+          true,                  // initial_allow_p2p_for_sharing
+          false,                 // omaha_disable_p2p_for_downloading
+          false,                 // omaha_disable_p2p_for_sharing
+          true,                  // payload_state_allow_p2p_attempt
+          true,                  // expect_p2p_client_lookup
+          "",                    // p2p_client_result_url
+          false,                 // expected_allow_p2p_for_downloading
+          true,                  // expected_allow_p2p_for_sharing
+          "");                   // expected_p2p_url
+}
+
+TEST(OmahaRequestActionTest, P2PDownloadNotAllowed) {
+  P2PTest(false,                 // initial_allow_p2p_for_downloading
+          true,                  // initial_allow_p2p_for_sharing
+          false,                 // omaha_disable_p2p_for_downloading
+          false,                 // omaha_disable_p2p_for_sharing
+          true,                  // payload_state_allow_p2p_attempt
+          false,                 // expect_p2p_client_lookup
+          "unset",               // p2p_client_result_url
+          false,                 // expected_allow_p2p_for_downloading
+          true,                  // expected_allow_p2p_for_sharing
+          "");                   // expected_p2p_url
+}
+
+TEST(OmahaRequestActionTest, P2PWithPeerDownloadDisabledByOmaha) {
+  P2PTest(true,                  // initial_allow_p2p_for_downloading
+          true,                  // initial_allow_p2p_for_sharing
+          true,                  // omaha_disable_p2p_for_downloading
+          false,                 // omaha_disable_p2p_for_sharing
+          true,                  // payload_state_allow_p2p_attempt
+          false,                 // expect_p2p_client_lookup
+          "unset",               // p2p_client_result_url
+          false,                 // expected_allow_p2p_for_downloading
+          true,                  // expected_allow_p2p_for_sharing
+          "");                   // expected_p2p_url
+}
+
+TEST(OmahaRequestActionTest, P2PWithPeerSharingDisabledByOmaha) {
+  P2PTest(true,                  // initial_allow_p2p_for_downloading
+          true,                  // initial_allow_p2p_for_sharing
+          false,                 // omaha_disable_p2p_for_downloading
+          true,                  // omaha_disable_p2p_for_sharing
+          true,                  // payload_state_allow_p2p_attempt
+          true,                  // expect_p2p_client_lookup
+          "http://1.3.5.7/p2p",  // p2p_client_result_url
+          true,                  // expected_allow_p2p_for_downloading
+          false,                 // expected_allow_p2p_for_sharing
+          "http://1.3.5.7/p2p"); // expected_p2p_url
+}
+
+TEST(OmahaRequestActionTest, P2PWithPeerBothDisabledByOmaha) {
+  P2PTest(true,                  // initial_allow_p2p_for_downloading
+          true,                  // initial_allow_p2p_for_sharing
+          true,                  // omaha_disable_p2p_for_downloading
+          true,                  // omaha_disable_p2p_for_sharing
+          true,                  // payload_state_allow_p2p_attempt
+          false,                 // expect_p2p_client_lookup
+          "unset",               // p2p_client_result_url
+          false,                 // expected_allow_p2p_for_downloading
+          false,                 // expected_allow_p2p_for_sharing
+          "");                   // expected_p2p_url
+}
+
 }  // namespace chromeos_update_engine
diff --git a/omaha_request_params.h b/omaha_request_params.h
index 19a1900..f644775 100644
--- a/omaha_request_params.h
+++ b/omaha_request_params.h
@@ -44,7 +44,9 @@
         max_update_checks_allowed_(kDefaultMaxUpdateChecks),
         is_powerwash_allowed_(false),
         force_lock_down_(false),
-        forced_lock_down_(false) {
+        forced_lock_down_(false),
+        use_p2p_for_downloading_(false),
+        use_p2p_for_sharing_(false) {
     InitFromLsbValue();
   }
 
@@ -64,7 +66,9 @@
                      bool in_interactive,
                      const std::string& in_update_url,
                      bool in_update_disabled,
-                     const std::string& in_target_version_prefix)
+                     const std::string& in_target_version_prefix,
+                     bool in_use_p2p_for_downloading,
+                     bool in_use_p2p_for_sharing)
       : system_state_(system_state),
         os_platform_(in_os_platform),
         os_version_(in_os_version),
@@ -90,7 +94,9 @@
         max_update_checks_allowed_(kDefaultMaxUpdateChecks),
         is_powerwash_allowed_(false),
         force_lock_down_(false),
-        forced_lock_down_(false) {}
+        forced_lock_down_(false),
+        use_p2p_for_downloading_(in_use_p2p_for_downloading),
+        use_p2p_for_sharing_(in_use_p2p_for_sharing) {}
 
   // Setters and getters for the various properties.
   inline std::string os_platform() const { return os_platform_; }
@@ -171,6 +177,27 @@
     return max_update_checks_allowed_;
   }
 
+  inline void set_use_p2p_for_downloading(bool value) {
+    use_p2p_for_downloading_ = value;
+  }
+  inline bool use_p2p_for_downloading() const {
+    return use_p2p_for_downloading_;
+  }
+
+  inline void set_use_p2p_for_sharing(bool value) {
+    use_p2p_for_sharing_ = value;
+  }
+  inline bool use_p2p_for_sharing() const {
+    return use_p2p_for_sharing_;
+  }
+
+  inline void set_p2p_url(const std::string& value) {
+    p2p_url_ = value;
+  }
+  inline std::string p2p_url() const {
+    return p2p_url_;
+  }
+
   // True if we're trying to update to a more stable channel.
   // i.e. index(target_channel) > index(current_channel).
   bool to_more_stable_channel() const;
@@ -354,6 +381,18 @@
   bool force_lock_down_;
   bool forced_lock_down_;
 
+  // True if we may use p2p to download. This is based on owner
+  // preferences and policy.
+  bool use_p2p_for_downloading_;
+
+  // True if we may use p2p to share. This is based on owner
+  // preferences and policy.
+  bool use_p2p_for_sharing_;
+
+  // An URL to a local peer serving the requested payload or "" if no
+  // such peer is available.
+  std::string p2p_url_;
+
   // TODO(jaysri): Uncomment this after fixing unit tests, as part of
   // chromium-os:39752
   // DISALLOW_COPY_AND_ASSIGN(OmahaRequestParams);
diff --git a/omaha_response.h b/omaha_response.h
index 85cec5a..04fb878 100644
--- a/omaha_response.h
+++ b/omaha_response.h
@@ -26,7 +26,9 @@
         max_failure_count_per_url(0),
         prompt(false),
         is_delta_payload(false),
-        disable_payload_backoff(false) {}
+        disable_payload_backoff(false),
+        disable_p2p_for_downloading(false),
+        disable_p2p_for_sharing(false) {}
 
   // True iff there is an update to be downloaded.
   bool update_exists;
@@ -61,6 +63,12 @@
   // True if the Omaha rule instructs us to disable the backoff logic
   // on the client altogether. False otherwise.
   bool disable_payload_backoff;
+
+  // True if the Omaha rule instructs us to disable p2p for downloading.
+  bool disable_p2p_for_downloading;
+
+  // True if the Omaha rule instructs us to disable p2p for sharing.
+  bool disable_p2p_for_sharing;
 };
 COMPILE_ASSERT(sizeof(off_t) == 8, off_t_not_64bit);
 
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
index 2acc803..7df4c8d 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -69,6 +69,16 @@
   install_plan_.download_url = current_url;
   install_plan_.version = response.version;
 
+  OmahaRequestParams* params = system_state_->request_params();
+
+  // If we're using p2p to download and there is a local peer, use it.
+  if (params->use_p2p_for_downloading() && !params->p2p_url().empty()) {
+    LOG(INFO) << "Replacing URL " << install_plan_.download_url
+              << " with local URL " << params->p2p_url()
+              << " since p2p is enabled.";
+    install_plan_.download_url = params->p2p_url();
+  }
+
   // Fill up the other properties based on the response.
   install_plan_.payload_size = response.size;
   install_plan_.payload_hash = response.hash;
@@ -98,7 +108,6 @@
       system_state_->hardware()->KernelDeviceOfBootDevice(
           install_plan_.install_path);
 
-  OmahaRequestParams* params = system_state_->request_params();
   if (params->to_more_stable_channel() && params->is_powerwash_allowed())
     install_plan_.powerwash_required = true;
 
@@ -139,6 +148,13 @@
     return false;
   }
 
+  // If we're using p2p, |install_plan_.download_url| may contain a
+  // HTTP URL even if |response.payload_urls| contain only HTTPS URLs.
+  if (!StartsWithASCII(install_plan_.download_url, "https://", false)) {
+    LOG(INFO) << "Mandating hash checks since download_url is not HTTPS.";
+    return true;
+  }
+
   // TODO(jaysri): VALIDATION: For official builds, we currently waive hash
   // checks for HTTPS until we have rolled out at least once and are confident
   // nothing breaks. chromium-os:37082 tracks turning this on for HTTPS
diff --git a/omaha_response_handler_action_unittest.cc b/omaha_response_handler_action_unittest.cc
index c3efb8b..9624b41 100644
--- a/omaha_response_handler_action_unittest.cc
+++ b/omaha_response_handler_action_unittest.cc
@@ -362,4 +362,29 @@
   ASSERT_TRUE(utils::RecursiveUnlinkDir(test_dir));
 }
 
+TEST_F(OmahaResponseHandlerActionTest, P2PUrlIsUsedAndHashChecksMandatory) {
+  OmahaResponse in;
+  in.update_exists = true;
+  in.version = "a.b.c.d";
+  in.payload_urls.push_back("https://would.not/cause/hash/checks");
+  in.more_info_url = "http://more/info";
+  in.hash = "HASHj+";
+  in.size = 12;
+
+  MockSystemState mock_system_state;
+  OmahaRequestParams params(&mock_system_state);
+  mock_system_state.set_request_params(&params);
+
+  string p2p_url = "http://9.8.7.6/p2p";
+  params.set_p2p_url(p2p_url);
+  params.set_use_p2p_for_downloading(true);
+
+  InstallPlan install_plan;
+  EXPECT_TRUE(DoTestCommon(&mock_system_state, in, "/dev/sda5", "",
+                           &install_plan));
+  EXPECT_EQ(in.hash, install_plan.payload_hash);
+  EXPECT_EQ(install_plan.download_url, p2p_url);
+  EXPECT_TRUE(install_plan.hash_checks_mandatory);
+}
+
 }  // namespace chromeos_update_engine
diff --git a/update_attempter.cc b/update_attempter.cc
index 9ed321d..72d77c9 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -27,6 +27,7 @@
 #include "update_engine/omaha_request_action.h"
 #include "update_engine/omaha_request_params.h"
 #include "update_engine/omaha_response_handler_action.h"
+#include "update_engine/p2p_manager.h"
 #include "update_engine/payload_state_interface.h"
 #include "update_engine/postinstall_runner_action.h"
 #include "update_engine/prefs_interface.h"
@@ -218,6 +219,48 @@
   system_state_->set_device_policy(device_policy);
 }
 
+void UpdateAttempter::CalculateP2PParams(bool interactive) {
+  bool use_p2p_for_downloading = false;
+  bool use_p2p_for_sharing = false;
+
+  // Never use p2p for downloading in interactive checks unless the
+  // developer has opted in for it via a marker file.
+  //
+  // (Why would a developer want to opt in? If he's working on the
+  // update_engine or p2p codebases so he can actually test his
+  // code.).
+
+  if (system_state_ != NULL) {
+    if (!system_state_->p2p_manager()->IsP2PEnabled()) {
+      LOG(INFO) << "p2p is not enabled - disallowing p2p for both"
+                << " downloading and sharing.";
+    } else {
+      // Allow p2p for sharing, even in interactive checks.
+      use_p2p_for_sharing = true;
+      if (!interactive) {
+        LOG(INFO) << "Non-interactive check - allowing p2p for downloading";
+        use_p2p_for_downloading = true;
+      } else {
+        // TODO(zeuthen,chromium:273251): Remove this marker once we
+        // can easily trigger non-interactive checks.
+        if (utils::IsP2PAllowedForInteractiveChecks()) {
+          LOG(INFO) << "Found marker file at "
+                    << kP2PAllowInteractiveMarkerFile
+                    << " - allowing p2p for downloading despite the fact"
+                    << " the attempt is interactive.";
+          use_p2p_for_downloading = true;
+        } else {
+          LOG(INFO) << "Forcibly disabling use of p2p for downloading "
+                    << "since this update attempt is interactive.";
+        }
+      }
+    }
+  }
+
+  omaha_request_params_->set_use_p2p_for_downloading(use_p2p_for_downloading);
+  omaha_request_params_->set_use_p2p_for_sharing(use_p2p_for_sharing);
+}
+
 bool UpdateAttempter::CalculateUpdateParams(const string& app_version,
                                             const string& omaha_url,
                                             bool obey_proxies,
@@ -252,6 +295,19 @@
 
   CalculateScatteringParams(interactive);
 
+  CalculateP2PParams(interactive);
+  if (omaha_request_params_->use_p2p_for_downloading() ||
+      omaha_request_params_->use_p2p_for_sharing()) {
+    // OK, p2p is to be used - start it and perform housekeeping.
+    if (!StartP2PAndPerformHousekeeping()) {
+      // If this fails, disable p2p for this attempt
+      LOG(INFO) << "Forcibly disabling use of p2p since starting p2p or "
+                << "performing housekeeping failed.";
+      omaha_request_params_->set_use_p2p_for_downloading(false);
+      omaha_request_params_->set_use_p2p_for_sharing(false);
+    }
+  }
+
   // Determine whether an alternative test address should be used.
   string omaha_url_to_use = omaha_url;
   if ((is_using_test_url_ = (omaha_url_to_use.empty() && is_test_mode_))) {
@@ -311,6 +367,11 @@
             << ", Waiting Period = " << utils::FormatSecs(
                omaha_request_params_->waiting_period().InSeconds());
 
+  LOG(INFO) << "Use p2p For Downloading = "
+            << omaha_request_params_->use_p2p_for_downloading()
+            << ", Use p2p For Sharing = "
+            << omaha_request_params_->use_p2p_for_sharing();
+
   obeying_proxies_ = true;
   if (obey_proxies || proxy_manual_checks_ == 0) {
     LOG(INFO) << "forced to obey proxies";
@@ -1250,8 +1311,51 @@
   return false;
 }
 
+
 void UpdateAttempter::UpdateEngineStarted() {
   system_state_->payload_state()->UpdateEngineStarted();
+  StartP2PAtStartup();
+}
+
+bool UpdateAttempter::StartP2PAtStartup() {
+  if (system_state_ == NULL ||
+      !system_state_->p2p_manager()->IsP2PEnabled()) {
+    LOG(INFO) << "Not starting p2p at startup since it's not enabled.";
+    return false;
+  }
+
+  if (system_state_->p2p_manager()->CountSharedFiles() < 1) {
+    LOG(INFO) << "Not starting p2p at startup since our application "
+              << "is not sharing any files.";
+    return false;
+  }
+
+  return StartP2PAndPerformHousekeeping();
+}
+
+bool UpdateAttempter::StartP2PAndPerformHousekeeping() {
+  if (system_state_ == NULL)
+    return false;
+
+  if (!system_state_->p2p_manager()->IsP2PEnabled()) {
+    LOG(INFO) << "Not starting p2p since it's not enabled.";
+    return false;
+  }
+
+  LOG(INFO) << "Ensuring that p2p is running.";
+  if (!system_state_->p2p_manager()->EnsureP2PRunning()) {
+    LOG(ERROR) << "Error starting p2p.";
+    return false;
+  }
+
+  LOG(INFO) << "Performing p2p housekeeping.";
+  if (!system_state_->p2p_manager()->PerformHousekeeping()) {
+    LOG(ERROR) << "Error performing housekeeping for p2p.";
+    return false;
+  }
+
+  LOG(INFO) << "Done performing p2p housekeeping.";
+  return true;
 }
 
 }  // namespace chromeos_update_engine
diff --git a/update_attempter.h b/update_attempter.h
index dea9bc9..b2ee3b0 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -305,6 +305,20 @@
   // Returns True if successfully decremented, false otherwise.
   bool DecrementUpdateCheckCount();
 
+  // Starts p2p and performs housekeeping. Returns true only if p2p is
+  // running and housekeeping was done.
+  bool StartP2PAndPerformHousekeeping();
+
+  // Calculates whether peer-to-peer should be used. Sets the
+  // |use_p2p_to_download_| and |use_p2p_to_share_| parameters
+  // on the |omaha_request_params_| object.
+  void CalculateP2PParams(bool interactive);
+
+  // Starts P2P if it's enabled and there are files to actually share.
+  // Called only at program startup. Returns true only if p2p was
+  // started and housekeeping was performed.
+  bool StartP2PAtStartup();
+
   // 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.
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 1a52095..7036c83 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -13,6 +13,7 @@
 #include "update_engine/install_plan.h"
 #include "update_engine/mock_dbus_interface.h"
 #include "update_engine/mock_http_fetcher.h"
+#include "update_engine/mock_p2p_manager.h"
 #include "update_engine/mock_payload_state.h"
 #include "update_engine/mock_system_state.h"
 #include "update_engine/postinstall_runner_action.h"
@@ -125,6 +126,21 @@
   static gboolean StaticNoScatteringDoneDuringManualUpdateTestStart(
       gpointer data);
 
+  void P2PNotEnabledStart();
+  static gboolean StaticP2PNotEnabled(gpointer data);
+
+  void P2PEnabledStart();
+  static gboolean StaticP2PEnabled(gpointer data);
+
+  void P2PEnabledInteractiveStart();
+  static gboolean StaticP2PEnabledInteractive(gpointer data);
+
+  void P2PEnabledStartingFailsStart();
+  static gboolean StaticP2PEnabledStartingFails(gpointer data);
+
+  void P2PEnabledHousekeepingFailsStart();
+  static gboolean StaticP2PEnabledHousekeepingFails(gpointer data);
+
   NiceMock<MockSystemState> mock_system_state_;
   NiceMock<MockDbusGlib> dbus_;
   UpdateAttempterUnderTest attempter_;
@@ -673,6 +689,167 @@
   g_idle_add(&StaticQuitMainLoop, this);
 }
 
+TEST_F(UpdateAttempterTest, P2PNotStartedAtStartupWhenNotEnabled) {
+  MockP2PManager mock_p2p_manager;
+  mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+  mock_p2p_manager.fake().SetP2PEnabled(false);
+  EXPECT_CALL(mock_p2p_manager, EnsureP2PRunning()).Times(0);
+  attempter_.UpdateEngineStarted();
+}
+
+TEST_F(UpdateAttempterTest, P2PNotStartedAtStartupWhenEnabledButNotSharing) {
+  MockP2PManager mock_p2p_manager;
+  mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+  mock_p2p_manager.fake().SetP2PEnabled(true);
+  EXPECT_CALL(mock_p2p_manager, EnsureP2PRunning()).Times(0);
+  attempter_.UpdateEngineStarted();
+}
+
+TEST_F(UpdateAttempterTest, P2PStartedAtStartupWhenEnabledAndSharing) {
+  MockP2PManager mock_p2p_manager;
+  mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+  mock_p2p_manager.fake().SetP2PEnabled(true);
+  mock_p2p_manager.fake().SetCountSharedFilesResult(1);
+  EXPECT_CALL(mock_p2p_manager, EnsureP2PRunning()).Times(1);
+  attempter_.UpdateEngineStarted();
+}
+
+TEST_F(UpdateAttempterTest, P2PNotEnabled) {
+  loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+  g_idle_add(&StaticP2PNotEnabled, this);
+  g_main_loop_run(loop_);
+  g_main_loop_unref(loop_);
+  loop_ = NULL;
+}
+gboolean UpdateAttempterTest::StaticP2PNotEnabled(gpointer data) {
+  UpdateAttempterTest* ua_test = reinterpret_cast<UpdateAttempterTest*>(data);
+  ua_test->P2PNotEnabledStart();
+  return FALSE;
+}
+void UpdateAttempterTest::P2PNotEnabledStart() {
+  // If P2P is not enabled, check that we do not attempt housekeeping
+  // and do not convey that p2p is to be used.
+  MockP2PManager mock_p2p_manager;
+  mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+  mock_p2p_manager.fake().SetP2PEnabled(false);
+  EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
+  attempter_.Update("", "", false, false, false);
+  EXPECT_FALSE(attempter_.omaha_request_params_->use_p2p_for_downloading());
+  EXPECT_FALSE(attempter_.omaha_request_params_->use_p2p_for_sharing());
+  g_idle_add(&StaticQuitMainLoop, this);
+}
+
+TEST_F(UpdateAttempterTest, P2PEnabledStartingFails) {
+  loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+  g_idle_add(&StaticP2PEnabledStartingFails, this);
+  g_main_loop_run(loop_);
+  g_main_loop_unref(loop_);
+  loop_ = NULL;
+}
+gboolean UpdateAttempterTest::StaticP2PEnabledStartingFails(
+    gpointer data) {
+  UpdateAttempterTest* ua_test = reinterpret_cast<UpdateAttempterTest*>(data);
+  ua_test->P2PEnabledStartingFailsStart();
+  return FALSE;
+}
+void UpdateAttempterTest::P2PEnabledStartingFailsStart() {
+  // If p2p is enabled, but starting it fails ensure we don't do
+  // any housekeeping and do not convey that p2p should be used.
+  MockP2PManager mock_p2p_manager;
+  mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+  mock_p2p_manager.fake().SetP2PEnabled(true);
+  mock_p2p_manager.fake().SetEnsureP2PRunningResult(false);
+  mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
+  EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
+  attempter_.Update("", "", false, false, false);
+  EXPECT_FALSE(attempter_.omaha_request_params_->use_p2p_for_downloading());
+  EXPECT_FALSE(attempter_.omaha_request_params_->use_p2p_for_sharing());
+  g_idle_add(&StaticQuitMainLoop, this);
+}
+
+TEST_F(UpdateAttempterTest, P2PEnabledHousekeepingFails) {
+  loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+  g_idle_add(&StaticP2PEnabledHousekeepingFails, this);
+  g_main_loop_run(loop_);
+  g_main_loop_unref(loop_);
+  loop_ = NULL;
+}
+gboolean UpdateAttempterTest::StaticP2PEnabledHousekeepingFails(
+    gpointer data) {
+  UpdateAttempterTest* ua_test = reinterpret_cast<UpdateAttempterTest*>(data);
+  ua_test->P2PEnabledHousekeepingFailsStart();
+  return FALSE;
+}
+void UpdateAttempterTest::P2PEnabledHousekeepingFailsStart() {
+  // If p2p is enabled, starting it works but housekeeping fails, ensure
+  // we do not convey p2p is to be used.
+  MockP2PManager mock_p2p_manager;
+  mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+  mock_p2p_manager.fake().SetP2PEnabled(true);
+  mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
+  mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
+  EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(1);
+  attempter_.Update("", "", false, false, false);
+  EXPECT_FALSE(attempter_.omaha_request_params_->use_p2p_for_downloading());
+  EXPECT_FALSE(attempter_.omaha_request_params_->use_p2p_for_sharing());
+  g_idle_add(&StaticQuitMainLoop, this);
+}
+
+TEST_F(UpdateAttempterTest, P2PEnabled) {
+  loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+  g_idle_add(&StaticP2PEnabled, this);
+  g_main_loop_run(loop_);
+  g_main_loop_unref(loop_);
+  loop_ = NULL;
+}
+gboolean UpdateAttempterTest::StaticP2PEnabled(gpointer data) {
+  UpdateAttempterTest* ua_test = reinterpret_cast<UpdateAttempterTest*>(data);
+  ua_test->P2PEnabledStart();
+  return FALSE;
+}
+void UpdateAttempterTest::P2PEnabledStart() {
+  MockP2PManager mock_p2p_manager;
+  mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+  // If P2P is enabled and starting it works, check that we performed
+  // housekeeping and that we convey p2p should be used.
+  mock_p2p_manager.fake().SetP2PEnabled(true);
+  mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
+  mock_p2p_manager.fake().SetPerformHousekeepingResult(true);
+  EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(1);
+  attempter_.Update("", "", false, false, false);
+  EXPECT_TRUE(attempter_.omaha_request_params_->use_p2p_for_downloading());
+  EXPECT_TRUE(attempter_.omaha_request_params_->use_p2p_for_sharing());
+  g_idle_add(&StaticQuitMainLoop, this);
+}
+
+TEST_F(UpdateAttempterTest, P2PEnabledInteractive) {
+  loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+  g_idle_add(&StaticP2PEnabledInteractive, this);
+  g_main_loop_run(loop_);
+  g_main_loop_unref(loop_);
+  loop_ = NULL;
+}
+gboolean UpdateAttempterTest::StaticP2PEnabledInteractive(gpointer data) {
+  UpdateAttempterTest* ua_test = reinterpret_cast<UpdateAttempterTest*>(data);
+  ua_test->P2PEnabledInteractiveStart();
+  return FALSE;
+}
+void UpdateAttempterTest::P2PEnabledInteractiveStart() {
+  MockP2PManager mock_p2p_manager;
+  mock_system_state_.set_p2p_manager(&mock_p2p_manager);
+  // For an interactive check, if P2P is enabled and starting it
+  // works, check that we performed housekeeping and that we convey
+  // p2p should be used for sharing but NOT for downloading.
+  mock_p2p_manager.fake().SetP2PEnabled(true);
+  mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
+  mock_p2p_manager.fake().SetPerformHousekeepingResult(true);
+  EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(1);
+  attempter_.Update("", "", false, true /* interactive */, false);
+  EXPECT_FALSE(attempter_.omaha_request_params_->use_p2p_for_downloading());
+  EXPECT_TRUE(attempter_.omaha_request_params_->use_p2p_for_sharing());
+  g_idle_add(&StaticQuitMainLoop, this);
+}
+
 TEST_F(UpdateAttempterTest, ReadTargetVersionPrefixFromPolicy) {
   loop_ = g_main_loop_new(g_main_context_default(), FALSE);
   g_idle_add(&StaticReadTargetVersionPrefixFromPolicyTestStart, this);
diff --git a/utils.cc b/utils.cc
index 8b4bffe..8f04134 100644
--- a/utils.cc
+++ b/utils.cc
@@ -1026,6 +1026,11 @@
   return true;
 }
 
+bool IsP2PAllowedForInteractiveChecks() {
+  struct stat statbuf;
+  return stat(kP2PAllowInteractiveMarkerFile, &statbuf) == 0;
+}
+
 Time TimeFromStructTimespec(struct timespec *ts) {
   int64 us = static_cast<int64>(ts->tv_sec) * Time::kMicrosecondsPerSecond +
       static_cast<int64>(ts->tv_nsec) / Time::kNanosecondsPerMicrosecond;
@@ -1056,6 +1061,16 @@
   return str;
 }
 
+string CalculateP2PFileId(const string& payload_hash, size_t payload_size) {
+  string encoded_hash;
+  OmahaHashCalculator::Base64Encode(payload_hash.c_str(),
+                                    payload_hash.size(),
+                                    &encoded_hash);
+  return StringPrintf("cros_update_size_%zu_hash_%s",
+                      payload_size,
+                      encoded_hash.c_str());
+}
+
 }  // namespace utils
 
 }  // namespace chromeos_update_engine
diff --git a/utils.h b/utils.h
index 79b6917..5518da6 100644
--- a/utils.h
+++ b/utils.h
@@ -35,6 +35,16 @@
 // boot mode. Returns false if the boot mode is developer.
 bool IsNormalBootMode();
 
+// Returns true if and only if P2P is allowed for interactive update
+// checks. This is implemented by checking the existence of a marker
+// file that can be created by the following command
+//
+// # touch /mnt/stateful_partition/p2p-allow-interactive
+//
+// This function returns false if the required marker file does not
+// exist or if an error occurred.
+bool IsP2PAllowedForInteractiveChecks();
+
 // Converts a struct timespec representing a number of seconds since
 // the Unix epoch to a base::Time. Sub-microsecond time is rounded
 // down.
@@ -49,6 +59,10 @@
 // Does no escaping, only use this for presentation in error messages.
 std::string StringVectorToString(const std::vector<std::string> &vector);
 
+// Calculates the p2p file id from payload hash and size
+std::string CalculateP2PFileId(const std::string& payload_hash,
+                               size_t payload_size);
+
 // Returns the HWID or an empty string on error.
 std::string GetHardwareClass();