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