Merge changes from topic "recovery-aidl" am: 18adf7deec
Original change: https://android-review.googlesource.com/c/platform/system/libvintf/+/1906172
Change-Id: I2f479d18de7600d1d33d13362ad039c2fed67070
diff --git a/Android.bp b/Android.bp
index 67a9edd..7e09272 100644
--- a/Android.bp
+++ b/Android.bp
@@ -110,6 +110,7 @@
host: {
srcs: [
"RuntimeInfo-host.cpp",
+ "VintfObjectRecovery.cpp",
],
},
android: {
@@ -117,6 +118,11 @@
"RuntimeInfo-target.cpp",
],
},
+ recovery: {
+ srcs: [
+ "VintfObjectRecovery.cpp",
+ ],
+ },
}
}
diff --git a/VintfObject.cpp b/VintfObject.cpp
index c9460a0..8d9df31 100644
--- a/VintfObject.cpp
+++ b/VintfObject.cpp
@@ -30,6 +30,7 @@
#include <hidl/metadata.h>
#include "CompatibilityMatrix.h"
+#include "VintfObjectUtils.h"
#include "constants-private.h"
#include "parse_string.h"
#include "parse_xml.h"
@@ -50,29 +51,6 @@
static constexpr bool kIsTarget = false;
#endif
-template <typename T, typename F>
-static std::shared_ptr<const T> Get(const char* id, LockedSharedPtr<T>* ptr,
- const F& fetchAllInformation) {
- std::unique_lock<std::mutex> _lock(ptr->mutex);
- if (!ptr->fetchedOnce) {
- LOG(INFO) << id << ": Reading VINTF information.";
- ptr->object = std::make_unique<T>();
- std::string error;
- status_t status = fetchAllInformation(ptr->object.get(), &error);
- if (status == OK) {
- ptr->fetchedOnce = true;
- LOG(INFO) << id << ": Successfully processed VINTF information";
- } else {
- // Doubled because a malformed error std::string might cause us to
- // lose the status.
- LOG(ERROR) << id << ": status from fetching VINTF information: " << status;
- LOG(ERROR) << id << ": " << status << " VINTF parse error: " << error;
- ptr->object = nullptr; // frees the old object
- }
- }
- return ptr->object;
-}
-
static std::unique_ptr<FileSystem> createDefaultFileSystem() {
std::unique_ptr<FileSystem> fileSystem;
if (kIsTarget) {
@@ -215,8 +193,9 @@
}
// Load and combine all of the manifests in a directory
+// If forceSchemaType, all fragment manifests are coerced into manifest->type().
status_t VintfObject::addDirectoryManifests(const std::string& directory, HalManifest* manifest,
- std::string* error) {
+ bool forceSchemaType, std::string* error) {
std::vector<std::string> fileNames;
status_t err = getFileSystem()->listFiles(directory, &fileNames, error);
// if the directory isn't there, that's okay
@@ -235,6 +214,10 @@
err = fetchOneHalManifest(directory + file, &fragmentManifest, error);
if (err != OK) return err;
+ if (forceSchemaType) {
+ fragmentManifest.setType(manifest->type());
+ }
+
if (!manifest->addAll(&fragmentManifest, error)) {
if (error) {
error->insert(0, "Cannot add manifest fragment " + directory + file + ": ");
@@ -263,7 +246,8 @@
if (vendorStatus == OK) {
*out = std::move(vendorManifest);
- status_t fragmentStatus = addDirectoryManifests(kVendorManifestFragmentDir, out, error);
+ status_t fragmentStatus = addDirectoryManifests(kVendorManifestFragmentDir, out,
+ false /* forceSchemaType*/, error);
if (fragmentStatus != OK) {
return fragmentStatus;
}
@@ -284,13 +268,15 @@
return UNKNOWN_ERROR;
}
}
- return addDirectoryManifests(kOdmManifestFragmentDir, out, error);
+ return addDirectoryManifests(kOdmManifestFragmentDir, out, false /* forceSchemaType */,
+ error);
}
// vendorStatus != OK, "out" is not changed.
if (odmStatus == OK) {
*out = std::move(odmManifest);
- return addDirectoryManifests(kOdmManifestFragmentDir, out, error);
+ return addDirectoryManifests(kOdmManifestFragmentDir, out, false /* forceSchemaType */,
+ error);
}
// Use legacy /vendor/manifest.xml
@@ -397,7 +383,8 @@
status_t VintfObject::fetchUnfilteredFrameworkHalManifest(HalManifest* out, std::string* error) {
auto systemEtcStatus = fetchOneHalManifest(kSystemManifest, out, error);
if (systemEtcStatus == OK) {
- auto dirStatus = addDirectoryManifests(kSystemManifestFragmentDir, out, error);
+ auto dirStatus = addDirectoryManifests(kSystemManifestFragmentDir, out,
+ false /* forceSchemaType */, error);
if (dirStatus != OK) {
return dirStatus;
}
@@ -421,7 +408,8 @@
}
}
- auto fragmentStatus = addDirectoryManifests(frags, out, error);
+ auto fragmentStatus =
+ addDirectoryManifests(frags, out, false /* forceSchemaType */, error);
if (fragmentStatus != OK) {
return fragmentStatus;
}
diff --git a/VintfObjectRecovery.cpp b/VintfObjectRecovery.cpp
new file mode 100644
index 0000000..cc44e1e
--- /dev/null
+++ b/VintfObjectRecovery.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 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 <vintf/VintfObjectRecovery.h>
+
+#include "VintfObjectUtils.h"
+#include "constants-private.h"
+
+using std::placeholders::_1;
+using std::placeholders::_2;
+
+namespace android::vintf {
+
+using details::Get;
+using details::kSystemManifest;
+using details::kSystemManifestFragmentDir;
+
+std::shared_ptr<VintfObjectRecovery> VintfObjectRecovery::GetInstance() {
+ static details::LockedSharedPtr<VintfObjectRecovery> sInstance{};
+ std::unique_lock<std::mutex> lock(sInstance.mutex);
+ if (sInstance.object == nullptr) {
+ std::unique_ptr<VintfObjectRecovery> uptr =
+ VintfObjectRecovery::Builder().build<VintfObjectRecovery>();
+ sInstance.object = std::shared_ptr<VintfObjectRecovery>(uptr.release());
+ }
+ return sInstance.object;
+}
+
+std::shared_ptr<const HalManifest> VintfObjectRecovery::getRecoveryHalManifest() {
+ return Get(__func__, &mRecoveryManifest,
+ std::bind(&VintfObjectRecovery::fetchRecoveryHalManifest, this, _1, _2));
+}
+
+// All manifests are installed under /system/etc/vintf.
+// There may be mixed framework and device manifests under that directory. Treat them all
+// as device manifest fragments.
+// Priority:
+// 1. /system/etc/vintf/manifest.xml
+// + /system/etc/vintf/manifest/*.xml if they exist
+status_t VintfObjectRecovery::fetchRecoveryHalManifest(HalManifest* out, std::string* error) {
+ HalManifest manifest;
+ status_t systemEtcStatus = fetchOneHalManifest(kSystemManifest, &manifest, error);
+ if (systemEtcStatus != OK && systemEtcStatus != NAME_NOT_FOUND) {
+ return systemEtcStatus;
+ }
+ // Merge |manifest| to |out| only if the main manifest is found.
+ if (systemEtcStatus == OK) {
+ *out = std::move(manifest);
+ }
+ out->setType(SchemaType::DEVICE);
+ status_t fragmentStatus =
+ addDirectoryManifests(kSystemManifestFragmentDir, out, true /* forceSchemaType */, error);
+ if (fragmentStatus != OK) {
+ return fragmentStatus;
+ }
+ return OK;
+}
+
+VintfObjectRecovery::Builder::Builder()
+ : VintfObjectBuilder(std::unique_ptr<VintfObject>(new VintfObjectRecovery())) {}
+
+} // namespace android::vintf
diff --git a/VintfObjectUtils.h b/VintfObjectUtils.h
new file mode 100644
index 0000000..ac55200
--- /dev/null
+++ b/VintfObjectUtils.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+// Special utils for VintfObject(s).
+
+#pragma once
+
+// This is okay because it is a header private to libvintf. Do not do this in exported headers!
+#include <android-base/logging.h>
+
+#include <vintf/VintfObject.h>
+
+namespace android {
+namespace vintf {
+namespace details {
+
+template <typename T, typename F>
+std::shared_ptr<const T> Get(const char* id, LockedSharedPtr<T>* ptr,
+ const F& fetchAllInformation) {
+ std::unique_lock<std::mutex> _lock(ptr->mutex);
+ if (!ptr->fetchedOnce) {
+ LOG(INFO) << id << ": Reading VINTF information.";
+ ptr->object = std::make_unique<T>();
+ std::string error;
+ status_t status = fetchAllInformation(ptr->object.get(), &error);
+ if (status == OK) {
+ ptr->fetchedOnce = true;
+ LOG(INFO) << id << ": Successfully processed VINTF information";
+ } else {
+ // Doubled because a malformed error std::string might cause us to
+ // lose the status.
+ LOG(ERROR) << id << ": status from fetching VINTF information: " << status;
+ LOG(ERROR) << id << ": " << status << " VINTF parse error: " << error;
+ ptr->object = nullptr; // frees the old object
+ }
+ }
+ return ptr->object;
+}
+
+} // namespace details
+} // namespace vintf
+} // namespace android
diff --git a/include/vintf/VintfObject.h b/include/vintf/VintfObject.h
index 80e6441..050de80 100644
--- a/include/vintf/VintfObject.h
+++ b/include/vintf/VintfObject.h
@@ -91,6 +91,7 @@
namespace testing {
class VintfObjectTestBase;
+class VintfObjectRecoveryTest;
class VintfObjectRuntimeInfoTest;
class VintfObjectCompatibleTest;
} // namespace testing
@@ -286,6 +287,7 @@
// Expose functions for testing and recovery
friend class testing::VintfObjectTestBase;
+ friend class testing::VintfObjectRecoveryTest;
friend class testing::VintfObjectRuntimeInfoTest;
friend class testing::VintfObjectCompatibleTest;
@@ -341,7 +343,7 @@
static std::shared_ptr<const RuntimeInfo> GetRuntimeInfo(
RuntimeInfo::FetchFlags flags = RuntimeInfo::FetchFlag::ALL);
- private:
+ protected:
status_t getCombinedFrameworkMatrix(const std::shared_ptr<const HalManifest>& deviceManifest,
Level kernelLevel, CompatibilityMatrix* out,
std::string* error = nullptr);
@@ -350,7 +352,7 @@
status_t getOneMatrix(const std::string& path, CompatibilityMatrix* out,
std::string* error = nullptr);
status_t addDirectoryManifests(const std::string& directory, HalManifest* manifests,
- std::string* error = nullptr);
+ bool ignoreSchemaType, std::string* error);
status_t fetchDeviceHalManifest(HalManifest* out, std::string* error = nullptr);
status_t fetchDeviceMatrix(CompatibilityMatrix* out, std::string* error = nullptr);
status_t fetchOdmHalManifest(HalManifest* out, std::string* error = nullptr);
@@ -392,7 +394,7 @@
Builder();
};
- private:
+ protected:
/* Empty VintfObject without any dependencies. Used by Builder and subclasses. */
VintfObject() = default;
};
diff --git a/include/vintf/VintfObjectRecovery.h b/include/vintf/VintfObjectRecovery.h
new file mode 100644
index 0000000..64be749
--- /dev/null
+++ b/include/vintf/VintfObjectRecovery.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
+#error VintfObjectRecovery is only supported in recovery and host.
+#endif
+
+#include <vintf/VintfObject.h>
+
+namespace android::vintf {
+
+/**
+ * A special variant of VintfObject for the recovery ramdisk.
+ *
+ * In recovery ramdisk, there is no Treble split. All VINTF data is stored in /system/etc/vintf.
+ *
+ * All getDevice* / getFramework* functions return nullptr. Instead, getRecovery* should be
+ * used instead.
+ */
+class VintfObjectRecovery : public VintfObject {
+ public:
+ /*
+ * Get global instance. Results are cached.
+ */
+ static std::shared_ptr<VintfObjectRecovery> GetInstance();
+
+ /*
+ * Return the API that access the HAL manifests built from component pieces on the
+ * recovery partition.
+ *
+ * Returned manifest has SchemaType::DEVICE.
+ *
+ * No SKU manifest support.
+ */
+ std::shared_ptr<const HalManifest> getRecoveryHalManifest();
+
+ // Not supported. Call getRecoveryHalManifest instead.
+ std::shared_ptr<const HalManifest> getDeviceHalManifest() override { return nullptr; }
+ std::shared_ptr<const HalManifest> getFrameworkHalManifest() override { return nullptr; }
+
+ // Not supported. No compatibility check in recovery because there is no Treble split.
+ std::shared_ptr<const CompatibilityMatrix> getDeviceCompatibilityMatrix() override {
+ return nullptr;
+ }
+ std::shared_ptr<const CompatibilityMatrix> getFrameworkCompatibilityMatrix() override {
+ return nullptr;
+ }
+
+ /** Builder of VintfObjectRecovery. See VintfObjectBuilder for details. */
+ class Builder : public details::VintfObjectBuilder {
+ public:
+ Builder();
+ };
+
+ private:
+ VintfObjectRecovery() = default;
+ status_t fetchRecoveryHalManifest(HalManifest* out, std::string* error);
+ details::LockedSharedPtr<HalManifest> mRecoveryManifest;
+};
+
+} // namespace android::vintf
diff --git a/test/Android.bp b/test/Android.bp
index dfd49dd..7096f89 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -125,3 +125,23 @@
"VintfFmTest.cpp",
],
}
+
+cc_test_host {
+ name: "vintf_object_recovery_test",
+ defaults: [
+ "libvintf-defaults",
+ "libvintf_static_user_defaults",
+ ],
+ static_libs: [
+ "libgmock",
+ "libvintf",
+ "libutils",
+ ],
+ header_libs: [
+ "libvintf_local_headers",
+ ],
+ srcs: [
+ "RuntimeInfo-fake.cpp",
+ "VintfObjectRecoveryTest.cpp",
+ ],
+}
diff --git a/test/VintfObjectRecoveryTest.cpp b/test/VintfObjectRecoveryTest.cpp
new file mode 100644
index 0000000..381d805
--- /dev/null
+++ b/test/VintfObjectRecoveryTest.cpp
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2021 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 <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <vintf/VintfObjectRecovery.h>
+#include <vintf/parse_string.h>
+
+#include "constants-private.h"
+#include "test_constants.h"
+#include "utils-fake.h"
+
+using android::base::ConsumePrefix;
+using android::base::StringPrintf;
+using testing::_;
+using testing::Combine;
+using testing::Invoke;
+using testing::IsEmpty;
+using testing::Mock;
+using testing::NiceMock;
+using testing::StartsWith;
+using testing::StrEq;
+using testing::TestParamInfo;
+using testing::UnorderedElementsAre;
+using testing::ValuesIn;
+
+namespace android::vintf::testing {
+
+using details::kSystemManifest;
+using details::kSystemManifestFragmentDir;
+using details::MockFileSystemWithError;
+using details::MockPropertyFetcher;
+using details::MockRuntimeInfo;
+using details::MockRuntimeInfoFactory;
+
+template <typename T>
+using StatusOr = std::variant<status_t, T>;
+
+using DirectoryContent = std::map<std::string, StatusOr<std::string>>;
+
+using OptionalType = std::optional<SchemaType>;
+std::vector<OptionalType> OptionalTypes() {
+ return {std::nullopt, SchemaType::DEVICE, SchemaType::FRAMEWORK};
+}
+
+std::string OptionalTypeToString(const OptionalType& optionalType) {
+ if (!optionalType.has_value()) return "broken";
+ return to_string(*optionalType);
+}
+
+constexpr const char* kMainFmt = R"(<manifest %s type="%s">
+ <hal format="aidl">
+ <name>android.hardware.main</name>
+ <fqname>IMain/default</fqname>
+ </hal>
+</manifest>
+)";
+
+constexpr const char* kFragment1Fmt = R"(<manifest %s type="%s">
+ <hal format="aidl">
+ <name>android.hardware.fragment1</name>
+ <fqname>IFragment/default</fqname>
+ </hal>
+</manifest>
+)";
+
+constexpr const char* kFragment2Fmt = R"(<manifest %s type="%s">
+ <hal format="aidl">
+ <name>android.hardware.fragment2</name>
+ <fqname>IFragment/default</fqname>
+ </hal>
+</manifest>
+)";
+
+std::string formatManifest(const char* fmt, const OptionalType& optionalType) {
+ if (!optionalType.has_value()) {
+ return "(broken manifest)";
+ }
+ return StringPrintf(fmt, kMetaVersionStr.c_str(), to_string(*optionalType).c_str());
+}
+
+using VintfObjectRecoveryTestParam = std::tuple<OptionalType, OptionalType, OptionalType>;
+class VintfObjectRecoveryTest : public ::testing::TestWithParam<VintfObjectRecoveryTestParam> {
+ public:
+ virtual void SetUp() {
+ vintfObject = VintfObjectRecovery::Builder()
+ .setFileSystem(std::make_unique<NiceMock<MockFileSystemWithError>>())
+ .setRuntimeInfoFactory(std::make_unique<NiceMock<MockRuntimeInfoFactory>>(
+ std::make_shared<NiceMock<MockRuntimeInfo>>()))
+ .setPropertyFetcher(std::make_unique<NiceMock<MockPropertyFetcher>>())
+ .build<VintfObjectRecovery>();
+ auto [mainType, fragType1, fragType2] = GetParam();
+ main = formatManifest(kMainFmt, mainType);
+ frag1 = formatManifest(kFragment1Fmt, fragType1);
+ frag2 = formatManifest(kFragment2Fmt, fragType2);
+ }
+ virtual void TearDown() { Mock::VerifyAndClear(&fs()); }
+
+ MockFileSystemWithError& fs() {
+ return static_cast<MockFileSystemWithError&>(*vintfObject->getFileSystem());
+ }
+
+ void setUpManifests(const StatusOr<std::string>& mainContent,
+ const StatusOr<DirectoryContent>& frags) {
+ // By default, no files exist in the file system.
+ ON_CALL(fs(), listFiles(_, _, _)).WillByDefault(Return(NAME_NOT_FOUND));
+ ON_CALL(fs(), fetch(_, _, _))
+ .WillByDefault(Invoke([](const auto& path, auto*, auto* error) {
+ if (error != nullptr) {
+ *error = "fetch " + path + ": cannot be found on empty filesystem: " +
+ statusToString(NAME_NOT_FOUND);
+ }
+ return NAME_NOT_FOUND;
+ }));
+ ON_CALL(fs(), fetch(StrEq(kSystemManifest), _, _))
+ .WillByDefault(Invoke([=](const auto& path, auto* content, auto* error) -> status_t {
+ if (std::holds_alternative<status_t>(mainContent)) {
+ if (error != nullptr) {
+ *error = "fetch " + path + ": set to return " +
+ statusToString(std::get<status_t>(mainContent));
+ }
+ return std::get<status_t>(mainContent);
+ }
+ *content = std::get<std::string>(mainContent);
+ return OK;
+ }));
+ ON_CALL(fs(), listFiles(StrEq(kSystemManifestFragmentDir), _, _))
+ .WillByDefault(Invoke([=](const std::string& path, std::vector<std::string>* out,
+ auto* error) -> status_t {
+ if (std::holds_alternative<status_t>(frags)) {
+ if (error != nullptr) {
+ *error = "list " + path + ": set to return " +
+ statusToString(std::get<status_t>(frags));
+ }
+ return std::get<status_t>(frags);
+ }
+ for (const auto& [name, statusOrFile] : std::get<DirectoryContent>(frags)) {
+ out->push_back(name);
+ }
+ return OK;
+ }));
+ ON_CALL(fs(), fetch(StartsWith(kSystemManifestFragmentDir), _, _))
+ .WillByDefault(Invoke([=](const auto& path, auto* content, auto* error) -> status_t {
+ if (std::holds_alternative<status_t>(frags)) {
+ if (error != nullptr) {
+ *error = "fetch " + path + ": for dir, set to return " +
+ statusToString(std::get<status_t>(frags));
+ }
+ return std::get<status_t>(frags);
+ }
+ const auto& directoryContent = std::get<DirectoryContent>(frags);
+ std::string_view subpath = path;
+ bool consumed = ConsumePrefix(&subpath, kSystemManifestFragmentDir);
+ EXPECT_TRUE(consumed)
+ << path << " does not start with " << kSystemManifestFragmentDir;
+ auto it = directoryContent.find(std::string(subpath));
+ if (it == directoryContent.end()) {
+ if (error != nullptr) {
+ *error = "fetch " + path +
+ ": not in DirectoryContent: " + statusToString(NAME_NOT_FOUND);
+ }
+ return NAME_NOT_FOUND;
+ }
+
+ const auto& [name, statusOrFile] = *it;
+ if (std::holds_alternative<status_t>(statusOrFile)) {
+ *error = "fetch " + path + ": for file, set to return " +
+ statusToString(std::get<status_t>(statusOrFile));
+ return std::get<status_t>(statusOrFile);
+ }
+ *content = std::get<std::string>(statusOrFile);
+ return OK;
+ }));
+ }
+
+ static std::string ParamToString(const TestParamInfo<ParamType>& info) {
+ auto [mainType, fragType1, fragType2] = info.param;
+ auto s = "main_" + OptionalTypeToString(mainType);
+ s += "_frag1_" + OptionalTypeToString(fragType1);
+ s += "_frag2_" + OptionalTypeToString(fragType2);
+ return s;
+ }
+
+ std::unique_ptr<VintfObjectRecovery> vintfObject;
+ std::string main;
+ std::string frag1;
+ std::string frag2;
+};
+
+TEST_P(VintfObjectRecoveryTest, Empty) {
+ setUpManifests(NAME_NOT_FOUND, NAME_NOT_FOUND);
+ auto manifest = vintfObject->getRecoveryHalManifest();
+ ASSERT_NE(nullptr, manifest);
+ auto hals = manifest->getHalNames();
+ EXPECT_THAT(hals, IsEmpty());
+}
+
+TEST_P(VintfObjectRecoveryTest, InaccessibleMainManifest) {
+ setUpManifests(UNKNOWN_ERROR, NAME_NOT_FOUND);
+ auto manifest = vintfObject->getRecoveryHalManifest();
+ EXPECT_EQ(nullptr, manifest);
+}
+
+TEST_P(VintfObjectRecoveryTest, MainManifestOnly) {
+ auto [mainType, fragType1, fragType2] = GetParam();
+ setUpManifests(main, NAME_NOT_FOUND);
+ auto manifest = vintfObject->getRecoveryHalManifest();
+ if (!mainType.has_value()) { // main manifest is broken
+ EXPECT_EQ(nullptr, manifest);
+ return;
+ }
+ ASSERT_NE(nullptr, manifest);
+ EXPECT_THAT(manifest->getHalNames(), UnorderedElementsAre("android.hardware.main"));
+}
+
+TEST_P(VintfObjectRecoveryTest, MainManifestAndDirectoryOnly) {
+ auto [mainType, fragType1, fragType2] = GetParam();
+ setUpManifests(main, {});
+ auto manifest = vintfObject->getRecoveryHalManifest();
+ if (!mainType.has_value()) { // main manifest is broken
+ EXPECT_EQ(nullptr, manifest);
+ return;
+ }
+ ASSERT_NE(nullptr, manifest);
+ EXPECT_THAT(manifest->getHalNames(), UnorderedElementsAre("android.hardware.main"));
+}
+
+TEST_P(VintfObjectRecoveryTest, MainManifestAndInaccessibleFragment) {
+ setUpManifests(main, DirectoryContent{{"frag1.xml", UNKNOWN_ERROR}});
+ auto manifest = vintfObject->getRecoveryHalManifest();
+ EXPECT_EQ(nullptr, manifest);
+}
+
+TEST_P(VintfObjectRecoveryTest, MainManifestAndFragments) {
+ auto [mainType, fragType1, fragType2] = GetParam();
+ setUpManifests(main, DirectoryContent{{"frag1.xml", frag1}, {"frag2.xml", frag2}});
+ auto manifest = vintfObject->getRecoveryHalManifest();
+ if (!mainType.has_value() || !fragType1.has_value() || !fragType2.has_value()) {
+ // some manifest(s) are broken
+ EXPECT_EQ(nullptr, manifest);
+ return;
+ }
+ ASSERT_NE(nullptr, manifest);
+ EXPECT_THAT(manifest->getHalNames(),
+ UnorderedElementsAre("android.hardware.main", "android.hardware.fragment1",
+ "android.hardware.fragment2"));
+}
+
+TEST_P(VintfObjectRecoveryTest, InaccessibleDirectory) {
+ setUpManifests(NAME_NOT_FOUND, UNKNOWN_ERROR);
+ auto manifest = vintfObject->getRecoveryHalManifest();
+ EXPECT_EQ(nullptr, manifest);
+}
+
+TEST_P(VintfObjectRecoveryTest, InaccessibleFragment) {
+ setUpManifests(NAME_NOT_FOUND, DirectoryContent{{"frag1.xml", UNKNOWN_ERROR}});
+ auto manifest = vintfObject->getRecoveryHalManifest();
+ EXPECT_EQ(nullptr, manifest);
+}
+
+TEST_P(VintfObjectRecoveryTest, SomeInaccessibleFragment) {
+ setUpManifests(NAME_NOT_FOUND,
+ DirectoryContent{{"frag1.xml", UNKNOWN_ERROR}, {"frag2.xml", frag2}});
+ auto manifest = vintfObject->getRecoveryHalManifest();
+ EXPECT_EQ(nullptr, manifest);
+}
+
+TEST_P(VintfObjectRecoveryTest, DirectoryOnly) {
+ setUpManifests(NAME_NOT_FOUND, {});
+ auto manifest = vintfObject->getRecoveryHalManifest();
+ ASSERT_NE(nullptr, manifest);
+ EXPECT_THAT(manifest->getHalNames(), IsEmpty());
+}
+
+TEST_P(VintfObjectRecoveryTest, FragmentsOnly) {
+ auto [mainType, fragType1, fragType2] = GetParam();
+ setUpManifests(NAME_NOT_FOUND, DirectoryContent{{"frag1.xml", frag1}, {"frag2.xml", frag2}});
+ auto manifest = vintfObject->getRecoveryHalManifest();
+ if (!fragType1.has_value() || !fragType2.has_value()) {
+ // some manifest(s) are broken
+ EXPECT_EQ(nullptr, manifest);
+ return;
+ }
+ ASSERT_NE(nullptr, manifest);
+ EXPECT_THAT(manifest->getHalNames(),
+ UnorderedElementsAre("android.hardware.fragment1", "android.hardware.fragment2"));
+}
+
+INSTANTIATE_TEST_CASE_P(VintfObjectRecoveryTest, VintfObjectRecoveryTest,
+ Combine(ValuesIn(OptionalTypes()), ValuesIn(OptionalTypes()),
+ ValuesIn(OptionalTypes())),
+ VintfObjectRecoveryTest::ParamToString);
+
+} // namespace android::vintf::testing
diff --git a/test/utils-fake.h b/test/utils-fake.h
index 846ea2c..37219fa 100644
--- a/test/utils-fake.h
+++ b/test/utils-fake.h
@@ -45,6 +45,14 @@
FileSystemImpl mImpl;
};
+class MockFileSystemWithError : public FileSystem {
+ public:
+ MOCK_METHOD(status_t, fetch, (const std::string&, std::string*, std::string*),
+ (const override));
+ MOCK_METHOD(status_t, listFiles, (const std::string&, std::vector<std::string>*, std::string*),
+ (const override));
+};
+
class MockRuntimeInfo : public RuntimeInfo {
public:
MockRuntimeInfo();