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