Merge "Remove obsolete com.android.bluetooth apex"
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index cc854a3..872e6a0 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -119,7 +119,10 @@
 		"external/icu":                           Bp2BuildDefaultTrueRecursively,
 		"external/icu/android_icu4j":             Bp2BuildDefaultFalse, // java rules incomplete
 		"external/icu/icu4j":                     Bp2BuildDefaultFalse, // java rules incomplete
+		"external/jacoco":                        Bp2BuildDefaultTrueRecursively,
 		"external/jarjar":                        Bp2BuildDefaultTrueRecursively,
+		"external/javassist":                     Bp2BuildDefaultTrueRecursively,
+		"external/javaparser":                    Bp2BuildDefaultTrueRecursively,
 		"external/javapoet":                      Bp2BuildDefaultTrueRecursively,
 		"external/jemalloc_new":                  Bp2BuildDefaultTrueRecursively,
 		"external/jsoncpp":                       Bp2BuildDefaultTrueRecursively,
@@ -154,9 +157,11 @@
 		"external/zopfli":                        Bp2BuildDefaultTrueRecursively,
 		"external/zstd":                          Bp2BuildDefaultTrueRecursively,
 
+		"frameworks/av":                                      Bp2BuildDefaultTrue,
 		"frameworks/av/media/codecs":                         Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/media/liberror":                       Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/services/minijail":                    Bp2BuildDefaultTrueRecursively,
+		"frameworks/av/media/module/minijail":                Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/media/tests/MediaDump":              Bp2BuildDefaultTrue,
 		"frameworks/base/services/tests/servicestests/aidl":  Bp2BuildDefaultTrue,
 		"frameworks/base/startop/apps/test":                  Bp2BuildDefaultTrue,
@@ -177,6 +182,7 @@
 		"hardware/interfaces/configstore/1.0":          Bp2BuildDefaultTrue,
 		"hardware/interfaces/configstore/1.1":          Bp2BuildDefaultTrue,
 		"hardware/interfaces/configstore/utils":        Bp2BuildDefaultTrue,
+		"hardware/interfaces/graphics/allocator/aidl":  Bp2BuildDefaultTrue,
 		"hardware/interfaces/graphics/allocator/2.0":   Bp2BuildDefaultTrue,
 		"hardware/interfaces/graphics/allocator/3.0":   Bp2BuildDefaultTrue,
 		"hardware/interfaces/graphics/allocator/4.0":   Bp2BuildDefaultTrue,
@@ -218,6 +224,7 @@
 		"packages/modules/adb/proto":                       Bp2BuildDefaultTrueRecursively,
 		"packages/modules/adb/tls":                         Bp2BuildDefaultTrueRecursively,
 		"packages/providers/MediaProvider/tools/dialogs":   Bp2BuildDefaultFalse, // TODO(b/242834374)
+		"packages/modules/NeuralNetworks/driver/cache":     Bp2BuildDefaultTrueRecursively,
 		"packages/screensavers/Basic":                      Bp2BuildDefaultTrue,
 		"packages/services/Car/tests/SampleRearViewCamera": Bp2BuildDefaultFalse, // TODO(b/242834321)
 
@@ -277,6 +284,7 @@
 		"system/timezone/apex":                                   Bp2BuildDefaultTrueRecursively,
 		"system/timezone/output_data":                            Bp2BuildDefaultTrueRecursively,
 		"system/tools/sysprop":                                   Bp2BuildDefaultTrue,
+		"system/tools/aidl/build/tests_bp2build":                 Bp2BuildDefaultTrue,
 		"system/unwinding/libunwindstack":                        Bp2BuildDefaultTrueRecursively,
 
 		"tools/apksig": Bp2BuildDefaultTrue,
@@ -298,10 +306,12 @@
 		// external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails
 		// e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
 		"external/bazelbuild-rules_android":/* recursive = */ true,
+		"external/bazelbuild-kotlin-rules":/* recursive = */ true,
 		"external/bazel-skylib":/* recursive = */ true,
 		"external/guava":/* recursive = */ true,
 		"external/jsr305":/* recursive = */ true,
 		"external/protobuf":/* recursive = */ false,
+		"frameworks/base/tools/codegen":/* recursive = */ true,
 		"frameworks/ex/common":/* recursive = */ true,
 
 		"packages/apps/Music":/* recursive = */ true,
@@ -324,6 +334,7 @@
 		"code_coverage.policy",
 		"code_coverage.policy.other",
 		"codec2_soft_exports",
+		"codecs_g711dec",
 		"com.android.media.swcodec-androidManifest",
 		"com.android.media.swcodec-ld.config.txt",
 		"com.android.media.swcodec-mediaswcodec.rc",
@@ -335,6 +346,7 @@
 		"flatbuffer_headers",
 		"gemmlowp_headers",
 		"gl_headers",
+		"libaidlcommonsupport",
 		"libandroid_runtime_lazy",
 		"libandroid_runtime_vm_headers",
 		"libaudioclient_aidl_conversion_util",
@@ -343,6 +355,7 @@
 		"libbinder_aidl",
 		"libbinder_headers",
 		"libbinder_headers_platform_shared",
+		"libbinderthreadstateutils",
 		"libbluetooth-types-header",
 		"libbufferhub_headers",
 		"libcodec2",
@@ -352,6 +365,10 @@
 		"libdvr_headers",
 		"libgsm",
 		"libgui_bufferqueue_sources",
+		"libgrallocusage",
+		"libgralloctypes",
+		"libnativewindow",
+		"libgraphicsenv",
 		"libhardware",
 		"libhardware_headers",
 		"libincfs_headers",
@@ -365,15 +382,28 @@
 		"libandroidio",
 		"libandroidio_srcs",
 		"libserviceutils",
+		"libstagefright_amrnbenc",
+		"libstagefright_amrnbdec",
+		"libstagefright_amrwbdec",
+		"libstagefright_amrwbenc",
+		"libstagefright_amrnb_common",
 		"libstagefright_enc_common",
+		"libstagefright_flacdec",
+		"libstagefright_foundation",
 		"libstagefright_foundation_headers",
 		"libstagefright_headers",
+		"libstagefright_m4vh263dec",
+		"libstagefright_m4vh263enc",
+		"libstagefright_mp3dec",
+		"libstagefright_mp3dec_headers",
 		"libsurfaceflinger_headers",
 		"libsync",
 		"libtextclassifier_hash_headers",
 		"libtextclassifier_hash_static",
 		"libtflite_kernel_utils",
 		"libtinyxml2",
+		"libgui_aidl",
+		"libui",
 		"libui-types",
 		"libui_headers",
 		"libvorbisidec",
@@ -381,11 +411,24 @@
 		"media_plugin_headers",
 		"mediaswcodec.policy",
 		"mediaswcodec.xml",
+		"neuralnetworks_types",
+		"neuralnetworks_utils_hal_aidl",
+		"neuralnetworks_utils_hal_common",
+		"neuralnetworks_utils_hal_1_0",
+		"neuralnetworks_utils_hal_1_1",
+		"neuralnetworks_utils_hal_1_2",
+		"neuralnetworks_utils_hal_1_3",
+		"libneuralnetworks_common",
 		"philox_random",
 		"philox_random_headers",
 		"server_configurable_flags",
 		"tensorflow_headers",
 
+		"libgui_headers",
+		"libstagefright_bufferpool@2.0",
+		"libstagefright_bufferpool@2.0.1",
+		"libSurfaceFlingerProp",
+
 		// fastboot
 		"bootimg_headers",
 		"fastboot",
@@ -445,6 +488,9 @@
 		"ILogcatManagerService_aidl",
 		"libincremental_aidl-cpp",
 		"incremental_aidl",
+
+		//frameworks/native/cmds/cmd
+		"libcmd",
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
@@ -524,7 +570,6 @@
 
 		// unconverted deps
 		"CarHTMLViewer",                                              // depends on unconverted modules android.car-stubs, car-ui-lib
-		"abb",                                                        // depends on unconverted modules: libcmd, libbinder
 		"adb",                                                        // depends on unconverted modules: AdbWinApi, libandroidfw, libopenscreen-discovery, libopenscreen-platform-impl, libusb, bin2c_fastdeployagent, AdbWinUsbApi
 		"android_icu4j_srcgen",                                       // depends on unconverted modules: currysrc
 		"android_icu4j_srcgen_binary",                                // depends on unconverted modules: android_icu4j_srcgen, currysrc
@@ -537,6 +582,7 @@
 		"generated_android_icu4j_resources",                          // depends on unconverted modules: android_icu4j_srcgen_binary, soong_zip
 		"generated_android_icu4j_test_resources",                     // depends on unconverted modules: android_icu4j_srcgen_binary, soong_zip
 		"host-libprotobuf-java-nano",                                 // b/220869005, depends on libprotobuf-java-nano
+		"jacoco-stubs",                                               // b/245767077, depends on droidstubs
 		"libapexutil",                                                // depends on unconverted modules: apex-info-list-tinyxml
 		"libart",                                                     // depends on unconverted modules: apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, heapprofd_client_api, art_operator_srcs, libcpu_features, libodrstatslog, libelffile, art_cmdlineparser_headers, cpp-define-generator-definitions, libdexfile, libnativebridge, libnativeloader, libsigchain, libartbase, libprofile, cpp-define-generator-asm-support
 		"libart-runtime-gtest",                                       // depends on unconverted modules: libgtest_isolated, libart-compiler, libdexfile, libprofile, libartbase, libartbase-art-gtest
@@ -605,11 +651,6 @@
 		"Mp3DecoderTest",       // depends on unconverted modules: libsndfile, libaudioutils
 		"Mpeg4H263DecoderTest", // depends on unconverted modules: libstagefright_foundation
 		"Mpeg4H263EncoderTest",
-		"adb_crypto_test",
-		"adb_pairing_auth_test",
-		"adb_pairing_connection_test",
-		"adb_tls_connection_test",
-		"adbd_test", // depends on unconverted modules: libusb
 		"avcdec",
 		"avcenc",
 		"bionic-benchmarks-tests",
@@ -658,7 +699,6 @@
 		"libBionicCtsGtestMain", // depends on unconverted modules: libgtest_isolated
 		"libBionicLoaderTests",  // depends on unconverted modules: libmeminfo
 		"libapexutil_tests",     // depends on unconverted modules: apex-info-list-tinyxml, libapexutil
-		"libavservices_minijail_unittest",
 		"libcutils_sockets_test",
 		"libexpectedutils_test",
 		"libhwbinder_latency",
@@ -705,7 +745,6 @@
 		"yuvconstants",
 		"yuvconvert",
 		"zipalign_tests",
-		// "zlib_tests",
 
 		// cc_test_library
 		"clang_diagnostic_tests",
@@ -1082,7 +1121,6 @@
 	}
 
 	ProdMixedBuildsEnabledList = []string{
-		// This list left intentionally empty for now. Add specific module names
-		// to have them built by Bazel in Prod Mixed Builds mode.
+		"com.android.adbd",
 	}
 )
diff --git a/android/androidmk.go b/android/androidmk.go
index 006e43d..18e3e7a 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -366,7 +366,9 @@
 	// Collate the contributions this module makes to the dist.
 	distContributions := &distContributions{}
 
