| // Copyright 2023 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <optional> |
| #include <set> |
| #include <string> |
| |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/hash/hash.h> |
| #include <base/logging.h> |
| #include <base/values.h> |
| #include <base/system/sys_info.h> |
| #include <brillo/data_encoding.h> |
| |
| #include "libsegmentation/device_info.pb.h" |
| #include "libsegmentation/feature_management.h" |
| #include "libsegmentation/feature_management_impl.h" |
| #include "libsegmentation/feature_management_interface.h" |
| #include "libsegmentation/feature_management_util.h" |
| |
| #include "proto/feature_management.pb.h" |
| |
| // Autogenerated by feature-management-data, |
| // Contains protobuf definitions. |
| #include <libsegmentation/libsegmentation_pb.h> |
| |
| using chromiumos::feature_management::api::software::Feature; |
| |
| namespace segmentation { |
| |
| // Sysfs file corresponding to VPD state. This will be used to persist device |
| // info state and read cache device info state. |
| constexpr const char kVpdSysfsFilePath[] = |
| "/sys/firmware/vpd/rw/feature_device_info"; |
| constexpr const char kTempDeviceInfoPath[] = "/tmp/feature_device_info"; |
| |
| #if USE_FEATURE_MANAGEMENT |
| // Sysfs file corresponding to VPD state. This will be used to persist device |
| // info state and read cache device info state. |
| const char* kDeviceInfoFilePath = kVpdSysfsFilePath; |
| #else |
| constexpr char kDeviceInfoFilePath[] = ""; |
| |
| FeatureManagementInterface::FeatureLevel |
| FeatureManagementImpl::GetFeatureLevel() { |
| return FeatureLevel::FEATURE_LEVEL_0; |
| } |
| |
| FeatureManagementInterface::ScopeLevel FeatureManagementImpl::GetScopeLevel() { |
| return ScopeLevel::SCOPE_LEVEL_0; |
| } |
| |
| #endif |
| |
| FeatureManagementImpl::FeatureManagementImpl() |
| : FeatureManagementImpl( |
| base::FilePath(kDeviceInfoFilePath), protobuf_features, "") {} |
| |
| FeatureManagementImpl::FeatureManagementImpl( |
| const base::FilePath& device_info_file_path, |
| const std::string& feature_db, |
| const std::string& os_version) |
| : device_info_file_path_(device_info_file_path), |
| temp_device_info_file_path_(kTempDeviceInfoPath) { |
| persist_via_vpd_ = |
| device_info_file_path_ == base::FilePath(kVpdSysfsFilePath); |
| std::string decoded_pb; |
| brillo::data_encoding::Base64Decode(feature_db, &decoded_pb); |
| bundle_.ParseFromString(decoded_pb); |
| |
| #if USE_FEATURE_MANAGEMENT |
| std::string version(os_version); |
| if (version.empty()) { |
| base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_VERSION", &version); |
| } |
| |
| if (!version.empty()) { |
| current_version_hash_ = base::PersistentHash(version); |
| } else { |
| current_version_hash_ = 0; |
| LOG(ERROR) << "Failed to retrieve CHROMEOS_RELEASE_VERSION"; |
| } |
| #endif |
| } |
| |
| bool FeatureManagementImpl::IsFeatureEnabled(const std::string& name) { |
| int prefix_len = strlen(FeatureManagement::kPrefix); |
| if (name.compare(0, prefix_len, FeatureManagement::kPrefix)) |
| return false; |
| |
| enum FeatureLevel feature_level = GetFeatureLevel(); |
| if (feature_level == FEATURE_LEVEL_UNKNOWN) |
| feature_level = FEATURE_LEVEL_0; |
| |
| enum ScopeLevel scope_level = GetScopeLevel(); |
| if (scope_level == SCOPE_LEVEL_UNKNOWN) |
| scope_level = SCOPE_LEVEL_0; |
| |
| // Now we have the feature_level and the scope_level, after we |
| // found that the feature exist, check if its level is equal or |
| // greater than the DUT feature_level and if its scope array contains |
| // the DUT scope_level. |
| std::string in_feature = name.substr(prefix_len); |
| for (const auto& feature : bundle_.features()) { |
| if (!feature.name().compare(in_feature)) { |
| auto it = std::find(feature.scopes().begin(), feature.scopes().end(), |
| scope_level - SCOPE_LEVEL_VALID_OFFSET); |
| |
| return feature_level - FEATURE_LEVEL_VALID_OFFSET >= |
| feature.feature_level() && |
| it != feature.scopes().end(); |
| } |
| } |
| return false; |
| } |
| |
| const std::set<std::string> FeatureManagementImpl::ListFeatures( |
| const FeatureUsage usage) { |
| enum FeatureLevel feature_level = GetFeatureLevel(); |
| if (feature_level == FEATURE_LEVEL_UNKNOWN) |
| feature_level = FEATURE_LEVEL_0; |
| |
| enum ScopeLevel scope_level = GetScopeLevel(); |
| if (scope_level == SCOPE_LEVEL_UNKNOWN) |
| scope_level = SCOPE_LEVEL_0; |
| |
| // Now we have the feature_level and the scope_level, return all features |
| // that: |
| // 1. matches the usage and |
| // 2. its feature level is equal or greater than the DUT feature_level and |
| // 3. its scope array contains the DUT scope_level. |
| std::set<std::string> features; |
| for (const auto& feature : bundle_.features()) { |
| auto it = std::find(feature.scopes().begin(), feature.scopes().end(), |
| scope_level - SCOPE_LEVEL_VALID_OFFSET); |
| |
| if (std::find(feature.usages().begin(), feature.usages().end(), usage) != |
| feature.usages().end() && |
| feature_level - FEATURE_LEVEL_VALID_OFFSET >= feature.feature_level() && |
| it != feature.scopes().end()) |
| features.emplace(FeatureManagement::kPrefix + feature.name()); |
| } |
| return features; |
| } |
| } // namespace segmentation |