// Copyright 2018 Google Inc. All rights reserved. // // 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 android import ( "fmt" "sort" "strconv" "strings" "sync" "github.com/google/blueprint" ) const ( SdkVersion_Android10 = 29 ) type ApexInfo struct { // Name of the apex variant that this module is mutated into ApexName string MinSdkVersion int Updatable bool } // Extracted from ApexModule to make it easier to define custom subsets of the // ApexModule interface and improve code navigation within the IDE. type DepIsInSameApex interface { // DepIsInSameApex tests if the other module 'dep' is installed to the same // APEX as this module DepIsInSameApex(ctx BaseModuleContext, dep Module) bool } // ApexModule is the interface that a module type is expected to implement if // the module has to be built differently depending on whether the module // is destined for an apex or not (installed to one of the regular partitions). // // Native shared libraries are one such module type; when it is built for an // APEX, it should depend only on stable interfaces such as NDK, stable AIDL, // or C APIs from other APEXs. // // A module implementing this interface will be mutated into multiple // variations by apex.apexMutator if it is directly or indirectly included // in one or more APEXs. Specifically, if a module is included in apex.foo and // apex.bar then three apex variants are created: platform, apex.foo and // apex.bar. The platform variant is for the regular partitions // (e.g., /system or /vendor, etc.) while the other two are for the APEXs, // respectively. type ApexModule interface { Module DepIsInSameApex apexModuleBase() *ApexModuleBase // Marks that this module should be built for the specified APEXes. // Call this before apex.apexMutator is run. BuildForApexes(apexes []ApexInfo) // Returns the APEXes that this module will be built for ApexVariations() []ApexInfo // Returns the name of APEX that this module will be built for. Empty string // is returned when 'IsForPlatform() == true'. Note that a module can be // included in multiple APEXes, in which case, the module is mutated into // multiple modules each of which for an APEX. This method returns the // name of the APEX that a variant module is for. // Call this after apex.apexMutator is run. ApexName() string // Tests whether this module will be built for the platform or not. // This is a shortcut for ApexName() == "" IsForPlatform() bool // Tests if this module could have APEX variants. APEX variants are // created only for the modules that returns true here. This is useful // for not creating APEX variants for certain types of shared libraries // such as NDK stubs. CanHaveApexVariants() bool // Tests if this module can be installed to APEX as a file. For example, // this would return true for shared libs while return false for static // libs. IsInstallableToApex() bool // Mutate this module into one or more variants each of which is built // for an APEX marked via BuildForApexes(). CreateApexVariations(mctx BottomUpMutatorContext) []Module // Tests if this module is available for the specified APEX or ":platform" AvailableFor(what string) bool // Return true if this module is not available to platform (i.e. apex_available // property doesn't have "//apex_available:platform"), or shouldn't be available // to platform, which is the case when this module depends on other module that // isn't available to platform. NotAvailableForPlatform() bool // Mark that this module is not available to platform. Set by the // check-platform-availability mutator in the apex package. SetNotAvailableForPlatform() // Returns the highest version which is <= maxSdkVersion. // For example, with maxSdkVersion is 10 and versionList is [9,11] // it returns 9 as string ChooseSdkVersion(versionList []string, maxSdkVersion int) (string, error) // Tests if the module comes from an updatable APEX. Updatable() bool // List of APEXes that this module tests. The module has access to // the private part of the listed APEXes even when it is not included in the // APEXes. TestFor() []string // Returns nil if this module supports sdkVersion // Otherwise, returns error with reason ShouldSupportSdkVersion(ctx BaseModuleContext, sdkVersion int) error } type ApexProperties struct { // Availability of this module in APEXes. Only the listed APEXes can contain // this module. If the module has stubs then other APEXes and the platform may // access it through them (subject to visibility). // // "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX. // "//apex_available:platform" refers to non-APEX partitions like "system.img". // Default is ["//apex_available:platform"]. Apex_available []string Info ApexInfo `blueprint:"mutated"` NotAvailableForPlatform bool `blueprint:"mutated"` } // Marker interface that identifies dependencies that are excluded from APEX // contents. type ExcludeFromApexContentsTag interface { blueprint.DependencyTag // Method that differentiates this interface from others. ExcludeFromApexContents() } // Provides default implementation for the ApexModule interface. APEX-aware // modules are expected to include this struct and call InitApexModule(). type ApexModuleBase struct { ApexProperties ApexProperties canHaveApexVariants bool apexVariationsLock sync.Mutex // protects apexVariations during parallel apexDepsMutator apexVariations []ApexInfo } func (m *ApexModuleBase) apexModuleBase() *ApexModuleBase { return m } func (m *ApexModuleBase) ApexAvailable() []string { return m.ApexProperties.Apex_available } func (m *ApexModuleBase) TestFor() []string { // To be implemented by concrete types inheriting ApexModuleBase return nil } func (m *ApexModuleBase) BuildForApexes(apexes []ApexInfo) { m.apexVariationsLock.Lock() defer m.apexVariationsLock.Unlock() nextApex: for _, apex := range apexes { for _, v := range m.apexVariations { if v.ApexName == apex.ApexName { continue nextApex } } m.apexVariations = append(m.apexVariations, apex) } } func (m *ApexModuleBase) ApexVariations() []ApexInfo { return m.apexVariations } func (m *ApexModuleBase) ApexName() string { return m.ApexProperties.Info.ApexName } func (m *ApexModuleBase) IsForPlatform() bool { return m.ApexProperties.Info.ApexName == "" } func (m *ApexModuleBase) CanHaveApexVariants() bool { return m.canHaveApexVariants } func (m *ApexModuleBase) IsInstallableToApex() bool { // should be overriden if needed return false } const ( AvailableToPlatform = "//apex_available:platform" AvailableToAnyApex = "//apex_available:anyapex" ) func CheckAvailableForApex(what string, apex_available []string) bool { if len(apex_available) == 0 { // apex_available defaults to ["//apex_available:platform"], // which means 'available to the platform but no apexes'. return what == AvailableToPlatform } return InList(what, apex_available) || (what != AvailableToPlatform && InList(AvailableToAnyApex, apex_available)) } func (m *ApexModuleBase) AvailableFor(what string) bool { return CheckAvailableForApex(what, m.ApexProperties.Apex_available) } func (m *ApexModuleBase) NotAvailableForPlatform() bool { return m.ApexProperties.NotAvailableForPlatform } func (m *ApexModuleBase) SetNotAvailableForPlatform() { m.ApexProperties.NotAvailableForPlatform = true } func (m *ApexModuleBase) DepIsInSameApex(ctx BaseModuleContext, dep Module) bool { // By default, if there is a dependency from A to B, we try to include both in the same APEX, // unless B is explicitly from outside of the APEX (i.e. a stubs lib). Thus, returning true. // This is overridden by some module types like apex.ApexBundle, cc.Module, java.Module, etc. return true } func (m *ApexModuleBase) ChooseSdkVersion(versionList []string, maxSdkVersion int) (string, error) { for i := range versionList { ver, _ := strconv.Atoi(versionList[len(versionList)-i-1]) if ver <= maxSdkVersion { return versionList[len(versionList)-i-1], nil } } return "", fmt.Errorf("not found a version(<=%d) in versionList: %v", maxSdkVersion, versionList) } func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) { for _, n := range m.ApexProperties.Apex_available { if n == AvailableToPlatform || n == AvailableToAnyApex { continue } if !mctx.OtherModuleExists(n) && !mctx.Config().AllowMissingDependencies() { mctx.PropertyErrorf("apex_available", "%q is not a valid module name", n) } } } func (m *ApexModuleBase) Updatable() bool { return m.ApexProperties.Info.Updatable } type byApexName []ApexInfo func (a byApexName) Len() int { return len(a) } func (a byApexName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a byApexName) Less(i, j int) bool { return a[i].ApexName < a[j].ApexName } func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []Module { if len(m.apexVariations) > 0 { m.checkApexAvailableProperty(mctx) sort.Sort(byApexName(m.apexVariations)) variations := []string{} variations = append(variations, "") // Original variation for platform for _, apex := range m.apexVariations { variations = append(variations, apex.ApexName) } defaultVariation := "" mctx.SetDefaultDependencyVariation(&defaultVariation) modules := mctx.CreateVariations(variations...) for i, mod := range modules { platformVariation := i == 0 if platformVariation && !mctx.Host() && !mod.(ApexModule).AvailableFor(AvailableToPlatform) { mod.SkipInstall() } if !platformVariation { mod.(ApexModule).apexModuleBase().ApexProperties.Info = m.apexVariations[i-1] } } return modules } return nil } var apexData OncePer var apexNamesMapMutex sync.Mutex var apexNamesKey = NewOnceKey("apexNames") // This structure maintains the global mapping in between modules and APEXes. // Examples: // // apexNamesMap()["foo"]["bar"] == true: module foo is directly depended on by APEX bar // apexNamesMap()["foo"]["bar"] == false: module foo is indirectly depended on by APEX bar // apexNamesMap()["foo"]["bar"] doesn't exist: foo is not built for APEX bar func apexNamesMap() map[string]map[string]bool { return apexData.Once(apexNamesKey, func() interface{} { return make(map[string]map[string]bool) }).(map[string]map[string]bool) } // Update the map to mark that a module named moduleName is directly or indirectly // depended on by the specified APEXes. Directly depending means that a module // is explicitly listed in the build definition of the APEX via properties like // native_shared_libs, java_libs, etc. func UpdateApexDependency(apexes []ApexInfo, moduleName string, directDep bool) { apexNamesMapMutex.Lock() defer apexNamesMapMutex.Unlock() for _, apex := range apexes { apexesForModule, ok := apexNamesMap()[moduleName] if !ok { apexesForModule = make(map[string]bool) apexNamesMap()[moduleName] = apexesForModule } apexesForModule[apex.ApexName] = apexesForModule[apex.ApexName] || directDep } } // TODO(b/146393795): remove this when b/146393795 is fixed func ClearApexDependency() { m := apexNamesMap() for k := range m { delete(m, k) } } // Tests whether a module named moduleName is directly depended on by an APEX // named apexName. func DirectlyInApex(apexName string, moduleName string) bool { apexNamesMapMutex.Lock() defer apexNamesMapMutex.Unlock() if apexNames, ok := apexNamesMap()[moduleName]; ok { return apexNames[apexName] } return false } type hostContext interface { Host() bool } // Tests whether a module named moduleName is directly depended on by any APEX. func DirectlyInAnyApex(ctx hostContext, moduleName string) bool { if ctx.Host() { // Host has no APEX. return false } apexNamesMapMutex.Lock() defer apexNamesMapMutex.Unlock() if apexNames, ok := apexNamesMap()[moduleName]; ok { for an := range apexNames { if apexNames[an] { return true } } } return false } // Tests whether a module named module is depended on (including both // direct and indirect dependencies) by any APEX. func InAnyApex(moduleName string) bool { apexNamesMapMutex.Lock() defer apexNamesMapMutex.Unlock() apexNames, ok := apexNamesMap()[moduleName] return ok && len(apexNames) > 0 } func GetApexesForModule(moduleName string) []string { ret := []string{} apexNamesMapMutex.Lock() defer apexNamesMapMutex.Unlock() if apexNames, ok := apexNamesMap()[moduleName]; ok { for an := range apexNames { ret = append(ret, an) } } return ret } func InitApexModule(m ApexModule) { base := m.apexModuleBase() base.canHaveApexVariants = true m.AddProperties(&base.ApexProperties) } // A dependency info for a single ApexModule, either direct or transitive. type ApexModuleDepInfo struct { // Name of the dependency To string // List of dependencies To belongs to. Includes APEX itself, if a direct dependency. From []string // Whether the dependency belongs to the final compiled APEX. IsExternal bool // min_sdk_version of the ApexModule MinSdkVersion string } // A map of a dependency name to its ApexModuleDepInfo type DepNameToDepInfoMap map[string]ApexModuleDepInfo type ApexBundleDepsInfo struct { flatListPath OutputPath fullListPath OutputPath } type ApexBundleDepsInfoIntf interface { Updatable() bool FlatListPath() Path FullListPath() Path } func (d *ApexBundleDepsInfo) FlatListPath() Path { return d.flatListPath } func (d *ApexBundleDepsInfo) FullListPath() Path { return d.fullListPath } // Generate two module out files: // 1. FullList with transitive deps and their parents in the dep graph // 2. FlatList with a flat list of transitive deps func (d *ApexBundleDepsInfo) BuildDepsInfoLists(ctx ModuleContext, minSdkVersion string, depInfos DepNameToDepInfoMap) { var fullContent strings.Builder var flatContent strings.Builder fmt.Fprintf(&flatContent, "%s(minSdkVersion:%s):\\n", ctx.ModuleName(), minSdkVersion) for _, key := range FirstUniqueStrings(SortedStringKeys(depInfos)) { info := depInfos[key] toName := fmt.Sprintf("%s(minSdkVersion:%s)", info.To, info.MinSdkVersion) if info.IsExternal { toName = toName + " (external)" } fmt.Fprintf(&fullContent, "%s <- %s\\n", toName, strings.Join(SortedUniqueStrings(info.From), ", ")) fmt.Fprintf(&flatContent, " %s\\n", toName) } d.fullListPath = PathForModuleOut(ctx, "depsinfo", "fulllist.txt").OutputPath ctx.Build(pctx, BuildParams{ Rule: WriteFile, Description: "Full Dependency Info", Output: d.fullListPath, Args: map[string]string{ "content": fullContent.String(), }, }) d.flatListPath = PathForModuleOut(ctx, "depsinfo", "flatlist.txt").OutputPath ctx.Build(pctx, BuildParams{ Rule: WriteFile, Description: "Flat Dependency Info", Output: d.flatListPath, Args: map[string]string{ "content": flatContent.String(), }, }) } // TODO(b/158059172): remove minSdkVersion allowlist var minSdkVersionAllowlist = map[string]int{ "adbd": 30, "android.net.ipsec.ike": 30, "androidx-constraintlayout_constraintlayout-solver": 30, "androidx.annotation_annotation": 28, "androidx.arch.core_core-common": 28, "androidx.collection_collection": 28, "androidx.lifecycle_lifecycle-common": 28, "apache-commons-compress": 29, "bouncycastle_ike_digests": 30, "brotli-java": 29, "captiveportal-lib": 28, "flatbuffer_headers": 30, "framework-permission": 30, "framework-statsd": 30, "gemmlowp_headers": 30, "ike-internals": 30, "kotlinx-coroutines-android": 28, "kotlinx-coroutines-core": 28, "libadb_crypto": 30, "libadb_pairing_auth": 30, "libadb_pairing_connection": 30, "libadb_pairing_server": 30, "libadb_protos": 30, "libadb_tls_connection": 30, "libadbconnection_client": 30, "libadbconnection_server": 30, "libadbd_core": 30, "libadbd_services": 30, "libadbd": 30, "libapp_processes_protos_lite": 30, "libasyncio": 30, "libbrotli": 30, "libbuildversion": 30, "libcrypto_static": 30, "libcrypto_utils": 30, "libdiagnose_usb": 30, "libeigen": 30, "liblz4": 30, "libmdnssd": 30, "libneuralnetworks_common": 30, "libneuralnetworks_headers": 30, "libneuralnetworks": 30, "libprocpartition": 30, "libprotobuf-java-lite": 30, "libprotoutil": 30, "libqemu_pipe": 30, "libstats_jni": 30, "libstatslog_statsd": 30, "libstatsmetadata": 30, "libstatspull": 30, "libstatssocket": 30, "libsync": 30, "libtextclassifier_hash_headers": 30, "libtextclassifier_hash_static": 30, "libtflite_kernel_utils": 30, "libwatchdog": 29, "libzstd": 30, "metrics-constants-protos": 28, "net-utils-framework-common": 29, "permissioncontroller-statsd": 28, "philox_random_headers": 30, "philox_random": 30, "service-permission": 30, "service-statsd": 30, "statsd-aidl-ndk_platform": 30, "statsd": 30, "tensorflow_headers": 30, "xz-java": 29, } // Function called while walking an APEX's payload dependencies. // // Return true if the `to` module should be visited, false otherwise. type PayloadDepsCallback func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool // UpdatableModule represents updatable APEX/APK type UpdatableModule interface { Module WalkPayloadDeps(ctx ModuleContext, do PayloadDepsCallback) } // CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version accordingly func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion int) { // do not enforce min_sdk_version for host if ctx.Host() { return } // do not enforce for coverage build if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT") || ctx.DeviceConfig().NativeCoverageEnabled() || ctx.DeviceConfig().ClangCoverageEnabled() { return } // do not enforce deps.min_sdk_version if APEX/APK doesn't set min_sdk_version or // min_sdk_version is not finalized (e.g. current or codenames) if minSdkVersion == FutureApiLevel { return } m.WalkPayloadDeps(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool { if externalDep { // external deps are outside the payload boundary, which is "stable" interface. // We don't have to check min_sdk_version for external dependencies. return false } if am, ok := from.(DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) { return false } if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil { toName := ctx.OtherModuleName(to) if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver > minSdkVersion { ctx.OtherModuleErrorf(to, "should support min_sdk_version(%v) for %q: %v. Dependency path: %s", minSdkVersion, ctx.ModuleName(), err.Error(), ctx.GetPathString(false)) return false } } return true }) }