1 /*
2 * Copyright (C) 2019 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 #define LOG_TAG "derive_sdk"
18
19 #include "derive_sdk.h"
20
21 #include <android-base/file.h>
22 #include <android-base/logging.h>
23 #include <android-base/properties.h>
24 #include <android-modules-utils/sdk_level.h>
25 #include <dirent.h>
26 #include <sys/stat.h>
27
28 #include <algorithm>
29 #include <iostream>
30 #include <vector>
31
32 #include "packages/modules/common/proto/sdk.pb.h"
33
34 namespace android {
35 namespace derivesdk {
36
37 static const std::unordered_map<std::string, SdkModule> kApexNameToModule = {
38 {"com.android.adservices", SdkModule::AD_SERVICES},
39 {"com.android.appsearch", SdkModule::APPSEARCH},
40 {"com.android.art", SdkModule::ART},
41 {"com.android.configinfrastructure", SdkModule::CONFIG_INFRASTRUCTURE},
42 {"com.android.conscrypt", SdkModule::CONSCRYPT},
43 {"com.android.extservices", SdkModule::EXT_SERVICES},
44 {"com.android.healthfitness", SdkModule::HEALTH_FITNESS},
45 {"com.android.ipsec", SdkModule::IPSEC},
46 {"com.android.media", SdkModule::MEDIA},
47 {"com.android.mediaprovider", SdkModule::MEDIA_PROVIDER},
48 {"com.android.ondevicepersonalization", SdkModule::ON_DEVICE_PERSONALIZATION},
49 {"com.android.permission", SdkModule::PERMISSIONS},
50 {"com.android.scheduling", SdkModule::SCHEDULING},
51 {"com.android.sdkext", SdkModule::SDK_EXTENSIONS},
52 {"com.android.os.statsd", SdkModule::STATSD},
53 {"com.android.tethering", SdkModule::TETHERING},
54 };
55
56 static const std::unordered_set<SdkModule> kRModules = {
57 SdkModule::CONSCRYPT, SdkModule::EXT_SERVICES, SdkModule::IPSEC,
58 SdkModule::MEDIA, SdkModule::MEDIA_PROVIDER, SdkModule::PERMISSIONS,
59 SdkModule::SDK_EXTENSIONS, SdkModule::STATSD, SdkModule::TETHERING,
60 };
61
62 static const std::unordered_set<SdkModule> kSModules = {SdkModule::ART, SdkModule::SCHEDULING};
63
64 static const std::unordered_set<SdkModule> kTModules = {
65 SdkModule::AD_SERVICES, SdkModule::APPSEARCH, SdkModule::ON_DEVICE_PERSONALIZATION};
66
67 static const std::unordered_set<SdkModule> kUModules = {SdkModule::CONFIG_INFRASTRUCTURE,
68 SdkModule::HEALTH_FITNESS};
69
70 static const std::string kSystemPropertiesPrefix = "build.version.extensions.";
71
ReadSystemProperties(std::map<std::string,std::string> & properties)72 void ReadSystemProperties(std::map<std::string, std::string>& properties) {
73 const std::string default_ = "<not set>";
74
75 for (const auto& dessert : {"r", "s", "t", "ad_services", "u"}) {
76 properties[kSystemPropertiesPrefix + dessert] =
77 android::base::GetProperty(kSystemPropertiesPrefix + dessert, default_);
78 }
79 properties["ro.build.version.sdk"] = android::base::GetProperty("ro.build.version.sdk", default_);
80 }
81
ReadDatabase(const std::string & db_path,ExtensionDatabase & db)82 bool ReadDatabase(const std::string& db_path, ExtensionDatabase& db) {
83 std::string contents;
84 if (!android::base::ReadFileToString(db_path, &contents, true)) {
85 PLOG(ERROR) << "failed to read " << db_path << ": ";
86 return false;
87 }
88 if (!db.ParseFromString(contents)) {
89 LOG(ERROR) << "failed to parse " << db_path;
90 return false;
91 }
92 return true;
93 }
94
VersionRequirementsMet(const ExtensionVersion & ext_version,const std::unordered_set<SdkModule> & relevant_modules,const std::unordered_map<SdkModule,int> & module_versions)95 bool VersionRequirementsMet(
96 const ExtensionVersion& ext_version,
97 const std::unordered_set<SdkModule>& relevant_modules,
98 const std::unordered_map<SdkModule, int>& module_versions) {
99 for (const auto& requirement : ext_version.requirements()) {
100 // Only requirements on modules relevant for this extension matter.
101 if (relevant_modules.find(requirement.module()) == relevant_modules.end())
102 continue;
103
104 auto version = module_versions.find(requirement.module());
105 if (version == module_versions.end()) {
106 LOG(DEBUG) << "Not version " << ext_version.version() << ": Module "
107 << requirement.module() << " is missing";
108 return false;
109 }
110 if (version->second < requirement.version().version()) {
111 LOG(DEBUG) << "Not version " << ext_version.version() << ": Module "
112 << requirement.module() << " version (" << version->second
113 << ") too low. Needed " << requirement.version().version();
114 return false;
115 }
116 }
117 return true;
118 }
119
GetSdkLevel(const ExtensionDatabase & db,const std::unordered_set<SdkModule> & relevant_modules,const std::unordered_map<SdkModule,int> & module_versions)120 int GetSdkLevel(const ExtensionDatabase& db,
121 const std::unordered_set<SdkModule>& relevant_modules,
122 const std::unordered_map<SdkModule, int>& module_versions) {
123 int max = 0;
124
125 for (const auto& ext_version : db.versions()) {
126 if (ext_version.version() > max &&
127 VersionRequirementsMet(ext_version, relevant_modules,
128 module_versions)) {
129 max = ext_version.version();
130 }
131 }
132 return max;
133 }
134
SetExtension(const std::string & extension_name,int version)135 bool SetExtension(const std::string& extension_name, int version) {
136 LOG(INFO) << "extension " << extension_name << " version is " << version;
137
138 const std::string property_name = kSystemPropertiesPrefix + extension_name;
139 if (!android::base::SetProperty(property_name, std::to_string(version))) {
140 LOG(ERROR) << "failed to set sdk_info prop " << property_name;
141 return false;
142 }
143 return true;
144 }
145
GetAndSetExtension(const std::string & extension_name,const ExtensionDatabase & db,const std::unordered_set<SdkModule> & relevant_modules,const std::unordered_map<SdkModule,int> & module_versions)146 bool GetAndSetExtension(const std::string& extension_name, const ExtensionDatabase& db,
147 const std::unordered_set<SdkModule>& relevant_modules,
148 const std::unordered_map<SdkModule, int>& module_versions) {
149 int version = GetSdkLevel(db, relevant_modules, module_versions);
150 return SetExtension(extension_name, version);
151 }
152
ReadSdkInfoFromApexes(const std::string & mountpath,std::unordered_map<SdkModule,int> & versions)153 bool ReadSdkInfoFromApexes(const std::string& mountpath,
154 std::unordered_map<SdkModule, int>& versions) {
155 std::unique_ptr<DIR, decltype(&closedir)> apex(opendir(mountpath.c_str()),
156 closedir);
157 if (!apex) {
158 LOG(ERROR) << "Could not read " + mountpath;
159 return false;
160 }
161 struct dirent* de;
162 while ((de = readdir(apex.get()))) {
163 std::string name = de->d_name;
164 if (name[0] == '.' || name.find('@') != std::string::npos) {
165 // Skip <name>@<ver> dirs, as they are bind-mounted to <name>
166 continue;
167 }
168 std::string path = mountpath + "/" + name + "/etc/sdkinfo.pb";
169 struct stat statbuf;
170 if (stat(path.c_str(), &statbuf) != 0) {
171 continue;
172 }
173 auto module_itr = kApexNameToModule.find(name);
174 if (module_itr == kApexNameToModule.end()) {
175 LOG(WARNING) << "Found sdkinfo in unexpected apex " << name;
176 continue;
177 }
178 std::string contents;
179 if (!android::base::ReadFileToString(path, &contents, true)) {
180 LOG(ERROR) << "failed to read " << path;
181 continue;
182 }
183 SdkVersion sdk_version;
184 if (!sdk_version.ParseFromString(contents)) {
185 LOG(ERROR) << "failed to parse " << path;
186 continue;
187 }
188 SdkModule module = module_itr->second;
189 LOG(INFO) << "Read version " << sdk_version.version() << " from " << module;
190 versions[module] = sdk_version.version();
191 }
192 return true;
193 }
194
SetSdkLevels(const std::string & mountpath)195 bool SetSdkLevels(const std::string& mountpath) {
196 ExtensionDatabase db;
197 if (!ReadDatabase(mountpath + "/com.android.sdkext/etc/extensions_db.pb", db)) {
198 LOG(ERROR) << "Failed to read database";
199 return false;
200 }
201
202 std::unordered_map<SdkModule, int> versions;
203 if (!ReadSdkInfoFromApexes(mountpath, versions)) {
204 LOG(ERROR) << "Failed to SDK info from apexes";
205 return false;
206 }
207
208 std::unordered_set<SdkModule> relevant_modules;
209 relevant_modules.insert(kRModules.begin(), kRModules.end());
210 if (!GetAndSetExtension("r", db, relevant_modules, versions)) {
211 return false;
212 }
213
214 relevant_modules.insert(kSModules.begin(), kSModules.end());
215 if (android::modules::sdklevel::IsAtLeastS()) {
216 if (!GetAndSetExtension("s", db, relevant_modules, versions)) {
217 return false;
218 }
219 }
220
221 relevant_modules.insert(kTModules.begin(), kTModules.end());
222 if (android::modules::sdklevel::IsAtLeastT()) {
223 if (!GetAndSetExtension("t", db, relevant_modules, versions)) {
224 return false;
225 }
226 }
227
228 relevant_modules.insert(kUModules.begin(), kUModules.end());
229 if (android::modules::sdklevel::IsAtLeastU()) {
230 if (!GetAndSetExtension("u", db, relevant_modules, versions)) {
231 return false;
232 }
233 }
234
235 // Consistency check: verify all modules with requirements is included in some dessert
236 for (const auto& ext_version : db.versions()) {
237 for (const auto& requirement : ext_version.requirements()) {
238 if (relevant_modules.find(requirement.module()) == relevant_modules.end()) {
239 LOG(ERROR) << "version " << ext_version.version() << " requires unmapped module"
240 << requirement.module();
241 return false;
242 }
243 }
244 }
245
246 if (android::modules::sdklevel::IsAtLeastT()) {
247 if (versions[AD_SERVICES] >= 7) {
248 if (!SetExtension("ad_services", versions[AD_SERVICES])) {
249 return false;
250 }
251 } else {
252 relevant_modules.clear();
253 relevant_modules.insert(SdkModule::AD_SERVICES);
254 if (!GetAndSetExtension("ad_services", db, relevant_modules, versions)) {
255 return false;
256 }
257 }
258 }
259 return true;
260 }
261
PrintHeader()262 bool PrintHeader() {
263 std::map<std::string, std::string> properties;
264 ReadSystemProperties(properties);
265
266 bool print_separator = false;
267 std::cout << "[";
268 for (const auto& property : properties) {
269 if (property.first.find(kSystemPropertiesPrefix) == 0) {
270 if (print_separator) {
271 std::cout << ", ";
272 }
273 const auto name = property.first.substr(kSystemPropertiesPrefix.size());
274 std::cout << name << "=" << property.second;
275 print_separator = true;
276 }
277 }
278 std::cout << "]\n";
279 return true;
280 }
281
PrintDump(const std::string & mountpath)282 bool PrintDump(const std::string& mountpath) {
283 std::map<std::string, std::string> properties;
284 ReadSystemProperties(properties);
285
286 std::unordered_map<SdkModule, int> versions;
287 if (!ReadSdkInfoFromApexes(mountpath, versions)) {
288 LOG(ERROR) << "Failed to read SDK info from apexes";
289 return false;
290 }
291
292 std::cout << "system properties:\n";
293 for (const auto& property : properties) {
294 std::cout << " " << property.first << ":" << property.second << "\n";
295 }
296
297 std::cout << "apex module versions:\n";
298 for (const auto& version : versions) {
299 std::cout << " " << SdkModule_Name(version.first) << ":" << version.second << "\n";
300 }
301
302 return true;
303 }
304
305 } // namespace derivesdk
306 } // namespace android
307