• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2020 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
15// A special genrule that creates OTA payload and payload_properties from a raw
16// image. This rule is created so that the two outputs, payload and
17// payload_properties, can be distinguished with tags.
18
19package gki
20
21import (
22	"fmt"
23	"sort"
24	"strings"
25
26	"android/soong/android"
27	"android/soong/java"
28
29	"github.com/google/blueprint"
30	"github.com/google/blueprint/proptools"
31)
32
33type dependencyTag struct {
34	blueprint.BaseDependencyTag
35	name string
36}
37
38// {"foo": "fooVal", "bar": "barVal"} -> ["${foo}", "${bar}"]
39func keysToVars(deps map[string]string) []string {
40	var ret []string
41	for dep := range deps {
42		ret = append(ret, fmt.Sprintf("${%s}", dep))
43	}
44	sort.Strings(ret)
45	return ret
46}
47
48var (
49	certificateTag = dependencyTag{name: "certificate"}
50	rawImageTag    = dependencyTag{name: "raw_image"}
51
52	pctx = android.NewPackageContext("android/gki")
53
54	otaFromRawImageDeps = map[string]string{
55		"ota_from_raw_image": "ota_from_raw_image",
56
57		// Needed by ota_from_target_files
58		"brillo_update_payload": "brillo_update_payload",
59
60		// Needed by brillo_update_payload
61		"delta_generator": "delta_generator",
62		// b/171581299: shflags isn't built to the path where HostBinToolVariable
63		// points to without explicitly declaring it, even if it is stated as
64		// required by brillo_update_payload.
65		"shflags": "lib/shflags/shflags",
66
67		// Needed by GetBootImageTimestamp
68		"lz4":            "lz4",
69		"toybox":         "toybox",
70		"unpack_bootimg": "unpack_bootimg",
71	}
72
73	otaFromRawImageVarDeps = keysToVars(otaFromRawImageDeps)
74
75	otaFromRawImageRule = pctx.AndroidStaticRule("ota_from_raw_image", blueprint.RuleParams{
76		Command: `${ota_from_raw_image} --tools ` + strings.Join(otaFromRawImageVarDeps, " ") +
77			` ${kwargs} --out ${outDir} -- ${inputArg}`,
78		CommandDeps: otaFromRawImageVarDeps,
79		Description: "ota_from_raw_image ${outDir}",
80	}, "kwargs", "outDir", "inputArg")
81
82	// Tags to OutputFiles
83	payloadTag           = "payload"
84	payloadPropertiesTag = "properties"
85)
86
87func init() {
88	for dep := range otaFromRawImageDeps {
89		pctx.HostBinToolVariable(dep, otaFromRawImageDeps[dep])
90	}
91	// Intentionally not register this module so that it can only be constructed by gki_apex.
92}
93
94type rawImageOtaProperties struct {
95	// The name of a certificate in the default certificate directory, blank to use the default product certificate,
96	// or an android_app_certificate module name in the form ":module".
97	Certificate *string
98
99	// A set of images and their related modules. Must be in this form
100	// IMAGE_NAME:MODULE, where IMAGE_NAME is an image name like "boot", and
101	// MODULE is the name of a makefile_goal.
102	Image_goals []string
103}
104
105type rawImageOta struct {
106	android.ModuleBase
107	properties rawImageOtaProperties
108
109	pem android.Path
110	key android.Path
111
112	outPayload    android.WritablePath
113	outProperties android.WritablePath
114}
115
116// Declare a rule that generates a signed OTA payload from a raw image. This
117// includes payload.bin and payload_properties.txt.
118func rawImageOtaFactory() android.Module {
119	r := &rawImageOta{}
120	r.AddProperties(&r.properties)
121	android.InitAndroidModule(r)
122	return r
123}
124
125func (r *rawImageOta) OutputFiles(tag string) (android.Paths, error) {
126	switch tag {
127	case "":
128		return android.Paths{r.outPayload, r.outProperties}, nil
129	case payloadTag:
130		return android.Paths{r.outPayload}, nil
131	case payloadPropertiesTag:
132		return android.Paths{r.outProperties}, nil
133	default:
134		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
135	}
136}
137
138var _ android.OutputFileProducer = (*rawImageOta)(nil)
139
140func (r *rawImageOta) getCertString(ctx android.BaseModuleContext) string {
141	moduleName := ctx.ModuleName()
142	certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(moduleName)
143	if overridden {
144		return ":" + certificate
145	}
146	return proptools.String(r.properties.Certificate)
147}
148
149// Returns module->image_name mapping, e.g. "bootimage_soong"->"boot"
150func (r *rawImageOta) goalToImage(ctx android.EarlyModuleContext) map[string]string {
151	ret := map[string]string{}
152	for _, imageGoal := range r.properties.Image_goals {
153		lst := strings.Split(imageGoal, ":")
154		if len(lst) != 2 {
155			ctx.PropertyErrorf("image_goals", "Must be in the form IMAGE_NAME:MODULE")
156			return map[string]string{}
157		}
158		ret[lst[1]] = lst[0]
159	}
160	return ret
161}
162
163func (r *rawImageOta) DepsMutator(ctx android.BottomUpMutatorContext) {
164	// Add dependency to modules in image_goals
165	for module, _ := range r.goalToImage(ctx) {
166		ctx.AddVariationDependencies(nil, rawImageTag, module)
167	}
168	// Add dependency to certificate module, if any.
169	cert := android.SrcIsModule(r.getCertString(ctx))
170	if cert != "" {
171		ctx.AddVariationDependencies(nil, certificateTag, cert)
172	}
173}
174
175func (r *rawImageOta) GenerateAndroidBuildActions(ctx android.ModuleContext) {
176	inputArg := []string{}
177	kwargs := []string{}
178	implicits := android.Paths{}
179
180	// Handle image_goals
181	goalToImage := r.goalToImage(ctx)
182	ctx.VisitDirectDepsWithTag(rawImageTag, func(module android.Module) {
183		depName := ctx.OtherModuleName(module)
184		imgPath := android.OutputFileForModule(ctx, module, "")
185		if imgPath != nil {
186			implicits = append(implicits, imgPath)
187			inputArg = append(inputArg, goalToImage[depName]+":"+imgPath.String())
188		} else {
189			ctx.ModuleErrorf("image dependency %q does not generate any output", depName)
190		}
191	})
192
193	// Handle certificate
194	ctx.VisitDirectDepsWithTag(certificateTag, func(module android.Module) {
195		depName := ctx.OtherModuleName(module)
196		if cert, ok := module.(*java.AndroidAppCertificate); ok {
197			r.pem = cert.Certificate.Pem
198			r.key = cert.Certificate.Key
199		} else {
200			ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
201		}
202	})
203	r.setCertificateAndPrivateKey(ctx)
204	keyName, keyError := removeCertExt(r.pem)
205	if keyError != nil {
206		ctx.ModuleErrorf("Cannot get certificate to sign the OTA payload binary: " + keyError.Error())
207	}
208	implicits = append(implicits, r.pem, r.key)
209	kwargs = append(kwargs, "--key "+proptools.String(keyName))
210
211	// Set outputs
212	outDir := android.PathForModuleGen(ctx, "payload_files")
213	r.outPayload = outDir.Join(ctx, "payload.bin")
214	r.outProperties = outDir.Join(ctx, "payload_properties.txt")
215
216	ctx.Build(pctx, android.BuildParams{
217		Rule:        otaFromRawImageRule,
218		Description: "Generate OTA from raw image",
219		Implicits:   implicits,
220		Outputs:     android.WritablePaths{r.outPayload, r.outProperties},
221		Args: map[string]string{
222			"kwargs":   strings.Join(kwargs, " "),
223			"outDir":   outDir.String(),
224			"inputArg": strings.Join(inputArg, " "),
225		},
226	})
227}
228
229func (r *rawImageOta) setCertificateAndPrivateKey(ctx android.ModuleContext) {
230	if r.pem == nil {
231		cert := proptools.String(r.properties.Certificate)
232		if cert == "" {
233			pem, key := ctx.Config().DefaultAppCertificate(ctx)
234			r.pem = pem
235			r.key = key
236		} else {
237			defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
238			r.pem = defaultDir.Join(ctx, cert+".x509.pem")
239			r.key = defaultDir.Join(ctx, cert+".pk8")
240		}
241	}
242}
243
244func removeCertExt(path android.Path) (*string, error) {
245	s := path.String()
246	if strings.HasSuffix(s, ".x509.pem") {
247		return proptools.StringPtr(strings.TrimSuffix(s, ".x509.pem")), nil
248	}
249	return nil, fmt.Errorf("Path %q does not end with .x509.pem", s)
250}
251