summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
Diffstat (limited to 'java')
-rw-r--r--java/Android.bp2
-rw-r--r--java/androidmk.go4
-rwxr-xr-xjava/app.go1
-rw-r--r--java/app_import.go1
-rw-r--r--java/boot_image.go136
-rw-r--r--java/boot_image_test.go31
-rw-r--r--java/boot_jars.go34
-rw-r--r--java/dex.go6
-rw-r--r--java/dexpreopt.go44
-rw-r--r--java/dexpreopt_bootjars.go44
-rw-r--r--java/dexpreopt_test.go2
-rw-r--r--java/hiddenapi.go55
-rw-r--r--java/hiddenapi_singleton.go61
-rw-r--r--java/hiddenapi_singleton_test.go10
-rw-r--r--java/java.go50
-rw-r--r--java/prebuilt_apis.go101
-rw-r--r--java/proto.go2
-rw-r--r--java/rro.go6
-rw-r--r--java/testing.go1
19 files changed, 490 insertions, 101 deletions
diff --git a/java/Android.bp b/java/Android.bp
index 9c28968f6..364566a8b 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -24,6 +24,7 @@ bootstrap_go_package {
"app.go",
"app_import.go",
"app_set.go",
+ "boot_image.go",
"boot_jars.go",
"builder.go",
"device_host_converter.go",
@@ -63,6 +64,7 @@ bootstrap_go_package {
"app_import_test.go",
"app_set_test.go",
"app_test.go",
+ "boot_image_test.go",
"device_host_converter_test.go",
"dexpreopt_test.go",
"dexpreopt_bootjars_test.go",
diff --git a/java/androidmk.go b/java/androidmk.go
index cc454b03d..21f3012a4 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -125,6 +125,10 @@ func (library *Library) AndroidMkEntries() []android.AndroidMkEntries {
entries.SetString("LOCAL_MODULE_STEM", library.Stem())
entries.SetOptionalPaths("LOCAL_SOONG_LINT_REPORTS", library.linter.reports)
+
+ if library.dexpreopter.configPath != nil {
+ entries.SetPath("LOCAL_SOONG_DEXPREOPT_CONFIG", library.dexpreopter.configPath)
+ }
},
},
})
diff --git a/java/app.go b/java/app.go
index e6c9a2d98..ce89e9bb6 100755
--- a/java/app.go
+++ b/java/app.go
@@ -455,6 +455,7 @@ func (a *AndroidApp) installPath(ctx android.ModuleContext) android.InstallPath
func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
a.dexpreopter.installPath = a.installPath(ctx)
+ a.dexpreopter.isApp = true
if a.dexProperties.Uncompress_dex == nil {
// If the value was not force-set by the user, use reasonable default based on the module.
a.dexProperties.Uncompress_dex = proptools.BoolPtr(a.shouldUncompressDex(ctx))
diff --git a/java/app_import.go b/java/app_import.go
index df940f1ff..6f21bfbbf 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -255,6 +255,7 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext
installDir = android.PathForModuleInstall(ctx, "app", a.BaseModuleName())
}
+ a.dexpreopter.isApp = true
a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk")
a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned)
a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
diff --git a/java/boot_image.go b/java/boot_image.go
new file mode 100644
index 000000000..0a525b752
--- /dev/null
+++ b/java/boot_image.go
@@ -0,0 +1,136 @@
+// Copyright (C) 2021 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 java
+
+import (
+ "fmt"
+ "strings"
+
+ "android/soong/android"
+ "android/soong/dexpreopt"
+ "github.com/google/blueprint"
+)
+
+func init() {
+ RegisterBootImageBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterBootImageBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("boot_image", bootImageFactory)
+}
+
+type bootImageProperties struct {
+ // The name of the image this represents.
+ //
+ // Must be one of "art" or "boot".
+ Image_name string
+}
+
+type BootImageModule struct {
+ android.ModuleBase
+ android.ApexModuleBase
+
+ properties bootImageProperties
+}
+
+func bootImageFactory() android.Module {
+ m := &BootImageModule{}
+ m.AddProperties(&m.properties)
+ android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
+ android.InitApexModule(m)
+ return m
+}
+
+var BootImageInfoProvider = blueprint.NewProvider(BootImageInfo{})
+
+type BootImageInfo struct {
+ // The image config, internal to this module (and the dex_bootjars singleton).
+ //
+ // Will be nil if the BootImageInfo has not been provided for a specific module. That can occur
+ // when SkipDexpreoptBootJars(ctx) returns true.
+ imageConfig *bootImageConfig
+}
+
+func (i BootImageInfo) Modules() android.ConfiguredJarList {
+ return i.imageConfig.modules
+}
+
+// Get a map from ArchType to the associated boot image's contents for Android.
+//
+// Extension boot images only return their own files, not the files of the boot images they extend.
+func (i BootImageInfo) AndroidBootImageFilesByArchType() map[android.ArchType]android.OutputPaths {
+ files := map[android.ArchType]android.OutputPaths{}
+ if i.imageConfig != nil {
+ for _, variant := range i.imageConfig.variants {
+ // We also generate boot images for host (for testing), but we don't need those in the apex.
+ // TODO(b/177892522) - consider changing this to check Os.OsClass = android.Device
+ if variant.target.Os == android.Android {
+ files[variant.target.Arch.ArchType] = variant.imagesDeps
+ }
+ }
+ }
+ return files
+}
+
+func (b *BootImageModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+ tag := ctx.OtherModuleDependencyTag(dep)
+ if tag == dexpreopt.Dex2oatDepTag {
+ // The dex2oat tool is only needed for building and is not required in the apex.
+ return false
+ }
+ panic(fmt.Errorf("boot_image module %q should not have a dependency on %q via tag %s", b, dep, android.PrettyPrintTag(tag)))
+}
+
+func (b *BootImageModule) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
+ return nil
+}
+
+func (b *BootImageModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ if SkipDexpreoptBootJars(ctx) {
+ return
+ }
+
+ // Add a dependency onto the dex2oat tool which is needed for creating the boot image. The
+ // path is retrieved from the dependency by GetGlobalSoongConfig(ctx).
+ dexpreopt.RegisterToolDeps(ctx)
+}
+
+func (b *BootImageModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // Nothing to do if skipping the dexpreopt of boot image jars.
+ if SkipDexpreoptBootJars(ctx) {
+ return
+ }
+
+ // Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
+ // GenerateSingletonBuildActions method as it cannot create it for itself.
+ dexpreopt.GetGlobalSoongConfig(ctx)
+
+ // Get a map of the image configs that are supported.
+ imageConfigs := genBootImageConfigs(ctx)
+
+ // Retrieve the config for this image.
+ imageName := b.properties.Image_name
+ imageConfig := imageConfigs[imageName]
+ if imageConfig == nil {
+ ctx.PropertyErrorf("image_name", "Unknown image name %q, expected one of %s", imageName, strings.Join(android.SortedStringKeys(imageConfigs), ", "))
+ return
+ }
+
+ // Construct the boot image info from the config.
+ info := BootImageInfo{imageConfig: imageConfig}
+
+ // Make it available for other modules.
+ ctx.SetProvider(BootImageInfoProvider, info)
+}
diff --git a/java/boot_image_test.go b/java/boot_image_test.go
new file mode 100644
index 000000000..a29578292
--- /dev/null
+++ b/java/boot_image_test.go
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 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 java
+
+import (
+ "testing"
+)
+
+// Contains some simple tests for boot_image logic, additional tests can be found in
+// apex/boot_image_test.go as the ART boot image requires modules from the ART apex.
+
+func TestUnknownBootImage(t *testing.T) {
+ testJavaError(t, "image_name: Unknown image name \\\"unknown\\\", expected one of art, boot", `
+ boot_image {
+ name: "unknown-boot-image",
+ image_name: "unknown",
+ }
+`)
+}
diff --git a/java/boot_jars.go b/java/boot_jars.go
index 823275b1d..ac8107b7c 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -49,14 +49,36 @@ func populateMapFromConfiguredJarList(ctx android.SingletonContext, moduleToApex
return true
}
+// isActiveModule returns true if the given module should be considered for boot
+// jars, i.e. if it's enabled and the preferred one in case of source and
+// prebuilt alternatives.
+func isActiveModule(module android.Module) bool {
+ if !module.Enabled() {
+ return false
+ }
+ if module.IsReplacedByPrebuilt() {
+ // A source module that has been replaced by a prebuilt counterpart.
+ return false
+ }
+ if prebuilt, ok := module.(android.PrebuiltInterface); ok {
+ if p := prebuilt.Prebuilt(); p != nil {
+ return p.UsePrebuilt()
+ }
+ }
+ return true
+}
+
func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
config := ctx.Config()
if config.SkipBootJarsCheck() {
return
}
- // Populate a map from module name to APEX from the boot jars. If there is a problem
- // such as duplicate modules then fail and return immediately.
+ // Populate a map from module name to APEX from the boot jars. If there is a
+ // problem such as duplicate modules then fail and return immediately. Note
+ // that both module and APEX names are tracked by base names here, so we need
+ // to be careful to remove "prebuilt_" prefixes when comparing them with
+ // actual modules and APEX bundles.
moduleToApex := make(map[string]string)
if !populateMapFromConfiguredJarList(ctx, moduleToApex, config.NonUpdatableBootJars(), "BootJars") ||
!populateMapFromConfiguredJarList(ctx, moduleToApex, config.UpdatableBootJars(), "UpdatableBootJars") {
@@ -69,10 +91,14 @@ func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
// Scan all the modules looking for the module/apex variants corresponding to the
// boot jars.
ctx.VisitAllModules(func(module android.Module) {
- name := ctx.ModuleName(module)
+ if !isActiveModule(module) {
+ return
+ }
+
+ name := android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName(module))
if apex, ok := moduleToApex[name]; ok {
apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
- if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApex(apex) {
+ if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApexByBaseName(apex) {
// The module name/apex variant should be unique in the system but double check
// just in case something has gone wrong.
if existing, ok := nameToApexVariant[name]; ok {
diff --git a/java/dex.go b/java/dex.go
index 055d47983..24600c20f 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -263,10 +263,10 @@ func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Fl
}
func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, minSdkVersion sdkSpec,
- classesJar android.Path, jarName string) android.ModuleOutPath {
+ classesJar android.Path, jarName string) android.OutputPath {
// Compile classes.jar into classes.dex and then javalib.jar
- javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
+ javalibJar := android.PathForModuleOut(ctx, "dex", jarName).OutputPath
outDir := android.PathForModuleOut(ctx, "dex")
zipFlags := "--ignore_missing_files"
@@ -329,7 +329,7 @@ func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, mi
})
}
if proptools.Bool(d.dexProperties.Uncompress_dex) {
- alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName)
+ alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName).OutputPath
TransformZipAlign(ctx, alignedJavalibJar, javalibJar)
javalibJar = alignedJavalibJar
}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index b5830c744..29c73c11f 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -30,6 +30,7 @@ type dexpreopter struct {
installPath android.InstallPath
uncompressedDex bool
isSDKLibrary bool
+ isApp bool
isTest bool
isPresignedPrebuilt bool
@@ -38,6 +39,11 @@ type dexpreopter struct {
classLoaderContexts dexpreopt.ClassLoaderContextMap
builtInstalled string
+
+ // A path to a dexpreopt.config file generated by Soong for libraries that may be used as a
+ // <uses-library> by Make modules. The path is passed to Make via LOCAL_SOONG_DEXPREOPT_CONFIG
+ // variable. If the path is nil, no config is generated (which is the case for apps and tests).
+ configPath android.WritablePath
}
type DexpreoptProperties struct {
@@ -112,12 +118,42 @@ func odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPat
return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
}
-func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.ModuleOutPath) {
+func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.WritablePath) {
// TODO(b/148690468): The check on d.installPath is to bail out in cases where
// the dexpreopter struct hasn't been fully initialized before we're called,
// e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively
// disabled, even if installable is true.
- if d.dexpreoptDisabled(ctx) || d.installPath.Base() == "." {
+ if d.installPath.Base() == "." {
+ return
+ }
+
+ dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
+
+ providesUsesLib := ctx.ModuleName()
+ if ulib, ok := ctx.Module().(ProvidesUsesLib); ok {
+ name := ulib.ProvidesUsesLib()
+ if name != nil {
+ providesUsesLib = *name
+ }
+ }
+
+ if !d.isApp && !d.isTest {
+ // Slim dexpreopt config is serialized to dexpreopt.config files and used by
+ // dex_preopt_config_merger.py to get information about <uses-library> dependencies.
+ // Note that it might be needed even if dexpreopt is disabled for this module.
+ slimDexpreoptConfig := &dexpreopt.ModuleConfig{
+ Name: ctx.ModuleName(),
+ DexLocation: dexLocation,
+ EnforceUsesLibraries: d.enforceUsesLibs,
+ ProvidesUsesLibrary: providesUsesLib,
+ ClassLoaderContexts: d.classLoaderContexts,
+ // The rest of the fields are not needed.
+ }
+ d.configPath = android.PathForModuleOut(ctx, "dexpreopt", "dexpreopt.config")
+ dexpreopt.WriteSlimModuleConfigForMake(ctx, slimDexpreoptConfig, d.configPath)
+ }
+
+ if d.dexpreoptDisabled(ctx) {
return
}
@@ -157,8 +193,6 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo
// The image locations for all Android variants are identical.
imageLocations := bootImage.getAnyAndroidVariant().imageLocations()
- dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
-
var profileClassListing android.OptionalPath
var profileBootListing android.OptionalPath
profileIsTextListing := false
@@ -177,6 +211,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo
}
}
+ // Full dexpreopt config, used to create dexpreopt build rules.
dexpreoptConfig := &dexpreopt.ModuleConfig{
Name: ctx.ModuleName(),
DexLocation: dexLocation,
@@ -192,6 +227,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo
ProfileBootListing: profileBootListing,
EnforceUsesLibraries: d.enforceUsesLibs,
+ ProvidesUsesLibrary: providesUsesLib,
ClassLoaderContexts: d.classLoaderContexts,
Archs: archs,
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 004cbbb5a..2a7eb42dc 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -35,6 +35,13 @@ import (
//
// Changes:
// 1) dex_bootjars is now a singleton module and not a plain singleton.
+// 2) Boot images are now represented by the boot_image module type.
+// 3) The art boot image is called "art-boot-image", the framework boot image is called
+// "framework-boot-image".
+// 4) They are defined in art/build/boot/Android.bp and frameworks/base/boot/Android.bp
+// respectively.
+// 5) Each boot_image retrieves the appropriate boot image configuration from the map returned by
+// genBootImageConfigs() using the image_name specified in the boot_image module.
// =================================================================================================
// This comment describes:
@@ -385,22 +392,6 @@ type dexpreoptBootJars struct {
dexpreoptConfigForMake android.WritablePath
}
-// Accessor function for the apex package. Returns nil if dexpreopt is disabled.
-func DexpreoptedArtApexJars(ctx android.BuilderContext) map[android.ArchType]android.OutputPaths {
- if SkipDexpreoptBootJars(ctx) {
- return nil
- }
- // Include dexpreopt files for the primary boot image.
- files := map[android.ArchType]android.OutputPaths{}
- for _, variant := range artBootImageConfig(ctx).variants {
- // We also generate boot images for host (for testing), but we don't need those in the apex.
- if variant.target.Os == android.Android {
- files[variant.target.Arch.ArchType] = variant.imagesDeps
- }
- }
- return files
-}
-
// Provide paths to boot images for use by modules that depend upon them.
//
// The build rules are created in GenerateSingletonBuildActions().
@@ -479,7 +470,7 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul
// A platform variant is required but this is for an apex so ignore it.
return -1, nil
}
- } else if !android.InList(requiredApex, apexInfo.InApexes) {
+ } else if !apexInfo.InApexByBaseName(requiredApex) {
// An apex variant for a specific apex is required but this is the wrong apex.
return -1, nil
}
@@ -489,7 +480,7 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul
switch image.name {
case artBootImageName:
- if len(apexInfo.InApexes) > 0 && allHavePrefix(apexInfo.InApexes, "com.android.art") {
+ if apexInfo.InApexByBaseName("com.android.art") || apexInfo.InApexByBaseName("com.android.art.debug") || apexInfo.InApexByBaseName("com.android.art,testing") {
// ok: found the jar in the ART apex
} else if name == "jacocoagent" && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
// exception (skip and continue): Jacoco platform variant for a coverage build
@@ -516,21 +507,17 @@ func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, modul
return index, jar.DexJarBuildPath()
}
-func allHavePrefix(list []string, prefix string) bool {
- for _, s := range list {
- if s != prefix && !strings.HasPrefix(s, prefix+".") {
- return false
- }
- }
- return true
-}
-
// buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootImageConfig {
// Collect dex jar paths for the boot image modules.
// This logic is tested in the apex package to avoid import cycle apex <-> java.
bootDexJars := make(android.Paths, image.modules.Len())
+
ctx.VisitAllModules(func(module android.Module) {
+ if !isActiveModule(module) {
+ return
+ }
+
if i, j := getBootImageJar(ctx, image, module); i != -1 {
if existing := bootDexJars[i]; existing != nil {
ctx.Errorf("Multiple dex jars found for %s:%s - %s and %s",
@@ -860,6 +847,9 @@ func updatableBcpPackagesRule(ctx android.SingletonContext, image *bootImageConf
// Collect `permitted_packages` for updatable boot jars.
var updatablePackages []string
ctx.VisitAllModules(func(module android.Module) {
+ if !isActiveModule(module) {
+ return
+ }
if j, ok := module.(PermittedPackagesForUpdatableBootJars); ok {
name := ctx.ModuleName(module)
if i := android.IndexList(name, updatableModules); i != -1 {
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 5550a4c17..a9e0773b7 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -148,7 +148,7 @@ func TestDexpreoptEnabled(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
ctx, _ := testJava(t, test.bp)
- dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeDescription("dexpreopt")
+ dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt")
enabled := dexpreopt.Rule != nil
if enabled != test.enabled {
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 71f1e576d..eafbf5df0 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -28,10 +28,40 @@ var hiddenAPIGenerateCSVRule = pctx.AndroidStaticRule("hiddenAPIGenerateCSV", bl
}, "outFlag", "stubAPIFlags")
type hiddenAPI struct {
- bootDexJarPath android.Path
- flagsCSVPath android.Path
- indexCSVPath android.Path
+ // The path to the dex jar that is in the boot class path. If this is nil then the associated
+ // module is not a boot jar, but could be one of the <x>-hiddenapi modules that provide additional
+ // annotations for the <x> boot dex jar but which do not actually provide a boot dex jar
+ // themselves.
+ bootDexJarPath android.Path
+
+ // The path to the CSV file that contains mappings from Java signature to various flags derived
+ // from annotations in the source, e.g. whether it is public or the sdk version above which it
+ // can no longer be used.
+ //
+ // It is created by the Class2NonSdkList tool which processes the .class files in the class
+ // implementation jar looking for UnsupportedAppUsage and CovariantReturnType annotations. The
+ // tool also consumes the hiddenAPISingletonPathsStruct.stubFlags file in order to perform
+ // consistency checks on the information in the annotations and to filter out bridge methods
+ // that are already part of the public API.
+ flagsCSVPath android.Path
+
+ // The path to the CSV file that contains mappings from Java signature to the value of properties
+ // specified on UnsupportedAppUsage annotations in the source.
+ //
+ // Like the flagsCSVPath file this is also created by the Class2NonSdkList in the same way.
+ // Although the two files could potentially be created in a single invocation of the
+ // Class2NonSdkList at the moment they are created using their own invocation, with the behavior
+ // being determined by the property that is used.
metadataCSVPath android.Path
+
+ // The path to the CSV file that contains mappings from Java signature to source location
+ // information.
+ //
+ // It is created by the merge_csv tool which processes the class implementation jar, extracting
+ // all the files ending in .uau (which are CSV files) and merges them together. The .uau files are
+ // created by the unsupported app usage annotation processor during compilation of the class
+ // implementation jar.
+ indexCSVPath android.Path
}
func (h *hiddenAPI) flagsCSV() android.Path {
@@ -59,8 +89,8 @@ type hiddenAPIIntf interface {
var _ hiddenAPIIntf = (*hiddenAPI)(nil)
-func (h *hiddenAPI) hiddenAPI(ctx android.ModuleContext, name string, primary bool, dexJar android.ModuleOutPath,
- implementationJar android.Path, uncompressDex bool) android.ModuleOutPath {
+func (h *hiddenAPI) hiddenAPI(ctx android.ModuleContext, name string, primary bool, dexJar android.OutputPath,
+ implementationJar android.Path, uncompressDex bool) android.OutputPath {
if !ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") {
// Modules whose names are of the format <x>-hiddenapi provide hiddenapi information
@@ -78,17 +108,15 @@ func (h *hiddenAPI) hiddenAPI(ctx android.ModuleContext, name string, primary bo
// not on the list then that will cause failures in the CtsHiddenApiBlacklist...
// tests.
if inList(bootJarName, ctx.Config().BootJars()) {
- // Derive the greylist from classes jar.
- flagsCSV := android.PathForModuleOut(ctx, "hiddenapi", "flags.csv")
- metadataCSV := android.PathForModuleOut(ctx, "hiddenapi", "metadata.csv")
- indexCSV := android.PathForModuleOut(ctx, "hiddenapi", "index.csv")
- h.hiddenAPIGenerateCSV(ctx, flagsCSV, metadataCSV, indexCSV, implementationJar)
+ // Create ninja rules to generate various CSV files needed by hiddenapi and store the paths
+ // in the hiddenAPI structure.
+ h.hiddenAPIGenerateCSV(ctx, implementationJar)
// If this module is actually on the boot jars list and not providing
// hiddenapi information for a module on the boot jars list then encode
// the gathered information in the generated dex file.
if name == bootJarName {
- hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", name+".jar")
+ hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", name+".jar").OutputPath
// More than one library with the same classes can be encoded but only one can
// be added to the global set of flags, otherwise it will result in duplicate
@@ -106,9 +134,10 @@ func (h *hiddenAPI) hiddenAPI(ctx android.ModuleContext, name string, primary bo
return dexJar
}
-func (h *hiddenAPI) hiddenAPIGenerateCSV(ctx android.ModuleContext, flagsCSV, metadataCSV, indexCSV android.WritablePath, classesJar android.Path) {
+func (h *hiddenAPI) hiddenAPIGenerateCSV(ctx android.ModuleContext, classesJar android.Path) {
stubFlagsCSV := hiddenAPISingletonPaths(ctx).stubFlags
+ flagsCSV := android.PathForModuleOut(ctx, "hiddenapi", "flags.csv")
ctx.Build(pctx, android.BuildParams{
Rule: hiddenAPIGenerateCSVRule,
Description: "hiddenapi flags",
@@ -122,6 +151,7 @@ func (h *hiddenAPI) hiddenAPIGenerateCSV(ctx android.ModuleContext, flagsCSV, me
})
h.flagsCSVPath = flagsCSV
+ metadataCSV := android.PathForModuleOut(ctx, "hiddenapi", "metadata.csv")
ctx.Build(pctx, android.BuildParams{
Rule: hiddenAPIGenerateCSVRule,
Description: "hiddenapi metadata",
@@ -135,6 +165,7 @@ func (h *hiddenAPI) hiddenAPIGenerateCSV(ctx android.ModuleContext, flagsCSV, me
})
h.metadataCSVPath = metadataCSV
+ indexCSV := android.PathForModuleOut(ctx, "hiddenapi", "index.csv")
rule := android.NewRuleBuilder(pctx, ctx)
rule.Command().
BuiltTool("merge_csv").
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 4bd255cbf..ccb874506 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -28,9 +28,64 @@ func init() {
}
type hiddenAPISingletonPathsStruct struct {
- flags android.OutputPath
- index android.OutputPath
- metadata android.OutputPath
+ // The path to the CSV file that contains the flags that will be encoded into the dex boot jars.
+ //
+ // It is created by the generate_hiddenapi_lists.py tool that is passed the stubFlags along with
+ // a number of additional files that are used to augment the information in the stubFlags with
+ // manually curated data.
+ flags android.OutputPath
+
+ // The path to the CSV index file that contains mappings from Java signature to source location
+ // information for all Java elements annotated with the UnsupportedAppUsage annotation in the
+ // source of all the boot jars.
+ //
+ // It is created by the merge_csv tool which merges all the hiddenAPI.indexCSVPath files that have
+ // been created by the rest of the build. That includes the index files generated for
+ // <x>-hiddenapi modules.
+ index android.OutputPath
+
+ // The path to the CSV metadata file that contains mappings from Java signature to the value of
+ // properties specified on UnsupportedAppUsage annotations in the source of all the boot jars.
+ //
+ // It is created by the merge_csv tool which merges all the hiddenAPI.metadataCSVPath files that
+ // have been created by the rest of the build. That includes the metadata files generated for
+ // <x>-hiddenapi modules.
+ metadata android.OutputPath
+
+ // The path to the CSV metadata file that contains mappings from Java signature to flags obtained
+ // from the public, system and test API stubs.
+ //
+ // This is created by the hiddenapi tool which is given dex files for the public, system and test
+ // API stubs (including product specific stubs) along with dex boot jars, so does not include
+ // <x>-hiddenapi modules. For each API surface (i.e. public, system, test) it records which
+ // members in the dex boot jars match a member in the dex stub jars for that API surface and then
+ // outputs a file containing the signatures of all members in the dex boot jars along with the
+ // flags that indicate which API surface it belongs, if any.
+ //
+ // e.g. a dex member that matches a member in the public dex stubs would have flags
+ // "public-api,system-api,test-api" set (as system and test are both supersets of public). A dex
+ // member that didn't match a member in any of the dex stubs is still output it just has an empty
+ // set of flags.
+ //
+ // The notion of matching is quite complex, it is not restricted to just exact matching but also
+ // follows the Java inheritance rules. e.g. if a method is public then all overriding/implementing
+ // methods are also public. If an interface method is public and a class inherits an
+ // implementation of that method from a super class then that super class method is also public.
+ // That ensures that any method that can be called directly by an App through a public method is
+ // visible to that App.
+ //
+ // Propagating the visibility of members across the inheritance hierarchy at build time will cause
+ // problems when modularizing and unbundling as it that propagation can cross module boundaries.
+ // e.g. Say that a private framework class implements a public interface and inherits an
+ // implementation of one of its methods from a core platform ART class. In that case the ART
+ // implementation method needs to be marked as public which requires the build to have access to
+ // the framework implementation classes at build time. The work to rectify this is being tracked
+ // at http://b/178693149.
+ //
+ // This file (or at least those items marked as being in the public-api) is used by hiddenapi when
+ // creating the metadata and flags for the individual modules in order to perform consistency
+ // checks and filter out bridge methods that are part of the public API. The latter relies on the
+ // propagation of visibility across the inheritance hierarchy.
stubFlags android.OutputPath
}
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index 0f9ef5841..27f363e0c 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -65,7 +65,7 @@ func TestHiddenAPISingleton(t *testing.T) {
srcs: ["a.java"],
compile_dex: true,
}
- `, []string{":foo"}, nil)
+ `, []string{"platform:foo"}, nil)
hiddenAPI := ctx.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -82,7 +82,7 @@ func TestHiddenAPISingletonWithPrebuilt(t *testing.T) {
jars: ["a.jar"],
compile_dex: true,
}
- `, []string{":foo"}, nil)
+ `, []string{"platform:foo"}, nil)
hiddenAPI := ctx.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -106,7 +106,7 @@ func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) {
compile_dex: true,
prefer: false,
}
- `, []string{":foo"}, nil)
+ `, []string{"platform:foo"}, nil)
hiddenAPI := ctx.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -135,7 +135,7 @@ func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) {
compile_dex: true,
prefer: true,
}
- `, []string{":foo"}, nil)
+ `, []string{"platform:foo"}, nil)
hiddenAPI := ctx.SingletonForTests("hiddenapi")
hiddenapiRule := hiddenAPI.Rule("hiddenapi")
@@ -236,7 +236,7 @@ func TestHiddenAPISingletonWithPrebuiltCsvFile(t *testing.T) {
jars: ["a.jar"],
compile_dex: true,
}
- `, []string{":foo"}, &prebuiltHiddenApiDir)
+ `, []string{"platform:foo"}, &prebuiltHiddenApiDir)
expectedCpInput := prebuiltHiddenApiDir + "/hiddenapi-flags.csv"
expectedCpOutput := buildDir + "/hiddenapi/hiddenapi-flags.csv"
diff --git a/java/java.go b/java/java.go
index 59ec94d5b..91944f6e3 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1689,35 +1689,44 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) {
// Combine the classes built from sources, any manifests, and any static libraries into
// classes.jar. If there is only one input jar this step will be skipped.
- var outputFile android.ModuleOutPath
+ var outputFile android.OutputPath
if len(jars) == 1 && !manifest.Valid() {
+ // Optimization: skip the combine step as there is nothing to do
+ // TODO(ccross): this leaves any module-info.class files, but those should only come from
+ // prebuilt dependencies until we support modules in the platform build, so there shouldn't be
+ // any if len(jars) == 1.
+
+ // Transform the single path to the jar into an OutputPath as that is required by the following
+ // code.
if moduleOutPath, ok := jars[0].(android.ModuleOutPath); ok {
- // Optimization: skip the combine step if there is nothing to do
- // TODO(ccross): this leaves any module-info.class files, but those should only come from
- // prebuilt dependencies until we support modules in the platform build, so there shouldn't be
- // any if len(jars) == 1.
- outputFile = moduleOutPath
+ // The path contains an embedded OutputPath so reuse that.
+ outputFile = moduleOutPath.OutputPath
+ } else if outputPath, ok := jars[0].(android.OutputPath); ok {
+ // The path is an OutputPath so reuse it directly.
+ outputFile = outputPath
} else {
+ // The file is not in the out directory so create an OutputPath into which it can be copied
+ // and which the following code can use to refer to it.
combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
ctx.Build(pctx, android.BuildParams{
Rule: android.Cp,
Input: jars[0],
Output: combinedJar,
})
- outputFile = combinedJar
+ outputFile = combinedJar.OutputPath
}
} else {
combinedJar := android.PathForModuleOut(ctx, "combined", jarName)
TransformJarsToJar(ctx, combinedJar, "for javac", jars, manifest,
false, nil, nil)
- outputFile = combinedJar
+ outputFile = combinedJar.OutputPath
}
// jarjar implementation jar if necessary
if j.expandJarjarRules != nil {
// Transform classes.jar into classes-jarjar.jar
- jarjarFile := android.PathForModuleOut(ctx, "jarjar", jarName)
+ jarjarFile := android.PathForModuleOut(ctx, "jarjar", jarName).OutputPath
TransformJarJar(ctx, jarjarFile, outputFile, j.expandJarjarRules)
outputFile = jarjarFile
@@ -1762,7 +1771,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) {
implementationAndResourcesJar := outputFile
if j.resourceJar != nil {
jars := android.Paths{j.resourceJar, implementationAndResourcesJar}
- combinedJar := android.PathForModuleOut(ctx, "withres", jarName)
+ combinedJar := android.PathForModuleOut(ctx, "withres", jarName).OutputPath
TransformJarsToJar(ctx, combinedJar, "for resources", jars, manifest,
false, nil, nil)
implementationAndResourcesJar = combinedJar
@@ -1788,7 +1797,7 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) {
android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags"))
}
// Dex compilation
- var dexOutputFile android.ModuleOutPath
+ var dexOutputFile android.OutputPath
dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
if ctx.Failed() {
return
@@ -1807,11 +1816,11 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) {
// merge dex jar with resources if necessary
if j.resourceJar != nil {
jars := android.Paths{dexOutputFile, j.resourceJar}
- combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName)
+ combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName).OutputPath
TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{},
false, nil, nil)
if *j.dexProperties.Uncompress_dex {
- combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName)
+ combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName).OutputPath
TransformZipAlign(ctx, combinedAlignedJar, combinedJar)
dexOutputFile = combinedAlignedJar
} else {
@@ -1875,7 +1884,7 @@ func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, i
jarName += strconv.Itoa(idx)
}
- classes := android.PathForModuleOut(ctx, "javac", jarName)
+ classes := android.PathForModuleOut(ctx, "javac", jarName).OutputPath
TransformJavaToClasses(ctx, classes, idx, srcFiles, srcJars, flags, extraJarDeps)
if ctx.Config().EmitXrefRules() {
@@ -1955,12 +1964,12 @@ func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars
}
func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags,
- classesJar android.Path, jarName string) android.ModuleOutPath {
+ classesJar android.Path, jarName string) android.OutputPath {
specs := j.jacocoModuleToZipCommand(ctx)
jacocoReportClassesFile := android.PathForModuleOut(ctx, "jacoco-report-classes", jarName)
- instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName)
+ instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName).OutputPath
jacocoInstrumentJar(ctx, instrumentedJar, jacocoReportClassesFile, classesJar, specs)
@@ -2374,7 +2383,7 @@ type testProperties struct {
// list of files or filegroup modules that provide data that should be installed alongside
// the test
- Data []string `android:"path"`
+ Data []string `android:"path,arch_variant"`
// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
@@ -2678,9 +2687,10 @@ func (j *Binary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
}
func (j *Binary) DepsMutator(ctx android.BottomUpMutatorContext) {
- if ctx.Arch().ArchType == android.Common {
+ if ctx.Arch().ArchType == android.Common || ctx.BazelConversionMode() {
j.deps(ctx)
- } else {
+ }
+ if ctx.Arch().ArchType != android.Common || ctx.BazelConversionMode() {
// These dependencies ensure the host installation rules will install the jar file and
// the jni libraries when the wrapper is installed.
ctx.AddVariationDependencies(nil, jniInstallTag, j.binaryProperties.Jni_libs...)
@@ -2937,7 +2947,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
}
j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
- var dexOutputFile android.ModuleOutPath
+ var dexOutputFile android.OutputPath
dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
if ctx.Failed() {
return
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 0ffbaaa40..1e90149ea 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -15,12 +15,14 @@
package java
import (
+ "fmt"
"strconv"
"strings"
"github.com/google/blueprint/proptools"
"android/soong/android"
+ "android/soong/genrule"
)
func init() {
@@ -35,6 +37,12 @@ type prebuiltApisProperties struct {
// list of api version directories
Api_dirs []string
+ // The next API directory can optionally point to a directory where
+ // files incompatibility-tracking files are stored for the current
+ // "in progress" API. Each module present in one of the api_dirs will have
+ // a <module>-incompatibilities.api.<scope>.latest module created.
+ Next_api_dir *string
+
// The sdk_version of java_import modules generated based on jar files.
// Defaults to "current"
Imports_sdk_version *string
@@ -98,28 +106,46 @@ func createImport(mctx android.LoadHookContext, module, scope, apiver, path, sdk
mctx.CreateModule(ImportFactory, &props)
}
-func createFilegroup(mctx android.LoadHookContext, module string, scope string, apiver string, path string) {
- fgName := module + ".api." + scope + "." + apiver
+func createFilegroup(mctx android.LoadHookContext, name string, path string) {
filegroupProps := struct {
Name *string
Srcs []string
}{}
- filegroupProps.Name = proptools.StringPtr(fgName)
+ filegroupProps.Name = proptools.StringPtr(name)
filegroupProps.Srcs = []string{path}
mctx.CreateModule(android.FileGroupFactory, &filegroupProps)
}
+func createEmptyFile(mctx android.LoadHookContext, name string) {
+ props := struct {
+ Name *string
+ Cmd *string
+ Out []string
+ }{}
+ props.Name = proptools.StringPtr(name)
+ props.Out = []string{name}
+ props.Cmd = proptools.StringPtr("touch $(genDir)/" + name)
+ mctx.CreateModule(genrule.GenRuleFactory, &props)
+}
+
func getPrebuiltFiles(mctx android.LoadHookContext, p *prebuiltApis, name string) []string {
- mydir := mctx.ModuleDir() + "/"
var files []string
for _, apiver := range p.properties.Api_dirs {
- for _, scope := range []string{"public", "system", "test", "core", "module-lib", "system-server"} {
- vfiles, err := mctx.GlobWithDeps(mydir+apiver+"/"+scope+"/"+name, nil)
- if err != nil {
- mctx.ModuleErrorf("failed to glob %s files under %q: %s", name, mydir+apiver+"/"+scope, err)
- }
- files = append(files, vfiles...)
+ files = append(files, getPrebuiltFilesInSubdir(mctx, apiver, name)...)
+ }
+ return files
+}
+
+func getPrebuiltFilesInSubdir(mctx android.LoadHookContext, subdir string, name string) []string {
+ var files []string
+ dir := mctx.ModuleDir() + "/" + subdir
+ for _, scope := range []string{"public", "system", "test", "core", "module-lib", "system-server"} {
+ glob := fmt.Sprintf("%s/%s/%s", dir, scope, name)
+ vfiles, err := mctx.GlobWithDeps(glob, nil)
+ if err != nil {
+ mctx.ModuleErrorf("failed to glob %s files under %q: %s", name, dir+"/"+scope, err)
}
+ files = append(files, vfiles...)
}
return files
}
@@ -181,11 +207,14 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) {
// Create filegroups for all (<module>, <scope, <version>) triplets,
// and a "latest" filegroup variant for each (<module>, <scope>) pair
+ moduleName := func(module, scope, version string) string {
+ return module + ".api." + scope + "." + version
+ }
m := make(map[string]latestApiInfo)
for _, f := range files {
localPath := strings.TrimPrefix(f, mydir)
module, apiver, scope := parseApiFilePath(mctx, localPath)
- createFilegroup(mctx, module, scope, apiver, localPath)
+ createFilegroup(mctx, moduleName(module, scope, apiver), localPath)
version, err := strconv.Atoi(apiver)
if err != nil {
@@ -193,20 +222,52 @@ func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) {
return
}
- key := module + "." + scope
- info, ok := m[key]
- if !ok {
- m[key] = latestApiInfo{module, scope, version, localPath}
- } else if version > info.version {
- info.version = version
- info.path = localPath
- m[key] = info
+ // Track latest version of each module/scope, except for incompatibilities
+ if !strings.HasSuffix(module, "incompatibilities") {
+ key := module + "." + scope
+ info, ok := m[key]
+ if !ok {
+ m[key] = latestApiInfo{module, scope, version, localPath}
+ } else if version > info.version {
+ info.version = version
+ info.path = localPath
+ m[key] = info
+ }
}
}
+
// Sort the keys in order to make build.ninja stable
for _, k := range android.SortedStringKeys(m) {
info := m[k]
- createFilegroup(mctx, info.module, info.scope, "latest", info.path)
+ name := moduleName(info.module, info.scope, "latest")
+ createFilegroup(mctx, name, info.path)
+ }
+
+ // Create incompatibilities tracking files for all modules, if we have a "next" api.
+ if nextApiDir := String(p.properties.Next_api_dir); nextApiDir != "" {
+ files := getPrebuiltFilesInSubdir(mctx, nextApiDir, "api/*incompatibilities.txt")
+ incompatibilities := make(map[string]bool)
+ for _, f := range files {
+ localPath := strings.TrimPrefix(f, mydir)
+ module, _, scope := parseApiFilePath(mctx, localPath)
+
+ // Figure out which module is referenced by this file. Special case for "android".
+ referencedModule := strings.TrimSuffix(module, "incompatibilities")
+ referencedModule = strings.TrimSuffix(referencedModule, "-")
+ if referencedModule == "" {
+ referencedModule = "android"
+ }
+
+ createFilegroup(mctx, moduleName(referencedModule+"-incompatibilities", scope, "latest"), localPath)
+
+ incompatibilities[referencedModule+"."+scope] = true
+ }
+ // Create empty incompatibilities files for remaining modules
+ for _, k := range android.SortedStringKeys(m) {
+ if _, ok := incompatibilities[k]; !ok {
+ createEmptyFile(mctx, moduleName(m[k].module+"-incompatibilities", m[k].scope, "latest"))
+ }
+ }
}
}
diff --git a/java/proto.go b/java/proto.go
index dc5519f41..652a4daec 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -82,7 +82,7 @@ func protoDeps(ctx android.BottomUpMutatorContext, p *android.ProtoProperties) {
case "lite", "":
ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-lite")
case "full":
- if ctx.Host() {
+ if ctx.Host() || ctx.BazelConversionMode() {
ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-full")
} else {
ctx.PropertyErrorf("proto.type", "full java protos only supported on the host")
diff --git a/java/rro.go b/java/rro.go
index 98cd3793e..aafa88e3b 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -55,7 +55,11 @@ type RuntimeResourceOverlayProperties struct {
// only when the ro.boot.vendor.overlay.theme system property is set to the same value.
Theme *string
- // if not blank, set to the version of the sdk to compile against.
+ // If not blank, set to the version of the sdk to compile against. This
+ // can be either an API version (e.g. "29" for API level 29 AKA Android 10)
+ // or special subsets of the current platform, for example "none", "current",
+ // "core", "system", "test". See build/soong/java/sdk.go for the full and
+ // up-to-date list of possible values.
// Defaults to compiling against the current platform.
Sdk_version *string
diff --git a/java/testing.go b/java/testing.go
index 0b1f2d137..5fcf84c6b 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -107,6 +107,7 @@ func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
RegisterAppBuildComponents(ctx)
RegisterAppImportBuildComponents(ctx)
RegisterAppSetBuildComponents(ctx)
+ RegisterBootImageBuildComponents(ctx)
RegisterDexpreoptBootJarsComponents(ctx)
RegisterDocsBuildComponents(ctx)
RegisterGenRuleBuildComponents(ctx)