blob: 32a6cc784269ddcc019b0328483d34830ab83ec7 [file] [log] [blame]
Cole Faust74ee4e02025-01-16 14:55:35 -08001// Copyright (C) 2024 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package filesystem
16
17import (
18 "android/soong/android"
Spandan Das21643c62025-03-18 22:24:34 +000019 "fmt"
Cole Faust74ee4e02025-01-16 14:55:35 -080020 "path/filepath"
Spandan Das21643c62025-03-18 22:24:34 +000021 "sort"
Cole Faust74ee4e02025-01-16 14:55:35 -080022 "strings"
Spandan Das21643c62025-03-18 22:24:34 +000023 "time"
Cole Faust74ee4e02025-01-16 14:55:35 -080024
25 "github.com/google/blueprint"
26 "github.com/google/blueprint/proptools"
27)
28
Cole Faust3c02ffa2025-02-19 15:20:20 -080029var (
30 systemOtherPropFileTweaks = pctx.AndroidStaticRule("system_other_prop_file_tweaks", blueprint.RuleParams{
31 Command: `rm -rf $out && sed -e 's@^mount_point=/$$@mount_point=system_other@g' -e 's@^partition_name=system$$@partition_name=system_other@g' $in > $out`,
32 })
33)
34
Cole Faust74ee4e02025-01-16 14:55:35 -080035type SystemOtherImageProperties struct {
36 // The system_other image always requires a reference to the system image. The system_other
37 // partition gets built into the system partition's "b" slot in a/b partition builds. Thus, it
38 // copies most of its configuration from the system image, such as filesystem type, avb signing
39 // info, etc. Including it here does not automatically mean that it will pick up the system
40 // image's dexpropt files, it must also be listed in Preinstall_dexpreopt_files_from for that.
41 System_image *string
42
43 // This system_other partition will include all the dexpreopt files from the apps on these
44 // partitions.
45 Preinstall_dexpreopt_files_from []string
46}
47
48type systemOtherImage struct {
49 android.ModuleBase
50 android.DefaultableModuleBase
51 properties SystemOtherImageProperties
52}
53
54// The system_other image is the default contents of the "b" slot of the system image.
55// It contains the dexpreopt files of all the apps on the device, for a faster first boot.
56// Afterwards, at runtime, it will be used as a regular b slot for OTA updates, and the initial
57// dexpreopt files will be deleted.
58func SystemOtherImageFactory() android.Module {
59 module := &systemOtherImage{}
60 module.AddProperties(&module.properties)
Cole Faustb8e280f2025-01-16 16:33:26 -080061 android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
Cole Faust74ee4e02025-01-16 14:55:35 -080062 android.InitDefaultableModule(module)
63 return module
64}
65
66type systemImageDeptag struct {
67 blueprint.BaseDependencyTag
68}
69
70var systemImageDependencyTag = systemImageDeptag{}
71
72type dexpreoptDeptag struct {
73 blueprint.BaseDependencyTag
74}
75
76var dexpreoptDependencyTag = dexpreoptDeptag{}
77
78func (m *systemOtherImage) DepsMutator(ctx android.BottomUpMutatorContext) {
79 if proptools.String(m.properties.System_image) == "" {
80 ctx.ModuleErrorf("system_image property must be set")
81 return
82 }
83 ctx.AddDependency(ctx.Module(), systemImageDependencyTag, *m.properties.System_image)
84 ctx.AddDependency(ctx.Module(), dexpreoptDependencyTag, m.properties.Preinstall_dexpreopt_files_from...)
85}
86
87func (m *systemOtherImage) GenerateAndroidBuildActions(ctx android.ModuleContext) {
88 systemImage := ctx.GetDirectDepProxyWithTag(*m.properties.System_image, systemImageDependencyTag)
89 systemInfo, ok := android.OtherModuleProvider(ctx, systemImage, FilesystemProvider)
90 if !ok {
91 ctx.PropertyErrorf("system_image", "Expected system_image module to provide FilesystemProvider")
92 return
93 }
94
95 output := android.PathForModuleOut(ctx, "system_other.img")
96 stagingDir := android.PathForModuleOut(ctx, "staging_dir")
Spandan Das7a42d1c2025-02-12 01:32:21 +000097 stagingDirTimestamp := android.PathForModuleOut(ctx, "staging_dir.timestamp")
Cole Faust74ee4e02025-01-16 14:55:35 -080098
99 builder := android.NewRuleBuilder(pctx, ctx)
100 builder.Command().Textf("rm -rf %s && mkdir -p %s", stagingDir, stagingDir)
101
Cole Faustb8e280f2025-01-16 16:33:26 -0800102 specs := make(map[string]android.PackagingSpec)
Cole Faust74ee4e02025-01-16 14:55:35 -0800103 for _, otherPartition := range m.properties.Preinstall_dexpreopt_files_from {
104 dexModule := ctx.GetDirectDepProxyWithTag(otherPartition, dexpreoptDependencyTag)
Cole Faustb8e280f2025-01-16 16:33:26 -0800105 fsInfo, ok := android.OtherModuleProvider(ctx, dexModule, FilesystemProvider)
Cole Faust74ee4e02025-01-16 14:55:35 -0800106 if !ok {
107 ctx.PropertyErrorf("preinstall_dexpreopt_files_from", "Expected module %q to provide FilesystemProvider", otherPartition)
108 return
109 }
Cole Faustb8e280f2025-01-16 16:33:26 -0800110 // Merge all the packaging specs into 1 map
111 for k := range fsInfo.SpecsForSystemOther {
112 if _, ok := specs[k]; ok {
113 ctx.ModuleErrorf("Packaging spec %s given by two different partitions", k)
114 continue
115 }
116 specs[k] = fsInfo.SpecsForSystemOther[k]
117 }
118 }
119
120 // TOOD: CopySpecsToDir only exists on PackagingBase, but doesn't use any fields from it. Clean this up.
121 (&android.PackagingBase{}).CopySpecsToDir(ctx, builder, specs, stagingDir)
122
Wei Liaf2b25c2025-03-19 16:31:24 -0700123 fullInstallPaths := []string{}
Cole Faustb8e280f2025-01-16 16:33:26 -0800124 if len(m.properties.Preinstall_dexpreopt_files_from) > 0 {
125 builder.Command().Textf("touch %s", filepath.Join(stagingDir.String(), "system-other-odex-marker"))
Wei Liaf2b25c2025-03-19 16:31:24 -0700126 installPath := android.PathForModuleInPartitionInstall(ctx, "system_other", "system-other-odex-marker")
127 fullInstallPaths = append(fullInstallPaths, installPath.String())
Cole Faust74ee4e02025-01-16 14:55:35 -0800128 }
Spandan Das7a42d1c2025-02-12 01:32:21 +0000129 builder.Command().Textf("touch").Output(stagingDirTimestamp)
130 builder.Build("assemble_filesystem_staging_dir", "Assemble filesystem staging dir")
Cole Faust74ee4e02025-01-16 14:55:35 -0800131
132 // Most of the time, if build_image were to call a host tool, it accepts the path to the
133 // host tool in a field in the prop file. However, it doesn't have that option for fec, which
134 // it expects to just be on the PATH. Add fec to the PATH.
135 fec := ctx.Config().HostToolPath(ctx, "fec")
136 pathToolDirs := []string{filepath.Dir(fec.String())}
137
Cole Faust3c02ffa2025-02-19 15:20:20 -0800138 // In make, the exact same prop file is used for both system and system_other. However, I
139 // believe make goes through a different build_image code path that is based on the name of
140 // the output file. So it sees the output file is named system_other.img and makes some changes.
141 // We don't use that codepath, so make the changes manually to the prop file.
142 propFile := android.PathForModuleOut(ctx, "prop")
143 ctx.Build(pctx, android.BuildParams{
144 Rule: systemOtherPropFileTweaks,
145 Input: systemInfo.BuildImagePropFile,
146 Output: propFile,
147 })
148
Spandan Das7a42d1c2025-02-12 01:32:21 +0000149 builder = android.NewRuleBuilder(pctx, ctx)
Cole Faust74ee4e02025-01-16 14:55:35 -0800150 builder.Command().
151 Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
152 BuiltTool("build_image").
153 Text(stagingDir.String()). // input directory
Cole Faust3c02ffa2025-02-19 15:20:20 -0800154 Input(propFile).
Cole Faust74ee4e02025-01-16 14:55:35 -0800155 Implicits(systemInfo.BuildImagePropFileDeps).
156 Implicit(fec).
Spandan Das7a42d1c2025-02-12 01:32:21 +0000157 Implicit(stagingDirTimestamp).
Cole Faust74ee4e02025-01-16 14:55:35 -0800158 Output(output).
159 Text(stagingDir.String())
160
161 builder.Build("build_system_other", "build system other")
162
Spandan Das7a42d1c2025-02-12 01:32:21 +0000163 // Create a hermetic system_other.img with pinned timestamps
164 builder = android.NewRuleBuilder(pctx, ctx)
165 outputHermetic := android.PathForModuleOut(ctx, "for_target_files", "system_other.img")
Cole Faust3c02ffa2025-02-19 15:20:20 -0800166 outputHermeticPropFile := m.propFileForHermeticImg(ctx, builder, propFile)
Spandan Das7a42d1c2025-02-12 01:32:21 +0000167 builder.Command().
168 Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
169 BuiltTool("build_image").
170 Text(stagingDir.String()). // input directory
171 Input(outputHermeticPropFile).
172 Implicits(systemInfo.BuildImagePropFileDeps).
173 Implicit(fec).
174 Implicit(stagingDirTimestamp).
175 Output(outputHermetic).
176 Text(stagingDir.String())
177
178 builder.Build("build_system_other_hermetic", "build system other")
179
180 fsInfo := FilesystemInfo{
Spandan Das21643c62025-03-18 22:24:34 +0000181 Output: output,
182 OutputHermetic: outputHermetic,
183 RootDir: stagingDir,
184 FilesystemConfig: m.generateFilesystemConfig(ctx, stagingDir, stagingDirTimestamp),
185 PropFileForMiscInfo: m.buildPropFileForMiscInfo(ctx),
Spandan Das7a42d1c2025-02-12 01:32:21 +0000186 }
187
188 android.SetProvider(ctx, FilesystemProvider, fsInfo)
189
Cole Faust74ee4e02025-01-16 14:55:35 -0800190 ctx.SetOutputFiles(android.Paths{output}, "")
191 ctx.CheckbuildFile(output)
Wei Liaf2b25c2025-03-19 16:31:24 -0700192
193 // Dump compliance metadata
194 complianceMetadataInfo := ctx.ComplianceMetadataInfo()
195 complianceMetadataInfo.SetFilesContained(fullInstallPaths)
Cole Faust74ee4e02025-01-16 14:55:35 -0800196}
Spandan Das7a42d1c2025-02-12 01:32:21 +0000197
Spandan Das6cc14722025-02-27 06:43:11 +0000198func (s *systemOtherImage) generateFilesystemConfig(ctx android.ModuleContext, stagingDir, stagingDirTimestamp android.Path) android.Path {
199 out := android.PathForModuleOut(ctx, "filesystem_config.txt")
200 ctx.Build(pctx, android.BuildParams{
201 Rule: fsConfigRule,
202 Input: stagingDirTimestamp, // assemble the staging directory
203 Output: out,
204 Args: map[string]string{
205 "rootDir": stagingDir.String(),
206 "prefix": "system/",
207 },
208 })
209 return out
210}
211
Spandan Das7a42d1c2025-02-12 01:32:21 +0000212func (f *systemOtherImage) propFileForHermeticImg(ctx android.ModuleContext, builder *android.RuleBuilder, inputPropFile android.Path) android.Path {
213 propFilePinnedTimestamp := android.PathForModuleOut(ctx, "for_target_files", "prop")
214 builder.Command().Textf("cat").Input(inputPropFile).Flag(">").Output(propFilePinnedTimestamp).
215 Textf(" && echo use_fixed_timestamp=true >> %s", propFilePinnedTimestamp)
216 return propFilePinnedTimestamp
217}
Spandan Das21643c62025-03-18 22:24:34 +0000218
219func (f *systemOtherImage) buildPropFileForMiscInfo(ctx android.ModuleContext) android.Path {
220 var lines []string
221 addStr := func(name string, value string) {
222 lines = append(lines, fmt.Sprintf("%s=%s", name, value))
223 }
224
225 addStr("building_system_other_image", "true")
226
227 systemImage := ctx.GetDirectDepProxyWithTag(*f.properties.System_image, systemImageDependencyTag)
228 systemInfo, ok := android.OtherModuleProvider(ctx, systemImage, FilesystemProvider)
229 if !ok {
230 ctx.PropertyErrorf("system_image", "Expected system_image module to provide FilesystemProvider")
231 return nil
232 }
233 if systemInfo.PartitionSize == nil {
234 addStr("system_other_disable_sparse", "true")
235 }
236 if systemInfo.UseAvb {
237 addStr("avb_system_other_hashtree_enable", "true")
238 addStr("avb_system_other_algorithm", systemInfo.AvbAlgorithm)
239 footerArgs := fmt.Sprintf("--hash_algorithm %s", systemInfo.AvbHashAlgorithm)
240 if rollbackIndex, err := f.avbRollbackIndex(ctx); err == nil {
241 footerArgs += fmt.Sprintf(" --rollback_index %d", rollbackIndex)
242 } else {
243 ctx.ModuleErrorf("Could not determine rollback_index %s\n", err)
244 }
245 addStr("avb_system_other_add_hashtree_footer_args", footerArgs)
246 if systemInfo.AvbKey != nil {
247 addStr("avb_system_other_key_path", systemInfo.AvbKey.String())
248 }
249 }
250
251 sort.Strings(lines)
252
253 propFile := android.PathForModuleOut(ctx, "prop_file")
254 android.WriteFileRule(ctx, propFile, strings.Join(lines, "\n"))
255 return propFile
256}
257
258// Use the default: PlatformSecurityPatch
259// TODO: Get this value from vbmeta_system
260func (f *systemOtherImage) avbRollbackIndex(ctx android.ModuleContext) (int64, error) {
261 t, err := time.Parse(time.DateOnly, ctx.Config().PlatformSecurityPatch())
262 if err != nil {
263 return -1, err
264 }
265 return t.Unix(), err
266}