Verify the package compatibility with libvintf.
am: 62e0bc7586

Change-Id: I35383abfd52766070df0de00013ec743dea190c7
diff --git a/Android.mk b/Android.mk
index d701013..1b83d75 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,6 +14,10 @@
 
 LOCAL_PATH := $(call my-dir)
 
+# Needed by build/make/core/Makefile.
+RECOVERY_API_VERSION := 3
+RECOVERY_FSTAB_VERSION := 2
+
 # libfusesideload (static library)
 # ===============================
 include $(CLEAR_VARS)
@@ -36,6 +40,22 @@
 LOCAL_STATIC_LIBRARIES := libbase
 include $(BUILD_STATIC_LIBRARY)
 
+# librecovery (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+    install.cpp
+LOCAL_CFLAGS := -Wno-unused-parameter -Werror
+LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
+LOCAL_MODULE := librecovery
+LOCAL_STATIC_LIBRARIES := \
+    libminui \
+    libcrypto_utils \
+    libcrypto \
+    libbase
+
+include $(BUILD_STATIC_LIBRARY)
+
 # recovery (static executable)
 # ===============================
 include $(CLEAR_VARS)
@@ -45,7 +65,6 @@
     asn1_decoder.cpp \
     device.cpp \
     fuse_sdcard_provider.cpp \
-    install.cpp \
     recovery.cpp \
     roots.cpp \
     rotate_logs.cpp \
@@ -65,8 +84,6 @@
 endif
 endif
 
-RECOVERY_API_VERSION := 3
-RECOVERY_FSTAB_VERSION := 2
 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
 LOCAL_CFLAGS += -Wno-unused-parameter -Werror
 LOCAL_CLANG := true
@@ -76,6 +93,7 @@
     system/core/adb \
 
 LOCAL_STATIC_LIBRARIES := \
+    librecovery \
     libbatterymonitor \
     libbootloader_message \
     libext4_utils \
diff --git a/error_code.h b/error_code.h
index 5dad6b2..cde4ee6 100644
--- a/error_code.h
+++ b/error_code.h
@@ -22,7 +22,8 @@
   kLowBattery = 20,
   kZipVerificationFailure,
   kZipOpenFailure,
-  kBootreasonInBlacklist
+  kBootreasonInBlacklist,
+  kPackageCompatibilityFailure,
 };
 
 enum CauseCode {
diff --git a/install.cpp b/install.cpp
index 0a2fa3c..b4b869b 100644
--- a/install.cpp
+++ b/install.cpp
@@ -489,6 +489,70 @@
   return INSTALL_SUCCESS;
 }
 
+// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the
+// entry doesn't exist. Note that the compatibility info is packed in a zip file inside the OTA
+// package.
+bool verify_package_compatibility(ZipArchiveHandle package_zip) {
+  LOG(INFO) << "Verifying package compatibility...";
+
+  static constexpr const char* COMPATIBILITY_ZIP_ENTRY = "compatibility.zip";
+  ZipString compatibility_entry_name(COMPATIBILITY_ZIP_ENTRY);
+  ZipEntry compatibility_entry;
+  if (FindEntry(package_zip, compatibility_entry_name, &compatibility_entry) != 0) {
+    LOG(INFO) << "Package doesn't contain " << COMPATIBILITY_ZIP_ENTRY << " entry";
+    return true;
+  }
+
+  std::string zip_content(compatibility_entry.uncompressed_length, '\0');
+  int32_t ret;
+  if ((ret = ExtractToMemory(package_zip, &compatibility_entry,
+                             reinterpret_cast<uint8_t*>(&zip_content[0]),
+                             compatibility_entry.uncompressed_length)) != 0) {
+    LOG(ERROR) << "Failed to read " << COMPATIBILITY_ZIP_ENTRY << ": " << ErrorCodeString(ret);
+    return false;
+  }
+
+  ZipArchiveHandle zip_handle;
+  ret = OpenArchiveFromMemory(static_cast<void*>(const_cast<char*>(zip_content.data())),
+                              zip_content.size(), COMPATIBILITY_ZIP_ENTRY, &zip_handle);
+  if (ret != 0) {
+    LOG(ERROR) << "Failed to OpenArchiveFromMemory: " << ErrorCodeString(ret);
+    return false;
+  }
+
+  // Iterate all the entries inside COMPATIBILITY_ZIP_ENTRY and read the contents.
+  void* cookie;
+  ret = StartIteration(zip_handle, &cookie, nullptr, nullptr);
+  if (ret != 0) {
+    LOG(ERROR) << "Failed to start iterating zip entries: " << ErrorCodeString(ret);
+    CloseArchive(zip_handle);
+    return false;
+  }
+  std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration);
+
+  std::vector<std::string> compatibility_info;
+  ZipEntry info_entry;
+  ZipString info_name;
+  while (Next(cookie, &info_entry, &info_name) == 0) {
+    std::string content(info_entry.uncompressed_length, '\0');
+    int32_t ret = ExtractToMemory(zip_handle, &info_entry, reinterpret_cast<uint8_t*>(&content[0]),
+                                  info_entry.uncompressed_length);
+    if (ret != 0) {
+      LOG(ERROR) << "Failed to read " << info_name.name << ": " << ErrorCodeString(ret);
+      CloseArchive(zip_handle);
+      return false;
+    }
+    compatibility_info.emplace_back(std::move(content));
+  }
+  EndIteration(cookie);
+  CloseArchive(zip_handle);
+
+  // TODO(b/36814503): Enable the actual verification when VintfObject::CheckCompatibility() lands.
+  // VintfObject::CheckCompatibility returns zero on success.
+  // return (android::vintf::VintfObject::CheckCompatibility(compatibility_info, true) == 0);
+  return true;
+}
+
 static int
 really_install_package(const char *path, bool* wipe_cache, bool needs_mount,
                        std::vector<std::string>& log_buffer, int retry_count, int* max_temperature)
