blob: 96d19232eb4cc9e080ee8b7766d3ea11d3be6b94 [file] [log] [blame]
// 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