1 /*
2 * Copyright (C) 2018 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 ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
18
19 #include "apexd.h"
20
21 #include <ApexProperties.sysprop.h>
22 #include <android-base/chrono_utils.h>
23 #include <android-base/file.h>
24 #include <android-base/logging.h>
25 #include <android-base/macros.h>
26 #include <android-base/parseint.h>
27 #include <android-base/properties.h>
28 #include <android-base/scopeguard.h>
29 #include <android-base/stringprintf.h>
30 #include <android-base/strings.h>
31 #include <android-base/unique_fd.h>
32 #include <dirent.h>
33 #include <fcntl.h>
34 #include <google/protobuf/util/message_differencer.h>
35 #include <libavb/libavb.h>
36 #include <libdm/dm.h>
37 #include <libdm/dm_table.h>
38 #include <libdm/dm_target.h>
39 #include <linux/f2fs.h>
40 #include <linux/loop.h>
41 #include <selinux/android.h>
42 #include <statssocket_lazy.h>
43 #include <stdlib.h>
44 #include <sys/inotify.h>
45 #include <sys/ioctl.h>
46 #include <sys/mount.h>
47 #include <sys/stat.h>
48 #include <sys/sysinfo.h>
49 #include <sys/types.h>
50 #include <unistd.h>
51 #include <utils/Trace.h>
52 #include <vintf/VintfObject.h>
53
54 #include <algorithm>
55 #include <array>
56 #include <chrono>
57 #include <cstdlib>
58 #include <filesystem>
59 #include <fstream>
60 #include <future>
61 #include <iomanip>
62 #include <iterator>
63 #include <memory>
64 #include <mutex>
65 #include <optional>
66 #include <queue>
67 #include <sstream>
68 #include <string>
69 #include <string_view>
70 #include <thread>
71 #include <unordered_map>
72 #include <unordered_set>
73
74 #include "VerityUtils.h"
75 #include "apex_constants.h"
76 #include "apex_database.h"
77 #include "apex_file.h"
78 #include "apex_file_repository.h"
79 #include "apex_manifest.h"
80 #include "apex_sha.h"
81 #include "apex_shim.h"
82 #include "apexd_checkpoint.h"
83 #include "apexd_lifecycle.h"
84 #include "apexd_loop.h"
85 #include "apexd_private.h"
86 #include "apexd_rollback_utils.h"
87 #include "apexd_session.h"
88 #include "apexd_utils.h"
89 #include "apexd_vendor_apex.h"
90 #include "apexd_verity.h"
91 #include "com_android_apex.h"
92 #include "statslog_apex.h"
93
94 using android::base::boot_clock;
95 using android::base::ConsumePrefix;
96 using android::base::ErrnoError;
97 using android::base::Error;
98 using android::base::GetProperty;
99 using android::base::Join;
100 using android::base::ParseUint;
101 using android::base::RemoveFileIfExists;
102 using android::base::Result;
103 using android::base::SetProperty;
104 using android::base::StartsWith;
105 using android::base::StringPrintf;
106 using android::base::unique_fd;
107 using android::dm::DeviceMapper;
108 using android::dm::DmDeviceState;
109 using android::dm::DmTable;
110 using android::dm::DmTargetVerity;
111 using ::apex::proto::ApexManifest;
112 using apex::proto::SessionState;
113 using google::protobuf::util::MessageDifferencer;
114
115 namespace android {
116 namespace apex {
117
118 using MountedApexData = MountedApexDatabase::MountedApexData;
119
120 namespace {
121
122 static constexpr const char* kBuildFingerprintSysprop = "ro.build.fingerprint";
123
124 // This should be in UAPI, but it's not :-(
125 static constexpr const char* kDmVerityRestartOnCorruption =
126 "restart_on_corruption";
127
128 MountedApexDatabase gMountedApexes;
129
130 // Can be set by SetConfig()
131 std::optional<ApexdConfig> gConfig;
132
133 // Set by InitializeSessionManager
134 ApexSessionManager* gSessionManager;
135
136 CheckpointInterface* gVoldService;
137 bool gSupportsFsCheckpoints = false;
138 bool gInFsCheckpointMode = false;
139
140 // APEXEs for which a different version was activated than in the previous boot.
141 // This can happen in the following scenarios:
142 // 1. This APEX is part of the staged session that was applied during this
143 // boot.
144 // 2. This is a compressed APEX that was decompressed during this boot.
145 // 3. We failed to activate APEX from /data/apex/active and fallback to the
146 // pre-installed APEX.
147 std::set<std::string> gChangedActiveApexes;
148
149 static constexpr size_t kLoopDeviceSetupAttempts = 3u;
150
151 // Please DO NOT add new modules to this list without contacting mainline-modularization@ first.
__anonb516d0280202() 152 static const std::vector<std::string> kBootstrapApexes = ([]() {
153 std::vector<std::string> ret = {
154 "com.android.i18n",
155 "com.android.runtime",
156 "com.android.tzdata",
157 };
158
159 auto vendor_vndk_ver = GetProperty("ro.vndk.version", "");
160 if (vendor_vndk_ver != "") {
161 ret.push_back("com.android.vndk.v" + vendor_vndk_ver);
162 }
163 auto product_vndk_ver = GetProperty("ro.product.vndk.version", "");
164 if (product_vndk_ver != "" && product_vndk_ver != vendor_vndk_ver) {
165 ret.push_back("com.android.vndk.v" + product_vndk_ver);
166 }
167 return ret;
168 })();
169
170 static constexpr const int kNumRetriesWhenCheckpointingEnabled = 1;
171
IsBootstrapApex(const ApexFile & apex)172 bool IsBootstrapApex(const ApexFile& apex) {
173 static std::vector<std::string> additional = []() {
174 std::vector<std::string> ret;
175 if (android::base::GetBoolProperty("ro.boot.apex.early_adbd", false)) {
176 ret.push_back("com.android.adbd");
177 }
178 return ret;
179 }();
180
181 if (IsVendorApex(apex) && apex.GetManifest().vendorbootstrap()) {
182 return true;
183 }
184 return std::find(kBootstrapApexes.begin(), kBootstrapApexes.end(),
185 apex.GetManifest().name()) != kBootstrapApexes.end() ||
186 std::find(additional.begin(), additional.end(),
187 apex.GetManifest().name()) != additional.end();
188 }
189
ReleaseF2fsCompressedBlocks(const std::string & file_path)190 void ReleaseF2fsCompressedBlocks(const std::string& file_path) {
191 unique_fd fd(
192 TEMP_FAILURE_RETRY(open(file_path.c_str(), O_RDONLY | O_CLOEXEC, 0)));
193 if (fd.get() == -1) {
194 PLOG(ERROR) << "Failed to open " << file_path;
195 return;
196 }
197 unsigned int flags;
198 if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) {
199 PLOG(ERROR) << "Failed to call FS_IOC_GETFLAGS on " << file_path;
200 return;
201 }
202 if ((flags & FS_COMPR_FL) == 0) {
203 // Doesn't support f2fs-compression.
204 return;
205 }
206 uint64_t blk_cnt;
207 if (ioctl(fd, F2FS_IOC_RELEASE_COMPRESS_BLOCKS, &blk_cnt) == -1) {
208 PLOG(ERROR) << "Failed to call F2FS_IOC_RELEASE_COMPRESS_BLOCKS on "
209 << file_path;
210 }
211 LOG(INFO) << "Released " << blk_cnt << " compressed blocks from "
212 << file_path;
213 }
214
CreateVerityTable(const ApexVerityData & verity_data,const std::string & block_device,const std::string & hash_device,bool restart_on_corruption)215 std::unique_ptr<DmTable> CreateVerityTable(const ApexVerityData& verity_data,
216 const std::string& block_device,
217 const std::string& hash_device,
218 bool restart_on_corruption) {
219 AvbHashtreeDescriptor* desc = verity_data.desc.get();
220 auto table = std::make_unique<DmTable>();
221
222 uint32_t hash_start_block = 0;
223 if (hash_device == block_device) {
224 hash_start_block = desc->tree_offset / desc->hash_block_size;
225 }
226
227 auto target = std::make_unique<DmTargetVerity>(
228 0, desc->image_size / 512, desc->dm_verity_version, block_device,
229 hash_device, desc->data_block_size, desc->hash_block_size,
230 desc->image_size / desc->data_block_size, hash_start_block,
231 verity_data.hash_algorithm, verity_data.root_digest, verity_data.salt);
232
233 target->IgnoreZeroBlocks();
234 if (restart_on_corruption) {
235 target->SetVerityMode(kDmVerityRestartOnCorruption);
236 }
237 table->AddTarget(std::move(target));
238
239 table->set_readonly(true);
240
241 return table;
242 }
243
244 // Deletes a dm-verity device with a given name and path
245 // Synchronizes on the device actually being deleted from userspace.
DeleteVerityDevice(const std::string & name,bool deferred)246 Result<void> DeleteVerityDevice(const std::string& name, bool deferred) {
247 DeviceMapper& dm = DeviceMapper::Instance();
248 if (deferred) {
249 if (!dm.DeleteDeviceDeferred(name)) {
250 return ErrnoError() << "Failed to issue deferred delete of verity device "
251 << name;
252 }
253 return {};
254 }
255 auto timeout = std::chrono::milliseconds(
256 android::sysprop::ApexProperties::dm_delete_timeout().value_or(750));
257 if (!dm.DeleteDevice(name, timeout)) {
258 return Error() << "Failed to delete dm-device " << name;
259 }
260 return {};
261 }
262
263 class DmVerityDevice {
264 public:
DmVerityDevice()265 DmVerityDevice() : cleared_(true) {}
DmVerityDevice(std::string name)266 explicit DmVerityDevice(std::string name)
267 : name_(std::move(name)), cleared_(false) {}
DmVerityDevice(std::string name,std::string dev_path)268 DmVerityDevice(std::string name, std::string dev_path)
269 : name_(std::move(name)),
270 dev_path_(std::move(dev_path)),
271 cleared_(false) {}
272
DmVerityDevice(DmVerityDevice && other)273 DmVerityDevice(DmVerityDevice&& other) noexcept
274 : name_(std::move(other.name_)),
275 dev_path_(std::move(other.dev_path_)),
276 cleared_(other.cleared_) {
277 other.cleared_ = true;
278 }
279
operator =(DmVerityDevice && other)280 DmVerityDevice& operator=(DmVerityDevice&& other) noexcept {
281 name_ = other.name_;
282 dev_path_ = other.dev_path_;
283 cleared_ = other.cleared_;
284 other.cleared_ = true;
285 return *this;
286 }
287
~DmVerityDevice()288 ~DmVerityDevice() {
289 if (!cleared_) {
290 Result<void> ret = DeleteVerityDevice(name_, /* deferred= */ false);
291 if (!ret.ok()) {
292 LOG(ERROR) << ret.error();
293 }
294 }
295 }
296
GetName() const297 const std::string& GetName() const { return name_; }
GetDevPath() const298 const std::string& GetDevPath() const { return dev_path_; }
299
Release()300 void Release() { cleared_ = true; }
301
302 private:
303 std::string name_;
304 std::string dev_path_;
305 bool cleared_;
306 };
307
CreateVerityDevice(DeviceMapper & dm,const std::string & name,const DmTable & table,const std::chrono::milliseconds & timeout)308 Result<DmVerityDevice> CreateVerityDevice(
309 DeviceMapper& dm, const std::string& name, const DmTable& table,
310 const std::chrono::milliseconds& timeout) {
311 std::string dev_path;
312 if (!dm.CreateDevice(name, table, &dev_path, timeout)) {
313 return Errorf("Couldn't create verity device.");
314 }
315 return DmVerityDevice(name, dev_path);
316 }
317
CreateVerityDevice(const std::string & name,const DmTable & table,bool reuse_device)318 Result<DmVerityDevice> CreateVerityDevice(const std::string& name,
319 const DmTable& table,
320 bool reuse_device) {
321 ATRACE_NAME("CreateVerityDevice");
322 LOG(VERBOSE) << "Creating verity device " << name;
323 auto timeout = std::chrono::milliseconds(
324 android::sysprop::ApexProperties::dm_create_timeout().value_or(1000));
325
326 DeviceMapper& dm = DeviceMapper::Instance();
327
328 auto state = dm.GetState(name);
329 if (state == DmDeviceState::INVALID) {
330 return CreateVerityDevice(dm, name, table, timeout);
331 }
332
333 if (reuse_device) {
334 if (state == DmDeviceState::ACTIVE) {
335 LOG(WARNING) << "Deleting existing active dm device " << name;
336 if (auto r = DeleteVerityDevice(name, /* deferred= */ false); !r.ok()) {
337 return r.error();
338 }
339 return CreateVerityDevice(dm, name, table, timeout);
340 }
341 if (!dm.LoadTableAndActivate(name, table)) {
342 dm.DeleteDevice(name);
343 return Error() << "Failed to activate dm device " << name;
344 }
345 std::string path;
346 if (!dm.WaitForDevice(name, timeout, &path)) {
347 dm.DeleteDevice(name);
348 return Error() << "Failed waiting for dm device " << name;
349 }
350 return DmVerityDevice(name, path);
351 } else {
352 if (state != DmDeviceState::INVALID) {
353 // Delete dangling dm-device. This can happen if apexd fails to delete it
354 // while unmounting an apex.
355 LOG(WARNING) << "Deleting existing dm device " << name;
356 if (auto r = DeleteVerityDevice(name, /* deferred= */ false); !r.ok()) {
357 return r.error();
358 }
359 }
360 return CreateVerityDevice(dm, name, table, timeout);
361 }
362 }
363
364 /**
365 * When we create hardlink for a new apex package in kActiveApexPackagesDataDir,
366 * there might be an older version of the same package already present in there.
367 * Since a new version of the same package is being installed on this boot, the
368 * old one needs to deleted so that we don't end up activating same package
369 * twice.
370 *
371 * @param affected_packages package names of the news apex that are being
372 * installed in this boot
373 * @param files_to_keep path to the new apex packages in
374 * kActiveApexPackagesDataDir
375 */
RemovePreviouslyActiveApexFiles(const std::unordered_set<std::string> & affected_packages,const std::unordered_set<std::string> & files_to_keep)376 Result<void> RemovePreviouslyActiveApexFiles(
377 const std::unordered_set<std::string>& affected_packages,
378 const std::unordered_set<std::string>& files_to_keep) {
379 auto all_active_apex_files =
380 FindFilesBySuffix(gConfig->active_apex_data_dir, {kApexPackageSuffix});
381
382 if (!all_active_apex_files.ok()) {
383 return all_active_apex_files.error();
384 }
385
386 for (const std::string& path : *all_active_apex_files) {
387 Result<ApexFile> apex_file = ApexFile::Open(path);
388 if (!apex_file.ok()) {
389 return apex_file.error();
390 }
391
392 const std::string& package_name = apex_file->GetManifest().name();
393 if (affected_packages.find(package_name) == affected_packages.end()) {
394 // This apex belongs to a package that wasn't part of this stage sessions,
395 // hence it should be kept.
396 continue;
397 }
398
399 if (files_to_keep.find(apex_file->GetPath()) != files_to_keep.end()) {
400 // This is a path that was staged and should be kept.
401 continue;
402 }
403
404 LOG(DEBUG) << "Deleting previously active apex " << apex_file->GetPath();
405 if (unlink(apex_file->GetPath().c_str()) != 0) {
406 return ErrnoError() << "Failed to unlink " << apex_file->GetPath();
407 }
408 }
409
410 return {};
411 }
412
413 // Reads the entire device to verify the image is authenticatic
ReadVerityDevice(const std::string & verity_device,uint64_t device_size)414 Result<void> ReadVerityDevice(const std::string& verity_device,
415 uint64_t device_size) {
416 static constexpr int kBlockSize = 4096;
417 static constexpr size_t kBufSize = 1024 * kBlockSize;
418 std::vector<uint8_t> buffer(kBufSize);
419
420 unique_fd fd(
421 TEMP_FAILURE_RETRY(open(verity_device.c_str(), O_RDONLY | O_CLOEXEC)));
422 if (fd.get() == -1) {
423 return ErrnoError() << "Can't open " << verity_device;
424 }
425
426 size_t bytes_left = device_size;
427 while (bytes_left > 0) {
428 size_t to_read = std::min(bytes_left, kBufSize);
429 if (!android::base::ReadFully(fd.get(), buffer.data(), to_read)) {
430 return ErrnoError() << "Can't verify " << verity_device << "; corrupted?";
431 }
432 bytes_left -= to_read;
433 }
434
435 return {};
436 }
437
VerifyMountedImage(const ApexFile & apex,const std::string & mount_point)438 Result<void> VerifyMountedImage(const ApexFile& apex,
439 const std::string& mount_point) {
440 // Verify that apex_manifest.pb inside mounted image matches the one in the
441 // outer .apex container.
442 Result<ApexManifest> verified_manifest =
443 ReadManifest(mount_point + "/" + kManifestFilenamePb);
444 if (!verified_manifest.ok()) {
445 return verified_manifest.error();
446 }
447 if (!MessageDifferencer::Equals(*verified_manifest, apex.GetManifest())) {
448 return Errorf(
449 "Manifest inside filesystem does not match manifest outside it");
450 }
451 if (shim::IsShimApex(apex)) {
452 return shim::ValidateShimApex(mount_point, apex);
453 }
454 return {};
455 }
456
MountPackageImpl(const ApexFile & apex,const std::string & mount_point,const std::string & device_name,const std::string & hashtree_file,bool verify_image,bool reuse_device,bool temp_mount=false)457 Result<MountedApexData> MountPackageImpl(const ApexFile& apex,
458 const std::string& mount_point,
459 const std::string& device_name,
460 const std::string& hashtree_file,
461 bool verify_image, bool reuse_device,
462 bool temp_mount = false) {
463 auto tag = "MountPackageImpl: " + apex.GetManifest().name();
464 ATRACE_NAME(tag.c_str());
465 if (apex.IsCompressed()) {
466 return Error() << "Cannot directly mount compressed APEX "
467 << apex.GetPath();
468 }
469
470 LOG(VERBOSE) << "Creating mount point: " << mount_point;
471 auto time_started = boot_clock::now();
472 // Note: the mount point could exist in case when the APEX was activated
473 // during the bootstrap phase (e.g., the runtime or tzdata APEX).
474 // Although we have separate mount namespaces to separate the early activated
475 // APEXes from the normally activate APEXes, the mount points themselves
476 // are shared across the two mount namespaces because /apex (a tmpfs) itself
477 // mounted at / which is (and has to be) a shared mount. Therefore, if apexd
478 // finds an empty directory under /apex, it's not a problem and apexd can use
479 // it.
480 auto exists = PathExists(mount_point);
481 if (!exists.ok()) {
482 return exists.error();
483 }
484 if (!*exists && mkdir(mount_point.c_str(), kMkdirMode) != 0) {
485 return ErrnoError() << "Could not create mount point " << mount_point;
486 }
487 auto deleter = [&mount_point]() {
488 if (rmdir(mount_point.c_str()) != 0) {
489 PLOG(WARNING) << "Could not rmdir " << mount_point;
490 }
491 };
492 auto scope_guard = android::base::make_scope_guard(deleter);
493 if (!IsEmptyDirectory(mount_point)) {
494 return ErrnoError() << mount_point << " is not empty";
495 }
496
497 const std::string& full_path = apex.GetPath();
498
499 if (!apex.GetImageOffset() || !apex.GetImageSize()) {
500 return Error() << "Cannot create mount point without image offset and size";
501 }
502 loop::LoopbackDeviceUniqueFd loopback_device;
503 for (size_t attempts = 1;; ++attempts) {
504 Result<loop::LoopbackDeviceUniqueFd> ret =
505 loop::CreateAndConfigureLoopDevice(full_path,
506 apex.GetImageOffset().value(),
507 apex.GetImageSize().value());
508 if (ret.ok()) {
509 loopback_device = std::move(*ret);
510 break;
511 }
512 if (attempts >= kLoopDeviceSetupAttempts) {
513 return Error() << "Could not create loop device for " << full_path << ": "
514 << ret.error();
515 }
516 }
517 LOG(VERBOSE) << "Loopback device created: " << loopback_device.name;
518
519 auto& instance = ApexFileRepository::GetInstance();
520
521 auto public_key = instance.GetPublicKey(apex.GetManifest().name());
522 if (!public_key.ok()) {
523 return public_key.error();
524 }
525
526 auto verity_data = apex.VerifyApexVerity(*public_key);
527 if (!verity_data.ok()) {
528 return Error() << "Failed to verify Apex Verity data for " << full_path
529 << ": " << verity_data.error();
530 }
531 if (instance.IsBlockApex(apex)) {
532 auto root_digest = instance.GetBlockApexRootDigest(apex.GetPath());
533 if (root_digest.has_value() &&
534 root_digest.value() != verity_data->root_digest) {
535 return Error() << "Failed to verify Apex Verity data for " << full_path
536 << ": root digest (" << verity_data->root_digest
537 << ") mismatches with the one (" << root_digest.value()
538 << ") specified in config";
539 }
540 }
541
542 std::string block_device = loopback_device.name;
543 MountedApexData apex_data(apex.GetManifest().version(), loopback_device.name,
544 apex.GetPath(), mount_point,
545 /* device_name = */ "",
546 /* hashtree_loop_name = */ "",
547 /* is_temp_mount */ temp_mount);
548
549 // for APEXes in immutable partitions, we don't need to mount them on
550 // dm-verity because they are already in the dm-verity protected partition;
551 // system. However, note that we don't skip verification to ensure that APEXes
552 // are correctly signed.
553 const bool mount_on_verity = !instance.IsPreInstalledApex(apex) ||
554 // decompressed apexes are on /data
555 instance.IsDecompressedApex(apex) ||
556 // block apexes are from host
557 instance.IsBlockApex(apex);
558
559 DmVerityDevice verity_dev;
560 loop::LoopbackDeviceUniqueFd loop_for_hash;
561 if (mount_on_verity) {
562 std::string hash_device = loopback_device.name;
563 if (verity_data->desc->tree_size == 0) {
564 if (auto st = PrepareHashTree(apex, *verity_data, hashtree_file);
565 !st.ok()) {
566 return st.error();
567 }
568 auto create_loop_status =
569 loop::CreateAndConfigureLoopDevice(hashtree_file,
570 /* image_offset= */ 0,
571 /* image_size= */ 0);
572 if (!create_loop_status.ok()) {
573 return create_loop_status.error();
574 }
575 loop_for_hash = std::move(*create_loop_status);
576 hash_device = loop_for_hash.name;
577 apex_data.hashtree_loop_name = hash_device;
578 }
579 auto verity_table =
580 CreateVerityTable(*verity_data, loopback_device.name, hash_device,
581 /* restart_on_corruption = */ !verify_image);
582 Result<DmVerityDevice> verity_dev_res =
583 CreateVerityDevice(device_name, *verity_table, reuse_device);
584 if (!verity_dev_res.ok()) {
585 return Error() << "Failed to create Apex Verity device " << full_path
586 << ": " << verity_dev_res.error();
587 }
588 verity_dev = std::move(*verity_dev_res);
589 apex_data.device_name = device_name;
590 block_device = verity_dev.GetDevPath();
591
592 Result<void> read_ahead_status =
593 loop::ConfigureReadAhead(verity_dev.GetDevPath());
594 if (!read_ahead_status.ok()) {
595 return read_ahead_status.error();
596 }
597 }
598 // TODO(b/158467418): consider moving this inside RunVerifyFnInsideTempMount.
599 if (mount_on_verity && verify_image) {
600 Result<void> verity_status =
601 ReadVerityDevice(block_device, (*verity_data).desc->image_size);
602 if (!verity_status.ok()) {
603 return verity_status.error();
604 }
605 }
606
607 uint32_t mount_flags = MS_NOATIME | MS_NODEV | MS_DIRSYNC | MS_RDONLY;
608 if (apex.GetManifest().nocode()) {
609 mount_flags |= MS_NOEXEC;
610 }
611
612 if (!apex.GetFsType()) {
613 return Error() << "Cannot mount package without FsType";
614 }
615 if (mount(block_device.c_str(), mount_point.c_str(),
616 apex.GetFsType().value().c_str(), mount_flags, nullptr) == 0) {
617 auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
618 boot_clock::now() - time_started).count();
619 LOG(INFO) << "Successfully mounted package " << full_path << " on "
620 << mount_point << " duration=" << time_elapsed;
621 auto status = VerifyMountedImage(apex, mount_point);
622 if (!status.ok()) {
623 if (umount2(mount_point.c_str(), UMOUNT_NOFOLLOW) != 0) {
624 PLOG(ERROR) << "Failed to umount " << mount_point;
625 }
626 return Error() << "Failed to verify " << full_path << ": "
627 << status.error();
628 }
629 // Time to accept the temporaries as good.
630 verity_dev.Release();
631 loopback_device.CloseGood();
632 loop_for_hash.CloseGood();
633
634 scope_guard.Disable(); // Accept the mount.
635 return apex_data;
636 } else {
637 return ErrnoError() << "Mounting failed for package " << full_path;
638 }
639 }
640
GetHashTreeFileName(const ApexFile & apex,bool is_new)641 std::string GetHashTreeFileName(const ApexFile& apex, bool is_new) {
642 const std::string& id = GetPackageId(apex.GetManifest());
643 std::string ret =
644 StringPrintf("%s/%s", gConfig->apex_hash_tree_dir, id.c_str());
645 return is_new ? ret + ".new" : ret;
646 }
647
VerifyAndTempMountPackage(const ApexFile & apex,const std::string & mount_point)648 Result<MountedApexData> VerifyAndTempMountPackage(
649 const ApexFile& apex, const std::string& mount_point) {
650 const std::string& package_id = GetPackageId(apex.GetManifest());
651 LOG(DEBUG) << "Temp mounting " << package_id << " to " << mount_point;
652 const std::string& temp_device_name = package_id + ".tmp";
653 std::string hashtree_file = GetHashTreeFileName(apex, /* is_new = */ true);
654 if (access(hashtree_file.c_str(), F_OK) == 0) {
655 LOG(DEBUG) << hashtree_file << " already exists. Deleting it";
656 if (TEMP_FAILURE_RETRY(unlink(hashtree_file.c_str())) != 0) {
657 return ErrnoError() << "Failed to unlink " << hashtree_file;
658 }
659 }
660 auto ret =
661 MountPackageImpl(apex, mount_point, temp_device_name, hashtree_file,
662 /* verify_image = */ true, /* reuse_device= */ false,
663 /* temp_mount = */ true);
664 if (!ret.ok()) {
665 LOG(DEBUG) << "Cleaning up " << hashtree_file;
666 if (TEMP_FAILURE_RETRY(unlink(hashtree_file.c_str())) != 0) {
667 PLOG(ERROR) << "Failed to unlink " << hashtree_file;
668 }
669 } else {
670 gMountedApexes.AddMountedApex(apex.GetManifest().name(), *ret);
671 }
672 return ret;
673 }
674
675 } // namespace
676
Unmount(const MountedApexData & data,bool deferred)677 Result<void> Unmount(const MountedApexData& data, bool deferred) {
678 LOG(DEBUG) << "Unmounting " << data.full_path << " from mount point "
679 << data.mount_point << " deferred = " << deferred;
680 // Unmount whatever is mounted.
681 if (umount2(data.mount_point.c_str(), UMOUNT_NOFOLLOW) != 0 &&
682 errno != EINVAL && errno != ENOENT) {
683 return ErrnoError() << "Failed to unmount directory " << data.mount_point;
684 }
685
686 if (!deferred) {
687 if (rmdir(data.mount_point.c_str()) != 0) {
688 PLOG(ERROR) << "Failed to rmdir " << data.mount_point;
689 }
690 }
691
692 // Try to free up the device-mapper device.
693 if (!data.device_name.empty()) {
694 const auto& result = DeleteVerityDevice(data.device_name, deferred);
695 if (!result.ok()) {
696 return result;
697 }
698 }
699
700 // Try to free up the loop device.
701 auto log_fn = [](const std::string& path, const std::string& /*id*/) {
702 LOG(VERBOSE) << "Freeing loop device " << path << " for unmount.";
703 };
704
705 // Since we now use LO_FLAGS_AUTOCLEAR when configuring loop devices, in
706 // theory we don't need to manually call DestroyLoopDevice here even if
707 // |deferred| is false. However we prefer to call it to ensure the invariant
708 // of SubmitStagedSession (after it's done, loop devices created for temp
709 // mount are freed).
710 if (!data.loop_name.empty() && !deferred) {
711 loop::DestroyLoopDevice(data.loop_name, log_fn);
712 }
713 if (!data.hashtree_loop_name.empty() && !deferred) {
714 loop::DestroyLoopDevice(data.hashtree_loop_name, log_fn);
715 }
716
717 return {};
718 }
719
720 namespace {
721
SendApexInstallationRequestedAtom(const std::string & package_path,const bool is_rollback,const unsigned int install_type)722 void SendApexInstallationRequestedAtom(const std::string& package_path,
723 const bool is_rollback,
724 const unsigned int install_type) {
725 if (!statssocket::lazy::IsAvailable()) {
726 LOG(WARNING) << "Unable to send Apex Atom; libstatssocket is not available";
727 return;
728 }
729 auto apex_file = ApexFile::Open(package_path);
730 if (!apex_file.ok()) {
731 LOG(WARNING) << "Unable to send Apex Atom; Failed to open ApexFile "
732 << package_path << ": " << apex_file.error();
733 return;
734 }
735 const std::string& module_name = apex_file->GetManifest().name();
736 struct stat stat_buf;
737 intmax_t apex_file_size;
738 if (stat(package_path.c_str(), &stat_buf) == 0) {
739 apex_file_size = stat_buf.st_size;
740 } else {
741 PLOG(WARNING) << "Failed to stat " << package_path;
742 apex_file_size = 0;
743 }
744 Result<std::string> apex_file_sha256_str = CalculateSha256(package_path);
745 if (!apex_file_sha256_str.ok()) {
746 LOG(WARNING) << "Unable to get sha256 of ApexFile: "
747 << apex_file_sha256_str.error();
748 }
749 const std::vector<const char*> hal_cstr_list;
750 int ret = stats::apex::stats_write(
751 stats::apex::APEX_INSTALLATION_REQUESTED, module_name.c_str(),
752 apex_file->GetManifest().version(), apex_file_size,
753 apex_file_sha256_str->c_str(), GetPreinstallPartitionEnum(*apex_file),
754 install_type, is_rollback,
755 apex_file->GetManifest().providesharedapexlibs(), hal_cstr_list);
756 if (ret < 0) {
757 LOG(WARNING) << "Failed to report apex_installation_requested stats";
758 }
759 }
760
SendApexInstallationEndedAtom(const std::string & package_path,int install_result)761 void SendApexInstallationEndedAtom(const std::string& package_path,
762 int install_result) {
763 if (!statssocket::lazy::IsAvailable()) {
764 LOG(WARNING) << "Unable to send Apex Atom; libstatssocket is not available";
765 return;
766 }
767 Result<std::string> apex_file_sha256_str = CalculateSha256(package_path);
768 if (!apex_file_sha256_str.ok()) {
769 LOG(WARNING) << "Unable to get sha256 of ApexFile: "
770 << apex_file_sha256_str.error();
771 }
772 int ret =
773 stats::apex::stats_write(stats::apex::APEX_INSTALLATION_ENDED,
774 apex_file_sha256_str->c_str(), install_result);
775 if (ret < 0) {
776 LOG(WARNING) << "Failed to report apex_installation_ended stats";
777 }
778 }
779
780 template <typename VerifyFn>
RunVerifyFnInsideTempMount(const ApexFile & apex,const VerifyFn & verify_fn,bool unmount_during_cleanup)781 Result<void> RunVerifyFnInsideTempMount(const ApexFile& apex,
782 const VerifyFn& verify_fn,
783 bool unmount_during_cleanup) {
784 // Temp mount image of this apex to validate it was properly signed;
785 // this will also read the entire block device through dm-verity, so
786 // we can be sure there is no corruption.
787 const std::string& temp_mount_point =
788 apexd_private::GetPackageTempMountPoint(apex.GetManifest());
789
790 Result<MountedApexData> mount_status =
791 VerifyAndTempMountPackage(apex, temp_mount_point);
792 if (!mount_status.ok()) {
793 LOG(ERROR) << "Failed to temp mount to " << temp_mount_point << " : "
794 << mount_status.error();
795 return mount_status.error();
796 }
797 auto cleaner = [&]() {
798 LOG(DEBUG) << "Unmounting " << temp_mount_point;
799 Result<void> result = Unmount(*mount_status, /* deferred= */ false);
800 if (!result.ok()) {
801 LOG(WARNING) << "Failed to unmount " << temp_mount_point << " : "
802 << result.error();
803 }
804 gMountedApexes.RemoveMountedApex(apex.GetManifest().name(), apex.GetPath(),
805 true);
806 };
807 auto scope_guard = android::base::make_scope_guard(cleaner);
808 auto result = verify_fn(temp_mount_point);
809 if (!result.ok()) {
810 return result.error();
811 }
812 if (!unmount_during_cleanup) {
813 scope_guard.Disable();
814 }
815 return {};
816 }
817
818 // Converts a list of apex file paths into a list of ApexFile objects
819 //
820 // Returns error when trying to open empty set of inputs.
OpenApexFiles(const std::vector<std::string> & paths)821 Result<std::vector<ApexFile>> OpenApexFiles(
822 const std::vector<std::string>& paths) {
823 if (paths.empty()) {
824 return Errorf("Empty set of inputs");
825 }
826 std::vector<ApexFile> ret;
827 for (const std::string& path : paths) {
828 Result<ApexFile> apex_file = ApexFile::Open(path);
829 if (!apex_file.ok()) {
830 return apex_file.error();
831 }
832 ret.emplace_back(std::move(*apex_file));
833 }
834 return ret;
835 }
836
ValidateStagingShimApex(const ApexFile & to)837 Result<void> ValidateStagingShimApex(const ApexFile& to) {
838 using android::base::StringPrintf;
839 auto system_shim = ApexFile::Open(
840 StringPrintf("%s/%s", kApexPackageSystemDir, shim::kSystemShimApexName));
841 if (!system_shim.ok()) {
842 return system_shim.error();
843 }
844 auto verify_fn = [&](const std::string& system_apex_path) {
845 return shim::ValidateUpdate(system_apex_path, to.GetPath());
846 };
847 return RunVerifyFnInsideTempMount(*system_shim, verify_fn, true);
848 }
849
VerifyVndkVersion(const ApexFile & apex_file)850 Result<void> VerifyVndkVersion(const ApexFile& apex_file) {
851 const std::string& vndk_version = apex_file.GetManifest().vndkversion();
852 if (vndk_version.empty()) {
853 return {};
854 }
855
856 static std::string vendor_vndk_version = GetProperty("ro.vndk.version", "");
857 static std::string product_vndk_version =
858 GetProperty("ro.product.vndk.version", "");
859
860 const auto& instance = ApexFileRepository::GetInstance();
861 const auto& preinstalled =
862 instance.GetPreInstalledApex(apex_file.GetManifest().name());
863 const auto& preinstalled_path = preinstalled.get().GetPath();
864 if (StartsWith(preinstalled_path, "/vendor/apex/") ||
865 StartsWith(preinstalled_path, "/system/vendor/apex/")) {
866 if (vndk_version != vendor_vndk_version) {
867 return Error() << "vndkVersion(" << vndk_version
868 << ") doesn't match with device VNDK version("
869 << vendor_vndk_version << ")";
870 }
871 return {};
872 }
873 if (StartsWith(preinstalled_path, "/product/apex/") ||
874 StartsWith(preinstalled_path, "/system/product/apex/")) {
875 if (vndk_version != product_vndk_version) {
876 return Error() << "vndkVersion(" << vndk_version
877 << ") doesn't match with device VNDK version("
878 << product_vndk_version << ")";
879 }
880 return {};
881 }
882 return Error() << "vndkVersion(" << vndk_version << ") is set";
883 }
884
885 // A version of apex verification that happens during boot.
886 // This function should only verification checks that are necessary to run on
887 // each boot. Try to avoid putting expensive checks inside this function.
VerifyPackageBoot(const ApexFile & apex_file)888 Result<void> VerifyPackageBoot(const ApexFile& apex_file) {
889 // TODO(ioffe): why do we need this here?
890 auto& instance = ApexFileRepository::GetInstance();
891 auto public_key = instance.GetPublicKey(apex_file.GetManifest().name());
892 if (!public_key.ok()) {
893 return public_key.error();
894 }
895 Result<ApexVerityData> verity_or = apex_file.VerifyApexVerity(*public_key);
896 if (!verity_or.ok()) {
897 return verity_or.error();
898 }
899
900 if (shim::IsShimApex(apex_file)) {
901 // Validating shim is not a very cheap operation, but it's fine to perform
902 // it here since it only runs during CTS tests and will never be triggered
903 // during normal flow.
904 const auto& result = ValidateStagingShimApex(apex_file);
905 if (!result.ok()) {
906 return result;
907 }
908 }
909
910 if (auto result = VerifyVndkVersion(apex_file); !result.ok()) {
911 return result;
912 }
913
914 return {};
915 }
916
917 // A version of apex verification that happens on SubmitStagedSession.
918 // This function contains checks that might be expensive to perform, e.g. temp
919 // mounting a package and reading entire dm-verity device, and shouldn't be run
920 // during boot.
VerifyPackageStagedInstall(const ApexFile & apex_file)921 Result<void> VerifyPackageStagedInstall(const ApexFile& apex_file) {
922 const auto& verify_package_boot_status = VerifyPackageBoot(apex_file);
923 if (!verify_package_boot_status.ok()) {
924 return verify_package_boot_status;
925 }
926
927 const auto validate_fn = [&apex_file](const std::string& mount_point) {
928 if (IsVendorApex(apex_file)) {
929 return CheckVendorApexUpdate(apex_file, mount_point);
930 }
931 return Result<void>{};
932 };
933 return RunVerifyFnInsideTempMount(apex_file, validate_fn, false);
934 }
935
936 template <typename VerifyApexFn>
VerifyPackages(const std::vector<std::string> & paths,const VerifyApexFn & verify_apex_fn)937 Result<std::vector<ApexFile>> VerifyPackages(
938 const std::vector<std::string>& paths, const VerifyApexFn& verify_apex_fn) {
939 Result<std::vector<ApexFile>> apex_files = OpenApexFiles(paths);
940 if (!apex_files.ok()) {
941 return apex_files.error();
942 }
943
944 LOG(DEBUG) << "VerifyPackages() for " << Join(paths, ',');
945
946 for (const ApexFile& apex_file : *apex_files) {
947 Result<void> result = verify_apex_fn(apex_file);
948 if (!result.ok()) {
949 return result.error();
950 }
951 }
952 return std::move(*apex_files);
953 }
954
VerifySessionDir(int session_id)955 Result<ApexFile> VerifySessionDir(int session_id) {
956 std::string session_dir_path =
957 StringPrintf("%s/session_%d", gConfig->staged_session_dir, session_id);
958 LOG(INFO) << "Scanning " << session_dir_path
959 << " looking for packages to be validated";
960 Result<std::vector<std::string>> scan =
961 FindFilesBySuffix(session_dir_path, {kApexPackageSuffix});
962 if (!scan.ok()) {
963 LOG(WARNING) << scan.error();
964 return scan.error();
965 }
966
967 if (scan->size() > 1) {
968 return Errorf(
969 "More than one APEX package found in the same session directory.");
970 }
971
972 auto verified = VerifyPackages(*scan, VerifyPackageStagedInstall);
973 if (!verified.ok()) {
974 return verified.error();
975 }
976 return std::move((*verified)[0]);
977 }
978
DeleteBackup()979 Result<void> DeleteBackup() {
980 auto exists = PathExists(std::string(kApexBackupDir));
981 if (!exists.ok()) {
982 return Error() << "Can't clean " << kApexBackupDir << " : "
983 << exists.error();
984 }
985 if (!*exists) {
986 LOG(DEBUG) << kApexBackupDir << " does not exist. Nothing to clean";
987 return {};
988 }
989 return DeleteDirContent(std::string(kApexBackupDir));
990 }
991
BackupActivePackages()992 Result<void> BackupActivePackages() {
993 LOG(DEBUG) << "Initializing backup of " << gConfig->active_apex_data_dir;
994
995 // Previous restore might've delete backups folder.
996 auto create_status = CreateDirIfNeeded(kApexBackupDir, 0700);
997 if (!create_status.ok()) {
998 return Error() << "Backup failed : " << create_status.error();
999 }
1000
1001 auto apex_active_exists =
1002 PathExists(std::string(gConfig->active_apex_data_dir));
1003 if (!apex_active_exists.ok()) {
1004 return Error() << "Backup failed : " << apex_active_exists.error();
1005 }
1006 if (!*apex_active_exists) {
1007 LOG(DEBUG) << gConfig->active_apex_data_dir
1008 << " does not exist. Nothing to backup";
1009 return {};
1010 }
1011
1012 auto active_packages =
1013 FindFilesBySuffix(gConfig->active_apex_data_dir, {kApexPackageSuffix});
1014 if (!active_packages.ok()) {
1015 return Error() << "Backup failed : " << active_packages.error();
1016 }
1017
1018 auto cleanup_status = DeleteBackup();
1019 if (!cleanup_status.ok()) {
1020 return Error() << "Backup failed : " << cleanup_status.error();
1021 }
1022
1023 auto backup_path_fn = [](const ApexFile& apex_file) {
1024 return StringPrintf("%s/%s%s", kApexBackupDir,
1025 GetPackageId(apex_file.GetManifest()).c_str(),
1026 kApexPackageSuffix);
1027 };
1028
1029 auto deleter = []() {
1030 auto result = DeleteDirContent(std::string(kApexBackupDir));
1031 if (!result.ok()) {
1032 LOG(ERROR) << "Failed to cleanup " << kApexBackupDir << " : "
1033 << result.error();
1034 }
1035 };
1036 auto scope_guard = android::base::make_scope_guard(deleter);
1037
1038 for (const std::string& path : *active_packages) {
1039 Result<ApexFile> apex_file = ApexFile::Open(path);
1040 if (!apex_file.ok()) {
1041 return Error() << "Backup failed : " << apex_file.error();
1042 }
1043 const auto& dest_path = backup_path_fn(*apex_file);
1044 if (link(apex_file->GetPath().c_str(), dest_path.c_str()) != 0) {
1045 return ErrnoError() << "Failed to backup " << apex_file->GetPath();
1046 }
1047 }
1048
1049 scope_guard.Disable(); // Accept the backup.
1050 return {};
1051 }
1052
RestoreActivePackages()1053 Result<void> RestoreActivePackages() {
1054 LOG(DEBUG) << "Initializing restore of " << gConfig->active_apex_data_dir;
1055
1056 auto backup_exists = PathExists(std::string(kApexBackupDir));
1057 if (!backup_exists.ok()) {
1058 return backup_exists.error();
1059 }
1060 if (!*backup_exists) {
1061 return Error() << kApexBackupDir << " does not exist";
1062 }
1063
1064 struct stat stat_data;
1065 if (stat(gConfig->active_apex_data_dir, &stat_data) != 0) {
1066 return ErrnoError() << "Failed to access " << gConfig->active_apex_data_dir;
1067 }
1068
1069 LOG(DEBUG) << "Deleting existing packages in "
1070 << gConfig->active_apex_data_dir;
1071 auto delete_status =
1072 DeleteDirContent(std::string(gConfig->active_apex_data_dir));
1073 if (!delete_status.ok()) {
1074 return delete_status;
1075 }
1076
1077 LOG(DEBUG) << "Renaming " << kApexBackupDir << " to "
1078 << gConfig->active_apex_data_dir;
1079 if (rename(kApexBackupDir, gConfig->active_apex_data_dir) != 0) {
1080 return ErrnoError() << "Failed to rename " << kApexBackupDir << " to "
1081 << gConfig->active_apex_data_dir;
1082 }
1083
1084 LOG(DEBUG) << "Restoring original permissions for "
1085 << gConfig->active_apex_data_dir;
1086 if (chmod(gConfig->active_apex_data_dir, stat_data.st_mode & ALLPERMS) != 0) {
1087 return ErrnoError() << "Failed to restore original permissions for "
1088 << gConfig->active_apex_data_dir;
1089 }
1090
1091 return {};
1092 }
1093
UnmountPackage(const ApexFile & apex,bool allow_latest,bool deferred,bool detach_mount_point)1094 Result<void> UnmountPackage(const ApexFile& apex, bool allow_latest,
1095 bool deferred, bool detach_mount_point) {
1096 LOG(INFO) << "Unmounting " << GetPackageId(apex.GetManifest())
1097 << " allow_latest : " << allow_latest << " deferred : " << deferred
1098 << " detach_mount_point : " << detach_mount_point;
1099
1100 const ApexManifest& manifest = apex.GetManifest();
1101
1102 std::optional<MountedApexData> data;
1103 bool latest = false;
1104
1105 auto fn = [&](const MountedApexData& d, bool l) {
1106 if (d.full_path == apex.GetPath()) {
1107 data.emplace(d);
1108 latest = l;
1109 }
1110 };
1111 gMountedApexes.ForallMountedApexes(manifest.name(), fn);
1112
1113 if (!data) {
1114 return Error() << "Did not find " << apex.GetPath();
1115 }
1116
1117 // Concept of latest sharedlibs apex is somewhat blurred. Since this is only
1118 // used in testing, it is ok to always allow unmounting sharedlibs apex.
1119 if (latest && !manifest.providesharedapexlibs()) {
1120 if (!allow_latest) {
1121 return Error() << "Package " << apex.GetPath() << " is active";
1122 }
1123 std::string mount_point = apexd_private::GetActiveMountPoint(manifest);
1124 LOG(INFO) << "Unmounting " << mount_point;
1125 int flags = UMOUNT_NOFOLLOW;
1126 if (detach_mount_point) {
1127 flags |= MNT_DETACH;
1128 }
1129 if (umount2(mount_point.c_str(), flags) != 0) {
1130 return ErrnoError() << "Failed to unmount " << mount_point;
1131 }
1132
1133 if (!deferred) {
1134 if (rmdir(mount_point.c_str()) != 0) {
1135 PLOG(ERROR) << "Failed to rmdir " << mount_point;
1136 }
1137 }
1138 }
1139
1140 // Clean up gMountedApexes now, even though we're not fully done.
1141 gMountedApexes.RemoveMountedApex(manifest.name(), apex.GetPath());
1142 return Unmount(*data, deferred);
1143 }
1144
1145 } // namespace
1146
SetConfig(const ApexdConfig & config)1147 void SetConfig(const ApexdConfig& config) { gConfig = config; }
1148
MountPackage(const ApexFile & apex,const std::string & mount_point,const std::string & device_name,bool reuse_device,bool temp_mount)1149 Result<void> MountPackage(const ApexFile& apex, const std::string& mount_point,
1150 const std::string& device_name, bool reuse_device,
1151 bool temp_mount) {
1152 auto ret =
1153 MountPackageImpl(apex, mount_point, device_name,
1154 GetHashTreeFileName(apex, /* is_new= */ false),
1155 /* verify_image = */ false, reuse_device, temp_mount);
1156 if (!ret.ok()) {
1157 return ret.error();
1158 }
1159
1160 gMountedApexes.AddMountedApex(apex.GetManifest().name(), *ret);
1161 return {};
1162 }
1163
1164 namespace apexd_private {
1165
UnmountTempMount(const ApexFile & apex)1166 Result<void> UnmountTempMount(const ApexFile& apex) {
1167 const ApexManifest& manifest = apex.GetManifest();
1168 LOG(VERBOSE) << "Unmounting all temp mounts for package " << manifest.name();
1169
1170 bool finished_unmounting = false;
1171 // If multiple temp mounts exist, ensure that all are unmounted.
1172 while (!finished_unmounting) {
1173 Result<MountedApexData> data =
1174 apexd_private::GetTempMountedApexData(manifest.name());
1175 if (!data.ok()) {
1176 finished_unmounting = true;
1177 } else {
1178 gMountedApexes.RemoveMountedApex(manifest.name(), data->full_path, true);
1179 Unmount(*data, /* deferred= */ false);
1180 }
1181 }
1182 return {};
1183 }
1184
GetTempMountedApexData(const std::string & package)1185 Result<MountedApexData> GetTempMountedApexData(const std::string& package) {
1186 bool found = false;
1187 Result<MountedApexData> mount_data;
1188 gMountedApexes.ForallMountedApexes(
1189 package,
1190 [&](const MountedApexData& data, [[maybe_unused]] bool latest) {
1191 if (!found) {
1192 mount_data = data;
1193 found = true;
1194 }
1195 },
1196 true);
1197 if (found) {
1198 return mount_data;
1199 }
1200 return Error() << "No temp mount data found for " << package;
1201 }
1202
IsMounted(const std::string & full_path)1203 bool IsMounted(const std::string& full_path) {
1204 bool found_mounted = false;
1205 gMountedApexes.ForallMountedApexes([&](const std::string&,
1206 const MountedApexData& data,
1207 [[maybe_unused]] bool latest) {
1208 if (full_path == data.full_path) {
1209 found_mounted = true;
1210 }
1211 });
1212 return found_mounted;
1213 }
1214
GetPackageMountPoint(const ApexManifest & manifest)1215 std::string GetPackageMountPoint(const ApexManifest& manifest) {
1216 return StringPrintf("%s/%s", kApexRoot, GetPackageId(manifest).c_str());
1217 }
1218
GetPackageTempMountPoint(const ApexManifest & manifest)1219 std::string GetPackageTempMountPoint(const ApexManifest& manifest) {
1220 return StringPrintf("%s.tmp", GetPackageMountPoint(manifest).c_str());
1221 }
1222
GetActiveMountPoint(const ApexManifest & manifest)1223 std::string GetActiveMountPoint(const ApexManifest& manifest) {
1224 return StringPrintf("%s/%s", kApexRoot, manifest.name().c_str());
1225 }
1226
1227 } // namespace apexd_private
1228
ResumeRevertIfNeeded()1229 Result<void> ResumeRevertIfNeeded() {
1230 auto sessions =
1231 gSessionManager->GetSessionsInState(SessionState::REVERT_IN_PROGRESS);
1232 if (sessions.empty()) {
1233 return {};
1234 }
1235 return RevertActiveSessions("", "");
1236 }
1237
ContributeToSharedLibs(const std::string & mount_point)1238 Result<void> ContributeToSharedLibs(const std::string& mount_point) {
1239 for (const auto& lib_path : {"lib", "lib64"}) {
1240 std::string apex_lib_path = mount_point + "/" + lib_path;
1241 auto lib_dir = PathExists(apex_lib_path);
1242 if (!lib_dir.ok() || !*lib_dir) {
1243 continue;
1244 }
1245
1246 auto iter = std::filesystem::directory_iterator(apex_lib_path);
1247 std::error_code ec;
1248
1249 while (iter != std::filesystem::end(iter)) {
1250 const auto& lib_entry = *iter;
1251 if (!lib_entry.is_directory()) {
1252 iter = iter.increment(ec);
1253 if (ec) {
1254 return Error() << "Failed to scan " << apex_lib_path << " : "
1255 << ec.message();
1256 }
1257 continue;
1258 }
1259
1260 const auto library_name = lib_entry.path().filename();
1261 const std::string library_symlink_dir =
1262 StringPrintf("%s/%s/%s/%s", kApexRoot, kApexSharedLibsSubDir,
1263 lib_path, library_name.c_str());
1264
1265 auto symlink_dir = PathExists(library_symlink_dir);
1266 if (!symlink_dir.ok() || !*symlink_dir) {
1267 std::filesystem::create_directory(library_symlink_dir, ec);
1268 if (ec) {
1269 return Error() << "Failed to create directory " << library_symlink_dir
1270 << ": " << ec.message();
1271 }
1272 }
1273
1274 auto inner_iter =
1275 std::filesystem::directory_iterator(lib_entry.path().string());
1276
1277 while (inner_iter != std::filesystem::end(inner_iter)) {
1278 const auto& lib_items = *inner_iter;
1279 const auto hash_value = lib_items.path().filename();
1280 const std::string library_symlink_hash = StringPrintf(
1281 "%s/%s", library_symlink_dir.c_str(), hash_value.c_str());
1282
1283 auto hash_dir = PathExists(library_symlink_hash);
1284 if (hash_dir.ok() && *hash_dir) {
1285 // Compare file size for two library files with same name and hash
1286 // value
1287 auto existing_file_path =
1288 library_symlink_hash + "/" + library_name.string();
1289 auto existing_file_size = GetFileSize(existing_file_path);
1290 if (!existing_file_size.ok()) {
1291 return existing_file_size.error();
1292 }
1293
1294 auto new_file_path =
1295 lib_items.path().string() + "/" + library_name.string();
1296 auto new_file_size = GetFileSize(new_file_path);
1297 if (!new_file_size.ok()) {
1298 return new_file_size.error();
1299 }
1300
1301 if (*existing_file_size != *new_file_size) {
1302 return Error() << "There are two libraries with same hash and "
1303 "different file size : "
1304 << existing_file_path << " and " << new_file_path;
1305 }
1306
1307 inner_iter = inner_iter.increment(ec);
1308 if (ec) {
1309 return Error() << "Failed to scan " << lib_entry.path().string()
1310 << " : " << ec.message();
1311 }
1312 continue;
1313 }
1314 std::filesystem::create_directory_symlink(lib_items.path(),
1315 library_symlink_hash, ec);
1316 if (ec) {
1317 return Error() << "Failed to create symlink from " << lib_items.path()
1318 << " to " << library_symlink_hash << ec.message();
1319 }
1320
1321 inner_iter = inner_iter.increment(ec);
1322 if (ec) {
1323 return Error() << "Failed to scan " << lib_entry.path().string()
1324 << " : " << ec.message();
1325 }
1326 }
1327
1328 iter = iter.increment(ec);
1329 if (ec) {
1330 return Error() << "Failed to scan " << apex_lib_path << " : "
1331 << ec.message();
1332 }
1333 }
1334 }
1335
1336 return {};
1337 }
1338
IsValidPackageName(const std::string & package_name)1339 bool IsValidPackageName(const std::string& package_name) {
1340 return kBannedApexName.count(package_name) == 0;
1341 }
1342
1343 // Activates given APEX file.
1344 //
1345 // In a nutshel activation of an APEX consist of the following steps:
1346 // 1. Create loop devices that is backed by the given apex_file
1347 // 2. If apex_file resides on /data partition then create a dm-verity device
1348 // backed by the loop device created in step (1).
1349 // 3. Create a mount point under /apex for this APEX.
1350 // 4. Mount the dm-verity device on that mount point.
1351 // 4.1 In case APEX file comes from a partition that is already
1352 // dm-verity protected (e.g. /system) then we mount the loop device.
1353 //
1354 //
1355 // Note: this function only does the job to activate this single APEX.
1356 // In case this APEX file contributes to the /apex/sharedlibs mount point, then
1357 // you must also call ContributeToSharedLibs after finishing activating all
1358 // APEXes. See ActivateApexPackages for more context.
ActivatePackageImpl(const ApexFile & apex_file,const std::string & device_name,bool reuse_device)1359 Result<void> ActivatePackageImpl(const ApexFile& apex_file,
1360 const std::string& device_name,
1361 bool reuse_device) {
1362 ATRACE_NAME("ActivatePackageImpl");
1363 const ApexManifest& manifest = apex_file.GetManifest();
1364
1365 if (!IsValidPackageName(manifest.name())) {
1366 return Errorf("Package name {} is not allowed.", manifest.name());
1367 }
1368
1369 // Validate upgraded shim apex
1370 if (shim::IsShimApex(apex_file) &&
1371 !ApexFileRepository::GetInstance().IsPreInstalledApex(apex_file)) {
1372 // This is not cheap for shim apex, but it is fine here since we have
1373 // upgraded shim apex only during CTS tests.
1374 Result<void> result = VerifyPackageBoot(apex_file);
1375 if (!result.ok()) {
1376 LOG(ERROR) << "Failed to validate shim apex: " << apex_file.GetPath();
1377 return result;
1378 }
1379 }
1380
1381 // See whether we think it's active, and do not allow to activate the same
1382 // version. Also detect whether this is the highest version.
1383 // We roll this into a single check.
1384 bool version_found_mounted = false;
1385 {
1386 uint64_t new_version = manifest.version();
1387 bool version_found_active = false;
1388 gMountedApexes.ForallMountedApexes(
1389 manifest.name(), [&](const MountedApexData& data, bool latest) {
1390 Result<ApexFile> other_apex = ApexFile::Open(data.full_path);
1391 if (!other_apex.ok()) {
1392 return;
1393 }
1394 if (static_cast<uint64_t>(other_apex->GetManifest().version()) ==
1395 new_version) {
1396 version_found_mounted = true;
1397 version_found_active = latest;
1398 }
1399 });
1400 // If the package provides shared libraries to other APEXs, we need to
1401 // activate all versions available (i.e. preloaded on /system/apex and
1402 // available on /data/apex/active). The reason is that there might be some
1403 // APEXs loaded from /system/apex that reference the libraries contained on
1404 // the preloaded version of the apex providing shared libraries.
1405 if (version_found_active && !manifest.providesharedapexlibs()) {
1406 LOG(DEBUG) << "Package " << manifest.name() << " with version "
1407 << manifest.version() << " already active";
1408 return {};
1409 }
1410 }
1411
1412 const std::string& mount_point =
1413 apexd_private::GetPackageMountPoint(manifest);
1414
1415 if (!version_found_mounted) {
1416 auto mount_status = MountPackage(apex_file, mount_point, device_name,
1417 reuse_device, /*temp_mount=*/false);
1418 if (!mount_status.ok()) {
1419 return mount_status;
1420 }
1421 }
1422
1423 // Bind mount the latest version to /apex/<package_name>, unless the
1424 // package provides shared libraries to other APEXs.
1425 if (!manifest.providesharedapexlibs()) {
1426 auto st = gMountedApexes.DoIfLatest(
1427 manifest.name(), apex_file.GetPath(), [&]() -> Result<void> {
1428 return apexd_private::BindMount(
1429 apexd_private::GetActiveMountPoint(manifest), mount_point);
1430 });
1431 if (!st.ok()) {
1432 return Error() << "Failed to update package " << manifest.name()
1433 << " to version " << manifest.version() << " : "
1434 << st.error();
1435 }
1436 }
1437
1438 LOG(DEBUG) << "Successfully activated " << apex_file.GetPath()
1439 << " package_name: " << manifest.name()
1440 << " version: " << manifest.version();
1441 return {};
1442 }
1443
1444 // Wrapper around ActivatePackageImpl.
1445 // Do not use, this wrapper is going away.
ActivatePackage(const std::string & full_path)1446 Result<void> ActivatePackage(const std::string& full_path) {
1447 LOG(INFO) << "Trying to activate " << full_path;
1448
1449 Result<ApexFile> apex_file = ApexFile::Open(full_path);
1450 if (!apex_file.ok()) {
1451 return apex_file.error();
1452 }
1453 return ActivatePackageImpl(*apex_file, GetPackageId(apex_file->GetManifest()),
1454 /* reuse_device= */ false);
1455 }
1456
DeactivatePackage(const std::string & full_path)1457 Result<void> DeactivatePackage(const std::string& full_path) {
1458 LOG(INFO) << "Trying to deactivate " << full_path;
1459
1460 Result<ApexFile> apex_file = ApexFile::Open(full_path);
1461 if (!apex_file.ok()) {
1462 return apex_file.error();
1463 }
1464
1465 return UnmountPackage(*apex_file, /* allow_latest= */ true,
1466 /* deferred= */ false, /* detach_mount_point= */ false);
1467 }
1468
GetStagedApexFiles(int session_id,const std::vector<int> & child_session_ids)1469 Result<std::vector<ApexFile>> GetStagedApexFiles(
1470 int session_id, const std::vector<int>& child_session_ids) {
1471 auto session = gSessionManager->GetSession(session_id);
1472 if (!session.ok()) {
1473 return session.error();
1474 }
1475 // We should only accept sessions in SessionState::STAGED state
1476 auto session_state = (*session).GetState();
1477 if (session_state != SessionState::STAGED) {
1478 return Error() << "Session " << session_id << " is not in state STAGED";
1479 }
1480
1481 std::vector<int> ids_to_scan;
1482 if (!child_session_ids.empty()) {
1483 ids_to_scan = child_session_ids;
1484 } else {
1485 ids_to_scan = {session_id};
1486 }
1487
1488 // Find apex files in the staging directory
1489 std::vector<std::string> apex_file_paths;
1490 for (int id_to_scan : ids_to_scan) {
1491 std::string session_dir_path = std::string(gConfig->staged_session_dir) +
1492 "/session_" + std::to_string(id_to_scan);
1493 Result<std::vector<std::string>> scan =
1494 FindFilesBySuffix(session_dir_path, {kApexPackageSuffix});
1495 if (!scan.ok()) {
1496 return scan.error();
1497 }
1498 if (scan->size() != 1) {
1499 return Error() << "Expected exactly one APEX file in directory "
1500 << session_dir_path << ". Found: " << scan->size();
1501 }
1502 std::string& apex_file_path = (*scan)[0];
1503 apex_file_paths.push_back(std::move(apex_file_path));
1504 }
1505
1506 return OpenApexFiles(apex_file_paths);
1507 }
1508
MountAndDeriveClassPath(const std::vector<ApexFile> & apex_files)1509 Result<ClassPath> MountAndDeriveClassPath(
1510 const std::vector<ApexFile>& apex_files) {
1511 auto guard = android::base::make_scope_guard([&]() {
1512 for (const auto& apex : apex_files) {
1513 apexd_private::UnmountTempMount(apex);
1514 }
1515 });
1516
1517 // Mount the staged apex files
1518 std::vector<std::string> temp_mounted_apex_paths;
1519 for (const auto& apex : apex_files) {
1520 const std::string& temp_mount_point =
1521 apexd_private::GetPackageTempMountPoint(apex.GetManifest());
1522 const std::string& package_id = GetPackageId(apex.GetManifest());
1523 const std::string& temp_device_name = package_id + ".tmp";
1524 auto mount_status =
1525 MountPackage(apex, temp_mount_point, temp_device_name,
1526 /*reuse_device=*/false, /*temp_mount=*/true);
1527 if (!mount_status.ok()) {
1528 return mount_status.error();
1529 }
1530 temp_mounted_apex_paths.push_back(temp_mount_point);
1531 }
1532
1533 // Calculate classpaths of temp mounted staged apexs
1534 return ClassPath::DeriveClassPath(temp_mounted_apex_paths);
1535 }
1536
GetActivePackages()1537 std::vector<ApexFile> GetActivePackages() {
1538 std::vector<ApexFile> ret;
1539 gMountedApexes.ForallMountedApexes(
1540 [&](const std::string&, const MountedApexData& data, bool latest) {
1541 if (!latest) {
1542 return;
1543 }
1544
1545 Result<ApexFile> apex_file = ApexFile::Open(data.full_path);
1546 if (!apex_file.ok()) {
1547 return;
1548 }
1549 ret.emplace_back(std::move(*apex_file));
1550 });
1551
1552 return ret;
1553 }
1554
CalculateInactivePackages(const std::vector<ApexFile> & active)1555 std::vector<ApexFile> CalculateInactivePackages(
1556 const std::vector<ApexFile>& active) {
1557 std::vector<ApexFile> inactive = GetFactoryPackages();
1558 auto new_end = std::remove_if(
1559 inactive.begin(), inactive.end(), [&active](const ApexFile& apex) {
1560 return std::any_of(active.begin(), active.end(),
1561 [&apex](const ApexFile& active_apex) {
1562 return apex.GetPath() == active_apex.GetPath();
1563 });
1564 });
1565 inactive.erase(new_end, inactive.end());
1566 return std::move(inactive);
1567 }
1568
EmitApexInfoList(bool is_bootstrap)1569 Result<void> EmitApexInfoList(bool is_bootstrap) {
1570 // Apexd runs both in "bootstrap" and "default" mount namespace.
1571 // To expose /apex/apex-info-list.xml separately in each mount namespaces,
1572 // we write /apex/.<namespace>-apex-info-list .xml file first and then
1573 // bind mount it to the canonical file (/apex/apex-info-list.xml).
1574 const std::string file_name =
1575 fmt::format("{}/.{}-{}", kApexRoot,
1576 is_bootstrap ? "bootstrap" : "default", kApexInfoList);
1577
1578 unique_fd fd(TEMP_FAILURE_RETRY(
1579 open(file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644)));
1580 if (fd.get() == -1) {
1581 return ErrnoErrorf("Can't open {}", file_name);
1582 }
1583
1584 const std::vector<ApexFile> active(GetActivePackages());
1585
1586 std::vector<ApexFile> inactive;
1587 // we skip for non-activated built-in apexes in bootstrap mode
1588 // in order to avoid boottime increase
1589 if (!is_bootstrap) {
1590 inactive = CalculateInactivePackages(active);
1591 }
1592
1593 std::stringstream xml;
1594 CollectApexInfoList(xml, active, inactive);
1595
1596 if (!android::base::WriteStringToFd(xml.str(), fd)) {
1597 return ErrnoErrorf("Can't write to {}", file_name);
1598 }
1599
1600 fd.reset();
1601
1602 const std::string mount_point =
1603 fmt::format("{}/{}", kApexRoot, kApexInfoList);
1604 if (access(mount_point.c_str(), F_OK) != 0) {
1605 close(open(mount_point.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
1606 0644));
1607 }
1608 if (mount(file_name.c_str(), mount_point.c_str(), nullptr, MS_BIND,
1609 nullptr) == -1) {
1610 return ErrnoErrorf("Can't bind mount {} to {}", file_name, mount_point);
1611 }
1612 return RestoreconPath(file_name);
1613 }
1614
1615 namespace {
GetActivePackagesMap()1616 std::unordered_map<std::string, uint64_t> GetActivePackagesMap() {
1617 std::vector<ApexFile> active_packages = GetActivePackages();
1618 std::unordered_map<std::string, uint64_t> ret;
1619 for (const auto& package : active_packages) {
1620 const ApexManifest& manifest = package.GetManifest();
1621 ret.insert({manifest.name(), manifest.version()});
1622 }
1623 return ret;
1624 }
1625
1626 } // namespace
1627
GetFactoryPackages()1628 std::vector<ApexFile> GetFactoryPackages() {
1629 std::vector<ApexFile> ret;
1630
1631 // Decompressed APEX is considered factory package
1632 std::vector<std::string> decompressed_pkg_names;
1633 auto active_pkgs = GetActivePackages();
1634 for (ApexFile& apex : active_pkgs) {
1635 if (ApexFileRepository::GetInstance().IsDecompressedApex(apex)) {
1636 decompressed_pkg_names.push_back(apex.GetManifest().name());
1637 ret.emplace_back(std::move(apex));
1638 }
1639 }
1640
1641 const auto& file_repository = ApexFileRepository::GetInstance();
1642 for (const auto& ref : file_repository.GetPreInstalledApexFiles()) {
1643 Result<ApexFile> apex_file = ApexFile::Open(ref.get().GetPath());
1644 if (!apex_file.ok()) {
1645 LOG(ERROR) << apex_file.error();
1646 continue;
1647 }
1648 // Ignore compressed APEX if it has been decompressed already
1649 if (apex_file->IsCompressed() &&
1650 std::find(decompressed_pkg_names.begin(), decompressed_pkg_names.end(),
1651 apex_file->GetManifest().name()) !=
1652 decompressed_pkg_names.end()) {
1653 continue;
1654 }
1655
1656 ret.emplace_back(std::move(*apex_file));
1657 }
1658 return ret;
1659 }
1660
GetActivePackage(const std::string & packageName)1661 Result<ApexFile> GetActivePackage(const std::string& packageName) {
1662 std::vector<ApexFile> packages = GetActivePackages();
1663 for (ApexFile& apex : packages) {
1664 if (apex.GetManifest().name() == packageName) {
1665 return std::move(apex);
1666 }
1667 }
1668
1669 return ErrnoError() << "Cannot find matching package for: " << packageName;
1670 }
1671
1672 /**
1673 * Abort individual staged session.
1674 *
1675 * Returns without error only if session was successfully aborted.
1676 **/
AbortStagedSession(int session_id)1677 Result<void> AbortStagedSession(int session_id) {
1678 auto session = gSessionManager->GetSession(session_id);
1679 if (!session.ok()) {
1680 return Error() << "No session found with id " << session_id;
1681 }
1682
1683 switch (session->GetState()) {
1684 case SessionState::VERIFIED:
1685 [[clang::fallthrough]];
1686 case SessionState::STAGED:
1687 return session->DeleteSession();
1688 default:
1689 return Error() << "Session " << *session << " can't be aborted";
1690 }
1691 }
1692
1693 namespace {
1694
1695 enum ActivationMode { kBootstrapMode = 0, kBootMode, kOtaChrootMode, kVmMode };
1696
ActivateApexWorker(ActivationMode mode,std::queue<const ApexFile * > & apex_queue,std::mutex & mutex)1697 std::vector<Result<const ApexFile*>> ActivateApexWorker(
1698 ActivationMode mode, std::queue<const ApexFile*>& apex_queue,
1699 std::mutex& mutex) {
1700 ATRACE_NAME("ActivateApexWorker");
1701 std::vector<Result<const ApexFile*>> ret;
1702
1703 while (true) {
1704 const ApexFile* apex;
1705 {
1706 std::lock_guard lock(mutex);
1707 if (apex_queue.empty()) break;
1708 apex = apex_queue.front();
1709 apex_queue.pop();
1710 }
1711
1712 std::string device_name;
1713 if (mode == ActivationMode::kBootMode) {
1714 device_name = apex->GetManifest().name();
1715 } else {
1716 device_name = GetPackageId(apex->GetManifest());
1717 }
1718 if (mode == ActivationMode::kOtaChrootMode) {
1719 device_name += ".chroot";
1720 }
1721 bool reuse_device = mode == ActivationMode::kBootMode;
1722 auto res = ActivatePackageImpl(*apex, device_name, reuse_device);
1723 if (!res.ok()) {
1724 ret.push_back(Error() << "Failed to activate " << apex->GetPath() << "("
1725 << device_name << "): " << res.error());
1726 } else {
1727 ret.push_back({apex});
1728 }
1729 }
1730
1731 return ret;
1732 }
1733
ActivateApexPackages(const std::vector<ApexFileRef> & apexes,ActivationMode mode)1734 Result<void> ActivateApexPackages(const std::vector<ApexFileRef>& apexes,
1735 ActivationMode mode) {
1736 ATRACE_NAME("ActivateApexPackages");
1737 std::queue<const ApexFile*> apex_queue;
1738 std::mutex apex_queue_mutex;
1739
1740 for (const ApexFile& apex : apexes) {
1741 apex_queue.emplace(&apex);
1742 }
1743
1744 size_t worker_num =
1745 android::sysprop::ApexProperties::boot_activation_threads().value_or(0);
1746
1747 // Setting number of workers to the number of packages to load
1748 // This seems to provide the best performance
1749 if (worker_num == 0) {
1750 worker_num = apex_queue.size();
1751 }
1752 worker_num = std::min(apex_queue.size(), worker_num);
1753
1754 // On -eng builds there might be two different pre-installed art apexes.
1755 // Attempting to activate them in parallel will result in UB (e.g.
1756 // apexd-bootstrap might crash). In order to avoid this, for the time being on
1757 // -eng builds activate apexes sequentially.
1758 // TODO(b/176497601): remove this.
1759 if (GetProperty("ro.build.type", "") == "eng") {
1760 worker_num = 1;
1761 }
1762
1763 std::vector<std::future<std::vector<Result<const ApexFile*>>>> futures;
1764 futures.reserve(worker_num);
1765 for (size_t i = 0; i < worker_num; i++) {
1766 futures.push_back(std::async(std::launch::async, ActivateApexWorker,
1767 std::ref(mode), std::ref(apex_queue),
1768 std::ref(apex_queue_mutex)));
1769 }
1770
1771 size_t activated_cnt = 0;
1772 size_t failed_cnt = 0;
1773 std::string error_message;
1774 std::vector<const ApexFile*> activated_sharedlibs_apexes;
1775 for (size_t i = 0; i < futures.size(); i++) {
1776 for (const auto& res : futures[i].get()) {
1777 if (res.ok()) {
1778 ++activated_cnt;
1779 if (res.value()->GetManifest().providesharedapexlibs()) {
1780 activated_sharedlibs_apexes.push_back(res.value());
1781 }
1782 } else {
1783 ++failed_cnt;
1784 LOG(ERROR) << res.error();
1785 if (failed_cnt == 1) {
1786 error_message = res.error().message();
1787 }
1788 }
1789 }
1790 }
1791
1792 // We finished activation of APEX packages and now are ready to populate the
1793 // /apex/sharedlibs mount point. Since there can be multiple different APEXes
1794 // contributing to shared libs (at the point of writing this comment there can
1795 // be up 2 APEXes: pre-installed sharedlibs APEX and its updated counterpart)
1796 // we need to call ContributeToSharedLibs sequentially to avoid potential race
1797 // conditions. See b/240291921
1798 const auto& apex_repo = ApexFileRepository::GetInstance();
1799 // To make things simpler we also provide an order in which APEXes contribute
1800 // to sharedlibs.
1801 auto cmp = [&apex_repo](const auto& apex_a, const auto& apex_b) {
1802 // An APEX with higher version should contribute first
1803 if (apex_a->GetManifest().version() != apex_b->GetManifest().version()) {
1804 return apex_a->GetManifest().version() > apex_b->GetManifest().version();
1805 }
1806 // If they have the same version, then we pick the updated APEX first.
1807 return !apex_repo.IsPreInstalledApex(*apex_a);
1808 };
1809 std::sort(activated_sharedlibs_apexes.begin(),
1810 activated_sharedlibs_apexes.end(), cmp);
1811 for (const auto& sharedlibs_apex : activated_sharedlibs_apexes) {
1812 LOG(DEBUG) << "Populating sharedlibs with APEX "
1813 << sharedlibs_apex->GetPath() << " ( "
1814 << sharedlibs_apex->GetManifest().name()
1815 << " ) version : " << sharedlibs_apex->GetManifest().version();
1816 auto mount_point =
1817 apexd_private::GetPackageMountPoint(sharedlibs_apex->GetManifest());
1818 if (auto ret = ContributeToSharedLibs(mount_point); !ret.ok()) {
1819 LOG(ERROR) << "Failed to populate sharedlibs with APEX package "
1820 << sharedlibs_apex->GetPath() << " : " << ret.error();
1821 ++failed_cnt;
1822 if (failed_cnt == 1) {
1823 error_message = ret.error().message();
1824 }
1825 }
1826 }
1827
1828 if (failed_cnt > 0) {
1829 return Error() << "Failed to activate " << failed_cnt
1830 << " APEX packages. One of the errors: " << error_message;
1831 }
1832 LOG(INFO) << "Activated " << activated_cnt << " packages.";
1833 return {};
1834 }
1835
1836 // A fallback function in case some of the apexes failed to activate. For all
1837 // such apexes that were coming from /data partition we will attempt to activate
1838 // their corresponding pre-installed copies.
ActivateMissingApexes(const std::vector<ApexFileRef> & apexes,ActivationMode mode)1839 Result<void> ActivateMissingApexes(const std::vector<ApexFileRef>& apexes,
1840 ActivationMode mode) {
1841 LOG(INFO) << "Trying to activate pre-installed versions of missing apexes";
1842 const auto& file_repository = ApexFileRepository::GetInstance();
1843 const auto& activated_apexes = GetActivePackagesMap();
1844 std::vector<ApexFileRef> fallback_apexes;
1845 for (const auto& apex_ref : apexes) {
1846 const auto& apex = apex_ref.get();
1847 if (apex.GetManifest().providesharedapexlibs()) {
1848 // We must mount both versions of sharedlibs apex anyway. Not much we can
1849 // do here.
1850 continue;
1851 }
1852 if (file_repository.IsPreInstalledApex(apex)) {
1853 // We tried to activate pre-installed apex in the first place. No need to
1854 // try again.
1855 continue;
1856 }
1857 const std::string& name = apex.GetManifest().name();
1858 if (activated_apexes.find(name) == activated_apexes.end()) {
1859 fallback_apexes.push_back(file_repository.GetPreInstalledApex(name));
1860 }
1861 }
1862
1863 // Process compressed APEX, if any
1864 std::vector<ApexFileRef> compressed_apex;
1865 for (auto it = fallback_apexes.begin(); it != fallback_apexes.end();) {
1866 if (it->get().IsCompressed()) {
1867 compressed_apex.emplace_back(*it);
1868 it = fallback_apexes.erase(it);
1869 } else {
1870 it++;
1871 }
1872 }
1873 std::vector<ApexFile> decompressed_apex;
1874 if (!compressed_apex.empty()) {
1875 decompressed_apex = ProcessCompressedApex(
1876 compressed_apex,
1877 /* is_ota_chroot= */ mode == ActivationMode::kOtaChrootMode);
1878 for (const ApexFile& apex_file : decompressed_apex) {
1879 fallback_apexes.emplace_back(std::cref(apex_file));
1880 }
1881 }
1882 if (mode == kBootMode) {
1883 // Treat fallback to pre-installed APEXes as a change of the acitve APEX,
1884 // since we are already in a pretty dire situation, so it's better if we
1885 // drop all the caches.
1886 for (const auto& apex : fallback_apexes) {
1887 gChangedActiveApexes.insert(apex.get().GetManifest().name());
1888 }
1889 }
1890 return ActivateApexPackages(fallback_apexes, mode);
1891 }
1892
1893 } // namespace
1894
1895 /**
1896 * Snapshots data from base_dir/apexdata/<apex name> to
1897 * base_dir/apexrollback/<rollback id>/<apex name>.
1898 */
SnapshotDataDirectory(const std::string & base_dir,const int rollback_id,const std::string & apex_name,bool pre_restore=false)1899 Result<void> SnapshotDataDirectory(const std::string& base_dir,
1900 const int rollback_id,
1901 const std::string& apex_name,
1902 bool pre_restore = false) {
1903 auto rollback_path =
1904 StringPrintf("%s/%s/%d%s", base_dir.c_str(), kApexSnapshotSubDir,
1905 rollback_id, pre_restore ? kPreRestoreSuffix : "");
1906 const Result<void> result = CreateDirIfNeeded(rollback_path, 0700);
1907 if (!result.ok()) {
1908 return Error() << "Failed to create snapshot directory for rollback "
1909 << rollback_id << " : " << result.error();
1910 }
1911 auto from_path = StringPrintf("%s/%s/%s", base_dir.c_str(), kApexDataSubDir,
1912 apex_name.c_str());
1913 auto to_path =
1914 StringPrintf("%s/%s", rollback_path.c_str(), apex_name.c_str());
1915
1916 return ReplaceFiles(from_path, to_path);
1917 }
1918
1919 /**
1920 * Restores snapshot from base_dir/apexrollback/<rollback id>/<apex name>
1921 * to base_dir/apexdata/<apex name>.
1922 * Note the snapshot will be deleted after restoration succeeded.
1923 */
RestoreDataDirectory(const std::string & base_dir,const int rollback_id,const std::string & apex_name,bool pre_restore=false)1924 Result<void> RestoreDataDirectory(const std::string& base_dir,
1925 const int rollback_id,
1926 const std::string& apex_name,
1927 bool pre_restore = false) {
1928 auto from_path = StringPrintf(
1929 "%s/%s/%d%s/%s", base_dir.c_str(), kApexSnapshotSubDir, rollback_id,
1930 pre_restore ? kPreRestoreSuffix : "", apex_name.c_str());
1931 auto to_path = StringPrintf("%s/%s/%s", base_dir.c_str(), kApexDataSubDir,
1932 apex_name.c_str());
1933 Result<void> result = ReplaceFiles(from_path, to_path);
1934 if (!result.ok()) {
1935 return result;
1936 }
1937 result = RestoreconPath(to_path);
1938 if (!result.ok()) {
1939 return result;
1940 }
1941 result = DeleteDir(from_path);
1942 if (!result.ok()) {
1943 LOG(ERROR) << "Failed to delete the snapshot: " << result.error();
1944 }
1945 return {};
1946 }
1947
SnapshotOrRestoreDeIfNeeded(const std::string & base_dir,const ApexSession & session)1948 void SnapshotOrRestoreDeIfNeeded(const std::string& base_dir,
1949 const ApexSession& session) {
1950 if (session.HasRollbackEnabled()) {
1951 for (const auto& apex_name : session.GetApexNames()) {
1952 Result<void> result =
1953 SnapshotDataDirectory(base_dir, session.GetRollbackId(), apex_name);
1954 if (!result.ok()) {
1955 LOG(ERROR) << "Snapshot failed for " << apex_name << ": "
1956 << result.error();
1957 }
1958 }
1959 } else if (session.IsRollback()) {
1960 for (const auto& apex_name : session.GetApexNames()) {
1961 if (!gSupportsFsCheckpoints) {
1962 // Snapshot before restore so this rollback can be reverted.
1963 SnapshotDataDirectory(base_dir, session.GetRollbackId(), apex_name,
1964 true /* pre_restore */);
1965 }
1966 Result<void> result =
1967 RestoreDataDirectory(base_dir, session.GetRollbackId(), apex_name);
1968 if (!result.ok()) {
1969 LOG(ERROR) << "Restore of data failed for " << apex_name << ": "
1970 << result.error();
1971 }
1972 }
1973 }
1974 }
1975
SnapshotOrRestoreDeSysData()1976 void SnapshotOrRestoreDeSysData() {
1977 auto sessions = gSessionManager->GetSessionsInState(SessionState::ACTIVATED);
1978
1979 for (const ApexSession& session : sessions) {
1980 SnapshotOrRestoreDeIfNeeded(kDeSysDataDir, session);
1981 }
1982 }
1983
SnapshotOrRestoreDeUserData()1984 int SnapshotOrRestoreDeUserData() {
1985 auto user_dirs = GetDeUserDirs();
1986
1987 if (!user_dirs.ok()) {
1988 LOG(ERROR) << "Error reading dirs " << user_dirs.error();
1989 return 1;
1990 }
1991
1992 auto sessions = gSessionManager->GetSessionsInState(SessionState::ACTIVATED);
1993
1994 for (const ApexSession& session : sessions) {
1995 for (const auto& user_dir : *user_dirs) {
1996 SnapshotOrRestoreDeIfNeeded(user_dir, session);
1997 }
1998 }
1999
2000 return 0;
2001 }
2002
SnapshotCeData(const int user_id,const int rollback_id,const std::string & apex_name)2003 Result<void> SnapshotCeData(const int user_id, const int rollback_id,
2004 const std::string& apex_name) {
2005 auto base_dir = StringPrintf("%s/%d", kCeDataDir, user_id);
2006 return SnapshotDataDirectory(base_dir, rollback_id, apex_name);
2007 }
2008
RestoreCeData(const int user_id,const int rollback_id,const std::string & apex_name)2009 Result<void> RestoreCeData(const int user_id, const int rollback_id,
2010 const std::string& apex_name) {
2011 auto base_dir = StringPrintf("%s/%d", kCeDataDir, user_id);
2012 return RestoreDataDirectory(base_dir, rollback_id, apex_name);
2013 }
2014
DestroySnapshots(const std::string & base_dir,const int rollback_id)2015 Result<void> DestroySnapshots(const std::string& base_dir,
2016 const int rollback_id) {
2017 auto path = StringPrintf("%s/%s/%d", base_dir.c_str(), kApexSnapshotSubDir,
2018 rollback_id);
2019 return DeleteDir(path);
2020 }
2021
DestroyDeSnapshots(const int rollback_id)2022 Result<void> DestroyDeSnapshots(const int rollback_id) {
2023 DestroySnapshots(kDeSysDataDir, rollback_id);
2024
2025 auto user_dirs = GetDeUserDirs();
2026 if (!user_dirs.ok()) {
2027 return Error() << "Error reading user dirs " << user_dirs.error();
2028 }
2029
2030 for (const auto& user_dir : *user_dirs) {
2031 DestroySnapshots(user_dir, rollback_id);
2032 }
2033
2034 return {};
2035 }
2036
DestroyCeSnapshots(const int user_id,const int rollback_id)2037 Result<void> DestroyCeSnapshots(const int user_id, const int rollback_id) {
2038 auto path = StringPrintf("%s/%d/%s/%d", kCeDataDir, user_id,
2039 kApexSnapshotSubDir, rollback_id);
2040 return DeleteDir(path);
2041 }
2042
2043 /**
2044 * Deletes all credential-encrypted snapshots for the given user, except for
2045 * those listed in retain_rollback_ids.
2046 */
DestroyCeSnapshotsNotSpecified(int user_id,const std::vector<int> & retain_rollback_ids)2047 Result<void> DestroyCeSnapshotsNotSpecified(
2048 int user_id, const std::vector<int>& retain_rollback_ids) {
2049 auto snapshot_root =
2050 StringPrintf("%s/%d/%s", kCeDataDir, user_id, kApexSnapshotSubDir);
2051 auto snapshot_dirs = GetSubdirs(snapshot_root);
2052 if (!snapshot_dirs.ok()) {
2053 return Error() << "Error reading snapshot dirs " << snapshot_dirs.error();
2054 }
2055
2056 for (const auto& snapshot_dir : *snapshot_dirs) {
2057 uint snapshot_id;
2058 bool parse_ok = ParseUint(
2059 std::filesystem::path(snapshot_dir).filename().c_str(), &snapshot_id);
2060 if (parse_ok &&
2061 std::find(retain_rollback_ids.begin(), retain_rollback_ids.end(),
2062 snapshot_id) == retain_rollback_ids.end()) {
2063 Result<void> result = DeleteDir(snapshot_dir);
2064 if (!result.ok()) {
2065 return Error() << "Destroy CE snapshot failed for " << snapshot_dir
2066 << " : " << result.error();
2067 }
2068 }
2069 }
2070 return {};
2071 }
2072
RestorePreRestoreSnapshotsIfPresent(const std::string & base_dir,const ApexSession & session)2073 void RestorePreRestoreSnapshotsIfPresent(const std::string& base_dir,
2074 const ApexSession& session) {
2075 auto pre_restore_snapshot_path =
2076 StringPrintf("%s/%s/%d%s", base_dir.c_str(), kApexSnapshotSubDir,
2077 session.GetRollbackId(), kPreRestoreSuffix);
2078 if (PathExists(pre_restore_snapshot_path).ok()) {
2079 for (const auto& apex_name : session.GetApexNames()) {
2080 Result<void> result = RestoreDataDirectory(
2081 base_dir, session.GetRollbackId(), apex_name, true /* pre_restore */);
2082 if (!result.ok()) {
2083 LOG(ERROR) << "Restore of pre-restore snapshot failed for " << apex_name
2084 << ": " << result.error();
2085 }
2086 }
2087 }
2088 }
2089
RestoreDePreRestoreSnapshotsIfPresent(const ApexSession & session)2090 void RestoreDePreRestoreSnapshotsIfPresent(const ApexSession& session) {
2091 RestorePreRestoreSnapshotsIfPresent(kDeSysDataDir, session);
2092
2093 auto user_dirs = GetDeUserDirs();
2094 if (!user_dirs.ok()) {
2095 LOG(ERROR) << "Error reading user dirs to restore pre-restore snapshots"
2096 << user_dirs.error();
2097 }
2098
2099 for (const auto& user_dir : *user_dirs) {
2100 RestorePreRestoreSnapshotsIfPresent(user_dir, session);
2101 }
2102 }
2103
DeleteDePreRestoreSnapshots(const std::string & base_dir,const ApexSession & session)2104 void DeleteDePreRestoreSnapshots(const std::string& base_dir,
2105 const ApexSession& session) {
2106 auto pre_restore_snapshot_path =
2107 StringPrintf("%s/%s/%d%s", base_dir.c_str(), kApexSnapshotSubDir,
2108 session.GetRollbackId(), kPreRestoreSuffix);
2109 Result<void> result = DeleteDir(pre_restore_snapshot_path);
2110 if (!result.ok()) {
2111 LOG(ERROR) << "Deletion of pre-restore snapshot failed: " << result.error();
2112 }
2113 }
2114
DeleteDePreRestoreSnapshots(const ApexSession & session)2115 void DeleteDePreRestoreSnapshots(const ApexSession& session) {
2116 DeleteDePreRestoreSnapshots(kDeSysDataDir, session);
2117
2118 auto user_dirs = GetDeUserDirs();
2119 if (!user_dirs.ok()) {
2120 LOG(ERROR) << "Error reading user dirs to delete pre-restore snapshots"
2121 << user_dirs.error();
2122 }
2123
2124 for (const auto& user_dir : *user_dirs) {
2125 DeleteDePreRestoreSnapshots(user_dir, session);
2126 }
2127 }
2128
OnBootCompleted()2129 void OnBootCompleted() {
2130 ApexdLifecycle::GetInstance().MarkBootCompleted();
2131 }
2132
2133 // Returns true if any session gets staged
ScanStagedSessionsDirAndStage()2134 void ScanStagedSessionsDirAndStage() {
2135 LOG(INFO) << "Scanning " << GetSessionsDir()
2136 << " looking for sessions to be activated.";
2137
2138 auto sessions_to_activate =
2139 gSessionManager->GetSessionsInState(SessionState::STAGED);
2140 if (gSupportsFsCheckpoints) {
2141 // A session that is in the ACTIVATED state should still be re-activated if
2142 // fs checkpointing is supported. In this case, a session may be in the
2143 // ACTIVATED state yet the data/apex/active directory may have been
2144 // reverted. The session should be reverted in this scenario.
2145 auto activated_sessions =
2146 gSessionManager->GetSessionsInState(SessionState::ACTIVATED);
2147 sessions_to_activate.insert(sessions_to_activate.end(),
2148 activated_sessions.begin(),
2149 activated_sessions.end());
2150 }
2151
2152 for (auto& session : sessions_to_activate) {
2153 auto session_id = session.GetId();
2154
2155 auto session_failed_fn = [&]() {
2156 LOG(WARNING) << "Marking session " << session_id << " as failed.";
2157 auto st = session.UpdateStateAndCommit(SessionState::ACTIVATION_FAILED);
2158 if (!st.ok()) {
2159 LOG(WARNING) << "Failed to mark session " << session_id
2160 << " as failed : " << st.error();
2161 }
2162 };
2163 auto scope_guard = android::base::make_scope_guard(session_failed_fn);
2164
2165 std::string build_fingerprint = GetProperty(kBuildFingerprintSysprop, "");
2166 if (session.GetBuildFingerprint().compare(build_fingerprint) != 0) {
2167 auto error_message = "APEX build fingerprint has changed";
2168 LOG(ERROR) << error_message;
2169 session.SetErrorMessage(error_message);
2170 continue;
2171 }
2172
2173 // If device supports fs-checkpoint, then apex session should only be
2174 // installed when in checkpoint-mode. Otherwise, we will not be able to
2175 // revert /data on error.
2176 if (gSupportsFsCheckpoints && !gInFsCheckpointMode) {
2177 auto error_message =
2178 "Cannot install apex session if not in fs-checkpoint mode";
2179 LOG(ERROR) << error_message;
2180 session.SetErrorMessage(error_message);
2181 continue;
2182 }
2183
2184 std::vector<std::string> dirs_to_scan =
2185 session.GetStagedApexDirs(gConfig->staged_session_dir);
2186
2187 std::vector<std::string> apexes;
2188 bool scan_successful = true;
2189 for (const auto& dir_to_scan : dirs_to_scan) {
2190 Result<std::vector<std::string>> scan =
2191 FindFilesBySuffix(dir_to_scan, {kApexPackageSuffix});
2192 if (!scan.ok()) {
2193 LOG(WARNING) << scan.error();
2194 session.SetErrorMessage(scan.error().message());
2195 scan_successful = false;
2196 break;
2197 }
2198
2199 if (scan->size() > 1) {
2200 std::string error_message = StringPrintf(
2201 "More than one APEX package found in the same session directory %s "
2202 ", skipping activation",
2203 dir_to_scan.c_str());
2204 LOG(WARNING) << error_message;
2205 session.SetErrorMessage(error_message);
2206 scan_successful = false;
2207 break;
2208 }
2209
2210 if (scan->empty()) {
2211 std::string error_message = StringPrintf(
2212 "No APEX packages found while scanning %s session id: %d.",
2213 dir_to_scan.c_str(), session_id);
2214 LOG(WARNING) << error_message;
2215 session.SetErrorMessage(error_message);
2216 scan_successful = false;
2217 break;
2218 }
2219 apexes.push_back(std::move((*scan)[0]));
2220 }
2221
2222 if (!scan_successful) {
2223 continue;
2224 }
2225
2226 std::vector<std::string> staged_apex_names;
2227 for (const auto& apex : apexes) {
2228 // TODO(b/158470836): Avoid opening ApexFile repeatedly.
2229 Result<ApexFile> apex_file = ApexFile::Open(apex);
2230 if (!apex_file.ok()) {
2231 LOG(ERROR) << "Cannot open apex file during staging: " << apex;
2232 continue;
2233 }
2234 staged_apex_names.push_back(apex_file->GetManifest().name());
2235 }
2236
2237 const Result<void> result = StagePackages(apexes);
2238 if (!result.ok()) {
2239 std::string error_message = StringPrintf(
2240 "Activation failed for packages %s : %s", Join(apexes, ',').c_str(),
2241 result.error().message().c_str());
2242 LOG(ERROR) << error_message;
2243 session.SetErrorMessage(error_message);
2244 continue;
2245 }
2246
2247 // Session was OK, release scopeguard.
2248 scope_guard.Disable();
2249
2250 for (const std::string& apex : staged_apex_names) {
2251 gChangedActiveApexes.insert(apex);
2252 }
2253
2254 auto st = session.UpdateStateAndCommit(SessionState::ACTIVATED);
2255 if (!st.ok()) {
2256 LOG(ERROR) << "Failed to mark " << session
2257 << " as activated : " << st.error();
2258 }
2259 }
2260 }
2261
2262 namespace {
StageDestPath(const ApexFile & apex_file)2263 std::string StageDestPath(const ApexFile& apex_file) {
2264 return StringPrintf("%s/%s%s", gConfig->active_apex_data_dir,
2265 GetPackageId(apex_file.GetManifest()).c_str(),
2266 kApexPackageSuffix);
2267 }
2268
2269 } // namespace
2270
StagePackages(const std::vector<std::string> & tmp_paths)2271 Result<void> StagePackages(const std::vector<std::string>& tmp_paths) {
2272 if (tmp_paths.empty()) {
2273 return Errorf("Empty set of inputs");
2274 }
2275 LOG(DEBUG) << "StagePackages() for " << Join(tmp_paths, ',');
2276
2277 // Note: this function is temporary. As such the code is not optimized, e.g.,
2278 // it will open ApexFiles multiple times.
2279
2280 // 1) Verify all packages.
2281 Result<std::vector<ApexFile>> apex_files = OpenApexFiles(tmp_paths);
2282 if (!apex_files.ok()) {
2283 return apex_files.error();
2284 }
2285 for (const ApexFile& apex_file : *apex_files) {
2286 if (shim::IsShimApex(apex_file)) {
2287 // Shim apex will be validated on every boot. No need to do it here.
2288 continue;
2289 }
2290 Result<void> result = VerifyPackageBoot(apex_file);
2291 if (!result.ok()) {
2292 return result.error();
2293 }
2294 }
2295
2296 // Make sure that kActiveApexPackagesDataDir exists.
2297 auto create_dir_status =
2298 CreateDirIfNeeded(std::string(gConfig->active_apex_data_dir), 0755);
2299 if (!create_dir_status.ok()) {
2300 return create_dir_status.error();
2301 }
2302
2303 // 2) Now stage all of them.
2304
2305 // Ensure the APEX gets removed on failure.
2306 std::unordered_set<std::string> staged_files;
2307 std::vector<std::string> changed_hashtree_files;
2308 auto deleter = [&staged_files, &changed_hashtree_files]() {
2309 for (const std::string& staged_path : staged_files) {
2310 if (TEMP_FAILURE_RETRY(unlink(staged_path.c_str())) != 0) {
2311 PLOG(ERROR) << "Unable to unlink " << staged_path;
2312 }
2313 }
2314 for (const std::string& hashtree_file : changed_hashtree_files) {
2315 if (TEMP_FAILURE_RETRY(unlink(hashtree_file.c_str())) != 0) {
2316 PLOG(ERROR) << "Unable to unlink " << hashtree_file;
2317 }
2318 }
2319 };
2320 auto scope_guard = android::base::make_scope_guard(deleter);
2321
2322 std::unordered_set<std::string> staged_packages;
2323 for (const ApexFile& apex_file : *apex_files) {
2324 // First promote new hashtree file to the one that will be used when
2325 // mounting apex.
2326 std::string new_hashtree_file = GetHashTreeFileName(apex_file,
2327 /* is_new = */ true);
2328 std::string old_hashtree_file = GetHashTreeFileName(apex_file,
2329 /* is_new = */ false);
2330 if (access(new_hashtree_file.c_str(), F_OK) == 0) {
2331 if (TEMP_FAILURE_RETRY(rename(new_hashtree_file.c_str(),
2332 old_hashtree_file.c_str())) != 0) {
2333 return ErrnoError() << "Failed to move " << new_hashtree_file << " to "
2334 << old_hashtree_file;
2335 }
2336 changed_hashtree_files.emplace_back(std::move(old_hashtree_file));
2337 }
2338 // And only then move apex to /data/apex/active.
2339 std::string dest_path = StageDestPath(apex_file);
2340 if (access(dest_path.c_str(), F_OK) == 0) {
2341 LOG(DEBUG) << dest_path << " already exists. Deleting";
2342 if (TEMP_FAILURE_RETRY(unlink(dest_path.c_str())) != 0) {
2343 return ErrnoError() << "Failed to unlink " << dest_path;
2344 }
2345 }
2346
2347 if (link(apex_file.GetPath().c_str(), dest_path.c_str()) != 0) {
2348 return ErrnoError() << "Unable to link " << apex_file.GetPath() << " to "
2349 << dest_path;
2350 }
2351 staged_files.insert(dest_path);
2352 staged_packages.insert(apex_file.GetManifest().name());
2353
2354 LOG(DEBUG) << "Success linking " << apex_file.GetPath() << " to "
2355 << dest_path;
2356 }
2357
2358 scope_guard.Disable(); // Accept the state.
2359
2360 return RemovePreviouslyActiveApexFiles(staged_packages, staged_files);
2361 }
2362
UnstagePackages(const std::vector<std::string> & paths)2363 Result<void> UnstagePackages(const std::vector<std::string>& paths) {
2364 if (paths.empty()) {
2365 return Errorf("Empty set of inputs");
2366 }
2367 LOG(DEBUG) << "UnstagePackages() for " << Join(paths, ',');
2368
2369 for (const std::string& path : paths) {
2370 auto apex = ApexFile::Open(path);
2371 if (!apex.ok()) {
2372 return apex.error();
2373 }
2374 if (ApexFileRepository::GetInstance().IsPreInstalledApex(*apex)) {
2375 return Error() << "Can't uninstall pre-installed apex " << path;
2376 }
2377 }
2378
2379 for (const std::string& path : paths) {
2380 if (unlink(path.c_str()) != 0) {
2381 return ErrnoError() << "Can't unlink " << path;
2382 }
2383 }
2384
2385 return {};
2386 }
2387
2388 /**
2389 * During apex installation, staged sessions located in /data/apex/sessions
2390 * mutate the active sessions in /data/apex/active. If some error occurs during
2391 * installation of apex, we need to revert /data/apex/active to its original
2392 * state and reboot.
2393 *
2394 * Also, we need to put staged sessions in /data/apex/sessions in REVERTED state
2395 * so that they do not get activated on next reboot.
2396 */
RevertActiveSessions(const std::string & crashing_native_process,const std::string & error_message)2397 Result<void> RevertActiveSessions(const std::string& crashing_native_process,
2398 const std::string& error_message) {
2399 // First check whenever there is anything to revert. If there is none, then
2400 // fail. This prevents apexd from boot looping a device in case a native
2401 // process is crashing and there are no apex updates.
2402 auto active_sessions = gSessionManager->GetSessions();
2403 active_sessions.erase(
2404 std::remove_if(active_sessions.begin(), active_sessions.end(),
2405 [](const auto& s) {
2406 return s.IsFinalized() ||
2407 s.GetState() == SessionState::UNKNOWN;
2408 }),
2409 active_sessions.end());
2410 if (active_sessions.empty()) {
2411 return Error() << "Revert requested, when there are no active sessions.";
2412 }
2413
2414 for (auto& session : active_sessions) {
2415 if (!crashing_native_process.empty()) {
2416 session.SetCrashingNativeProcess(crashing_native_process);
2417 }
2418 if (!error_message.empty()) {
2419 session.SetErrorMessage(error_message);
2420 }
2421 auto status =
2422 session.UpdateStateAndCommit(SessionState::REVERT_IN_PROGRESS);
2423 if (!status.ok()) {
2424 return Error() << "Revert of session " << session
2425 << " failed : " << status.error();
2426 }
2427 }
2428
2429 if (!gSupportsFsCheckpoints) {
2430 auto restore_status = RestoreActivePackages();
2431 if (!restore_status.ok()) {
2432 for (auto& session : active_sessions) {
2433 auto st = session.UpdateStateAndCommit(SessionState::REVERT_FAILED);
2434 LOG(DEBUG) << "Marking " << session << " as failed to revert";
2435 if (!st.ok()) {
2436 LOG(WARNING) << "Failed to mark session " << session
2437 << " as failed to revert : " << st.error();
2438 }
2439 }
2440 return restore_status;
2441 }
2442 } else {
2443 LOG(INFO) << "Not restoring active packages in checkpoint mode.";
2444 }
2445
2446 for (auto& session : active_sessions) {
2447 if (!gSupportsFsCheckpoints && session.IsRollback()) {
2448 // If snapshots have already been restored, undo that by restoring the
2449 // pre-restore snapshot.
2450 RestoreDePreRestoreSnapshotsIfPresent(session);
2451 }
2452
2453 auto status = session.UpdateStateAndCommit(SessionState::REVERTED);
2454 if (!status.ok()) {
2455 LOG(WARNING) << "Failed to mark session " << session
2456 << " as reverted : " << status.error();
2457 }
2458 }
2459
2460 return {};
2461 }
2462
RevertActiveSessionsAndReboot(const std::string & crashing_native_process,const std::string & error_message)2463 Result<void> RevertActiveSessionsAndReboot(
2464 const std::string& crashing_native_process,
2465 const std::string& error_message) {
2466 auto status = RevertActiveSessions(crashing_native_process, error_message);
2467 if (!status.ok()) {
2468 return status;
2469 }
2470 LOG(ERROR) << "Successfully reverted. Time to reboot device.";
2471 if (gInFsCheckpointMode) {
2472 Result<void> res = gVoldService->AbortChanges(
2473 "apexd_initiated" /* message */, false /* retry */);
2474 if (!res.ok()) {
2475 LOG(ERROR) << res.error();
2476 }
2477 }
2478 Reboot();
2479 return {};
2480 }
2481
CreateSharedLibsApexDir()2482 Result<void> CreateSharedLibsApexDir() {
2483 // Creates /apex/sharedlibs/lib{,64} for SharedLibs APEXes.
2484 std::string shared_libs_sub_dir =
2485 StringPrintf("%s/%s", kApexRoot, kApexSharedLibsSubDir);
2486 auto dir_exists = PathExists(shared_libs_sub_dir);
2487 if (!dir_exists.ok() || !*dir_exists) {
2488 std::error_code error_code;
2489 std::filesystem::create_directory(shared_libs_sub_dir, error_code);
2490 if (error_code) {
2491 return Error() << "Failed to create directory " << shared_libs_sub_dir
2492 << ": " << error_code.message();
2493 }
2494 }
2495 for (const auto& lib_path : {"lib", "lib64"}) {
2496 std::string apex_lib_path =
2497 StringPrintf("%s/%s", shared_libs_sub_dir.c_str(), lib_path);
2498 auto lib_dir_exists = PathExists(apex_lib_path);
2499 if (!lib_dir_exists.ok() || !*lib_dir_exists) {
2500 std::error_code error_code;
2501 std::filesystem::create_directory(apex_lib_path, error_code);
2502 if (error_code) {
2503 return Error() << "Failed to create directory " << apex_lib_path << ": "
2504 << error_code.message();
2505 }
2506 }
2507 }
2508
2509 return {};
2510 }
2511
OnBootstrap()2512 int OnBootstrap() {
2513 ATRACE_NAME("OnBootstrap");
2514 auto time_started = boot_clock::now();
2515
2516 ApexFileRepository& instance = ApexFileRepository::GetInstance();
2517 Result<void> status =
2518 instance.AddPreInstalledApex(gConfig->apex_built_in_dirs);
2519 if (!status.ok()) {
2520 LOG(ERROR) << "Failed to collect APEX keys : " << status.error();
2521 return 1;
2522 }
2523
2524 const auto& pre_installed_apexes = instance.GetPreInstalledApexFiles();
2525 int loop_device_cnt = pre_installed_apexes.size();
2526 // Find all bootstrap apexes
2527 std::vector<ApexFileRef> bootstrap_apexes;
2528 for (const auto& apex : pre_installed_apexes) {
2529 if (IsBootstrapApex(apex.get())) {
2530 LOG(INFO) << "Found bootstrap APEX " << apex.get().GetPath();
2531 bootstrap_apexes.push_back(apex);
2532 loop_device_cnt++;
2533 }
2534 if (apex.get().GetManifest().providesharedapexlibs()) {
2535 LOG(INFO) << "Found sharedlibs APEX " << apex.get().GetPath();
2536 // Sharedlis APEX might be mounted 2 times:
2537 // * Pre-installed sharedlibs APEX will be mounted in OnStart
2538 // * Updated sharedlibs APEX (if it exists) will be mounted in OnStart
2539 //
2540 // We already counted a loop device for one of these 2 mounts, need to add
2541 // 1 more.
2542 loop_device_cnt++;
2543 }
2544 }
2545 LOG(INFO) << "Need to pre-allocate " << loop_device_cnt
2546 << " loop devices for " << pre_installed_apexes.size()
2547 << " APEX packages";
2548 // TODO(b/209491448) Remove this.
2549 auto block_count = AddBlockApex(instance);
2550 if (!block_count.ok()) {
2551 LOG(ERROR) << status.error();
2552 return 1;
2553 }
2554 if (*block_count > 0) {
2555 LOG(INFO) << "Also need to pre-allocate " << *block_count
2556 << " loop devices for block APEXes";
2557 loop_device_cnt += *block_count;
2558 }
2559 if (auto res = loop::PreAllocateLoopDevices(loop_device_cnt); !res.ok()) {
2560 LOG(ERROR) << "Failed to pre-allocate loop devices : " << res.error();
2561 }
2562
2563 DeviceMapper& dm = DeviceMapper::Instance();
2564 // Create empty dm device for each found APEX.
2565 // This is a boot time optimization that makes use of the fact that user space
2566 // paths will be created by ueventd before apexd is started, and hence
2567 // reducing the time to activate APEXEs on /data.
2568 // Note: since at this point we don't know which APEXes are updated, we are
2569 // optimistically creating a verity device for all of them. Once boot
2570 // finishes, apexd will clean up unused devices.
2571 // TODO(b/192241176): move to apexd_verity.{h,cpp}
2572 for (const auto& apex : pre_installed_apexes) {
2573 const std::string& name = apex.get().GetManifest().name();
2574 if (!dm.CreatePlaceholderDevice(name)) {
2575 LOG(ERROR) << "Failed to create empty device " << name;
2576 }
2577 }
2578
2579 // Create directories for APEX shared libraries.
2580 auto sharedlibs_apex_dir = CreateSharedLibsApexDir();
2581 if (!sharedlibs_apex_dir.ok()) {
2582 LOG(ERROR) << sharedlibs_apex_dir.error();
2583 return 1;
2584 }
2585
2586 // Now activate bootstrap apexes.
2587 auto ret =
2588 ActivateApexPackages(bootstrap_apexes, ActivationMode::kBootstrapMode);
2589 if (!ret.ok()) {
2590 LOG(ERROR) << "Failed to activate bootstrap apex files : " << ret.error();
2591 return 1;
2592 }
2593
2594 OnAllPackagesActivated(/*is_bootstrap=*/true);
2595 auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
2596 boot_clock::now() - time_started).count();
2597 LOG(INFO) << "OnBootstrap done, duration=" << time_elapsed;
2598 return 0;
2599 }
2600
RemountApexFile(const std::string & path)2601 Result<void> RemountApexFile(const std::string& path) {
2602 if (auto ret = DeactivatePackage(path); !ret.ok()) {
2603 return ret;
2604 }
2605 return ActivatePackage(path);
2606 }
2607
InitializeVold(CheckpointInterface * checkpoint_service)2608 void InitializeVold(CheckpointInterface* checkpoint_service) {
2609 if (checkpoint_service != nullptr) {
2610 gVoldService = checkpoint_service;
2611 Result<bool> supports_fs_checkpoints =
2612 gVoldService->SupportsFsCheckpoints();
2613 if (supports_fs_checkpoints.ok()) {
2614 gSupportsFsCheckpoints = *supports_fs_checkpoints;
2615 } else {
2616 LOG(ERROR) << "Failed to check if filesystem checkpoints are supported: "
2617 << supports_fs_checkpoints.error();
2618 }
2619 if (gSupportsFsCheckpoints) {
2620 Result<bool> needs_checkpoint = gVoldService->NeedsCheckpoint();
2621 if (needs_checkpoint.ok()) {
2622 gInFsCheckpointMode = *needs_checkpoint;
2623 } else {
2624 LOG(ERROR) << "Failed to check if we're in filesystem checkpoint mode: "
2625 << needs_checkpoint.error();
2626 }
2627 }
2628 }
2629 }
2630
InitializeSessionManager(ApexSessionManager * session_manager)2631 void InitializeSessionManager(ApexSessionManager* session_manager) {
2632 gSessionManager = session_manager;
2633 }
2634
Initialize(CheckpointInterface * checkpoint_service)2635 void Initialize(CheckpointInterface* checkpoint_service) {
2636 InitializeVold(checkpoint_service);
2637 ApexFileRepository& instance = ApexFileRepository::GetInstance();
2638 Result<void> status = instance.AddPreInstalledApex(kApexPackageBuiltinDirs);
2639 if (!status.ok()) {
2640 LOG(ERROR) << "Failed to collect pre-installed APEX files : "
2641 << status.error();
2642 return;
2643 }
2644
2645 // TODO(b/209491448) Remove this.
2646 if (auto block_status = AddBlockApex(instance); !block_status.ok()) {
2647 LOG(ERROR) << status.error();
2648 return;
2649 }
2650
2651 gMountedApexes.PopulateFromMounts(
2652 {gConfig->active_apex_data_dir, gConfig->decompression_dir},
2653 gConfig->apex_hash_tree_dir);
2654 }
2655
2656 // Note: Pre-installed apex are initialized in Initialize(CheckpointInterface*)
2657 // TODO(b/172911822): Consolidate this with Initialize() when
2658 // ApexFileRepository can act as cache and re-scanning is not expensive
InitializeDataApex()2659 void InitializeDataApex() {
2660 ApexFileRepository& instance = ApexFileRepository::GetInstance();
2661 Result<void> status = instance.AddDataApex(kActiveApexPackagesDataDir);
2662 if (!status.ok()) {
2663 LOG(ERROR) << "Failed to collect data APEX files : " << status.error();
2664 return;
2665 }
2666 }
2667
2668 /**
2669 * For every package X, there can be at most two APEX, pre-installed vs
2670 * installed on data. We usually select only one of these APEX for each package
2671 * based on the following conditions:
2672 * - Package X must be pre-installed on one of the built-in directories.
2673 * - If there are multiple APEX, we select the one with highest version.
2674 * - If there are multiple with same version, we give priority to APEX on
2675 * /data partition.
2676 *
2677 * Typically, only one APEX is activated for each package, but APEX that provide
2678 * shared libs are exceptions. We have to activate both APEX for them.
2679 *
2680 * @param all_apex all the APEX grouped by their package name
2681 * @return list of ApexFile that needs to be activated
2682 */
SelectApexForActivation(const std::unordered_map<std::string,std::vector<ApexFileRef>> & all_apex,const ApexFileRepository & instance)2683 std::vector<ApexFileRef> SelectApexForActivation(
2684 const std::unordered_map<std::string, std::vector<ApexFileRef>>& all_apex,
2685 const ApexFileRepository& instance) {
2686 LOG(INFO) << "Selecting APEX for activation";
2687 std::vector<ApexFileRef> activation_list;
2688 // For every package X, select which APEX to activate
2689 for (auto& apex_it : all_apex) {
2690 const std::string& package_name = apex_it.first;
2691 const std::vector<ApexFileRef>& apex_files = apex_it.second;
2692
2693 if (apex_files.size() > 2 || apex_files.size() == 0) {
2694 LOG(FATAL) << "Unexpectedly found more than two versions or none for "
2695 "APEX package "
2696 << package_name;
2697 continue;
2698 }
2699
2700 // The package must have a pre-installed version before we consider it for
2701 // activation
2702 if (!instance.HasPreInstalledVersion(package_name)) {
2703 LOG(INFO) << "Package " << package_name << " is not pre-installed";
2704 continue;
2705 }
2706
2707 if (apex_files.size() == 1) {
2708 LOG(DEBUG) << "Selecting the only APEX: " << package_name << " "
2709 << apex_files[0].get().GetPath();
2710 activation_list.emplace_back(apex_files[0]);
2711 continue;
2712 }
2713
2714 // TODO(b/179497746): Now that we are dealing with list of reference, this
2715 // selection process can be simplified by sorting the vector.
2716
2717 // Given an APEX A and the version of the other APEX B, should we activate
2718 // it?
2719 auto select_apex = [&instance, &activation_list](
2720 const ApexFileRef& a_ref,
2721 const int version_b) mutable {
2722 const ApexFile& a = a_ref.get();
2723 // If A has higher version than B, then it should be activated
2724 const bool higher_version = a.GetManifest().version() > version_b;
2725 // If A has same version as B, then data version should get activated
2726 const bool same_version_priority_to_data =
2727 a.GetManifest().version() == version_b &&
2728 !instance.IsPreInstalledApex(a);
2729
2730 // APEX that provides shared library are special:
2731 // - if preinstalled version is lower than data version, both versions
2732 // are activated.
2733 // - if preinstalled version is equal to data version, data version only
2734 // is activated.
2735 // - if preinstalled version is higher than data version, preinstalled
2736 // version only is activated.
2737 const bool provides_shared_apex_libs =
2738 a.GetManifest().providesharedapexlibs();
2739 bool activate = false;
2740 if (provides_shared_apex_libs) {
2741 // preinstalled version gets activated in all cases except when same
2742 // version as data.
2743 if (instance.IsPreInstalledApex(a) &&
2744 (a.GetManifest().version() != version_b)) {
2745 LOG(DEBUG) << "Activating preinstalled shared libs APEX: "
2746 << a.GetManifest().name() << " " << a.GetPath();
2747 activate = true;
2748 }
2749 // data version gets activated in all cases except when its version
2750 // is lower than preinstalled version.
2751 if (!instance.IsPreInstalledApex(a) &&
2752 (a.GetManifest().version() >= version_b)) {
2753 LOG(DEBUG) << "Activating shared libs APEX: "
2754 << a.GetManifest().name() << " " << a.GetPath();
2755 activate = true;
2756 }
2757 } else if (higher_version || same_version_priority_to_data) {
2758 LOG(DEBUG) << "Selecting between two APEX: " << a.GetManifest().name()
2759 << " " << a.GetPath();
2760 activate = true;
2761 }
2762 if (activate) {
2763 activation_list.emplace_back(a_ref);
2764 }
2765 };
2766 const int version_0 = apex_files[0].get().GetManifest().version();
2767 const int version_1 = apex_files[1].get().GetManifest().version();
2768 select_apex(apex_files[0].get(), version_1);
2769 select_apex(apex_files[1].get(), version_0);
2770 }
2771 return activation_list;
2772 }
2773
2774 namespace {
2775
OpenAndValidateDecompressedApex(const ApexFile & capex,const std::string & apex_path)2776 Result<ApexFile> OpenAndValidateDecompressedApex(const ApexFile& capex,
2777 const std::string& apex_path) {
2778 auto apex = ApexFile::Open(apex_path);
2779 if (!apex.ok()) {
2780 return Error() << "Failed to open decompressed APEX: " << apex.error();
2781 }
2782 auto result = ValidateDecompressedApex(capex, *apex);
2783 if (!result.ok()) {
2784 return result.error();
2785 }
2786 auto ctx = GetfileconPath(apex_path);
2787 if (!ctx.ok()) {
2788 return ctx.error();
2789 }
2790 if (!StartsWith(*ctx, gConfig->active_apex_selinux_ctx)) {
2791 return Error() << apex_path << " has wrong SELinux context " << *ctx;
2792 }
2793 return std::move(*apex);
2794 }
2795
2796 // Process a single compressed APEX. Returns the decompressed APEX if
2797 // successful.
ProcessCompressedApex(const ApexFile & capex,bool is_ota_chroot)2798 Result<ApexFile> ProcessCompressedApex(const ApexFile& capex,
2799 bool is_ota_chroot) {
2800 LOG(INFO) << "Processing compressed APEX " << capex.GetPath();
2801 const auto decompressed_apex_path =
2802 StringPrintf("%s/%s%s", gConfig->decompression_dir,
2803 GetPackageId(capex.GetManifest()).c_str(),
2804 kDecompressedApexPackageSuffix);
2805 // Check if decompressed APEX already exist
2806 auto decompressed_path_exists = PathExists(decompressed_apex_path);
2807 if (decompressed_path_exists.ok() && *decompressed_path_exists) {
2808 // Check if existing decompressed APEX is valid
2809 auto result =
2810 OpenAndValidateDecompressedApex(capex, decompressed_apex_path);
2811 if (result.ok()) {
2812 LOG(INFO) << "Skipping decompression for " << capex.GetPath();
2813 return result;
2814 }
2815 // Do not delete existing decompressed APEX when is_ota_chroot is true
2816 if (!is_ota_chroot) {
2817 // Existing decompressed APEX is not valid. We will have to redecompress
2818 LOG(WARNING) << "Existing decompressed APEX is invalid: "
2819 << result.error();
2820 RemoveFileIfExists(decompressed_apex_path);
2821 }
2822 }
2823
2824 // We can also reuse existing OTA APEX, depending on situation
2825 auto ota_apex_path = StringPrintf("%s/%s%s", gConfig->decompression_dir,
2826 GetPackageId(capex.GetManifest()).c_str(),
2827 kOtaApexPackageSuffix);
2828 auto ota_path_exists = PathExists(ota_apex_path);
2829 if (ota_path_exists.ok() && *ota_path_exists) {
2830 if (is_ota_chroot) {
2831 // During ota_chroot, we try to reuse ota APEX as is
2832 auto result = OpenAndValidateDecompressedApex(capex, ota_apex_path);
2833 if (result.ok()) {
2834 LOG(INFO) << "Skipping decompression for " << ota_apex_path;
2835 return result;
2836 }
2837 // Existing ota_apex is not valid. We will have to decompress
2838 LOG(WARNING) << "Existing decompressed OTA APEX is invalid: "
2839 << result.error();
2840 RemoveFileIfExists(ota_apex_path);
2841 } else {
2842 // During boot, we can avoid decompression by renaming OTA apex
2843 // to expected decompressed_apex path
2844
2845 // Check if ota_apex APEX is valid
2846 auto result = OpenAndValidateDecompressedApex(capex, ota_apex_path);
2847 if (result.ok()) {
2848 // ota_apex matches with capex. Slot has been switched.
2849
2850 // Rename ota_apex to expected decompressed_apex path
2851 if (rename(ota_apex_path.c_str(), decompressed_apex_path.c_str()) ==
2852 0) {
2853 // Check if renamed decompressed APEX is valid
2854 result =
2855 OpenAndValidateDecompressedApex(capex, decompressed_apex_path);
2856 if (result.ok()) {
2857 LOG(INFO) << "Renamed " << ota_apex_path << " to "
2858 << decompressed_apex_path;
2859 return result;
2860 }
2861 // Renamed ota_apex is not valid. We will have to decompress
2862 LOG(WARNING) << "Renamed decompressed APEX from " << ota_apex_path
2863 << " to " << decompressed_apex_path
2864 << " is invalid: " << result.error();
2865 RemoveFileIfExists(decompressed_apex_path);
2866 } else {
2867 PLOG(ERROR) << "Failed to rename file " << ota_apex_path;
2868 }
2869 }
2870 }
2871 }
2872
2873 // There was no way to avoid decompression
2874
2875 // Clean up reserved space before decompressing capex
2876 if (auto ret = DeleteDirContent(gConfig->ota_reserved_dir); !ret.ok()) {
2877 LOG(ERROR) << "Failed to clean up reserved space: " << ret.error();
2878 }
2879
2880 auto decompression_dest =
2881 is_ota_chroot ? ota_apex_path : decompressed_apex_path;
2882 auto scope_guard = android::base::make_scope_guard(
2883 [&]() { RemoveFileIfExists(decompression_dest); });
2884
2885 auto decompression_result = capex.Decompress(decompression_dest);
2886 if (!decompression_result.ok()) {
2887 return Error() << "Failed to decompress : " << capex.GetPath().c_str()
2888 << " " << decompression_result.error();
2889 }
2890
2891 // Fix label of decompressed file
2892 auto restore = RestoreconPath(decompression_dest);
2893 if (!restore.ok()) {
2894 return restore.error();
2895 }
2896
2897 // Validate the newly decompressed APEX
2898 auto return_apex = OpenAndValidateDecompressedApex(capex, decompression_dest);
2899 if (!return_apex.ok()) {
2900 return Error() << "Failed to decompress CAPEX: " << return_apex.error();
2901 }
2902
2903 gChangedActiveApexes.insert(return_apex->GetManifest().name());
2904 /// Release compressed blocks in case decompression_dest is on f2fs-compressed
2905 // filesystem.
2906 ReleaseF2fsCompressedBlocks(decompression_dest);
2907
2908 scope_guard.Disable();
2909 return return_apex;
2910 }
2911 } // namespace
2912
2913 /**
2914 * For each compressed APEX, decompress it to kApexDecompressedDir
2915 * and return the decompressed APEX.
2916 *
2917 * Returns list of decompressed APEX.
2918 */
ProcessCompressedApex(const std::vector<ApexFileRef> & compressed_apex,bool is_ota_chroot)2919 std::vector<ApexFile> ProcessCompressedApex(
2920 const std::vector<ApexFileRef>& compressed_apex, bool is_ota_chroot) {
2921 LOG(INFO) << "Processing compressed APEX";
2922
2923 std::vector<ApexFile> decompressed_apex_list;
2924 for (const ApexFile& capex : compressed_apex) {
2925 if (!capex.IsCompressed()) {
2926 continue;
2927 }
2928
2929 auto decompressed_apex = ProcessCompressedApex(capex, is_ota_chroot);
2930 if (decompressed_apex.ok()) {
2931 decompressed_apex_list.emplace_back(std::move(*decompressed_apex));
2932 continue;
2933 }
2934 LOG(ERROR) << "Failed to process compressed APEX: "
2935 << decompressed_apex.error();
2936 }
2937 return std::move(decompressed_apex_list);
2938 }
2939
ValidateDecompressedApex(const ApexFile & capex,const ApexFile & apex)2940 Result<void> ValidateDecompressedApex(const ApexFile& capex,
2941 const ApexFile& apex) {
2942 // Decompressed APEX must have same public key as CAPEX
2943 if (capex.GetBundledPublicKey() != apex.GetBundledPublicKey()) {
2944 return Error()
2945 << "Public key of compressed APEX is different than original "
2946 << "APEX for " << apex.GetPath();
2947 }
2948 // Decompressed APEX must have same version as CAPEX
2949 if (capex.GetManifest().version() != apex.GetManifest().version()) {
2950 return Error()
2951 << "Compressed APEX has different version than decompressed APEX "
2952 << apex.GetPath();
2953 }
2954 // Decompressed APEX must have same root digest as what is stored in CAPEX
2955 auto apex_verity = apex.VerifyApexVerity(apex.GetBundledPublicKey());
2956 if (!apex_verity.ok() ||
2957 capex.GetManifest().capexmetadata().originalapexdigest() !=
2958 apex_verity->root_digest) {
2959 return Error() << "Root digest of " << apex.GetPath()
2960 << " does not match with"
2961 << " expected root digest in " << capex.GetPath();
2962 }
2963 return {};
2964 }
2965
OnStart()2966 void OnStart() {
2967 ATRACE_NAME("OnStart");
2968 LOG(INFO) << "Marking APEXd as starting";
2969 auto time_started = boot_clock::now();
2970 if (!SetProperty(gConfig->apex_status_sysprop, kApexStatusStarting)) {
2971 PLOG(ERROR) << "Failed to set " << gConfig->apex_status_sysprop << " to "
2972 << kApexStatusStarting;
2973 }
2974
2975 // Ask whether we should revert any active sessions; this can happen if
2976 // we've exceeded the retry count on a device that supports filesystem
2977 // checkpointing.
2978 if (gSupportsFsCheckpoints) {
2979 Result<bool> needs_revert = gVoldService->NeedsRollback();
2980 if (!needs_revert.ok()) {
2981 LOG(ERROR) << "Failed to check if we need a revert: "
2982 << needs_revert.error();
2983 } else if (*needs_revert) {
2984 LOG(INFO) << "Exceeded number of session retries ("
2985 << kNumRetriesWhenCheckpointingEnabled
2986 << "). Starting a revert";
2987 RevertActiveSessions("", "");
2988 }
2989 }
2990
2991 // Create directories for APEX shared libraries.
2992 auto sharedlibs_apex_dir = CreateSharedLibsApexDir();
2993 if (!sharedlibs_apex_dir.ok()) {
2994 LOG(ERROR) << sharedlibs_apex_dir.error();
2995 }
2996
2997 // If there is any new apex to be installed on /data/app-staging, hardlink
2998 // them to /data/apex/active first.
2999 ScanStagedSessionsDirAndStage();
3000 if (auto status = ApexFileRepository::GetInstance().AddDataApex(
3001 gConfig->active_apex_data_dir);
3002 !status.ok()) {
3003 LOG(ERROR) << "Failed to collect data APEX files : " << status.error();
3004 }
3005
3006 auto status = ResumeRevertIfNeeded();
3007 if (!status.ok()) {
3008 LOG(ERROR) << "Failed to resume revert : " << status.error();
3009 }
3010
3011 // Group every ApexFile on device by name
3012 const auto& instance = ApexFileRepository::GetInstance();
3013 const auto& all_apex = instance.AllApexFilesByName();
3014 // There can be multiple APEX packages with package name X. Determine which
3015 // one to activate.
3016 auto activation_list = SelectApexForActivation(all_apex, instance);
3017
3018 // Process compressed APEX, if any
3019 std::vector<ApexFileRef> compressed_apex;
3020 for (auto it = activation_list.begin(); it != activation_list.end();) {
3021 if (it->get().IsCompressed()) {
3022 compressed_apex.emplace_back(*it);
3023 it = activation_list.erase(it);
3024 } else {
3025 it++;
3026 }
3027 }
3028 std::vector<ApexFile> decompressed_apex;
3029 if (!compressed_apex.empty()) {
3030 decompressed_apex =
3031 ProcessCompressedApex(compressed_apex, /* is_ota_chroot= */ false);
3032 for (const ApexFile& apex_file : decompressed_apex) {
3033 activation_list.emplace_back(std::cref(apex_file));
3034 }
3035 }
3036
3037 // TODO(b/179248390): activate parallelly if possible
3038 auto activate_status =
3039 ActivateApexPackages(activation_list, ActivationMode::kBootMode);
3040 if (!activate_status.ok()) {
3041 std::string error_message =
3042 StringPrintf("Failed to activate packages: %s",
3043 activate_status.error().message().c_str());
3044 LOG(ERROR) << error_message;
3045 Result<void> revert_status =
3046 RevertActiveSessionsAndReboot("", error_message);
3047 if (!revert_status.ok()) {
3048 LOG(ERROR) << "Failed to revert : " << revert_status.error();
3049 }
3050 auto retry_status =
3051 ActivateMissingApexes(activation_list, ActivationMode::kBootMode);
3052 if (!retry_status.ok()) {
3053 LOG(ERROR) << retry_status.error();
3054 }
3055 }
3056
3057 // Clean up inactive APEXes on /data. We don't need them anyway.
3058 RemoveInactiveDataApex();
3059
3060 // Now that APEXes are mounted, snapshot or restore DE_sys data.
3061 SnapshotOrRestoreDeSysData();
3062
3063 auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
3064 boot_clock::now() - time_started).count();
3065 LOG(INFO) << "OnStart done, duration=" << time_elapsed;
3066 }
3067
OnAllPackagesActivated(bool is_bootstrap)3068 void OnAllPackagesActivated(bool is_bootstrap) {
3069 auto result = EmitApexInfoList(is_bootstrap);
3070 if (!result.ok()) {
3071 LOG(ERROR) << "cannot emit apex info list: " << result.error();
3072 }
3073
3074 // Because apexd in bootstrap mode runs in blocking mode
3075 // we don't have to set as activated.
3076 if (is_bootstrap) {
3077 return;
3078 }
3079
3080 // Set a system property to let other components know that APEXs are
3081 // activated, but are not yet ready to be used. init is expected to wait
3082 // for this status before performing configuration based on activated
3083 // apexes. Other components that need to use APEXs should wait for the
3084 // ready state instead.
3085 LOG(INFO) << "Marking APEXd as activated";
3086 if (!SetProperty(gConfig->apex_status_sysprop, kApexStatusActivated)) {
3087 PLOG(ERROR) << "Failed to set " << gConfig->apex_status_sysprop << " to "
3088 << kApexStatusActivated;
3089 }
3090 }
3091
OnAllPackagesReady()3092 void OnAllPackagesReady() {
3093 // Set a system property to let other components know that APEXs are
3094 // correctly mounted and ready to be used. Before using any file from APEXs,
3095 // they can query this system property to ensure that they are okay to
3096 // access. Or they may have a on-property trigger to delay a task until
3097 // APEXs become ready.
3098 LOG(INFO) << "Marking APEXd as ready";
3099 if (!SetProperty(gConfig->apex_status_sysprop, kApexStatusReady)) {
3100 PLOG(ERROR) << "Failed to set " << gConfig->apex_status_sysprop << " to "
3101 << kApexStatusReady;
3102 }
3103 // Since apexd.status property is a system property, we expose yet another
3104 // property as system_restricted_prop so that, for example, vendor can rely on
3105 // the "ready" event.
3106 if (!SetProperty(kApexAllReadyProp, "true")) {
3107 PLOG(ERROR) << "Failed to set " << kApexAllReadyProp << " to true";
3108 }
3109 }
3110
SubmitStagedSession(const int session_id,const std::vector<int> & child_session_ids,const bool has_rollback_enabled,const bool is_rollback,const int rollback_id)3111 Result<std::vector<ApexFile>> SubmitStagedSession(
3112 const int session_id, const std::vector<int>& child_session_ids,
3113 const bool has_rollback_enabled, const bool is_rollback,
3114 const int rollback_id) {
3115 if (session_id == 0) {
3116 return Error() << "Session id was not provided.";
3117 }
3118
3119 if (!gSupportsFsCheckpoints) {
3120 Result<void> backup_status = BackupActivePackages();
3121 if (!backup_status.ok()) {
3122 // Do not proceed with staged install without backup
3123 return backup_status.error();
3124 }
3125 }
3126
3127 std::vector<int> ids_to_scan;
3128 if (!child_session_ids.empty()) {
3129 ids_to_scan = child_session_ids;
3130 } else {
3131 ids_to_scan = {session_id};
3132 }
3133
3134 std::vector<ApexFile> ret;
3135 auto guard = android::base::make_scope_guard([&]() {
3136 for (const auto& apex : ret) {
3137 apexd_private::UnmountTempMount(apex);
3138 }
3139 });
3140 for (int id_to_scan : ids_to_scan) {
3141 auto verified = VerifySessionDir(id_to_scan);
3142 if (!verified.ok()) {
3143 return verified.error();
3144 }
3145 LOG(DEBUG) << verified->GetPath() << " is verified";
3146 ret.push_back(std::move(*verified));
3147 }
3148
3149 if (has_rollback_enabled && is_rollback) {
3150 return Error() << "Cannot set session " << session_id << " as both a"
3151 << " rollback and enabled for rollback.";
3152 }
3153
3154 auto session = gSessionManager->CreateSession(session_id);
3155 if (!session.ok()) {
3156 return session.error();
3157 }
3158 (*session).SetChildSessionIds(child_session_ids);
3159 std::string build_fingerprint = GetProperty(kBuildFingerprintSysprop, "");
3160 (*session).SetBuildFingerprint(build_fingerprint);
3161 session->SetHasRollbackEnabled(has_rollback_enabled);
3162 session->SetIsRollback(is_rollback);
3163 session->SetRollbackId(rollback_id);
3164 for (const auto& apex_file : ret) {
3165 session->AddApexName(apex_file.GetManifest().name());
3166 }
3167 Result<void> commit_status =
3168 (*session).UpdateStateAndCommit(SessionState::VERIFIED);
3169 if (!commit_status.ok()) {
3170 return commit_status.error();
3171 }
3172
3173 for (const auto& apex : ret) {
3174 // Release compressed blocks in case /data is f2fs-compressed filesystem.
3175 ReleaseF2fsCompressedBlocks(apex.GetPath());
3176 }
3177
3178 // The scope guard above uses lambda that captures ret by reference.
3179 // Unfortunately, for the capture by-reference, lifetime of the captured
3180 // reference ends together with the lifetime of the closure object. This means
3181 // that we need to manually call UnmountTempMount here.
3182 for (const auto& apex : ret) {
3183 apexd_private::UnmountTempMount(apex);
3184 }
3185
3186 return ret;
3187 }
3188
MarkStagedSessionReady(const int session_id)3189 Result<void> MarkStagedSessionReady(const int session_id) {
3190 auto session = gSessionManager->GetSession(session_id);
3191 if (!session.ok()) {
3192 return session.error();
3193 }
3194 // We should only accept sessions in SessionState::VERIFIED or
3195 // SessionState::STAGED state. In the SessionState::STAGED case, this
3196 // function is effectively a no-op.
3197 auto session_state = (*session).GetState();
3198 if (session_state == SessionState::STAGED) {
3199 return {};
3200 }
3201 if (session_state == SessionState::VERIFIED) {
3202 return (*session).UpdateStateAndCommit(SessionState::STAGED);
3203 }
3204 return Error() << "Invalid state for session " << session_id
3205 << ". Cannot mark it as ready.";
3206 }
3207
MarkStagedSessionSuccessful(const int session_id)3208 Result<void> MarkStagedSessionSuccessful(const int session_id) {
3209 auto session = gSessionManager->GetSession(session_id);
3210 if (!session.ok()) {
3211 return session.error();
3212 }
3213 // Only SessionState::ACTIVATED or SessionState::SUCCESS states are accepted.
3214 // In the SessionState::SUCCESS state, this function is a no-op.
3215 if (session->GetState() == SessionState::SUCCESS) {
3216 return {};
3217 } else if (session->GetState() == SessionState::ACTIVATED) {
3218 auto cleanup_status = DeleteBackup();
3219 if (!cleanup_status.ok()) {
3220 return Error() << "Failed to mark session " << *session
3221 << " as successful : " << cleanup_status.error();
3222 }
3223 if (session->IsRollback() && !gSupportsFsCheckpoints) {
3224 DeleteDePreRestoreSnapshots(*session);
3225 }
3226 return session->UpdateStateAndCommit(SessionState::SUCCESS);
3227 } else {
3228 return Error() << "Session " << *session << " can not be marked successful";
3229 }
3230 }
3231
3232 // Removes APEXes on /data that have not been activated
RemoveInactiveDataApex()3233 void RemoveInactiveDataApex() {
3234 std::vector<std::string> all_apex_files;
3235 Result<std::vector<std::string>> active_apex =
3236 FindFilesBySuffix(gConfig->active_apex_data_dir, {kApexPackageSuffix});
3237 if (!active_apex.ok()) {
3238 LOG(ERROR) << "Failed to scan " << gConfig->active_apex_data_dir << " : "
3239 << active_apex.error();
3240 } else {
3241 all_apex_files.insert(all_apex_files.end(),
3242 std::make_move_iterator(active_apex->begin()),
3243 std::make_move_iterator(active_apex->end()));
3244 }
3245 Result<std::vector<std::string>> decompressed_apex = FindFilesBySuffix(
3246 gConfig->decompression_dir, {kDecompressedApexPackageSuffix});
3247 if (!decompressed_apex.ok()) {
3248 LOG(ERROR) << "Failed to scan " << gConfig->decompression_dir << " : "
3249 << decompressed_apex.error();
3250 } else {
3251 all_apex_files.insert(all_apex_files.end(),
3252 std::make_move_iterator(decompressed_apex->begin()),
3253 std::make_move_iterator(decompressed_apex->end()));
3254 }
3255
3256 for (const auto& path : all_apex_files) {
3257 if (!apexd_private::IsMounted(path)) {
3258 LOG(INFO) << "Removing inactive data APEX " << path;
3259 if (unlink(path.c_str()) != 0) {
3260 PLOG(ERROR) << "Failed to unlink inactive data APEX " << path;
3261 }
3262 }
3263 }
3264 }
3265
IsApexDevice(const std::string & dev_name)3266 bool IsApexDevice(const std::string& dev_name) {
3267 auto& repo = ApexFileRepository::GetInstance();
3268 for (const auto& apex : repo.GetPreInstalledApexFiles()) {
3269 if (StartsWith(dev_name, apex.get().GetManifest().name())) {
3270 return true;
3271 }
3272 }
3273 return false;
3274 }
3275
3276 // TODO(b/192241176): move to apexd_verity.{h,cpp}.
DeleteUnusedVerityDevices()3277 void DeleteUnusedVerityDevices() {
3278 DeviceMapper& dm = DeviceMapper::Instance();
3279 std::vector<DeviceMapper::DmBlockDevice> all_devices;
3280 if (!dm.GetAvailableDevices(&all_devices)) {
3281 LOG(WARNING) << "Failed to fetch dm devices";
3282 return;
3283 }
3284 for (const auto& dev : all_devices) {
3285 auto state = dm.GetState(dev.name());
3286 if (state == DmDeviceState::SUSPENDED && IsApexDevice(dev.name())) {
3287 LOG(INFO) << "Deleting unused dm device " << dev.name();
3288 auto res = DeleteVerityDevice(dev.name(), /* deferred= */ false);
3289 if (!res.ok()) {
3290 LOG(WARNING) << res.error();
3291 }
3292 }
3293 }
3294 }
3295
BootCompletedCleanup()3296 void BootCompletedCleanup() {
3297 auto sessions = gSessionManager->GetSessions();
3298 for (const ApexSession& session : sessions) {
3299 if (!session.IsFinalized()) {
3300 continue;
3301 }
3302 auto result = session.DeleteSession();
3303 if (!result.ok()) {
3304 LOG(WARNING) << "Failed to delete finalized session: " << session.GetId();
3305 }
3306 }
3307
3308 DeleteUnusedVerityDevices();
3309 }
3310
UnmountAll(bool also_include_staged_apexes)3311 int UnmountAll(bool also_include_staged_apexes) {
3312 std::vector<std::string> data_dirs = {gConfig->active_apex_data_dir,
3313 gConfig->decompression_dir};
3314
3315 if (also_include_staged_apexes) {
3316 for (const ApexSession& session :
3317 gSessionManager->GetSessionsInState(SessionState::STAGED)) {
3318 std::vector<std::string> dirs_to_scan =
3319 session.GetStagedApexDirs(gConfig->staged_session_dir);
3320 std::move(dirs_to_scan.begin(), dirs_to_scan.end(),
3321 std::back_inserter(data_dirs));
3322 }
3323 }
3324
3325 gMountedApexes.PopulateFromMounts(data_dirs, gConfig->apex_hash_tree_dir);
3326 int ret = 0;
3327 gMountedApexes.ForallMountedApexes([&](const std::string& /*package*/,
3328 const MountedApexData& data,
3329 bool latest) {
3330 LOG(INFO) << "Unmounting " << data.full_path << " mounted on "
3331 << data.mount_point;
3332 auto apex = ApexFile::Open(data.full_path);
3333 if (!apex.ok()) {
3334 LOG(ERROR) << "Failed to open " << data.full_path << " : "
3335 << apex.error();
3336 ret = 1;
3337 return;
3338 }
3339 if (latest && !apex->GetManifest().providesharedapexlibs()) {
3340 auto pos = data.mount_point.find('@');
3341 CHECK(pos != std::string::npos);
3342 std::string bind_mount = data.mount_point.substr(0, pos);
3343 if (umount2(bind_mount.c_str(), UMOUNT_NOFOLLOW) != 0) {
3344 PLOG(ERROR) << "Failed to unmount bind-mount " << bind_mount;
3345 ret = 1;
3346 return;
3347 }
3348 }
3349 if (auto status = Unmount(data, /* deferred= */ false); !status.ok()) {
3350 LOG(ERROR) << "Failed to unmount " << data.mount_point << " : "
3351 << status.error();
3352 ret = 1;
3353 }
3354 });
3355 return ret;
3356 }
3357
RemountPackages()3358 Result<void> RemountPackages() {
3359 std::vector<std::string> apexes;
3360 gMountedApexes.ForallMountedApexes([&apexes](const std::string& /*package*/,
3361 const MountedApexData& data,
3362 bool latest) {
3363 if (latest) {
3364 LOG(DEBUG) << "Found active APEX " << data.full_path;
3365 apexes.push_back(data.full_path);
3366 }
3367 });
3368 std::vector<std::string> failed;
3369 for (const std::string& apex : apexes) {
3370 // Since this is only used during development workflow, we are trying to
3371 // remount as many apexes as possible instead of failing fast.
3372 if (auto ret = RemountApexFile(apex); !ret.ok()) {
3373 LOG(WARNING) << "Failed to remount " << apex << " : " << ret.error();
3374 failed.emplace_back(apex);
3375 }
3376 }
3377 static constexpr const char* kErrorMessage =
3378 "Failed to remount following APEX packages, hence previous versions of "
3379 "them are still active. If APEX you are developing is in this list, it "
3380 "means that there still are alive processes holding a reference to the "
3381 "previous version of your APEX.\n";
3382 if (!failed.empty()) {
3383 return Error() << kErrorMessage << "Failed (" << failed.size() << ") "
3384 << "APEX packages: [" << Join(failed, ',') << "]";
3385 }
3386 return {};
3387 }
3388
3389 // Given a single new APEX incoming via OTA, should we allocate space for it?
ShouldAllocateSpaceForDecompression(const std::string & new_apex_name,const int64_t new_apex_version,const ApexFileRepository & instance)3390 bool ShouldAllocateSpaceForDecompression(const std::string& new_apex_name,
3391 const int64_t new_apex_version,
3392 const ApexFileRepository& instance) {
3393 // An apex at most will have two versions on device: pre-installed and data.
3394
3395 // Check if there is a pre-installed version for the new apex.
3396 if (!instance.HasPreInstalledVersion(new_apex_name)) {
3397 // We are introducing a new APEX that doesn't exist at all
3398 return true;
3399 }
3400
3401 // Check if there is a data apex
3402 if (!instance.HasDataVersion(new_apex_name)) {
3403 // Data apex doesn't exist. Compare against pre-installed APEX
3404 auto pre_installed_apex = instance.GetPreInstalledApex(new_apex_name);
3405 if (!pre_installed_apex.get().IsCompressed()) {
3406 // Compressing an existing uncompressed system APEX.
3407 return true;
3408 }
3409 // Since there is no data apex, it means device is using the compressed
3410 // pre-installed version. If new apex has higher version, we are upgrading
3411 // the pre-install version and if new apex has lower version, we are
3412 // downgrading it. So the current decompressed apex should be replaced
3413 // with the new decompressed apex to reflect that.
3414 const int64_t pre_installed_version =
3415 instance.GetPreInstalledApex(new_apex_name)
3416 .get()
3417 .GetManifest()
3418 .version();
3419 return new_apex_version != pre_installed_version;
3420 }
3421
3422 // From here on, data apex exists. So we should compare directly against data
3423 // apex.
3424 auto data_apex = instance.GetDataApex(new_apex_name);
3425 // Compare the data apex version with new apex
3426 const int64_t data_version = data_apex.get().GetManifest().version();
3427 // We only decompress the new_apex if it has higher version than data apex.
3428 return new_apex_version > data_version;
3429 }
3430
CalculateSizeForCompressedApex(const std::vector<std::tuple<std::string,int64_t,int64_t>> & compressed_apexes,const ApexFileRepository & instance)3431 int64_t CalculateSizeForCompressedApex(
3432 const std::vector<std::tuple<std::string, int64_t, int64_t>>&
3433 compressed_apexes,
3434 const ApexFileRepository& instance) {
3435 int64_t result = 0;
3436 for (const auto& compressed_apex : compressed_apexes) {
3437 std::string module_name;
3438 int64_t version_code;
3439 int64_t decompressed_size;
3440 std::tie(module_name, version_code, decompressed_size) = compressed_apex;
3441 if (ShouldAllocateSpaceForDecompression(module_name, version_code,
3442 instance)) {
3443 result += decompressed_size;
3444 }
3445 }
3446 return result;
3447 }
3448
CollectApexInfoList(std::ostream & os,const std::vector<ApexFile> & active_apexs,const std::vector<ApexFile> & inactive_apexs)3449 void CollectApexInfoList(std::ostream& os,
3450 const std::vector<ApexFile>& active_apexs,
3451 const std::vector<ApexFile>& inactive_apexs) {
3452 std::vector<com::android::apex::ApexInfo> apex_infos;
3453
3454 auto convert_to_autogen = [&apex_infos](const ApexFile& apex,
3455 bool is_active) {
3456 auto& instance = ApexFileRepository::GetInstance();
3457
3458 auto preinstalled_path =
3459 instance.GetPreinstalledPath(apex.GetManifest().name());
3460 std::optional<std::string> preinstalled_module_path;
3461 if (preinstalled_path.ok()) {
3462 preinstalled_module_path = *preinstalled_path;
3463 }
3464
3465 std::optional<int64_t> mtime =
3466 instance.GetBlockApexLastUpdateSeconds(apex.GetPath());
3467 if (!mtime.has_value()) {
3468 struct stat stat_buf;
3469 if (stat(apex.GetPath().c_str(), &stat_buf) == 0) {
3470 mtime.emplace(stat_buf.st_mtime);
3471 } else {
3472 PLOG(WARNING) << "Failed to stat " << apex.GetPath();
3473 }
3474 }
3475 com::android::apex::ApexInfo apex_info(
3476 apex.GetManifest().name(), apex.GetPath(), preinstalled_module_path,
3477 apex.GetManifest().version(), apex.GetManifest().versionname(),
3478 instance.IsPreInstalledApex(apex), is_active, mtime,
3479 apex.GetManifest().providesharedapexlibs());
3480 apex_infos.emplace_back(std::move(apex_info));
3481 };
3482 for (const auto& apex : active_apexs) {
3483 convert_to_autogen(apex, /* is_active= */ true);
3484 }
3485 for (const auto& apex : inactive_apexs) {
3486 convert_to_autogen(apex, /* is_active= */ false);
3487 }
3488 com::android::apex::ApexInfoList apex_info_list(apex_infos);
3489 com::android::apex::write(os, apex_info_list);
3490 }
3491
3492 // Reserve |size| bytes in |dest_dir| by creating a zero-filled file.
3493 // Also, we always clean up ota_apex that has been processed as
3494 // part of pre-reboot decompression whenever we reserve space.
ReserveSpaceForCompressedApex(int64_t size,const std::string & dest_dir)3495 Result<void> ReserveSpaceForCompressedApex(int64_t size,
3496 const std::string& dest_dir) {
3497 if (size < 0) {
3498 return Error() << "Cannot reserve negative byte of space";
3499 }
3500
3501 // Since we are reserving space, then we must be preparing for a new OTA.
3502 // Clean up any processed ota_apex from previous OTA.
3503 auto ota_apex_files =
3504 FindFilesBySuffix(gConfig->decompression_dir, {kOtaApexPackageSuffix});
3505 if (!ota_apex_files.ok()) {
3506 return Error() << "Failed to clean up ota_apex: " << ota_apex_files.error();
3507 }
3508 for (const std::string& ota_apex : *ota_apex_files) {
3509 RemoveFileIfExists(ota_apex);
3510 }
3511
3512 auto file_path = StringPrintf("%s/full.tmp", dest_dir.c_str());
3513 if (size == 0) {
3514 LOG(INFO) << "Cleaning up reserved space for compressed APEX";
3515 // Ota is being cancelled. Clean up reserved space
3516 RemoveFileIfExists(file_path);
3517 return {};
3518 }
3519
3520 LOG(INFO) << "Reserving " << size << " bytes for compressed APEX";
3521 unique_fd dest_fd(
3522 open(file_path.c_str(), O_WRONLY | O_CLOEXEC | O_CREAT, 0644));
3523 if (dest_fd.get() == -1) {
3524 return ErrnoError() << "Failed to open file for reservation "
3525 << file_path.c_str();
3526 }
3527
3528 // Resize to required size, posix_fallocate will not shrink files so resize
3529 // is needed.
3530 std::error_code ec;
3531 std::filesystem::resize_file(file_path, size, ec);
3532 if (ec) {
3533 RemoveFileIfExists(file_path);
3534 return ErrnoError() << "Failed to resize file " << file_path.c_str()
3535 << " : " << ec.message();
3536 }
3537
3538 // Allocate blocks for the requested size.
3539 // resize_file will create sparse file with 0 blocks on filesystems that
3540 // supports sparse files.
3541 if ((errno = posix_fallocate(dest_fd.get(), 0, size))) {
3542 RemoveFileIfExists(file_path);
3543 return ErrnoError() << "Failed to allocate blocks for file "
3544 << file_path.c_str();
3545 }
3546
3547 return {};
3548 }
3549
3550 // Adds block apexes if system property is set.
AddBlockApex(ApexFileRepository & instance)3551 Result<int> AddBlockApex(ApexFileRepository& instance) {
3552 auto prop = GetProperty(gConfig->vm_payload_metadata_partition_prop, "");
3553 if (prop != "") {
3554 auto block_count = instance.AddBlockApex(prop);
3555 if (!block_count.ok()) {
3556 return Error() << "Failed to scan block APEX files: "
3557 << block_count.error();
3558 }
3559 return block_count;
3560 } else {
3561 LOG(INFO) << "No block apex metadata partition found, not adding block "
3562 << "apexes";
3563 }
3564 return 0;
3565 }
3566
3567 // When running in the VM mode, we follow the minimal start-up operations.
3568 // - CreateSharedLibsApexDir
3569 // - AddPreInstalledApex: note that CAPEXes are not supported in the VM mode
3570 // - AddBlockApex
3571 // - ActivateApexPackages
3572 // - setprop apexd.status: activated/ready
OnStartInVmMode()3573 int OnStartInVmMode() {
3574 Result<void> loop_ready = WaitForFile("/dev/loop-control", 20s);
3575 if (!loop_ready.ok()) {
3576 LOG(ERROR) << loop_ready.error();
3577 }
3578
3579 // Create directories for APEX shared libraries.
3580 if (auto status = CreateSharedLibsApexDir(); !status.ok()) {
3581 LOG(ERROR) << "Failed to create /apex/sharedlibs : " << status.ok();
3582 return 1;
3583 }
3584
3585 auto& instance = ApexFileRepository::GetInstance();
3586
3587 // Scan pre-installed apexes
3588 if (auto status = instance.AddPreInstalledApex(gConfig->apex_built_in_dirs);
3589 !status.ok()) {
3590 LOG(ERROR) << "Failed to scan pre-installed APEX files: " << status.error();
3591 return 1;
3592 }
3593
3594 if (auto status = AddBlockApex(instance); !status.ok()) {
3595 LOG(ERROR) << status.error();
3596 return 1;
3597 }
3598
3599 if (auto status = ActivateApexPackages(instance.GetPreInstalledApexFiles(),
3600 ActivationMode::kVmMode);
3601 !status.ok()) {
3602 LOG(ERROR) << "Failed to activate apex packages : " << status.error();
3603 return 1;
3604 }
3605 if (auto status = ActivateApexPackages(instance.GetDataApexFiles(),
3606 ActivationMode::kVmMode);
3607 !status.ok()) {
3608 LOG(ERROR) << "Failed to activate apex packages : " << status.error();
3609 return 1;
3610 }
3611
3612 OnAllPackagesActivated(false);
3613 // In VM mode, we don't run a separate --snapshotde mode.
3614 // Instead, we mark apexd.status "ready" right now.
3615 OnAllPackagesReady();
3616 return 0;
3617 }
3618
OnOtaChrootBootstrap(bool also_include_staged_apexes)3619 int OnOtaChrootBootstrap(bool also_include_staged_apexes) {
3620 auto& instance = ApexFileRepository::GetInstance();
3621 if (auto status = instance.AddPreInstalledApex(gConfig->apex_built_in_dirs);
3622 !status.ok()) {
3623 LOG(ERROR) << "Failed to scan pre-installed apexes from "
3624 << Join(gConfig->apex_built_in_dirs, ',');
3625 return 1;
3626 }
3627 if (also_include_staged_apexes) {
3628 // Scan staged dirs, and then scan the active dir. If a module is in both a
3629 // staged dir and the active dir, the APEX with a higher version will be
3630 // picked. If the versions are equal, the APEX in staged dir will be picked.
3631 //
3632 // The result is an approximation of what the active dir will actually have
3633 // after the reboot. In case of a downgrade install, it differs from the
3634 // actual, but this is not a supported case.
3635 for (const ApexSession& session :
3636 gSessionManager->GetSessionsInState(SessionState::STAGED)) {
3637 std::vector<std::string> dirs_to_scan =
3638 session.GetStagedApexDirs(gConfig->staged_session_dir);
3639 for (const std::string& dir_to_scan : dirs_to_scan) {
3640 if (auto status = instance.AddDataApex(dir_to_scan); !status.ok()) {
3641 LOG(ERROR) << "Failed to scan staged apexes from " << dir_to_scan;
3642 return 1;
3643 }
3644 }
3645 }
3646 }
3647 if (auto status = instance.AddDataApex(gConfig->active_apex_data_dir);
3648 !status.ok()) {
3649 LOG(ERROR) << "Failed to scan upgraded apexes from "
3650 << gConfig->active_apex_data_dir;
3651 // Fail early because we know we will be wasting cycles generating garbage
3652 // if we continue.
3653 return 1;
3654 }
3655
3656 // Create directories for APEX shared libraries.
3657 if (auto status = CreateSharedLibsApexDir(); !status.ok()) {
3658 LOG(ERROR) << "Failed to create /apex/sharedlibs : " << status.ok();
3659 return 1;
3660 }
3661
3662 auto activation_list =
3663 SelectApexForActivation(instance.AllApexFilesByName(), instance);
3664
3665 // TODO(b/179497746): This is the third time we are duplicating this code
3666 // block. This will be easier to dedup once we start opening ApexFiles via
3667 // ApexFileRepository. That way, ProcessCompressedApex can return list of
3668 // ApexFileRef, instead of ApexFile.
3669
3670 // Process compressed APEX, if any
3671 std::vector<ApexFileRef> compressed_apex;
3672 for (auto it = activation_list.begin(); it != activation_list.end();) {
3673 if (it->get().IsCompressed()) {
3674 compressed_apex.emplace_back(*it);
3675 it = activation_list.erase(it);
3676 } else {
3677 it++;
3678 }
3679 }
3680 std::vector<ApexFile> decompressed_apex;
3681 if (!compressed_apex.empty()) {
3682 decompressed_apex =
3683 ProcessCompressedApex(compressed_apex, /* is_ota_chroot= */ true);
3684
3685 for (const ApexFile& apex_file : decompressed_apex) {
3686 activation_list.emplace_back(std::cref(apex_file));
3687 }
3688 }
3689
3690 auto activate_status =
3691 ActivateApexPackages(activation_list, ActivationMode::kOtaChrootMode);
3692 if (!activate_status.ok()) {
3693 LOG(ERROR) << "Failed to activate apex packages : "
3694 << activate_status.error();
3695 auto retry_status =
3696 ActivateMissingApexes(activation_list, ActivationMode::kOtaChrootMode);
3697 if (!retry_status.ok()) {
3698 LOG(ERROR) << retry_status.error();
3699 }
3700 }
3701
3702 // There are a bunch of places that are producing apex-info.xml file.
3703 // We should consolidate the logic in one function and make all other places
3704 // use it.
3705 auto active_apexes = GetActivePackages();
3706 std::vector<ApexFile> inactive_apexes = GetFactoryPackages();
3707 auto new_end = std::remove_if(
3708 inactive_apexes.begin(), inactive_apexes.end(),
3709 [&active_apexes](const ApexFile& apex) {
3710 return std::any_of(active_apexes.begin(), active_apexes.end(),
3711 [&apex](const ApexFile& active_apex) {
3712 return apex.GetPath() == active_apex.GetPath();
3713 });
3714 });
3715 inactive_apexes.erase(new_end, inactive_apexes.end());
3716 std::stringstream xml;
3717 CollectApexInfoList(xml, active_apexes, inactive_apexes);
3718 std::string file_name = StringPrintf("%s/%s", kApexRoot, kApexInfoList);
3719 unique_fd fd(TEMP_FAILURE_RETRY(
3720 open(file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644)));
3721 if (fd.get() == -1) {
3722 PLOG(ERROR) << "Can't open " << file_name;
3723 return 1;
3724 }
3725
3726 if (!android::base::WriteStringToFd(xml.str(), fd)) {
3727 PLOG(ERROR) << "Can't write to " << file_name;
3728 return 1;
3729 }
3730
3731 fd.reset();
3732
3733 if (auto status = RestoreconPath(file_name); !status.ok()) {
3734 LOG(ERROR) << "Failed to restorecon " << file_name << " : "
3735 << status.error();
3736 return 1;
3737 }
3738
3739 return 0;
3740 }
3741
GetApexDatabaseForTesting()3742 android::apex::MountedApexDatabase& GetApexDatabaseForTesting() {
3743 return gMountedApexes;
3744 }
3745
3746 // A version of apex verification that happens during non-staged APEX
3747 // installation.
VerifyPackageNonStagedInstall(const ApexFile & apex_file,bool force)3748 Result<void> VerifyPackageNonStagedInstall(const ApexFile& apex_file,
3749 bool force) {
3750 const auto& verify_package_boot_status = VerifyPackageBoot(apex_file);
3751 if (!verify_package_boot_status.ok()) {
3752 return verify_package_boot_status;
3753 }
3754
3755 auto check_fn = [&apex_file,
3756 &force](const std::string& mount_point) -> Result<void> {
3757 if (force) {
3758 return Result<void>{};
3759 }
3760 if (access((mount_point + "/app").c_str(), F_OK) == 0) {
3761 return Error() << apex_file.GetPath() << " contains app inside";
3762 }
3763 if (access((mount_point + "/priv-app").c_str(), F_OK) == 0) {
3764 return Error() << apex_file.GetPath() << " contains priv-app inside";
3765 }
3766 if (IsVendorApex(apex_file)) {
3767 return CheckVendorApexUpdate(apex_file, mount_point);
3768 }
3769 return Result<void>{};
3770 };
3771 return RunVerifyFnInsideTempMount(apex_file, check_fn, true);
3772 }
3773
CheckSupportsNonStagedInstall(const ApexFile & new_apex,bool force)3774 Result<void> CheckSupportsNonStagedInstall(const ApexFile& new_apex,
3775 bool force) {
3776 const auto& new_manifest = new_apex.GetManifest();
3777
3778 if (!force) {
3779 if (!new_manifest.supportsrebootlessupdate()) {
3780 return Error() << new_apex.GetPath()
3781 << " does not support non-staged update";
3782 }
3783
3784 // Check if update will impact linkerconfig.
3785
3786 // Updates to shared libs APEXes must be done via staged install flow.
3787 if (new_manifest.providesharedapexlibs()) {
3788 return Error() << new_apex.GetPath() << " is a shared libs APEX";
3789 }
3790
3791 // This APEX provides native libs to other parts of the platform. It can
3792 // only be updated via staged install flow.
3793 if (new_manifest.providenativelibs_size() > 0) {
3794 return Error() << new_apex.GetPath() << " provides native libs";
3795 }
3796
3797 // This APEX requires libs provided by dynamic common library APEX, hence it
3798 // can only be installed using staged install flow.
3799 if (new_manifest.requiresharedapexlibs_size() > 0) {
3800 return Error() << new_apex.GetPath() << " requires shared apex libs";
3801 }
3802
3803 // We don't allow non-staged updates of APEXES that have java libs inside.
3804 if (new_manifest.jnilibs_size() > 0) {
3805 return Error() << new_apex.GetPath() << " requires JNI libs";
3806 }
3807 }
3808
3809 auto expected_public_key =
3810 ApexFileRepository::GetInstance().GetPublicKey(new_manifest.name());
3811 if (!expected_public_key.ok()) {
3812 return expected_public_key.error();
3813 }
3814 auto verity_data = new_apex.VerifyApexVerity(*expected_public_key);
3815 if (!verity_data.ok()) {
3816 return verity_data.error();
3817 }
3818 // Supporting non-staged install of APEXes without a hashtree is additional
3819 // hassle, it's easier not to support it.
3820 if (verity_data->desc->tree_size == 0) {
3821 return Error() << new_apex.GetPath()
3822 << " does not have an embedded hash tree";
3823 }
3824 return {};
3825 }
3826
ComputePackageIdMinor(const ApexFile & apex)3827 Result<size_t> ComputePackageIdMinor(const ApexFile& apex) {
3828 static constexpr size_t kMaxVerityDevicesPerApexName = 3u;
3829 DeviceMapper& dm = DeviceMapper::Instance();
3830 std::vector<DeviceMapper::DmBlockDevice> dm_devices;
3831 if (!dm.GetAvailableDevices(&dm_devices)) {
3832 return Error() << "Failed to list dm devices";
3833 }
3834 size_t devices = 0;
3835 size_t next_minor = 1;
3836 for (const auto& dm_device : dm_devices) {
3837 std::string_view dm_name(dm_device.name());
3838 // Format is <module_name>@<version_code>[_<minor>]
3839 if (!ConsumePrefix(&dm_name, apex.GetManifest().name())) {
3840 continue;
3841 }
3842 devices++;
3843 auto pos = dm_name.find_last_of('_');
3844 if (pos == std::string_view::npos) {
3845 continue;
3846 }
3847 size_t minor;
3848 if (!ParseUint(std::string(dm_name.substr(pos + 1)), &minor)) {
3849 return Error() << "Unexpected dm device name " << dm_device.name();
3850 }
3851 if (next_minor < minor + 1) {
3852 next_minor = minor + 1;
3853 }
3854 }
3855 if (devices > kMaxVerityDevicesPerApexName) {
3856 return Error() << "There are too many (" << devices
3857 << ") dm block devices associated with package "
3858 << apex.GetManifest().name();
3859 }
3860 while (true) {
3861 std::string target_file =
3862 StringPrintf("%s/%s_%zu.apex", gConfig->active_apex_data_dir,
3863 GetPackageId(apex.GetManifest()).c_str(), next_minor);
3864 if (access(target_file.c_str(), F_OK) == 0) {
3865 next_minor++;
3866 } else {
3867 break;
3868 }
3869 }
3870
3871 return next_minor;
3872 }
3873
UpdateApexInfoList()3874 Result<void> UpdateApexInfoList() {
3875 std::vector<ApexFile> active(GetActivePackages());
3876 std::vector<ApexFile> inactive = CalculateInactivePackages(active);
3877
3878 std::stringstream xml;
3879 CollectApexInfoList(xml, active, inactive);
3880
3881 std::string name = StringPrintf("%s/.default-%s", kApexRoot, kApexInfoList);
3882 unique_fd fd(TEMP_FAILURE_RETRY(
3883 open(name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644)));
3884 if (fd.get() == -1) {
3885 return ErrnoError() << "Can't open " << name;
3886 }
3887 if (!WriteStringToFd(xml.str(), fd)) {
3888 return ErrnoError() << "Failed to write to " << name;
3889 }
3890
3891 return {};
3892 }
3893
3894 // TODO(b/238820991) Handle failures
UnloadApexFromInit(const std::string & apex_name)3895 Result<void> UnloadApexFromInit(const std::string& apex_name) {
3896 if (!SetProperty(kCtlApexUnloadSysprop, apex_name)) {
3897 // When failed to SetProperty(), there's nothing we can do here.
3898 // Log error and return early to avoid indefinite waiting for ack.
3899 return Error() << "Failed to set " << kCtlApexUnloadSysprop << " to "
3900 << apex_name;
3901 }
3902 SetProperty("apex." + apex_name + ".ready", "false");
3903 return {};
3904 }
3905
3906 // TODO(b/238820991) Handle failures
LoadApexFromInit(const std::string & apex_name)3907 Result<void> LoadApexFromInit(const std::string& apex_name) {
3908 if (!SetProperty(kCtlApexLoadSysprop, apex_name)) {
3909 // When failed to SetProperty(), there's nothing we can do here.
3910 // Log error and return early to avoid indefinite waiting for ack.
3911 return Error() << "Failed to set " << kCtlApexLoadSysprop << " to "
3912 << apex_name;
3913 }
3914 SetProperty("apex." + apex_name + ".ready", "true");
3915 return {};
3916 }
3917
InstallPackageImpl(const std::string & package_path,bool force)3918 Result<ApexFile> InstallPackageImpl(const std::string& package_path,
3919 bool force) {
3920 auto temp_apex = ApexFile::Open(package_path);
3921 if (!temp_apex.ok()) {
3922 return temp_apex.error();
3923 }
3924
3925 const std::string& module_name = temp_apex->GetManifest().name();
3926 // Don't allow non-staged update if there are no active versions of this APEX.
3927 auto cur_mounted_data = gMountedApexes.GetLatestMountedApex(module_name);
3928 if (!cur_mounted_data.has_value()) {
3929 return Error() << "No active version found for package " << module_name;
3930 }
3931
3932 auto cur_apex = ApexFile::Open(cur_mounted_data->full_path);
3933 if (!cur_apex.ok()) {
3934 return cur_apex.error();
3935 }
3936
3937 // Do a quick check if this APEX can be installed without a reboot.
3938 // Note that passing this check doesn't guarantee that APEX will be
3939 // successfully installed.
3940 if (auto r = CheckSupportsNonStagedInstall(*temp_apex, force); !r.ok()) {
3941 return r.error();
3942 }
3943
3944 // 1. Verify that APEX is correct. This is a heavy check that involves
3945 // mounting an APEX on a temporary mount point and reading the entire
3946 // dm-verity block device.
3947 if (auto res = VerifyPackageNonStagedInstall(*temp_apex, force); !res.ok()) {
3948 return res.error();
3949 }
3950
3951 // 2. Compute params for mounting new apex.
3952 auto new_id_minor = ComputePackageIdMinor(*temp_apex);
3953 if (!new_id_minor.ok()) {
3954 return new_id_minor.error();
3955 }
3956
3957 std::string new_id = GetPackageId(temp_apex->GetManifest()) + "_" +
3958 std::to_string(*new_id_minor);
3959
3960 // Before unmounting the current apex, unload it from the init process:
3961 // terminates services started from the apex and init scripts read from the
3962 // apex.
3963 OR_RETURN(UnloadApexFromInit(module_name));
3964
3965 // And then reload it from the init process whether it succeeds or not.
3966 auto reload_apex = android::base::make_scope_guard([&]() {
3967 if (auto status = LoadApexFromInit(module_name); !status.ok()) {
3968 LOG(ERROR) << "Failed to load apex " << module_name
3969 << " : " << status.error().message();
3970 }
3971 });
3972
3973 // 2. Unmount currently active APEX.
3974 if (auto res =
3975 UnmountPackage(*cur_apex, /* allow_latest= */ true,
3976 /* deferred= */ true, /* detach_mount_point= */ force);
3977 !res.ok()) {
3978 return res.error();
3979 }
3980
3981 // 3. Hard link to final destination.
3982 std::string target_file =
3983 StringPrintf("%s/%s.apex", gConfig->active_apex_data_dir, new_id.c_str());
3984
3985 auto guard = android::base::make_scope_guard([&]() {
3986 if (unlink(target_file.c_str()) != 0 && errno != ENOENT) {
3987 PLOG(ERROR) << "Failed to unlink " << target_file;
3988 }
3989 // We can't really rely on the fact that dm-verity device backing up
3990 // previously active APEX is still around. We need to create a new one.
3991 std::string old_new_id = GetPackageId(temp_apex->GetManifest()) + "_" +
3992 std::to_string(*new_id_minor + 1);
3993 auto res = ActivatePackageImpl(*cur_apex, old_new_id,
3994 /* reuse_device= */ false);
3995 if (!res.ok()) {
3996 // At this point not much we can do... :(
3997 LOG(ERROR) << res.error();
3998 }
3999 });
4000
4001 // At this point it should be safe to hard link |temp_apex| to
4002 // |params->target_file|. In case reboot happens during one of the stages
4003 // below, then on next boot apexd will pick up the new verified APEX.
4004 if (link(package_path.c_str(), target_file.c_str()) != 0) {
4005 return ErrnoError() << "Failed to link " << package_path << " to "
4006 << target_file;
4007 }
4008
4009 auto new_apex = ApexFile::Open(target_file);
4010 if (!new_apex.ok()) {
4011 return new_apex.error();
4012 }
4013
4014 // 4. And activate new one.
4015 auto activate_status = ActivatePackageImpl(*new_apex, new_id,
4016 /* reuse_device= */ false);
4017 if (!activate_status.ok()) {
4018 return activate_status.error();
4019 }
4020
4021 // Accept the install.
4022 guard.Disable();
4023
4024 // 4. Now we can unlink old APEX if it's not pre-installed.
4025 if (!ApexFileRepository::GetInstance().IsPreInstalledApex(*cur_apex)) {
4026 if (unlink(cur_mounted_data->full_path.c_str()) != 0) {
4027 PLOG(ERROR) << "Failed to unlink " << cur_mounted_data->full_path;
4028 }
4029 }
4030
4031 if (auto res = UpdateApexInfoList(); !res.ok()) {
4032 LOG(ERROR) << res.error();
4033 }
4034
4035 // Release compressed blocks in case target_file is on f2fs-compressed
4036 // filesystem.
4037 ReleaseF2fsCompressedBlocks(target_file);
4038
4039 return new_apex;
4040 }
4041
InstallPackage(const std::string & package_path,bool force)4042 Result<ApexFile> InstallPackage(const std::string& package_path, bool force) {
4043 LOG(INFO) << "Installing " << package_path;
4044 SendApexInstallationRequestedAtom(
4045 package_path, /* is_rollback */ false,
4046 stats::apex::APEX_INSTALLATION_REQUESTED__INSTALLATION_TYPE__REBOOTLESS);
4047 // TODO: Add error-enums
4048 Result<ApexFile> ret = InstallPackageImpl(package_path, force);
4049 SendApexInstallationEndedAtom(
4050 package_path,
4051 ret.ok()
4052 ? stats::apex::
4053 APEX_INSTALLATION_ENDED__INSTALLATION_RESULT__INSTALL_SUCCESSFUL
4054 : stats::apex::
4055 APEX_INSTALLATION_ENDED__INSTALLATION_RESULT__INSTALL_FAILURE_APEX_INSTALLATION);
4056 return ret;
4057 }
4058
IsActiveApexChanged(const ApexFile & apex)4059 bool IsActiveApexChanged(const ApexFile& apex) {
4060 return gChangedActiveApexes.find(apex.GetManifest().name()) !=
4061 gChangedActiveApexes.end();
4062 }
4063
GetChangedActiveApexesForTesting()4064 std::set<std::string>& GetChangedActiveApexesForTesting() {
4065 return gChangedActiveApexes;
4066 }
4067
4068 } // namespace apex
4069 } // namespace android
4070