/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gsi_validation_utils.h" #include "kernel_version_matrix.pb.h" namespace { using android::base::testing::Ok; using KernelVersionMatrix = std::map; 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& kernel_release) : kernel_version_(kernel_version), kernel_release_(kernel_release) {} [[nodiscard]] android::vintf::Version kernel_version() const { return kernel_version_.dropMinor(); } [[nodiscard]] std::optional 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 kernel_release_; }; // Read the raw compatibility matrix from test data. std::optional 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& raw_in, google::protobuf::RepeatedPtrField* 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 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* 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 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(kernel_version, kernel_release); } // Check kernel version and KMI against a list of requirements. bool KernelVersionIsSupported( const ActualKmi& actual, const google::protobuf::RepeatedPtrField& 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(); } // Returns true if the device has the specified feature. bool DeviceSupportsFeature(const char* feature) { bool device_supports_feature = false; FILE* p = popen("pm list features", "re"); if (p) { char* line = NULL; size_t len = 0; while (getline(&line, &len, p) > 0) { if (strstr(line, feature)) { device_supports_feature = true; break; } } pclose(p); } return device_supports_feature; } 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 << "."; auto runtime_info = android::vintf::VintfObject::GetInstance()->getRuntimeInfo( android::vintf::RuntimeInfo::FetchFlag::CPU_VERSION); ASSERT_NE(runtime_info, nullptr); const static bool is_tv_device = DeviceSupportsFeature("android.software.leanback"); if (is_tv_device && (product_first_api_level <= 33 || (runtime_info->hardwareId() != "aarch64" && runtime_info->hardwareId() != "armv8l"))) { GTEST_SKIP() << "Exempt from GKI test on TV devices launched before Android U or using 32bit kernel"; } 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"; } auto vendor_api_level = GetVendorApiLevel(); ASSERT_TRUE(vendor_api_level != 0) << "Unable to determine board API level on GRF devices"; if (vendor_api_level <= __ANDROID_API_R__) { GTEST_SKIP() << "[VSR-3.4.1-001] does not enforce latest kernel x.y for " << "vendor_api_level == " << vendor_api_level << " <= R"; } auto corresponding_vintf_level = android::vintf::testing::GetFcmVersionFromApiLevel(vendor_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 " << vendor_api_level << " (corresponding to VINTF level " << *corresponding_vintf_level << ") must use kernel version (" << *latest_min_lts << ")+, but kernel version is " << kernel_version; } } // namespace