-	distContributions.licenseMetadataFile = amod.licenseMetadataFile
+	if !exemptFromRequiredApplicableLicensesProperty(mod.(Module)) {
+		distContributions.licenseMetadataFile = amod.licenseMetadataFile
+	}
 
 	// Iterate over this module's dist structs, merged from the dist and dists properties.
 	for _, dist := range amod.Dists() {
@@ -458,10 +460,12 @@
 		ret = append(ret, fmt.Sprintf(".PHONY: %s\n", d.goals))
 		// Create dist-for-goals calls for each of the copy instructions.
 		for _, c := range d.copies {
-			ret = append(
-				ret,
-				fmt.Sprintf("$(if $(strip $(ALL_TARGETS.%s.META_LIC)),,$(eval ALL_TARGETS.%s.META_LIC := %s))\n",
-					c.from.String(), c.from.String(), distContributions.licenseMetadataFile.String()))
+			if distContributions.licenseMetadataFile != nil {
+				ret = append(
+					ret,
+					fmt.Sprintf("$(if $(strip $(ALL_TARGETS.%s.META_LIC)),,$(eval ALL_TARGETS.%s.META_LIC := %s))\n",
+						c.from.String(), c.from.String(), distContributions.licenseMetadataFile.String()))
+			}
 			ret = append(
 				ret,
 				fmt.Sprintf("$(call dist-for-goals,%s,%s:%s)\n", d.goals, c.from.String(), c.dest))
diff --git a/android/bazel.go b/android/bazel.go
index eb6aca4..dd1de7b 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -32,10 +32,13 @@
 	Bp2BuildTopLevel = "."
 )
 
-// Bp2buildAidlLibrary describes a filegroup module that are converted to aidl_library
-type Bp2buildAidlLibrary interface {
+// FileGroupAsLibrary describes a filegroup module that is converted to some library
+// such as aidl_library or proto_library.
+type FileGroupAsLibrary interface {
 	ShouldConvertToAidlLibrary(ctx BazelConversionPathContext) bool
+	ShouldConvertToProtoLibrary(ctx BazelConversionPathContext) bool
 	GetAidlLibraryLabel(ctx BazelConversionPathContext) string
+	GetProtoLibraryLabel(ctx BazelConversionPathContext) string
 }
 
 type BazelConversionStatus struct {
@@ -443,8 +446,8 @@
 	if ok, directoryPath := bp2buildDefaultTrueRecursively(packagePath, allowlist.defaultConfig); ok {
 		if moduleNameAllowed {
 			ctx.ModuleErrorf("A module cannot be in a directory marked Bp2BuildDefaultTrue"+
-				" or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: '%s'",
-				directoryPath)
+				" or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: '%s'"+
+				" Module: '%s'", directoryPath, moduleName)
 			return false
 		}
 
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 93b6779..3e490dd 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -694,6 +694,31 @@
 	formatString := `
 # This file is generated by soong_build. Do not edit.
 
+# a drop-in replacement for json.encode(), not available in cquery environment
+# TODO(cparsons): bring json module in and remove this function
+def json_encode(input):
+  # Avoiding recursion by limiting
+  #  - a dict to contain anything except a dict
+  #  - a list to contain only primitives
+  def encode_primitive(p):
+    t = type(p)
+    if t == "string" or t == "int":
+      return repr(p)
+    fail("unsupported value '%%s' of type '%%s'" %% (p, type(p)))
+
+  def encode_list(list):
+    return "[%%s]" %% ", ".join([encode_primitive(item) for item in list])
+
+  def encode_list_or_primitive(v):
+    return encode_list(v) if type(v) == "list" else encode_primitive(v)
+
+  if type(input) == "dict":
+    # TODO(juu): the result is read line by line so can't use '\n' yet
+    kv_pairs = [("%%s: %%s" %% (encode_primitive(k), encode_list_or_primitive(v))) for (k, v) in input.items()]
+    return "{ %%s }" %% ", ".join(kv_pairs)
+  else:
+    return encode_list_or_primitive(input)
+
 # Label Map Section
 %s
 
@@ -727,15 +752,6 @@
     fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms))
     return "UNKNOWN"
 
-def json_for_file(key, file):
-    return '"' + key + '":"' + file.path + '"'
-
-def json_for_files(key, files):
-    return '"' + key + '":[' + ",".join(['"' + f.path + '"' for f in files]) + ']'
-
-def json_for_labels(key, ll):
-    return '"' + key + '":[' + ",".join(['"' + str(x) + '"' for x in ll]) + ']'
-
 def format(target):
   id_string = str(target.label) + "|" + get_arch(target)
 
diff --git a/android/bazel_test.go b/android/bazel_test.go
index b578cca..dbe6067 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -266,7 +266,7 @@
 		{
 			description:    "module allowlist and enabled directory",
 			shouldConvert:  false,
-			expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir'"},
+			expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir' Module: 'foo'"},
 			module: TestBazelModule{
 				TestModuleInfo: bazel.TestModuleInfo{
 					ModuleName: "foo",
@@ -287,7 +287,7 @@
 		{
 			description:    "module allowlist and enabled subdirectory",
 			shouldConvert:  false,
-			expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir'"},
+			expectedErrors: []string{"A module cannot be in a directory marked Bp2BuildDefaultTrue or Bp2BuildDefaultTrueRecursively and also be in moduleAlwaysConvert. Directory: 'existing/build/dir' Module: 'foo'"},
 			module: TestBazelModule{
 				TestModuleInfo: bazel.TestModuleInfo{
 					ModuleName: "foo",
diff --git a/android/filegroup.go b/android/filegroup.go
index e609f63..e5eec87 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -33,6 +33,8 @@
 	ctx.RegisterModuleType("filegroup", FileGroupFactory)
 })
 
+var convertedProtoLibrarySuffix = "_bp2build_converted"
+
 // IsFilegroup checks that a module is a filegroup type
 func IsFilegroup(ctx bazel.OtherModuleContext, m blueprint.Module) bool {
 	return ctx.OtherModuleType(m) == "filegroup"
@@ -117,6 +119,20 @@
 
 		ctx.CreateBazelTargetModule(props, CommonAttributes{Name: fg.Name()}, attrs)
 	} else {
+		if fg.ShouldConvertToProtoLibrary(ctx) {
+			attrs := &ProtoAttrs{
+				Srcs:                srcs,
+				Strip_import_prefix: fg.properties.Path,
+			}
+
+			ctx.CreateBazelTargetModule(
+				bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
+				CommonAttributes{Name: fg.Name() + convertedProtoLibrarySuffix},
+				attrs)
+		}
+
+		// TODO(b/242847534): Still convert to a filegroup because other unconverted
+		// modules may depend on the filegroup
 		attrs := &bazelFilegroupAttributes{
 			Srcs: srcs,
 		}
@@ -150,14 +166,14 @@
 type fileGroup struct {
 	ModuleBase
 	BazelModuleBase
-	Bp2buildAidlLibrary
+	FileGroupAsLibrary
 	properties fileGroupProperties
 	srcs       Paths
 }
 
 var _ MixedBuildBuildable = (*fileGroup)(nil)
 var _ SourceFileProducer = (*fileGroup)(nil)
-var _ Bp2buildAidlLibrary = (*fileGroup)(nil)
+var _ FileGroupAsLibrary = (*fileGroup)(nil)
 
 // filegroup contains a list of files that are referenced by other modules
 // properties (such as "srcs") using the syntax ":<name>". filegroup are
@@ -243,11 +259,19 @@
 }
 
 func (fg *fileGroup) ShouldConvertToAidlLibrary(ctx BazelConversionPathContext) bool {
+	return fg.shouldConvertToLibrary(ctx, ".aidl")
+}
+
+func (fg *fileGroup) ShouldConvertToProtoLibrary(ctx BazelConversionPathContext) bool {
+	return fg.shouldConvertToLibrary(ctx, ".proto")
+}
+
+func (fg *fileGroup) shouldConvertToLibrary(ctx BazelConversionPathContext, suffix string) bool {
 	if len(fg.properties.Srcs) == 0 || !fg.ShouldConvertWithBp2build(ctx) {
 		return false
 	}
 	for _, src := range fg.properties.Srcs {
-		if !strings.HasSuffix(src, ".aidl") {
+		if !strings.HasSuffix(src, suffix) {
 			return false
 		}
 	}
@@ -255,6 +279,14 @@
 }
 
 func (fg *fileGroup) GetAidlLibraryLabel(ctx BazelConversionPathContext) string {
+	return fg.getFileGroupAsLibraryLabel(ctx)
+}
+
+func (fg *fileGroup) GetProtoLibraryLabel(ctx BazelConversionPathContext) string {
+	return fg.getFileGroupAsLibraryLabel(ctx) + convertedProtoLibrarySuffix
+}
+
+func (fg *fileGroup) getFileGroupAsLibraryLabel(ctx BazelConversionPathContext) string {
 	if ctx.OtherModuleDir(fg.module) == ctx.ModuleDir() {
 		return ":" + fg.Name()
 	} else {
@@ -265,12 +297,19 @@
 // Given a name in srcs prop, check to see if the name references a filegroup
 // and the filegroup is converted to aidl_library
 func IsConvertedToAidlLibrary(ctx BazelConversionPathContext, name string) bool {
+	if fg, ok := ToFileGroupAsLibrary(ctx, name); ok {
+		return fg.ShouldConvertToAidlLibrary(ctx)
+	}
+	return false
+}
+
+func ToFileGroupAsLibrary(ctx BazelConversionPathContext, name string) (FileGroupAsLibrary, bool) {
 	if module, ok := ctx.ModuleFromName(name); ok {
 		if IsFilegroup(ctx, module) {
-			if fg, ok := module.(Bp2buildAidlLibrary); ok {
-				return fg.ShouldConvertToAidlLibrary(ctx)
+			if fg, ok := module.(FileGroupAsLibrary); ok {
+				return fg, true
 			}
 		}
 	}
-	return false
+	return nil, false
 }
diff --git a/android/gen_notice.go b/android/gen_notice.go
index e2b839f..008aac5 100644
--- a/android/gen_notice.go
+++ b/android/gen_notice.go
@@ -111,6 +111,9 @@
 }
 
 func (m *genNoticeModule) DepsMutator(ctx BottomUpMutatorContext) {
+	if ctx.ContainsProperty("licenses") {
+		ctx.PropertyErrorf("licenses", "not supported on \"gen_notice\" modules")
+	}
 	if proptools.Bool(m.properties.Html) && proptools.Bool(m.properties.Xml) {
 		ctx.ModuleErrorf("can be html or xml but not both")
 	}
@@ -179,7 +182,7 @@
 	// The visibility property needs to be checked and parsed by the visibility module.
 	setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
 
-	initAndroidModuleBase(module)
+	InitAndroidArchModule(module, DeviceSupported, MultilibCommon)
 	InitDefaultableModule(module)
 
 	return module
@@ -195,6 +198,16 @@
 	return nil, fmt.Errorf("unrecognized tag %q", tag)
 }
 
+var _ AndroidMkEntriesProvider = (*genNoticeModule)(nil)
+
+// Implements AndroidMkEntriesProvider
+func (m *genNoticeModule) AndroidMkEntries() []AndroidMkEntries {
+	return []AndroidMkEntries{AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: OptionalPathForPath(m.output),
+	}}
+}
+
 // missingReferencesRule emits an ErrorRule for missing module references.
 func missingReferencesRule(ctx BuilderContext, m *genNoticeModule) {
 	if len(m.missing) < 1 {
diff --git a/android/gen_notice_test.go b/android/gen_notice_test.go
index 4ad2ecf..99d982b 100644
--- a/android/gen_notice_test.go
+++ b/android/gen_notice_test.go
@@ -21,7 +21,7 @@
 				}`),
 		},
 		expectedErrors: []string{
-			`unrecognized property "licenses"`,
+			`not supported on "gen_notice" modules`,
 		},
 	},
 	{
diff --git a/android/proto.go b/android/proto.go
index 8ad16a6..3a5d4da 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -15,9 +15,10 @@
 package android
 
 import (
-	"android/soong/bazel"
 	"strings"
 
+	"android/soong/bazel"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -154,13 +155,21 @@
 
 // Bp2buildProtoInfo contains information necessary to pass on to language specific conversion.
 type Bp2buildProtoInfo struct {
-	Type *string
-	Name string
+	Type       *string
+	Name       string
+	Proto_libs bazel.LabelList
 }
 
-type protoAttrs struct {
+type ProtoAttrs struct {
 	Srcs                bazel.LabelListAttribute
 	Strip_import_prefix *string
+	Deps                bazel.LabelListAttribute
+}
+
+// For each package in the include_dirs property a proto_library target should
+// be added to the BUILD file in that package and a mapping should be added here
+var includeDirsToProtoDeps = map[string]string{
+	"external/protobuf/src": "//external/protobuf:libprotobuf-proto",
 }
 
 // Bp2buildProtoProperties converts proto properties, creating a proto_library and returning the
@@ -171,36 +180,70 @@
 		return info, false
 	}
 
-	info.Name = m.Name() + "_proto"
-	attrs := protoAttrs{
-		Srcs: srcs,
-	}
+	var protoLibraries bazel.LabelList
+	var directProtoSrcs bazel.LabelList
 
-	for axis, configToProps := range m.GetArchVariantProperties(ctx, &ProtoProperties{}) {
-		for _, rawProps := range configToProps {
-			var props *ProtoProperties
-			var ok bool
-			if props, ok = rawProps.(*ProtoProperties); !ok {
-				ctx.ModuleErrorf("Could not cast ProtoProperties to expected type")
-			}
-			if axis == bazel.NoConfigAxis {
-				info.Type = props.Proto.Type
-
-				if !proptools.BoolDefault(props.Proto.Canonical_path_from_root, canonicalPathFromRootDefault) {
-					// an empty string indicates to strips the package path
-					path := ""
-					attrs.Strip_import_prefix = &path
-				}
-			} else if props.Proto.Type != info.Type && props.Proto.Type != nil {
-				ctx.ModuleErrorf("Cannot handle arch-variant types for protos at this time.")
-			}
+	// For filegroups that should be converted to proto_library just collect the
+	// labels of converted proto_library targets.
+	for _, protoSrc := range srcs.Value.Includes {
+		src := protoSrc.OriginalModuleName
+		if fg, ok := ToFileGroupAsLibrary(ctx, src); ok &&
+			fg.ShouldConvertToProtoLibrary(ctx) {
+			protoLibraries.Add(&bazel.Label{
+				Label: fg.GetProtoLibraryLabel(ctx),
+			})
+		} else {
+			directProtoSrcs.Add(&protoSrc)
 		}
 	}
 
-	ctx.CreateBazelTargetModule(
-		bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
-		CommonAttributes{Name: info.Name},
-		&attrs)
+	info.Name = m.Name() + "_proto"
+
+	if len(directProtoSrcs.Includes) > 0 {
+		attrs := ProtoAttrs{
+			Srcs: bazel.MakeLabelListAttribute(directProtoSrcs),
+		}
+
+		for axis, configToProps := range m.GetArchVariantProperties(ctx, &ProtoProperties{}) {
+			for _, rawProps := range configToProps {
+				var props *ProtoProperties
+				var ok bool
+				if props, ok = rawProps.(*ProtoProperties); !ok {
+					ctx.ModuleErrorf("Could not cast ProtoProperties to expected type")
+				}
+				if axis == bazel.NoConfigAxis {
+					info.Type = props.Proto.Type
+
+					if !proptools.BoolDefault(props.Proto.Canonical_path_from_root, canonicalPathFromRootDefault) {
+						// an empty string indicates to strips the package path
+						path := ""
+						attrs.Strip_import_prefix = &path
+					}
+
+					for _, dir := range props.Proto.Include_dirs {
+						if dep, ok := includeDirsToProtoDeps[dir]; ok {
+							attrs.Deps.Add(bazel.MakeLabelAttribute(dep))
+						} else {
+							ctx.PropertyErrorf("Could not find the proto_library target for include dir", dir)
+						}
+					}
+				} else if props.Proto.Type != info.Type && props.Proto.Type != nil {
+					ctx.ModuleErrorf("Cannot handle arch-variant types for protos at this time.")
+				}
+			}
+		}
+
+		ctx.CreateBazelTargetModule(
+			bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
+			CommonAttributes{Name: info.Name},
+			&attrs)
+
+		protoLibraries.Add(&bazel.Label{
+			Label: ":" + info.Name,
+		})
+	}
+
+	info.Proto_libs = protoLibraries
 
 	return info, true
 }
diff --git a/android/testing.go b/android/testing.go
index 3708501..7b74c89 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -1037,7 +1037,7 @@
 		}
 	}
 	if !found {
-		t.Errorf("missing the expected error %q (checked %d error(s))", pattern, len(errs))
+		t.Errorf("could not match the expected error regex %q (checked %d error(s))", pattern, len(errs))
 		for i, err := range errs {
 			t.Errorf("errs[%d] = %q", i, err)
 		}
diff --git a/android/variable.go b/android/variable.go
index 7a080fe..b156051 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -592,6 +592,9 @@
 	// "acme__board__soc_a", "acme__board__soc_b", and
 	// "acme__board__conditions_default"
 	FullConfig string
+
+	// keeps track of whether this product variable is nested under an arch variant
+	OuterAxis bazel.ConfigurationAxis
 }
 
 func (p *ProductConfigProperty) AlwaysEmit() bool {
@@ -600,11 +603,11 @@
 
 func (p *ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
 	if p.Namespace == "" {
-		return bazel.ProductVariableConfigurationAxis(p.FullConfig)
+		return bazel.ProductVariableConfigurationAxis(p.FullConfig, p.OuterAxis)
 	} else {
 		// Soong config variables can be uniquely identified by the namespace
 		// (e.g. acme, android) and the product variable name (e.g. board, size)
-		return bazel.ProductVariableConfigurationAxis(p.Namespace + "__" + p.Name)
+		return bazel.ProductVariableConfigurationAxis(p.Namespace+"__"+p.Name, bazel.NoConfigAxis)
 	}
 }
 
@@ -663,9 +666,11 @@
 			moduleBase.variableProperties,
 			"",
 			"",
-			&productConfigProperties)
+			&productConfigProperties,
+			bazel.ConfigurationAxis{},
+		)
 
