371 lines
13 KiB
C++
371 lines
13 KiB
C++
/*
|
|
* Copyright (C) 2023 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 <algorithm>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <ostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
#include <android-base/file.h>
|
|
#include <android-base/parseint.h>
|
|
#include <android-base/properties.h>
|
|
#include <android-base/result-gmock.h>
|
|
#include <android-base/strings.h>
|
|
#include <android/api-level.h>
|
|
#include <gmock/gmock.h>
|
|
#include <google/protobuf/repeated_field.h>
|
|
#include <google/protobuf/text_format.h>
|
|
#include <gtest/gtest.h>
|
|
#include <kver/kernel_release.h>
|
|
#include <libvts_vintf_test_common/common.h>
|
|
#include <vintf/Version.h>
|
|
#include <vintf/VintfObject.h>
|
|
#include <vintf/parse_string.h>
|
|
|
|
#include "gsi_validation_utils.h"
|
|
#include "kernel_version_matrix.pb.h"
|
|
|
|
namespace {
|
|
|
|
using android::base::testing::Ok;
|
|
using KernelVersionMatrix = std::map<uint64_t, AndroidReleaseRequirement>;
|
|
|
|
std::ostream& operator<<(std::ostream& os, const Kmi& kmi) {
|
|
os << "android";
|
|
if (kmi.android_release() != 0) {
|
|
os << kmi.android_release();
|
|
}
|
|
return os << "-" << kmi.kernel_version().major_version() << "."
|
|
<< kmi.kernel_version().minor_version();
|
|
}
|
|
|
|
// Represents the information about the running kernel.
|
|
struct ActualKmi {
|
|
public:
|
|
ActualKmi(const android::vintf::KernelVersion& kernel_version,
|
|
const std::optional<android::kver::KernelRelease>& kernel_release)
|
|
: kernel_version_(kernel_version), kernel_release_(kernel_release) {}
|
|
|
|
[[nodiscard]] android::vintf::Version kernel_version() const {
|
|
return kernel_version_.dropMinor();
|
|
}
|
|
|
|
[[nodiscard]] std::optional<uint64_t> android_release() const {
|
|
if (kernel_release_.has_value()) {
|
|
return kernel_release_->android_release();
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
[[nodiscard]] std::string string() const {
|
|
std::string ret = android::vintf::to_string(kernel_version_);
|
|
if (kernel_release_.has_value()) {
|
|
ret += " (" + kernel_release_->string() + ")";
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
friend std::ostream& operator<<(std::ostream& os, const ActualKmi& kmi) {
|
|
os << kmi.kernel_version_;
|
|
if (kmi.kernel_release_.has_value()) {
|
|
os << " (" + kmi.kernel_release_->string() << ")";
|
|
}
|
|
return os;
|
|
}
|
|
|
|
private:
|
|
android::vintf::KernelVersion kernel_version_;
|
|
std::optional<android::kver::KernelRelease> kernel_release_;
|
|
};
|
|
|
|
// Read the raw compatibility matrix from test data.
|
|
std::optional<RawKernelVersionMatrix> ReadRawKernelVersionMatrix() {
|
|
auto exec_dir = android::base::GetExecutableDirectory();
|
|
auto matrix_path = exec_dir + "/kernel_version_matrix.textproto";
|
|
std::string matrix_content;
|
|
if (!android::base::ReadFileToString(matrix_path, &matrix_content)) {
|
|
ADD_FAILURE() << "Can't read " << matrix_path;
|
|
return std::nullopt;
|
|
}
|
|
|
|
RawKernelVersionMatrix ret;
|
|
if (!google::protobuf::TextFormat::ParseFromString(matrix_content, &ret)) {
|
|
ADD_FAILURE() << matrix_path << " is not valid";
|
|
return std::nullopt;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Parse a raw KMI string (e.g. "android14-5.15") to structured Kmi object.
|
|
bool FromRaw(const std::string& s, Kmi* mutable_out) {
|
|
auto tokens = android::base::Split(s, "-");
|
|
if (tokens.size() != 2) {
|
|
ADD_FAILURE() << "Unrecognized requirement: " << s;
|
|
return false;
|
|
}
|
|
|
|
std::string_view android_release_sv = tokens[0];
|
|
if (!android::base::ConsumePrefix(&android_release_sv, "android")) {
|
|
ADD_FAILURE() << "Unrecognized requirement: " << s;
|
|
return false;
|
|
}
|
|
|
|
uint64_t android_release;
|
|
if (android_release_sv.empty()) {
|
|
mutable_out->clear_android_release();
|
|
} else {
|
|
if (!android::base::ParseUint(std::string(android_release_sv),
|
|
&android_release)) {
|
|
ADD_FAILURE() << "Unrecognized requirement: " << s;
|
|
return false;
|
|
}
|
|
mutable_out->set_android_release(android_release);
|
|
}
|
|
|
|
android::vintf::Version vintf_kernel_version;
|
|
if (!android::vintf::parse(tokens[1], &vintf_kernel_version)) {
|
|
ADD_FAILURE() << "Unrecognized requirement: " << s;
|
|
return false;
|
|
}
|
|
mutable_out->mutable_kernel_version()->set_major_version(
|
|
vintf_kernel_version.majorVer);
|
|
mutable_out->mutable_kernel_version()->set_minor_version(
|
|
vintf_kernel_version.minorVer);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Parse an array of raw KMI strings to an array of structured Kmi object.
|
|
bool FromRaw(const google::protobuf::RepeatedPtrField<std::string>& raw_in,
|
|
google::protobuf::RepeatedPtrField<Kmi>* mutable_out) {
|
|
mutable_out->Reserve(raw_in.size());
|
|
for (const auto& raw_s : raw_in) {
|
|
if (!FromRaw(raw_s, mutable_out->Add())) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Turn the raw compatibility matrix into structured data.
|
|
std::optional<KernelVersionMatrix> FromRaw(
|
|
const RawKernelVersionMatrix& raw_kernel_version_matrix) {
|
|
KernelVersionMatrix ret;
|
|
for (const auto& [api_level, raw_android_release_requirements] :
|
|
raw_kernel_version_matrix.release_requirements()) {
|
|
AndroidReleaseRequirement ret_value;
|
|
if (!FromRaw(raw_android_release_requirements.upgrade(),
|
|
ret_value.mutable_upgrade())) {
|
|
return std::nullopt;
|
|
}
|
|
if (!FromRaw(raw_android_release_requirements.launch(),
|
|
ret_value.mutable_launch())) {
|
|
return std::nullopt;
|
|
}
|
|
if (!FromRaw(raw_android_release_requirements.launch_grf(),
|
|
ret_value.mutable_launch_grf())) {
|
|
return std::nullopt;
|
|
}
|
|
ret.emplace(api_level, std::move(ret_value));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Return requirements on kernel version and KMI for the given platform SDK
|
|
// level. Return nullptr on failure.
|
|
const google::protobuf::RepeatedPtrField<Kmi>* GetKernelVersionRequirements(
|
|
const KernelVersionMatrix& kernel_version_matrix,
|
|
uint32_t android_platform_release, bool is_launch, bool is_grf) {
|
|
auto release_requirements_it =
|
|
kernel_version_matrix.find(android_platform_release);
|
|
if (release_requirements_it == kernel_version_matrix.end()) {
|
|
ADD_FAILURE() << "Unable to find requirement for SDK level "
|
|
<< android_platform_release << ". Is the test updated?";
|
|
return nullptr;
|
|
}
|
|
|
|
if (is_launch) {
|
|
if (is_grf) {
|
|
return &release_requirements_it->second.launch_grf();
|
|
}
|
|
return &release_requirements_it->second.launch();
|
|
}
|
|
return &release_requirements_it->second.upgrade();
|
|
}
|
|
|
|
// Read the information about the running kernel that this test needs.
|
|
std::optional<ActualKmi> GetActualKmi() {
|
|
auto vintf_object = android::vintf::VintfObject::GetInstance();
|
|
if (vintf_object == nullptr) {
|
|
ADD_FAILURE() << "Cannot get VintfObject instance";
|
|
return std::nullopt;
|
|
}
|
|
auto runtime_info = vintf_object->getRuntimeInfo(
|
|
android::vintf::RuntimeInfo::FetchFlag::CPU_VERSION);
|
|
if (runtime_info == nullptr) {
|
|
ADD_FAILURE() << "Cannot get kernel release";
|
|
return std::nullopt;
|
|
}
|
|
|
|
auto kernel_release = android::kver::KernelRelease::Parse(
|
|
runtime_info->osRelease(), true /* allow suffix */);
|
|
auto kernel_version = runtime_info->kernelVersion();
|
|
|
|
return std::make_optional<ActualKmi>(kernel_version, kernel_release);
|
|
}
|
|
|
|
// Check kernel version and KMI against a list of requirements.
|
|
bool KernelVersionIsSupported(
|
|
const ActualKmi& actual,
|
|
const google::protobuf::RepeatedPtrField<Kmi>& requirements,
|
|
std::string* error) {
|
|
for (const auto& req : requirements) {
|
|
if (req.kernel_version().major_version() !=
|
|
actual.kernel_version().majorVer ||
|
|
req.kernel_version().minor_version() !=
|
|
actual.kernel_version().minorVer) {
|
|
GTEST_LOG_(INFO) << "Failed to match " << actual << " against required "
|
|
<< req << ": kernel version does not match.";
|
|
continue; // check next item
|
|
}
|
|
|
|
if (req.android_release() != actual.android_release().value_or(0)) {
|
|
GTEST_LOG_(INFO) << "Failed to match " << actual << " against required "
|
|
<< req
|
|
<< ": The Android release part of KMI does not match.";
|
|
continue; // check next item
|
|
}
|
|
|
|
GTEST_LOG_(INFO) << "Matched " << actual << " against requirement " << req;
|
|
return true;
|
|
}
|
|
|
|
std::stringstream error_stream;
|
|
error_stream << "Kernel " << actual << " is not valid. It must be one of [\n";
|
|
for (const auto& req : requirements) {
|
|
error_stream << " " << req << ",\n";
|
|
}
|
|
error_stream << "].";
|
|
*error = error_stream.str();
|
|
return false;
|
|
}
|
|
|
|
// If ro.build.version.codename == REL, expect the given condition is true.
|
|
// Otherwise, only log a warning message, but don't fail the test.
|
|
struct ExpectTrueOnRelease : public std::stringstream {
|
|
explicit ExpectTrueOnRelease(bool cond) : cond_(cond) {}
|
|
~ExpectTrueOnRelease() override {
|
|
if (cond_) {
|
|
return;
|
|
}
|
|
if (!IsReleasedAndroidVersion()) {
|
|
GTEST_LOG_(ERROR) << str() << " This will be an error upon release.";
|
|
} else {
|
|
ADD_FAILURE() << str();
|
|
}
|
|
}
|
|
|
|
private:
|
|
bool cond_;
|
|
};
|
|
|
|
bool IsGrf() {
|
|
return !android::base::GetProperty("ro.board.first_api_level", "").empty() ||
|
|
!android::base::GetProperty("ro.board.api_level", "").empty();
|
|
}
|
|
|
|
TEST(KernelVersionTest, AgainstPlatformRelease) {
|
|
auto raw_kernel_version_matrix = ReadRawKernelVersionMatrix();
|
|
ASSERT_TRUE(raw_kernel_version_matrix.has_value());
|
|
ASSERT_GT(raw_kernel_version_matrix->release_requirements_size(), 0);
|
|
|
|
auto kernel_version_matrix = FromRaw(*raw_kernel_version_matrix);
|
|
ASSERT_TRUE(kernel_version_matrix.has_value());
|
|
ASSERT_FALSE(kernel_version_matrix->empty());
|
|
|
|
auto android_platform_release = GetSdkLevel();
|
|
|
|
auto min_enforcing_android_release = kernel_version_matrix->begin()->first;
|
|
if (android_platform_release < min_enforcing_android_release) {
|
|
GTEST_SKIP() << "Kernel version is not enforced for platform SDK level "
|
|
<< android_platform_release << " ( < "
|
|
<< min_enforcing_android_release << " )";
|
|
}
|
|
|
|
auto product_first_api_level = GetProductFirstApiLevel();
|
|
ExpectTrueOnRelease(product_first_api_level <= android_platform_release)
|
|
<< "Product first API level " << product_first_api_level
|
|
<< " should not exceed the platform release " << android_platform_release
|
|
<< ".";
|
|
|
|
bool is_launch = product_first_api_level >= android_platform_release;
|
|
|
|
auto requirements = GetKernelVersionRequirements(
|
|
*kernel_version_matrix, android_platform_release, is_launch, IsGrf());
|
|
ASSERT_NE(requirements, nullptr);
|
|
|
|
auto actual = GetActualKmi();
|
|
ASSERT_TRUE(actual.has_value());
|
|
|
|
std::string error;
|
|
ExpectTrueOnRelease(KernelVersionIsSupported(*actual, *requirements, &error))
|
|
<< error;
|
|
}
|
|
|
|
TEST(KernelVersionTest, GrfDevicesMustUseLatestKernel) {
|
|
if (!IsGrf()) {
|
|
GTEST_SKIP() << "Non-GRF device kernel requirements are checked in "
|
|
"SystemVendorTest.KernelCompatibility";
|
|
}
|
|
|
|
// Use board API level, not vendor API level, to ignore
|
|
// ro.product.first_api_level. ro.vendor.api_level is considering
|
|
// the ro.product.api_level which could potentially yield a lower api_level
|
|
// than the ro.board.{first_,}api_level.
|
|
auto board_api_level = GetBoardApiLevel();
|
|
ASSERT_TRUE(board_api_level.has_value())
|
|
<< "Unable to determine board API level on GRF devices";
|
|
|
|
if (*board_api_level <= __ANDROID_API_R__) {
|
|
GTEST_SKIP() << "[VSR-3.4.1-001] does not enforce latest kernel x.y for "
|
|
<< "board_api_level == " << *board_api_level << " <= R";
|
|
}
|
|
|
|
auto corresponding_vintf_level =
|
|
android::vintf::testing::GetFcmVersionFromApiLevel(*board_api_level);
|
|
ASSERT_THAT(corresponding_vintf_level, Ok());
|
|
|
|
auto latest_min_lts =
|
|
android::vintf::VintfObject::GetInstance()->getLatestMinLtsAtFcmVersion(
|
|
*corresponding_vintf_level);
|
|
ASSERT_THAT(latest_min_lts, Ok());
|
|
auto runtime_info =
|
|
android::vintf::VintfObject::GetInstance()->getRuntimeInfo(
|
|
android::vintf::RuntimeInfo::FetchFlag::CPU_VERSION);
|
|
ASSERT_NE(runtime_info, nullptr);
|
|
auto kernel_version = runtime_info->kernelVersion();
|
|
|
|
ASSERT_GE(kernel_version, *latest_min_lts)
|
|
<< "[VSR-3.4.1-001] CHIPSETs that are on GRF and are frozen on API level "
|
|
<< *board_api_level << " (corresponding to VINTF level "
|
|
<< *corresponding_vintf_level << ") must use kernel version ("
|
|
<< *latest_min_lts << ")+, but kernel version is " << kernel_version;
|
|
}
|
|
|
|
} // namespace
|