• 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.conscrypt", SdkModule::CONSCRYPT},
42     {"com.android.ipsec", SdkModule::IPSEC},
43     {"com.android.media", SdkModule::MEDIA},
44     {"com.android.mediaprovider", SdkModule::MEDIA_PROVIDER},
45     {"com.android.ondevicepersonalization", SdkModule::ON_DEVICE_PERSONALIZATION},
46     {"com.android.permission", SdkModule::PERMISSIONS},
47     {"com.android.scheduling", SdkModule::SCHEDULING},
48     {"com.android.sdkext", SdkModule::SDK_EXTENSIONS},
49     {"com.android.os.statsd", SdkModule::STATSD},
50     {"com.android.tethering", SdkModule::TETHERING},
51 };
52 
53 static const std::unordered_set<SdkModule> kRModules = {
54     SdkModule::CONSCRYPT,   SdkModule::IPSEC,          SdkModule::MEDIA,  SdkModule::MEDIA_PROVIDER,
55     SdkModule::PERMISSIONS, SdkModule::SDK_EXTENSIONS, SdkModule::STATSD, SdkModule::TETHERING,
56 };
57 
58 static const std::unordered_set<SdkModule> kSModules = {SdkModule::ART, SdkModule::SCHEDULING};
59 
60 static const std::unordered_set<SdkModule> kTModules = {
61     SdkModule::AD_SERVICES, SdkModule::APPSEARCH, SdkModule::ON_DEVICE_PERSONALIZATION};
62 
ReadDatabase(const std::string & db_path,ExtensionDatabase & db)63 bool ReadDatabase(const std::string& db_path, ExtensionDatabase& db) {
64   std::string contents;
65   if (!android::base::ReadFileToString(db_path, &contents, true)) {
66     PLOG(ERROR) << "failed to read " << db_path << ": ";
67     return false;
68   }
69   if (!db.ParseFromString(contents)) {
70     LOG(ERROR) << "failed to parse " << db_path;
71     return false;
72   }
73   return true;
74 }
75 
VersionRequirementsMet(const ExtensionVersion & ext_version,const std::unordered_set<SdkModule> & relevant_modules,const std::unordered_map<SdkModule,int> & module_versions)76 bool VersionRequirementsMet(
77     const ExtensionVersion& ext_version,
78     const std::unordered_set<SdkModule>& relevant_modules,
79     const std::unordered_map<SdkModule, int>& module_versions) {
80   for (const auto& requirement : ext_version.requirements()) {
81     // Only requirements on modules relevant for this extension matter.
82     if (relevant_modules.find(requirement.module()) == relevant_modules.end())
83       continue;
84 
85     auto version = module_versions.find(requirement.module());
86     if (version == module_versions.end()) {
87       LOG(DEBUG) << "Not version " << ext_version.version() << ": Module "
88                  << requirement.module() << " is missing";
89       return false;
90     }
91     if (version->second < requirement.version().version()) {
92       LOG(DEBUG) << "Not version " << ext_version.version() << ": Module "
93                  << requirement.module() << " version (" << version->second
94                  << ") too low. Needed " << requirement.version().version();
95       return false;
96     }
97   }
98   return true;
99 }
100 
GetSdkLevel(const ExtensionDatabase & db,const std::unordered_set<SdkModule> & relevant_modules,const std::unordered_map<SdkModule,int> & module_versions)101 int GetSdkLevel(const ExtensionDatabase& db,
102                 const std::unordered_set<SdkModule>& relevant_modules,
103                 const std::unordered_map<SdkModule, int>& module_versions) {
104   int max = 0;
105 
106   for (const auto& ext_version : db.versions()) {
107     if (ext_version.version() > max &&
108         VersionRequirementsMet(ext_version, relevant_modules,
109                                module_versions)) {
110       max = ext_version.version();
111     }
112   }
113   return max;
114 }
115 
SetSdkLevels(const std::string & mountpath)116 bool SetSdkLevels(const std::string& mountpath) {
117   ExtensionDatabase db;
118   if (!ReadDatabase(mountpath + "/com.android.sdkext/etc/extensions_db.pb", db)) {
119     LOG(ERROR) << "Failed to read database";
120     return false;
121   }
122   std::unique_ptr<DIR, decltype(&closedir)> apex(opendir(mountpath.c_str()),
123                                                  closedir);
124   if (!apex) {
125     LOG(ERROR) << "Could not read " + mountpath;
126     return false;
127   }
128   struct dirent* de;
129   std::unordered_map<SdkModule, int> versions;
130   while ((de = readdir(apex.get()))) {
131     std::string name = de->d_name;
132     if (name[0] == '.' || name.find('@') != std::string::npos) {
133       // Skip <name>@<ver> dirs, as they are bind-mounted to <name>
134       continue;
135     }
136     std::string path = mountpath + "/" + name + "/etc/sdkinfo.pb";
137     struct stat statbuf;
138     if (stat(path.c_str(), &statbuf) != 0) {
139       continue;
140     }
141     auto module_itr = kApexNameToModule.find(name);
142     if (module_itr == kApexNameToModule.end()) {
143       LOG(WARNING) << "Found sdkinfo in unexpected apex " << name;
144       continue;
145     }
146     std::string contents;
147     if (!android::base::ReadFileToString(path, &contents, true)) {
148       LOG(ERROR) << "failed to read " << path;
149       continue;
150     }
151     SdkVersion sdk_version;
152     if (!sdk_version.ParseFromString(contents)) {
153       LOG(ERROR) << "failed to parse " << path;
154       continue;
155     }
156     SdkModule module = module_itr->second;
157     LOG(INFO) << "Read version " << sdk_version.version() << " from " << module;
158     versions[module] = sdk_version.version();
159   }
160 
161   std::unordered_set<SdkModule> relevant_modules;
162   relevant_modules.insert(kRModules.begin(), kRModules.end());
163 
164   int version_R = GetSdkLevel(db, relevant_modules, versions);
165   LOG(INFO) << "R extension version is " << version_R;
166 
167   if (!android::base::SetProperty("build.version.extensions.r",
168                                   std::to_string(version_R))) {
169     LOG(ERROR) << "failed to set r sdk_info prop";
170     return false;
171   }
172   relevant_modules.insert(kSModules.begin(), kSModules.end());
173   if (android::modules::sdklevel::IsAtLeastS()) {
174     int version_S = GetSdkLevel(db, relevant_modules, versions);
175     LOG(INFO) << "S extension version is " << version_S;
176     if (!android::base::SetProperty("build.version.extensions.s",
177                                     std::to_string(version_S))) {
178       LOG(ERROR) << "failed to set s sdk_info prop";
179       return false;
180     }
181   }
182 
183   relevant_modules.insert(kTModules.begin(), kTModules.end());
184   if (android::modules::sdklevel::IsAtLeastT()) {
185     int version_T = GetSdkLevel(db, relevant_modules, versions);
186     LOG(INFO) << "T extension version is " << version_T;
187     if (!android::base::SetProperty("build.version.extensions.t", std::to_string(version_T))) {
188       LOG(ERROR) << "failed to set t sdk_info prop";
189       return false;
190     }
191   }
192 
193   // Consistency check: verify all modules with requirements is included in some dessert
194   for (const auto& ext_version : db.versions()) {
195     for (const auto& requirement : ext_version.requirements()) {
196       if (relevant_modules.find(requirement.module()) == relevant_modules.end()) {
197         LOG(ERROR) << "version " << ext_version.version() << " requires unmapped module"
198                    << requirement.module();
199         return false;
200       }
201     }
202   }
203 
204   return true;
205 }
206 
207 }  // namespace derivesdk
208 }  // namespace android
209