/*
* 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.
*/
#define LOG_TAG "VtsTestabilityChecker"
#include "VtsTestabilityChecker.h"
#include <algorithm>
#include <iostream>
#include <set>
#include <android-base/strings.h>
#include <vintf/parse_string.h>
using android::base::Join;
using android::vintf::Arch;
using android::vintf::CompatibilityMatrix;
using android::vintf::gArchStrings;
using android::vintf::HalManifest;
using android::vintf::ManifestHal;
using android::vintf::ManifestInstance;
using android::vintf::MatrixHal;
using android::vintf::MatrixInstance;
using android::vintf::toFQNameString;
using android::vintf::Transport;
using android::vintf::Version;
using android::vintf::operator<<;
using std::set;
using std::string;
using std::vector;
namespace android {
namespace vts {
bool VtsTestabilityChecker::CheckHalForComplianceTest(
const string& hal_package_name, const Version& hal_version,
const string& hal_interface_name, const Arch& arch,
set<string>* instances) {
CHECK(instances) << "instances set should not be NULL.";
set<string> famework_hal_instances;
set<string> vendor_hal_instances;
bool check_framework_hal = CheckFrameworkManifestHal(
hal_package_name, hal_version, hal_interface_name, arch,
&famework_hal_instances);
bool check_vendor_hal =
CheckVendorManifestHal(hal_package_name, hal_version, hal_interface_name,
arch, &vendor_hal_instances);
set_union(famework_hal_instances.begin(), famework_hal_instances.end(),
vendor_hal_instances.begin(), vendor_hal_instances.end(),
std::inserter(*instances, instances->begin()));
return check_framework_hal || check_vendor_hal;
}
bool VtsTestabilityChecker::CheckHalForNonComplianceTest(
const string& hal_package_name, const Version& hal_version,
const string& hal_interface_name, const Arch& arch,
set<string>* instances) {
CHECK(instances) << "instances set should not be NULL.";
set<string> vendor_hal_instances;
set<string> test_hal_instances;
bool check_vendor_hal =
CheckVendorManifestHal(hal_package_name, hal_version, hal_interface_name,
arch, &vendor_hal_instances);
bool check_test_hal = CheckTestHalWithHwManager(
hal_package_name, hal_version, hal_interface_name, &test_hal_instances);
set_union(vendor_hal_instances.begin(), vendor_hal_instances.end(),
test_hal_instances.begin(), test_hal_instances.end(),
std::inserter(*instances, instances->begin()));
return check_vendor_hal || check_test_hal;
}
vector<const ManifestInstance*> VtsTestabilityChecker::FindInstance(
const vector<ManifestInstance>& manifest_instances,
const MatrixInstance& matrix_instance) {
vector<const ManifestInstance*> ret;
for (const auto& e : manifest_instances) {
if (matrix_instance.matchInstance(e.instance())) {
ret.push_back(&e);
}
}
return ret;
}
vector<const ManifestInstance*> VtsTestabilityChecker::FindInterface(
const vector<ManifestInstance>& manifest_instances,
const MatrixInstance& matrix_instance) {
vector<const ManifestInstance*> ret;
for (const auto& e : manifest_instances) {
if (e.interface() == matrix_instance.interface()) {
ret.push_back(&e);
}
}
return ret;
}
bool VtsTestabilityChecker::CheckFrameworkCompatibleHal(
const string& hal_package_name, const Version& hal_version,
const string& hal_interface_name, const Arch& arch,
set<string>* instances) {
CHECK(instances) << "instances set should not be NULL.";
auto matrix_instances = framework_comp_matrix_->getFqInstances(
hal_package_name, hal_version, hal_interface_name);
auto manifest_instances = device_hal_manifest_->getFqInstances(
hal_package_name, hal_version, hal_interface_name);
bool testable = false;
for (const auto& matrix_instance : matrix_instances) {
const auto& matched_instances =
FindInstance(manifest_instances, matrix_instance);
if (!matrix_instance.optional() && matched_instances.empty()) {
// In matrix but not in manifest.
// The test should still run, but expect the test
// to fail (due to incompatible vendor and framework HAL).
LOG(ERROR) << "Compatibility error. Hal " << hal_package_name
<< " is required by framework but not supported by vendor";
if (!hal_interface_name.empty()) {
if (!matrix_instance.isRegex()) {
instances->insert(matrix_instance.exactInstance());
} else {
LOG(ERROR) << "Ignore regex-instance '"
<< matrix_instance.regexPattern();
}
}
testable |= true;
continue;
}
if (hal_interface_name.empty()) {
testable |= !matched_instances.empty();
continue;
}
auto get_testable_instances =
[&](const vector<const vintf::ManifestInstance*>& manifest_instances) {
vector<string> ret;
for (const auto& manifest_instance : manifest_instances) {
if ((manifest_instance->transport() == Transport::PASSTHROUGH &&
CheckPassthroughManifestArch(manifest_instance->arch(),
arch)) ||
manifest_instance->transport() == Transport::HWBINDER) {
ret.push_back(manifest_instance->instance());
}
}
return ret;
};
auto testable_instances = get_testable_instances(matched_instances);
if (!testable_instances.empty()) {
instances->insert(testable_instances.begin(), testable_instances.end());
testable |= true;
continue;
}
// Special case: if a.h.foo@1.0::IFoo/default is in matrix but /custom
// is in manifest, the interface is still testable, but /default should
// not be added to instances.
const auto& matched_interface_instances =
FindInterface(manifest_instances, matrix_instance);
auto testable_interfaces =
get_testable_instances(matched_interface_instances);
if (!testable_interfaces.empty()) {
testable |= true;
continue;
}
}
if (instances->empty()) {
LOG(ERROR) << "Hal "
<< toFQNameString(hal_package_name, hal_version,
hal_interface_name)
<< " has no testable instance";
}
return testable;
}
bool VtsTestabilityChecker::CheckPassthroughManifestArch(
const Arch& manifest_arch, const Arch& arch) {
switch (arch) {
case Arch::ARCH_32: {
if (android::vintf::has32(manifest_arch)) {
return true;
}
break;
}
case Arch::ARCH_64: {
if (android::vintf::has64(manifest_arch)) {
return true;
}
break;
}
default: {
LOG(ERROR) << "Unexpected arch to check: " << arch;
break;
}
}
return false;
}
bool VtsTestabilityChecker::CheckFrameworkManifestHal(
const string& hal_package_name, const Version& hal_version,
const string& hal_interface_name, const Arch& arch,
set<string>* instances) {
return CheckManifestHal(framework_hal_manifest_, hal_package_name,
hal_version, hal_interface_name, arch, instances);
}
bool VtsTestabilityChecker::CheckVendorManifestHal(
const string& hal_package_name, const Version& hal_version,
const string& hal_interface_name, const Arch& arch,
set<string>* instances) {
return CheckManifestHal(device_hal_manifest_, hal_package_name, hal_version,
hal_interface_name, arch, instances);
}
bool VtsTestabilityChecker::CheckManifestHal(const HalManifest* hal_manifest,
const string& hal_package_name,
const Version& hal_version,
const string& hal_interface_name,
const Arch& arch,
set<string>* instances) {
CHECK(instances) << "instances set should not be NULL.";
const auto& manifest_instances = hal_manifest->getFqInstances(
hal_package_name, hal_version, hal_interface_name);
const auto& fq_instance_name =
toFQNameString(hal_package_name, hal_version, hal_interface_name);
if (manifest_instances.empty()) {
LOG(DEBUG) << "Does not find instances for " << fq_instance_name
<< " in manifest file";
return false;
}
bool testable = false;
for (const auto& manifest_instance : manifest_instances) {
if (manifest_instance.transport() == Transport::PASSTHROUGH &&
!CheckPassthroughManifestArch(manifest_instance.arch(), arch)) {
LOG(DEBUG) << "Manifest HAL " << fq_instance_name
<< " is passthrough and does not support arch " << arch;
continue; // skip this instance
}
if (!hal_interface_name.empty()) {
instances->insert(manifest_instance.instance());
}
testable = true;
}
return testable;
}
bool VtsTestabilityChecker::CheckTestHalWithHwManager(
const string& hal_package_name, const Version& hal_version,
const string& hal_interface_name, set<string>* instances) {
CHECK(instances) << "instances set should not be NULL.";
string fqName =
toFQNameString(hal_package_name, hal_version, hal_interface_name);
bool registered = false;
hardware::Return<void> res;
if (!hal_interface_name.empty()) {
res = sm_->listByInterface(fqName, [&](const auto& registered_instances) {
for (const string& instance : registered_instances) {
registered = true;
instances->insert(instance);
}
});
} else { // handle legacy data without interface info.
res = sm_->list([&](const auto& services) {
for (const string& service : services) {
if (service.find(fqName) == 0) {
registered = true;
break;
}
}
});
}
if (!res.isOk()) {
LOG(ERROR) << "failed to check services: " << res.description();
return false;
}
return registered;
}
} // namespace vts
} // namespace android