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