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