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
ScanBuiltInDir(const std::string & dir)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
GetInstance()90 ApexFileRepository& ApexFileRepository::GetInstance() {
91 static ApexFileRepository instance;
92 return instance;
93 }
94
AddPreInstalledApex(const std::vector<std::string> & prebuilt_dirs)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.
AddDataApex(const std::string & data_dir)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
GetPublicKey(const std::string & name) const171 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
GetPreinstalledPath(const std::string & name) const182 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
GetDataPath(const std::string & name) const193 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
HasPreInstalledVersion(const std::string & name) const202 bool ApexFileRepository::HasPreInstalledVersion(const std::string& name) const {
203 return pre_installed_store_.find(name) != pre_installed_store_.end();
204 }
205
HasDataVersion(const std::string & name) const206 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
IsDecompressedApex(const ApexFile & apex) const212 bool ApexFileRepository::IsDecompressedApex(const ApexFile& apex) const {
213 return apex.GetPath().starts_with(decompression_dir_);
214 }
215
IsPreInstalledApex(const ApexFile & apex) const216 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
GetPreInstalledApexFiles() const224 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
GetDataApexFiles() const232 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>>
AllApexFilesByName() const242 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
GetDataApex(const std::string & name) const266 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
GetPreInstalledApex(const std::string & name) const272 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