Merge "delta_generator: Accept a list of partitions in the command line."
diff --git a/Android.mk b/Android.mk
index abbae26..a1edf1c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -45,6 +45,7 @@
     LOCAL_LDFLAGS += \
         -Wl,--gc-sections
     LOCAL_C_INCLUDES += \
+        $(LOCAL_PATH)/client_library/include \
         external/gtest/include \
         system
     LOCAL_SHARED_LIBRARIES += \
@@ -177,6 +178,7 @@
     update_manager/real_updater_provider.cc \
     update_manager/state_factory.cc \
     update_manager/update_manager.cc \
+    update_status_utils.cc \
     utils.cc \
     xz_extent_writer.cc
 $(eval $(update_engine_common))
@@ -321,6 +323,36 @@
 LOCAL_DBUS_PROXY_PREFIX := update_engine
 include $(BUILD_STATIC_LIBRARY)
 
+# libupdate_engine_client
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libupdate_engine_client
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_CFLAGS := \
+    -Wall \
+    -Werror \
+    -Wno-unused-parameter
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/client_library/include \
+    external/cros/system_api/dbus \
+    system \
+    external/gtest/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/client_library/include
+LOCAL_SHARED_LIBRARIES := \
+    libchrome \
+    libchrome-dbus \
+    libchromeos \
+    libchromeos-dbus
+LOCAL_STATIC_LIBRARIES := \
+    update_engine_client-dbus-proxies
+LOCAL_SRC_FILES := \
+    client_library/client.cc \
+    client_library/client_impl.cc \
+    update_status_utils.cc
+include $(BUILD_SHARED_LIBRARY)
+
 
 # Update payload signing public key.
 # ========================================================
diff --git a/client_library/client.cc b/client_library/client.cc
new file mode 100644
index 0000000..d6e7382
--- /dev/null
+++ b/client_library/client.cc
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2015 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.
+//
+
+#include "update_engine/client_library/include/update_engine/client.h"
+
+#include <memory>
+
+#include "update_engine/client_library/client_impl.h"
+
+using std::unique_ptr;
+
+namespace update_engine {
+
+std::unique_ptr<UpdateEngineClient> UpdateEngineClient::CreateInstance() {
+  return unique_ptr<UpdateEngineClient>{new internal::UpdateEngineClientImpl{}};
+}
+
+}  // namespace update_engine
diff --git a/client_library/client_impl.cc b/client_library/client_impl.cc
new file mode 100644
index 0000000..84ca184
--- /dev/null
+++ b/client_library/client_impl.cc
@@ -0,0 +1,91 @@
+//
+// Copyright (C) 2015 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.
+//
+
+#include "update_engine/client_library/client_impl.h"
+
+#include <dbus/bus.h>
+#include <update_engine/dbus-constants.h>
+
+#include "update_engine/update_status_utils.h"
+
+using chromeos_update_engine::StringToUpdateStatus;
+using dbus::Bus;
+using org::chromium::UpdateEngineInterfaceProxy;
+using std::string;
+
+namespace update_engine {
+namespace internal {
+
+UpdateEngineClientImpl::UpdateEngineClientImpl() {
+  Bus::Options options;
+  options.bus_type = Bus::SYSTEM;
+  scoped_refptr<Bus> bus{new Bus{options}};
+  proxy_.reset(new UpdateEngineInterfaceProxy{bus});
+}
+
+bool UpdateEngineClientImpl::AttemptUpdate(const string& in_app_version,
+                                           const string& in_omaha_url,
+                                           bool at_user_request) {
+  return proxy_->AttemptUpdateWithFlags(
+      in_app_version,
+      in_omaha_url,
+      (at_user_request) ? 0 : kAttemptUpdateFlagNonInteractive,
+      nullptr);
+}
+
+bool UpdateEngineClientImpl::GetStatus(int64_t* out_last_checked_time,
+                                       double* out_progress,
+                                       UpdateStatus* out_update_status,
+                                       string* out_new_version,
+                                       int64_t* out_new_size) {
+  string status_as_string;
+  const bool success = proxy_->GetStatus(
+      out_last_checked_time,
+      out_progress,
+      &status_as_string,
+      out_new_version,
+      out_new_size,
+      nullptr);
+  if (!success) {
+    return false;
+  }
+
+  return StringToUpdateStatus(status_as_string, out_update_status);
+}
+
+bool UpdateEngineClientImpl::SetTargetChannel(const string& in_target_channel) {
+  return proxy_->SetChannel(
+      in_target_channel,
+      true,
+      nullptr);
+}
+
+bool UpdateEngineClientImpl::GetTargetChannel(string* out_channel) {
+  return proxy_->GetChannel(
+      false,  // Get the target channel.
+      out_channel,
+      nullptr);
+}
+
+bool UpdateEngineClientImpl::GetChannel(string* out_channel) {
+  return proxy_->GetChannel(
+      true,  // Get the current channel.
+      out_channel,
+      nullptr);
+}
+
+}  // namespace internal
+}  // namespace update_engine
diff --git a/client_library/client_impl.h b/client_library/client_impl.h
new file mode 100644
index 0000000..a16136c
--- /dev/null
+++ b/client_library/client_impl.h
@@ -0,0 +1,62 @@
+//
+// Copyright (C) 2015 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.
+//
+
+#ifndef UPDATE_ENGINE_CLIENT_LIBRARY_CLIENT_IMPL_H_
+#define UPDATE_ENGINE_CLIENT_LIBRARY_CLIENT_IMPL_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+
+#include "update_engine/dbus-proxies.h"
+#include "update_engine/client_library/include/update_engine/client.h"
+
+namespace update_engine {
+namespace internal {
+
+class UpdateEngineClientImpl : public UpdateEngineClient {
+ public:
+  UpdateEngineClientImpl();
+  virtual ~UpdateEngineClientImpl() = default;
+
+  bool AttemptUpdate(const std::string& app_version,
+                     const std::string& omaha_url,
+                     bool at_user_request) override;
+
+  bool GetStatus(int64_t* out_last_checked_time,
+                 double* out_progress,
+                 UpdateStatus* out_update_status,
+                 std::string* out_new_version,
+                 int64_t* out_new_size) override;
+
+  bool SetTargetChannel(const std::string& target_channel) override;
+
+  bool GetTargetChannel(std::string* out_channel) override;
+
+  bool GetChannel(std::string* out_channel) override;
+
+ private:
+  std::unique_ptr<org::chromium::UpdateEngineInterfaceProxy> proxy_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientImpl);
+};  // class UpdateEngineClientImpl
+
+}  // namespace internal
+}  // namespace update_engine
+
+#endif  // UPDATE_ENGINE_CLIENT_LIBRARY_CLIENT_IMPL_H_
diff --git a/client_library/include/update_engine/client.h b/client_library/include/update_engine/client.h
new file mode 100644
index 0000000..ec39253
--- /dev/null
+++ b/client_library/include/update_engine/client.h
@@ -0,0 +1,89 @@
+//
+// Copyright (C) 2015 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.
+//
+
+#ifndef UDPATE_ENGINE_CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_CLIENT_H_
+#define UDPATE_ENGINE_CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_CLIENT_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "update_engine/update_status.h"
+
+namespace update_engine {
+
+class UpdateEngineClient {
+ public:
+  static std::unique_ptr<UpdateEngineClient> CreateInstance();
+
+  virtual ~UpdateEngineClient() = default;
+
+  // Force the update_engine to attempt an update.
+  // |app_version|
+  //     Attempt to update to this version.  An empty string indicates that
+  //     update engine should pick the most recent image on the current channel.
+  // |omaha_url|
+  //     Force update_engine to look for updates from the given server.  Passing
+  //     empty indicates update_engine should get this parameter from its
+  //     config.  Note that update_engine will ignore this parameter in
+  //     production mode to avoid pulling untrusted updates.
+  // |at_user_request|
+  //     This update was directly requested by the user.
+  virtual bool AttemptUpdate(const std::string& app_version,
+                             const std::string& omaha_url,
+                             bool at_user_request) = 0;
+
+  // Returns the current status of the Update Engine.
+  //
+  // |out_last_checked_time|
+  //     the last time the update engine checked for an update in seconds since
+  //     the epoc.
+  // |out_progress|
+  //     when downloading an update, this is calculated as
+  //     (number of bytes received) / (total bytes).
+  // |out_update_status|
+  //     See update_status.h.
+  // |out_new_version|
+  //     string version of the new system image.
+  // |out_new_size|
+  //     number of bytes in the new system image.
+  virtual bool GetStatus(int64_t* out_last_checked_time,
+                         double* out_progress,
+                         UpdateStatus* out_update_status,
+                         std::string* out_new_version,
+                         int64_t* out_new_size) = 0;
+
+  // Changes the current channel of the device to the target channel.
+  virtual bool SetTargetChannel(const std::string& target_channel) = 0;
+
+  // Get the channel the device will switch to on reboot.
+  virtual bool GetTargetChannel(std::string* out_channel) = 0;
+
+  // Get the channel the device is currently on.
+  virtual bool GetChannel(std::string* out_channel) = 0;
+
+ protected:
+  // Use CreateInstance().
+  UpdateEngineClient() = default;
+
+ private:
+  UpdateEngineClient(const UpdateEngineClient&) = delete;
+  void operator=(const UpdateEngineClient&) = delete;
+};  // class UpdateEngineClient
+
+}  // namespace update_engine
+
+#endif  // UDPATE_ENGINE_CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_CLIENT_H_
diff --git a/client_library/include/update_engine/update_status.h b/client_library/include/update_engine/update_status.h
new file mode 100644
index 0000000..525249c
--- /dev/null
+++ b/client_library/include/update_engine/update_status.h
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2015 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.
+//
+
+#ifndef CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_UPDATE_STATUS_H_
+#define CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_UPDATE_STATUS_H_
+
+namespace update_engine {
+
+enum class UpdateStatus {
+  IDLE = 0,
+  CHECKING_FOR_UPDATE,
+  UPDATE_AVAILABLE,
+  DOWNLOADING,
+  VERIFYING,
+  FINALIZING,
+  UPDATED_NEED_REBOOT,
+  REPORTING_ERROR_EVENT,
+  ATTEMPTING_ROLLBACK,
+  DISABLED,
+};
+
+}  // namespace update_engine
+
+#endif  // CLIENT_LIBRARY_INCLUDE_UPDATE_ENGINE_UPDATE_STATUS_H_
diff --git a/delta_performer.cc b/delta_performer.cc
index c8a00d3..8aff634 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -18,6 +18,7 @@
 
 #include <endian.h>
 #include <errno.h>