-		for _, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
+		for axis, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
 			for config, props := range configToProps {
 				// GetArchVariantProperties is creating an instance of the requested type
 				// and productVariablesValues expects an interface, so no need to cast
@@ -674,7 +679,8 @@
 					props,
 					"",
 					config,
-					&productConfigProperties)
+					&productConfigProperties,
+					axis)
 			}
 		}
 	}
@@ -687,7 +693,8 @@
 					namespacedVariableProp,
 					namespace,
 					"",
-					&productConfigProperties)
+					&productConfigProperties,
+					bazel.NoConfigAxis)
 			}
 		}
 	}
@@ -803,6 +810,7 @@
 					p.Name,
 					p.FullConfig,
 					zeroValue,
+					bazel.NoConfigAxis,
 				)
 			}
 		}
@@ -810,7 +818,7 @@
 }
 
 func (p *ProductConfigProperties) AddProductConfigProperty(
-	propertyName, namespace, productVariableName, config string, property interface{}) {
+	propertyName, namespace, productVariableName, config string, property interface{}, outerAxis bazel.ConfigurationAxis) {
 	if (*p)[propertyName] == nil {
 		(*p)[propertyName] = make(map[ProductConfigProperty]interface{})
 	}
@@ -819,6 +827,7 @@
 		Namespace:  namespace,           // e.g. acme, android
 		Name:       productVariableName, // e.g. size, feature1, feature2, FEATURE3, board
 		FullConfig: config,              // e.g. size, feature1-x86, size__conditions_default
+		OuterAxis:  outerAxis,
 	}
 
 	if existing, ok := (*p)[propertyName][productConfigProp]; ok && namespace != "" {
@@ -869,7 +878,7 @@
 	return v, true
 }
 
-func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(namespace, suffix string, variableValues reflect.Value) {
+func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(namespace, suffix string, variableValues reflect.Value, outerAxis bazel.ConfigurationAxis) {
 	// variableValues can either be a product_variables or
 	// soong_config_variables struct.
 	//
@@ -974,7 +983,8 @@
 						namespace,           // e.g. acme, android
 						productVariableName, // e.g. size, feature1, FEATURE2, board
 						config,
-						field.Field(k).Interface(), // e.g. ["-DDEFAULT"], ["foo", "bar"]
+						field.Field(k).Interface(), // e.g. ["-DDEFAULT"], ["foo", "bar"],
+						outerAxis,
 					)
 				}
 			} else if property.Kind() != reflect.Interface {
@@ -988,6 +998,7 @@
 					productVariableName,
 					config,
 					property.Interface(),
+					outerAxis,
 				)
 			}
 		}
@@ -998,14 +1009,14 @@
 // product_variables and soong_config_variables to structs that can be generated
 // as select statements.
 func productVariableValues(
-	fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties) {
+	fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties, outerAxis bazel.ConfigurationAxis) {
 	if suffix != "" {
 		suffix = "-" + suffix
 	}
 
 	// variableValues represent the product_variables or soong_config_variables struct.
 	variableValues := reflect.ValueOf(variableProps).Elem().FieldByName(fieldName)
-	productConfigProperties.AddProductConfigProperties(namespace, suffix, variableValues)
+	productConfigProperties.AddProductConfigProperties(namespace, suffix, variableValues, outerAxis)
 }
 
 func VariableMutator(mctx BottomUpMutatorContext) {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index d6803f6..1805291 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -8843,19 +8843,7 @@
 		android.FixtureWithRootAndroidBp(bp),
 		dexpreopt.FixtureSetApexBootJars("myapex:mybootclasspathlib"),
 		dexpreopt.FixtureSetApexSystemServerJars("myapex:mysystemserverclasspathlib"),
-		android.FixtureMergeEnv(map[string]string{
-			"EMMA_INSTRUMENT": "true",
-		}),
-		// need to mock jacocoagent here to satisfy dependency added for
-		// instrumented libraries at build time
-		android.FixtureAddFile("jacocoagent/Android.bp", []byte(`
-			java_library {
-				name: "jacocoagent",
-				srcs: ["Test.java"],
-				system_modules: "none",
-				sdk_version: "none",
-			}
-		`)),
+		java.PrepareForTestWithJacocoInstrumentation,
 	).RunTest(t)
 
 	// Make sure jacoco ran on both mylib and mybootclasspathlib
diff --git a/bazel/configurability.go b/bazel/configurability.go
index e1cdd4a..7ff202b 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -295,10 +295,11 @@
 )
 
 // ProductVariableConfigurationAxis returns an axis for the given product variable
