Support binary pb format for manifest in apexd.

For now, fall back to the JSON version if the manifest.pb file is not
present. This is an intermediate step until all prebuilts are recompiled
to contain the manifest.pb.

Bug: 143654022
Test: CtsStagedInstallHostTestCases
Change-Id: I5cfc69198ccc3085d27623251286aa97a4aadc98
diff --git a/apexd/Android.bp b/apexd/Android.bp
index c597cb1..600bb8a 100644
--- a/apexd/Android.bp
+++ b/apexd/Android.bp
@@ -288,11 +288,12 @@
   name: "gen_bad_apexes",
   out: ["apex.apexd_test_manifest_mismatch.apex"],
   srcs: [":apex.apexd_test"],
-  tools: ["soong_zip", "zipalign"],
+  tools: ["soong_zip", "zipalign", "conv_apex_manifest"],
   cmd: "unzip -q $(in) -d $(genDir) && " +
        "sed -i -e 's/\"version\": 1/\"version\": 137/' $(genDir)/apex_manifest.json && " +
+       "$(location conv_apex_manifest) proto $(genDir)/apex_manifest.json -o $(genDir)/apex_manifest.pb && " +
        "$(location soong_zip) -d -C $(genDir) -D $(genDir) " +
-       "-s apex_manifest.json -s apex_payload.img -s apex_pubkey " +
+       "-s apex_manifest.pb -s apex_manifest.json -s apex_payload.img -s apex_pubkey " +
        "-o $(genDir)/unaligned.apex && " +
        "$(location zipalign) -f 4096 $(genDir)/unaligned.apex " +
        "$(genDir)/apex.apexd_test_manifest_mismatch.apex"
@@ -308,7 +309,7 @@
   cmd: "unzip -q $(in) -d $(genDir) && " +
        "dd if=/dev/zero of=$(genDir)/apex_payload.img conv=notrunc bs=1024 seek=16 count=1 && " +
        "$(location soong_zip) -d -C $(genDir) -D $(genDir) " +
-       "-s apex_manifest.json -s apex_payload.img -s apex_pubkey " +
+       "-s apex_manifest.pb -s apex_manifest.json -s apex_payload.img -s apex_pubkey " +
        "-o $(genDir)/unaligned.apex && " +
        "$(location zipalign) -f 4096 $(genDir)/unaligned.apex " +
        "$(genDir)/apex.apexd_test_corrupt_apex.apex"
diff --git a/apexd/apex_constants.h b/apexd/apex_constants.h
index eb2ec7d..e36ea8c 100644
--- a/apexd/apex_constants.h
+++ b/apexd/apex_constants.h
@@ -33,6 +33,7 @@
 
 static constexpr const char* kApexPackageSuffix = ".apex";
 
-static constexpr const char* kManifestFilename = "apex_manifest.json";
+static constexpr const char* kManifestFilenameJson = "apex_manifest.json";
+static constexpr const char* kManifestFilenamePb = "apex_manifest.pb";
 }  // namespace apex
 }  // namespace android
diff --git a/apexd/apex_file.cpp b/apexd/apex_file.cpp
index 10a987b..13e76f3 100644
--- a/apexd/apex_file.cpp
+++ b/apexd/apex_file.cpp
@@ -85,10 +85,19 @@
   image_offset = entry.offset;
   image_size = entry.uncompressed_length;
 
-  ret = FindEntry(handle, kManifestFilename, &entry);
+  ret = FindEntry(handle, kManifestFilenamePb, &entry);
+  bool isJsonManifest = false;
   if (ret < 0) {
-    return Error() << "Could not find entry \"" << kManifestFilename
-                   << "\" in package " << path << ": " << ErrorCodeString(ret);
+    LOG(ERROR) << "Could not find entry \"" << kManifestFilenamePb
+               << "\" in package " << path << ": " << ErrorCodeString(ret);
+    LOG(ERROR) << "Falling back to JSON if present.";
+    isJsonManifest = true;
+    ret = FindEntry(handle, kManifestFilenameJson, &entry);
+    if (ret < 0) {
+      return Error() << "Could not find entry \"" << kManifestFilenameJson
+                     << "\" in package " << path << ": "
+                     << ErrorCodeString(ret);
+    }
   }
 
   uint32_t length = entry.uncompressed_length;