+#include <linux/fs.h>
 
 #include <algorithm>
 #include <cstring>
@@ -56,8 +57,13 @@
 
 namespace chromeos_update_engine {
 
+const uint64_t DeltaPerformer::kDeltaVersionOffset = sizeof(kDeltaMagic);
 const uint64_t DeltaPerformer::kDeltaVersionSize = 8;
+const uint64_t DeltaPerformer::kDeltaManifestSizeOffset =
+    kDeltaVersionOffset + kDeltaVersionSize;
 const uint64_t DeltaPerformer::kDeltaManifestSizeSize = 8;
+const uint64_t DeltaPerformer::kDeltaMetadataSignatureSizeSize = 4;
+const uint64_t DeltaPerformer::kMaxPayloadHeaderSize = 24;
 const uint64_t DeltaPerformer::kSupportedMajorPayloadVersion = 1;
 const uint64_t DeltaPerformer::kSupportedMinorPayloadVersion = 2;
 
@@ -327,25 +333,39 @@
 
 }  // namespace
 
-uint64_t DeltaPerformer::GetVersionOffset() {
-  // Manifest size is stored right after the magic string and the version.
-  return strlen(kDeltaMagic);
+bool DeltaPerformer::GetMetadataSignatureSizeOffset(
+    uint64_t* out_offset) const {
+  if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
+    *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
+    return true;
+  }
+  return false;
 }
 
-uint64_t DeltaPerformer::GetManifestSizeOffset() {
-  // Manifest size is stored right after the magic string and the version.
-  return strlen(kDeltaMagic) + kDeltaVersionSize;
-}
-
-uint64_t DeltaPerformer::GetManifestOffset() {
-  // Actual manifest begins right after the manifest size field.
-  return GetManifestSizeOffset() + kDeltaManifestSizeSize;
+bool DeltaPerformer::GetManifestOffset(uint64_t* out_offset) const {
+  // Actual manifest begins right after the manifest size field or
+  // metadata signature size field if major version >= 2.
+  if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
+    *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
+    return true;
+  }
+  if (major_payload_version_ == kBrilloMajorPayloadVersion) {
+    *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
+                  kDeltaMetadataSignatureSizeSize;
+    return true;
+  }
+  LOG(ERROR) << "Unknown major payload version: " << major_payload_version_;
+  return false;
 }
 
 uint64_t DeltaPerformer::GetMetadataSize() const {
   return metadata_size_;
 }
 
