• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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