@@ -114,7 +123,12 @@
     }
   }
 
-  Result<ApexManifest> manifest = ParseManifest(manifest_content);
+  Result<ApexManifest> manifest;
+  if (isJsonManifest) {
+    manifest = ParseManifestJson(manifest_content);
+  } else {
+    manifest = ParseManifest(manifest_content);
+  }
   if (!manifest) {
     return manifest.error();
   }
@@ -376,16 +390,17 @@
 
 Result<void> ApexFile::VerifyManifestMatches(
     const std::string& mount_path) const {
-  std::string manifest_content;
-  const std::string manifest_path = mount_path + "/" + kManifestFilename;
-
-  if (!android::base::ReadFileToString(manifest_path, &manifest_content)) {
-    return Error() << "Failed to read manifest file: " << manifest_path;
-  }
-
-  Result<ApexManifest> verifiedManifest = ParseManifest(manifest_content);
+  Result<ApexManifest> verifiedManifest =
+      ReadManifest(mount_path + "/" + kManifestFilenamePb);
   if (!verifiedManifest) {
-    return verifiedManifest.error();
+    LOG(ERROR) << "Could not read manifest from  " << mount_path << "/"
+               << kManifestFilenamePb << " : " << verifiedManifest.error();
+    // Fallback to Json manifest if present.
+    LOG(ERROR) << "Trying to find a JSON manifest";
+    verifiedManifest = ReadManifest(mount_path + "/" + kManifestFilenameJson);
+    if (!verifiedManifest) {
+      return verifiedManifest.error();
+    }
   }
 
   if (!MessageDifferencer::Equals(manifest_, *verifiedManifest)) {
diff --git a/apexd/apex_manifest.cpp b/apexd/apex_manifest.cpp
index 1d1de4d..76b484b 100644
--- a/apexd/apex_manifest.cpp
+++ b/apexd/apex_manifest.cpp
@@ -17,57 +17,39 @@
 #include "apex_manifest.h"
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/strings.h>
+#include "apex_constants.h"
 #include "string_log.h"
 
 #include <google/protobuf/util/json_util.h>
-#include <google/protobuf/util/type_resolver_util.h>
 #include <memory>
 #include <string>
 
+using android::base::EndsWith;
 using android::base::Error;
 using android::base::Result;
-using google::protobuf::DescriptorPool;
-using google::protobuf::util::NewTypeResolverForDescriptorPool;
-using google::protobuf::util::TypeResolver;
+using google::protobuf::util::JsonParseOptions;
 
 namespace android {
 namespace apex {
 namespace {
-const char kTypeUrlPrefix[] = "type.googleapis.com";
 
-std::string GetTypeUrl(const ApexManifest& apex_manifest) {
-  const google::protobuf::Descriptor* message = apex_manifest.GetDescriptor();
-  return std::string(kTypeUrlPrefix) + "/" + message->full_name();
-}
-
-// TODO: JsonStringToMessage is a newly added function in protobuf
-// and is not yet available in the android tree. Replace this function with
-// https://developers.google.com/protocol-buffers/docs/reference/cpp/
-// google.protobuf.util.json_util#JsonStringToMessage.details
-// as and when the android tree gets updated
 Result<void> JsonToApexManifestMessage(const std::string& content,
                                        ApexManifest* apex_manifest) {
-  std::unique_ptr<TypeResolver> resolver(NewTypeResolverForDescriptorPool(
-      kTypeUrlPrefix, DescriptorPool::generated_pool()));
-  std::string binary;
-  auto parse_status = JsonToBinaryString(
-      resolver.get(), GetTypeUrl(*apex_manifest), content, &binary);
+  JsonParseOptions options;
+  options.ignore_unknown_fields = true;
+  auto parse_status = JsonStringToMessage(content, apex_manifest, options);
   if (!parse_status.ok()) {
     return Error() << "Failed to parse APEX Manifest JSON config: "
                    << parse_status.error_message().as_string();
   }
-
-  if (!apex_manifest->ParseFromString(binary)) {
-    return Error() << "Unexpected fields in APEX Manifest JSON config";
-  }
   return {};
 }
 
 }  // namespace
 
-Result<ApexManifest> ParseManifest(const std::string& content) {
+Result<ApexManifest> ParseManifestJson(const std::string& content) {
   ApexManifest apex_manifest;
-  std::string err;
   Result<void> parse_manifest_status =
       JsonToApexManifestMessage(content, &apex_manifest);
   if (!parse_manifest_status) {
@@ -87,6 +69,27 @@
   return apex_manifest;
 }
 
+Result<ApexManifest> ParseManifest(const std::string& content) {
+  ApexManifest apex_manifest;
+  std::string err;
+
+  if (!apex_manifest.ParseFromString(content)) {
+    return Error() << "Can't parse APEX manifest.";
+  }
+
+  // Verifying required fields.
+  // name
+  if (apex_manifest.name().empty()) {
+    return Error() << "Missing required field \"name\" from APEX manifest.";
+  }
+
+  // version
+  if (apex_manifest.version() == 0) {
+    return Error() << "Missing required field \"version\" from APEX manifest.";
+  }
+  return apex_manifest;
+}
+
 std::string GetPackageId(const ApexManifest& apexManifest) {
   return apexManifest.name() + "@" + std::to_string(apexManifest.version());
 }
@@ -96,6 +99,9 @@
   if (!android::base::ReadFileToString(path, &content)) {
     return Error() << "Failed to read manifest file: " << path;
   }
+  if (EndsWith(path, kManifestFilenameJson)) {
+    return ParseManifestJson(content);
+  }
   return ParseManifest(content);
 }
 
diff --git a/apexd/apex_manifest.h b/apexd/apex_manifest.h
index f299648..58f24bc 100644
--- a/apexd/apex_manifest.h
+++ b/apexd/apex_manifest.h
@@ -29,6 +29,9 @@
 namespace apex {
 // Parses and validates APEX manifest.
 android::base::Result<ApexManifest> ParseManifest(const std::string& content);
+// Parses and validates APEX manifest (in JSON format);
+android::base::Result<ApexManifest> ParseManifestJson(
+    const std::string& content);
 // Returns package id of an ApexManifest
 std::string GetPackageId(const ApexManifest& apex_manifest);
 // Reads and parses APEX manifest from the file on disk.
diff --git a/apexd/apex_manifest_test.cpp b/apexd/apex_manifest_test.cpp
index 44528fa..dec6d76 100644
--- a/apexd/apex_manifest_test.cpp
+++ b/apexd/apex_manifest_test.cpp
@@ -26,7 +26,7 @@
 namespace apex {
 
 TEST(ApexManifestTest, SimpleTest) {
-  auto apex_manifest = ParseManifest(
+  auto apex_manifest = ParseManifestJson(
       "{\"name\": \"com.android.example.apex\", \"version\": 1}\n");
   ASSERT_TRUE(apex_manifest) << apex_manifest.error();
   EXPECT_EQ("com.android.example.apex", std::string(apex_manifest->name()));
@@ -35,7 +35,7 @@
 }
 
 TEST(ApexManifestTest, NameMissing) {
-  auto apex_manifest = ParseManifest("{\"version\": 1}\n");
+  auto apex_manifest = ParseManifestJson("{\"version\": 1}\n");
   ASSERT_FALSE(apex_manifest);
   EXPECT_EQ(apex_manifest.error().message(),
             std::string("Missing required field \"name\" from APEX manifest."))
@@ -44,7 +44,7 @@
 
 TEST(ApexManifestTest, VersionMissing) {
   auto apex_manifest =
-      ParseManifest("{\"name\": \"com.android.example.apex\"}\n");
+      ParseManifestJson("{\"name\": \"com.android.example.apex\"}\n");
   ASSERT_FALSE(apex_manifest);
   EXPECT_EQ(
       apex_manifest.error().message(),
@@ -53,7 +53,7 @@
 }
 
 TEST(ApexManifestTest, VersionNotNumber) {
-  auto apex_manifest = ParseManifest(
+  auto apex_manifest = ParseManifestJson(
       "{\"name\": \"com.android.example.apex\", \"version\": \"a\"}\n");
 
   ASSERT_FALSE(apex_manifest);
@@ -64,14 +64,14 @@
 }
 
 TEST(ApexManifestTest, NoPreInstallHook) {
-  auto apex_manifest = ParseManifest(
+  auto apex_manifest = ParseManifestJson(
       "{\"name\": \"com.android.example.apex\", \"version\": 1}\n");
   ASSERT_TRUE(apex_manifest) << apex_manifest.error();
   EXPECT_EQ("", std::string(apex_manifest->preinstallhook()));
 }
 
 TEST(ApexManifestTest, PreInstallHook) {
-  auto apex_manifest = ParseManifest(
+  auto apex_manifest = ParseManifestJson(
       "{\"name\": \"com.android.example.apex\", \"version\": 1, "
       "\"preInstallHook\": \"bin/preInstallHook\"}\n");
   ASSERT_TRUE(apex_manifest) << apex_manifest.error();
@@ -79,14 +79,14 @@
 }
 
 TEST(ApexManifestTest, NoPostInstallHook) {
-  auto apex_manifest = ParseManifest(
+  auto apex_manifest = ParseManifestJson(
       "{\"name\": \"com.android.example.apex\", \"version\": 1}\n");
   ASSERT_TRUE(apex_manifest) << apex_manifest.error();
   EXPECT_EQ("", std::string(apex_manifest->postinstallhook()));
 }
 
 TEST(ApexManifestTest, PostInstallHook) {
-  auto apex_manifest = ParseManifest(
+  auto apex_manifest = ParseManifestJson(
       "{\"name\": \"com.android.example.apex\", \"version\": 1, "
       "\"postInstallHook\": \"bin/postInstallHook\"}\n");
   ASSERT_TRUE(apex_manifest) << apex_manifest.error();
@@ -95,7 +95,7 @@
 }
 
 TEST(ApexManifestTest, UnparsableManifest) {
-  auto apex_manifest = ParseManifest("This is an invalid pony");
+  auto apex_manifest = ParseManifestJson("This is an invalid pony");
   ASSERT_FALSE(apex_manifest);
   EXPECT_EQ(apex_manifest.error().message(),
             std::string("Failed to parse APEX Manifest JSON config: Unexpected "
@@ -104,7 +104,7 @@
 }
 
 TEST(ApexManifestTest, NoCode) {
-  auto apex_manifest = ParseManifest(
+  auto apex_manifest = ParseManifestJson(
       "{\"name\": \"com.android.example.apex\", \"version\": 1, "
       "\"noCode\": true}\n");
   ASSERT_TRUE(apex_manifest) << apex_manifest.error();
diff --git a/apexd/apexd_prepostinstall.cpp b/apexd/apexd_prepostinstall.cpp
index d9ece73..0f162ff 100644
--- a/apexd/apexd_prepostinstall.cpp
+++ b/apexd/apexd_prepostinstall.cpp
@@ -165,11 +165,20 @@
       std::string active_point;
       {
         Result<ApexManifest> manifest_or =
-            ReadManifest(mount_point + "/" + kManifestFilename);
+            ReadManifest(mount_point + "/" + kManifestFilenamePb);
         if (!manifest_or) {
-          LOG(ERROR) << "Could not read manifest from  " << mount_point
-                     << " for " << name << ": " << manifest_or.error();
-          _exit(202);
+          LOG(ERROR) << "Could not read manifest from  " << mount_point << "/"
+                     << kManifestFilenamePb << " for " << name << ": "
+                     << manifest_or.error();
+          // Fallback to Json manifest if present.
+          LOG(ERROR) << "Trying to find a JSON manifest";
+          manifest_or = ReadManifest(mount_point + "/" + kManifestFilenameJson);
+          if (!manifest_or) {
+            LOG(ERROR) << "Could not read manifest from  " << mount_point << "/"
+                       << kManifestFilenameJson << " for " << name << ": "
+                       << manifest_or.error();
+            _exit(202);
+          }
         }
         const auto& manifest = *manifest_or;
         hook = (manifest.*fn)();