+uint64_t DeltaPerformer::GetMajorVersion() const {
+  return major_payload_version_;
+}
+
 uint32_t DeltaPerformer::GetMinorVersion() const {
   if (manifest_.has_minor_version()) {
     return manifest_.minor_version();
@@ -363,56 +383,82 @@
   return true;
 }
 
+bool DeltaPerformer::IsHeaderParsed() const {
+  return metadata_size_ != 0;
+}
 
 DeltaPerformer::MetadataParseResult DeltaPerformer::ParsePayloadMetadata(
     const chromeos::Blob& payload, ErrorCode* error) {
   *error = ErrorCode::kSuccess;
-  const uint64_t manifest_offset = GetManifestOffset();
-  uint64_t manifest_size = (metadata_size_ ?
-                            metadata_size_ - manifest_offset : 0);
+  uint64_t manifest_offset;
 
-  if (!manifest_size) {
-    // Ensure we have data to cover the payload header.
-    if (payload.size() < manifest_offset)
+  if (!IsHeaderParsed()) {
+    // Ensure we have data to cover the major payload version.
+    if (payload.size() < kDeltaManifestSizeOffset)
       return kMetadataParseInsufficientData;
 
     // Validate the magic string.
-    if (memcmp(payload.data(), kDeltaMagic, strlen(kDeltaMagic)) != 0) {
+    if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
       LOG(ERROR) << "Bad payload format -- invalid delta magic.";
       *error = ErrorCode::kDownloadInvalidMetadataMagicString;
       return kMetadataParseError;
     }
 
     // Extract the payload version from the metadata.
-    uint64_t major_payload_version;
-    COMPILE_ASSERT(sizeof(major_payload_version) == kDeltaVersionSize,
+    COMPILE_ASSERT(sizeof(major_payload_version_) == kDeltaVersionSize,
                    major_payload_version_size_mismatch);
-    memcpy(&major_payload_version,
-           &payload[GetVersionOffset()],
+    memcpy(&major_payload_version_,
+           &payload[kDeltaVersionOffset],
            kDeltaVersionSize);
     // switch big endian to host
-    major_payload_version = be64toh(major_payload_version);
+    major_payload_version_ = be64toh(major_payload_version_);
 
-    if (major_payload_version != kSupportedMajorPayloadVersion) {
+    if (major_payload_version_ != supported_major_version_) {
       LOG(ERROR) << "Bad payload format -- unsupported payload version: "
-          << major_payload_version;
+          << major_payload_version_;
       *error = ErrorCode::kUnsupportedMajorPayloadVersion;
       return kMetadataParseError;
     }
 
+    // Get the manifest offset now that we have payload version.
+    if (!GetManifestOffset(&manifest_offset)) {
+      *error = ErrorCode::kUnsupportedMajorPayloadVersion;
+      return kMetadataParseError;
+    }
+    // Check again with the manifest offset.
+    if (payload.size() < manifest_offset)
+      return kMetadataParseInsufficientData;
+
     // Next, parse the manifest size.
-    COMPILE_ASSERT(sizeof(manifest_size) == kDeltaManifestSizeSize,
+    COMPILE_ASSERT(sizeof(manifest_size_) == kDeltaManifestSizeSize,
                    manifest_size_size_mismatch);
-    memcpy(&manifest_size,
-           &payload[GetManifestSizeOffset()],
+    memcpy(&manifest_size_,
+           &payload[kDeltaManifestSizeOffset],
            kDeltaManifestSizeSize);
-    manifest_size = be64toh(manifest_size);  // switch big endian to host
+    manifest_size_ = be64toh(manifest_size_);  // switch big endian to host
+
+    uint32_t metadata_signature_size = 0;
+    if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
+      // Parse the metadata signature size.
+      COMPILE_ASSERT(sizeof(metadata_signature_size) ==
+                     kDeltaMetadataSignatureSizeSize,
+                     metadata_signature_size_size_mismatch);
+      uint64_t metadata_signature_size_offset;
+      if (!GetMetadataSignatureSizeOffset(&metadata_signature_size_offset)) {
+        *error = ErrorCode::kError;
+        return kMetadataParseError;
+      }
+      memcpy(&metadata_signature_size,
+             &payload[metadata_signature_size_offset],
+             kDeltaMetadataSignatureSizeSize);
+      metadata_signature_size = be32toh(metadata_signature_size);
+    }
 
     // If the metadata size is present in install plan, check for it immediately
     // even before waiting for that many number of bytes to be downloaded in the
     // payload. This will prevent any attack which relies on us downloading data
     // beyond the expected metadata size.
-    metadata_size_ = manifest_offset + manifest_size;
+    metadata_size_ = manifest_offset + manifest_size_ + metadata_signature_size;
     if (install_plan_->hash_checks_mandatory) {
       if (install_plan_->metadata_size != metadata_size_) {
         LOG(ERROR) << "Mandatory metadata size in Omaha response ("
@@ -460,8 +506,12 @@
     *error = ErrorCode::kSuccess;
   }
 
+  if (!GetManifestOffset(&manifest_offset)) {
+    *error = ErrorCode::kUnsupportedMajorPayloadVersion;
+    return kMetadataParseError;
+  }
   // The payload metadata is deemed valid, it's safe to parse the protobuf.
-  if (!manifest_.ParseFromArray(&payload[manifest_offset], manifest_size)) {
+  if (!manifest_.ParseFromArray(&payload[manifest_offset], manifest_size_)) {
     LOG(ERROR) << "Unable to parse manifest in update file.";
     *error = ErrorCode::kDownloadManifestParseError;
     return kMetadataParseError;
@@ -485,11 +535,11 @@
   UpdateOverallProgress(false, "Completed ");
 
   while (!manifest_valid_) {
-    // Read data up to the needed limit; this is either the payload header size,
-    // or the full metadata size (once it becomes known).
-    const bool do_read_header = !metadata_size_;
+    // Read data up to the needed limit; this is either maximium payload header
+    // size, or the full metadata size (once it becomes known).
+    const bool do_read_header = !IsHeaderParsed();
     CopyDataToBuffer(&c_bytes, &count,
-                     (do_read_header ? GetManifestOffset() :
+                     (do_read_header ? kMaxPayloadHeaderSize :
                       metadata_size_));
 
     MetadataParseResult result = ParsePayloadMetadata(buffer_, error);
@@ -497,7 +547,7 @@
       return false;
     if (result == kMetadataParseInsufficientData) {
       // If we just processed the header, make an attempt on the manifest.
-      if (do_read_header && metadata_size_)
+      if (do_read_header && IsHeaderParsed())
         continue;
 
       return true;
@@ -601,6 +651,10 @@
       case InstallOperation::REPLACE_XZ:
         op_result = PerformReplaceOperation(op, is_kernel_partition);
         break;
+      case InstallOperation::ZERO:
+      case InstallOperation::DISCARD:
+        op_result = PerformZeroOrDiscardOperation(op, is_kernel_partition);
+        break;
       case InstallOperation::MOVE:
         op_result = PerformMoveOperation(op, is_kernel_partition);
         break;
@@ -690,6 +744,44 @@
   return true;
 }
 
+bool DeltaPerformer::PerformZeroOrDiscardOperation(
+    const InstallOperation& operation,
+    bool is_kernel_partition) {
+  CHECK(operation.type() == InstallOperation::DISCARD ||
+        operation.type() == InstallOperation::ZERO);
+
+  // These operations have no blob.
+  TEST_AND_RETURN_FALSE(!operation.has_data_offset());
+  TEST_AND_RETURN_FALSE(!operation.has_data_length());
+
+  int request =
+      (operation.type() == InstallOperation::ZERO ? BLKZEROOUT : BLKDISCARD);
+
+  FileDescriptorPtr fd = is_kernel_partition ? kernel_fd_ : fd_;
+  bool attempt_ioctl = true;
+  chromeos::Blob zeros;
+  for (int i = 0; i < operation.dst_extents_size(); i++) {
+    Extent extent = operation.dst_extents(i);
+    const uint64_t start = extent.start_block() * block_size_;
+    const uint64_t length = extent.num_blocks() * block_size_;
+    if (attempt_ioctl) {
+      int result = 0;
+      if (fd->BlkIoctl(request, start, length, &result) && result == 0)
+        continue;
+      attempt_ioctl = false;
+      zeros.resize(16 * block_size_);
+    }
+    // In case of failure, we fall back to writing 0 to the selected region.
+    for (uint64_t offset = 0; offset < length; offset += zeros.size()) {
+      uint64_t chunk_length = min(length - offset,
+                                  static_cast<uint64_t>(zeros.size()));
+      TEST_AND_RETURN_FALSE(
+          utils::PWriteAll(fd, zeros.data(), chunk_length, start + offset));
+    }
+  }
+  return true;
+}
+
 bool DeltaPerformer::PerformMoveOperation(const InstallOperation& operation,
                                           bool is_kernel_partition) {
   // Calculate buffer size. Note, this function doesn't do a sliding
diff --git a/delta_performer.h b/delta_performer.h
index 087c2ad..34cb137 100644
--- a/delta_performer.h
+++ b/delta_performer.h
@@ -50,8 +50,12 @@
     kMetadataParseInsufficientData,
   };
 
+  static const uint64_t kDeltaVersionOffset;
   static const uint64_t kDeltaVersionSize;
+  static const uint64_t kDeltaManifestSizeOffset;
   static const uint64_t kDeltaManifestSizeSize;
+  static const uint64_t kDeltaMetadataSignatureSizeSize;
+  static const uint64_t kMaxPayloadHeaderSize;
   static const uint64_t kSupportedMajorPayloadVersion;
   static const uint64_t kSupportedMinorPayloadVersion;
 
@@ -81,6 +85,8 @@
         manifest_parsed_(false),
         manifest_valid_(false),
         metadata_size_(0),
+        manifest_size_(0),
+        major_payload_version_(0),
         next_operation_num_(0),
         buffer_offset_(0),
         last_updated_buffer_offset_(kuint64max),
@@ -93,6 +99,7 @@
         last_progress_chunk_(0),
         forced_progress_log_wait_(
             base::TimeDelta::FromSeconds(kProgressLogTimeoutSeconds)),
+        supported_major_version_(kSupportedMajorPayloadVersion),
         supported_minor_version_(kSupportedMinorPayloadVersion) {}
 
   // Opens the kernel. Should be called before or after Open(), but before
@@ -186,26 +193,30 @@
     public_key_path_ = public_key_path;
   }
 
-  // Returns the byte offset at which the payload version can be found.
-  static uint64_t GetVersionOffset();
+  // Set |*out_offset| to the byte offset where the size of the metadata signature
+  // is stored in a payload. Return true on success, if this field is not
+  // present in the payload, return false.
+  bool GetMetadataSignatureSizeOffset(uint64_t* out_offset) const;
 
-  // Returns the byte offset where the size of the manifest is stored in
-  // a payload. This offset precedes the actual start of the manifest
-  // that's returned by the GetManifestOffset method.
-  static uint64_t GetManifestSizeOffset();
-
-  // Returns the byte offset at which the manifest protobuf begins in a
-  // payload.
-  static uint64_t GetManifestOffset();
+  // Set |*out_offset| to the byte offset at which the manifest protobuf begins
+  // in a payload. Return true on success, false if the offset is unknown.
+  bool GetManifestOffset(uint64_t* out_offset) const;
 
   // Returns the size of the payload metadata, which includes the payload header
-  // and the manifest. Is the header was not yet parsed, returns zero.
+  // and the manifest. If the header was not yet parsed, returns zero.
   uint64_t GetMetadataSize() const;
 
   // If the manifest was successfully parsed, copies it to |*out_manifest_p|.
   // Returns true on success.
   bool GetManifest(DeltaArchiveManifest* out_manifest_p) const;
 
+  // Return true if header parsing is finished and no errors occurred.
+  bool IsHeaderParsed() const;
+
+  // Returns the major payload version. If the version was not yet parsed,
+  // returns zero.
+  uint64_t GetMajorVersion() const;
+
   // Returns the delta minor version. If this value is defined in the manifest,
   // it returns that value, otherwise it returns the default value.
   uint32_t GetMinorVersion() const;
@@ -268,6 +279,8 @@
   // These perform a specific type of operation and return true on success.
   bool PerformReplaceOperation(const InstallOperation& operation,
                                bool is_kernel_partition);
+  bool PerformZeroOrDiscardOperation(const InstallOperation& operation,
+                                     bool is_kernel_partition);
   bool PerformMoveOperation(const InstallOperation& operation,
                             bool is_kernel_partition);
   bool PerformBsdiffOperation(const InstallOperation& operation,
@@ -331,6 +344,8 @@
   bool manifest_parsed_;
   bool manifest_valid_;
   uint64_t metadata_size_;
+  uint64_t manifest_size_;
+  uint64_t major_payload_version_;
 
   // Index of the next operation to perform in the manifest.
   size_t next_operation_num_;
@@ -380,6 +395,9 @@
   const base::TimeDelta forced_progress_log_wait_;
   base::Time forced_progress_log_time_;
 
+  // The payload major payload version supported by DeltaPerformer.
+  uint64_t supported_major_version_;
+
   // The delta minor payload version supported by DeltaPerformer.
   uint32_t supported_minor_version_;
 
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index 839f253..12dd1d3 100644
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -138,14 +138,29 @@
   // Apply |payload_data| on partition specified in |source_path|.
   chromeos::Blob ApplyPayload(const chromeos::Blob& payload_data,
                               const string& source_path) {
-    install_plan_.source_path = source_path;
-    install_plan_.kernel_source_path = "/dev/null";
+    return ApplyPayloadToData(payload_data, source_path, chromeos::Blob());
+  }
 
+  // Apply the payload provided in |payload_data| reading from the |source_path|
+  // file and writing the contents to a new partition. The existing data in the
+  // new target file are set to |target_data| before applying the payload.
+  // Returns the result of the payload application.
+  chromeos::Blob ApplyPayloadToData(const chromeos::Blob& payload_data,
+                                    const string& source_path,
+                                    const chromeos::Blob& target_data) {
     string new_part;
     EXPECT_TRUE(utils::MakeTempFile("Partition-XXXXXX", &new_part, nullptr));
     ScopedPathUnlinker partition_unlinker(new_part);
+    EXPECT_TRUE(utils::WriteFile(new_part.c_str(), target_data.data(),
+                                 target_data.size()));
+
+    install_plan_.source_path = source_path;
+    install_plan_.kernel_source_path = "/dev/null";
+    install_plan_.install_path = new_part;
+    install_plan_.kernel_install_path = "/dev/null";
 
     EXPECT_EQ(0, performer_.Open(new_part.c_str(), 0, 0));
+    EXPECT_TRUE(performer_.OpenSourceRootfs(source_path.c_str()));
     EXPECT_TRUE(performer_.Write(payload_data.data(), payload_data.size()));
     EXPECT_EQ(0, performer_.Close());
 
@@ -260,6 +275,9 @@
     EXPECT_EQ(install_plan_.metadata_size, performer_.GetMetadataSize());
   }
 
+  void SetSupportedMajorVersion(uint64_t major_version) {
+    performer_.supported_major_version_ = major_version;
+  }
   FakePrefs prefs_;
   InstallPlan install_plan_;
   FakeSystemState fake_system_state_;
@@ -282,7 +300,7 @@
   chromeos::Blob payload_data = GeneratePayload(expected_data, aops, false,
       kFullPayloadMinorVersion);
 
-  EXPECT_EQ(expected_data, ApplyPayload(payload_data, ""));
+  EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null"));
 }
 
 TEST_F(DeltaPerformerTest, ReplaceOperationTest) {
@@ -345,6 +363,29 @@
   EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null"));
 }
 
+TEST_F(DeltaPerformerTest, ZeroOperationTest) {
+  chromeos::Blob existing_data = chromeos::Blob(4096 * 10, 'a');
+  chromeos::Blob expected_data = existing_data;
+  // Blocks 4, 5 and 7 should have zeros instead of 'a' after the operation is
+  // applied.
+  std::fill(expected_data.data() + 4096 * 4, expected_data.data() + 4096 * 6,
+            0);
+  std::fill(expected_data.data() + 4096 * 7, expected_data.data() + 4096 * 8,
+            0);
+
+  AnnotatedOperation aop;
+  *(aop.op.add_dst_extents()) = ExtentForRange(4, 2);
+  *(aop.op.add_dst_extents()) = ExtentForRange(7, 1);
+  aop.op.set_type(InstallOperation::ZERO);
+  vector<AnnotatedOperation> aops = {aop};
+
+  chromeos::Blob payload_data = GeneratePayload(chromeos::Blob(), aops, false,
+                                                kSourceMinorPayloadVersion);
+
+  EXPECT_EQ(expected_data,
+            ApplyPayloadToData(payload_data, "/dev/null", existing_data));
+}
+
 TEST_F(DeltaPerformerTest, SourceCopyOperationTest) {
   chromeos::Blob expected_data = chromeos::Blob(std::begin(kRandomString),
                                                 std::end(kRandomString));
@@ -460,11 +501,38 @@
                         ErrorCode::kUnsupportedMinorPayloadVersion);
 }
 
+TEST_F(DeltaPerformerTest, BrilloMetadataSignatureSizeTest) {
+  SetSupportedMajorVersion(kBrilloMajorPayloadVersion);
+  EXPECT_EQ(0, performer_.Open("/dev/null", 0, 0));
+  EXPECT_TRUE(performer_.OpenKernel("/dev/null"));
+  EXPECT_TRUE(performer_.Write(kDeltaMagic, sizeof(kDeltaMagic)));
+
+  uint64_t major_version = htobe64(kBrilloMajorPayloadVersion);
+  EXPECT_TRUE(performer_.Write(&major_version, 8));
+
+  uint64_t manifest_size = rand() % 256;
+  uint64_t manifest_size_be = htobe64(manifest_size);
+  EXPECT_TRUE(performer_.Write(&manifest_size_be, 8));
+
+  uint32_t metadata_signature_size = rand() % 256;
+  uint32_t metadata_signature_size_be = htobe32(metadata_signature_size);
+  EXPECT_TRUE(performer_.Write(&metadata_signature_size_be, 4));
+
+  EXPECT_LT(performer_.Close(), 0);
+
+  EXPECT_TRUE(performer_.IsHeaderParsed());
+  EXPECT_EQ(kBrilloMajorPayloadVersion, performer_.GetMajorVersion());
+  uint64_t manifest_offset;
+  EXPECT_TRUE(performer_.GetManifestOffset(&manifest_offset));
+  EXPECT_EQ(24, manifest_offset);  // 4 + 8 + 8 + 4
+  EXPECT_EQ(24 + manifest_size + metadata_signature_size,
+            performer_.GetMetadataSize());
+}
+
 TEST_F(DeltaPerformerTest, BadDeltaMagicTest) {
   EXPECT_EQ(0, performer_.Open("/dev/null", 0, 0));
   EXPECT_TRUE(performer_.OpenKernel("/dev/null"));
   EXPECT_TRUE(performer_.Write("junk", 4));
-  EXPECT_TRUE(performer_.Write("morejunk", 8));
   EXPECT_FALSE(performer_.Write("morejunk", 8));
   EXPECT_LT(performer_.Close(), 0);
 }
@@ -476,10 +544,9 @@
   EXPECT_CALL(*(fake_system_state_.mock_payload_state()),
               DownloadProgress(4)).Times(1);
   EXPECT_CALL(*(fake_system_state_.mock_payload_state()),
-              DownloadProgress(8)).Times(2);
+              DownloadProgress(8)).Times(1);
 
   EXPECT_TRUE(performer_.Write("junk", 4));
-  EXPECT_TRUE(performer_.Write("morejunk", 8));
   EXPECT_FALSE(performer_.Write("morejunk", 8));
   EXPECT_LT(performer_.Close(), 0);
 }
diff --git a/download_action.cc b/download_action.cc
index b913c97..a31c8a3 100644
--- a/download_action.cc
+++ b/download_action.cc
@@ -25,6 +25,7 @@
 #include <base/strings/stringprintf.h>
 
 #include "update_engine/action_pipe.h"
+#include "update_engine/boot_control_interface.h"
 #include "update_engine/omaha_request_params.h"
 #include "update_engine/p2p_manager.h"
 #include "update_engine/payload_state_interface.h"
@@ -168,6 +169,14 @@
 
   install_plan_.Dump();
 
+  LOG(INFO) << "Marking new slot as unbootable";
+  if (!system_state_->boot_control()->MarkSlotUnbootable(
+          install_plan_.target_slot)) {
+    LOG(WARNING) << "Unable to mark new slot "
+                 << BootControlInterface::SlotName(install_plan_.target_slot)
+                 << ". Proceeding with the update anyway.";
+  }
+
   if (writer_) {
     LOG(INFO) << "Using writer for test.";
   } else {
diff --git a/download_action_unittest.cc b/download_action_unittest.cc
index 486f2c5..d1f4bc9 100644
--- a/download_action_unittest.cc
+++ b/download_action_unittest.cc
@@ -134,6 +134,7 @@
                   bool use_download_delegate) {
   chromeos::FakeMessageLoop loop(nullptr);
   loop.SetAsCurrent();
+  FakeSystemState fake_system_state;
 
   // TODO(adlr): see if we need a different file for build bots
   ScopedTempFile output_temp_file;
@@ -157,6 +158,14 @@
                            "",
                            "",
                            "");
+  install_plan.source_slot = 0;
+  install_plan.target_slot = 1;
+  // We mark both slots as bootable. Only the target slot should be unbootable
+  // after the download starts.
+  fake_system_state.fake_boot_control()->SetSlotBootable(
+      install_plan.source_slot, true);
+  fake_system_state.fake_boot_control()->SetSlotBootable(
+      install_plan.target_slot, true);
   ObjectFeederAction<InstallPlan> feeder_action;
   feeder_action.set_obj(install_plan);
   MockPrefs prefs;
@@ -164,7 +173,7 @@
                                                       data.size(),
                                                       nullptr);
   // takes ownership of passed in HttpFetcher
-  DownloadAction download_action(&prefs, nullptr, http_fetcher);
+  DownloadAction download_action(&prefs, &fake_system_state, http_fetcher);
   download_action.SetTestFileWriter(&writer);
   BondActions(&feeder_action, &download_action);
   DownloadActionDelegateMock download_delegate;
@@ -193,6 +202,11 @@
                 base::Bind(&StartProcessorInRunLoop, &processor, http_fetcher));
   loop.Run();
   EXPECT_FALSE(loop.PendingTasks());
+
+  EXPECT_TRUE(fake_system_state.fake_boot_control()->IsSlotBootable(
+      install_plan.source_slot));
+  EXPECT_FALSE(fake_system_state.fake_boot_control()->IsSlotBootable(
+      install_plan.target_slot));
 }
 }  // namespace
 
@@ -269,8 +283,9 @@
     InstallPlan install_plan(false, false, "", 0, "", 0, "",
                              temp_file.GetPath(), "", "", "", "");
     feeder_action.set_obj(install_plan);
+    FakeSystemState fake_system_state_;
     MockPrefs prefs;
-    DownloadAction download_action(&prefs, nullptr,
+    DownloadAction download_action(&prefs, &fake_system_state_,
                                    new MockHttpFetcher(data.data(),
                                                        data.size(),
                                                        nullptr));
@@ -377,7 +392,8 @@
   ObjectFeederAction<InstallPlan> feeder_action;
   feeder_action.set_obj(install_plan);
   MockPrefs prefs;
-  DownloadAction download_action(&prefs, nullptr,
+  FakeSystemState fake_system_state_;
+  DownloadAction download_action(&prefs, &fake_system_state_,
                                  new MockHttpFetcher("x", 1, nullptr));
   download_action.SetTestFileWriter(&writer);
 
@@ -414,7 +430,8 @@
   ObjectFeederAction<InstallPlan> feeder_action;
   feeder_action.set_obj(install_plan);
   MockPrefs prefs;
-  DownloadAction download_action(&prefs, nullptr,
+  FakeSystemState fake_system_state_;
+  DownloadAction download_action(&prefs, &fake_system_state_,
                                  new MockHttpFetcher("x", 1, nullptr));
   download_action.SetTestFileWriter(&writer);
 
diff --git a/file_descriptor.cc b/file_descriptor.cc
index 8b8fa72..4718528 100644
--- a/file_descriptor.cc
+++ b/file_descriptor.cc
@@ -17,6 +17,8 @@
 #include "update_engine/file_descriptor.h"
 
 #include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
@@ -63,6 +65,42 @@
   return lseek64(fd_, offset, whence);
 }
 
+bool EintrSafeFileDescriptor::BlkIoctl(int request,
+                                       uint64_t start,
+                                       uint64_t length,
+                                       int* result) {
+  DCHECK(request == BLKDISCARD || request == BLKZEROOUT ||
+         request == BLKSECDISCARD);
+  // On some devices, the BLKDISCARD will actually read back as zeros, instead
+  // of "undefined" data. The BLKDISCARDZEROES ioctl tells whether that's the
+  // case, so we issue a BLKDISCARD in those cases to speed up the writes.
+  unsigned int arg;
+  if (request == BLKZEROOUT && ioctl(fd_, BLKDISCARDZEROES, &arg) == 0 && arg)
+    request = BLKDISCARD;
+
+  // Ensure the |fd_| is in O_DIRECT mode during this operation, so the write
+  // cache for this region is invalidated. This is required since otherwise
+  // reading back this region could consume stale data from the cache.
+  int flags = fcntl(fd_, F_GETFL, 0);
+  if (flags == -1) {
+    PLOG(WARNING) << "Couldn't get flags on fd " << fd_;
+    return false;
+  }
+  if ((flags & O_DIRECT) == 0 && fcntl(fd_, F_SETFL, flags | O_DIRECT) == -1) {
+    PLOG(WARNING) << "Couldn't set O_DIRECT on fd " << fd_;
+    return false;
+  }
+
+  uint64_t range[2] = {start, length};
+  *result = ioctl(fd_, request, range);
+
+  if ((flags & O_DIRECT) == 0 && fcntl(fd_, F_SETFL, flags) == -1) {
+    PLOG(WARNING) << "Couldn't remove O_DIRECT on fd " << fd_;
+    return false;
+  }
+  return true;
+}
+
 bool EintrSafeFileDescriptor::Close() {
   CHECK_GE(fd_, 0);
   if (IGNORE_EINTR(close(fd_)))
diff --git a/file_descriptor.h b/file_descriptor.h
index 4d6e970..bf13611 100644
--- a/file_descriptor.h
+++ b/file_descriptor.h
@@ -78,6 +78,17 @@
   // may set errno accordingly.
   virtual off64_t Seek(off64_t offset, int whence) = 0;
 
+  // Runs a ioctl() on the file descriptor if supported. Returns whether
+  // the operation is supported. The |request| can be one of BLKDISCARD,
+  // BLKZEROOUT and BLKSECDISCARD to discard, write zeros or securely discard
+  // the blocks. These ioctls accept a range of bytes (|start| and |length|)
+  // over which they perform the operation. The return value from the ioctl is
+  // stored in |result|.
+  virtual bool BlkIoctl(int request,
+                        uint64_t start,
+                        uint64_t length,
+                        int* result) = 0;
+
   // Closes a file descriptor. The descriptor must be open prior to this call.
   // Returns true on success, false otherwise. Specific implementations may set
   // errno accordingly.
@@ -108,6 +119,10 @@
   ssize_t Read(void* buf, size_t count) override;
   ssize_t Write(const void* buf, size_t count) override;
   off64_t Seek(off64_t offset, int whence) override;
+  bool BlkIoctl(int request,
+                uint64_t start,
+                uint64_t length,
+                int* result) override;
   bool Close() override;
   void Reset() override;
   bool IsSettingErrno() override {
diff --git a/filesystem_verifier_action.cc b/filesystem_verifier_action.cc
index d28526f..2291bc9 100644
--- a/filesystem_verifier_action.cc
+++ b/filesystem_verifier_action.cc
@@ -73,16 +73,6 @@
   }
   install_plan_ = GetInputObject();
 
-  // TODO(deymo): Remove this from the FileSystemVerifierAction.
-  if (partition_type_ == PartitionType::kKernel) {
-    LOG(INFO) << "verifying kernel, marking as unbootable";
-    if (!system_state_->boot_control()->MarkSlotUnbootable(
-            install_plan_.target_slot)) {
-      PLOG(ERROR) << "Unable to clear kernel GPT boot flags: " <<
-          install_plan_.kernel_install_path;
-    }
-  }
-
   if (install_plan_.is_full_update &&
       (partition_type_ == PartitionType::kSourceRootfs ||
        partition_type_ == PartitionType::kSourceKernel)) {
diff --git a/filesystem_verifier_action_unittest.cc b/filesystem_verifier_action_unittest.cc
index 32f3c59..8280328 100644
--- a/filesystem_verifier_action_unittest.cc
+++ b/filesystem_verifier_action_unittest.cc
@@ -204,9 +204,6 @@
       break;
   }
 
-  fake_system_state_.fake_boot_control()->SetSlotBootable(
-    install_plan.target_slot, true);
-
   ActionProcessor processor;
 
   ObjectFeederAction<InstallPlan> feeder_action;
@@ -263,14 +260,6 @@
   bool is_install_plan_eq = (collector_action.object() == install_plan);
   EXPECT_TRUE(is_install_plan_eq);
   success = success && is_install_plan_eq;
-
-  LOG(INFO) << "Verifying bootable flag on: " << a_dev;
-
-  // We should always mark a partition as unbootable if it's a kernel
-  // partition, but never if it's anything else.
-  EXPECT_EQ((partition_type != PartitionType::kKernel),
-            fake_system_state_.fake_boot_control()->IsSlotBootable(
-                install_plan.target_slot));
   return success;
 }
 
diff --git a/mtd_file_descriptor.h b/mtd_file_descriptor.h
index 1d023c4..954e5e5 100644
--- a/mtd_file_descriptor.h
+++ b/mtd_file_descriptor.h
@@ -40,6 +40,12 @@
   ssize_t Read(void* buf, size_t count) override;
   ssize_t Write(const void* buf, size_t count) override;
   off64_t Seek(off64_t offset, int whence) override;
+  bool BlkIoctl(int request,
+                uint64_t start,
+                uint64_t length,
+                int* result) override {
+    return false;
+  }
   bool Close() override;
 
  private:
@@ -69,6 +75,12 @@
   ssize_t Read(void* buf, size_t count) override;
   ssize_t Write(const void* buf, size_t count) override;
   off64_t Seek(off64_t offset, int whence) override;
+  bool BlkIoctl(int request,
+                uint64_t start,
+                uint64_t length,
+                int* result) override {
+    return false;
+  }
   bool Close() override;
 
  private:
diff --git a/payload_constants.cc b/payload_constants.cc
index b28461b..cc18430 100644
--- a/payload_constants.cc
+++ b/payload_constants.cc
@@ -28,7 +28,7 @@
 const char kLegacyPartitionNameKernel[] = "boot";
 const char kLegacyPartitionNameRoot[] = "system";
 
-const char kDeltaMagic[] = "CrAU";
+const char kDeltaMagic[4] = {'C', 'r', 'A', 'U'};
 const char kBspatchPath[] = "bspatch";
 
 const char* InstallOperationTypeName(InstallOperation_Type op_type) {
diff --git a/payload_constants.h b/payload_constants.h
index 81bc36a..38cc075 100644
--- a/payload_constants.h
+++ b/payload_constants.h
@@ -48,7 +48,7 @@
 extern const char kLegacyPartitionNameRoot[];
 
 extern const char kBspatchPath[];
-extern const char kDeltaMagic[];
+extern const char kDeltaMagic[4];
 
 // A block number denoting a hole on a sparse file. Used on Extents to refer to
 // section of blocks not present on disk on a sparse file.
diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc
index f0a66cc..cdcc967 100644
--- a/payload_generator/payload_file.cc
+++ b/payload_generator/payload_file.cc
@@ -191,7 +191,7 @@
   ScopedFileWriterCloser writer_closer(&writer);
 
   // Write header
-  TEST_AND_RETURN_FALSE(writer.Write(kDeltaMagic, strlen(kDeltaMagic)));
+  TEST_AND_RETURN_FALSE(writer.Write(kDeltaMagic, sizeof(kDeltaMagic)));
 
   // Write major version number
   TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer, major_version_));
@@ -241,7 +241,7 @@
   }
 
   *medatata_size_out =
-      strlen(kDeltaMagic) + 2 * sizeof(uint64_t) + serialized_manifest.size();
+      sizeof(kDeltaMagic) + 2 * sizeof(uint64_t) + serialized_manifest.size();
   ReportPayloadUsage(*medatata_size_out);
   return true;
 }
diff --git a/update_attempter.cc b/update_attempter.cc
index d4e1654..433d112 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -64,6 +64,7 @@
 #include "update_engine/system_state.h"
 #include "update_engine/update_manager/policy.h"
 #include "update_engine/update_manager/update_manager.h"
+#include "update_engine/update_status_utils.h"
 #include "update_engine/utils.h"
 
 using base::Bind;
@@ -98,33 +99,6 @@
 const char kScheduledAUTestURLRequest[] = "autest-scheduled";
 }  // namespace
 
-const char* UpdateStatusToString(UpdateStatus status) {
-  switch (status) {
-    case UPDATE_STATUS_IDLE:
-      return update_engine::kUpdateStatusIdle;
-    case UPDATE_STATUS_CHECKING_FOR_UPDATE:
-      return update_engine::kUpdateStatusCheckingForUpdate;
-    case UPDATE_STATUS_UPDATE_AVAILABLE:
-      return update_engine::kUpdateStatusUpdateAvailable;
-    case UPDATE_STATUS_DOWNLOADING:
-      return update_engine::kUpdateStatusDownloading;
-    case UPDATE_STATUS_VERIFYING:
-      return update_engine::kUpdateStatusVerifying;
-    case UPDATE_STATUS_FINALIZING:
-      return update_engine::kUpdateStatusFinalizing;
-    case UPDATE_STATUS_UPDATED_NEED_REBOOT:
-      return update_engine::kUpdateStatusUpdatedNeedReboot;
-    case UPDATE_STATUS_REPORTING_ERROR_EVENT:
-      return update_engine::kUpdateStatusReportingErrorEvent;
-    case UPDATE_STATUS_ATTEMPTING_ROLLBACK:
-      return update_engine::kUpdateStatusAttemptingRollback;
-    case UPDATE_STATUS_DISABLED:
-      return update_engine::kUpdateStatusDisabled;
-    default:
-      return "unknown status";
-  }
-}
-
 // Turns a generic ErrorCode::kError to a generic error code specific
 // to |action| (e.g., ErrorCode::kFilesystemVerifierError). If |code| is
 // not ErrorCode::kError, or the action is not matched, returns |code|
@@ -166,9 +140,9 @@
       debugd_proxy_(debugd_proxy) {
   if (!update_completed_marker_.empty() &&
       utils::FileExists(update_completed_marker_.c_str())) {
-    status_ = UPDATE_STATUS_UPDATED_NEED_REBOOT;
+    status_ = UpdateStatus::UPDATED_NEED_REBOOT;
   } else {
-    status_ = UPDATE_STATUS_IDLE;
+    status_ = UpdateStatus::IDLE;
   }
 }
 
@@ -278,7 +252,7 @@
 
   chrome_proxy_resolver_.Init();
   fake_update_success_ = false;
-  if (status_ == UPDATE_STATUS_UPDATED_NEED_REBOOT) {
+  if (status_ == UpdateStatus::UPDATED_NEED_REBOOT) {
     // Although we have applied an update, we still want to ping Omaha
     // to ensure the number of active statistics is accurate.
     //
@@ -293,7 +267,7 @@
     PingOmaha();
     return;
   }
-  if (status_ != UPDATE_STATUS_IDLE) {
+  if (status_ != UpdateStatus::IDLE) {
     // Update in progress. Do nothing
     return;
   }
@@ -309,7 +283,7 @@
 
   BuildUpdateActions(interactive);
 
-  SetStatusAndNotify(UPDATE_STATUS_CHECKING_FOR_UPDATE);
+  SetStatusAndNotify(UpdateStatus::CHECKING_FOR_UPDATE);
 
   // Update the last check time here; it may be re-updated when an Omaha
   // response is received, but this will prevent us from repeatedly scheduling
@@ -751,7 +725,7 @@
   // Update the payload state for Rollback.
   system_state_->payload_state()->Rollback();
 
-  SetStatusAndNotify(UPDATE_STATUS_ATTEMPTING_ROLLBACK);
+  SetStatusAndNotify(UpdateStatus::ATTEMPTING_ROLLBACK);
 
   // Just in case we didn't update boot flags yet, make sure they're updated
   // before any update processing starts. This also schedules the start of the
@@ -764,7 +738,7 @@
 bool UpdateAttempter::CanRollback() const {
   // We can only rollback if the update_engine isn't busy and we have a valid
   // rollback partition.
-  return (status_ == UPDATE_STATUS_IDLE &&
+  return (status_ == UpdateStatus::IDLE &&
           GetRollbackSlot() != BootControlInterface::kInvalidSlot);
 }
 
@@ -829,7 +803,7 @@
 }
 
 bool UpdateAttempter::RebootIfNeeded() {
-  if (status_ != UPDATE_STATUS_UPDATED_NEED_REBOOT) {
+  if (status_ != UpdateStatus::UPDATED_NEED_REBOOT) {
     LOG(INFO) << "Reboot requested, but status is "
               << UpdateStatusToString(status_) << ", so not rebooting.";
     return false;
@@ -890,8 +864,8 @@
       // actually notice one on subsequent calls. Note that we don't need to
       // re-schedule a check in this case as updates are permanently disabled;
       // further (forced) checks may still initiate a scheduling call.
-      SetStatusAndNotify(UPDATE_STATUS_DISABLED);
-      SetStatusAndNotify(UPDATE_STATUS_IDLE);
+      SetStatusAndNotify(UpdateStatus::DISABLED);
+      SetStatusAndNotify(UpdateStatus::IDLE);
       return;
     }
 
@@ -937,11 +911,11 @@
   // Reset cpu shares back to normal.
   CleanupCpuSharesManagement();
 
-  if (status_ == UPDATE_STATUS_REPORTING_ERROR_EVENT) {
+  if (status_ == UpdateStatus::REPORTING_ERROR_EVENT) {
     LOG(INFO) << "Error event sent.";
 
     // Inform scheduler of new status;
-    SetStatusAndNotify(UPDATE_STATUS_IDLE);
+    SetStatusAndNotify(UpdateStatus::IDLE);
     ScheduleUpdates();
 
     if (!fake_update_success_) {
@@ -973,7 +947,7 @@
     system_state_->payload_state()->SetScatteringWaitPeriod(TimeDelta());
     prefs_->Delete(kPrefsUpdateFirstSeenAt);
 
-    SetStatusAndNotify(UPDATE_STATUS_UPDATED_NEED_REBOOT);
+    SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
     ScheduleUpdates();
     LOG(INFO) << "Update successfully applied, waiting to reboot.";
 
@@ -1006,7 +980,7 @@
     return;
   }
   LOG(INFO) << "No update.";
-  SetStatusAndNotify(UPDATE_STATUS_IDLE);
+  SetStatusAndNotify(UpdateStatus::IDLE);
   ScheduleUpdates();
 }
 
@@ -1014,7 +988,7 @@
   // Reset cpu shares back to normal.
   CleanupCpuSharesManagement();
   download_progress_ = 0.0;
-  SetStatusAndNotify(UPDATE_STATUS_IDLE);
+  SetStatusAndNotify(UpdateStatus::IDLE);
   ScheduleUpdates();
   actions_.clear();
   error_event_.reset(nullptr);
@@ -1058,7 +1032,7 @@
     // If the current state is at or past the download phase, count the failure
     // in case a switch to full update becomes necessary. Ignore network
     // transfer timeouts and failures.
-    if (status_ >= UPDATE_STATUS_DOWNLOADING &&
+    if (status_ >= UpdateStatus::DOWNLOADING &&
         code != ErrorCode::kDownloadTransferError) {
       MarkDeltaUpdateFailure();
     }
@@ -1079,9 +1053,9 @@
     new_payload_size_ = plan.payload_size;
     SetupDownload();
     SetupCpuSharesManagement();
-    SetStatusAndNotify(UPDATE_STATUS_UPDATE_AVAILABLE);
+    SetStatusAndNotify(UpdateStatus::UPDATE_AVAILABLE);
   } else if (type == DownloadAction::StaticType()) {
-    SetStatusAndNotify(UPDATE_STATUS_FINALIZING);
+    SetStatusAndNotify(UpdateStatus::FINALIZING);
   }
 }
 
@@ -1100,32 +1074,32 @@
   // Self throttle based on progress. Also send notifications if
   // progress is too slow.
   const double kDeltaPercent = 0.01;  // 1%
-  if (status_ != UPDATE_STATUS_DOWNLOADING ||
+  if (status_ != UpdateStatus::DOWNLOADING ||
       bytes_received == total ||
       progress - download_progress_ >= kDeltaPercent ||
       TimeTicks::Now() - last_notify_time_ >= TimeDelta::FromSeconds(10)) {
     download_progress_ = progress;
-    SetStatusAndNotify(UPDATE_STATUS_DOWNLOADING);
+    SetStatusAndNotify(UpdateStatus::DOWNLOADING);
   }
 }
 
 bool UpdateAttempter::ResetStatus() {
   LOG(INFO) << "Attempting to reset state from "
-            << UpdateStatusToString(status_) << " to UPDATE_STATUS_IDLE";
+            << UpdateStatusToString(status_) << " to UpdateStatus::IDLE";
 
   switch (status_) {
-    case UPDATE_STATUS_IDLE:
+    case UpdateStatus::IDLE:
       // no-op.
       return true;
 
-    case UPDATE_STATUS_UPDATED_NEED_REBOOT:  {
+    case UpdateStatus::UPDATED_NEED_REBOOT:  {
       bool ret_value = true;
-      status_ = UPDATE_STATUS_IDLE;
+      status_ = UpdateStatus::IDLE;
       LOG(INFO) << "Reset Successful";
 
       // Remove the reboot marker so that if the machine is rebooted
       // after resetting to idle state, it doesn't go back to
-      // UPDATE_STATUS_UPDATED_NEED_REBOOT state.
+      // UpdateStatus::UPDATED_NEED_REBOOT state.
       if (!update_completed_marker_.empty()) {
         if (!base::DeleteFile(base::FilePath(update_completed_marker_), false))
           ret_value = false;
@@ -1261,7 +1235,7 @@
   // don't schedule another. This shouldn't really happen but just in case...
   if ((action->Type() == OmahaResponseHandlerAction::StaticType() &&
        code == ErrorCode::kError) ||
-      status_ == UPDATE_STATUS_REPORTING_ERROR_EVENT) {
+      status_ == UpdateStatus::REPORTING_ERROR_EVENT) {
     return;
   }
 
@@ -1310,7 +1284,7 @@
                              false));
   actions_.push_back(shared_ptr<AbstractAction>(error_event_action));
   processor_->EnqueueAction(error_event_action.get());
-  SetStatusAndNotify(UPDATE_STATUS_REPORTING_ERROR_EVENT);
+  SetStatusAndNotify(UpdateStatus::REPORTING_ERROR_EVENT);
   processor_->StartProcessing();
   return true;
 }
@@ -1439,7 +1413,7 @@
   UpdateLastCheckedTime();
 
   // Update the status which will schedule the next update check
-  SetStatusAndNotify(UPDATE_STATUS_UPDATED_NEED_REBOOT);
+  SetStatusAndNotify(UpdateStatus::UPDATED_NEED_REBOOT);
   ScheduleUpdates();
 }
 
@@ -1574,8 +1548,8 @@
 }
 
 bool UpdateAttempter::IsUpdateRunningOrScheduled() {
-  return ((status_ != UPDATE_STATUS_IDLE &&
-           status_ != UPDATE_STATUS_UPDATED_NEED_REBOOT) ||
+  return ((status_ != UpdateStatus::IDLE &&
+           status_ != UpdateStatus::UPDATED_NEED_REBOOT) ||
           waiting_for_scheduled_check_);
 }
 
diff --git a/update_attempter.h b/update_attempter.h
index 06a3dac..a106a2e 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -39,6 +39,7 @@
 #include "update_engine/system_state.h"
 #include "update_engine/update_manager/policy.h"
 #include "update_engine/update_manager/update_manager.h"
+#include "update_engine/update_status.h"
 
 class MetricsLibraryInterface;
 
@@ -50,24 +51,10 @@
 
 class UpdateEngineAdaptor;
 
-enum UpdateStatus {
-  UPDATE_STATUS_IDLE = 0,
-  UPDATE_STATUS_CHECKING_FOR_UPDATE,
-  UPDATE_STATUS_UPDATE_AVAILABLE,
-  UPDATE_STATUS_DOWNLOADING,
-  UPDATE_STATUS_VERIFYING,
-  UPDATE_STATUS_FINALIZING,
-  UPDATE_STATUS_UPDATED_NEED_REBOOT,
-  UPDATE_STATUS_REPORTING_ERROR_EVENT,
-  UPDATE_STATUS_ATTEMPTING_ROLLBACK,
-  UPDATE_STATUS_DISABLED,
-};
-
-const char* UpdateStatusToString(UpdateStatus status);
-
 class UpdateAttempter : public ActionProcessorDelegate,
                         public DownloadActionDelegate {
  public:
+  using UpdateStatus = update_engine::UpdateStatus;
   static const int kMaxDeltaUpdateFailures;
 
   UpdateAttempter(SystemState* system_state,
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 749ad3b..51e7920 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -71,6 +71,7 @@
 using testing::SaveArg;
 using testing::SetArgumentPointee;
 using testing::_;
+using update_engine::UpdateStatus;
 
 namespace chromeos_update_engine {
 
@@ -137,7 +138,7 @@
     EXPECT_EQ(utils::kCpuSharesNormal, attempter_.shares_);
     EXPECT_EQ(MessageLoop::kTaskIdNull, attempter_.manage_shares_id_);
     EXPECT_FALSE(attempter_.download_active_);
-    EXPECT_EQ(UPDATE_STATUS_IDLE, attempter_.status_);
+    EXPECT_EQ(UpdateStatus::IDLE, attempter_.status_);
     EXPECT_EQ(0.0, attempter_.download_progress_);
     EXPECT_EQ(0, attempter_.last_checked_time_);
     EXPECT_EQ("0.0.0.0", attempter_.new_version_);
@@ -227,14 +228,14 @@
   EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _)).Times(0);
   attempter_.ActionCompleted(nullptr, &action, ErrorCode::kSuccess);
   EXPECT_EQ(503, attempter_.http_response_code());
-  EXPECT_EQ(UPDATE_STATUS_FINALIZING, attempter_.status());
+  EXPECT_EQ(UpdateStatus::FINALIZING, attempter_.status());
   ASSERT_EQ(nullptr, attempter_.error_event_.get());
 }
 
 TEST_F(UpdateAttempterTest, ActionCompletedErrorTest) {
   MockAction action;
   EXPECT_CALL(action, Type()).WillRepeatedly(Return("MockAction"));
-  attempter_.status_ = UPDATE_STATUS_DOWNLOADING;
+  attempter_.status_ = UpdateStatus::DOWNLOADING;
   EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _))
       .WillOnce(Return(false));
   attempter_.ActionCompleted(nullptr, &action, ErrorCode::kError);
@@ -254,7 +255,7 @@
   EXPECT_CALL(*prefs_, GetInt64(kPrefsDeltaUpdateFailures, _)).Times(0);
   attempter_.ActionCompleted(nullptr, &action, ErrorCode::kSuccess);
   EXPECT_EQ(500, attempter_.http_response_code());
-  EXPECT_EQ(UPDATE_STATUS_IDLE, attempter_.status());
+  EXPECT_EQ(UpdateStatus::IDLE, attempter_.status());
   EXPECT_EQ(234, attempter_.server_dictated_poll_interval_);
   ASSERT_TRUE(attempter_.error_event_.get() == nullptr);
 }
@@ -272,7 +273,7 @@
                                      nullptr,
                                      &debugd_proxy_mock_,
                                      test_update_completed_marker);
-  EXPECT_EQ(UPDATE_STATUS_UPDATED_NEED_REBOOT, attempter.status());
+  EXPECT_EQ(UpdateStatus::UPDATED_NEED_REBOOT, attempter.status());
 }
 
 TEST_F(UpdateAttempterTest, GetErrorCodeForActionTest) {
@@ -373,7 +374,7 @@
                                                OmahaEvent::kResultError,
                                                err));
   attempter_.ScheduleErrorEventAction();
-  EXPECT_EQ(UPDATE_STATUS_REPORTING_ERROR_EVENT, attempter_.status());
+  EXPECT_EQ(UpdateStatus::REPORTING_ERROR_EVENT, attempter_.status());
 }
 
 namespace {
@@ -439,7 +440,7 @@
       dynamic_cast<DownloadAction*>(attempter_.actions_[5].get());
   ASSERT_NE(nullptr, download_action);
   EXPECT_EQ(&attempter_, download_action->delegate());
-  EXPECT_EQ(UPDATE_STATUS_CHECKING_FOR_UPDATE, attempter_.status());
+  EXPECT_EQ(UpdateStatus::CHECKING_FOR_UPDATE, attempter_.status());
   loop_.BreakLoop();
 }
 
@@ -515,7 +516,7 @@
   for (size_t i = 0; i < arraysize(kRollbackActionTypes); ++i) {
     EXPECT_EQ(kRollbackActionTypes[i], attempter_.actions_[i]->Type());
   }
-  EXPECT_EQ(UPDATE_STATUS_ATTEMPTING_ROLLBACK, attempter_.status());
+  EXPECT_EQ(UpdateStatus::ATTEMPTING_ROLLBACK, attempter_.status());
   InstallPlanAction* install_plan_action =
         dynamic_cast<InstallPlanAction*>(attempter_.actions_[0].get());
   InstallPlan* install_plan = install_plan_action->install_plan();
@@ -573,7 +574,7 @@
                  base::Bind(&UpdateAttempterTest::PingOmahaTestStart,
                             base::Unretained(this)));
   chromeos::MessageLoopRunMaxIterations(&loop_, 100);
-  EXPECT_EQ(UPDATE_STATUS_UPDATED_NEED_REBOOT, attempter_.status());
+  EXPECT_EQ(UpdateStatus::UPDATED_NEED_REBOOT, attempter_.status());
   EXPECT_TRUE(attempter_.schedule_updates_called());
 }
 