-func ProductVariableConfigurationAxis(variable string) ConfigurationAxis {
+func ProductVariableConfigurationAxis(variable string, outerAxis ConfigurationAxis) ConfigurationAxis {
 	return ConfigurationAxis{
 		configurationType: productVariables,
 		subType:           variable,
+		outerAxisType:     outerAxis.configurationType,
 	}
 }
 
@@ -309,6 +310,8 @@
 	// some configuration types (e.g. productVariables) have multiple independent axes, subType helps
 	// distinguish between them without needing to list all 17 product variables.
 	subType string
+	// used to keep track of which product variables are arch variant
+	outerAxisType configurationType
 }
 
 func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 5cee6ac..cf8e9f7 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -235,14 +235,14 @@
 //   - The function body should not be indented outside of its own scope.
 func (g getApexInfoType) StarlarkFunctionBody() string {
 	return `info = providers(target)["//build/bazel/rules/apex:apex.bzl%ApexInfo"]
-return "{%s}" % ",".join([
-    json_for_file("signed_output", info.signed_output),
-    json_for_file("unsigned_output", info.unsigned_output),
-    json_for_labels("provides_native_libs", info.provides_native_libs),
-    json_for_labels("requires_native_libs", info.requires_native_libs),
-    json_for_files("bundle_key_pair", info.bundle_key_pair),
-    json_for_files("container_key_pair", info.container_key_pair)
-    ])`
+return json_encode({
+    "signed_output": info.signed_output.path,
+    "unsigned_output": info.unsigned_output.path,
+    "provides_native_libs": [str(lib) for lib in info.provides_native_libs],
+    "requires_native_libs": [str(lib) for lib in info.requires_native_libs],
+    "bundle_key_pair": [f.path for f in info.bundle_key_pair],
+    "container_key_pair": [f.path for f in info.container_key_pair]
+})`
 }
 
 type ApexCqueryInfo struct {
@@ -259,7 +259,9 @@
 // Starlark given in StarlarkFunctionBody.
 func (g getApexInfoType) ParseResult(rawString string) ApexCqueryInfo {
 	var info ApexCqueryInfo
-	if err := json.Unmarshal([]byte(rawString), &info); err != nil {
+	decoder := json.NewDecoder(strings.NewReader(rawString))
+	decoder.DisallowUnknownFields() //useful to detect typos, e.g. in unit tests
+	if err := decoder.Decode(&info); err != nil {
 		panic(fmt.Errorf("cannot parse cquery result '%s': %s", rawString, err))
 	}
 	return info
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 34248ce..46eb0b6 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -161,7 +161,7 @@
 		actualOutput, err := GetCcInfo.ParseResult(tc.input)
 		if (err == nil && tc.expectedErrorMessage != "") ||
 			(err != nil && err.Error() != tc.expectedErrorMessage) {
-			t.Errorf("%q:\nexpected Error %s\n, got %s", tc.description, tc.expectedErrorMessage, err)
+			t.Errorf("%q:\n%12s: %q\n%12s: %q", tc.description, "expect Error", tc.expectedErrorMessage, "but got", err)
 		} else if err == nil && !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
 			t.Errorf("%q:\n expected %#v\n!= actual %#v", tc.description, tc.expectedOutput, actualOutput)
 		}
diff --git a/bazel/properties.go b/bazel/properties.go
index d82fa64..11f6247 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -309,7 +309,19 @@
 	_, containsProductVariables := axisTypes[productVariables]
 	if containsProductVariables {
 		if containsOs || containsArch || containsOsArch {
-			return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
+			if containsArch {
+				allProductVariablesAreArchVariant := true
+				for k := range la.ConfigurableValues {
+					if k.configurationType == productVariables && k.outerAxisType != arch {
+						allProductVariablesAreArchVariant = false
+					}
+				}
+				if !allProductVariablesAreArchVariant {
+					return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
+				}
+			} else {
+				return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
+			}
 		}
 	}
 	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index dc4d5b0..8729381 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -247,13 +247,13 @@
 			OsArchConfigurationAxis: labelListSelectValues{
 				"linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}),
 			},
-			ProductVariableConfigurationAxis("product_with_defaults"): labelListSelectValues{
+			ProductVariableConfigurationAxis("product_with_defaults", NoConfigAxis): labelListSelectValues{
 				"a":                        makeLabelList([]string{}, []string{"not_in_value"}),
 				"b":                        makeLabelList([]string{"b_val"}, []string{}),
 				"c":                        makeLabelList([]string{"c_val"}, []string{}),
 				ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2"}, []string{}),
 			},
-			ProductVariableConfigurationAxis("product_only_with_excludes"): labelListSelectValues{
+			ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): labelListSelectValues{
 				"a": makeLabelList([]string{}, []string{"not_in_value"}),
 			},
 		},
@@ -281,7 +281,7 @@
 			"linux_x86":                makeLabels("linux_x86_include"),
 			ConditionsDefaultConfigKey: nilLabels,
 		},
-		ProductVariableConfigurationAxis("product_with_defaults"): {
+		ProductVariableConfigurationAxis("product_with_defaults", NoConfigAxis): {
 			"a":                        nilLabels,
 			"b":                        makeLabels("b_val"),
 			"c":                        makeLabels("c_val"),
@@ -674,7 +674,7 @@
 			OsArchConfigurationAxis: stringListSelectValues{
 				"linux_x86": {"linux_x86_include"},
 			},
-			ProductVariableConfigurationAxis("a"): stringListSelectValues{
+			ProductVariableConfigurationAxis("a", NoConfigAxis): stringListSelectValues{
 				"a": []string{"not_in_value"},
 			},
 		},
@@ -699,7 +699,7 @@
 			"linux": []string{"linux_include"},
 		},
 		OsArchConfigurationAxis: stringListSelectValues{},
-		ProductVariableConfigurationAxis("a"): stringListSelectValues{
+		ProductVariableConfigurationAxis("a", NoConfigAxis): stringListSelectValues{
 			"a": []string{"not_in_value"},
 		},
 	}
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index cf46533..ca8185e 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -289,7 +289,9 @@
 						return
 					}
 				}
-				targets = generateBazelTargets(bpCtx, aModule)
+				var targetErrs []error
+				targets, targetErrs = generateBazelTargets(bpCtx, aModule)
+				errs = append(errs, targetErrs...)
 				for _, t := range targets {
 					// A module can potentially generate more than 1 Bazel
 					// target, each of a different rule class.
@@ -306,7 +308,10 @@
 				// be mapped cleanly to a bazel label.
 				return
 			}
-			t := generateSoongModuleTarget(bpCtx, m)
+			t, err := generateSoongModuleTarget(bpCtx, m)
+			if err != nil {
+				errs = append(errs, err)
+			}
 			targets = append(targets, t)
 		default:
 			errs = append(errs, fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
@@ -347,12 +352,18 @@
 	}, errs
 }
 
-func generateBazelTargets(ctx bpToBuildContext, m android.Module) []BazelTarget {
+func generateBazelTargets(ctx bpToBuildContext, m android.Module) ([]BazelTarget, []error) {
 	var targets []BazelTarget
+	var errs []error
 	for _, m := range m.Bp2buildTargets() {
-		targets = append(targets, generateBazelTarget(ctx, m))
+		target, err := generateBazelTarget(ctx, m)
+		if err != nil {
+			errs = append(errs, err)
+			return targets, errs
+		}
+		targets = append(targets, target)
 	}
-	return targets
+	return targets, errs
 }
 
 type bp2buildModule interface {
@@ -363,13 +374,16 @@
 	BazelAttributes() []interface{}
 }
 
-func generateBazelTarget(ctx bpToBuildContext, m bp2buildModule) BazelTarget {
+func generateBazelTarget(ctx bpToBuildContext, m bp2buildModule) (BazelTarget, error) {
 	ruleClass := m.BazelRuleClass()
 	bzlLoadLocation := m.BazelRuleLoadLocation()
 
 	// extract the bazel attributes from the module.
 	attrs := m.BazelAttributes()
-	props := extractModuleProperties(attrs, true)
+	props, err := extractModuleProperties(attrs, true)
+	if err != nil {
+		return BazelTarget{}, err
+	}
 
 	// name is handled in a special manner
 	delete(props.Attrs, "name")
@@ -389,13 +403,13 @@
 			targetName,
 			attributes,
 		),
-	}
+	}, nil
 }
 
 // Convert a module and its deps and props into a Bazel macro/rule
 // representation in the BUILD file.
-func generateSoongModuleTarget(ctx bpToBuildContext, m blueprint.Module) BazelTarget {
-	props := getBuildProperties(ctx, m)
+func generateSoongModuleTarget(ctx bpToBuildContext, m blueprint.Module) (BazelTarget, error) {
+	props, err := getBuildProperties(ctx, m)
 
 	// TODO(b/163018919): DirectDeps can have duplicate (module, variant)
 	// items, if the modules are added using different DependencyTag. Figure
@@ -429,21 +443,21 @@
 			ctx.ModuleSubDir(m),
 			depLabelList,
 			attributes),
-	}
+	}, err
 }
 
-func getBuildProperties(ctx bpToBuildContext, m blueprint.Module) BazelAttributes {
+func getBuildProperties(ctx bpToBuildContext, m blueprint.Module) (BazelAttributes, error) {
 	// TODO: this omits properties for blueprint modules (blueprint_go_binary,
 	// bootstrap_go_binary, bootstrap_go_package), which will have to be handled separately.
 	if aModule, ok := m.(android.Module); ok {
 		return extractModuleProperties(aModule.GetProperties(), false)
 	}
 
-	return BazelAttributes{}
+	return BazelAttributes{}, nil
 }
 
 // Generically extract module properties and types into a map, keyed by the module property name.
-func extractModuleProperties(props []interface{}, checkForDuplicateProperties bool) BazelAttributes {
+func extractModuleProperties(props []interface{}, checkForDuplicateProperties bool) (BazelAttributes, error) {
 	ret := map[string]string{}
 
 	// Iterate over this android.Module's property structs.
@@ -456,24 +470,29 @@
 		// manipulate internal props, if needed.
 		if isStructPtr(propertiesValue.Type()) {
 			structValue := propertiesValue.Elem()
-			for k, v := range extractStructProperties(structValue, 0) {
+			ok, err := extractStructProperties(structValue, 0)
+			if err != nil {
+				return BazelAttributes{}, err
+			}
+			for k, v := range ok {
 				if existing, exists := ret[k]; checkForDuplicateProperties && exists {
-					panic(fmt.Errorf(
+					return BazelAttributes{}, fmt.Errorf(
 						"%s (%v) is present in properties whereas it should be consolidated into a commonAttributes",
-						k, existing))
+						k, existing)
 				}
 				ret[k] = v
 			}
 		} else {
-			panic(fmt.Errorf(
-				"properties must be a pointer to a struct, got %T",
-				propertiesValue.Interface()))
+			return BazelAttributes{},
+				fmt.Errorf(
+					"properties must be a pointer to a struct, got %T",
+					propertiesValue.Interface())
 		}
 	}
 
 	return BazelAttributes{
 		Attrs: ret,
-	}
+	}, nil
 }
 
 func isStructPtr(t reflect.Type) bool {
@@ -531,7 +550,12 @@
 		}
 
 		// Sort and print the struct props by the key.
-		structProps := extractStructProperties(propertyValue, indent)
+		structProps, err := extractStructProperties(propertyValue, indent)
+
+		if err != nil {
+			return "", err
+		}
+
 		if len(structProps) == 0 {
 			return "", nil
 		}
@@ -550,11 +574,13 @@
 // which each property value correctly pretty-printed and indented at the right nest level,
 // since property structs can be nested. In Starlark, nested structs are represented as nested
 // dicts: https://docs.bazel.build/skylark/lib/dict.html
-func extractStructProperties(structValue reflect.Value, indent int) map[string]string {
+func extractStructProperties(structValue reflect.Value, indent int) (map[string]string, error) {
 	if structValue.Kind() != reflect.Struct {
-		panic(fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind()))
+		return map[string]string{}, fmt.Errorf("Expected a reflect.Struct type, but got %s", structValue.Kind())
 	}
 
+	var err error
+
 	ret := map[string]string{}
 	structType := structValue.Type()
 	for i := 0; i < structValue.NumField(); i++ {
@@ -575,7 +601,10 @@
 				fieldValue = fieldValue.Elem()
 			}
 			if fieldValue.Type().Kind() == reflect.Struct {
-				propsToMerge := extractStructProperties(fieldValue, indent)
+				propsToMerge, err := extractStructProperties(fieldValue, indent)
+				if err != nil {
+					return map[string]string{}, err
+				}
 				for prop, value := range propsToMerge {
 					ret[prop] = value
 				}
@@ -584,20 +613,20 @@
 		}
 
 		propertyName := proptools.PropertyNameForField(field.Name)
-		prettyPrintedValue, err := prettyPrint(fieldValue, indent+1, false)
+		var prettyPrintedValue string
+		prettyPrintedValue, err = prettyPrint(fieldValue, indent+1, false)
 		if err != nil {
-			panic(
-				fmt.Errorf(
-					"Error while parsing property: %q. %s",
-					propertyName,
-					err))
+			return map[string]string{}, fmt.Errorf(
+				"Error while parsing property: %q. %s",
+				propertyName,
+				err)
 		}
 		if prettyPrintedValue != "" {
 			ret[propertyName] = prettyPrintedValue
 		}
 	}
 
-	return ret
+	return ret, nil
 }
 
 func isZero(value reflect.Value) bool {
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 5117c62..ba7af09 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -2217,6 +2217,174 @@
 	})
 }
 
+func TestCcLibraryProtoIncludeDirs(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	srcs: ["foo.proto"],
+	proto: {
+		include_dirs: ["external/protobuf/src"],
+	},
+	include_build_directory: false,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("proto_library", "foo_proto", AttrNameToString{
+				"srcs": `["foo.proto"]`,
+				"deps": `["//external/protobuf:libprotobuf-proto"]`,
+			}), MakeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", AttrNameToString{
+				"deps": `[":foo_proto"]`,
+			}), MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"deps":                              `[":libprotobuf-cpp-lite"]`,
+				"implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
+			}), MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryProtoIncludeDirsUnknown(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	srcs: ["foo.proto"],
+	proto: {
+		include_dirs: ["external/protobuf/abc"],
+	},
+	include_build_directory: false,
+}`,
+		ExpectedErr: fmt.Errorf("module \"foo\": Could not find the proto_library target for include dir: external/protobuf/abc"),
+	})
+}
+
+func TestCcLibraryConvertedProtoFilegroups(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: soongCcProtoPreamble + `
+filegroup {
+	name: "a_fg_proto",
+	srcs: ["a_fg.proto"],
+}
+
+cc_library {
+	name: "a",
+	srcs: [
+    ":a_fg_proto",
+    "a.proto",
+  ],
+	proto: {
+		export_proto_headers: true,
+	},
+	include_build_directory: false,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("proto_library", "a_proto", AttrNameToString{
+				"srcs": `["a.proto"]`,
+			}), MakeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", AttrNameToString{
+				"deps": `[
+        ":a_fg_proto_bp2build_converted",
+        ":a_proto",
+    ]`,
+			}), MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
+				"deps":               `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":a_cc_proto_lite"]`,
+			}), MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
+				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":a_cc_proto_lite"]`,
+			}), MakeBazelTargetNoRestrictions("proto_library", "a_fg_proto_bp2build_converted", AttrNameToString{
+				"srcs": `["a_fg.proto"]`,
+			}), MakeBazelTargetNoRestrictions("filegroup", "a_fg_proto", AttrNameToString{
+				"srcs": `["a_fg.proto"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryConvertedProtoFilegroupsNoProtoFiles(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: soongCcProtoPreamble + `
+filegroup {
+	name: "a_fg_proto",
+	srcs: ["a_fg.proto"],
+}
+
+cc_library {
+	name: "a",
+	srcs: [
+    ":a_fg_proto",
+  ],
+	proto: {
+		export_proto_headers: true,
+	},
+	include_build_directory: false,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", AttrNameToString{
+				"deps": `[":a_fg_proto_bp2build_converted"]`,
+			}), MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
+				"deps":               `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":a_cc_proto_lite"]`,
+			}), MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
+				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":a_cc_proto_lite"]`,
+			}), MakeBazelTargetNoRestrictions("proto_library", "a_fg_proto_bp2build_converted", AttrNameToString{
+				"srcs": `["a_fg.proto"]`,
+			}), MakeBazelTargetNoRestrictions("filegroup", "a_fg_proto", AttrNameToString{
+				"srcs": `["a_fg.proto"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryExternalConvertedProtoFilegroups(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Filesystem: map[string]string{
+			"path/to/A/Android.bp": `
+filegroup {
+	name: "a_fg_proto",
+	srcs: ["a_fg.proto"],
+}`,
+		},
+		Blueprint: soongCcProtoPreamble + `
+cc_library {
+	name: "a",
+	srcs: [
+    ":a_fg_proto",
+    "a.proto",
+  ],
+	proto: {
+		export_proto_headers: true,
+	},
+	include_build_directory: false,
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("proto_library", "a_proto", AttrNameToString{
+				"srcs": `["a.proto"]`,
+			}), MakeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", AttrNameToString{
+				"deps": `[
+        "//path/to/A:a_fg_proto_bp2build_converted",
+        ":a_proto",
+    ]`,
+			}), MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
+				"deps":               `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":a_cc_proto_lite"]`,
+			}), MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
+				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":a_cc_proto_lite"]`,
+			}),
+		},
+	})
+}
+
 func TestCcLibraryProtoFilegroups(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		ModuleTypeUnderTest:        "cc_library",
@@ -2745,12 +2913,13 @@
     ]`,
 			}),
 			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
-				"whole_archive_deps": `[":foo_cc_aidl_library"]`,
-				"local_includes":     `["."]`,
+				"implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
+				"local_includes":                    `["."]`,
 			}),
+			// TODO(b/239311679) Add implementation_whole_archive_deps to cc_library_shared
+			// for bp2build to be fully correct. This fallback is affecting proto as well.
 			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
-				"whole_archive_deps": `[":foo_cc_aidl_library"]`,
-				"local_includes":     `["."]`,
+				"local_includes": `["."]`,
 			}),
 		},
 	})
@@ -2764,23 +2933,58 @@
 		Filesystem: map[string]string{
 			"path/to/A/Android.bp": `
 filegroup {
-	name: "A_aidl",
-	srcs: ["aidl/A.aidl"],
-	path: "aidl",
+    name: "A_aidl",
+    srcs: ["aidl/A.aidl"],
+    path: "aidl",
 }`,
 		},
 		Blueprint: `
 cc_library {
-	name: "foo",
-	srcs: [
-		":A_aidl",
-	],
+    name: "foo",
+    srcs: [
+        ":A_aidl",
+    ],
 }`,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
 				"deps": `["//path/to/A:A_aidl"]`,
 			}),
 			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
+				"local_includes":                    `["."]`,
+			}),
+			// TODO(b/239311679) Add implementation_whole_archive_deps to cc_library_shared
+			// for bp2build to be fully correct. This fallback is affecting proto as well.
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryWithExportAidlHeaders(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library with export aidl headers",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+    name: "foo",
+    srcs: [
+        "Foo.aidl",
+    ],
+    aidl: {
+        export_aidl_headers: true,
+    }
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{
+				"srcs": `["Foo.aidl"]`,
+			}),
+			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+				"deps": `[":foo_aidl_library"]`,
+			}),
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
 				"whole_archive_deps": `[":foo_cc_aidl_library"]`,
 				"local_includes":     `["."]`,
 			}),
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 0637ba2..316fa3e 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -1392,6 +1392,16 @@
 		Description: "cc_library_static system_shared_lib empty for linux_bionic variant",
 		Blueprint: soongCcLibraryStaticPreamble +
 			simpleModuleDoNotConvertBp2build("cc_library", "libc") + `
