Apex的由来,我们都知道普通的apk我们可以通过应用商店playstore等进行更新,apex的引入是google希望也能通过playstore更新bin文件.so etc配置文件等类型文件. 这些文件的安装实际通过apexd来进行,现在我们来解析一下apexd, apexd的启动分为两个阶段,bootstrap和普通apexd启动,下面分析apexd bootstrap
- rc文件启动
/system/core/rootdir/init.rc#80
本阶段启动三个bootstrap apex,分别为com.android.i18n,com.android.runtime com.android.tzdata,在bootstrap 阶段主要就是提供 critical shared libraries
bash
77 # Run apexd-bootstrap so that APEXes that provide critical libraries
78 # become available. Note that this is executed as exec_start to ensure that
79 # the libraries are available to the processes started after this statement.
80 exec_start apexd-bootstrap
- main函数
/system/apex/apexd/apexd_main.cpp#118
cpp
int main(int /*argc*/, char** argv) {
...
118 android::apex::SetConfig(android::apex::kDefaultConfig); → 全局config
...
151 if (has_subcommand) {
152 return HandleSubcommand(argv); → 这里走 apexd --bootstrap
153 }
全局config 就是这些常量:
cpp
61 static const ApexdConfig kDefaultConfig = {
62 kApexStatusSysprop, → kApexStatusSysprop = "apexd.status" apex的状态
63 kApexPackageBuiltinDirs, → 所有apex文件所在的目录data、product、system、system_ext、vendor
64 kActiveApexPackagesDataDir, → kActiveApexPackagesDataDir = "/data/apex/active"
65 kApexDecompressedDir, → kApexDecompressedDir = "/data/apex/decompressed";
66 kOtaReservedDir, → kOtaReservedDir = "/data/apex/ota_reserved";
67 kApexHashTreeDir, → kApexHashTreeDir = "/data/apex/hashtree";
68 kStagedSessionsDir, → StagedSessionsDir = "/data/app-staging"
69 kMetadataSepolicyStagedDir, → kMetadataSepolicyStagedDir = "/metadata/sepolicy/staged";
70 kVmPayloadMetadataPartitionProp, → kVmPayloadMetadataPartitionProp = "apexd.payload_metadata.path"
71 "u:object_r:staging_data_file", → staging_data_file的 file contexts
72 };
- OnBootstrap()
cpp
/system/apex/apexd/apexd_main.cpp#38
36 int HandleSubcommand(char** argv) {
37 if (strcmp("--bootstrap", argv[1]) == 0) {
38 SetDefaultTag("apexd-bootstrap");
39 LOG(INFO) << "Bootstrap subcommand detected";
40 return android::apex::OnBootstrap();
41 }
/system/apex/apexd/apexd.cpp#2566
2566 int OnBootstrap() {
2567 ATRACE_NAME("OnBootstrap");
2568 auto time_started = boot_clock::now();
2569
2570 ApexFileRepository& instance = ApexFileRepository::GetInstance(); → 创建个实例 啥也没做
2571 Result<void> status =
2572 instance.AddPreInstalledApex(gConfig->apex_built_in_dirs); → 3.1 scan kApexPackageBuiltinDirs下的所有apex, 详见3.1.1
2573 if (!status.ok()) {
2574 LOG(ERROR) << "Failed to collect APEX keys : " << status.error();
2575 return 1;
2576 }
2577
2578 const auto& pre_installed_apexes = instance.GetPreInstalledApexFiles(); → 从全局变量中得到 pre_installed_store_
2579 int loop_device_cnt = pre_installed_apexes.size();
2580 // Find all bootstrap apexes
2581 std::vector<ApexFileRef> bootstrap_apexes;
2582 for (const auto& apex : pre_installed_apexes) { → 遍历 所有apexfile
2583 if (IsBootstrapApex(apex.get())) { → 判断是否是bootstrap apex,有三个com.android.i18n com.android.runtime com.android.tzdata
2584 LOG(INFO) << "Found bootstrap APEX " << apex.get().GetPath();
2585 bootstrap_apexes.push_back(apex);
2586 loop_device_cnt++;
2587 }
2588 if (apex.get().GetManifest().providesharedapexlibs()) {
2589 LOG(INFO) << "Found sharedlibs APEX " << apex.get().GetPath();
2590 // Sharedlis APEX might be mounted 2 times:
2591 // * Pre-installed sharedlibs APEX will be mounted in OnStart
2592 // * Updated sharedlibs APEX (if it exists) will be mounted in OnStart
2593 //
2594 // We already counted a loop device for one of these 2 mounts, need to add
2595 // 1 more.
2596 loop_device_cnt++;
2597 }
2598 }
.........
2613 if (auto res = loop::PreAllocateLoopDevices(loop_device_cnt); !res.ok()) { -----> 通过ioctl /dev/loop-control,按apex数量预先分配loop设备
2614 LOG(ERROR) << "Failed to pre-allocate loop devices : " << res.error();
2615 }
2616
2617 DeviceMapper& dm = DeviceMapper::Instance(); -----> 创建 DeviceMapper实例,其实就是open(/dev/device-mapper)
2618 // Create empty dm device for each found APEX.
2619 // This is a boot time optimization that makes use of the fact that user space
2620 // paths will be created by ueventd before apexd is started, and hence
2621 // reducing the time to activate APEXEs on /data.
2622 // Note: since at this point we don't know which APEXes are updated, we are
2623 // optimistically creating a verity device for all of them. Once boot
2624 // finishes, apexd will clean up unused devices.
2625 // TODO(b/192241176): move to apexd_verity.{h,cpp}
2626 for (const auto& apex : pre_installed_apexes) {
2627 const std::string& name = apex.get().GetManifest().name();
2628 if (!dm.CreateEmptyDevice(name)) { -----> 按照apex的名字,所有apex创建空的dm device,ioctl(fd_, DM_DEV_CREATE, &io)
2629 LOG(ERROR) << "Failed to create empty device " << name;
2630 }
2631 }
2632
2633 // Create directories for APEX shared libraries.
2634 auto sharedlibs_apex_dir = CreateSharedLibsApexDir(); -----> 创建目录 /apex/sharedlibs/lib{,64} for SharedLibs APEXes
2635 if (!sharedlibs_apex_dir.ok()) {
2636 LOG(ERROR) << sharedlibs_apex_dir.error();
2637 return 1;
2638 }
2639
2640 // Now activate bootstrap apexes.
2641 auto ret =
2642 ActivateApexPackages(bootstrap_apexes, ActivationMode::kBootstrapMode); ----> 真正的重点来了 bootstrap阶段的ActivateApexPackages(),见3.1.2
2643 if (!ret.ok()) {
2644 LOG(ERROR) << "Failed to activate bootstrap apex files : " << ret.error();
2645 return 1;
2646 }
2647
2648 OnAllPackagesActivated(/*is_bootstrap=*/true);
2649 auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
2650 boot_clock::now() - time_started).count();
2651 LOG(INFO) << "OnBootstrap done, duration=" << time_elapsed;
2652 return 0;
2653 }
3.1.1 ScanBuiltInDir 遍历
cpp
59 Result<void> ApexFileRepository::ScanBuiltInDir(const std::string& dir) {
60 LOG(INFO) << "Scanning " << dir << " for pre-installed ApexFiles";
.....
72 // TODO(b/179248390): scan parallelly if possible
73 for (const auto& file : *all_apex_files) { → 遍历所有apex
74 LOG(INFO) << "Found pre-installed APEX " << file;
75 Result<ApexFile> apex_file = ApexFile::Open(file); → 得到解析的ApexFile
.....
134 auto it = pre_installed_store_.find(name);
135 if (it == pre_installed_store_.end()) {
136 pre_installed_store_.emplace(name, std::move(*apex_file)); → apex_file保存到全局变量中pre_installed_store_
普通apex解压如下:
cpp
1 普通apex:
com.android.runtime-arm64$ ls
AndroidManifest.xml apex_build_info.pb apex_manifest.pb apex_payload.img apex_pubkey assets META-INF resources.arsc
2 compressed apex解压如下:
compressed ape实际上是对origin_apex又包了一层
com.google.android.art_compressed$ ls
AndroidManifest.xml apex_build_info.pb apex_manifest.pb apex_pubkey META-INF original_apex 'original_apex (1)' stamp-cert-sha256
解压original_apex
com.google.android.art_compressed/original_apex (1)$ ls
AndroidManifest.xml apex_build_info.pb apex_manifest.pb apex_payload.img apex_pubkey assets META-INF stamp-cert-sha256
关键文件是: apex_payload.img(可以直接挂载) apex_pubkey(用于avb验证) apex_manifest.pb
主要解压以下内容,compressed apex只解压一层:
cpp
/system/apex/apexd/apex_file.cpp#81
81 Result<ApexFile> ApexFile::Open(const std::string& path) {
82 std::optional<uint32_t> image_offset;
83 std::optional<size_t> image_size;
84 std::string manifest_content;
85 std::string pubkey;
86 std::optional<std::string> fs_type;
87 ZipEntry entry;
88
89 unique_fd fd(open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
90 if (fd < 0) {
91 return ErrnoError() << "Failed to open package " << path << ": "
92 << "I/O error";
93 }
94
95 ZipArchiveHandle handle;
96 auto handle_guard =
97 android::base::make_scope_guard([&handle] { CloseArchive(handle); });
98 int ret = OpenArchiveFd(fd.get(), path.c_str(), &handle,
99 /*assume_ownership=*/false);
100 if (ret < 0) {
101 return Error() << "Failed to open package " << path << ": "
102 << ErrorCodeString(ret);
103 }
104
105 bool is_compressed = true;
106 ret = FindEntry(handle, kCompressedApexFilename, &entry); ==> 常量为original_apex 只是check 一下是否是compressed apex啥也不做
107 if (ret < 0) {
108 is_compressed = false;
109 }
110
111 if (!is_compressed) {
112 // Locate the mountable image within the zipfile and store offset and size.
113 ret = FindEntry(handle, kImageFilename, &entry); ==> 不是compressed apex,解压apex_payload.img,获取文件系统类型 mount时使用
114 if (ret < 0) {
115 return Error() << "Could not find entry \"" << kImageFilename
116 << "\" or \"" << kCompressedApexFilename
117 << "\" in package " << path << ": "
118 << ErrorCodeString(ret);
119 }
120 image_offset = entry.offset;
121 image_size = entry.uncompressed_length;
122
123 auto fs_type_result = RetrieveFsType(fd, image_offset.value()); ==》 f2fs ext4 erofs
124 if (!fs_type_result.ok()) {
125 return Error() << "Failed to retrieve filesystem type for " << path
126 << ": " << fs_type_result.error();
127 }
128 fs_type = std::move(*fs_type_result);
129 }
130
131 ret = FindEntry(handle, kManifestFilenamePb, &entry); ==> 解压apex_manifest.pb的内容保存起来
132 if (ret < 0) {
133 return Error() << "Could not find entry \"" << kManifestFilenamePb
134 << "\" in package " << path << ": " << ErrorCodeString(ret);
135 }
136
137 uint32_t length = entry.uncompressed_length;
138 manifest_content.resize(length, '\0');
139 ret = ExtractToMemory(handle, &entry,
140 reinterpret_cast<uint8_t*>(&(manifest_content)[0]),
141 length);
142 if (ret != 0) {
143 return Error() << "Failed to extract manifest from package " << path << ": "
144 << ErrorCodeString(ret);
145 }
146
147 ret = FindEntry(handle, kBundledPublicKeyFilename, &entry); ==> 解压apex_pubkey的内容保存起来
148 if (ret >= 0) {
149 length = entry.uncompressed_length;
150 pubkey.resize(length, '\0');
151 ret = ExtractToMemory(handle, &entry,
152 reinterpret_cast<uint8_t*>(&(pubkey)[0]), length);
153 if (ret != 0) {
154 return Error() << "Failed to extract public key from package " << path
155 << ": " << ErrorCodeString(ret);
156 }
157 }
158
159 Result<ApexManifest> manifest = ParseManifest(manifest_content);
160 if (!manifest.ok()) {
161 return manifest.error();
162 }
163
164 if (is_compressed && manifest->providesharedapexlibs()) {
165 return Error() << "Apex providing sharedlibs shouldn't be compressed";
166 }
167
168 // b/179211712 the stored path should be the realpath, otherwise the path we
169 // get by scanning the directory would be different from the path we get
170 // by reading /proc/mounts, if the apex file is on a symlink dir.
171 std::string realpath;
172 if (!android::base::Realpath(path, &realpath)) {
173 return ErrnoError() << "can't get realpath of " << path;
174 }
175
176 return ApexFile(realpath, image_offset, image_size, std::move(*manifest), ==> 返回一个ApexFile
177 pubkey, fs_type, is_compressed);
178 }
apex_manifest.pb的内容就是:
bash
/system/apex/proto/apex_manifest.proto#17
syntax = "proto3";
18
19 package apex.proto;
20
21 option java_package = "com.android.apex";
22 option java_outer_classname = "Protos";
23
24 message ApexManifest {
25
26 // APEX Name. Note that this can be different from what PackageManager sees.
27 // This is used to identify an APEX and to mount under /apex directory.
28 string name = 1;
29
30 // Version Number
31 int64 version = 2;
32
33 // Pre Install Hook
34 string preInstallHook = 3;
35
36 // Post Install Hook
37 // This feature is not supported.
38 string postInstallHook = 4 [ deprecated = true ];
39
40 // Version Name
41 string versionName = 5;
42
43 // Signals whenever this APEX doesn't contain any executable code.
44 // If this field is set to true, then apexd will mount this apex
45 // with MS_NOEXEC flag.
46 bool noCode = 6;
47
48 // List of native libs which can be used by other apexes or system.
49 repeated string provideNativeLibs = 7;
50
51 // List of native libs which this apex uses from other apexes or system.
52 repeated string requireNativeLibs = 8;
53
54 // List of JNI libs.
55 // linkerconfig/libnativeloader use this field so that java libraries can
56 // load JNI libraries in the same apex.
57 // This is supposed to be filled by the build system with libraries which are
58 // marked as "is_jni: true" from the list of "native_shared_libs".
59 repeated string jniLibs = 9;
60
61 // List of libs required that are located in a shared libraries APEX. The
62 // Android platform only checks whether this list is non-empty, and by default
63 // the Android build system never sets this. This field can be used when
64 // producing or processing an APEX using libraries in /apex/sharedlibs (see
65 // `provideSharedApexLibs` field) to store some information about the
66 // libraries.
67 repeated string requireSharedApexLibs = 10;
68
69 // Whether this APEX provides libraries to be shared with other APEXs. This
70 // causes libraries contained in the APEX to be made available under
71 // /apex/sharedlibs .
72 bool provideSharedApexLibs = 11;
73
74 message CompressedApexMetadata { ---> 只有compressed apex存在此字段,用于avb验证
75
76 // Valid only for compressed APEX. This field contains the root digest of
77 // the original_apex contained inside CAPEX.
78 string originalApexDigest = 1;
79 }
80
81 // Exists only for compressed APEX
82 CompressedApexMetadata capexMetadata = 12;
83
84 // Indicates that this APEX can be updated without rebooting device.
85 bool supportsRebootlessUpdate = 13;
86
87 // VNDK version for apexes depending on a specific version of VNDK libs.
88 string vndkVersion = 14;
89 }
3.1.2 bootstrap阶段的三个bootstrap apex的激活ActivateApexPackages()
cpp
1810 std::vector<std::future<std::vector<Result<const ApexFile*>>>> futures;
1811 futures.reserve(worker_num);
1812 for (size_t i = 0; i < worker_num; i++) {
1813 futures.push_back(std::async(std::launch::async, ActivateApexWorker, ------> 使用ActivateApexWorker 异步 activate apexes
1814 std::ref(mode), std::ref(apex_queue),
1815 std::ref(apex_queue_mutex)));
1816 }
cpp
/system/apex/apexd/apexd.cpp#1744
1744 std::vector<Result<const ApexFile*>> ActivateApexWorker(
1745 ActivationMode mode, std::queue<const ApexFile*>& apex_queue,
1746 std::mutex& mutex) {
1747 ATRACE_NAME("ActivateApexWorker");
1748 std::vector<Result<const ApexFile*>> ret;
1749
1750 while (true) {
1751 const ApexFile* apex;
1752 { //为什么这写了个代码块,所有的线程操作同一个变量apex_queue,当然要加智能锁(代码块完事自动释放),多少个apex就开多少个线程,FIFO, pop
1753 std::lock_guard lock(mutex);
1754 if (apex_queue.empty()) break;
1755 apex = apex_queue.front();
1756 apex_queue.pop();
1757 }
1758
1759 std::string device_name;
1760 if (mode == ActivationMode::kBootMode) {
1761 device_name = apex->GetManifest().name();
1762 } else {
1763 device_name = GetPackageId(apex->GetManifest());
1764 }
1765 if (mode == ActivationMode::kOtaChrootMode) {
1766 device_name += ".chroot";
1767 }
1768 bool reuse_device = mode == ActivationMode::kBootMode;
1769 auto res = ActivatePackageImpl(*apex, device_name, reuse_device); ----> 正式激活apex package
1770 if (!res.ok()) {
1771 ret.push_back(Error() << "Failed to activate " << apex->GetPath() << "("
1772 << device_name << "): " << res.error());
1773 } else {
1774 ret.push_back({apex});
1775 }
1776 }
1777
1778 return ret;
1779 }
cpp
/system/apex/apexd/apexd.cpp?#1361
1361 Result<void> ActivatePackageImpl(const ApexFile& apex_file,
1362 const std::string& device_name,
1363 bool reuse_device) {
...................................................................
1419 const std::string& mount_point =
1420 apexd_private::GetPackageMountPoint(manifest);
1421
1422 if (!version_found_mounted) {
1423 auto mount_status = MountPackage(apex_file, mount_point, device_name, ------------------> 实际的mount动作 见第四节
1424 reuse_device, /*temp_mount=*/false);
1425 if (!mount_status.ok()) {
1426 return mount_status;
1427 }
1428 }
bind mount:
1419 const std::string& mount_point =
1420 apexd_private::GetPackageMountPoint(manifest); -----------> /aepx/com.android.adbd@340912002
1421
...........
1439 bool mounted_latest = false;
1440 // Bind mount the latest version to /apex/<package_name>, unless the
1441 // package provides shared libraries to other APEXs.
1442 if (is_newest_version) {
1443 const Result<void>& update_st = apexd_private::BindMount(
1444 apexd_private::GetActiveMountPoint(manifest), mount_point); ----------> GetActiveMountPoint() /aepx/com.android.adbd
例:
mount --bind /aepx/com.android.adbd /aepx/com.android.adbd@340912002
4 这里是把各个目录的apex mount到根目录的/apex下 顺便介绍下avb验证
cpp
/system/apex/apexd/apexd.cpp#MountPackage
1151 Result<void> MountPackage(const ApexFile& apex, const std::string& mount_point,
1152 const std::string& device_name, bool reuse_device,
1153 bool temp_mount) {
1154 auto ret =
1155 MountPackageImpl(apex, mount_point, device_name,
1156 GetHashTreeFileName(apex, /* is_new= */ false),
1157 /* verify_image = */ false, reuse_device, temp_mount);
/system/apex/apexd/apexd.cpp#436
436 Result<MountedApexData> MountPackageImpl(const ApexFile& apex,
437 const std::string& mount_point,
438 const std::string& device_name,
439 const std::string& hashtree_file,
440 bool verify_image, bool reuse_device,
441 bool temp_mount = false) {
.......................
498 auto& instance = ApexFileRepository::GetInstance();
499
500 auto public_key = instance.GetPublicKey(apex.GetManifest().name()); ----------------> 获取avbpubkey
501 if (!public_key.ok()) {
502 return public_key.error();
503 }
504
505 auto verity_data = apex.VerifyApexVerity(*public_key); -------> 验证apex_payload.img的vbmeta,这个类似于chain partition的验证,验证摘要签名再对比pubkey
......................
557 auto verity_table =
558 CreateVerityTable(*verity_data, loopback_device.name, hash_device, -----------> 创建hashtree
559 /* restart_on_corruption = */ !verify_image);
560 Result<DmVerityDevice> verity_dev_res =
561 CreateVerityDevice(device_name, *verity_table, reuse_device); -----------> 创建dm-verity