diff --git a/android/Android.bp b/android/Android.bp index 5d0f2b941..36b2670d5 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -110,6 +110,7 @@ bootstrap_go_package { "paths_test.go", "prebuilt_test.go", "rule_builder_test.go", + "sdk_version_test.go", "singleton_module_test.go", "soong_config_modules_test.go", "util_test.go", diff --git a/android/apex.go b/android/apex.go index 4618fe97e..b5d96e4ae 100644 --- a/android/apex.go +++ b/android/apex.go @@ -903,16 +903,18 @@ var minSdkVersionAllowlist = func(apiMap map[string]int) map[string]ApiLevel { // // Return true if the `to` module should be visited, false otherwise. type PayloadDepsCallback func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool +type WalkPayloadDepsFunc func(ctx ModuleContext, do PayloadDepsCallback) -// UpdatableModule represents updatable APEX/APK -type UpdatableModule interface { +// ModuleWithMinSdkVersionCheck represents a module that implements min_sdk_version checks +type ModuleWithMinSdkVersionCheck interface { Module - WalkPayloadDeps(ctx ModuleContext, do PayloadDepsCallback) + MinSdkVersion(ctx EarlyModuleContext) SdkSpec + CheckMinSdkVersion(ctx ModuleContext) } // CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version // accordingly -func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiLevel) { +func CheckMinSdkVersion(ctx ModuleContext, minSdkVersion ApiLevel, walk WalkPayloadDepsFunc) { // do not enforce min_sdk_version for host if ctx.Host() { return @@ -928,7 +930,7 @@ func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiL return } - m.WalkPayloadDeps(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool { + walk(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 @@ -938,6 +940,14 @@ func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiL if am, ok := from.(DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) { return false } + if m, ok := to.(ModuleWithMinSdkVersionCheck); ok { + // This dependency performs its own min_sdk_version check, just make sure it sets min_sdk_version + // to trigger the check. + if !m.MinSdkVersion(ctx).Specified() { + ctx.OtherModuleErrorf(m, "must set min_sdk_version") + } + return false + } if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil { toName := ctx.OtherModuleName(to) if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver.GreaterThan(minSdkVersion) { diff --git a/android/api_levels.go b/android/api_levels.go index 93583bc07..fe3f41753 100644 --- a/android/api_levels.go +++ b/android/api_levels.go @@ -188,8 +188,8 @@ var FirstNonLibAndroidSupportVersion = uncheckedFinalApiLevel(21) // * "30" -> "30" // * "R" -> "30" // * "S" -> "S" -func ReplaceFinalizedCodenames(ctx PathContext, raw string) string { - num, ok := getFinalCodenamesMap(ctx.Config())[raw] +func ReplaceFinalizedCodenames(config Config, raw string) string { + num, ok := getFinalCodenamesMap(config)[raw] if !ok { return raw } @@ -197,7 +197,7 @@ func ReplaceFinalizedCodenames(ctx PathContext, raw string) string { return strconv.Itoa(num) } -// Converts the given string `raw` to an ApiLevel, possibly returning an error. +// ApiLevelFromUser converts the given string `raw` to an ApiLevel, possibly returning an error. // // `raw` must be non-empty. Passing an empty string results in a panic. // @@ -212,6 +212,12 @@ func ReplaceFinalizedCodenames(ctx PathContext, raw string) string { // Inputs that are not "current", known previews, or convertible to an integer // will return an error. func ApiLevelFromUser(ctx PathContext, raw string) (ApiLevel, error) { + return ApiLevelFromUserWithConfig(ctx.Config(), raw) +} + +// ApiLevelFromUserWithConfig implements ApiLevelFromUser, see comments for +// ApiLevelFromUser for more details. +func ApiLevelFromUserWithConfig(config Config, raw string) (ApiLevel, error) { if raw == "" { panic("API level string must be non-empty") } @@ -220,13 +226,13 @@ func ApiLevelFromUser(ctx PathContext, raw string) (ApiLevel, error) { return FutureApiLevel, nil } - for _, preview := range ctx.Config().PreviewApiLevels() { + for _, preview := range config.PreviewApiLevels() { if raw == preview.String() { return preview, nil } } - canonical := ReplaceFinalizedCodenames(ctx, raw) + canonical := ReplaceFinalizedCodenames(config, raw) asInt, err := strconv.Atoi(canonical) if err != nil { return NoneApiLevel, fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", canonical) diff --git a/android/config.go b/android/config.go index 61383e762..8e9a0a2e6 100644 --- a/android/config.go +++ b/android/config.go @@ -698,6 +698,16 @@ func (c *config) PreviewApiLevels() []ApiLevel { return levels } +func (c *config) LatestPreviewApiLevel() ApiLevel { + level := NoneApiLevel + for _, l := range c.PreviewApiLevels() { + if l.GreaterThan(level) { + level = l + } + } + return level +} + func (c *config) AllSupportedApiLevels() []ApiLevel { var levels []ApiLevel levels = append(levels, c.FinalApiLevels()...) diff --git a/android/sdk_version.go b/android/sdk_version.go index c6c75a399..49e607502 100644 --- a/android/sdk_version.go +++ b/android/sdk_version.go @@ -117,7 +117,7 @@ func (s SdkSpec) Stable() bool { return false } -// PrebuiltSdkAvailableForUnbundledBuilt tells whether this SdkSpec can have a prebuilt SDK +// PrebuiltSdkAvailableForUnbundledBuild tells whether this SdkSpec can have a prebuilt SDK // that can be used for unbundled builds. func (s SdkSpec) PrebuiltSdkAvailableForUnbundledBuild() bool { // "", "none", and "core_platform" are not available for unbundled build @@ -212,6 +212,10 @@ var ( ) func SdkSpecFrom(ctx EarlyModuleContext, str string) SdkSpec { + return SdkSpecFromWithConfig(ctx.Config(), str) +} + +func SdkSpecFromWithConfig(config Config, str string) SdkSpec { switch str { // special cases first case "": @@ -252,7 +256,7 @@ func SdkSpecFrom(ctx EarlyModuleContext, str string) SdkSpec { return SdkSpec{SdkInvalid, NoneApiLevel, str} } - apiLevel, err := ApiLevelFromUser(ctx, versionString) + apiLevel, err := ApiLevelFromUserWithConfig(config, versionString) if err != nil { return SdkSpec{SdkInvalid, apiLevel, str} } diff --git a/android/sdk_version_test.go b/android/sdk_version_test.go new file mode 100644 index 000000000..8924fd295 --- /dev/null +++ b/android/sdk_version_test.go @@ -0,0 +1,89 @@ +// Copyright 2015 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 ( + "testing" +) + +func TestSdkSpecFrom(t *testing.T) { + testCases := []struct { + input string + expected string + }{ + { + input: "", + expected: "private_current", + }, + { + input: "none", + expected: "none_(no version)", + }, + { + input: "core_platform", + expected: "core_platform_current", + }, + { + input: "_", + expected: "invalid_(no version)", + }, + { + input: "_31", + expected: "invalid_(no version)", + }, + { + input: "system_R", + expected: "system_30", + }, + { + input: "test_31", + expected: "test_31", + }, + { + input: "module_current", + expected: "module-lib_current", + }, + { + input: "31", + expected: "public_31", + }, + { + input: "S", + expected: "public_31", + }, + { + input: "current", + expected: "public_current", + }, + { + input: "Tiramisu", + expected: "public_Tiramisu", + }, + } + + config := NullConfig("") + + config.productVariables = productVariables{ + Platform_sdk_version: intPtr(31), + Platform_sdk_codename: stringPtr("Tiramisu"), + Platform_version_active_codenames: []string{"Tiramisu"}, + } + + for _, tc := range testCases { + if got := SdkSpecFromWithConfig(config, tc.input).String(); tc.expected != got { + t.Errorf("Expected %v, got %v", tc.expected, got) + } + } +} diff --git a/apex/apex.go b/apex/apex.go index 77b2d1300..4caf1c058 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -1632,7 +1632,7 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { // 1) do some validity checks such as apex_available, min_sdk_version, etc. a.checkApexAvailability(ctx) a.checkUpdatable(ctx) - a.checkMinSdkVersion(ctx) + a.CheckMinSdkVersion(ctx) a.checkStaticLinkingToStubLibraries(ctx) if len(a.properties.Tests) > 0 && !a.testApex { ctx.PropertyErrorf("tests", "property allowed only in apex_test module type") @@ -2246,18 +2246,28 @@ func overrideApexFactory() android.Module { // // TODO(jiyong): move these checks to a separate go file. +var _ android.ModuleWithMinSdkVersionCheck = (*apexBundle)(nil) + // Entures that min_sdk_version of the included modules are equal or less than the min_sdk_version // of this apexBundle. -func (a *apexBundle) checkMinSdkVersion(ctx android.ModuleContext) { +func (a *apexBundle) CheckMinSdkVersion(ctx android.ModuleContext) { if a.testApex || a.vndkApex { return } // apexBundle::minSdkVersion reports its own errors. minSdkVersion := a.minSdkVersion(ctx) - android.CheckMinSdkVersion(a, ctx, minSdkVersion) + android.CheckMinSdkVersion(ctx, minSdkVersion, a.WalkPayloadDeps) } -func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) android.ApiLevel { +func (a *apexBundle) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { + return android.SdkSpec{ + Kind: android.SdkNone, + ApiLevel: a.minSdkVersion(ctx), + Raw: String(a.properties.Min_sdk_version), + } +} + +func (a *apexBundle) minSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel { ver := proptools.String(a.properties.Min_sdk_version) if ver == "" { return android.NoneApiLevel diff --git a/apex/apex_test.go b/apex/apex_test.go index 12317a275..06d89fd6f 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -7742,6 +7742,184 @@ func TestApexJavaCoverage(t *testing.T) { } } +func TestSdkLibraryCanHaveHigherMinSdkVersion(t *testing.T) { + preparer := android.GroupFixturePreparers( + PrepareForTestWithApexBuildComponents, + prepareForTestWithMyapex, + java.PrepareForTestWithJavaSdkLibraryFiles, + java.PrepareForTestWithJavaDefaultModules, + android.PrepareForTestWithAndroidBuildComponents, + dexpreopt.FixtureSetApexBootJars("myapex:mybootclasspathlib"), + dexpreopt.FixtureSetApexSystemServerJars("myapex:mysystemserverclasspathlib"), + ) + + // Test java_sdk_library in bootclasspath_fragment may define higher min_sdk_version than the apex + t.Run("bootclasspath_fragment jar has higher min_sdk_version than apex", func(t *testing.T) { + preparer.RunTestWithBp(t, ` + apex { + name: "myapex", + key: "myapex.key", + bootclasspath_fragments: ["mybootclasspathfragment"], + min_sdk_version: "30", + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + bootclasspath_fragment { + name: "mybootclasspathfragment", + contents: ["mybootclasspathlib"], + apex_available: ["myapex"], + } + + java_sdk_library { + name: "mybootclasspathlib", + srcs: ["mybootclasspathlib.java"], + apex_available: ["myapex"], + compile_dex: true, + unsafe_ignore_missing_latest_api: true, + min_sdk_version: "31", + static_libs: ["util"], + } + + java_library { + name: "util", + srcs: ["a.java"], + apex_available: ["myapex"], + min_sdk_version: "31", + static_libs: ["another_util"], + } + + java_library { + name: "another_util", + srcs: ["a.java"], + min_sdk_version: "31", + apex_available: ["myapex"], + } + `) + }) + + // Test java_sdk_library in systemserverclasspath_fragment may define higher min_sdk_version than the apex + t.Run("systemserverclasspath_fragment jar has higher min_sdk_version than apex", func(t *testing.T) { + preparer.RunTestWithBp(t, ` + apex { + name: "myapex", + key: "myapex.key", + systemserverclasspath_fragments: ["mysystemserverclasspathfragment"], + min_sdk_version: "30", + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + systemserverclasspath_fragment { + name: "mysystemserverclasspathfragment", + contents: ["mysystemserverclasspathlib"], + apex_available: ["myapex"], + } + + java_sdk_library { + name: "mysystemserverclasspathlib", + srcs: ["mysystemserverclasspathlib.java"], + apex_available: ["myapex"], + compile_dex: true, + min_sdk_version: "32", + unsafe_ignore_missing_latest_api: true, + static_libs: ["util"], + } + + java_library { + name: "util", + srcs: ["a.java"], + apex_available: ["myapex"], + min_sdk_version: "31", + static_libs: ["another_util"], + } + + java_library { + name: "another_util", + srcs: ["a.java"], + min_sdk_version: "31", + apex_available: ["myapex"], + } + `) + }) + + t.Run("bootclasspath_fragment jar must set min_sdk_version", func(t *testing.T) { + preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "mybootclasspathlib".*must set min_sdk_version`)). + RunTestWithBp(t, ` + apex { + name: "myapex", + key: "myapex.key", + bootclasspath_fragments: ["mybootclasspathfragment"], + min_sdk_version: "30", + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + bootclasspath_fragment { + name: "mybootclasspathfragment", + contents: ["mybootclasspathlib"], + apex_available: ["myapex"], + } + + java_sdk_library { + name: "mybootclasspathlib", + srcs: ["mybootclasspathlib.java"], + apex_available: ["myapex"], + compile_dex: true, + unsafe_ignore_missing_latest_api: true, + } + `) + }) + + t.Run("systemserverclasspath_fragment jar must set min_sdk_version", func(t *testing.T) { + preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "mysystemserverclasspathlib".*must set min_sdk_version`)). + RunTestWithBp(t, ` + apex { + name: "myapex", + key: "myapex.key", + systemserverclasspath_fragments: ["mysystemserverclasspathfragment"], + min_sdk_version: "30", + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + systemserverclasspath_fragment { + name: "mysystemserverclasspathfragment", + contents: ["mysystemserverclasspathlib"], + apex_available: ["myapex"], + } + + java_sdk_library { + name: "mysystemserverclasspathlib", + srcs: ["mysystemserverclasspathlib.java"], + apex_available: ["myapex"], + compile_dex: true, + unsafe_ignore_missing_latest_api: true, + } + `) + }) +} + func TestMain(m *testing.M) { os.Exit(m.Run()) } diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go index 2f996555f..63d5317c5 100644 --- a/dexpreopt/testing.go +++ b/dexpreopt/testing.go @@ -122,6 +122,14 @@ func FixtureSetBootJars(bootJars ...string) android.FixturePreparer { func FixtureSetApexBootJars(bootJars ...string) android.FixturePreparer { return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) { dexpreoptConfig.ApexBootJars = android.CreateTestConfiguredJarList(bootJars) + + }) +} + +// FixtureSetApexSystemServerJars sets the ApexSystemServerJars property in the global config. +func FixtureSetApexSystemServerJars(jars ...string) android.FixturePreparer { + return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) { + dexpreoptConfig.ApexSystemServerJars = android.CreateTestConfiguredJarList(jars) }) } diff --git a/java/app.go b/java/app.go index fc1ace07b..06967690b 100755 --- a/java/app.go +++ b/java/app.go @@ -288,7 +288,7 @@ func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) { if minSdkVersion, err := a.MinSdkVersion(ctx).EffectiveVersion(ctx); err == nil { a.checkJniLibsSdkVersion(ctx, minSdkVersion) - android.CheckMinSdkVersion(a, ctx, minSdkVersion) + android.CheckMinSdkVersion(ctx, minSdkVersion, a.WalkPayloadDeps) } else { ctx.PropertyErrorf("min_sdk_version", "%s", err.Error()) } diff --git a/java/base.go b/java/base.go index 5d2927ac9..4780cc909 100644 --- a/java/base.go +++ b/java/base.go @@ -185,6 +185,10 @@ type DeviceProperties struct { // Defaults to sdk_version if not set. Min_sdk_version *string + // if not blank, set the maximum version of the sdk that the compiled artifacts will run against. + // Defaults to empty string "". See sdk_version for possible values. + Max_sdk_version *string + // if not blank, set the targetSdkVersion in the AndroidManifest.xml. // Defaults to sdk_version if not set. Target_sdk_version *string @@ -367,6 +371,7 @@ type Module struct { sdkVersion android.SdkSpec minSdkVersion android.SdkSpec + maxSdkVersion android.SdkSpec } func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error { @@ -524,6 +529,13 @@ func (j *Module) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { return j.SdkVersion(ctx) } +func (j *Module) MaxSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { + maxSdkVersion := proptools.StringDefault(j.deviceProperties.Max_sdk_version, "") + // SdkSpecFrom returns SdkSpecPrivate for this, which may be confusing. + // TODO(b/208456999): ideally MaxSdkVersion should be an ApiLevel and not SdkSpec. + return android.SdkSpecFrom(ctx, maxSdkVersion) +} + func (j *Module) MinSdkVersionString() string { return j.minSdkVersion.Raw } @@ -1488,8 +1500,7 @@ func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Modu } // Implements android.ApexModule -func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, - sdkVersion android.ApiLevel) error { +func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error { sdkSpec := j.MinSdkVersion(ctx) if !sdkSpec.Specified() { return fmt.Errorf("min_sdk_version is not specified") diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go index f63d81d6e..7eadfe6c3 100644 --- a/java/classpath_fragment.go +++ b/java/classpath_fragment.go @@ -84,11 +84,10 @@ func initClasspathFragment(c classpathFragment, classpathType classpathType) { // Matches definition of Jar in packages/modules/SdkExtensions/proto/classpaths.proto type classpathJar struct { - path string - classpath classpathType - // TODO(satayev): propagate min/max sdk versions for the jars - minSdkVersion int32 - maxSdkVersion int32 + path string + classpath classpathType + minSdkVersion string + maxSdkVersion string } // gatherPossibleApexModuleNamesAndStems returns a set of module and stem names from the @@ -120,10 +119,32 @@ func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars jars := make([]classpathJar, 0, len(paths)*len(classpaths)) for i := 0; i < len(paths); i++ { for _, classpathType := range classpaths { - jars = append(jars, classpathJar{ + jar := classpathJar{ classpath: classpathType, path: paths[i], + } + ctx.VisitDirectDepsIf(func(m android.Module) bool { + return m.Name() == configuredJars.Jar(i) + }, func(m android.Module) { + if s, ok := m.(*SdkLibrary); ok { + // TODO(208456999): instead of mapping "current" to latest, min_sdk_version should never be set to "current" + if s.minSdkVersion.Specified() { + if s.minSdkVersion.ApiLevel.IsCurrent() { + jar.minSdkVersion = ctx.Config().LatestPreviewApiLevel().String() + } else { + jar.minSdkVersion = s.minSdkVersion.ApiLevel.String() + } + } + if s.maxSdkVersion.Specified() { + if s.maxSdkVersion.ApiLevel.IsCurrent() { + jar.maxSdkVersion = ctx.Config().LatestPreviewApiLevel().String() + } else { + jar.maxSdkVersion = s.maxSdkVersion.ApiLevel.String() + } + } + } }) + jars = append(jars, jar) } } return jars @@ -136,15 +157,15 @@ func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.M c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths") - generatedJson := android.PathForModuleOut(ctx, outputFilename+".json") - writeClasspathsJson(ctx, generatedJson, jars) + generatedTextproto := android.PathForModuleOut(ctx, outputFilename+".textproto") + writeClasspathsTextproto(ctx, generatedTextproto, jars) rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). BuiltTool("conv_classpaths_proto"). Flag("encode"). - Flag("--format=json"). - FlagWithInput("--input=", generatedJson). + Flag("--format=textproto"). + FlagWithInput("--input=", generatedTextproto). FlagWithOutput("--output=", c.outputFilepath) rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String()) @@ -159,24 +180,18 @@ func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.M ctx.SetProvider(ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo) } -func writeClasspathsJson(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) { +func writeClasspathsTextproto(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) { var content strings.Builder - fmt.Fprintf(&content, "{\n") - fmt.Fprintf(&content, "\"jars\": [\n") - for idx, jar := range jars { - fmt.Fprintf(&content, "{\n") - fmt.Fprintf(&content, "\"path\": \"%s\",\n", jar.path) - fmt.Fprintf(&content, "\"classpath\": \"%s\"\n", jar.classpath) - - if idx < len(jars)-1 { - fmt.Fprintf(&content, "},\n") - } else { - fmt.Fprintf(&content, "}\n") - } + for _, jar := range jars { + fmt.Fprintf(&content, "jars {\n") + fmt.Fprintf(&content, "path: \"%s\"\n", jar.path) + fmt.Fprintf(&content, "classpath: %s\n", jar.classpath) + fmt.Fprintf(&content, "min_sdk_version: \"%s\"\n", jar.minSdkVersion) + fmt.Fprintf(&content, "max_sdk_version: \"%s\"\n", jar.maxSdkVersion) + fmt.Fprintf(&content, "}\n") } - fmt.Fprintf(&content, "]\n") - fmt.Fprintf(&content, "}\n") + android.WriteFileRule(ctx, output, content.String()) } diff --git a/java/java.go b/java/java.go index 02e06e0c0..a61ddf426 100644 --- a/java/java.go +++ b/java/java.go @@ -487,6 +487,7 @@ func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bo func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.sdkVersion = j.SdkVersion(ctx) j.minSdkVersion = j.MinSdkVersion(ctx) + j.maxSdkVersion = j.MaxSdkVersion(ctx) apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) if !apexInfo.IsForPlatform() { diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index cf266b80b..ca87f92fd 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -68,7 +68,6 @@ type platformBootclasspathProperties struct { func platformBootclasspathFactory() android.SingletonModule { m := &platformBootclasspathModule{} m.AddProperties(&m.properties) - // TODO(satayev): split apex jars into separate configs. initClasspathFragment(m, BOOTCLASSPATH) android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) return m diff --git a/java/sdk_library.go b/java/sdk_library.go index 751a71d0c..c6747c730 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -1122,6 +1122,22 @@ func (module *SdkLibrary) getGeneratedApiScopes(ctx android.EarlyModuleContext) return generatedScopes } +var _ android.ModuleWithMinSdkVersionCheck = (*SdkLibrary)(nil) + +func (module *SdkLibrary) CheckMinSdkVersion(ctx android.ModuleContext) { + android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx).ApiLevel, func(c android.ModuleContext, do android.PayloadDepsCallback) { + ctx.WalkDeps(func(child android.Module, parent android.Module) bool { + isExternal := !module.depIsInSameApex(ctx, child) + if am, ok := child.(android.ApexModule); ok { + if !do(ctx, parent, am, isExternal) { + return false + } + } + return !isExternal + }) + }) +} + type sdkLibraryComponentTag struct { blueprint.BaseDependencyTag name string @@ -1207,6 +1223,10 @@ func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) { } func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { + if proptools.String(module.deviceProperties.Min_sdk_version) != "" { + module.CheckMinSdkVersion(ctx) + } + module.generateCommonBuildActions(ctx) // Only build an implementation library if required. @@ -2466,12 +2486,12 @@ func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleConte func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries { if module.hideApexVariantFromMake { - return []android.AndroidMkEntries{android.AndroidMkEntries{ + return []android.AndroidMkEntries{{ Disabled: true, }} } - return []android.AndroidMkEntries{android.AndroidMkEntries{ + return []android.AndroidMkEntries{{ Class: "ETC", OutputFile: android.OptionalPathForPath(module.outputFilePath), ExtraEntries: []android.AndroidMkExtraEntriesFunc{ diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go index 3cf4987e1..0c7b79853 100644 --- a/java/sdk_library_test.go +++ b/java/sdk_library_test.go @@ -958,3 +958,87 @@ func TestJavaSdkLibraryDist(t *testing.T) { }) } } + +func TestSdkLibrary_CheckMinSdkVersion(t *testing.T) { + preparer := android.GroupFixturePreparers( + PrepareForTestWithJavaBuildComponents, + PrepareForTestWithJavaDefaultModules, + PrepareForTestWithJavaSdkLibraryFiles, + ) + + preparer.RunTestWithBp(t, ` + java_sdk_library { + name: "sdklib", + srcs: ["a.java"], + static_libs: ["util"], + min_sdk_version: "30", + unsafe_ignore_missing_latest_api: true, + } + + java_library { + name: "util", + srcs: ["a.java"], + min_sdk_version: "30", + } + `) + + preparer. + RunTestWithBp(t, ` + java_sdk_library { + name: "sdklib", + srcs: ["a.java"], + libs: ["util"], + impl_only_libs: ["util"], + stub_only_libs: ["util"], + stub_only_static_libs: ["util"], + min_sdk_version: "30", + unsafe_ignore_missing_latest_api: true, + } + + java_library { + name: "util", + srcs: ["a.java"], + } + `) + + preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "util".*should support min_sdk_version\(30\)`)). + RunTestWithBp(t, ` + java_sdk_library { + name: "sdklib", + srcs: ["a.java"], + static_libs: ["util"], + min_sdk_version: "30", + unsafe_ignore_missing_latest_api: true, + } + + java_library { + name: "util", + srcs: ["a.java"], + min_sdk_version: "31", + } + `) + + preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "another_util".*should support min_sdk_version\(30\)`)). + RunTestWithBp(t, ` + java_sdk_library { + name: "sdklib", + srcs: ["a.java"], + static_libs: ["util"], + min_sdk_version: "30", + unsafe_ignore_missing_latest_api: true, + } + + java_library { + name: "util", + srcs: ["a.java"], + static_libs: ["another_util"], + min_sdk_version: "30", + } + + java_library { + name: "another_util", + srcs: ["a.java"], + min_sdk_version: "31", + } + `) +}