@@ -536,6 +600,15 @@
         return INSTALL_CORRUPT;
     }
 
+    // Additionally verify the compatibility of the package.
+    if (!verify_package_compatibility(zip)) {
+      LOG(ERROR) << "Failed to verify package compatibility";
+      log_buffer.push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure));
+      sysReleaseMap(&map);
+      CloseArchive(zip);
+      return INSTALL_CORRUPT;
+    }
+
     // Verify and install the contents of the package.
     ui->Print("Installing update...\n");
     if (retry_count > 0) {
diff --git a/install.h b/install.h
index 1ec01e8..68f0a8d 100644
--- a/install.h
+++ b/install.h
@@ -37,4 +37,8 @@
 // Return true if succeed, otherwise return false.
 bool read_metadata_from_package(ZipArchiveHandle zip, std::string* meta_data);
 
+// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the
+// entry doesn't exist.
+bool verify_package_compatibility(ZipArchiveHandle package_zip);
+
 #endif  // RECOVERY_INSTALL_H_
diff --git a/tests/Android.mk b/tests/Android.mk
index 974aa0e..a1f0d48 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -93,6 +93,7 @@
     component/bootloader_message_test.cpp \
     component/edify_test.cpp \
     component/imgdiff_test.cpp \
+    component/install_test.cpp \
     component/sideload_test.cpp \
     component/uncrypt_test.cpp \
     component/updater_test.cpp \
@@ -117,6 +118,7 @@
     libbsdiff \
     libbspatch \
     libotafault \
+    librecovery \
     libupdater \
     libbootloader_message \
     libverifier \
@@ -131,7 +133,6 @@
     libsparse \
     libcrypto_utils \
     libcrypto \
-    libcutils \
     libbz \
     libziparchive \
     libutils \
diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp
new file mode 100644
index 0000000..3b6fbc3
--- /dev/null
+++ b/tests/component/install_test.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 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 agree 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 <stdio.h>
+
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <ziparchive/zip_archive.h>
+#include <ziparchive/zip_writer.h>
+
+#include "install.h"
+
+TEST(InstallTest, verify_package_compatibility_no_entry) {
+  TemporaryFile temp_file;
+  FILE* zip_file = fdopen(temp_file.fd, "w");
+  ZipWriter writer(zip_file);
+  // The archive must have something to be opened correctly.
+  ASSERT_EQ(0, writer.StartEntry("dummy_entry", 0));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
+  ASSERT_EQ(0, fclose(zip_file));
+
+  // Doesn't contain compatibility zip entry.
+  ZipArchiveHandle zip;
+  ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
+  ASSERT_TRUE(verify_package_compatibility(zip));
+  CloseArchive(zip);
+}
+
+TEST(InstallTest, verify_package_compatibility_invalid_entry) {
+  TemporaryFile temp_file;
+  FILE* zip_file = fdopen(temp_file.fd, "w");
+  ZipWriter writer(zip_file);
+  ASSERT_EQ(0, writer.StartEntry("compatibility.zip", 0));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
+  ASSERT_EQ(0, fclose(zip_file));
+
+  // Empty compatibility zip entry.
+  ZipArchiveHandle zip;
+  ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
+  ASSERT_FALSE(verify_package_compatibility(zip));
+  CloseArchive(zip);
+}