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 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 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 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 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