+
+cc_library {
+    name: "libm",
+    stubs: {
+        symbol_file: "libm.map.txt",
+        versions: ["current"],
+    },
+    bazel_module: { bp2build_available: false },
+}
+
 cc_library_static {
     name: "used_in_bionic_oses",
     target: {
@@ -1414,7 +1424,20 @@
 cc_library_static {
     name: "keep_for_empty_system_shared_libs",
     shared_libs: ["libc"],
-		system_shared_libs: [],
+    system_shared_libs: [],
+    include_build_directory: false,
+}
+
+cc_library_static {
+    name: "used_with_stubs",
+    shared_libs: ["libm"],
+    include_build_directory: false,
+}
+
+cc_library_static {
+    name: "keep_with_stubs",
+    shared_libs: ["libm"],
+    system_shared_libs: [],
     include_build_directory: false,
 }
 `,
@@ -1424,7 +1447,15 @@
 				"implementation_dynamic_deps": `[":libc"]`,
 				"system_dynamic_deps":         `[]`,
 			}),
+			MakeBazelTarget("cc_library_static", "keep_with_stubs", AttrNameToString{
+				"implementation_dynamic_deps": `select({
+        "//build/bazel/rules/apex:android-in_apex": [":libm_stub_libs_current"],
+        "//conditions:default": [":libm"],
+    })`,
+				"system_dynamic_deps": `[]`,
+			}),
 			MakeBazelTarget("cc_library_static", "used_in_bionic_oses", AttrNameToString{}),
+			MakeBazelTarget("cc_library_static", "used_with_stubs", AttrNameToString{}),
 		},
 	})
 }
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 6fb2823..731b17e 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -136,16 +136,17 @@
 var (
 	// Certain module property names are blocklisted/ignored here, for the reasons commented.
 	ignoredPropNames = map[string]bool{
-		"name":       true, // redundant, since this is explicitly generated for every target
-		"from":       true, // reserved keyword
-		"in":         true, // reserved keyword
-		"size":       true, // reserved for tests
-		"arch":       true, // interface prop type is not supported yet.
-		"multilib":   true, // interface prop type is not supported yet.
-		"target":     true, // interface prop type is not supported yet.
-		"visibility": true, // Bazel has native visibility semantics. Handle later.
-		"features":   true, // There is already a built-in attribute 'features' which cannot be overridden.
-		"for":        true, // reserved keyword, b/233579439
+		"name":               true, // redundant, since this is explicitly generated for every target
+		"from":               true, // reserved keyword
+		"in":                 true, // reserved keyword
+		"size":               true, // reserved for tests
+		"arch":               true, // interface prop type is not supported yet.
+		"multilib":           true, // interface prop type is not supported yet.
+		"target":             true, // interface prop type is not supported yet.
+		"visibility":         true, // Bazel has native visibility semantics. Handle later.
+		"features":           true, // There is already a built-in attribute 'features' which cannot be overridden.
+		"for":                true, // reserved keyword, b/233579439
+		"versions_with_info": true, // TODO(b/245730552) struct properties not fully supported
 	}
 )
 
diff --git a/bp2build/filegroup_conversion_test.go b/bp2build/filegroup_conversion_test.go
index de09a17..d84d4b3 100644
--- a/bp2build/filegroup_conversion_test.go
+++ b/bp2build/filegroup_conversion_test.go
@@ -15,10 +15,10 @@
 package bp2build
 
 import (
-	"android/soong/android"
 	"fmt"
-
 	"testing"
+
+	"android/soong/android"
 )
 
 func runFilegroupTestCase(t *testing.T, tc Bp2buildTestCase) {
@@ -121,3 +121,43 @@
     ]`}),
 		}})
 }
+
+func TestFilegroupWithProtoSrcs(t *testing.T) {
+	runFilegroupTestCase(t, Bp2buildTestCase{
+		Description: "filegroup with proto and non-proto srcs",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+filegroup {
+		name: "foo",
+		srcs: ["proto/foo.proto"],
+		path: "proto",
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTargetNoRestrictions("proto_library", "foo_bp2build_converted", AttrNameToString{
+				"srcs":                `["proto/foo.proto"]`,
+				"strip_import_prefix": `"proto"`}),
+			MakeBazelTargetNoRestrictions("filegroup", "foo", AttrNameToString{
+				"srcs": `["proto/foo.proto"]`}),
+		}})
+}
+
+func TestFilegroupWithProtoAndNonProtoSrcs(t *testing.T) {
+	runFilegroupTestCase(t, Bp2buildTestCase{
+		Description: "filegroup with proto and non-proto srcs",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+filegroup {
+    name: "foo",
+    srcs: [
+		"foo.proto",
+		"buf.cpp",
+	],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTargetNoRestrictions("filegroup", "foo", AttrNameToString{
+				"srcs": `[
+        "foo.proto",
+        "buf.cpp",
+    ]`}),
+		}})
+}
diff --git a/bp2build/prebuilt_etc_conversion_test.go b/bp2build/prebuilt_etc_conversion_test.go
index b1e70dc..aa0a5b7 100644
--- a/bp2build/prebuilt_etc_conversion_test.go
+++ b/bp2build/prebuilt_etc_conversion_test.go
@@ -15,10 +15,11 @@
 package bp2build
 
 import (
+	"fmt"
+	"testing"
+
 	"android/soong/android"
 	"android/soong/etc"
-
-	"testing"
 )
 
 func runPrebuiltEtcTestCase(t *testing.T, tc Bp2buildTestCase) {
@@ -128,6 +129,32 @@
 				"dir": `"etc/tz"`,
 			})}})
 }
+func TestPrebuiltEtcProductVariables(t *testing.T) {
+	runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+		Description: "prebuilt etc - product variables",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc {
+    name: "apex_tz_version",
+    src: "version/tz_version",
+    filename: "tz_version",
+    product_variables: {
+      native_coverage: {
+        src: "src1",
+      },
+    },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("prebuilt_file", "apex_tz_version", AttrNameToString{
+				"filename": `"tz_version"`,
+				"src": `select({
+        "//build/bazel/product_variables:native_coverage": "src1",
+        "//conditions:default": "version/tz_version",
+    })`,
+				"dir": `"etc"`,
+			})}})
+}
 
 func runPrebuiltUsrShareTestCase(t *testing.T, tc Bp2buildTestCase) {
 	t.Helper()
@@ -265,3 +292,57 @@
 				"dir":      `"etc"`,
 			})}})
 }
