blob: 348c01035fd7a13f84425575c7c6e6a3816038ab [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
123 if len(m.properties.Preinstall_dexpreopt_files_from) > 0 {
124 builder.Command().Textf("touch %s", filepath.Join(stagingDir.String(), "system-other-odex-marker"))
Cole Faust74ee4e02025-01-16 14:55:35 -0800125 }
Spandan Das7a42d1c2025-02-12 01:32:21 +0000126 builder.Command().Textf("touch").Output(stagingDirTimestamp)
127 builder.Build("assemble_filesystem_staging_dir", "Assemble filesystem staging dir")
Cole Faust74ee4e02025-01-16 14:55:35 -0800128
129 // Most of the time, if build_image were to call a host tool, it accepts the path to the
130 // host tool in a field in the prop file. However, it doesn't have that option for fec, which
131 // it expects to just be on the PATH. Add fec to the PATH.
132 fec := ctx.Config().HostToolPath(ctx, "fec")
133 pathToolDirs := []string{filepath.Dir(fec.String())}
134
Cole Faust3c02ffa2025-02-19 15:20:20 -0800135 // In make, the exact same prop file is used for both system and system_other. However, I
136 // believe make goes through a different build_image code path that is based on the name of
137 // the output file. So it sees the output file is named system_other.img and makes some changes.
138 // We don't use that codepath, so make the changes manually to the prop file.
139 propFile := android.PathForModuleOut(ctx, "prop")
140 ctx.Build(pctx, android.BuildParams{
141 Rule: systemOtherPropFileTweaks,
142 Input: systemInfo.BuildImagePropFile,
143 Output: propFile,
144 })
145
Spandan Das7a42d1c2025-02-12 01:32:21 +0000146 builder = android.NewRuleBuilder(pctx, ctx)
Cole Faust74ee4e02025-01-16 14:55:35 -0800147 builder.Command().
148 Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
149 BuiltTool("build_image").
150 Text(stagingDir.String()). // input directory
Cole Faust3c02ffa2025-02-19 15:20:20 -0800151 Input(propFile).
Cole Faust74ee4e02025-01-16 14:55:35 -0800152 Implicits(systemInfo.BuildImagePropFileDeps).
153 Implicit(fec).
Spandan Das7a42d1c2025-02-12 01:32:21 +0000154 Implicit(stagingDirTimestamp).
Cole Faust74ee4e02025-01-16 14:55:35 -0800155 Output(output).
156 Text(stagingDir.String())
157
158 builder.Build("build_system_other", "build system other")
159
Spandan Das7a42d1c2025-02-12 01:32:21 +0000160 // Create a hermetic system_other.img with pinned timestamps
161 builder = android.NewRuleBuilder(pctx, ctx)
162 outputHermetic := android.PathForModuleOut(ctx, "for_target_files", "system_other.img")
Cole Faust3c02ffa2025-02-19 15:20:20 -0800163 outputHermeticPropFile := m.propFileForHermeticImg(ctx, builder, propFile)
Spandan Das7a42d1c2025-02-12 01:32:21 +0000164 builder.Command().
165 Textf("PATH=%s:$PATH", strings.Join(pathToolDirs, ":")).
166 BuiltTool("build_image").
167 Text(stagingDir.String()). // input directory
168 Input(outputHermeticPropFile).
169 Implicits(systemInfo.BuildImagePropFileDeps).
170 Implicit(fec).
171 Implicit(stagingDirTimestamp).
172 Output(outputHermetic).
173 Text(stagingDir.String())
174
175 builder.Build("build_system_other_hermetic", "build system other")
176
177 fsInfo := FilesystemInfo{
Spandan Das21643c62025-03-18 22:24:34 +0000178 Output: output,
179 OutputHermetic: outputHermetic,
180 RootDir: stagingDir,
181 FilesystemConfig: m.generateFilesystemConfig(ctx, stagingDir, stagingDirTimestamp),
182 PropFileForMiscInfo: m.buildPropFileForMiscInfo(ctx),
Spandan Das7a42d1c2025-02-12 01:32:21 +0000183 }
184
185 android.SetProvider(ctx, FilesystemProvider, fsInfo)
186
Cole Faust74ee4e02025-01-16 14:55:35 -0800187 ctx.SetOutputFiles(android.Paths{output}, "")
188 ctx.CheckbuildFile(output)
189}
Spandan Das7a42d1c2025-02-12 01:32:21 +0000190
Spandan Das6cc14722025-02-27 06:43:11 +0000191func (s *systemOtherImage) generateFilesystemConfig(ctx android.ModuleContext, stagingDir, stagingDirTimestamp android.Path) android.Path {
192 out := android.PathForModuleOut(ctx, "filesystem_config.txt")
193 ctx.Build(pctx, android.BuildParams{
194 Rule: fsConfigRule,
195 Input: stagingDirTimestamp, // assemble the staging directory
196 Output: out,
197 Args: map[string]string{
198 "rootDir": stagingDir.String(),
199 "prefix": "system/",
200 },
201 })
202 return out
203}
204
Spandan Das7a42d1c2025-02-12 01:32:21 +0000205func (f *systemOtherImage) propFileForHermeticImg(ctx android.ModuleContext, builder *android.RuleBuilder, inputPropFile android.Path) android.Path {
206 propFilePinnedTimestamp := android.PathForModuleOut(ctx, "for_target_files", "prop")
207 builder.Command().Textf("cat").Input(inputPropFile).Flag(">").Output(propFilePinnedTimestamp).
208 Textf(" && echo use_fixed_timestamp=true >> %s", propFilePinnedTimestamp)
209 return propFilePinnedTimestamp
210}
Spandan Das21643c62025-03-18 22:24:34 +0000211
212func (f *systemOtherImage) buildPropFileForMiscInfo(ctx android.ModuleContext) android.Path {
213 var lines []string
214 addStr := func(name string, value string) {
215 lines = append(lines, fmt.Sprintf("%s=%s", name, value))
216 }
217
218 addStr("building_system_other_image", "true")
219
220 systemImage := ctx.GetDirectDepProxyWithTag(*f.properties.System_image, systemImageDependencyTag)
221 systemInfo, ok := android.OtherModuleProvider(ctx, systemImage, FilesystemProvider)
222 if !ok {
223 ctx.PropertyErrorf("system_image", "Expected system_image module to provide FilesystemProvider")
224 return nil
225 }
226 if systemInfo.PartitionSize == nil {
227 addStr("system_other_disable_sparse", "true")
228 }
229 if systemInfo.UseAvb {
230 addStr("avb_system_other_hashtree_enable", "true")
231 addStr("avb_system_other_algorithm", systemInfo.AvbAlgorithm)
232 footerArgs := fmt.Sprintf("--hash_algorithm %s", systemInfo.AvbHashAlgorithm)
233 if rollbackIndex, err := f.avbRollbackIndex(ctx); err == nil {
234 footerArgs += fmt.Sprintf(" --rollback_index %d", rollbackIndex)
235 } else {
236 ctx.ModuleErrorf("Could not determine rollback_index %s\n", err)
237 }
238 addStr("avb_system_other_add_hashtree_footer_args", footerArgs)
239 if systemInfo.AvbKey != nil {
240 addStr("avb_system_other_key_path", systemInfo.AvbKey.String())
241 }
242 }
243
244 sort.Strings(lines)
245
246 propFile := android.PathForModuleOut(ctx, "prop_file")
247 android.WriteFileRule(ctx, propFile, strings.Join(lines, "\n"))
248 return propFile
249}
250
251// Use the default: PlatformSecurityPatch
252// TODO: Get this value from vbmeta_system
253func (f *systemOtherImage) avbRollbackIndex(ctx android.ModuleContext) (int64, error) {
254 t, err := time.Parse(time.DateOnly, ctx.Config().PlatformSecurityPatch())
255 if err != nil {
256 return -1, err
257 }
258 return t.Unix(), err
259}