Add proto3 support for care_map

Switching to the protobuf format helps to make the care_map more
extensible. As we have such plans in the future, add the support to
parse the protobuf message in the update_verifier.

Bug: 77867897
Test: unit tests pass, update_verifier successfully verifies a care_map.pb
Change-Id: I9fe83cb4dd3cc8d6fd0260f2a47338fe142d3938
diff --git a/update_verifier/Android.bp b/update_verifier/Android.bp
index f6c7056..f4dc1f4 100644
--- a/update_verifier/Android.bp
+++ b/update_verifier/Android.bp
@@ -33,6 +33,7 @@
     ],
 
     srcs: [
+        "care_map.proto",
         "update_verifier.cpp",
     ],
 
@@ -49,6 +50,11 @@
         "libbase",
         "libcutils",
     ],
+
+    proto: {
+        type: "lite",
+        export_proto_headers: true,
+    }
 }
 
 cc_binary {
@@ -74,6 +80,7 @@
         "libhardware",
         "libhidlbase",
         "liblog",
+        "libprotobuf-cpp-lite",
         "libutils",
     ],
 
diff --git a/update_verifier/care_map.proto b/update_verifier/care_map.proto
new file mode 100644
index 0000000..442ddd4
--- /dev/null
+++ b/update_verifier/care_map.proto
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+syntax = "proto3";
+
+package UpdateVerifier;
+option optimize_for = LITE_RUNTIME;
+
+message CareMap {
+  message PartitionInfo {
+    string name = 1;
+    string ranges = 2;
+    string id = 3;
+    string fingerprint = 4;
+  }
+
+  repeated PartitionInfo partitions = 1;
+}
diff --git a/update_verifier/include/update_verifier/update_verifier.h b/update_verifier/include/update_verifier/update_verifier.h
index 16b394e..534384e 100644
--- a/update_verifier/include/update_verifier/update_verifier.h
+++ b/update_verifier/include/update_verifier/update_verifier.h
@@ -20,5 +20,13 @@
 
 int update_verifier(int argc, char** argv);
 
-// Exposed for testing purpose.
+// Returns true to indicate a passing verification (or the error should be ignored); Otherwise
+// returns false on fatal errors, where we should reject the current boot and trigger a fallback.
+// This function tries to process the care_map.txt as protobuf message; and falls back to use the
+// plain text format if the parse failed.
+//
+// Note that update_verifier should be backward compatible to not reject care_map.txt from old
+// releases, which could otherwise fail to boot into the new release. For example, we've changed
+// the care_map format between N and O. An O update_verifier would fail to work with N care_map.txt.
+// This could be a result of sideloading an O OTA while the device having a pending N update.
 bool verify_image(const std::string& care_map_name);
diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp
index dc72763..5e5aa18 100644
--- a/update_verifier/update_verifier.cpp
+++ b/update_verifier/update_verifier.cpp
@@ -60,6 +60,7 @@
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <cutils/android_reboot.h>
 
+#include "care_map.pb.h"
 #include "otautil/rangeset.h"
 
 using android::sp;
@@ -189,33 +190,12 @@
   return ret;
 }
 
-// Returns true to indicate a passing verification (or the error should be ignored); Otherwise
-// returns false on fatal errors, where we should reject the current boot and trigger a fallback.
-// Note that update_verifier should be backward compatible to not reject care_map.txt from old
-// releases, which could otherwise fail to boot into the new release. For example, we've changed
-// the care_map format between N and O. An O update_verifier would fail to work with N
-// care_map.txt. This could be a result of sideloading an O OTA while the device having a pending N
-// update.
-bool verify_image(const std::string& care_map_name) {
-  android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY)));
-  // If the device is flashed before the current boot, it may not have care_map.txt
-  // in /data/ota_package. To allow the device to continue booting in this situation,
-  // we should print a warning and skip the block verification.
-  if (care_map_fd.get() == -1) {
-    PLOG(WARNING) << "Failed to open " << care_map_name;
-    return true;
-  }
+static bool process_care_map_plain_text(const std::string& care_map_contents) {
   // care_map file has up to six lines, where every two lines make a pair. Within each pair, the
   // first line has the partition name (e.g. "system"), while the second line holds the ranges of
   // all the blocks to verify.
-  std::string file_content;
-  if (!android::base::ReadFdToString(care_map_fd.get(), &file_content)) {
-    LOG(ERROR) << "Error reading care map contents to string.";
-    return false;
-  }
-
-  std::vector<std::string> lines;
-  lines = android::base::Split(android::base::Trim(file_content), "\n");
+  std::vector<std::string> lines =
+      android::base::Split(android::base::Trim(care_map_contents), "\n");
   if (lines.size() != 2 && lines.size() != 4 && lines.size() != 6) {
     LOG(ERROR) << "Invalid lines in care_map: found " << lines.size()
                << " lines, expecting 2 or 4 or 6 lines.";
@@ -237,6 +217,50 @@
   return true;
 }
 
+bool verify_image(const std::string& care_map_name) {
+  android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY)));
+  // If the device is flashed before the current boot, it may not have care_map.txt in
+  // /data/ota_package. To allow the device to continue booting in this situation, we should
+  // print a warning and skip the block verification.
+  if (care_map_fd.get() == -1) {
+    PLOG(WARNING) << "Failed to open " << care_map_name;
+    return true;
+  }
+
+  std::string file_content;
+  if (!android::base::ReadFdToString(care_map_fd.get(), &file_content)) {
+    PLOG(ERROR) << "Failed to read " << care_map_name;
+    return false;
+  }
+
+  if (file_content.empty()) {
+    LOG(ERROR) << "Unexpected empty care map";
+    return false;
+  }
+
+  UpdateVerifier::CareMap care_map;
+  // Falls back to use the plain text version if we cannot parse the file as protobuf message.
+  if (!care_map.ParseFromString(file_content)) {
+    return process_care_map_plain_text(file_content);
+  }
+
+  for (const auto& partition : care_map.partitions()) {
+    if (partition.name().empty()) {
+      LOG(ERROR) << "Unexpected empty partition name.";
+      return false;
+    }
+    if (partition.ranges().empty()) {
+      LOG(ERROR) << "Unexpected block ranges for partition " << partition.name();
+      return false;
+    }
+    if (!read_blocks(partition.name(), partition.ranges())) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 static int reboot_device() {
   if (android_reboot(ANDROID_RB_RESTART2, 0, nullptr) == -1) {
     LOG(ERROR) << "Failed to reboot.";