// 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) }