diff --git a/update_engine.gyp b/update_engine.gyp
index 2a1768e..ec32550 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -51,6 +51,7 @@
       # We need this include dir because we include all the local code as
       # "update_engine/...".
       '<(platform2_root)/../aosp/system',
+      '<(platform2_root)/../aosp/system/update_engine/client_library/include',
     ],
   },
   'targets': [
@@ -213,6 +214,7 @@
         'update_manager/real_updater_provider.cc',
         'update_manager/state_factory.cc',
         'update_manager/update_manager.cc',
+        'update_status_utils.cc',
         'utils.cc',
         'xz_extent_writer.cc',
       ],
diff --git a/update_status_utils.cc b/update_status_utils.cc
new file mode 100644
index 0000000..ff039b8
--- /dev/null
+++ b/update_status_utils.cc
@@ -0,0 +1,89 @@
+//
+// Copyright (C) 2015 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.
+//
+#include "update_engine/update_status_utils.h"
+
+#include <base/logging.h>
+#include <update_engine/dbus-constants.h>
+
+using update_engine::UpdateStatus;
+
+namespace chromeos_update_engine {
+
+const char* UpdateStatusToString(const UpdateStatus& status) {
+  switch (status) {
+    case UpdateStatus::IDLE:
+      return update_engine::kUpdateStatusIdle;
+    case UpdateStatus::CHECKING_FOR_UPDATE:
+      return update_engine::kUpdateStatusCheckingForUpdate;
+    case UpdateStatus::UPDATE_AVAILABLE:
+      return update_engine::kUpdateStatusUpdateAvailable;
+    case UpdateStatus::DOWNLOADING:
+      return update_engine::kUpdateStatusDownloading;
+    case UpdateStatus::VERIFYING:
+      return update_engine::kUpdateStatusVerifying;
+    case UpdateStatus::FINALIZING:
+      return update_engine::kUpdateStatusFinalizing;
+    case UpdateStatus::UPDATED_NEED_REBOOT:
+      return update_engine::kUpdateStatusUpdatedNeedReboot;
+    case UpdateStatus::REPORTING_ERROR_EVENT:
+      return update_engine::kUpdateStatusReportingErrorEvent;
+    case UpdateStatus::ATTEMPTING_ROLLBACK:
+      return update_engine::kUpdateStatusAttemptingRollback;
+    case UpdateStatus::DISABLED:
+      return update_engine::kUpdateStatusDisabled;
+  }
+
+  NOTREACHED();
+  return nullptr;
+}
+
+bool StringToUpdateStatus(const std::string& s,
+                          UpdateStatus* status) {
+  if (s == update_engine::kUpdateStatusIdle) {
+    *status = UpdateStatus::IDLE;
+    return true;
+  } else if (s == update_engine::kUpdateStatusCheckingForUpdate) {
+    *status = UpdateStatus::CHECKING_FOR_UPDATE;
+    return true;
+  } else if (s == update_engine::kUpdateStatusUpdateAvailable) {
+    *status = UpdateStatus::UPDATE_AVAILABLE;
+    return true;
+  } else if (s == update_engine::kUpdateStatusDownloading) {
+    *status = UpdateStatus::DOWNLOADING;
+    return true;
+  } else if (s == update_engine::kUpdateStatusVerifying) {
+    *status = UpdateStatus::VERIFYING;
+    return true;
+  } else if (s == update_engine::kUpdateStatusFinalizing) {
+    *status = UpdateStatus::FINALIZING;
+    return true;
+  } else if (s == update_engine::kUpdateStatusUpdatedNeedReboot) {
+    *status = UpdateStatus::UPDATED_NEED_REBOOT;
+    return true;
+  } else if (s == update_engine::kUpdateStatusReportingErrorEvent) {
+    *status = UpdateStatus::REPORTING_ERROR_EVENT;
+    return true;
+  } else if (s == update_engine::kUpdateStatusAttemptingRollback) {
+    *status = UpdateStatus::ATTEMPTING_ROLLBACK;
+    return true;
+  } else if (s == update_engine::kUpdateStatusDisabled) {
+    *status = UpdateStatus::DISABLED;
+    return true;
+  }
+  return false;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/update_status_utils.h b/update_status_utils.h
new file mode 100644
index 0000000..78d3530
--- /dev/null
+++ b/update_status_utils.h
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2015 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.
+//
+
+#ifndef CLIENT_LIBRARY_UPDATE_STATUS_TO_STRING_H_
+#define CLIENT_LIBRARY_UPDATE_STATUS_TO_STRING_H_
+
+#include <string>
+
+#include "update_engine/update_status.h"
+
+namespace chromeos_update_engine {
+
+const char* UpdateStatusToString(const update_engine::UpdateStatus& status);
+
+bool StringToUpdateStatus(const std::string& update_status_as_string,
+                          update_engine::UpdateStatus* status);
+
+}  // namespace chromeos_update_engine
+
+#endif  // CLIENT_LIBRARY_UPDATE_STATUS_TO_STRING_H_