312 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			312 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
| // 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.
 | |
| 
 | |
| package gki
 | |
| 
 | |
| import (
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 
 | |
| 	"android/soong/android"
 | |
| 	"android/soong/apex"
 | |
| 	"android/soong/etc"
 | |
| 	"android/soong/genrule"
 | |
| 
 | |
| 	"github.com/google/blueprint/proptools"
 | |
| )
 | |
| 
 | |
| type gkiApexProperties struct {
 | |
| 	// Path relative to $(PRODUCT_OUT) that points to the boot image. This is
 | |
| 	// passed to the generated makefile_goal.
 | |
| 	// Exactly one of [factory, product_out_path] must be set.
 | |
| 	Product_out_path *string
 | |
| 
 | |
| 	// Declared KMI version of the boot image. Example: "5.4-android12-0"
 | |
| 	Kmi_version *string
 | |
| 
 | |
| 	// The certificate to sign the OTA payload.
 | |
| 	// 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".
 | |
| 	Ota_payload_certificate *string
 | |
| 
 | |
| 	// Whether test APEXes are generated. Test APEXes are named with
 | |
| 	// ${name}_test_high and ${name}_test_low, respectively.
 | |
| 	Gen_test *bool
 | |
| 
 | |
| 	// Whether this APEX is installable to one of the partitions. Default:
 | |
| 	// see apex.installable.
 | |
| 	Installable *bool
 | |
| 
 | |
| 	// Whether modules should be enabled according to board variables.
 | |
| 	ModulesEnabled bool `blueprint:"mutated"`
 | |
| 	// APEX package name that will be declared in the APEX manifest.
 | |
| 	// e.g. com.android.gki.kmi_5_4_android12_0
 | |
| 	ApexName *string `blueprint:"mutated"`
 | |
| }
 | |
| 
 | |
| type gkiApex struct {
 | |
| 	android.ModuleBase
 | |
| 	properties gkiApexProperties
 | |
| }
 | |
| 
 | |
| func init() {
 | |
| 	android.RegisterModuleType("gki_apex", gkiApexFactory)
 | |
| }
 | |
| 
 | |
| // Declare a GKI APEX. Generate a set of modules to define an apex with name
 | |
| // "com.android.gki" + sanitized(kmi_version).
 | |
| func gkiApexFactory() android.Module {
 | |
| 	g := &gkiApex{}
 | |
| 	g.AddProperties(&g.properties)
 | |
| 	android.InitAndroidModule(g)
 | |
| 	android.AddLoadHook(g, func(ctx android.LoadHookContext) { gkiApexMutator(ctx, g) })
 | |
| 	return g
 | |
| }
 | |
| 
 | |
| func gkiApexMutator(mctx android.LoadHookContext, g *gkiApex) {
 | |
| 	g.validateAndSetMutableProperties(mctx)
 | |
| 	g.createModulesRealApexes(mctx)
 | |
| }
 | |
| 
 | |
