update_engine: Create cros vs. aosp boundary clear

Its time to make the boundary between Chrome OS and Android code more
clear. This CL moves all CrOS only code to "chromeos" directory and the
same for Android (in "android" directory). This way we would easily know
which code is uses in which project and can keep the code cleaner and
more maintainable.

One big remaining problem is download_action* files. It seems like
DownloadAction class does a lot of things that chrome OS needs and it
depends on a lot of Chrome OS stuff, but Android is also using thie
Action in a way that circumvent the Chrome OS stuff. For example Android
checks for SystemState to be nullptr to not do things. This is really
fragile and needs to change. Probably Android Team has to implement
their own DownloadAction of some sort and not re use the Chrome OS one
in a very fragile way.

Removed a few android files that have not been used anywhere.

Changed some clang-format and lint issues in order to pass preupload.

BUG=b:171829801
TEST=cros_workon_make --board reef --test update_engine

Change-Id: I3fff1d4a100a065a5c1484a845241b5521614d9f
Reviewed-on: https://chromium-review.googlesource.com/c/aosp/platform/system/update_engine/+/2508965
Tested-by: Amin Hassani <ahassani@chromium.org>
Auto-Submit: Amin Hassani <ahassani@chromium.org>
Reviewed-by: Jae Hoon Kim <kimjae@chromium.org>
Reviewed-by: Tianjie Xu <xunchang@google.com>
Reviewed-by: Kelvin Zhang <zhangkelvin@google.com>
Commit-Queue: Amin Hassani <ahassani@chromium.org>
diff --git a/cros/p2p_manager.cc b/cros/p2p_manager.cc
new file mode 100644
index 0000000..dc12b35
--- /dev/null
+++ b/cros/p2p_manager.cc
@@ -0,0 +1,738 @@
+//
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// This provides access to timestamps with nanosecond resolution in
+// struct stat, See NOTES in stat(2) for details.
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE
+#endif
+
+#include "update_engine/cros/p2p_manager.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/falloc.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <base/bind.h>
+#include <base/files/file_enumerator.h>
+#include <base/files/file_path.h>
+#include <base/format_macros.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+#include "update_engine/common/subprocess.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/update_manager/policy.h"
+#include "update_engine/update_manager/update_manager.h"
+
+using base::Bind;
+using base::Callback;
+using base::FilePath;
+using base::StringPrintf;
+using base::Time;
+using base::TimeDelta;
+using brillo::MessageLoop;
+using chromeos_update_manager::EvalStatus;
+using chromeos_update_manager::Policy;
+using chromeos_update_manager::UpdateManager;
+using std::pair;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// The default p2p directory.
+const char kDefaultP2PDir[] = "/var/cache/p2p";
+
+// The p2p xattr used for conveying the final size of a file - see the
+// p2p ddoc for details.
+const char kCrosP2PFileSizeXAttrName[] = "user.cros-p2p-filesize";
+
+}  // namespace
+
+// The default P2PManager::Configuration implementation.
+class ConfigurationImpl : public P2PManager::Configuration {
+ public:
+  ConfigurationImpl() {}
+
+  FilePath GetP2PDir() override { return FilePath(kDefaultP2PDir); }
+
+  vector<string> GetInitctlArgs(bool is_start) override {
+    vector<string> args;
+    args.push_back("initctl");
+    args.push_back(is_start ? "start" : "stop");
+    args.push_back("p2p");
+    return args;
+  }
+
+  vector<string> GetP2PClientArgs(const string& file_id,
+                                  size_t minimum_size) override {
+    vector<string> args;
+    args.push_back("p2p-client");
+    args.push_back(string("--get-url=") + file_id);
+    args.push_back(StringPrintf("--minimum-size=%" PRIuS, minimum_size));
+    return args;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ConfigurationImpl);
+};
+
+// The default P2PManager implementation.
+class P2PManagerImpl : public P2PManager {
+ public:
+  P2PManagerImpl(Configuration* configuration,
+                 ClockInterface* clock,
+                 UpdateManager* update_manager,
+                 const string& file_extension,
+                 const int num_files_to_keep,
+                 const TimeDelta& max_file_age);
+
+  // P2PManager methods.
+  void SetDevicePolicy(const policy::DevicePolicy* device_policy) override;
+  bool IsP2PEnabled() override;
+  bool EnsureP2PRunning() override;
+  bool EnsureP2PNotRunning() override;
+  bool PerformHousekeeping() override;
+  void LookupUrlForFile(const string& file_id,
+                        size_t minimum_size,
+                        TimeDelta max_time_to_wait,
+                        LookupCallback callback) override;
+  bool FileShare(const string& file_id, size_t expected_size) override;
+  FilePath FileGetPath(const string& file_id) override;
+  ssize_t FileGetSize(const string& file_id) override;
+  ssize_t FileGetExpectedSize(const string& file_id) override;
+  bool FileGetVisible(const string& file_id, bool* out_result) override;
+  bool FileMakeVisible(const string& file_id) override;
+  int CountSharedFiles() override;
+
+ private:
+  // Enumeration for specifying visibility.
+  enum Visibility { kVisible, kNonVisible };
+
+  // Returns "." + |file_extension_| + ".p2p" if |visibility| is
+  // |kVisible|. Returns the same concatenated with ".tmp" otherwise.
+  string GetExt(Visibility visibility);
+
+  // Gets the on-disk path for |file_id| depending on if the file
+  // is visible or not.
+  FilePath GetPath(const string& file_id, Visibility visibility);
+
+  // Utility function used by EnsureP2PRunning() and EnsureP2PNotRunning().
+  bool EnsureP2P(bool should_be_running);
+
+  // Utility function to delete a file given by |path| and log the
+  // path as well as |reason|. Returns false on failure.
+  bool DeleteP2PFile(const FilePath& path, const string& reason);
+
+  // Schedules an async request for tracking changes in P2P enabled status.
+  void ScheduleEnabledStatusChange();
+
+  // An async callback used by the above.
+  void OnEnabledStatusChange(EvalStatus status, const bool& result);
+
+  // The device policy being used or null if no policy is being used.
+  const policy::DevicePolicy* device_policy_ = nullptr;
+
+  // Configuration object.
+  unique_ptr<Configuration> configuration_;
+
+  // Object for telling the time.
+  ClockInterface* clock_;
+
+  // A pointer to the global Update Manager.
+  UpdateManager* update_manager_;
+
+  // A short string unique to the application (for example "cros_au")
+  // used to mark a file as being owned by a particular application.
+  const string file_extension_;
+
+  // If non-zero, this number denotes how many files in /var/cache/p2p
+  // owned by the application (cf. |file_extension_|) to keep after
+  // performing housekeeping.
+  const int num_files_to_keep_;
+
+  // If non-zero, files older than this will not be kept after
+  // performing housekeeping.
+  const TimeDelta max_file_age_;
+
+  // The string ".p2p".
+  static const char kP2PExtension[];
+
+  // The string ".tmp".
+  static const char kTmpExtension[];
+
+  // Whether P2P service may be running; initially, we assume it may be.
+  bool may_be_running_ = true;
+
+  // The current known enabled status of the P2P feature (initialized lazily),
+  // and whether an async status check has been scheduled.
+  bool is_enabled_;
+  bool waiting_for_enabled_status_change_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(P2PManagerImpl);
+};
+
+const char P2PManagerImpl::kP2PExtension[] = ".p2p";
+
+const char P2PManagerImpl::kTmpExtension[] = ".tmp";
+
+P2PManagerImpl::P2PManagerImpl(Configuration* configuration,
+                               ClockInterface* clock,
+                               UpdateManager* update_manager,
+                               const string& file_extension,
+                               const int num_files_to_keep,
+                               const TimeDelta& max_file_age)
+    : clock_(clock),
+      update_manager_(update_manager),
+      file_extension_(file_extension),
+      num_files_to_keep_(num_files_to_keep),
+      max_file_age_(max_file_age) {
+  configuration_.reset(configuration != nullptr ? configuration
+                                                : new ConfigurationImpl());
+}
+
+void P2PManagerImpl::SetDevicePolicy(
+    const policy::DevicePolicy* device_policy) {
+  device_policy_ = device_policy;
+}
+
+bool P2PManagerImpl::IsP2PEnabled() {
+  if (!waiting_for_enabled_status_change_) {
+    // Get and store an initial value.
+    if (update_manager_->PolicyRequest(&Policy::P2PEnabled, &is_enabled_) ==
+        EvalStatus::kFailed) {
+      is_enabled_ = false;
+      LOG(ERROR) << "Querying P2P enabled status failed, disabling.";
+    }
+
+    // Track future changes (async).
+    ScheduleEnabledStatusChange();
+  }
+
+  return is_enabled_;
+}
+
+bool P2PManagerImpl::EnsureP2P(bool should_be_running) {
+  int return_code = 0;
+  string stderr;
+
+  may_be_running_ = true;  // Unless successful, we must be conservative.
+
+  vector<string> args = configuration_->GetInitctlArgs(should_be_running);
+  if (!Subprocess::SynchronousExec(args, &return_code, nullptr, &stderr)) {
+    LOG(ERROR) << "Error spawning " << utils::StringVectorToString(args);
+    return false;
+  }
+
+  // If initctl(8) does not exit normally (exit status other than zero), ensure
+  // that the error message is not benign by scanning stderr; this is a
+  // necessity because initctl does not offer actions such as "start if not
+  // running" or "stop if running".
+  // TODO(zeuthen,chromium:277051): Avoid doing this.
+  if (return_code != 0) {
+    const char* expected_error_message =
+        should_be_running ? "initctl: Job is already running: p2p\n"
+                          : "initctl: Unknown instance \n";
+    if (stderr != expected_error_message)
+      return false;
+  }
+
+  may_be_running_ = should_be_running;  // Successful after all.
+  return true;
+}
+
+bool P2PManagerImpl::EnsureP2PRunning() {
+  return EnsureP2P(true);
+}
+
+bool P2PManagerImpl::EnsureP2PNotRunning() {
+  return EnsureP2P(false);
+}
+
+// Returns True if the timestamp in the first pair is greater than the
+// timestamp in the latter. If used with std::sort() this will yield a
+// sequence of elements where newer (high timestamps) elements precede
+// older ones (low timestamps).
+static bool MatchCompareFunc(const pair<FilePath, Time>& a,
+                             const pair<FilePath, Time>& b) {
+  return a.second > b.second;
+}
+
+string P2PManagerImpl::GetExt(Visibility visibility) {
+  string ext = string(".") + file_extension_ + kP2PExtension;
+  switch (visibility) {
+    case kVisible:
+      break;
+    case kNonVisible:
+      ext += kTmpExtension;
+      break;
+      // Don't add a default case to let the compiler warn about newly
+      // added enum values.
+  }
+  return ext;
+}
+
+FilePath P2PManagerImpl::GetPath(const string& file_id, Visibility visibility) {
+  return configuration_->GetP2PDir().Append(file_id + GetExt(visibility));
+}
+
+bool P2PManagerImpl::DeleteP2PFile(const FilePath& path, const string& reason) {
+  LOG(INFO) << "Deleting p2p file " << path.value() << " (reason: " << reason
+            << ")";
+  if (unlink(path.value().c_str()) != 0) {
+    PLOG(ERROR) << "Error deleting p2p file " << path.value();
+    return false;
+  }
+  return true;
+}
+
+bool P2PManagerImpl::PerformHousekeeping() {
+  // Open p2p dir.
+  FilePath p2p_dir = configuration_->GetP2PDir();
+  const string ext_visible = GetExt(kVisible);
+  const string ext_non_visible = GetExt(kNonVisible);
+
+  bool deletion_failed = false;
+  vector<pair<FilePath, Time>> matches;
+
+  base::FileEnumerator dir(p2p_dir, false, base::FileEnumerator::FILES);
+  // Go through all files and collect their mtime.
+  for (FilePath name = dir.Next(); !name.empty(); name = dir.Next()) {
+    if (!(base::EndsWith(
+              name.value(), ext_visible, base::CompareCase::SENSITIVE) ||
+          base::EndsWith(
+              name.value(), ext_non_visible, base::CompareCase::SENSITIVE))) {
+      continue;
+    }
+
+    Time time = dir.GetInfo().GetLastModifiedTime();
+
+    // If instructed to keep only files younger than a given age
+    // (|max_file_age_| != 0), delete files satisfying this criteria
+    // right now. Otherwise add it to a list we'll consider for later.
+    if (clock_ != nullptr && max_file_age_ != TimeDelta() &&
+        clock_->GetWallclockTime() - time > max_file_age_) {
+      if (!DeleteP2PFile(name, "file too old"))
+        deletion_failed = true;
+    } else {
+      matches.push_back(std::make_pair(name, time));
+    }
+  }
+
+  // If instructed to only keep N files (|max_files_to_keep_ != 0),
+  // sort list of matches, newest (biggest time) to oldest (lowest
+  // time). Then delete starting at element |num_files_to_keep_|.
+  if (num_files_to_keep_ > 0) {
+    std::sort(matches.begin(), matches.end(), MatchCompareFunc);
+    vector<pair<FilePath, Time>>::const_iterator i;
+    for (i = matches.begin() + num_files_to_keep_; i < matches.end(); ++i) {
+      if (!DeleteP2PFile(i->first, "too many files"))
+        deletion_failed = true;
+    }
+  }
+
+  return !deletion_failed;
+}
+
+// Helper class for implementing LookupUrlForFile().
+class LookupData {
+ public:
+  explicit LookupData(P2PManager::LookupCallback callback)
+      : callback_(callback) {}
+
+  ~LookupData() {
+    if (timeout_task_ != MessageLoop::kTaskIdNull)
+      MessageLoop::current()->CancelTask(timeout_task_);
+    if (child_pid_)
+      Subprocess::Get().KillExec(child_pid_);
+  }
+
+  void InitiateLookup(const vector<string>& cmd, TimeDelta timeout) {
+    // NOTE: if we fail early (i.e. in this method), we need to schedule
+    // an idle to report the error. This is because we guarantee that
+    // the callback is always called from the message loop (this
+    // guarantee is useful for testing).
+
+    // We expect to run just "p2p-client" and find it in the path.
+    child_pid_ = Subprocess::Get().ExecFlags(
+        cmd,
+        Subprocess::kSearchPath,
+        {},
+        Bind(&LookupData::OnLookupDone, base::Unretained(this)));
+
+    if (!child_pid_) {
+      LOG(ERROR) << "Error spawning " << utils::StringVectorToString(cmd);
+      ReportErrorAndDeleteInIdle();
+      return;
+    }
+
+    if (timeout > TimeDelta()) {
+      timeout_task_ = MessageLoop::current()->PostDelayedTask(
+          FROM_HERE,
+          Bind(&LookupData::OnTimeout, base::Unretained(this)),
+          timeout);
+    }
+  }
+
+ private:
+  void ReportErrorAndDeleteInIdle() {
+    MessageLoop::current()->PostTask(
+        FROM_HERE,
+        Bind(&LookupData::OnIdleForReportErrorAndDelete,
+             base::Unretained(this)));
+  }
+
+  void OnIdleForReportErrorAndDelete() {
+    ReportError();
+    delete this;
+  }
+
+  void IssueCallback(const string& url) {
+    if (!callback_.is_null())
+      callback_.Run(url);
+  }
+
+  void ReportError() {
+    if (reported_)
+      return;
+    IssueCallback("");
+    reported_ = true;
+  }
+
+  void ReportSuccess(const string& output) {
+    if (reported_)
+      return;
+    string url = output;
+    size_t newline_pos = url.find('\n');
+    if (newline_pos != string::npos)
+      url.resize(newline_pos);
+
+    // Since p2p-client(1) is constructing this URL itself strictly
+    // speaking there's no need to validate it... but, anyway, can't
+    // hurt.
+    if (url.compare(0, 7, "http://") == 0) {
+      IssueCallback(url);
+    } else {
+      LOG(ERROR) << "p2p URL '" << url << "' does not look right. Ignoring.";
+      ReportError();
+    }
+    reported_ = true;
+  }
+
+  void OnLookupDone(int return_code, const string& output) {
+    child_pid_ = 0;
+    if (return_code != 0) {
+      LOG(INFO) << "Child exited with non-zero exit code " << return_code;
+      ReportError();
+    } else {
+      ReportSuccess(output);
+    }
+    delete this;
+  }
+
+  void OnTimeout() {
+    timeout_task_ = MessageLoop::kTaskIdNull;
+    ReportError();
+    delete this;
+  }
+
+  P2PManager::LookupCallback callback_;
+
+  // The Subprocess tag of the running process. A value of 0 means that the
+  // process is not running.
+  pid_t child_pid_{0};
+
+  // The timeout task_id we are waiting on, if any.
+  MessageLoop::TaskId timeout_task_{MessageLoop::kTaskIdNull};
+
+  bool reported_{false};
+};
+
+void P2PManagerImpl::LookupUrlForFile(const string& file_id,
+                                      size_t minimum_size,
+                                      TimeDelta max_time_to_wait,
+                                      LookupCallback callback) {
+  LookupData* lookup_data = new LookupData(callback);
+  string file_id_with_ext = file_id + "." + file_extension_;
+  vector<string> args =
+      configuration_->GetP2PClientArgs(file_id_with_ext, minimum_size);
+  lookup_data->InitiateLookup(args, max_time_to_wait);
+}
+
+bool P2PManagerImpl::FileShare(const string& file_id, size_t expected_size) {
+  // Check if file already exist.
+  FilePath path = FileGetPath(file_id);
+  if (!path.empty()) {
+    // File exists - double check its expected size though.
+    ssize_t file_expected_size = FileGetExpectedSize(file_id);
+    if (file_expected_size == -1 ||
+        static_cast<size_t>(file_expected_size) != expected_size) {
+      LOG(ERROR) << "Existing p2p file " << path.value()
+                 << " with expected_size=" << file_expected_size
+                 << " does not match the passed in"
+                 << " expected_size=" << expected_size;
+      return false;
+    }
+    return true;
+  }
+
+  // Before creating the file, bail if statvfs(3) indicates that at
+  // least twice the size is not available in P2P_DIR.
+  struct statvfs statvfsbuf;
+  FilePath p2p_dir = configuration_->GetP2PDir();
+  if (statvfs(p2p_dir.value().c_str(), &statvfsbuf) != 0) {
+    PLOG(ERROR) << "Error calling statvfs() for dir " << p2p_dir.value();
+    return false;
+  }
+  size_t free_bytes =
+      static_cast<size_t>(statvfsbuf.f_bsize) * statvfsbuf.f_bavail;
+  if (free_bytes < 2 * expected_size) {
+    // This can easily happen and is worth reporting.
+    LOG(INFO) << "Refusing to allocate p2p file of " << expected_size
+              << " bytes since the directory " << p2p_dir.value()
+              << " only has " << free_bytes
+              << " bytes available and this is less than twice the"
+              << " requested size.";
+    return false;
+  }
+
+  // Okie-dokey looks like enough space is available - create the file.
+  path = GetPath(file_id, kNonVisible);
+  int fd = open(path.value().c_str(), O_CREAT | O_RDWR, 0644);
+  if (fd == -1) {
+    PLOG(ERROR) << "Error creating file with path " << path.value();
+    return false;
+  }
+  ScopedFdCloser fd_closer(&fd);
+
+  // If the final size is known, allocate the file (e.g. reserve disk
+  // space) and set the user.cros-p2p-filesize xattr.
+  if (expected_size != 0) {
+    if (fallocate(fd,
+                  FALLOC_FL_KEEP_SIZE,  // Keep file size as 0.
+                  0,
+                  expected_size) != 0) {
+      if (errno == ENOSYS || errno == EOPNOTSUPP) {
+        // If the filesystem doesn't support the fallocate, keep
+        // going. This is helpful when running unit tests on build
+        // machines with ancient filesystems and/or OSes.
+        PLOG(WARNING) << "Ignoring fallocate(2) failure";
+      } else {
+        // ENOSPC can happen (funky race though, cf. the statvfs() check
+        // above), handle it gracefully, e.g. use logging level INFO.
+        PLOG(INFO) << "Error allocating " << expected_size << " bytes for file "
+                   << path.value();
+        if (unlink(path.value().c_str()) != 0) {
+          PLOG(ERROR) << "Error deleting file with path " << path.value();
+        }
+        return false;
+      }
+    }
+
+    string decimal_size = std::to_string(expected_size);
+    if (fsetxattr(fd,
+                  kCrosP2PFileSizeXAttrName,
+                  decimal_size.c_str(),
+                  decimal_size.size(),
+                  0) != 0) {
+      PLOG(ERROR) << "Error setting xattr " << path.value();
+      return false;
+    }
+  }
+
+  return true;
+}
+
+FilePath P2PManagerImpl::FileGetPath(const string& file_id) {
+  struct stat statbuf;
+  FilePath path;
+
+  path = GetPath(file_id, kVisible);
+  if (stat(path.value().c_str(), &statbuf) == 0) {
+    return path;
+  }
+
+  path = GetPath(file_id, kNonVisible);
+  if (stat(path.value().c_str(), &statbuf) == 0) {
+    return path;
+  }
+
+  path.clear();
+  return path;
+}
+
+bool P2PManagerImpl::FileGetVisible(const string& file_id, bool* out_result) {
+  FilePath path = FileGetPath(file_id);
+  if (path.empty()) {
+    LOG(ERROR) << "No file for id " << file_id;
+    return false;
+  }
+  if (out_result != nullptr)
+    *out_result = path.MatchesExtension(kP2PExtension);
+  return true;
+}
+
+bool P2PManagerImpl::FileMakeVisible(const string& file_id) {
+  FilePath path = FileGetPath(file_id);
+  if (path.empty()) {
+    LOG(ERROR) << "No file for id " << file_id;
+    return false;
+  }
+
+  // Already visible?
+  if (path.MatchesExtension(kP2PExtension))
+    return true;
+
+  LOG_ASSERT(path.MatchesExtension(kTmpExtension));
+  FilePath new_path = path.RemoveExtension();
+  LOG_ASSERT(new_path.MatchesExtension(kP2PExtension));
+  if (rename(path.value().c_str(), new_path.value().c_str()) != 0) {
+    PLOG(ERROR) << "Error renaming " << path.value() << " to "
+                << new_path.value();
+    return false;
+  }
+
+  return true;
+}
+
+ssize_t P2PManagerImpl::FileGetSize(const string& file_id) {
+  FilePath path = FileGetPath(file_id);
+  if (path.empty())
+    return -1;
+
+  return utils::FileSize(path.value());
+}
+
+ssize_t P2PManagerImpl::FileGetExpectedSize(const string& file_id) {
+  FilePath path = FileGetPath(file_id);
+  if (path.empty())
+    return -1;
+
+  char ea_value[64] = {0};
+  ssize_t ea_size;
+  ea_size = getxattr(path.value().c_str(),
+                     kCrosP2PFileSizeXAttrName,
+                     &ea_value,
+                     sizeof(ea_value) - 1);
+  if (ea_size == -1) {
+    PLOG(ERROR) << "Error calling getxattr() on file " << path.value();
+    return -1;
+  }
+
+  char* endp = nullptr;
+  long long int val = strtoll(ea_value, &endp, 0);  // NOLINT(runtime/int)
+  if (*endp != '\0') {
+    LOG(ERROR) << "Error parsing the value '" << ea_value << "' of the xattr "
+               << kCrosP2PFileSizeXAttrName << " as an integer";
+    return -1;
+  }
+
+  return val;
+}
+
+int P2PManagerImpl::CountSharedFiles() {
+  int num_files = 0;
+
+  FilePath p2p_dir = configuration_->GetP2PDir();
+  const string ext_visible = GetExt(kVisible);
+  const string ext_non_visible = GetExt(kNonVisible);
+
+  base::FileEnumerator dir(p2p_dir, false, base::FileEnumerator::FILES);
+  for (FilePath name = dir.Next(); !name.empty(); name = dir.Next()) {
+    if (base::EndsWith(
+            name.value(), ext_visible, base::CompareCase::SENSITIVE) ||
+        base::EndsWith(
+            name.value(), ext_non_visible, base::CompareCase::SENSITIVE)) {
+      num_files += 1;
+    }
+  }
+
+  return num_files;
+}
+
+void P2PManagerImpl::ScheduleEnabledStatusChange() {
+  if (waiting_for_enabled_status_change_)
+    return;
+
+  Callback<void(EvalStatus, const bool&)> callback =
+      Bind(&P2PManagerImpl::OnEnabledStatusChange, base::Unretained(this));
+  update_manager_->AsyncPolicyRequest(
+      callback, &Policy::P2PEnabledChanged, is_enabled_);
+  waiting_for_enabled_status_change_ = true;
+}
+
+void P2PManagerImpl::OnEnabledStatusChange(EvalStatus status,
+                                           const bool& result) {
+  waiting_for_enabled_status_change_ = false;
+
+  if (status == EvalStatus::kSucceeded) {
+    if (result == is_enabled_) {
+      LOG(WARNING) << "P2P enabled status did not change, which means that it "
+                      "is permanent; not scheduling further checks.";
+      waiting_for_enabled_status_change_ = true;
+      return;
+    }
+
+    is_enabled_ = result;
+
+    // If P2P is running but shouldn't be, make sure it isn't.
+    if (may_be_running_ && !is_enabled_ && !EnsureP2PNotRunning()) {
+      LOG(WARNING) << "Failed to stop P2P service.";
+    }
+  } else {
+    LOG(WARNING)
+        << "P2P enabled tracking failed (possibly timed out); retrying.";
+  }
+
+  ScheduleEnabledStatusChange();
+}
+
+P2PManager* P2PManager::Construct(Configuration* configuration,
+                                  ClockInterface* clock,
+                                  UpdateManager* update_manager,
+                                  const string& file_extension,
+                                  const int num_files_to_keep,
+                                  const TimeDelta& max_file_age) {
+  return new P2PManagerImpl(configuration,
+                            clock,
+                            update_manager,
+                            file_extension,
+                            num_files_to_keep,
+                            max_file_age);
+}
+
+}  // namespace chromeos_update_engine