You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

251 lines
7.7 KiB

// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// A special genrule that creates OTA payload and payload_properties from a raw
// image. This rule is created so that the two outputs, payload and
// payload_properties, can be distinguished with tags.
package gki
import (
"fmt"
"sort"
"strings"
"android/soong/android"
"android/soong/java"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
type dependencyTag struct {
blueprint.BaseDependencyTag
name string
}
// {"foo": "fooVal", "bar": "barVal"} -> ["${foo}", "${bar}"]
func keysToVars(deps map[string]string) []string {
var ret []string
for dep := range deps {
ret = append(ret, fmt.Sprintf("${%s}", dep))
}
sort.Strings(ret)
return ret
}
var (
certificateTag = dependencyTag{name: "certificate"}
rawImageTag = dependencyTag{name: "raw_image"}
pctx = android.NewPackageContext("android/gki")
otaFromRawImageDeps = map[string]string{
"ota_from_raw_image": "ota_from_raw_image",
// Needed by ota_from_target_files
"brillo_update_payload": "brillo_update_payload",
// Needed by brillo_update_payload
"delta_generator": "delta_generator",
// b/171581299: shflags isn't built to the path where HostBinToolVariable
// points to without explicitly declaring it, even if it is stated as
// required by brillo_update_payload.
"shflags": "lib/shflags/shflags",
// Needed by GetBootImageTimestamp
"lz4": "lz4",
"toybox": "toybox",
"unpack_bootimg": "unpack_bootimg",
}
otaFromRawImageVarDeps = keysToVars(otaFromRawImageDeps)
otaFromRawImageRule = pctx.AndroidStaticRule("ota_from_raw_image", blueprint.RuleParams{
Command: `${ota_from_raw_image} --tools ` + strings.Join(otaFromRawImageVarDeps, " ") +
` ${kwargs} --out ${outDir} -- ${inputArg}`,
CommandDeps: otaFromRawImageVarDeps,
Description: "ota_from_raw_image ${outDir}",
}, "kwargs", "outDir", "inputArg")
// Tags to OutputFiles
payloadTag = "payload"
payloadPropertiesTag = "properties"
)
func init() {
for dep := range otaFromRawImageDeps {
pctx.HostBinToolVariable(dep, otaFromRawImageDeps[dep])
}
// Intentionally not register this module so that it can only be constructed by gki_apex.
}
type rawImageOtaProperties struct {
// The name of a certificate in the default certificate directory, blank to use the default product certificate,
// or an android_app_certificate module name in the form ":module".
Certificate *string
// A set of images and their related modules. Must be in this form
// IMAGE_NAME:MODULE, where IMAGE_NAME is an image name like "boot", and
// MODULE is the name of a makefile_goal.
Image_goals []string
}
type rawImageOta struct {
android.ModuleBase
properties rawImageOtaProperties
pem android.Path
key android.Path
outPayload android.WritablePath
outProperties android.WritablePath
}
// Declare a rule that generates a signed OTA payload from a raw image. This
// includes payload.bin and payload_properties.txt.
func rawImageOtaFactory() android.Module {
r := &rawImageOta{}
r.AddProperties(&r.properties)
android.InitAndroidModule(r)
return r
}
func (r *rawImageOta) OutputFiles(tag string) (android.Paths, error) {
switch tag {
case "":
return android.Paths{r.outPayload, r.outProperties}, nil
case payloadTag:
return android.Paths{r.outPayload}, nil
case payloadPropertiesTag:
return android.Paths{r.outProperties}, nil
default:
return nil, fmt.Errorf("unsupported module reference tag %q", tag)
}
}
var _ android.OutputFileProducer = (*rawImageOta)(nil)
func (r *rawImageOta) getCertString(ctx android.BaseModuleContext) string {
moduleName := ctx.ModuleName()
certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(moduleName)
if overridden {
return ":" + certificate
}
return proptools.String(r.properties.Certificate)
}
// Returns module->image_name mapping, e.g. "bootimage_soong"->"boot"
func (r *rawImageOta) goalToImage(ctx android.EarlyModuleContext) map[string]string {
ret := map[string]string{}
for _, imageGoal := range r.properties.Image_goals {
lst := strings.Split(imageGoal, ":")
if len(lst) != 2 {
ctx.PropertyErrorf("image_goals", "Must be in the form IMAGE_NAME:MODULE")
return map[string]string{}
}
ret[lst[1]] = lst[0]
}
return ret
}
func (r *rawImageOta) DepsMutator(ctx android.BottomUpMutatorContext) {
// Add dependency to modules in image_goals
for module, _ := range r.goalToImage(ctx) {
ctx.AddVariationDependencies(nil, rawImageTag, module)
}
// Add dependency to certificate module, if any.
cert := android.SrcIsModule(r.getCertString(ctx))
if cert != "" {
ctx.AddVariationDependencies(nil, certificateTag, cert)
}
}
func (r *rawImageOta) GenerateAndroidBuildActions(ctx android.ModuleContext) {
inputArg := []string{}
kwargs := []string{}
implicits := android.Paths{}
// Handle image_goals
goalToImage := r.goalToImage(ctx)
ctx.VisitDirectDepsWithTag(rawImageTag, func(module android.Module) {
depName := ctx.OtherModuleName(module)
imgPath := android.OutputFileForModule(ctx, module, "")
if imgPath != nil {
implicits = append(implicits, imgPath)
inputArg = append(inputArg, goalToImage[depName]+":"+imgPath.String())
} else {
ctx.ModuleErrorf("image dependency %q does not generate any output", depName)
}
})
// Handle certificate
ctx.VisitDirectDepsWithTag(certificateTag, func(module android.Module) {
depName := ctx.OtherModuleName(module)
if cert, ok := module.(*java.AndroidAppCertificate); ok {
r.pem = cert.Certificate.Pem
r.key = cert.Certificate.Key
} else {
ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
}
})
r.setCertificateAndPrivateKey(ctx)
keyName, keyError := removeCertExt(r.pem)
if keyError != nil {
ctx.ModuleErrorf("Cannot get certificate to sign the OTA payload binary: " + keyError.Error())
}
implicits = append(implicits, r.pem, r.key)
kwargs = append(kwargs, "--key "+proptools.String(keyName))
// Set outputs
outDir := android.PathForModuleGen(ctx, "payload_files")
r.outPayload = outDir.Join(ctx, "payload.bin")
r.outProperties = outDir.Join(ctx, "payload_properties.txt")
ctx.Build(pctx, android.BuildParams{
Rule: otaFromRawImageRule,
Description: "Generate OTA from raw image",
Implicits: implicits,
Outputs: android.WritablePaths{r.outPayload, r.outProperties},
Args: map[string]string{
"kwargs": strings.Join(kwargs, " "),
"outDir": outDir.String(),
"inputArg": strings.Join(inputArg, " "),
},
})
}
func (r *rawImageOta) setCertificateAndPrivateKey(ctx android.ModuleContext) {
if r.pem == nil {
cert := proptools.String(r.properties.Certificate)
if cert == "" {
pem, key := ctx.Config().DefaultAppCertificate(ctx)
r.pem = pem
r.key = key
} else {
defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
r.pem = defaultDir.Join(ctx, cert+".x509.pem")
r.key = defaultDir.Join(ctx, cert+".pk8")
}
}
}
func removeCertExt(path android.Path) (*string, error) {
s := path.String()
if strings.HasSuffix(s, ".x509.pem") {
return proptools.StringPtr(strings.TrimSuffix(s, ".x509.pem")), nil
}
return nil, fmt.Errorf("Path %q does not end with .x509.pem", s)
}