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