Add VintfObjectRecovery

A special VintfObject for recovery. CheckCompatibility
knows how to mount /system and /vendor if necessary.

Test: testing with a test CL that replaces graphics test
      into calling android::vintf::details::mountSystem
      / mountVendor, and the partitions are actually
      mounted.

Bug: 36814503
Change-Id: Ie6a8e533841a128a411134973006ac4afb17a78e
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..b7b2ec0
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,27 @@
+# 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 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libvintf_recovery
+LOCAL_SRC_FILES := VintfObjectRecovery.cpp
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include/vintf
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_STATIC_LIBRARIES := \
+    libbase \
+    libvintf \
+    libfs_mgr
+include $(BUILD_STATIC_LIBRARY)
diff --git a/VintfObject.cpp b/VintfObject.cpp
index 00e02dd..674031a 100644
--- a/VintfObject.cpp
+++ b/VintfObject.cpp
@@ -73,13 +73,7 @@
 
 namespace details {
 
-static status_t mountSystem() {
-    // TODO(b/36814503): mount system partition here for recovery mode.
-    return OK;
-}
-
-static status_t mountVendor() {
-    // TODO(b/36814503): mount vendor partition here for recovery mode.
+static status_t fakeMountUmount() {
     return OK;
 }
 
@@ -90,7 +84,7 @@
     DUPLICATED_DEV_ENTRY,
 };
 
-std::string toString(ParseStatus status) {
+static std::string toString(ParseStatus status) {
     switch(status) {
         case ParseStatus::OK:                   return "OK";
         case ParseStatus::PARSE_ERROR:          return "parse error";
@@ -101,7 +95,7 @@
 }
 
 template<typename T>
-ParseStatus tryParse(const std::string &xml, const XmlConverter<T> &parse,
+static ParseStatus tryParse(const std::string &xml, const XmlConverter<T> &parse,
         std::unique_ptr<T> *fwk, std::unique_ptr<T> *dev) {
     std::unique_ptr<T> ret = std::make_unique<T>();
     if (!parse(ret.get(), xml)) {
@@ -124,6 +118,7 @@
 template<typename T, typename GetFunction>
 static status_t getMissing(const T *pkg, bool mount,
         std::function<status_t(void)> mountFunction,
+        std::function<status_t(void)> umountFunction,
         const T **updated,
         GetFunction getFunction) {
     if (pkg != nullptr) {
@@ -133,6 +128,9 @@
             (void)mountFunction(); // ignore mount errors
         }
         *updated = getFunction();
+        if (mount) {
+            (void)umountFunction(); // ignore umount errors
+        }
     }
     return OK;
 }
@@ -166,7 +164,9 @@
 // Do compatibility check.
 int32_t checkCompatibility(const std::vector<std::string> &xmls, bool mount,
         std::function<status_t(void)> mountSystem,
+        std::function<status_t(void)> umountSystem,
         std::function<status_t(void)> mountVendor,
+        std::function<status_t(void)> umountVendor,
         std::function<const HalManifest *(bool)> GetFrameworkHalManifest,
         std::function<const HalManifest *(bool)> GetDeviceHalManifest,
         std::function<const RuntimeInfo *(bool)> GetRuntimeInfo,
@@ -205,11 +205,13 @@
     }
 
     // get missing info from device
-    if ((status = getMissing(pkg.fwk.manifest.get(), mount, mountSystem, &updated.fwk.manifest,
+    if ((status = getMissing(pkg.fwk.manifest.get(), mount, mountSystem, umountSystem,
+            &updated.fwk.manifest,
             std::bind(GetFrameworkHalManifest, true /* skipCache */))) != OK) {
         return status;
     }
-    if ((status = getMissing(pkg.dev.manifest.get(), mount, mountVendor, &updated.dev.manifest,
+    if ((status = getMissing(pkg.dev.manifest.get(), mount, mountVendor, umountVendor,
+            &updated.dev.manifest,
             std::bind(GetDeviceHalManifest, true /* skipCache */))) != OK) {
         return status;
     }
@@ -272,10 +274,10 @@
 
 // static
 int32_t VintfObject::CheckCompatibility(
-        const std::vector<std::string> &xmls,
-        bool mount, std::string *error) {
-    return details::checkCompatibility(xmls, mount,
-            &details::mountSystem, &details::mountVendor,
+        const std::vector<std::string> &xmls, std::string *error) {
+    return details::checkCompatibility(xmls, false /* mount */,
+            &details::fakeMountUmount, &details::fakeMountUmount,
+            &details::fakeMountUmount, &details::fakeMountUmount,
             &VintfObject::GetFrameworkHalManifest,
             &VintfObject::GetDeviceHalManifest,
             &VintfObject::GetRuntimeInfo,
diff --git a/VintfObjectRecovery.cpp b/VintfObjectRecovery.cpp
new file mode 100644
index 0000000..ccae798
--- /dev/null
+++ b/VintfObjectRecovery.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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 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 "VintfObjectRecovery.h"
+
+#include <android-base/properties.h>
+#include <sys/mount.h>
+
+#include "fs_mgr.h"
+
+namespace android {
+namespace vintf {
+
+namespace details {
+using FstabMgr = std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)>;
+
+static status_t mountAt(const FstabMgr &fstab, const char* path, const char* mount_point) {
+    fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), path);
+    if (rec == nullptr) {
+        return UNKNOWN_ERROR;
+    }
+    int result = mount(rec->blk_device, mount_point, rec->fs_type, rec->flags, rec->fs_options);
+    return result == 0 ? OK : -errno;
+}
+
+FstabMgr defaultFstabMgr() {
+    return FstabMgr{fs_mgr_read_fstab_default(), &fs_mgr_free_fstab};
+}
+
+status_t mountSystem() {
+    FstabMgr fstab = defaultFstabMgr();
+    if (fstab == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
+        return mountAt(fstab, "/", "/system_root");
+    } else {
+        return mountAt(fstab, "/system", "/system");
+    }
+}
+
+status_t mountVendor() {
+    FstabMgr fstab = defaultFstabMgr();
+    if (fstab == NULL) {
+        return UNKNOWN_ERROR;
+    }
+    return mountAt(fstab, "/vendor", "/vendor");
+}
+
+status_t umountSystem() {
+    if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
+        return umount("/system_root");
+    } else {
+        return umount("/system");
+    }
+}
+
+status_t umountVendor() {
+    return umount("/vendor");
+}
+
+} // namespace details
+
+// static
+int32_t VintfObjectRecovery::CheckCompatibility(
+        const std::vector<std::string> &xmls, std::string *error) {
+    return details::checkCompatibility(xmls, true /* mount */,
+            &details::mountSystem, &details::umountSystem,
+            &details::mountVendor, &details::umountVendor,
+            &VintfObject::GetFrameworkHalManifest,
+            &VintfObject::GetDeviceHalManifest,
+            &VintfObject::GetRuntimeInfo,
+            error);
+}
+
+
+} // namespace vintf
+} // namespace android
diff --git a/include/vintf/VintfObject.h b/include/vintf/VintfObject.h
index 4f38735..157d00b 100644
--- a/include/vintf/VintfObject.h
+++ b/include/vintf/VintfObject.h
@@ -68,11 +68,9 @@
     /**
      * Check compatibility, given a set of manifests / matrices in packageInfo.
      * They will be checked against the manifests / matrices on the device.
-     * Partitions (/system, /vendor) are mounted if necessary.
      *
      * @param packageInfo a list of XMLs of HalManifest /
      * CompatibilityMatrix objects.
-     * @param mount whether partitions should be mounted to do the check.
      *
      * @return = 0 if success (compatible)
      *         > 0 if incompatible
@@ -80,7 +78,6 @@
      */
     static int32_t CheckCompatibility(
             const std::vector<std::string> &packageInfo,
-            bool mount = false,
             std::string *error = nullptr);
 };
 
@@ -89,6 +86,19 @@
     INCOMPATIBLE = 1,
 };
 
+// exposed for testing and VintfObjectRecovery.
+namespace details {
+int32_t checkCompatibility(const std::vector<std::string> &xmls, bool mount,
+        std::function<status_t(void)> mountSystem,
+        std::function<status_t(void)> umountSystem,
+        std::function<status_t(void)> mountVendor,
+        std::function<status_t(void)> umountVendor,
+        std::function<const HalManifest *(bool)> GetFrameworkHalManifest,
+        std::function<const HalManifest *(bool)> GetDeviceHalManifest,
+        std::function<const RuntimeInfo *(bool)> GetRuntimeInfo,
+        std::string *error);
+} // namespace details
+
 } // namespace vintf
 } // namespace android
 
diff --git a/include/vintf/VintfObjectRecovery.h b/include/vintf/VintfObjectRecovery.h
new file mode 100644
index 0000000..3b82bda
--- /dev/null
+++ b/include/vintf/VintfObjectRecovery.h
@@ -0,0 +1,60 @@
+/*
+ * 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 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 ANDROID_VINTF_VINTF_OBJECT_RECOVERY_H_
+#define ANDROID_VINTF_VINTF_OBJECT_RECOVERY_H_
+
+#include "VintfObject.h"
+
+#include "utils/Errors.h"
+
+namespace android {
+namespace vintf {
+
+// Recovery version of VintfObject. It mount partitions if necessary. Partitions
+// are unmounted afterwards.
+
+class VintfObjectRecovery {
+    /**
+     * Check compatibility, given a set of manifests / matrices in packageInfo.
+     * They will be checked against the manifests / matrices on the device.
+     * Partitions (/system, /vendor) are mounted if necessary.
+     *
+     * @param packageInfo a list of XMLs of HalManifest /
+     * CompatibilityMatrix objects.
+     *
+     * @return = 0 if success (compatible)
+     *         > 0 if incompatible
+     *         < 0 if any error (mount partition fails, illformed XML, etc.)
+     */
+    static int32_t CheckCompatibility(
+            const std::vector<std::string> &packageInfo,
+            std::string *error = nullptr);
+
+};
+
+// exposed for testing purposes.
+namespace details {
+status_t mountSystem();
+status_t mountVendor();
+status_t umountSystem();
+status_t umountVendor();
+}
+
+} // namespace vintf
+} // namespace android
+
+#endif // ANDROID_VINTF_VINTF_OBJECT_RECOVERY_H_
diff --git a/test/main.cpp b/test/main.cpp
index da93b94..c7a243f 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -733,16 +733,6 @@
     EXPECT_TRUE(manifest.checkCompatibility(matrix, &error)) << error;
 }
 
-namespace details {
-int32_t checkCompatibility(const std::vector<std::string> &xmls, bool mount,
-        std::function<status_t(void)> mountSystem,
-        std::function<status_t(void)> mountVendor,
-        std::function<const HalManifest *(bool)> GetFrameworkHalManifest,
-        std::function<const HalManifest *(bool)> GetDeviceHalManifest,
-        std::function<const RuntimeInfo *(bool)> GetRuntimeInfo,
-        std::string *error);
-} // namespace details
-
 TEST_F(LibVintfTest, Compat2) {
     std::string deviceManifestXml =
         "<manifest version=\"1.0\" type=\"device\">\n"
@@ -789,49 +779,61 @@
 
     bool systemMounted = false;
     bool vendorMounted = false;
-    auto mountSystem = [&systemMounted] () { systemMounted = true; return OK; };
-    auto mountVendor = [&vendorMounted] () { vendorMounted = true; return OK; };
+    bool systemUmounted = false;
+    bool vendorUmounted = false;
+    auto mountSystem  = [&systemMounted]  () { systemMounted = true;  return OK; };
+    auto mountVendor  = [&vendorMounted]  () { vendorMounted = true;  return OK; };
+    auto umountSystem = [&systemUmounted] () { systemUmounted = true; return OK; };
+    auto umountVendor = [&vendorUmounted] () { vendorUmounted = true; return OK; };
     auto nullManifestFunc = [](bool) -> const HalManifest * { return nullptr; };
     auto runtimeInfoFunc = [&](bool) { return &runtimeInfo; };
     // full OTA
     EXPECT_EQ(COMPATIBLE, details::checkCompatibility(
             {deviceManifestXml, deviceMatrixXml, frameworkManifestXml, frameworkMatrixXml},
-            false /* mount */, mountSystem, mountVendor,
+            false /* mount */, mountSystem, umountSystem, mountVendor, umountVendor,
             nullManifestFunc,
             nullManifestFunc,
             runtimeInfoFunc,
             &error)) << error;
     EXPECT_FALSE(systemMounted);
     EXPECT_FALSE(vendorMounted);
+    EXPECT_FALSE(systemUmounted);
+    EXPECT_FALSE(vendorUmounted);
     EXPECT_EQ(COMPATIBLE, details::checkCompatibility(
             {deviceManifestXml, deviceMatrixXml, frameworkManifestXml, frameworkMatrixXml},
-            true /* mount */, mountSystem, mountVendor,
+            true /* mount */, mountSystem, umountSystem, mountVendor, umountVendor,
             nullManifestFunc,
             nullManifestFunc,
             runtimeInfoFunc,
             &error)) << error;
     EXPECT_FALSE(systemMounted);
     EXPECT_FALSE(vendorMounted);
+    EXPECT_FALSE(systemUmounted);
+    EXPECT_FALSE(vendorUmounted);
 
     // framework only OTA
     EXPECT_GT(0, details::checkCompatibility(
             {frameworkManifestXml, frameworkMatrixXml},
-            false /* mount */, mountSystem, mountVendor,
+            false /* mount */, mountSystem, umountSystem, mountVendor, umountVendor,
             nullManifestFunc,
             nullManifestFunc,
             runtimeInfoFunc,
             &error)) << "should not mount, thus info should be missing";
     EXPECT_FALSE(systemMounted);
     EXPECT_FALSE(vendorMounted);
+    EXPECT_FALSE(systemUmounted);
+    EXPECT_FALSE(vendorUmounted);
     EXPECT_EQ(COMPATIBLE, details::checkCompatibility(
             {frameworkManifestXml, frameworkMatrixXml},
-            true /* mount */, mountSystem, mountVendor,
+            true /* mount */, mountSystem, umountSystem, mountVendor, umountVendor,
             nullManifestFunc,
             [&](auto) { return &devManifest; },
             runtimeInfoFunc,
             &error)) << error;
     EXPECT_FALSE(systemMounted);
     EXPECT_TRUE(vendorMounted);
+    EXPECT_FALSE(systemUmounted);
+    EXPECT_TRUE(vendorUmounted);
 
     CompatibilityMatrix failedFwkMatrix;
     std::string failedFrameworkMatrixXml;
@@ -856,13 +858,15 @@
     EXPECT_FALSE(runtimeInfo.checkCompatibility(failedFwkMatrix, &error)) << error;
     EXPECT_EQ(INCOMPATIBLE, details::checkCompatibility(
             {frameworkManifestXml, failedFrameworkMatrixXml},
-            true /* mount */, mountSystem, mountVendor,
+            true /* mount */, mountSystem, umountSystem, mountVendor, umountVendor,
             nullManifestFunc,
             [&](auto) { return &devManifest; },
             runtimeInfoFunc,
             &error)) << error;
     EXPECT_FALSE(systemMounted);
     EXPECT_TRUE(vendorMounted);
+    EXPECT_FALSE(systemUmounted);
+    EXPECT_TRUE(vendorUmounted);
 
     // Failed framework only OTA example 2: unsupported HAL
     failedFwkMatrix = {};
@@ -888,13 +892,15 @@
     EXPECT_TRUE(runtimeInfo.checkCompatibility(failedFwkMatrix, &error)) << error;
     EXPECT_EQ(INCOMPATIBLE, details::checkCompatibility(
             {frameworkManifestXml, failedFrameworkMatrixXml},
-            true /* mount */, mountSystem, mountVendor,
+            true /* mount */, mountSystem, umountSystem, mountVendor, umountVendor,
             nullManifestFunc,
             [&](auto) { return &devManifest; },
             runtimeInfoFunc,
             &error)) << error;
     EXPECT_FALSE(systemMounted);
     EXPECT_TRUE(vendorMounted);
+    EXPECT_FALSE(systemUmounted);
+    EXPECT_TRUE(vendorUmounted);
 }
 
 } // namespace vintf