| func (g *gkiApex) validateAndSetMutableProperties(mctx android.LoadHookContext) {
 | |
| 	// Parse kmi_version property to find APEX name.
 | |
| 	apexName, err := kmiVersionToApexName(proptools.String(g.properties.Kmi_version))
 | |
| 	if err != nil {
 | |
| 		mctx.PropertyErrorf("kmi_version", err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Set mutable properties.
 | |
| 	g.properties.ModulesEnabled = g.bootImgHasRules(mctx) && g.boardDefinesKmiVersion(mctx)
 | |
| 	g.properties.ApexName = proptools.StringPtr(apexName)
 | |
| }
 | |
| 
 | |
| func testApexBundleFactory() android.Module {
 | |
| 	return apex.ApexBundleFactory(true /* testApex */)
 | |
| }
 | |
| 
 | |
| // Create modules for a real APEX package that contains an OTA payload.
 | |
| func (g *gkiApex) createModulesRealApexes(mctx android.LoadHookContext) {
 | |
| 	// Import $(PRODUCT_OUT)/boot.img to Soong
 | |
| 	bootImage := g.moduleName() + "_bootimage"
 | |
| 	mctx.CreateModule(android.MakefileGoalFactory, &moduleCommonProperties{
 | |
| 		Name:    proptools.StringPtr(bootImage),
 | |
| 		Enabled: proptools.BoolPtr(g.properties.ModulesEnabled),
 | |
| 	}, &makefileGoalProperties{
 | |
| 		Product_out_path: g.properties.Product_out_path,
 | |
| 	})
 | |
| 	// boot.img -> kernel_release.txt
 | |
| 	mctx.CreateModule(genrule.GenRuleFactory, &moduleCommonProperties{
 | |
| 		Name:    proptools.StringPtr(g.kernelReleaseFileName()),
 | |
| 		Enabled: proptools.BoolPtr(g.properties.ModulesEnabled),
 | |
| 	}, &genRuleProperties{
 | |
| 		Defaults: []string{"extract_kernel_release_defaults"},
 | |
| 		Srcs:     []string{":" + bootImage},
 | |
| 	})
 | |
| 	// boot.img -> payload.bin and payload_properties.txt
 | |
| 	otaPayloadGen := g.moduleName() + "_ota_payload_gen"
 | |
| 	mctx.CreateModule(rawImageOtaFactory, &moduleCommonProperties{
 | |
| 		Name:    proptools.StringPtr(otaPayloadGen),
 | |
| 		Enabled: proptools.BoolPtr(g.properties.ModulesEnabled),
 | |
| 	}, &rawImageOtaProperties{
 | |
| 		Certificate: g.properties.Ota_payload_certificate,
 | |
| 		Image_goals: []string{"boot:" + bootImage},
 | |
| 	})
 | |
| 	// copy payload.bin to <apex>/etc/ota
 | |
| 	mctx.CreateModule(etc.PrebuiltEtcFactory, &moduleCommonProperties{
 | |
| 		Name:    proptools.StringPtr(g.otaPayloadName()),
 | |
| 		Enabled: proptools.BoolPtr(g.properties.ModulesEnabled),
 | |
| 	}, &prebuiltEtcProperties{
 | |
| 		Src:                   proptools.StringPtr(":" + otaPayloadGen + "{" + payloadTag + "}"),
 | |
| 		Filename_from_src:     proptools.BoolPtr(true),
 | |
| 		Relative_install_path: proptools.StringPtr("ota"),
 | |
| 		Installable:           proptools.BoolPtr(false),
 | |
| 	})
 | |
| 	// copy payload_properties.txt to <apex>/etc/ota
 | |
| 	mctx.CreateModule(etc.PrebuiltEtcFactory, &moduleCommonProperties{
 | |
| 		Name:    proptools.StringPtr(g.otaPropertiesName()),
 | |
| 		Enabled: proptools.BoolPtr(g.properties.ModulesEnabled),
 | |
| 	}, &prebuiltEtcProperties{
 | |
| 		Src:                   proptools.StringPtr(":" + otaPayloadGen + "{" + payloadPropertiesTag + "}"),
 | |
| 		Filename_from_src:     proptools.BoolPtr(true),
 | |
| 		Relative_install_path: proptools.StringPtr("ota"),
 | |
| 		Installable:           proptools.BoolPtr(false),
 | |
| 	})
 | |
| 	// Create the APEX module with name g.moduleName(). Use factory APEX version.
 | |
| 	g.createModulesRealApex(mctx, g.moduleName(), false, "")
 | |
| 
 | |
| 	// Create test APEX modules if gen_test. Test packages are not installable.
 | |
| 	// Use hard-coded APEX version.
 | |
| 	if proptools.Bool(g.properties.Gen_test) {
 | |
| 		g.createModulesRealApex(mctx, g.moduleName()+"_test_high", true, "1000000000")
 | |
| 		g.createModulesRealApex(mctx, g.moduleName()+"_test_low", true, "1")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (g *gkiApex) createModulesRealApex(mctx android.LoadHookContext,
 | |
| 	moduleName string,
 | |
| 	isTestApex bool,
 | |
| 	overrideApexVersion string) {
 | |
| 	// Check kmi_version property against kernel_release.txt, then
 | |
| 	// kernel_release.txt -> apex_manifest.json.
 | |
| 	apexManifest := moduleName + "_apex_manifest"
 | |
| 	mctx.CreateModule(genrule.GenRuleFactory, &moduleCommonProperties{
 | |
| 		Name:    proptools.StringPtr(apexManifest),
 | |
| 		Enabled: proptools.BoolPtr(g.properties.ModulesEnabled),
 | |
| 	}, &genRuleProperties{
 | |
| 		Tools: []string{"build_gki_apex_manifest"},
 | |
| 		Out:   []string{"apex_manifest.json"},
 | |
| 		Srcs:  []string{":" + g.kernelReleaseFileName()},
 | |
| 		Cmd:   proptools.StringPtr(g.createApexManifestCmd(overrideApexVersion)),
 | |
| 	})
 | |
| 
 | |
| 	// The APEX module.
 | |
| 
 | |
| 	// For test APEXes, if module is not enabled because KMI version is not
 | |
| 	// compatible with the device, create a stub module that produces an empty
 | |
| 	// file. This is so that the module name can be used in tests.
 | |
| 	if isTestApex && !g.properties.ModulesEnabled {
 | |
| 		mctx.CreateModule(genrule.GenRuleFactory, &moduleCommonProperties{
 | |
| 			Name: proptools.StringPtr(moduleName),
 | |
| 		}, &genRuleProperties{
 | |
| 			Out: []string{moduleName + ".apex"},
 | |
| 			Cmd: proptools.StringPtr(`touch $(out)`),
 | |
| 		})
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// For test APEXes, if module is enabled, build an apex_test with installable: false.
 | |
| 	// For installed APEXes, build apex, respecting installable and enabled.
 | |
| 	apexFactory := apex.BundleFactory
 | |
| 	overrideInstallable := g.properties.Installable
 | |
| 	if isTestApex {
 | |
| 		apexFactory = testApexBundleFactory
 | |
| 		overrideInstallable = proptools.BoolPtr(false)
 | |
| 	}
 | |
| 
 | |
| 	mctx.CreateModule(apexFactory, &moduleCommonProperties{
 | |
| 		Name:    proptools.StringPtr(moduleName),
 | |
| 		Enabled: proptools.BoolPtr(g.properties.ModulesEnabled),
 | |
| 	}, &apexProperties{
 | |
| 		Apex_name: g.properties.ApexName,
 | |
| 		Manifest:  proptools.StringPtr(":" + apexManifest),
 | |
| 		Defaults:  []string{"com.android.gki_defaults"},
 | |
| 		// A real GKI APEX cannot be preinstalled to the device.
 | |
| 		// It can only be provided as an update.
 | |
| 		Installable: overrideInstallable,
 | |
| 		Prebuilts: []string{
 | |
| 			g.otaPayloadName(),
 | |
| 			g.otaPropertiesName(),
 | |
| 		},
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // Original module name as specified by the "name" property.
 | |
| // This is also the APEX module name, i.e. the file name of the APEX file.
 | |
| // This is also the prefix of names of all generated modules that the phony module depends on.
 | |
| // e.g. com.android.gki.kmi_5_4_android12_0_boot
 | |
| func (g *gkiApex) moduleName() string {
 | |
| 	return g.BaseModuleName()
 | |
| }
 | |
| 
 | |
| // The appeared name of this gkiApex object. Exposed to Soong to avoid conflicting with
 | |
| // the generated APEX module with name moduleName().
 | |
| // e.g. com.android.gki.kmi_5_4_android12_0_boot_all
 | |
| func (g *gkiApex) Name() string {
 | |
| 	return g.moduleName() + "_all"
 | |
| }
 | |
| 
 | |
| // Names for intermediate modules.
 | |
| func (g *gkiApex) kernelReleaseFileName() string {
 | |
| 	return g.moduleName() + "_bootimage_kernel_release_file"
 | |
| }
 | |
| 
 | |
| func (g *gkiApex) otaPayloadName() string {
 | |
| 	return g.moduleName() + "_ota_payload"
 | |
| }
 | |
| 
 | |
| func (g *gkiApex) otaPropertiesName() string {
 | |
| 	return g.moduleName() + "_ota_payload_properties"
 | |
| }
 | |
| 
 | |
| // If the boot image pointed at product_out_path has no rule to be generated, do not generate any
 | |
| // build rules for this gki_apex module. For example, if this gki_apex module is:
 | |
| //     { name: "foo", product_out_path: "boot-bar.img" }
 | |
| // But there is no rule to generate boot-bar.img, then
 | |
| // - `m foo` fails with `unknown target 'foo'`
 | |
| // - checkbuild is still successful. The module foo doesn't even exist, so there
 | |
| //   is no dependency on boot-bar.img
 | |
| //
 | |
| // There is a rule to generate "boot-foo.img" if "kernel-foo" is in BOARD_KERNEL_BINARIES.
 | |
| // As a special case, there is a rule to generate "boot.img" if BOARD_KERNEL_BINARIES is empty,
 | |
| // or "kernel" is in BOARD_KERNEL_BINARIES.
 | |
| func (g *gkiApex) bootImgHasRules(mctx android.EarlyModuleContext) bool {
 | |
| 	kernelNames := mctx.DeviceConfig().BoardKernelBinaries()
 | |
| 	if len(kernelNames) == 0 {
 | |
| 		return proptools.String(g.properties.Product_out_path) == "boot.img"
 | |
| 	}
 | |
| 	for _, kernelName := range kernelNames {
 | |
| 		validBootImagePath := strings.Replace(kernelName, "kernel", "boot", -1) + ".img"
 | |
| 		if proptools.String(g.properties.Product_out_path) == validBootImagePath {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // Only generate if this module's kmi_version property is in BOARD_KERNEL_MODULE_INTERFACE_VERSIONS.
 | |
| // Otherwise, this board does not support GKI APEXes, so no modules are generated at all.
 | |
| // This function also avoids building invalid modules in checkbuild. For example, if these
 | |
| // gki_apex modules are defined:
 | |
| //   gki_apex { name: "boot-kmi-1", kmi_version: "1", product_out_path: "boot.img" }
 | |
| //   gki_apex { name: "boot-kmi-2", kmi_version: "2", product_out_path: "boot.img" }
 | |
| // But a given device's $PRODUCT_OUT/boot.img can only support at most one KMI version.
 | |
| // Disable some modules accordingly to make sure checkbuild still works.
 | |
| func boardDefinesKmiVersion(mctx android.EarlyModuleContext, kmiVersion string) bool {
 | |
| 	kmiVersions := mctx.DeviceConfig().BoardKernelModuleInterfaceVersions()
 | |
| 	return android.InList(kmiVersion, kmiVersions)
 | |
| }
 | |
| 
 | |
| func (g *gkiApex) boardDefinesKmiVersion(mctx android.EarlyModuleContext) bool {
 | |
| 	return boardDefinesKmiVersion(mctx, proptools.String(g.properties.Kmi_version))
 | |
| }
 | |
| 
 | |
| // Transform kernel release file in $(in) to KMI version + sublevel.
 | |
| // e.g. 5.4.42-android12-0 => name: "com.android.gki.kmi_5_4_android12_0", version: "300000000"
 | |
| // Finally, write APEX manifest JSON to $(out).
 | |
| func (g *gkiApex) createApexManifestCmd(apexVersion string) string {
 | |
| 	ret := `$(location build_gki_apex_manifest) ` +
 | |
| 		`--kmi_version "` + proptools.String(g.properties.Kmi_version) + `" ` +
 | |
| 		`--apex_manifest $(out) --kernel_release_file $(in)`
 | |
| 	// Override version field if set.
 | |
| 	if apexVersion != "" {
 | |
| 		ret += ` --apex_version ` + apexVersion
 | |
| 	}
 | |
| 	return ret
 | |
| }
 | |
| 
 | |
| func (g *gkiApex) DepsMutator(ctx android.BottomUpMutatorContext) {
 | |
| }
 | |
| 
 | |
| func (g *gkiApex) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 | |
| }
 | |
| 
 | |
| // OTA payload binary is signed with default_system_dev_certificate, which is equivalent to
 | |
| // DefaultAppCertificate().
 | |
| func getDefaultCertificate(ctx android.EarlyModuleContext) string {
 | |
| 	pem, _ := ctx.Config().DefaultAppCertificate(ctx)
 | |
| 	return strings.TrimSuffix(pem.String(), filepath.Ext(pem.String()))
 | |
| }
 |