1 /*
2 * Copyright 2025 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <feature_override/FeatureOverrideParser.h>
18
19 #include <chrono>
20 #include <fstream>
21 #include <sstream>
22 #include <string>
23 #include <sys/stat.h>
24 #include <vector>
25
26 #include <android-base/macros.h>
27 #include <graphicsenv/FeatureOverrides.h>
28 #include <log/log.h>
29 #include <vkjson.h>
30
31 #include "feature_config.pb.h"
32
33 namespace {
34
resetFeatureOverrides(android::FeatureOverrides & featureOverrides)35 void resetFeatureOverrides(android::FeatureOverrides &featureOverrides) {
36 featureOverrides.mGlobalFeatures.clear();
37 featureOverrides.mPackageFeatures.clear();
38 }
39
40 bool
gpuVendorIdMatches(const VkJsonInstance & vkJsonInstance,const uint32_t & configVendorId)41 gpuVendorIdMatches(const VkJsonInstance &vkJsonInstance,
42 const uint32_t &configVendorId) {
43 if (vkJsonInstance.devices.empty()) {
44 return false;
45 }
46
47 // Always match the TEST Vendor ID
48 if (configVendorId == feature_override::GpuVendorID::VENDOR_ID_TEST) {
49 return true;
50 }
51
52 // Always assume one GPU device.
53 uint32_t vendorID = vkJsonInstance.devices.front().properties.vendorID;
54
55 return vendorID == configVendorId;
56 }
57
58 bool
conditionsMet(const VkJsonInstance & vkJsonInstance,const android::FeatureConfig & featureConfig)59 conditionsMet(const VkJsonInstance &vkJsonInstance,
60 const android::FeatureConfig &featureConfig) {
61 bool gpuVendorIdMatch = false;
62
63 if (featureConfig.mGpuVendorIDs.empty()) {
64 gpuVendorIdMatch = true;
65 } else {
66 for (const auto &gpuVendorID: featureConfig.mGpuVendorIDs) {
67 if (gpuVendorIdMatches(vkJsonInstance, gpuVendorID)) {
68 gpuVendorIdMatch = true;
69 break;
70 }
71 }
72 }
73
74 return gpuVendorIdMatch;
75 }
76
initFeatureConfig(android::FeatureConfig & featureConfig,const feature_override::FeatureConfig & featureConfigProto)77 void initFeatureConfig(android::FeatureConfig &featureConfig,
78 const feature_override::FeatureConfig &featureConfigProto) {
79 featureConfig.mFeatureName = featureConfigProto.feature_name();
80 featureConfig.mEnabled = featureConfigProto.enabled();
81 for (const auto &gpuVendorIdProto: featureConfigProto.gpu_vendor_ids()) {
82 featureConfig.mGpuVendorIDs.emplace_back(static_cast<uint32_t>(gpuVendorIdProto));
83 }
84 }
85
readFeatureConfigProtos(const std::string & configFilePath)86 feature_override::FeatureOverrideProtos readFeatureConfigProtos(const std::string &configFilePath) {
87 feature_override::FeatureOverrideProtos overridesProtos;
88
89 std::ifstream protobufBinaryFile(configFilePath.c_str());
90 if (protobufBinaryFile.fail()) {
91 ALOGE("Failed to open feature config file: `%s`.", configFilePath.c_str());
92 return overridesProtos;
93 }
94
95 std::stringstream buffer;
96 buffer << protobufBinaryFile.rdbuf();
97 std::string serializedConfig = buffer.str();
98 std::vector<uint8_t> serialized(
99 reinterpret_cast<const uint8_t *>(serializedConfig.data()),
100 reinterpret_cast<const uint8_t *>(serializedConfig.data()) +
101 serializedConfig.size());
102
103 if (!overridesProtos.ParseFromArray(serialized.data(),
104 static_cast<int>(serialized.size()))) {
105 ALOGE("Failed to parse GpuConfig protobuf data.");
106 }
107
108 return overridesProtos;
109 }
110
111 } // namespace
112
113 namespace android {
114
getFeatureOverrideFilePath() const115 std::string FeatureOverrideParser::getFeatureOverrideFilePath() const {
116 const std::string kConfigFilePath = "/system/etc/angle/feature_config_vk.binarypb";
117
118 return kConfigFilePath;
119 }
120
shouldReloadFeatureOverrides() const121 bool FeatureOverrideParser::shouldReloadFeatureOverrides() const {
122 std::string configFilePath = getFeatureOverrideFilePath();
123
124 std::ifstream configFile(configFilePath);
125 if (!configFile.good()) {
126 return false;
127 }
128
129 struct stat fileStat{};
130 if (stat(configFilePath.c_str(), &fileStat) != 0) {
131 ALOGE("Error getting file information for '%s': %s", configFilePath.c_str(),
132 strerror(errno));
133 // stat'ing the file failed, so return false since reading it will also likely fail.
134 return false;
135 }
136
137 return fileStat.st_mtime > mLastProtobufReadTime;
138 }
139
forceFileRead()140 void FeatureOverrideParser::forceFileRead() {
141 mLastProtobufReadTime = 0;
142 }
143
parseFeatureOverrides()144 void FeatureOverrideParser::parseFeatureOverrides() {
145 const feature_override::FeatureOverrideProtos overridesProtos = readFeatureConfigProtos(
146 getFeatureOverrideFilePath());
147
148 // Clear out the stale values before adding the newly parsed data.
149 resetFeatureOverrides(mFeatureOverrides);
150
151 if (overridesProtos.global_features().empty() &&
152 overridesProtos.package_features().empty()) {
153 // No overrides to parse.
154 return;
155 }
156
157 const VkJsonInstance vkJsonInstance = VkJsonGetInstance();
158
159 // Global feature overrides.
160 for (const auto &featureConfigProto: overridesProtos.global_features()) {
161 FeatureConfig featureConfig;
162 initFeatureConfig(featureConfig, featureConfigProto);
163
164 if (conditionsMet(vkJsonInstance, featureConfig)) {
165 mFeatureOverrides.mGlobalFeatures.emplace_back(featureConfig);
166 }
167 }
168
169 // App-specific feature overrides.
170 for (auto const &pkgConfigProto: overridesProtos.package_features()) {
171 const std::string &packageName = pkgConfigProto.package_name();
172
173 if (mFeatureOverrides.mPackageFeatures.count(packageName)) {
174 ALOGE("Package already has feature overrides! Skipping.");
175 continue;
176 }
177
178 std::vector<FeatureConfig> featureConfigs;
179 for (const auto &featureConfigProto: pkgConfigProto.feature_configs()) {
180 FeatureConfig featureConfig;
181 initFeatureConfig(featureConfig, featureConfigProto);
182
183 if (conditionsMet(vkJsonInstance, featureConfig)) {
184 featureConfigs.emplace_back(featureConfig);
185 }
186 }
187
188 mFeatureOverrides.mPackageFeatures[packageName] = featureConfigs;
189 }
190
191 mLastProtobufReadTime = std::chrono::system_clock::to_time_t(
192 std::chrono::system_clock::now());
193 }
194
getFeatureOverrides()195 FeatureOverrides FeatureOverrideParser::getFeatureOverrides() {
196 if (shouldReloadFeatureOverrides()) {
197 parseFeatureOverrides();
198 }
199
200 return mFeatureOverrides;
201 }
202
203 } // namespace android
204