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 "apexd" 18 19 #include "apex_file_repository.h" 20 21 #include <unordered_map> 22 23 #include <android-base/file.h> 24 #include <android-base/properties.h> 25 #include <android-base/result.h> 26 #include <android-base/stringprintf.h> 27 #include <android-base/strings.h> 28 29 #include "apex_constants.h" 30 #include "apex_file.h" 31 #include "apexd_utils.h" 32 33 using android::base::EndsWith; 34 using android::base::Error; 35 using android::base::GetProperty; 36 using android::base::Result; 37 38 namespace android { 39 namespace apex { 40 41 Result<void> ApexFileRepository::ScanBuiltInDir(const std::string& dir) { 42 LOG(INFO) << "Scanning " << dir << " for pre-installed ApexFiles"; 43 if (access(dir.c_str(), F_OK) != 0 && errno == ENOENT) { 44 LOG(WARNING) << dir << " does not exist. Skipping"; 45 return {}; 46 } 47 48 Result<std::vector<std::string>> all_apex_files = FindFilesBySuffix( 49 dir, {kApexPackageSuffix, kCompressedApexPackageSuffix}); 50 if (!all_apex_files.ok()) { 51 return all_apex_files.error(); 52 } 53 54 // TODO(b/179248390): scan parallelly if possible 55 for (const auto& file : *all_apex_files) { 56 LOG(INFO) << "Found pre-installed APEX " << file; 57 Result<ApexFile> apex_file = ApexFile::Open(file); 58 if (!apex_file.ok()) { 59 return Error() << "Failed to open " << file << " : " << apex_file.error(); 60 } 61 62 const std::string& name = apex_file->GetManifest().name(); 63 auto it = pre_installed_store_.find(name); 64 if (it == pre_installed_store_.end()) { 65 pre_installed_store_.emplace(name, std::move(*apex_file)); 66 } else if (it->second.GetPath() != apex_file->GetPath()) { 67 auto level = base::FATAL; 68 // On some development (non-REL) builds the VNDK apex could be in /vendor. 69 // When testing CTS-on-GSI on these builds, there would be two VNDK apexes 70 // in the system, one in /system and one in /vendor. 71 static constexpr char kVndkApexModuleNamePrefix[] = "com.android.vndk."; 72 static constexpr char kPlatformVersionCodenameProperty[] = 73 "ro.build.version.codename"; 74 if (android::base::StartsWith(name, kVndkApexModuleNamePrefix) && 75 GetProperty(kPlatformVersionCodenameProperty, "REL") != "REL") { 76 level = android::base::INFO; 77 } 78 LOG(level) << "Found two apex packages " << it->second.GetPath() 79 << " and " << apex_file->GetPath() 80 << " with the same module name " << name; 81 } else if (it->second.GetBundledPublicKey() != 82 apex_file->GetBundledPublicKey()) { 83 LOG(FATAL) << "Public key of apex package " << it->second.GetPath() 84 << " (" << name << ") has unexpectedly changed"; 85 } 86 } 87 return {}; 88 } 89 90 ApexFileRepository& ApexFileRepository::GetInstance() { 91 static ApexFileRepository instance; 92 return instance; 93 } 94 95 android::base::Result<void> ApexFileRepository::AddPreInstalledApex( 96 const std::vector<std::string>& prebuilt_dirs) { 97 for (const auto& dir : prebuilt_dirs) { 98 if (auto result = ScanBuiltInDir(dir); !result.ok()) { 99 return result.error(); 100 } 101 } 102 return {}; 103 } 104 105 // TODO(b/179497746): AddDataApex should not concern with filtering out invalid 106 // apex. 107 Result<void> ApexFileRepository::AddDataApex(const std::string& data_dir) { 108 LOG(INFO) << "Scanning " << data_dir << " for data ApexFiles"; 109 if (access(data_dir.c_str(), F_OK) != 0 && errno == ENOENT) { 110 LOG(WARNING) << data_dir << " does not exist. Skipping"; 111 return {}; 112 } 113 114 Result<std::vector<std::string>> active_apex = 115 FindFilesBySuffix(data_dir, {kApexPackageSuffix}); 116 if (!active_apex.ok()) { 117 return active_apex.error(); 118 } 119 120 // TODO(b/179248390): scan parallelly if possible 121 for (const auto& file : *active_apex) { 122 LOG(INFO) << "Found updated apex " << file; 123 Result<ApexFile> apex_file = ApexFile::Open(file); 124 if (!apex_file.ok()) { 125 LOG(ERROR) << "Failed to open " << file << " : " << apex_file.error(); 126 continue; 127 } 128 129 const std::string& name = apex_file->GetManifest().name(); 130 if (!HasPreInstalledVersion(name)) { 131 LOG(ERROR) << "Skipping " << file << " : no preisntalled apex"; 132 // Ignore data apex without corresponding pre-installed apex 133 continue; 134 } 135 auto pre_installed_public_key = GetPublicKey(name); 136 if (!pre_installed_public_key.ok() || 137 apex_file->GetBundledPublicKey() != *pre_installed_public_key) { 138 // Ignore data apex if public key doesn't match with pre-installed apex 139 LOG(ERROR) << "Skipping " << file 140 << " : public key doesn't match pre-installed one"; 141 continue; 142 } 143 144 if (EndsWith(apex_file->GetPath(), kDecompressedApexPackageSuffix)) { 145 LOG(WARNING) << "Skipping " << file 146 << " : Non-decompressed APEX should not have " 147 << kDecompressedApexPackageSuffix << " suffix"; 148 continue; 149 } 150 151 auto it = data_store_.find(name); 152 if (it == data_store_.end()) { 153 data_store_.emplace(name, std::move(*apex_file)); 154 continue; 155 } 156 157 const auto& existing_version = it->second.GetManifest().version(); 158 const auto new_version = apex_file->GetManifest().version(); 159 // If multiple data apexs are preset, select the one with highest version 160 bool prioritize_higher_version = new_version > existing_version; 161 // For same version, non-decompressed apex gets priority 162 if (prioritize_higher_version) { 163 it->second = std::move(*apex_file); 164 } 165 } 166 return {}; 167 } 168 169 // TODO(b/179497746): remove this method when we add api for fetching ApexFile 170 // by name 171 Result<const std::string> ApexFileRepository::GetPublicKey( 172 const std::string& name) const { 173 auto it = pre_installed_store_.find(name); 174 if (it == pre_installed_store_.end()) { 175 return Error() << "No preinstalled apex found for package " << name; 176 } 177 return it->second.GetBundledPublicKey(); 178 } 179 180 // TODO(b/179497746): remove this method when we add api for fetching ApexFile 181 // by name 182 Result<const std::string> ApexFileRepository::GetPreinstalledPath( 183 const std::string& name) const { 184 auto it = pre_installed_store_.find(name); 185 if (it == pre_installed_store_.end()) { 186 return Error() << "No preinstalled data found for package " << name; 187 } 188 return it->second.GetPath(); 189 } 190 191 // TODO(b/179497746): remove this method when we add api for fetching ApexFile 192 // by name 193 Result<const std::string> ApexFileRepository::GetDataPath( 194 const std::string& name) const { 195 auto it = data_store_.find(name); 196 if (it == data_store_.end()) { 197 return Error() << "No data apex found for package " << name; 198 } 199 return it->second.GetPath(); 200 } 201 202 bool ApexFileRepository::HasPreInstalledVersion(const std::string& name) const { 203 return pre_installed_store_.find(name) != pre_installed_store_.end(); 204 } 205 206 bool ApexFileRepository::HasDataVersion(const std::string& name) const { 207 return data_store_.find(name) != data_store_.end(); 208 } 209 210 // ApexFile is considered a decompressed APEX if it is located in decompression 211 // dir 212 bool ApexFileRepository::IsDecompressedApex(const ApexFile& apex) const { 213 return apex.GetPath().starts_with(decompression_dir_); 214 } 215 216 bool ApexFileRepository::IsPreInstalledApex(const ApexFile& apex) const { 217 auto it = pre_installed_store_.find(apex.GetManifest().name()); 218 if (it == pre_installed_store_.end()) { 219 return false; 220 } 221 return it->second.GetPath() == apex.GetPath() || IsDecompressedApex(apex); 222 } 223 224 std::vector<ApexFileRef> ApexFileRepository::GetPreInstalledApexFiles() const { 225 std::vector<ApexFileRef> result; 226 for (const auto& it : pre_installed_store_) { 227 result.emplace_back(std::cref(it.second)); 228 } 229 return std::move(result); 230 } 231 232 std::vector<ApexFileRef> ApexFileRepository::GetDataApexFiles() const { 233 std::vector<ApexFileRef> result; 234 for (const auto& it : data_store_) { 235 result.emplace_back(std::cref(it.second)); 236 } 237 return std::move(result); 238 } 239 240 // Group pre-installed APEX and data APEX by name 241 std::unordered_map<std::string, std::vector<ApexFileRef>> 242 ApexFileRepository::AllApexFilesByName() const { 243 // Collect all apex files 244 std::vector<ApexFileRef> all_apex_files; 245 auto pre_installed_apexs = GetPreInstalledApexFiles(); 246 auto data_apexs = GetDataApexFiles(); 247 std::move(pre_installed_apexs.begin(), pre_installed_apexs.end(), 248 std::back_inserter(all_apex_files)); 249 std::move(data_apexs.begin(), data_apexs.end(), 250 std::back_inserter(all_apex_files)); 251 252 // Group them by name 253 std::unordered_map<std::string, std::vector<ApexFileRef>> result; 254 for (const auto& apex_file_ref : all_apex_files) { 255 const ApexFile& apex_file = apex_file_ref.get(); 256 const std::string& package_name = apex_file.GetManifest().name(); 257 if (result.find(package_name) == result.end()) { 258 result[package_name] = std::vector<ApexFileRef>{}; 259 } 260 result[package_name].emplace_back(apex_file_ref); 261 } 262 263 return std::move(result); 264 } 265 266 ApexFileRef ApexFileRepository::GetDataApex(const std::string& name) const { 267 auto it = data_store_.find(name); 268 CHECK(it != data_store_.end()); 269 return std::cref(it->second); 270 } 271 272 ApexFileRef ApexFileRepository::GetPreInstalledApex( 273 const std::string& name) const { 274 auto it = pre_installed_store_.find(name); 275 CHECK(it != pre_installed_store_.end()); 276 return std::cref(it->second); 277 } 278 279 } // namespace apex 280 } // namespace android 281