+
+func TestPrebuiltEtcProductVariableArchSrcs(t *testing.T) {
+	runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+		Description: "prebuilt etc- SRcs from arch variant product variables",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc {
+    name: "foo",
+    filename: "fooFilename",
+    arch: {
+      arm: {
+        src: "armSrc",
+        product_variables: {
+          native_coverage: {
+            src: "nativeCoverageArmSrc",
+          },
+        },
+      },
+    },
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("prebuilt_file", "foo", AttrNameToString{
+				"filename": `"fooFilename"`,
+				"dir":      `"etc"`,
+				"src": `select({
+        "//build/bazel/platforms/arch:arm": "armSrc",
+        "//build/bazel/product_variables:native_coverage-arm": "nativeCoverageArmSrc",
+        "//conditions:default": None,
+    })`,
+			})}})
+}
+
+func TestPrebuiltEtcProductVariableError(t *testing.T) {
+	runPrebuiltEtcTestCase(t, Bp2buildTestCase{
+		Description: "",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+prebuilt_etc {
+    name: "foo",
+    filename: "fooFilename",
+    arch: {
+      arm: {
+        src: "armSrc",
+      },
+    },
+    product_variables: {
+      native_coverage: {
+        src: "nativeCoverageArmSrc",
+      },
+    },
+}`,
+		ExpectedErr: fmt.Errorf("label attribute could not be collapsed"),
+	})
+}
diff --git a/bp2build/testing.go b/bp2build/testing.go
index a0d512f..ac1268c 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -384,6 +384,9 @@
 func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) (BazelTargets, []error) {
 	// TODO: Set generateFilegroups to true and/or remove the generateFilegroups argument completely
 	res, err := GenerateBazelTargets(codegenCtx, false)
+	if err != nil {
+		return BazelTargets{}, err
+	}
 	return res.buildFileToTargets[dir], err
 }
 
diff --git a/cc/bp2build.go b/cc/bp2build.go
index c1a3bef..51d8e22 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -36,6 +36,8 @@
 	cppSrcPartition   = "cpp"
 	protoSrcPartition = "proto"
 	aidlSrcPartition  = "aidl"
+
+	stubsSuffix = "_stub_libs_current"
 )
 
 // staticOrSharedAttributes are the Bazel-ified versions of StaticOrSharedProperties --
@@ -77,10 +79,10 @@
 			if !exists || !android.IsFilegroup(otherModuleCtx, m) {
 				return labelStr, false
 			}
-			// If the filegroup is already converted to aidl_library, skip creating
-			// _c_srcs, _as_srcs, _cpp_srcs filegroups
-			fg, _ := m.(android.Bp2buildAidlLibrary)
-			if fg.ShouldConvertToAidlLibrary(ctx) {
+			// If the filegroup is already converted to aidl_library or proto_library,
+			// skip creating _c_srcs, _as_srcs, _cpp_srcs filegroups
+			fg, _ := m.(android.FileGroupAsLibrary)
+			if fg.ShouldConvertToAidlLibrary(ctx) || fg.ShouldConvertToProtoLibrary(ctx) {
 				return labelStr, false
 			}
 			return labelStr + suffix, true
@@ -504,19 +506,6 @@
 	return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedSrcsLabelList), anySrcs
 }
 
-// Given a name in srcs prop, check to see if the name references a filegroup
-// and the filegroup is converted to aidl_library
-func isConvertedToAidlLibrary(ctx android.BazelConversionPathContext, name string) bool {
-	if module, ok := ctx.ModuleFromName(name); ok {
-		if android.IsFilegroup(ctx, module) {
-			if fg, ok := module.(android.Bp2buildAidlLibrary); ok {
-				return fg.ShouldConvertToAidlLibrary(ctx)
-			}
-		}
-	}
-	return false
-}
-
 func bp2buildStdVal(std *string, prefix string, useGnu bool) *string {
 	defaultVal := prefix + "_std_default"
 	// If c{,pp}std properties are not specified, don't generate them in the BUILD file.
@@ -723,15 +712,23 @@
 	(&compilerAttrs.srcs).Add(bp2BuildYasm(ctx, module, compilerAttrs))
 
 	protoDep := bp2buildProto(ctx, module, compilerAttrs.protoSrcs)
-	aidlDep := bp2buildCcAidlLibrary(ctx, module, compilerAttrs.aidlSrcs)
 
 	// bp2buildProto will only set wholeStaticLib or implementationWholeStaticLib, but we don't know
 	// which. This will add the newly generated proto library to the appropriate attribute and nothing
 	// to the other
 	(&linkerAttrs).wholeArchiveDeps.Add(protoDep.wholeStaticLib)
 	(&linkerAttrs).implementationWholeArchiveDeps.Add(protoDep.implementationWholeStaticLib)
-	// TODO(b/243023967) Add aidlDep to implementationWholeArchiveDeps if aidl.export_aidl_headers is true
-	(&linkerAttrs).wholeArchiveDeps.Add(aidlDep)
+
+	aidlDep := bp2buildCcAidlLibrary(ctx, module, compilerAttrs.aidlSrcs)
+	if aidlDep != nil {
+		if lib, ok := module.linker.(*libraryDecorator); ok {
+			if proptools.Bool(lib.Properties.Aidl.Export_aidl_headers) {
+				(&linkerAttrs).wholeArchiveDeps.Add(aidlDep)
+			} else {
+				(&linkerAttrs).implementationWholeArchiveDeps.Add(aidlDep)
+			}
+		}
+	}
 
 	convertedLSrcs := bp2BuildLex(ctx, module.Name(), compilerAttrs)
 	(&compilerAttrs).srcs.Add(&convertedLSrcs.srcName)
@@ -760,9 +757,8 @@
 	// Make a list of labels that correspond to filegroups that are already converted to aidl_library
 	for _, aidlSrc := range aidlSrcs.Value.Includes {
 		src := aidlSrc.OriginalModuleName
-		if isConvertedToAidlLibrary(ctx, src) {
-			module, _ := ctx.ModuleFromName(src)
-			fg, _ := module.(android.Bp2buildAidlLibrary)
+		if fg, ok := android.ToFileGroupAsLibrary(ctx, src); ok &&
+			fg.ShouldConvertToAidlLibrary(ctx) {
 			aidlLibraries.Add(&bazel.Label{
 				Label: fg.GetAidlLibraryLabel(ctx),
 			})
@@ -927,7 +923,7 @@
 
 			stubLibLabels := []bazel.Label{}
 			for _, l := range depsWithStubs {
-				l.Label = l.Label + "_stub_libs_current"
+				l.Label = l.Label + stubsSuffix
 				stubLibLabels = append(stubLibLabels, l)
 			}
 			inApexSelectValue := la.implementationDynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex)
@@ -1088,12 +1084,20 @@
 	if la.systemDynamicDeps.IsNil() && len(la.usedSystemDynamicDepAsDynamicDep) > 0 {
 		toRemove := bazelLabelForSharedDeps(ctx, android.SortedStringKeys(la.usedSystemDynamicDepAsDynamicDep))
 		la.dynamicDeps.Exclude(bazel.NoConfigAxis, "", toRemove)
-		la.implementationDynamicDeps.Exclude(bazel.NoConfigAxis, "", toRemove)
-		la.implementationDynamicDeps.Exclude(bazel.NoConfigAxis, "", toRemove)
 		la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
 		la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
+		la.implementationDynamicDeps.Exclude(bazel.NoConfigAxis, "", toRemove)
 		la.implementationDynamicDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
 		la.implementationDynamicDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
+
+		la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey, toRemove)
+		la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, toRemove)
+		stubsToRemove := make([]bazel.Label, 0, len(la.usedSystemDynamicDepAsDynamicDep))
+		for _, lib := range toRemove.Includes {
+			lib.Label += stubsSuffix
+			stubsToRemove = append(stubsToRemove, lib)
+		}
+		la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.MakeLabelList(stubsToRemove))
 	}
 
 	la.deps.ResolveExcludes()
diff --git a/cc/compiler.go b/cc/compiler.go
index 3c904b8..f9f7b6f 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -565,7 +565,7 @@
 			flags.aidlFlags = append(flags.aidlFlags, includeDirsToFlags(rootAidlIncludeDirs))
 		}
 
-		if Bool(compiler.Properties.Aidl.Generate_traces) {
+		if proptools.BoolDefault(compiler.Properties.Aidl.Generate_traces, true) {
 			flags.aidlFlags = append(flags.aidlFlags, "-t")
 		}
 
diff --git a/cc/config/global.go b/cc/config/global.go
index 357ea44..f920471 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -290,7 +290,12 @@
 		"-Wno-deprecated-non-prototype",
 	}
 
-	llvmNextExtraCommonGlobalCflags = []string{}
+	llvmNextExtraCommonGlobalCflags = []string{
+		// New warnings to be fixed after clang-r468909
+		"-Wno-error=array-parameter",     // http://b/241941550
+		"-Wno-error=deprecated-builtins", // http://b/241601211
+		"-Wno-error=deprecated",          // in external/googletest/googletest
+	}
 
 	IllegalFlags = []string{
 		"-w",
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 1b126de..07b95e1 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -109,7 +109,7 @@
 	}, "-l")
 
 	muslCrtBeginStaticBinary, muslCrtEndStaticBinary   = []string{"libc_musl_crtbegin_static"}, []string{"libc_musl_crtend"}
-	muslCrtBeginSharedBinary, muslCrtEndSharedBinary   = []string{"libc_musl_crtbegin_dynamic", "musl_linker_script"}, []string{"libc_musl_crtend"}
+	muslCrtBeginSharedBinary, muslCrtEndSharedBinary   = []string{"libc_musl_crtbegin_dynamic"}, []string{"libc_musl_crtend"}
 	muslCrtBeginSharedLibrary, muslCrtEndSharedLibrary = []string{"libc_musl_crtbegin_so"}, []string{"libc_musl_crtend_so"}
 
 	muslDefaultSharedLibraries = []string{"libc_musl"}
diff --git a/cc/lto.go b/cc/lto.go
index 5d2fba0..b3e5e74 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -101,6 +101,7 @@
 		}
 
 		flags.Local.CFlags = append(flags.Local.CFlags, ltoCFlag)
+		flags.Local.AsFlags = append(flags.Local.AsFlags, ltoCFlag)
 		flags.Local.LdFlags = append(flags.Local.LdFlags, ltoCFlag)
 		flags.Local.LdFlags = append(flags.Local.LdFlags, ltoLdFlag)
 
diff --git a/cc/object.go b/cc/object.go
index 65a11e0..20eef30 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -256,13 +256,18 @@
 	builderFlags := flagsToBuilderFlags(flags)
 
 	if len(objs.objFiles) == 1 && String(object.Properties.Linker_script) == "" {
-		outputFile = objs.objFiles[0]
+		output := android.PathForModuleOut(ctx, ctx.ModuleName()+objectExtension)
+		outputFile = output
 
 		if String(object.Properties.Prefix_symbols) != "" {
-			output := android.PathForModuleOut(ctx, ctx.ModuleName()+objectExtension)
-			transformBinaryPrefixSymbols(ctx, String(object.Properties.Prefix_symbols), outputFile,
+			transformBinaryPrefixSymbols(ctx, String(object.Properties.Prefix_symbols), objs.objFiles[0],
 				builderFlags, output)
-			outputFile = output
+		} else {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.Cp,
+				Input:  objs.objFiles[0],
+				Output: output,
+			})
 		}
 	} else {
 		output := android.PathForModuleOut(ctx, ctx.ModuleName()+objectExtension)
diff --git a/cc/object_test.go b/cc/object_test.go
index 259a892..00f83d4 100644
--- a/cc/object_test.go
+++ b/cc/object_test.go
@@ -15,6 +15,7 @@
 package cc
 
 import (
+	"fmt"
 	"testing"
 
 	"android/soong/android"
@@ -107,3 +108,53 @@
 	expectedOutputFiles := []string{"outputbase/execroot/__main__/bazel_out.o"}
 	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
 }
+
+func TestCcObjectOutputFile(t *testing.T) {
+	testcases := []struct {
+		name string
+		bp   string
+	}{
+		{
+			name: "normal",
+			bp: `
+				srcs: ["bar.c"],
+			`,
+		},
+		{
+			name: "keep symbols",
+			bp: `
+				srcs: ["bar.c"],
+				prefix_symbols: "foo_",
+			`,
+		},
+		{
+			name: "partial linking",
+			bp: `
+				srcs: ["bar.c", "baz.c"],
+			`,
+		},
+		{
+			name: "partial linking and prefix symbols",
+			bp: `
+				srcs: ["bar.c", "baz.c"],
+				prefix_symbols: "foo_",
+			`,
+		},
+	}
+
+	for _, testcase := range testcases {
+		bp := fmt.Sprintf(`
+			cc_object {
+				name: "foo",
+				%s
+			}
+		`, testcase.bp)
+		t.Run(testcase.name, func(t *testing.T) {
+			ctx := PrepareForIntegrationTestWithCc.RunTestWithBp(t, bp)
+			android.AssertPathRelativeToTopEquals(t, "expected output file foo.o",
+				"out/soong/.intermediates/foo/android_arm64_armv8-a/foo.o",
+				ctx.ModuleForTests("foo", "android_arm64_armv8-a").Output("foo.o").Output)
+		})
+	}
+
+}
diff --git a/cc/proto.go b/cc/proto.go
index 8e6d5ed..cf5ed04 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -178,7 +178,7 @@
 	var ret bp2buildProtoDeps
 
 	protoInfo, ok := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, protoSrcs)
-	if !ok {
+	if !ok || protoInfo.Proto_libs.IsEmpty() {
 		return ret
 	}
 
@@ -201,9 +201,8 @@
 	dep := android.BazelLabelForModuleDepSingle(ctx, depName)
 	ret.protoDep = &bazel.LabelAttribute{Value: &dep}
 
-	protoLabel := bazel.Label{Label: ":" + protoInfo.Name}
 	var protoAttrs protoAttributes
-	protoAttrs.Deps.SetValue(bazel.LabelList{Includes: []bazel.Label{protoLabel}})
+	protoAttrs.Deps.SetValue(protoInfo.Proto_libs)
 
 	name := m.Name() + suffix
 
diff --git a/cc/sanitize.go b/cc/sanitize.go
index edff059..436b149 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -714,8 +714,13 @@
 
 	if Bool(sanitize.Properties.Sanitize.Memtag_stack) {
 		flags.Local.CFlags = append(flags.Local.CFlags, memtagStackCommonFlags...)
+		// TODO(fmayer): remove -Wno-error once https://reviews.llvm.org/D127917 is in Android toolchain.
+		flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-error=frame-larger-than")
 		flags.Local.AsFlags = append(flags.Local.AsFlags, memtagStackCommonFlags...)
 		flags.Local.LdFlags = append(flags.Local.LdFlags, memtagStackCommonFlags...)
+		// This works around LLD complaining about the stack frame size.
+		// TODO(fmayer): remove once https://reviews.llvm.org/D127917 is in Android toolchain.
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-fatal-warnings")
 	}
 
 	if (Bool(sanitize.Properties.Sanitize.Memtag_heap) || Bool(sanitize.Properties.Sanitize.Memtag_stack)) && ctx.binary() {
diff --git a/cmd/extract_apks/main.go b/cmd/extract_apks/main.go
index 1cf64de..d200502 100644
--- a/cmd/extract_apks/main.go
+++ b/cmd/extract_apks/main.go
@@ -75,7 +75,7 @@
 		return nil, err
 	}
 	bytes := make([]byte, tocFile.FileHeader.UncompressedSize64)
-	if _, err := rc.Read(bytes); err != io.EOF {
+	if _, err := rc.Read(bytes); err != nil && err != io.EOF {
 		return nil, err
 	}
 	rc.Close()
diff --git a/docs/perf.md b/docs/perf.md
index 86a27b4..694dcf1 100644
--- a/docs/perf.md
+++ b/docs/perf.md
@@ -221,6 +221,18 @@
 various .ninja files. The files are (mostly) human-readable, but a (slow) web
 interface can be used by running `NINJA_ARGS="-t browse <target>" m`.
 
+There is also `SOONG_UI_NINJA_ARGS`, which passes ninja arguments to soong ui's
+ninja invocations, e.g. to emit $OUT_DIR/soong/build.ninja, $OUT_DIR/soong/module-graph.json, etc.
+
+```bash
+$ m nothing
+$ touch Android.bp
+$ SOONG_UI_NINJA_ARGS="-d explain" m nothing
+...
+ninja explain: restat of output out/soong/build.ninja older than most recent input Android.bp
+...
+```
+
 #### Builds take a long time
 
 If the long part in the trace view of a build is a relatively solid block, then
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index b2361ce..7520f58 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -31,6 +31,7 @@
 	"encoding/json"
 	"fmt"
 	"path/filepath"
+	"reflect"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -692,6 +693,22 @@
 				src.SetSelectValue(axis, config, label)
 			}
 		}
+
+		for propName, productConfigProps := range android.ProductVariableProperties(ctx) {
+			for configProp, propVal := range productConfigProps {
+				if propName == "Src" {
+					props, ok := propVal.(*string)
+					if !ok {
+						ctx.PropertyErrorf(" Expected Property to have type string, but was %s\n", reflect.TypeOf(propVal).String())
+						continue
+					}
+					if props != nil {
+						label := android.BazelLabelForModuleSrcSingle(ctx, *props)
+						src.SetSelectValue(configProp.ConfigurationAxis(), configProp.SelectKey(), label)
+					}
+				}
+			}
+		}
 	}
 
 	var filename string
diff --git a/java/base.go b/java/base.go
index 53f0f52..23b4d46 100644
--- a/java/base.go
+++ b/java/base.go
@@ -650,6 +650,10 @@
 	return false
 }
 
+func (j *Module) setInstrument(value bool) {
+	j.properties.Instrument = value
+}
+
 func (j *Module) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
 	return android.SdkSpecFrom(ctx, String(j.deviceProperties.Sdk_version))
 }
@@ -789,9 +793,6 @@
 	} else if j.shouldInstrumentStatic(ctx) {
 		ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent")
 	}
-	if j.shouldInstrument(ctx) {
-		ctx.AddVariationDependencies(nil, libTag, "jacocoagent")
-	}
 
 	if j.useCompose() {
 		ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), kotlinPluginTag,
@@ -862,7 +863,9 @@
 	// add flags for dirs containing AIDL srcs that haven't been specified yet
 	flags = append(flags, genAidlIncludeFlags(ctx, aidlSrcs, includeDirs))
 
-	if Bool(j.deviceProperties.Aidl.Generate_traces) {
+	sdkVersion := (j.SdkVersion(ctx)).Kind
+	defaultTrace := ((sdkVersion == android.SdkSystemServer) || (sdkVersion == android.SdkCore) || (sdkVersion == android.SdkCorePlatform))
+	if proptools.BoolDefault(j.deviceProperties.Aidl.Generate_traces, defaultTrace) {
 		flags = append(flags, "-t")
 	}
 
@@ -1435,10 +1438,6 @@
 		j.headerJarFile = j.implementationJarFile
 	}
 
-	if j.shouldInstrumentInApex(ctx) {
-		j.properties.Instrument = true
-	}
-
 	// enforce syntax check to jacoco filters for any build (http://b/183622051)
 	specs := j.jacocoModuleToZipCommand(ctx)
 	if ctx.Failed() {
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 2bfb255..c63df59 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -96,23 +96,6 @@
 }
 
 func TestBootclasspathFragment_Coverage(t *testing.T) {
-	prepareForTestWithFrameworkCoverage := android.GroupFixturePreparers(
-		android.FixtureMergeEnv(map[string]string{
-			"EMMA_INSTRUMENT":           "true",
-			"EMMA_INSTRUMENT_FRAMEWORK": "true",
-		}),
-		// need to mock jacocoagent here to satisfy dependency added for
-		// instrumented libraries at build time
-		android.FixtureAddFile("jacocoagent/Android.bp", []byte(`
-			java_library {
-				name: "jacocoagent",
-				srcs: ["Test.java"],
-				system_modules: "none",
-				sdk_version: "none",
-			}
-		`)),
-	)
-
 	prepareWithBp := android.FixtureWithRootAndroidBp(`
 		bootclasspath_fragment {
 			name: "myfragment",
@@ -191,7 +174,7 @@
 
 	t.Run("with coverage", func(t *testing.T) {
 		result := android.GroupFixturePreparers(
-			prepareForTestWithFrameworkCoverage,
+			prepareForTestWithFrameworkJacocoInstrumentation,
 			preparer,
 		).RunTest(t)
 		checkContents(t, result, "mybootlib", "coveragelib")
diff --git a/java/config/config.go b/java/config/config.go
index 422f860..03288fe 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -78,7 +78,7 @@
 func init() {
 	pctx.Import("github.com/google/blueprint/bootstrap")
 
-	exportedVars.ExportStringStaticVariable("JavacHeapSize", "2048M")
+	exportedVars.ExportStringStaticVariable("JavacHeapSize", "4096M")
 	exportedVars.ExportStringStaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
 
 	// ErrorProne can use significantly more memory than javac alone, give it a higher heap
@@ -124,6 +124,10 @@
 		// This is set up and guaranteed by soong_ui
 		return ctx.Config().Getenv("ANDROID_JAVA_HOME")
 	})
+	pctx.VariableFunc("Java11Home", func(ctx android.PackageVarContext) string {
+		// This is set up and guaranteed by soong_ui
+		return ctx.Config().Getenv("ANDROID_JAVA11_HOME")
+	})
 	pctx.VariableFunc("JlinkVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("OVERRIDE_JLINK_VERSION_NUMBER"); override != "" {
 			return override
@@ -137,11 +141,12 @@
 	})
 
 	pctx.SourcePathVariable("JavaToolchain", "${JavaHome}/bin")
+	pctx.SourcePathVariable("Java11Toolchain", "${Java11Home}/bin")
 	pctx.SourcePathVariableWithEnvOverride("JavacCmd",
 		"${JavaToolchain}/javac", "ALTERNATE_JAVAC")
 	pctx.SourcePathVariable("JavaCmd", "${JavaToolchain}/java")
 	pctx.SourcePathVariable("JarCmd", "${JavaToolchain}/jar")
-	pctx.SourcePathVariable("JavadocCmd", "${JavaToolchain}/javadoc")
+	pctx.SourcePathVariable("JavadocCmd", "${Java11Toolchain}/javadoc")
 	pctx.SourcePathVariable("JlinkCmd", "${JavaToolchain}/jlink")
 	pctx.SourcePathVariable("JmodCmd", "${JavaToolchain}/jmod")
 	pctx.SourcePathVariable("JrtFsJar", "${JavaHome}/lib/jrt-fs.jar")
@@ -267,7 +272,7 @@
 
 // JavadocCmd returns a SourcePath object with the path to the java command.
 func JavadocCmd(ctx android.PathContext) android.SourcePath {
-	return javaTool(ctx, "javadoc")
+	return java11Tool(ctx, "javadoc")
 }
 
 func javaTool(ctx android.PathContext, tool string) android.SourcePath {
@@ -281,6 +286,17 @@
 
 }
 
+func java11Tool(ctx android.PathContext, tool string) android.SourcePath {
+	type javaToolKey string
+
+	key := android.NewCustomOnceKey(javaToolKey(tool))
+
+	return ctx.Config().OnceSourcePath(key, func() android.SourcePath {
+		return java11Toolchain(ctx).Join(ctx, tool)
+	})
+
+}
+
 var javaToolchainKey = android.NewOnceKey("javaToolchain")
 
 func javaToolchain(ctx android.PathContext) android.SourcePath {
@@ -289,6 +305,14 @@
 	})
 }
 
+var java11ToolchainKey = android.NewOnceKey("java11Toolchain")
+
+func java11Toolchain(ctx android.PathContext) android.SourcePath {
+	return ctx.Config().OnceSourcePath(java11ToolchainKey, func() android.SourcePath {
+		return java11Home(ctx).Join(ctx, "bin")
+	})
+}
+
 var javaHomeKey = android.NewOnceKey("javaHome")
 
 func javaHome(ctx android.PathContext) android.SourcePath {
@@ -297,3 +321,12 @@
 		return android.PathForSource(ctx, ctx.Config().Getenv("ANDROID_JAVA_HOME"))
 	})
 }
