Support per-partition timestamps

update_engine is heading toward supporting partial updates, which an OTA
update can update just a subset of all partitions. In this context, a
single max_timestamp in OTA manifest is insufficient for checking
potential downgrades, as different partitions can have different
timestamps. This CL adds per-partition timestamp support on
update_engine side. update_engine will accept a payload with
per-partition timestamps and reject the update if any partition has an
older timestamp.

Changes made:
  1. Add new version field to PartitionUpdate protobuf message.
  2. Add new methods to HardwareInterface for fetching/checking
  timestamp of each partition.
  3. Update delta_performer to invoke new APIs in 2 properly.
  4. Add relevant testcases.

Test: unittest
Bug: 162553432
Change-Id: I767343e003fd35ce0d22197b15040488cf30be30
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 19d1297..aa0b4f5 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -1628,17 +1628,15 @@
     LOG(ERROR) << "Manifest contains deprecated fields.";
     return ErrorCode::kPayloadMismatchedType;
   }
-
-  if (manifest_.max_timestamp() < hardware_->GetBuildTimestamp()) {
-    LOG(ERROR) << "The current OS build timestamp ("
-               << hardware_->GetBuildTimestamp()
-               << ") is newer than the maximum timestamp in the manifest ("
-               << manifest_.max_timestamp() << ")";
+  TimestampCheckResult result = CheckTimestampError();
+  if (result == TimestampCheckResult::DOWNGRADE) {
     if (!hardware_->AllowDowngrade()) {
       return ErrorCode::kPayloadTimestampError;
     }
     LOG(INFO) << "The current OS build allows downgrade, continuing to apply"
                  " the payload with an older timestamp.";
+  } else if (result == TimestampCheckResult::FAILURE) {
+    return ErrorCode::kPayloadTimestampError;
   }
 
   // TODO(crbug.com/37661) we should be adding more and more manifest checks,
@@ -1647,6 +1645,53 @@
   return ErrorCode::kSuccess;
 }
 
+TimestampCheckResult DeltaPerformer::CheckTimestampError() const {
+  bool is_partial_update =
+      manifest_.has_partial_update() && manifest_.partial_update();
+  const auto& partitions = manifest_.partitions();
+  auto&& timestamp_valid = [this](const PartitionUpdate& partition) {
+    return hardware_->IsPartitionUpdateValid(partition.partition_name(),
+                                             partition.version());
+  };
+  if (is_partial_update) {
+    // for partial updates, all partition MUST have valid timestamps
+    // But max_timestamp can be empty
+    for (const auto& partition : partitions) {
+      if (!partition.has_version()) {
+        LOG(ERROR)
+            << "PartitionUpdate " << partition.partition_name()
+            << " does ot have a version field. Not allowed in partial updates.";
+        return TimestampCheckResult::FAILURE;
+      }
+      if (!timestamp_valid(partition)) {
+        // Warning because the system might allow downgrade.
+        LOG(WARNING) << "PartitionUpdate " << partition.partition_name()
+                     << " has an older version than partition on device.";
+        return TimestampCheckResult::DOWNGRADE;
+      }
+    }
+
+    return TimestampCheckResult::SUCCESS;
+  }
+  if (manifest_.max_timestamp() < hardware_->GetBuildTimestamp()) {
+    LOG(ERROR) << "The current OS build timestamp ("
+               << hardware_->GetBuildTimestamp()
+               << ") is newer than the maximum timestamp in the manifest ("
+               << manifest_.max_timestamp() << ")";
+    return TimestampCheckResult::DOWNGRADE;
+  }
+  // Otherwise... partitions can have empty timestamps.
+  for (const auto& partition : partitions) {
+    if (partition.has_version() && !timestamp_valid(partition)) {
+      // Warning because the system might allow downgrade.
+      LOG(WARNING) << "PartitionUpdate " << partition.partition_name()
+                   << " has an older version than partition on device.";
+      return TimestampCheckResult::DOWNGRADE;
+    }
+  }
+  return TimestampCheckResult::SUCCESS;
+}
+
 ErrorCode DeltaPerformer::ValidateOperationHash(
     const InstallOperation& operation) {
   if (!operation.data_sha256_hash().size()) {