diff options
Diffstat (limited to 'java')
-rw-r--r-- | java/bootclasspath_fragment.go | 4 | ||||
-rw-r--r-- | java/sdk_library.go | 285 | ||||
-rw-r--r-- | java/sdk_library_test.go | 184 |
3 files changed, 445 insertions, 28 deletions
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index bfa683824..ad30780ec 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -621,6 +621,10 @@ func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext) } else if global.ApexBootJars.Len() != 0 && !android.IsModuleInVersionedSdk(ctx.Module()) { unknown = android.RemoveListFromList(unknown, b.properties.Coverage.Contents) _, unknown = android.RemoveFromList("core-icu4j", unknown) + // This module only exists in car products. + // So ignore it even if it is not in PRODUCT_APEX_BOOT_JARS. + // TODO(b/202896428): Add better way to handle this. + _, unknown = android.RemoveFromList("android.car-module", unknown) if len(unknown) > 0 { ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown) } diff --git a/java/sdk_library.go b/java/sdk_library.go index 273efec13..b0880839c 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -21,6 +21,7 @@ import ( "reflect" "regexp" "sort" + "strconv" "strings" "sync" @@ -32,25 +33,7 @@ import ( ) const ( - sdkXmlFileSuffix = ".xml" - permissionsTemplate = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n` + - `<!-- Copyright (C) 2018 The Android Open Source Project\n` + - `\n` + - ` Licensed under the Apache License, Version 2.0 (the \"License\");\n` + - ` you may not use this file except in compliance with the License.\n` + - ` You may obtain a copy of the License at\n` + - `\n` + - ` http://www.apache.org/licenses/LICENSE-2.0\n` + - `\n` + - ` Unless required by applicable law or agreed to in writing, software\n` + - ` distributed under the License is distributed on an \"AS IS\" BASIS,\n` + - ` WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n` + - ` See the License for the specific language governing permissions and\n` + - ` limitations under the License.\n` + - `-->\n` + - `<permissions>\n` + - ` <library name=\"%s\" file=\"%s\"/>\n` + - `</permissions>\n` + sdkXmlFileSuffix = ".xml" ) // A tag to associated a dependency with a specific api scope. @@ -636,6 +619,33 @@ type commonToSdkLibraryAndImportProperties struct { // Files containing information about supported java doc tags. Doctag_files []string `android:"path"` + + // Signals that this shared library is part of the bootclasspath starting + // on the version indicated in this attribute. + // + // This will make platforms at this level and above to ignore + // <uses-library> tags with this library name because the library is already + // available + On_bootclasspath_since *string + + // Signals that this shared library was part of the bootclasspath before + // (but not including) the version indicated in this attribute. + // + // The system will automatically add a <uses-library> tag with this library to + // apps that target any SDK less than the version indicated in this attribute. + On_bootclasspath_before *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is below the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Min_device_sdk *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is above the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Max_device_sdk *string } // commonSdkLibraryAndImportModule defines the interface that must be provided by a module that @@ -1579,14 +1589,29 @@ func (module *SdkLibrary) UniqueApexVariations() bool { // Creates the xml file that publicizes the runtime library func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) { + moduleMinApiLevel := module.Library.MinSdkVersion(mctx).ApiLevel + var moduleMinApiLevelStr = moduleMinApiLevel.String() + if moduleMinApiLevel == android.NoneApiLevel { + moduleMinApiLevelStr = "current" + } props := struct { - Name *string - Lib_name *string - Apex_available []string + Name *string + Lib_name *string + Apex_available []string + On_bootclasspath_since *string + On_bootclasspath_before *string + Min_device_sdk *string + Max_device_sdk *string + Sdk_library_min_api_level *string }{ - Name: proptools.StringPtr(module.xmlPermissionsModuleName()), - Lib_name: proptools.StringPtr(module.BaseModuleName()), - Apex_available: module.ApexProperties.Apex_available, + Name: proptools.StringPtr(module.xmlPermissionsModuleName()), + Lib_name: proptools.StringPtr(module.BaseModuleName()), + Apex_available: module.ApexProperties.Apex_available, + On_bootclasspath_since: module.commonSdkLibraryProperties.On_bootclasspath_since, + On_bootclasspath_before: module.commonSdkLibraryProperties.On_bootclasspath_before, + Min_device_sdk: module.commonSdkLibraryProperties.Min_device_sdk, + Max_device_sdk: module.commonSdkLibraryProperties.Max_device_sdk, + Sdk_library_min_api_level: &moduleMinApiLevelStr, } mctx.CreateModule(sdkLibraryXmlFactory, &props) @@ -2382,6 +2407,38 @@ type sdkLibraryXml struct { type sdkLibraryXmlProperties struct { // canonical name of the lib Lib_name *string + + // Signals that this shared library is part of the bootclasspath starting + // on the version indicated in this attribute. + // + // This will make platforms at this level and above to ignore + // <uses-library> tags with this library name because the library is already + // available + On_bootclasspath_since *string + + // Signals that this shared library was part of the bootclasspath before + // (but not including) the version indicated in this attribute. + // + // The system will automatically add a <uses-library> tag with this library to + // apps that target any SDK less than the version indicated in this attribute. + On_bootclasspath_before *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is below the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Min_device_sdk *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is above the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Max_device_sdk *string + + // The SdkLibrary's min api level as a string + // + // This value comes from the ApiLevel of the MinSdkVersion property. + Sdk_library_min_api_level *string } // java_sdk_library_xml builds the permission xml file for a java_sdk_library. @@ -2458,11 +2515,81 @@ func (module *sdkLibraryXml) implPath(ctx android.ModuleContext) string { return "/" + partition + "/framework/" + implName + ".jar" } +func formattedOptionalSdkLevelAttribute(ctx android.ModuleContext, attrName string, value *string) string { + if value == nil { + return "" + } + apiLevel, err := android.ApiLevelFromUser(ctx, *value) + if err != nil { + // attributes in bp files have underscores but in the xml have dashes. + ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error()) + return "" + } + intStr := strconv.Itoa(apiLevel.FinalOrPreviewInt()) + return formattedOptionalAttribute(attrName, &intStr) +} + +// formats an attribute for the xml permissions file if the value is not null +// returns empty string otherwise +func formattedOptionalAttribute(attrName string, value *string) string { + if value == nil { + return "" + } + return fmt.Sprintf(` %s=\"%s\"\n`, attrName, *value) +} + +func (module *sdkLibraryXml) permissionsContents(ctx android.ModuleContext) string { + libName := proptools.String(module.properties.Lib_name) + libNameAttr := formattedOptionalAttribute("name", &libName) + filePath := module.implPath(ctx) + filePathAttr := formattedOptionalAttribute("file", &filePath) + implicitFromAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-since", module.properties.On_bootclasspath_since) + implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before) + minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk) + maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk) + // <library> is understood in all android versions whereas <updatable-library> is only understood from API T (and ignored before that). + // similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the updatable-library to make sure this library is not loaded before T + var libraryTag string + if module.properties.Min_device_sdk != nil { + libraryTag = ` <updatable-library\n` + } else { + libraryTag = ` <library\n` + } + + return strings.Join([]string{ + `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n`, + `<!-- Copyright (C) 2018 The Android Open Source Project\n`, + `\n`, + ` Licensed under the Apache License, Version 2.0 (the \"License\");\n`, + ` you may not use this file except in compliance with the License.\n`, + ` You may obtain a copy of the License at\n`, + `\n`, + ` http://www.apache.org/licenses/LICENSE-2.0\n`, + `\n`, + ` Unless required by applicable law or agreed to in writing, software\n`, + ` distributed under the License is distributed on an \"AS IS\" BASIS,\n`, + ` WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n`, + ` See the License for the specific language governing permissions and\n`, + ` limitations under the License.\n`, + `-->\n`, + `<permissions>\n`, + libraryTag, + libNameAttr, + filePathAttr, + implicitFromAttr, + implicitUntilAttr, + minSdkAttr, + maxSdkAttr, + ` />\n`, + `</permissions>\n`}, "") +} + func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) { module.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() libName := proptools.String(module.properties.Lib_name) - xmlContent := fmt.Sprintf(permissionsTemplate, libName, module.implPath(ctx)) + module.selfValidate(ctx) + xmlContent := module.permissionsContents(ctx) module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath rule := android.NewRuleBuilder(pctx, ctx) @@ -2495,6 +2622,81 @@ func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries { }} } +func (module *sdkLibraryXml) selfValidate(ctx android.ModuleContext) { + module.validateAtLeastTAttributes(ctx) + module.validateMinAndMaxDeviceSdk(ctx) + module.validateMinMaxDeviceSdkAndModuleMinSdk(ctx) + module.validateOnBootclasspathBeforeRequirements(ctx) +} + +func (module *sdkLibraryXml) validateAtLeastTAttributes(ctx android.ModuleContext) { + t := android.ApiLevelOrPanic(ctx, "Tiramisu") + module.attrAtLeastT(ctx, t, module.properties.Min_device_sdk, "min_device_sdk") + module.attrAtLeastT(ctx, t, module.properties.Max_device_sdk, "max_device_sdk") + module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_before, "on_bootclasspath_before") + module.attrAtLeastT(ctx, t, module.properties.On_bootclasspath_since, "on_bootclasspath_since") +} + +func (module *sdkLibraryXml) attrAtLeastT(ctx android.ModuleContext, t android.ApiLevel, attr *string, attrName string) { + if attr != nil { + if level, err := android.ApiLevelFromUser(ctx, *attr); err == nil { + // we will inform the user of invalid inputs when we try to write the + // permissions xml file so we don't need to do it here + if t.GreaterThan(level) { + ctx.PropertyErrorf(attrName, "Attribute value needs to be at least T") + } + } + } +} + +func (module *sdkLibraryXml) validateMinAndMaxDeviceSdk(ctx android.ModuleContext) { + if module.properties.Min_device_sdk != nil && module.properties.Max_device_sdk != nil { + min, minErr := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk) + max, maxErr := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk) + if minErr == nil && maxErr == nil { + // we will inform the user of invalid inputs when we try to write the + // permissions xml file so we don't need to do it here + if min.GreaterThan(max) { + ctx.ModuleErrorf("min_device_sdk can't be greater than max_device_sdk") + } + } + } +} + +func (module *sdkLibraryXml) validateMinMaxDeviceSdkAndModuleMinSdk(ctx android.ModuleContext) { + moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level) + if module.properties.Min_device_sdk != nil { + api, err := android.ApiLevelFromUser(ctx, *module.properties.Min_device_sdk) + if err == nil { + if moduleMinApi.GreaterThan(api) { + ctx.PropertyErrorf("min_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi) + } + } + } + if module.properties.Max_device_sdk != nil { + api, err := android.ApiLevelFromUser(ctx, *module.properties.Max_device_sdk) + if err == nil { + if moduleMinApi.GreaterThan(api) { + ctx.PropertyErrorf("max_device_sdk", "Can't be less than module's min sdk (%s)", moduleMinApi) + } + } + } +} + +func (module *sdkLibraryXml) validateOnBootclasspathBeforeRequirements(ctx android.ModuleContext) { + moduleMinApi := android.ApiLevelOrPanic(ctx, *module.properties.Sdk_library_min_api_level) + if module.properties.On_bootclasspath_before != nil { + t := android.ApiLevelOrPanic(ctx, "Tiramisu") + // if we use the attribute, then we need to do this validation + if moduleMinApi.LessThan(t) { + // if minAPi is < T, then we need to have min_device_sdk (which only accepts T+) + if module.properties.Min_device_sdk == nil { + ctx.PropertyErrorf("on_bootclasspath_before", "Using this property requires that the module's min_sdk_version or the shared library's min_device_sdk is at least T") + } + } + } +} + type sdkLibrarySdkMemberType struct { android.SdkMemberTypeBase } @@ -2546,6 +2748,33 @@ type sdkLibrarySdkMemberProperties struct { Doctag_paths android.Paths Permitted_packages []string + + // Signals that this shared library is part of the bootclasspath starting + // on the version indicated in this attribute. + // + // This will make platforms at this level and above to ignore + // <uses-library> tags with this library name because the library is already + // available + On_bootclasspath_since *string + + // Signals that this shared library was part of the bootclasspath before + // (but not including) the version indicated in this attribute. + // + // The system will automatically add a <uses-library> tag with this library to + // apps that target any SDK less than the version indicated in this attribute. + On_bootclasspath_before *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is below the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Min_device_sdk *string + + // Indicates that PackageManager should ignore this shared library if the + // platform is above the version indicated in this attribute. + // + // This means that the device won't recognise this library as installed. + Max_device_sdk *string } type scopeProperties struct { @@ -2592,6 +2821,10 @@ func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMembe s.Compile_dex = sdk.dexProperties.Compile_dex s.Doctag_paths = sdk.doctagPaths s.Permitted_packages = sdk.PermittedPackagesForUpdatableBootJars() + s.On_bootclasspath_since = sdk.commonSdkLibraryProperties.On_bootclasspath_since + s.On_bootclasspath_before = sdk.commonSdkLibraryProperties.On_bootclasspath_before + s.Min_device_sdk = sdk.commonSdkLibraryProperties.Min_device_sdk + s.Max_device_sdk = sdk.commonSdkLibraryProperties.Max_device_sdk } func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) { diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go index be23536ea..2271573b6 100644 --- a/java/sdk_library_test.go +++ b/java/sdk_library_test.go @@ -15,12 +15,13 @@ package java import ( - "android/soong/android" "fmt" "path/filepath" "regexp" "testing" + "android/soong/android" + "github.com/google/blueprint/proptools" ) @@ -107,7 +108,7 @@ func TestJavaSdkLibrary(t *testing.T) { libs: ["foo"], sdk_version: "module_30", } - `) + `) // check the existence of the internal modules foo := result.ModuleForTests("foo", "android_common") @@ -162,6 +163,185 @@ func TestJavaSdkLibrary(t *testing.T) { } } +func TestJavaSdkLibrary_UpdatableLibrary(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "28": {"foo"}, + "29": {"foo"}, + "30": {"foo", "fooUpdatable", "fooUpdatableErr"}, + }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V", "W"} + }), + ).RunTestWithBp(t, + ` + java_sdk_library { + name: "fooUpdatable", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + on_bootclasspath_since: "U", + on_bootclasspath_before: "V", + min_device_sdk: "W", + max_device_sdk: "current", + min_sdk_version: "S", + } + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + } +`) + // test that updatability attributes are passed on correctly + fooUpdatable := result.ModuleForTests("fooUpdatable.xml", "android_common").Rule("java_sdk_xml") + android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-since=\"9001\"`) + android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-before=\"9002\"`) + android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `min-device-sdk=\"9003\"`) + android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `max-device-sdk=\"10000\"`) + + // double check that updatability attributes are not written if they don't exist in the bp file + // the permissions file for the foo library defined above + fooPermissions := result.ModuleForTests("foo.xml", "android_common").Rule("java_sdk_xml") + android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `on-bootclasspath-since`) + android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `on-bootclasspath-before`) + android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `min-device-sdk`) + android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooPermissions.RuleParams.Command, `max-device-sdk`) +} + +func TestJavaSdkLibrary_UpdatableLibrary_Validation_ValidVersion(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "30": {"fooUpdatable", "fooUpdatableErr"}, + }), + ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern( + []string{ + `on_bootclasspath_since: "aaa" could not be parsed as an integer and is not a recognized codename`, + `on_bootclasspath_before: "bbc" could not be parsed as an integer and is not a recognized codename`, + `min_device_sdk: "ccc" could not be parsed as an integer and is not a recognized codename`, + `max_device_sdk: "ddd" could not be parsed as an integer and is not a recognized codename`, + })).RunTestWithBp(t, + ` + java_sdk_library { + name: "fooUpdatableErr", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + on_bootclasspath_since: "aaa", + on_bootclasspath_before: "bbc", + min_device_sdk: "ccc", + max_device_sdk: "ddd", + } +`) +} + +func TestJavaSdkLibrary_UpdatableLibrary_Validation_AtLeastTAttributes(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "28": {"foo"}, + }), + ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern( + []string{ + "on_bootclasspath_since: Attribute value needs to be at least T", + "on_bootclasspath_before: Attribute value needs to be at least T", + "min_device_sdk: Attribute value needs to be at least T", + "max_device_sdk: Attribute value needs to be at least T", + }, + )).RunTestWithBp(t, + ` + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + on_bootclasspath_since: "S", + on_bootclasspath_before: "S", + min_device_sdk: "S", + max_device_sdk: "S", + min_sdk_version: "S", + } +`) +} + +func TestJavaSdkLibrary_UpdatableLibrary_Validation_MinAndMaxDeviceSdk(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "28": {"foo"}, + }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V"} + }), + ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern( + []string{ + "min_device_sdk can't be greater than max_device_sdk", + }, + )).RunTestWithBp(t, + ` + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + min_device_sdk: "V", + max_device_sdk: "U", + min_sdk_version: "S", + } +`) +} + +func TestJavaSdkLibrary_UpdatableLibrary_Validation_MinAndMaxDeviceSdkAndModuleMinSdk(t *testing.T) { + android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "28": {"foo"}, + }), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V"} + }), + ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern( + []string{ + regexp.QuoteMeta("min_device_sdk: Can't be less than module's min sdk (V)"), + regexp.QuoteMeta("max_device_sdk: Can't be less than module's min sdk (V)"), + }, + )).RunTestWithBp(t, + ` + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + api_packages: ["foo"], + min_device_sdk: "U", + max_device_sdk: "U", + min_sdk_version: "V", + } +`) +} + +func TestJavaSdkLibrary_UpdatableLibrary_usesNewTag(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithPrebuiltApis(map[string][]string{ + "30": {"foo"}, + }), + ).RunTestWithBp(t, + ` + java_sdk_library { + name: "foo", + srcs: ["a.java", "b.java"], + min_device_sdk: "Tiramisu", + min_sdk_version: "S", + } +`) + // test that updatability attributes are passed on correctly + fooUpdatable := result.ModuleForTests("foo.xml", "android_common").Rule("java_sdk_xml") + android.AssertStringDoesContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<updatable-library`) + android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<library`) +} + func TestJavaSdkLibrary_StubOrImplOnlyLibs(t *testing.T) { result := android.GroupFixturePreparers( prepareForJavaTest, |