+
+var java11HomeKey = android.NewOnceKey("java11Home")
+
+func java11Home(ctx android.PathContext) android.SourcePath {
+	return ctx.Config().OnceSourcePath(java11HomeKey, func() android.SourcePath {
+		// This is set up and guaranteed by soong_ui
+		return android.PathForSource(ctx, ctx.Config().Getenv("ANDROID_JAVA11_HOME"))
+	})
+}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 9b1f43b..fc95184 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -755,6 +755,7 @@
 	return rule.Command().
 		BuiltTool("dokka").
 		Flag(config.JavacVmFlags).
+		Flag("-J--add-opens=java.base/java.lang=ALL-UNNAMED").
 		Flag(srcJarDir.String()).
 		FlagWithInputList("-classpath ", dokkaClasspath, ":").
 		FlagWithArg("-format ", "dac").
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 12590ca..d9efb40 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"path/filepath"
+	"regexp"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -142,6 +143,10 @@
 	// if set to true, collect the values used by the Dev tools and
 	// write them in files packaged with the SDK. Defaults to false.
 	Write_sdk_values *bool
+
+	// path or filegroup to file defining extension an SDK name <-> numerical ID mapping and
+	// what APIs exist in which SDKs; passed to metalava via --sdk-extensions-info
+	Extensions_info_file *string `android:"path"`
 }
 
 // Used by xsd_config
@@ -398,9 +403,20 @@
 	filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
 
 	var dirs []string
+	var extensions_dir string
 	ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
 		if t, ok := m.(*ExportedDroiddocDir); ok {
+			extRegex := regexp.MustCompile(t.dir.String() + `/extensions/[0-9]+/public/.*\.jar`)
+
+			// Grab the first extensions_dir and we find while scanning ExportedDroiddocDir.deps;
+			// ideally this should be read from prebuiltApis.properties.Extensions_*
 			for _, dep := range t.deps {
+				if extRegex.MatchString(dep.String()) && d.properties.Extensions_info_file != nil {
+					if extensions_dir == "" {
+						extensions_dir = t.dir.String() + "/extensions"
+					}
+					cmd.Implicit(dep)
+				}
 				if dep.Base() == filename {
 					cmd.Implicit(dep)
 				}
@@ -445,6 +461,16 @@
 			cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkDir, filename))
 		}
 	}
+
+	if d.properties.Extensions_info_file != nil {
+		if extensions_dir == "" {
+			ctx.ModuleErrorf("extensions_info_file set, but no SDK extension dirs found")
+		}
+		info_file := android.PathForModuleSrc(ctx, *d.properties.Extensions_info_file)
+		cmd.Implicit(info_file)
+		cmd.FlagWithArg("--sdk-extensions-root ", extensions_dir)
+		cmd.FlagWithArg("--sdk-extensions-info ", info_file.String())
+	}
 }
 
 func metalavaUseRbe(ctx android.ModuleContext) bool {
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 9fdfdde..2443692 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -259,3 +259,33 @@
 		t.Errorf("inputs of %q must be []string{%q}, but was %#v.", moduleName, systemJar, systemJars)
 	}
 }
+
+func TestDroidstubsWithSdkExtensions(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+		droiddoc_exported_dir {
+			name: "sdk-dir",
+			path: "sdk",
+		}
+
+		droidstubs {
+			name: "baz-stubs",
+			api_levels_annotations_dirs: ["sdk-dir"],
+			api_levels_annotations_enabled: true,
+			extensions_info_file: ":info-file",
+		}
+
+		filegroup {
+			name: "info-file",
+			srcs: ["sdk/extensions/info.txt"],
+		}
+		`,
+		map[string][]byte{
+			"sdk/extensions/1/public/some-mainline-module-stubs.jar": nil,
+			"sdk/extensions/info.txt":                                nil,
+		})
+	m := ctx.ModuleForTests("baz-stubs", "android_common")
+	manifest := m.Output("metalava.sbox.textproto")
+	cmdline := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command)
+	android.AssertStringDoesContain(t, "sdk-extensions-root present", cmdline, "--sdk-extensions-root sdk/extensions")
+	android.AssertStringDoesContain(t, "sdk-extensions-info present", cmdline, "--sdk-extensions-info sdk/extensions/info.txt")
+}
diff --git a/java/jacoco.go b/java/jacoco.go
index e11c2ce..f8012b8 100644
--- a/java/jacoco.go
+++ b/java/jacoco.go
@@ -47,6 +47,34 @@
 		"strippedJar", "stripSpec", "tmpDir", "tmpJar")
 )
 
+func jacocoDepsMutator(ctx android.BottomUpMutatorContext) {
+	type instrumentable interface {
+		shouldInstrument(ctx android.BaseModuleContext) bool
+		shouldInstrumentInApex(ctx android.BaseModuleContext) bool
+		setInstrument(value bool)
+	}
+
+	j, ok := ctx.Module().(instrumentable)
+	if !ctx.Module().Enabled() || !ok {
+		return
+	}
+
+	if j.shouldInstrumentInApex(ctx) {
+		j.setInstrument(true)
+	}
+
+	if j.shouldInstrument(ctx) && ctx.ModuleName() != "jacocoagent" {
+		// We can use AddFarVariationDependencies here because, since this dep
+		// is added as libs only (i.e. a compiletime CLASSPATH entry only),
+		// the first variant of jacocoagent is sufficient to prevent
+		// compile time errors.
+		// At this stage in the build, AddVariationDependencies is not always
+		// able to procure a variant of jacocoagent that matches the calling
+		// module.
+		ctx.AddFarVariationDependencies(ctx.Module().Target().Variations(), libTag, "jacocoagent")
+	}
+}
+
 // Instruments a jar using the Jacoco command line interface.  Uses stripSpec to extract a subset
 // of the classes in inputJar into strippedJar, instruments strippedJar into tmpJar, and then
 // combines the classes in tmpJar with inputJar (preferring the instrumented classes in tmpJar)
diff --git a/java/java.go b/java/java.go
index 0251b57..d04e52a 100644
--- a/java/java.go
+++ b/java/java.go
@@ -66,6 +66,8 @@
 	// to support the checks in dexpreoptDisabled().
 	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("dexpreopt_tool_deps", dexpreoptToolDepsMutator).Parallel()
+		// needs access to ApexInfoProvider which is available after variant creation
+		ctx.BottomUp("jacoco_deps", jacocoDepsMutator).Parallel()
 	})
 
 	ctx.RegisterSingletonType("logtags", LogtagsSingleton)
diff --git a/java/testing.go b/java/testing.go
index 511cc5d..1f41191 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -59,11 +59,9 @@
 	}.AddToFixture(),
 )
 
-// Test fixture preparer that will define all default java modules except the
-// fake_tool_binary for dex2oatd.
-var PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd = android.GroupFixturePreparers(
-	// Make sure that all the module types used in the defaults are registered.
-	PrepareForTestWithJavaBuildComponents,
+var prepareForTestWithFrameworkDeps = android.GroupFixturePreparers(
+	// The java default module definitions.
+	android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", gatherRequiredDepsForTest()),
 	// Additional files needed when test disallows non-existent source.
 	android.MockFS{
 		// Needed for framework-res
@@ -77,8 +75,14 @@
 		"build/make/core/proguard.flags":             nil,
 		"build/make/core/proguard_basic_keeps.flags": nil,
 	}.AddToFixture(),
-	// The java default module definitions.
-	android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", gatherRequiredDepsForTest()),
+)
+
+// Test fixture preparer that will define all default java modules except the
+// fake_tool_binary for dex2oatd.
+var PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd = android.GroupFixturePreparers(
+	// Make sure that all the module types used in the defaults are registered.
+	PrepareForTestWithJavaBuildComponents,
+	prepareForTestWithFrameworkDeps,
 	// Add dexpreopt compat libs (android.test.base, etc.) and a fake dex2oatd module.
 	dexpreopt.PrepareForTestWithDexpreoptCompatLibs,
 )
@@ -141,6 +145,30 @@
 	"30": {},
 })
 
+var prepareForTestWithFrameworkJacocoInstrumentation = android.GroupFixturePreparers(
+	android.FixtureMergeEnv(map[string]string{
+		"EMMA_INSTRUMENT_FRAMEWORK": "true",
+	}),
+	PrepareForTestWithJacocoInstrumentation,
+)
+
+// PrepareForTestWithJacocoInstrumentation creates a mock jacocoagent library that can be
+// depended on as part of the build process for instrumented Java modules.
+var PrepareForTestWithJacocoInstrumentation = android.GroupFixturePreparers(
+	android.FixtureMergeEnv(map[string]string{
+		"EMMA_INSTRUMENT": "true",
+	}),
+	android.FixtureAddFile("jacocoagent/Test.java", nil),
+	android.FixtureAddFile("jacocoagent/Android.bp", []byte(`
+		java_library {
+			name: "jacocoagent",
+			host_supported: true,
+			srcs: ["Test.java"],
+			sdk_version: "current",
+		}
+	`)),
+)
+
 // FixtureWithPrebuiltApis creates a preparer that will define prebuilt api modules for the
 // specified releases and modules.
 //
diff --git a/python/scripts/stub_template_host.txt b/python/scripts/stub_template_host.txt
index 138404b..23897b3 100644
--- a/python/scripts/stub_template_host.txt
+++ b/python/scripts/stub_template_host.txt
@@ -81,7 +81,9 @@
     os.environ.update(new_env)
 
     sys.stdout.flush()
-    retCode = subprocess.call(args)
+    # close_fds=False so that you can run binaries with files provided on the command line:
+    # my_python_app --file <(echo foo)
+    retCode = subprocess.call(args, close_fds=False)
     sys.exit(retCode)
   except:
     raise
diff --git a/tests/apex_comparison_tests.sh b/tests/apex_comparison_tests.sh
index ac3c177..6bc0165 100755
--- a/tests/apex_comparison_tests.sh
+++ b/tests/apex_comparison_tests.sh
@@ -60,7 +60,8 @@
 
 BAZEL_OUT="$(call_bazel info output_path)"
 
-call_bazel build --config=bp2build --config=ci --config=android_arm \
+export TARGET_PRODUCT="module_arm"
+call_bazel build --config=bp2build --config=ci --config=android \
   //packages/modules/adb/apex:com.android.adbd \
   //system/timezone/apex:com.android.tzdata \
   //build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal.apex
@@ -86,7 +87,7 @@
 
   # Compare the outputs of `deapexer list`, which lists the contents of the apex filesystem image.
   local SOONG_APEX="$SOONG_OUTPUT_DIR/$APEX"
-  local BAZEL_APEX="$BAZEL_OUT/android_arm-fastbuild/bin/$APEX_DIR/$APEX"
+  local BAZEL_APEX="$BAZEL_OUT/android_target-fastbuild/bin/$APEX_DIR/$APEX"
 
   local SOONG_LIST="$OUTPUT_DIR/soong.list"
   local BAZEL_LIST="$OUTPUT_DIR/bazel.list"
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 3cdf6aa..78af54d 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -103,7 +103,7 @@
   fi
 
   # NOTE: We don't actually use the extra BUILD file for anything here
-  run_bazel build --package_path=out/soong/workspace //foo/...
+  run_bazel build --config=android --package_path=out/soong/workspace //foo/...
 
   local the_answer_file="bazel-out/android_target-fastbuild/bin/foo/convertible_soong_module/the_answer.txt"
   if [[ ! -f "${the_answer_file}" ]]; then
@@ -146,10 +146,10 @@
 
   run_soong bp2build
 
-  run_bazel build --package_path=out/soong/workspace //a:qq
+  run_bazel build --config=android --package_path=out/soong/workspace //a:qq
   local -r output_mtime1=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
 
-  run_bazel build --package_path=out/soong/workspace //a:qq
+  run_bazel build --config=android --package_path=out/soong/workspace //a:qq
   local -r output_mtime2=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
 
   if [[ "$output_mtime1" != "$output_mtime2" ]]; then
@@ -160,7 +160,7 @@
 #define QQ 2
 EOF
 
-  run_bazel build --package_path=out/soong/workspace //a:qq
+  run_bazel build --config=android --package_path=out/soong/workspace //a:qq
   local -r output_mtime3=$(stat -c "%y" bazel-bin/a/_objs/qq/qq.o)
 
   if [[ "$output_mtime1" == "$output_mtime3" ]]; then
diff --git a/tests/lib.sh b/tests/lib.sh
index 6210e77..7248ade 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -123,6 +123,7 @@
   symlink_directory prebuilts/jdk
   symlink_directory external/bazel-skylib
   symlink_directory external/bazelbuild-rules_android
+  symlink_directory external/bazelbuild-kotlin-rules
 
   symlink_file WORKSPACE
   symlink_file BUILD