Install sdk variants in unbundled builds and package uninstallable variants am: 3d6f3a02f8

Original change: https://googleplex-android-review.googlesource.com/c/platform/build/soong/+/24086298

Change-Id: I9a32c851e0ece8866d0f5bbd515674ffba1be152
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/aidl_library/Android.bp b/aidl_library/Android.bp
new file mode 100644
index 0000000..ec21504
--- /dev/null
+++ b/aidl_library/Android.bp
@@ -0,0 +1,32 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-aidl-library",
+    pkgPath: "android/soong/aidl_library",
+    deps: [
+        "soong-android",
+    ],
+    srcs: [
+        "aidl_library.go",
+    ],
+    testSrcs: [
+        "aidl_library_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/aidl_library/aidl_library.go b/aidl_library/aidl_library.go
new file mode 100644
index 0000000..5985103
--- /dev/null
+++ b/aidl_library/aidl_library.go
@@ -0,0 +1,187 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aidl_library
+
+import (
+	"android/soong/android"
+	"android/soong/bazel"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+var PrepareForTestWithAidlLibrary = android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+	registerAidlLibraryBuildComponents(ctx)
+})
+
+func init() {
+	registerAidlLibraryBuildComponents(android.InitRegistrationContext)
+}
+
+func registerAidlLibraryBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("aidl_library", AidlLibraryFactory)
+}
+
+type aidlLibraryProperties struct {
+	// srcs lists files that are included in this module for aidl compilation
+	Srcs []string `android:"path"`
+
+	// hdrs lists the headers that are imported by srcs but are not compiled by aidl to language binding code
+	// hdrs is provided to support Bazel migration. It is a no-op until
+	// we enable input sandbox in aidl compilation action
+	Hdrs []string `android:"path"`
+
+	// The prefix to strip from the paths of the .aidl files
+	// The remaining path is the package path of the aidl interface
+	Strip_import_prefix *string
+
+	// List of aidl files or aidl_library depended on by the module
+	Deps []string `android:"arch_variant"`
+}
+
+type AidlLibrary struct {
+	android.ModuleBase
+	android.BazelModuleBase
+	properties aidlLibraryProperties
+}
+
+type bazelAidlLibraryAttributes struct {
+	Srcs                bazel.LabelListAttribute
+	Hdrs                bazel.LabelListAttribute
+	Strip_import_prefix *string
+	Deps                bazel.LabelListAttribute
+}
+
+func (lib *AidlLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	srcs := bazel.MakeLabelListAttribute(
+		android.BazelLabelForModuleSrc(
+			ctx,
+			lib.properties.Srcs,
+		),
+	)
+
+	hdrs := bazel.MakeLabelListAttribute(
+		android.BazelLabelForModuleSrc(
+			ctx,
+			lib.properties.Hdrs,
+		),
+	)
+
+	tags := []string{"apex_available=//apex_available:anyapex"}
+	deps := bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, lib.properties.Deps))
+
+	attrs := &bazelAidlLibraryAttributes{
+		Srcs:                srcs,
+		Hdrs:                hdrs,
+		Strip_import_prefix: lib.properties.Strip_import_prefix,
+		Deps:                deps,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "aidl_library",
+		Bzl_load_location: "//build/bazel/rules/aidl:aidl_library.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(
+		props,
+		android.CommonAttributes{
+			Name: lib.Name(),
+			Tags: bazel.MakeStringListAttribute(tags),
+		},
+		attrs,
+	)
+}
+
+type AidlLibraryInfo struct {
+	// The direct aidl files of the module
+	Srcs android.Paths
+	// The include dirs to the direct aidl files and those provided from transitive aidl_library deps
+	IncludeDirs android.DepSet
+	// The direct hdrs and hdrs from transitive deps
+	Hdrs android.DepSet
+}
+
+// AidlLibraryProvider provides the srcs and the transitive include dirs
+var AidlLibraryProvider = blueprint.NewProvider(AidlLibraryInfo{})
+
+func (lib *AidlLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	includeDirsDepSetBuilder := android.NewDepSetBuilder(android.PREORDER)
+	hdrsDepSetBuilder := android.NewDepSetBuilder(android.PREORDER)
+
+	if len(lib.properties.Srcs) == 0 && len(lib.properties.Hdrs) == 0 {
+		ctx.ModuleErrorf("at least srcs or hdrs prop must be non-empty")
+	}
+
+	srcs := android.PathsForModuleSrc(ctx, lib.properties.Srcs)
+	hdrs := android.PathsForModuleSrc(ctx, lib.properties.Hdrs)
+
+	if lib.properties.Strip_import_prefix != nil {
+		srcs = android.PathsWithModuleSrcSubDir(
+			ctx,
+			srcs,
+			android.String(lib.properties.Strip_import_prefix),
+		)
+
+		hdrs = android.PathsWithModuleSrcSubDir(
+			ctx,
+			hdrs,
+			android.String(lib.properties.Strip_import_prefix),
+		)
+	}
+	hdrsDepSetBuilder.Direct(hdrs...)
+
+	includeDir := android.PathForModuleSrc(
+		ctx,
+		proptools.StringDefault(lib.properties.Strip_import_prefix, ""),
+	)
+	includeDirsDepSetBuilder.Direct(includeDir)
+
+	for _, dep := range ctx.GetDirectDepsWithTag(aidlLibraryTag) {
+		if ctx.OtherModuleHasProvider(dep, AidlLibraryProvider) {
+			info := ctx.OtherModuleProvider(dep, AidlLibraryProvider).(AidlLibraryInfo)
+			includeDirsDepSetBuilder.Transitive(&info.IncludeDirs)
+			hdrsDepSetBuilder.Transitive(&info.Hdrs)
+		}
+	}
+
+	ctx.SetProvider(AidlLibraryProvider, AidlLibraryInfo{
+		Srcs:        srcs,
+		IncludeDirs: *includeDirsDepSetBuilder.Build(),
+		Hdrs:        *hdrsDepSetBuilder.Build(),
+	})
+}
+
+// aidl_library contains a list of .aidl files and the strip_import_prefix to
+// to strip from the paths of the .aidl files. The sub-path left-over after stripping
+// corresponds to the aidl package path the aidl interfaces are scoped in
+func AidlLibraryFactory() android.Module {
+	module := &AidlLibrary{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidModule(module)
+	android.InitBazelModule(module)
+	return module
+}
+
+type aidlDependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+var aidlLibraryTag = aidlDependencyTag{}
+
+func (lib *AidlLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+	for _, dep := range lib.properties.Deps {
+		ctx.AddDependency(lib, aidlLibraryTag, dep)
+	}
+}
diff --git a/aidl_library/aidl_library_test.go b/aidl_library/aidl_library_test.go
new file mode 100644
index 0000000..d9dd245
--- /dev/null
+++ b/aidl_library/aidl_library_test.go
@@ -0,0 +1,136 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package aidl_library
+
+import (
+	"android/soong/android"
+	"testing"
+)
+
+func TestAidlLibrary(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		PrepareForTestWithAidlLibrary,
+		android.MockFS{
+			"package_bar/Android.bp": []byte(`
+			aidl_library {
+					name: "bar",
+					srcs: ["x/y/Bar.aidl"],
+					strip_import_prefix: "x",
+				}
+			`),
+		}.AddToFixture(),
+		android.MockFS{
+			"package_foo/Android.bp": []byte(`
+			aidl_library {
+					name: "foo",
+					srcs: ["a/b/Foo.aidl"],
+					hdrs: ["a/Header.aidl"],
+					strip_import_prefix: "a",
+					deps: ["bar"],
+				}
+			`),
+		}.AddToFixture(),
+	).RunTest(t).TestContext
+
+	foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary)
+	actualInfo := ctx.ModuleProvider(foo, AidlLibraryProvider).(AidlLibraryInfo)
+
+	android.AssertArrayString(
+		t,
+		"aidl include dirs",
+		[]string{"package_foo/a", "package_bar/x"},
+		actualInfo.IncludeDirs.ToList().Strings(),
+	)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"aidl srcs paths",
+		[]string{"package_foo/a/b/Foo.aidl"},
+		actualInfo.Srcs,
+	)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"aidl hdrs paths",
+		[]string{"package_foo/a/Header.aidl"},
+		actualInfo.Hdrs.ToList(),
+	)
+}
+
+func TestAidlLibraryWithoutStripImportPrefix(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		PrepareForTestWithAidlLibrary,
+		android.MockFS{
+			"package_bar/Android.bp": []byte(`
+			aidl_library {
+					name: "bar",
+					srcs: ["x/y/Bar.aidl"],
+					hdrs: ["BarHeader.aidl"],
+				}
+			`),
+		}.AddToFixture(),
+		android.MockFS{
+			"package_foo/Android.bp": []byte(`
+			aidl_library {
+					name: "foo",
+					srcs: ["a/b/Foo.aidl"],
+					deps: ["bar"],
+				}
+			`),
+		}.AddToFixture(),
+	).RunTest(t).TestContext
+
+	foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary)
+	actualInfo := ctx.ModuleProvider(foo, AidlLibraryProvider).(AidlLibraryInfo)
+
+	android.AssertArrayString(
+		t,
+		"aidl include dirs",
+		[]string{"package_foo", "package_bar"},
+		actualInfo.IncludeDirs.ToList().Strings(),
+	)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"aidl srcs paths",
+		[]string{"package_foo/a/b/Foo.aidl"},
+		actualInfo.Srcs,
+	)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"aidl hdrs paths",
+		[]string{"package_bar/BarHeader.aidl"},
+		actualInfo.Hdrs.ToList(),
+	)
+}
+
+func TestAidlLibraryWithNoSrcsHdrsDeps(t *testing.T) {
+	t.Parallel()
+	android.GroupFixturePreparers(
+		PrepareForTestWithAidlLibrary,
+		android.MockFS{
+			"package_bar/Android.bp": []byte(`
+			aidl_library {
+					name: "bar",
+				}
+			`),
+		}.AddToFixture(),
+	).
+		ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("at least srcs or hdrs prop must be non-empty")).
+		RunTest(t)
+}
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 63d4e11..b9bbcdc 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -201,6 +201,7 @@
 		"frameworks/av/media/module/minijail":                Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/services/minijail":                    Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/libs/androidfw":                     Bp2BuildDefaultTrue,
+		"frameworks/base/libs/services":                      Bp2BuildDefaultTrue,
 		"frameworks/base/media/tests/MediaDump":              Bp2BuildDefaultTrue,
 		"frameworks/base/services/tests/servicestests/aidl":  Bp2BuildDefaultTrue,
 		"frameworks/base/proto":                              Bp2BuildDefaultTrue,
@@ -559,6 +560,9 @@
 		//external/fec
 		"libfec_rs",
 
+		//frameworks/base/core/java
+		"IDropBoxManagerService_aidl",
+
 		//system/core/libsparse
 		"libsparse",
 
diff --git a/android/apex.go b/android/apex.go
index c9b4a0b..35e1d02 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -451,6 +451,14 @@
 	AvailableToGkiApex  = "com.android.gki.*"
 )
 
+var (
+	AvailableToRecognziedWildcards = []string{
+		AvailableToPlatform,
+		AvailableToAnyApex,
+		AvailableToGkiApex,
+	}
+)
+
 // CheckAvailableForApex provides the default algorithm for checking the apex availability. When the
 // availability is empty, it defaults to ["//apex_available:platform"] which means "available to the
 // platform but not available to any APEX". When the list is not empty, `what` is matched against
@@ -909,3 +917,9 @@
 		return true
 	})
 }
+
+// Implemented by apexBundle.
+type ApexTestInterface interface {
+	// Return true if the apex bundle is an apex_test
+	IsTestApex() bool
+}
diff --git a/android/config.go b/android/config.go
index 980460a..dfbb46f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -702,6 +702,14 @@
 	return path
 }
 
+func (c *config) HostCcSharedLibPath(ctx PathContext, lib string) Path {
+	libDir := "lib"
+	if ctx.Config().BuildArch.Multilib == "lib64" {
+		libDir = "lib64"
+	}
+	return pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, libDir, false, lib+".so")
+}
+
 // PrebuiltOS returns the name of the host OS used in prebuilts directories.
 func (c *config) PrebuiltOS() string {
 	switch runtime.GOOS {
diff --git a/android/filegroup.go b/android/filegroup.go
index f30ee51..121368d 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -82,7 +82,6 @@
 type bazelAidlLibraryAttributes struct {
 	Srcs                bazel.LabelListAttribute
 	Strip_import_prefix *string
-	Deps                bazel.LabelListAttribute
 }
 
 // api srcs can be contained in filegroups.
@@ -121,12 +120,9 @@
 	// and then convert
 	if fg.ShouldConvertToAidlLibrary(ctx) {
 		tags := []string{"apex_available=//apex_available:anyapex"}
-		deps := bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, fg.properties.Aidl.Deps))
-
 		attrs := &bazelAidlLibraryAttributes{
 			Srcs:                srcs,
 			Strip_import_prefix: fg.properties.Path,
-			Deps:                deps,
 		}
 
 		props := bazel.BazelTargetModuleProperties{
@@ -192,14 +188,6 @@
 	// Create a make variable with the specified name that contains the list of files in the
 	// filegroup, relative to the root of the source tree.
 	Export_to_make_var *string
-
-	// aidl is explicitly provided for implicit aidl dependencies
-	// TODO(b/278298615): aidl prop is a no-op in Soong and is an escape hatch
-	// to include implicit aidl dependencies for bazel migration compatibility
-	Aidl struct {
-		// List of aidl files or filegroup depended on by srcs
-		Deps []string `android:"path"`
-	}
 }
 
 type fileGroup struct {
diff --git a/android/mutator.go b/android/mutator.go
index 4ec9604..013fa77 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -758,6 +758,35 @@
 	return attr
 }
 
+func ApexAvailableTagsWithoutTestApexes(ctx BaseModuleContext, mod Module) bazel.StringListAttribute {
+	attr := bazel.StringListAttribute{}
+	if am, ok := mod.(ApexModule); ok {
+		apexAvailableWithoutTestApexes := removeTestApexes(ctx, am.apexModuleBase().ApexAvailable())
+		// If a user does not specify apex_available in Android.bp, then soong provides a default.
+		// To avoid verbosity of BUILD files, remove this default from user-facing BUILD files.
+		if len(am.apexModuleBase().ApexProperties.Apex_available) == 0 {
+			apexAvailableWithoutTestApexes = []string{}
+		}
+		attr.Value = ConvertApexAvailableToTags(apexAvailableWithoutTestApexes)
+	}
+	return attr
+}
+
+func removeTestApexes(ctx BaseModuleContext, apex_available []string) []string {
+	testApexes := []string{}
+	for _, aa := range apex_available {
+		// ignore the wildcards
+		if InList(aa, AvailableToRecognziedWildcards) {
+			continue
+		}
+		mod, _ := ctx.ModuleFromName(aa)
+		if apex, ok := mod.(ApexTestInterface); ok && apex.IsTestApex() {
+			testApexes = append(testApexes, aa)
+		}
+	}
+	return RemoveListFromList(CopyOf(apex_available), testApexes)
+}
+
 func ConvertApexAvailableToTags(apexAvailable []string) []string {
 	if len(apexAvailable) == 0 {
 		// We need nil specifically to make bp2build not add the tags property at all,
@@ -771,6 +800,13 @@
 	return result
 }
 
+// ConvertApexAvailableToTagsWithoutTestApexes converts a list of apex names to a list of bazel tags
+// This function drops any test apexes from the input.
+func ConvertApexAvailableToTagsWithoutTestApexes(ctx BaseModuleContext, apexAvailable []string) []string {
+	noTestApexes := removeTestApexes(ctx, apexAvailable)
+	return ConvertApexAvailableToTags(noTestApexes)
+}
+
 func (t *topDownMutatorContext) createBazelTargetModule(
 	bazelProps bazel.BazelTargetModuleProperties,
 	commonAttrs CommonAttributes,
diff --git a/android/proto.go b/android/proto.go
index 09e50c8..cebbd59 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -234,7 +234,7 @@
 			}
 		}
 
-		tags := ApexAvailableTags(ctx.Module())
+		tags := ApexAvailableTagsWithoutTestApexes(ctx.(TopDownMutatorContext), ctx.Module())
 
 		ctx.CreateBazelTargetModule(
 			bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
diff --git a/android/test_asserts.go b/android/test_asserts.go
index 4143f15..5cc7e4a 100644
--- a/android/test_asserts.go
+++ b/android/test_asserts.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"reflect"
+	"regexp"
 	"strings"
 	"testing"
 )
@@ -137,6 +138,20 @@
 	}
 }
 
+// AssertStringMatches checks if the string matches the given regular expression. If it does not match,
+// then an error is reported with the supplied message including a reason for why it failed.
+func AssertStringMatches(t *testing.T, message, s, expectedRex string) {
+	t.Helper()
+	ok, err := regexp.MatchString(expectedRex, s)
+	if err != nil {
+		t.Fatalf("regexp failure trying to match %s against `%s` expression: %s", s, expectedRex, err)
+		return
+	}
+	if !ok {
+		t.Errorf("%s does not match regular expression %s", s, expectedRex)
+	}
+}
+
 // AssertStringListContains checks if the list of strings contains the expected string. If it does
 // not then it reports an error prefixed with the supplied message and including a reason for why it
 // failed.
diff --git a/android/updatable_modules.go b/android/updatable_modules.go
index e6ad3fe..db45637 100644
--- a/android/updatable_modules.go
+++ b/android/updatable_modules.go
@@ -29,8 +29,8 @@
 // based on the branch such that the builds from testing and development
 // branches will have a version higher than the prebuilts.
 // Versions per branch:
-// * x-dev           - xx2090000 (where xx is the branch SDK level)
+// * x-dev           - xx0090000 (where xx is the branch SDK level)
 // * AOSP            - xx9990000
 // * x-mainline-prod - xx9990000
 // * master          - 990090000
-const DefaultUpdatableModuleVersion = "342090000"
+const DefaultUpdatableModuleVersion = "349990000"
diff --git a/apex/apex.go b/apex/apex.go
index c1c9e5c..f7c7444 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -220,6 +220,13 @@
 	// imageApex or flattenedApex depending on Config.FlattenApex(). When payload_type is zip,
 	// this becomes zipApex.
 	ApexType apexPackaging `blueprint:"mutated"`
+
+	// Name that dependencies can specify in their apex_available properties to refer to this module.
+	// If not specified, this defaults to Soong module name.
+	Apex_available_name *string
+
+	// Variant version of the mainline module. Must be an integer between 0-9
+	Variant_version *string
 }
 
 type ApexNativeDependencies struct {
@@ -1823,6 +1830,7 @@
 	Certificate() java.Certificate
 	BaseModuleName() string
 	LintDepSets() java.LintDepSets
+	PrivAppAllowlist() android.OptionalPath
 }
 
 var _ androidApp = (*java.AndroidApp)(nil)
@@ -1843,7 +1851,7 @@
 	return buildId
 }
 
-func apexFileForAndroidApp(ctx android.BaseModuleContext, aapp androidApp) apexFile {
+func apexFilesForAndroidApp(ctx android.BaseModuleContext, aapp androidApp) []apexFile {
 	appDir := "app"
 	if aapp.Privileged() {
 		appDir = "priv-app"
@@ -1865,7 +1873,18 @@
 	}); ok {
 		af.overriddenPackageName = app.OverriddenManifestPackageName()
 	}
-	return af
+
+	apexFiles := []apexFile{}
+
+	if allowlist := aapp.PrivAppAllowlist(); allowlist.Valid() {
+		dirInApex := filepath.Join("etc", "permissions")
+		privAppAllowlist := newApexFile(ctx, allowlist.Path(), aapp.BaseModuleName()+"_privapp", dirInApex, etc, aapp)
+		apexFiles = append(apexFiles, privAppAllowlist)
+	}
+
+	apexFiles = append(apexFiles, af)
+
+	return apexFiles
 }
 
 func apexFileForRuntimeResourceOverlay(ctx android.BaseModuleContext, rro java.RuntimeResourceOverlayModule) apexFile {
@@ -2310,12 +2329,12 @@
 		case androidAppTag:
 			switch ap := child.(type) {
 			case *java.AndroidApp:
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
 				return true // track transitive dependencies
 			case *java.AndroidAppImport:
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
 			case *java.AndroidTestHelperApp:
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
 			case *java.AndroidAppSet:
 				appDir := "app"
 				if ap.Privileged() {
@@ -3083,6 +3102,13 @@
 		}
 
 		apexName := ctx.ModuleName()
+		for _, props := range ctx.Module().GetProperties() {
+			if apexProps, ok := props.(*apexBundleProperties); ok {
+				if apexProps.Apex_available_name != nil {
+					apexName = *apexProps.Apex_available_name
+				}
+			}
+		}
 		fromName := ctx.OtherModuleName(from)
 		toName := ctx.OtherModuleName(to)
 
@@ -3790,3 +3816,7 @@
 func invalidCompileMultilib(ctx android.TopDownMutatorContext, value string) {
 	ctx.PropertyErrorf("compile_multilib", "Invalid value: %s", value)
 }
+
+func (a *apexBundle) IsTestApex() bool {
+	return a.testApex
+}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index e12f758..2eb3593 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -6166,6 +6166,7 @@
 			sdk_version: "current",
 			system_modules: "none",
 			privileged: true,
+			privapp_allowlist: "privapp_allowlist_com.android.AppFooPriv.xml",
 			stl: "none",
 			apex_available: [ "myapex" ],
 		}
@@ -6195,6 +6196,7 @@
 
 	ensureContains(t, copyCmds, "image.apex/app/AppFoo@TEST.BUILD_ID/AppFoo.apk")
 	ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPriv@TEST.BUILD_ID/AppFooPriv.apk")
+	ensureContains(t, copyCmds, "image.apex/etc/permissions/privapp_allowlist_com.android.AppFooPriv.xml")
 
 	appZipRule := ctx.ModuleForTests("AppFoo", "android_common_apex10000").Description("zip jni libs")
 	// JNI libraries are uncompressed
@@ -6209,6 +6211,18 @@
 		// ... and not directly inside the APEX
 		ensureNotContains(t, copyCmds, "image.apex/lib64/"+jni+".so")
 	}
+
+	apexBundle := module.Module().(*apexBundle)
+	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
+	var builder strings.Builder
+	data.Custom(&builder, apexBundle.Name(), "TARGET_", "", data)
+	androidMk := builder.String()
+	ensureContains(t, androidMk, "LOCAL_MODULE := AppFooPriv.myapex")
+	ensureContains(t, androidMk, "LOCAL_MODULE := AppFoo.myapex")
+	ensureMatches(t, androidMk, "LOCAL_SOONG_INSTALLED_MODULE := \\S+AppFooPriv.apk")
+	ensureMatches(t, androidMk, "LOCAL_SOONG_INSTALLED_MODULE := \\S+AppFoo.apk")
+	ensureMatches(t, androidMk, "LOCAL_SOONG_INSTALL_PAIRS := \\S+AppFooPriv.apk")
+	ensureContains(t, androidMk, "LOCAL_SOONG_INSTALL_PAIRS := privapp_allowlist_com.android.AppFooPriv.xml:$(PRODUCT_OUT)/apex/myapex/etc/permissions/privapp_allowlist_com.android.AppFooPriv.xml")
 }
 
 func TestApexWithAppImportBuildId(t *testing.T) {
@@ -6543,6 +6557,266 @@
 	}`)
 }
 
+func TestApexAvailable_ApexAvailableNameWithVersionCodeError(t *testing.T) {
+	t.Run("negative variant_version produces error", func(t *testing.T) {
+		testApexError(t, "expected an integer between 0-9; got -1", `
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				apex_available_name: "com.android.foo",
+				variant_version: "-1",
+				updatable: false,
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+		`)
+	})
+
+	t.Run("variant_version greater than 9 produces error", func(t *testing.T) {
+		testApexError(t, "expected an integer between 0-9; got 10", `
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				apex_available_name: "com.android.foo",
+				variant_version: "10",
+				updatable: false,
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+		`)
+	})
+}
+
+func TestApexAvailable_ApexAvailableNameWithVersionCode(t *testing.T) {
+	context := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		PrepareForTestWithApexBuildComponents,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/foo-file_contexts": nil,
+			"system/sepolicy/apex/bar-file_contexts": nil,
+		}),
+	)
+	result := context.RunTestWithBp(t, `
+		apex {
+			name: "foo",
+			key: "myapex.key",
+			apex_available_name: "com.android.foo",
+			variant_version: "0",
+			updatable: false,
+		}
+		apex {
+			name: "bar",
+			key: "myapex.key",
+			apex_available_name: "com.android.foo",
+			variant_version: "3",
+			updatable: false,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	fooManifestRule := result.ModuleForTests("foo", "android_common_foo_image").Rule("apexManifestRule")
+	fooExpectedDefaultVersion := android.DefaultUpdatableModuleVersion
+	fooActualDefaultVersion := fooManifestRule.Args["default_version"]
+	if fooActualDefaultVersion != fooExpectedDefaultVersion {
+		t.Errorf("expected to find defaultVersion %q; got %q", fooExpectedDefaultVersion, fooActualDefaultVersion)
+	}
+
+	barManifestRule := result.ModuleForTests("bar", "android_common_bar_image").Rule("apexManifestRule")
+	defaultVersionInt, _ := strconv.Atoi(android.DefaultUpdatableModuleVersion)
+	barExpectedDefaultVersion := fmt.Sprint(defaultVersionInt + 3)
+	barActualDefaultVersion := barManifestRule.Args["default_version"]
+	if barActualDefaultVersion != barExpectedDefaultVersion {
+		t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion)
+	}
+}
+
+func TestApexAvailable_ApexAvailableName(t *testing.T) {
+	t.Run("using name of apex that sets apex_available_name is not allowed", func(t *testing.T) {
+		testApexError(t, "Consider adding \"myapex\" to 'apex_available' property of \"AppFoo\"", `
+			apex {
+				name: "myapex_sminus",
+				key: "myapex.key",
+				apps: ["AppFoo"],
+				apex_available_name: "myapex",
+				updatable: false,
+			}
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				apps: ["AppFoo"],
+				updatable: false,
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+			android_app {
+				name: "AppFoo",
+				srcs: ["foo/bar/MyClass.java"],
+				sdk_version: "none",
+				system_modules: "none",
+				apex_available: [ "myapex_sminus" ],
+			}`,
+			android.FixtureMergeMockFs(android.MockFS{
+				"system/sepolicy/apex/myapex_sminus-file_contexts": nil,
+			}),
+		)
+	})
+
+	t.Run("apex_available_name allows module to be used in two different apexes", func(t *testing.T) {
+		testApex(t, `
+			apex {
+				name: "myapex_sminus",
+				key: "myapex.key",
+				apps: ["AppFoo"],
+				apex_available_name: "myapex",
+				updatable: false,
+			}
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				apps: ["AppFoo"],
+				updatable: false,
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+			android_app {
+				name: "AppFoo",
+				srcs: ["foo/bar/MyClass.java"],
+				sdk_version: "none",
+				system_modules: "none",
+				apex_available: [ "myapex" ],
+			}`,
+			android.FixtureMergeMockFs(android.MockFS{
+				"system/sepolicy/apex/myapex_sminus-file_contexts": nil,
+			}),
+		)
+	})
+
+	t.Run("override_apexes work with apex_available_name", func(t *testing.T) {
+		testApex(t, `
+			override_apex {
+				name: "myoverrideapex_sminus",
+				base: "myapex_sminus",
+				key: "myapex.key",
+				apps: ["AppFooOverride"],
+			}
+			override_apex {
+				name: "myoverrideapex",
+				base: "myapex",
+				key: "myapex.key",
+				apps: ["AppFooOverride"],
+			}
+			apex {
+				name: "myapex_sminus",
+				key: "myapex.key",
+				apps: ["AppFoo"],
+				apex_available_name: "myapex",
+				updatable: false,
+			}
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				apps: ["AppFoo"],
+				updatable: false,
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+			android_app {
+				name: "AppFooOverride",
+				srcs: ["foo/bar/MyClass.java"],
+				sdk_version: "none",
+				system_modules: "none",
+				apex_available: [ "myapex" ],
+			}
+			android_app {
+				name: "AppFoo",
+				srcs: ["foo/bar/MyClass.java"],
+				sdk_version: "none",
+				system_modules: "none",
+				apex_available: [ "myapex" ],
+			}`,
+			android.FixtureMergeMockFs(android.MockFS{
+				"system/sepolicy/apex/myapex_sminus-file_contexts": nil,
+			}),
+		)
+	})
+}
+
+func TestApexAvailable_ApexAvailableNameWithOverrides(t *testing.T) {
+	context := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		PrepareForTestWithApexBuildComponents,
+		java.PrepareForTestWithDexpreopt,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/myapex-file_contexts":        nil,
+			"system/sepolicy/apex/myapex_sminus-file_contexts": nil,
+		}),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildId = proptools.StringPtr("buildid")
+		}),
+	)
+	context.RunTestWithBp(t, `
+	override_apex {
+		name: "myoverrideapex_sminus",
+		base: "myapex_sminus",
+	}
+	override_apex {
+		name: "myoverrideapex",
+		base: "myapex",
+	}
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		apps: ["AppFoo"],
+		updatable: false,
+	}
+	apex {
+		name: "myapex_sminus",
+		apex_available_name: "myapex",
+		key: "myapex.key",
+		apps: ["AppFoo_sminus"],
+		updatable: false,
+	}
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+	android_app {
+		name: "AppFoo",
+		srcs: ["foo/bar/MyClass.java"],
+		sdk_version: "none",
+		system_modules: "none",
+		apex_available: [ "myapex" ],
+	}
+	android_app {
+		name: "AppFoo_sminus",
+		srcs: ["foo/bar/MyClass.java"],
+		sdk_version: "none",
+		min_sdk_version: "29",
+		system_modules: "none",
+		apex_available: [ "myapex" ],
+	}`)
+}
+
 func TestApexAvailable_CheckForPlatform(t *testing.T) {
 	ctx := testApex(t, `
 	apex {
diff --git a/apex/builder.go b/apex/builder.go
index 3c7671b..f49bed9 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -275,6 +275,22 @@
 
 	manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json")
 	defaultVersion := android.DefaultUpdatableModuleVersion
+	if a.properties.Variant_version != nil {
+		defaultVersionInt, err := strconv.Atoi(defaultVersion)
+		if err != nil {
+			ctx.ModuleErrorf("expected DefaultUpdatableModuleVersion to be an int, but got %s", defaultVersion)
+		}
+		if defaultVersionInt%10 != 0 {
+			ctx.ModuleErrorf("expected DefaultUpdatableModuleVersion to end in a zero, but got %s", defaultVersion)
+		}
+		variantVersion := []rune(*a.properties.Variant_version)
+		if len(variantVersion) != 1 || variantVersion[0] < '0' || variantVersion[0] > '9' {
+			ctx.PropertyErrorf("variant_version", "expected an integer between 0-9; got %s", *a.properties.Variant_version)
+		}
+		defaultVersionRunes := []rune(defaultVersion)
+		defaultVersionRunes[len(defaultVersion)-1] = []rune(variantVersion)[0]
+		defaultVersion = string(defaultVersionRunes)
+	}
 	if override := ctx.Config().Getenv("OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION"); override != "" {
 		defaultVersion = override
 	}
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index b6635c4..9ec3a40 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -19,6 +19,7 @@
         "testing.go",
     ],
     deps: [
+        "soong-aidl-library",
         "soong-android",
         "soong-android-allowlists",
         "soong-android-soongconfig",
@@ -37,6 +38,7 @@
     ],
     testSrcs: [
         "aar_conversion_test.go",
+        "aidl_library_conversion_test.go",
         "android_app_certificate_conversion_test.go",
         "android_app_conversion_test.go",
         "apex_conversion_test.go",
diff --git a/bp2build/aidl_library_conversion_test.go b/bp2build/aidl_library_conversion_test.go
new file mode 100644
index 0000000..0522da4
--- /dev/null
+++ b/bp2build/aidl_library_conversion_test.go
@@ -0,0 +1,119 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+	"testing"
+
+	"android/soong/aidl_library"
+	"android/soong/android"
+)
+
+func runAidlLibraryTestCase(t *testing.T, tc Bp2buildTestCase) {
+	t.Helper()
+	(&tc).ModuleTypeUnderTest = "aidl_library"
+	(&tc).ModuleTypeUnderTestFactory = aidl_library.AidlLibraryFactory
+	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
+}
+
+func TestAidlLibrary(t *testing.T) {
+	testcases := []struct {
+		name               string
+		bp                 string
+		expectedBazelAttrs AttrNameToString
+	}{
+		{
+			name: "aidl_library with strip_import_prefix",
+			bp: `
+	aidl_library {
+		name: "foo",
+		srcs: ["aidl/foo.aidl"],
+		hdrs: ["aidl/header.aidl"],
+		strip_import_prefix: "aidl",
+	}`,
+			expectedBazelAttrs: AttrNameToString{
+				"srcs":                `["aidl/foo.aidl"]`,
+				"hdrs":                `["aidl/header.aidl"]`,
+				"strip_import_prefix": `"aidl"`,
+				"tags":                `["apex_available=//apex_available:anyapex"]`,
+			},
+		},
+		{
+			name: "aidl_library without strip_import_prefix",
+			bp: `
+	aidl_library {
+		name: "foo",
+		srcs: ["aidl/foo.aidl"],
+		hdrs: ["aidl/header.aidl"],
+	}`,
+			expectedBazelAttrs: AttrNameToString{
+				"srcs": `["aidl/foo.aidl"]`,
+				"hdrs": `["aidl/header.aidl"]`,
+				"tags": `["apex_available=//apex_available:anyapex"]`,
+			},
+		},
+	}
+
+	for _, test := range testcases {
+		t.Run(test.name, func(t *testing.T) {
+			expectedBazelTargets := []string{
+				MakeBazelTargetNoRestrictions("aidl_library", "foo", test.expectedBazelAttrs),
+			}
+			runAidlLibraryTestCase(t, Bp2buildTestCase{
+				Description:          test.name,
+				Blueprint:            test.bp,
+				ExpectedBazelTargets: expectedBazelTargets,
+			})
+		})
+	}
+}
+
+func TestAidlLibraryWithDeps(t *testing.T) {
+	bp := `
+	aidl_library {
+		name: "bar",
+		srcs: ["Bar.aidl"],
+		hdrs: ["aidl/BarHeader.aidl"],
+	}
+	aidl_library {
+		name: "foo",
+		srcs: ["aidl/Foo.aidl"],
+		hdrs: ["aidl/FooHeader.aidl"],
+		strip_import_prefix: "aidl",
+		deps: ["bar"],
+	}`
+
+	t.Run("aidl_library with deps", func(t *testing.T) {
+		expectedBazelTargets := []string{
+			MakeBazelTargetNoRestrictions("aidl_library", "bar", AttrNameToString{
+				"srcs": `["Bar.aidl"]`,
+				"hdrs": `["aidl/BarHeader.aidl"]`,
+				"tags": `["apex_available=//apex_available:anyapex"]`,
+			}),
+			MakeBazelTargetNoRestrictions("aidl_library", "foo", AttrNameToString{
+				"srcs":                `["aidl/Foo.aidl"]`,
+				"hdrs":                `["aidl/FooHeader.aidl"]`,
+				"strip_import_prefix": `"aidl"`,
+				"deps":                `[":bar"]`,
+				"tags":                `["apex_available=//apex_available:anyapex"]`,
+			}),
+		}
+		runAidlLibraryTestCase(t, Bp2buildTestCase{
+			Description:          "aidl_library with deps",
+			Blueprint:            bp,
+			ExpectedBazelTargets: expectedBazelTargets,
+		})
+	})
+}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 7165ac4..86aa4aa 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"testing"
 
+	"android/soong/aidl_library"
 	"android/soong/android"
 	"android/soong/cc"
 )
@@ -63,6 +64,7 @@
 	ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
 	ctx.RegisterModuleType("cc_prebuilt_library_static", cc.PrebuiltStaticLibraryFactory)
 	ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
+	ctx.RegisterModuleType("aidl_library", aidl_library.AidlLibraryFactory)
 }
 
 func TestCcLibrarySimple(t *testing.T) {
@@ -3310,6 +3312,51 @@
 	})
 }
 
+func TestCcLibraryWithAidlLibrary(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library with aidl_library",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+aidl_library {
+    name: "A_aidl",
+    srcs: ["aidl/A.aidl"],
+	hdrs: ["aidl/Header.aidl"],
+	strip_import_prefix: "aidl",
+}
+cc_library {
+	name: "foo",
+	aidl: {
+		libs: ["A_aidl"],
+	},
+	export_include_dirs: ["include"],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTargetNoRestrictions("aidl_library", "A_aidl", AttrNameToString{
+				"srcs":                `["aidl/A.aidl"]`,
+				"hdrs":                `["aidl/Header.aidl"]`,
+				"strip_import_prefix": `"aidl"`,
+				"tags":                `["apex_available=//apex_available:anyapex"]`,
+			}),
+			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+				"deps":            `[":A_aidl"]`,
+				"local_includes":  `["."]`,
+				"export_includes": `["include"]`,
+			}),
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
+				"local_includes":                    `["."]`,
+				"export_includes":                   `["include"]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
+				"local_includes":                    `["."]`,
+				"export_includes":                   `["include"]`,
+			}),
+		},
+	})
+}
+
 func TestCcLibraryWithAidlSrcs(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		Description:                "cc_library with aidl srcs",
@@ -3338,6 +3385,7 @@
 				"srcs": `["B.aidl"]`,
 			}),
 			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+				"local_includes": `["."]`,
 				"deps": `[
         ":A_aidl",
         ":foo_aidl_library",
@@ -3377,7 +3425,8 @@
 }`,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
-				"deps": `["//path/to/A:A_aidl"]`,
+				"local_includes": `["."]`,
+				"deps":           `["//path/to/A:A_aidl"]`,
 			}),
 			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
 				"implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
@@ -3392,37 +3441,78 @@
 }
 
 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":     `["."]`,
-			}),
-			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
-				"whole_archive_deps": `[":foo_cc_aidl_library"]`,
-				"local_includes":     `["."]`,
-			}),
+	t.Parallel()
+
+	expectedBazelTargets := []string{
+		MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+			"local_includes": `["."]`,
+			"deps":           `[":foo_aidl_library"]`,
+		}),
+		MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+			"whole_archive_deps": `[":foo_cc_aidl_library"]`,
+			"local_includes":     `["."]`,
+		}),
+		MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+			"whole_archive_deps": `[":foo_cc_aidl_library"]`,
+			"local_includes":     `["."]`,
+		}),
+	}
+	testCases := []struct {
+		description          string
+		bp                   string
+		expectedBazelTargets []string
+	}{
+		{
+			description: "cc_library with aidl srcs and aidl.export_aidl_headers set",
+			bp: `
+			cc_library {
+				name: "foo",
+				srcs: [
+					"Foo.aidl",
+				],
+				aidl: {
+					export_aidl_headers: true,
+				}
+			}`,
+			expectedBazelTargets: append(
+				expectedBazelTargets,
+				MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{
+					"srcs": `["Foo.aidl"]`,
+				})),
 		},
-	})
+		{
+			description: "cc_library with aidl.libs and aidl.export_aidl_headers set",
+			bp: `
+			aidl_library {
+				name: "foo_aidl_library",
+				srcs: ["Foo.aidl"],
+			}
+			cc_library {
+				name: "foo",
+				aidl: {
+					libs: ["foo_aidl_library"],
+					export_aidl_headers: true,
+				}
+			}`,
+			expectedBazelTargets: append(
+				expectedBazelTargets,
+				MakeBazelTargetNoRestrictions("aidl_library", "foo_aidl_library", AttrNameToString{
+					"srcs": `["Foo.aidl"]`,
+					"tags": `["apex_available=//apex_available:anyapex"]`,
+				}),
+			),
+		},
+	}
+
+	for _, testCase := range testCases {
+		runCcLibraryTestCase(t, Bp2buildTestCase{
+			Description:                "cc_library with export aidl headers",
+			ModuleTypeUnderTest:        "cc_library",
+			ModuleTypeUnderTestFactory: cc.LibraryFactory,
+			Blueprint:                  testCase.bp,
+			ExpectedBazelTargets:       testCase.expectedBazelTargets,
+		})
+	}
 }
 
 func TestCcLibraryWithTargetApex(t *testing.T) {
@@ -3637,7 +3727,8 @@
 				"srcs": `["Foo.aidl"]`,
 			}),
 			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
-				"deps": `[":foo_aidl_library"]`,
+				"local_includes": `["."]`,
+				"deps":           `[":foo_aidl_library"]`,
 				"implementation_deps": `[
         ":baz-static",
         ":bar-static",
diff --git a/bp2build/filegroup_conversion_test.go b/bp2build/filegroup_conversion_test.go
index 273d556..7ce559d 100644
--- a/bp2build/filegroup_conversion_test.go
+++ b/bp2build/filegroup_conversion_test.go
@@ -105,42 +105,6 @@
 	}
 }
 
-func TestFilegroupWithAidlDeps(t *testing.T) {
-	bp := `
-	filegroup {
-		name: "bar",
-		srcs: ["bar.aidl"],
-	}
-	filegroup {
-		name: "foo",
-		srcs: ["aidl/foo.aidl"],
-		path: "aidl",
-		aidl: {
-			deps: [":bar"],
-		}
-	}`
-
-	t.Run("filegroup with aidl deps", func(t *testing.T) {
-		expectedBazelTargets := []string{
-			MakeBazelTargetNoRestrictions("aidl_library", "bar", AttrNameToString{
-				"srcs": `["bar.aidl"]`,
-				"tags": `["apex_available=//apex_available:anyapex"]`,
-			}),
-			MakeBazelTargetNoRestrictions("aidl_library", "foo", AttrNameToString{
-				"srcs":                `["aidl/foo.aidl"]`,
-				"strip_import_prefix": `"aidl"`,
-				"deps":                `[":bar"]`,
-				"tags":                `["apex_available=//apex_available:anyapex"]`,
-			}),
-		}
-		runFilegroupTestCase(t, Bp2buildTestCase{
-			Description:          "filegroup with aidl deps",
-			Blueprint:            bp,
-			ExpectedBazelTargets: expectedBazelTargets,
-		})
-	})
-}
-
 func TestFilegroupWithAidlAndNonAidlSrcs(t *testing.T) {
 	runFilegroupTestCase(t, Bp2buildTestCase{
 		Description: "filegroup with aidl and non-aidl srcs",
diff --git a/cc/Android.bp b/cc/Android.bp
index be2cc5a..f49dc1a 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -9,6 +9,7 @@
         "blueprint",
         "blueprint-pathtools",
         "soong",
+        "soong-aidl-library",
         "soong-android",
         "soong-bazel",
         "soong-cc-config",
@@ -22,7 +23,6 @@
     srcs: [
         "afdo.go",
         "fdo_profile.go",
-
         "androidmk.go",
         "api_level.go",
         "bp2build.go",
diff --git a/cc/binary.go b/cc/binary.go
index 097f822..98b9923 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -661,7 +661,7 @@
 	// shared with cc_test
 	binaryAttrs := binaryBp2buildAttrs(ctx, m)
 
-	tags := android.ApexAvailableTags(m)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, m)
 	ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
 		Rule_class:        "cc_binary",
 		Bzl_load_location: "//build/bazel/rules/cc:cc_binary.bzl",
diff --git a/cc/bp2build.go b/cc/bp2build.go
index adf5a08..5e1c2d3 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -268,7 +268,7 @@
 	attrs.Srcs_c = partitionedSrcs[cSrcPartition]
 	attrs.Srcs_as = partitionedSrcs[asSrcPartition]
 
-	attrs.Apex_available = android.ConvertApexAvailableToTags(apexAvailable)
+	attrs.Apex_available = android.ConvertApexAvailableToTagsWithoutTestApexes(ctx.(android.TopDownMutatorContext), apexAvailable)
 
 	if !partitionedSrcs[protoSrcPartition].IsEmpty() {
 		// TODO(b/208815215): determine whether this is used and add support if necessary
@@ -717,6 +717,8 @@
 	compilerAttrs := compilerAttributes{}
 	linkerAttrs := linkerAttributes{}
 
+	var aidlLibs bazel.LabelList
+
 	// Iterate through these axes in a deterministic order. This is required
 	// because processing certain dependencies may result in concatenating
 	// elements along other axes. (For example, processing NoConfig may result
@@ -732,6 +734,7 @@
 					compilerAttrs.lexopts.SetSelectValue(axis, cfg, baseCompilerProps.Lex.Flags)
 				}
 				(&compilerAttrs).bp2buildForAxisAndConfig(ctx, axis, cfg, baseCompilerProps)
+				aidlLibs.Append(android.BazelLabelForModuleDeps(ctx, baseCompilerProps.Aidl.Libs))
 			}
 
 			var exportHdrs []string
@@ -804,7 +807,15 @@
 	(&linkerAttrs).wholeArchiveDeps.Add(protoDep.wholeStaticLib)
 	(&linkerAttrs).implementationWholeArchiveDeps.Add(protoDep.implementationWholeStaticLib)
 
-	aidlDep := bp2buildCcAidlLibrary(ctx, module, compilerAttrs.aidlSrcs, linkerAttrs)
+	aidlDep := bp2buildCcAidlLibrary(
+		ctx, module,
+		compilerAttrs.aidlSrcs,
+		bazel.LabelListAttribute{
+			Value: aidlLibs,
+		},
+		linkerAttrs,
+		compilerAttrs,
+	)
 	if aidlDep != nil {
 		if lib, ok := module.linker.(*libraryDecorator); ok {
 			if proptools.Bool(lib.Properties.Aidl.Export_aidl_headers) {
@@ -900,11 +911,16 @@
 func bp2buildCcAidlLibrary(
 	ctx android.Bp2buildMutatorContext,
 	m *Module,
-	aidlLabelList bazel.LabelListAttribute,
+	aidlSrcs bazel.LabelListAttribute,
+	aidlLibs bazel.LabelListAttribute,
 	linkerAttrs linkerAttributes,
+	compilerAttrs compilerAttributes,
 ) *bazel.LabelAttribute {
-	if !aidlLabelList.IsEmpty() {
-		aidlLibs, aidlSrcs := aidlLabelList.Partition(func(src bazel.Label) bool {
+	var aidlLibsFromSrcs, aidlFiles bazel.LabelListAttribute
+	apexAvailableTags := android.ApexAvailableTagsWithoutTestApexes(ctx.(android.TopDownMutatorContext), ctx.Module())
+
+	if !aidlSrcs.IsEmpty() {
+		aidlLibsFromSrcs, aidlFiles = aidlSrcs.Partition(func(src bazel.Label) bool {
 			if fg, ok := android.ToFileGroupAsLibrary(ctx, src.OriginalModuleName); ok &&
 				fg.ShouldConvertToAidlLibrary(ctx) {
 				return true
@@ -912,57 +928,71 @@
 			return false
 		})
 
-		apexAvailableTags := android.ApexAvailableTags(ctx.Module())
-		sdkAttrs := bp2BuildParseSdkAttributes(m)
-
-		if !aidlSrcs.IsEmpty() {
+		if !aidlFiles.IsEmpty() {
 			aidlLibName := m.Name() + "_aidl_library"
 			ctx.CreateBazelTargetModule(
 				bazel.BazelTargetModuleProperties{
 					Rule_class:        "aidl_library",
 					Bzl_load_location: "//build/bazel/rules/aidl:aidl_library.bzl",
 				},
-				android.CommonAttributes{Name: aidlLibName},
-				&aidlLibraryAttributes{
-					Srcs: aidlSrcs,
+				android.CommonAttributes{
+					Name: aidlLibName,
 					Tags: apexAvailableTags,
 				},
-			)
-			aidlLibs.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + aidlLibName}})
-		}
-
-		if !aidlLibs.IsEmpty() {
-			ccAidlLibrarylabel := m.Name() + "_cc_aidl_library"
-			// Since parent cc_library already has these dependencies, we can add them as implementation
-			// deps so that they don't re-export
-			implementationDeps := linkerAttrs.deps.Clone()
-			implementationDeps.Append(linkerAttrs.implementationDeps)
-			implementationDynamicDeps := linkerAttrs.dynamicDeps.Clone()
-			implementationDynamicDeps.Append(linkerAttrs.implementationDynamicDeps)
-
-			ctx.CreateBazelTargetModule(
-				bazel.BazelTargetModuleProperties{
-					Rule_class:        "cc_aidl_library",
-					Bzl_load_location: "//build/bazel/rules/cc:cc_aidl_library.bzl",
-				},
-				android.CommonAttributes{Name: ccAidlLibrarylabel},
-				&ccAidlLibraryAttributes{
-					Deps:                        aidlLibs,
-					Implementation_deps:         *implementationDeps,
-					Implementation_dynamic_deps: *implementationDynamicDeps,
-					Tags:                        apexAvailableTags,
-					sdkAttributes:               sdkAttrs,
+				&aidlLibraryAttributes{
+					Srcs: aidlFiles,
 				},
 			)
-			label := &bazel.LabelAttribute{
-				Value: &bazel.Label{
-					Label: ":" + ccAidlLibrarylabel,
-				},
-			}
-			return label
+			aidlLibsFromSrcs.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + aidlLibName}})
 		}
 	}
 
+	allAidlLibs := aidlLibs.Clone()
+	allAidlLibs.Append(aidlLibsFromSrcs)
+
+	if !allAidlLibs.IsEmpty() {
+		ccAidlLibrarylabel := m.Name() + "_cc_aidl_library"
+		// Since parent cc_library already has these dependencies, we can add them as implementation
+		// deps so that they don't re-export
+		implementationDeps := linkerAttrs.deps.Clone()
+		implementationDeps.Append(linkerAttrs.implementationDeps)
+		implementationDynamicDeps := linkerAttrs.dynamicDeps.Clone()
+		implementationDynamicDeps.Append(linkerAttrs.implementationDynamicDeps)
+
+		sdkAttrs := bp2BuildParseSdkAttributes(m)
+
+		exportedIncludes := bp2BuildParseExportedIncludes(ctx, m, &compilerAttrs.includes)
+		includeAttrs := includesAttributes{
+			Export_includes:          exportedIncludes.Includes,
+			Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+			Export_system_includes:   exportedIncludes.SystemIncludes,
+			Local_includes:           compilerAttrs.localIncludes,
+			Absolute_includes:        compilerAttrs.absoluteIncludes,
+		}
+
+		ctx.CreateBazelTargetModule(
+			bazel.BazelTargetModuleProperties{
+				Rule_class:        "cc_aidl_library",
+				Bzl_load_location: "//build/bazel/rules/cc:cc_aidl_library.bzl",
+			},
+			android.CommonAttributes{Name: ccAidlLibrarylabel},
+			&ccAidlLibraryAttributes{
+				Deps:                        *allAidlLibs,
+				Implementation_deps:         *implementationDeps,
+				Implementation_dynamic_deps: *implementationDynamicDeps,
+				Tags:                        apexAvailableTags,
+				sdkAttributes:               sdkAttrs,
+				includesAttributes:          includeAttrs,
+			},
+		)
+		label := &bazel.LabelAttribute{
+			Value: &bazel.Label{
+				Label: ":" + ccAidlLibrarylabel,
+			},
+		}
+		return label
+	}
+
 	return nil
 }
 
diff --git a/cc/cc.go b/cc/cc.go
index 0addb60..307441d 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -27,6 +27,7 @@
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
+	"android/soong/aidl_library"
 	"android/soong/android"
 	"android/soong/bazel/cquery"
 	"android/soong/cc/config"
@@ -110,6 +111,9 @@
 	// Used by DepsMutator to pass system_shared_libs information to check_elf_file.py.
 	SystemSharedLibs []string
 
+	// Used by DepMutator to pass aidl_library modules to aidl compiler
+	AidlLibs []string
+
 	// If true, statically link the unwinder into native libraries/binaries.
 	StaticUnwinderIfLegacy bool
 
@@ -182,6 +186,9 @@
 	// For Darwin builds, the path to the second architecture's output that should
 	// be combined with this architectures's output into a FAT MachO file.
 	DarwinSecondArchOutput android.OptionalPath
+
+	// Paths to direct srcs and transitive include dirs from direct aidl_library deps
+	AidlLibraryInfos []aidl_library.AidlLibraryInfo
 }
 
 // LocalOrGlobalFlags contains flags that need to have values set globally by the build system or locally by the module
@@ -765,6 +772,7 @@
 	stubImplDepTag        = dependencyTag{name: "stub_impl"}
 	JniFuzzLibTag         = dependencyTag{name: "jni_fuzz_lib_tag"}
 	FdoProfileTag         = dependencyTag{name: "fdo_profile"}
+	aidlLibraryTag        = dependencyTag{name: "aidl_library"}
 )
 
 func IsSharedDepTag(depTag blueprint.DependencyTag) bool {
@@ -2685,6 +2693,14 @@
 		}
 	}
 
+	if len(deps.AidlLibs) > 0 {
+		actx.AddDependency(
+			c,
+			aidlLibraryTag,
+			deps.AidlLibs...,
+		)
+	}
+
 	updateImportedLibraryDependency(ctx)
 }
 
@@ -2989,6 +3005,17 @@
 			return
 		}
 
+		if depTag == aidlLibraryTag {
+			if ctx.OtherModuleHasProvider(dep, aidl_library.AidlLibraryProvider) {
+				depPaths.AidlLibraryInfos = append(
+					depPaths.AidlLibraryInfos,
+					ctx.OtherModuleProvider(
+						dep,
+						aidl_library.AidlLibraryProvider).(aidl_library.AidlLibraryInfo),
+				)
+			}
+		}
+
 		ccDep, ok := dep.(LinkableInterface)
 		if !ok {
 
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 3ae4b15..d6b969a 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -24,6 +24,7 @@
 	"strings"
 	"testing"
 
+	"android/soong/aidl_library"
 	"android/soong/android"
 	"android/soong/bazel/cquery"
 )
@@ -4419,9 +4420,80 @@
 	}
 }
 
+func TestAidlLibraryWithHeaders(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		prepareForCcTest,
+		aidl_library.PrepareForTestWithAidlLibrary,
+		android.MockFS{
+			"package_bar/Android.bp": []byte(`
+			aidl_library {
+				name: "bar",
+				srcs: ["x/y/Bar.aidl"],
+				hdrs: ["x/HeaderBar.aidl"],
+				strip_import_prefix: "x",
+			}
+			`)}.AddToFixture(),
+		android.MockFS{
+			"package_foo/Android.bp": []byte(`
+			aidl_library {
+				name: "foo",
+				srcs: ["a/b/Foo.aidl"],
+				hdrs: ["a/HeaderFoo.aidl"],
+				strip_import_prefix: "a",
+				deps: ["bar"],
+			}
+			cc_library {
+				name: "libfoo",
+				aidl: {
+					libs: ["foo"],
+				}
+			}
+			`),
+		}.AddToFixture(),
+	).RunTest(t).TestContext
+
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"aidl headers",
+		[]string{
+			"package_bar/x/HeaderBar.aidl",
+			"package_foo/a/HeaderFoo.aidl",
+			"package_foo/a/b/Foo.aidl",
+			"out/soong/.intermediates/package_foo/libfoo/android_arm64_armv8-a_static/gen/aidl_library.sbox.textproto",
+		},
+		libfoo.Rule("aidl_library").Implicits,
+	)
+
+	manifest := android.RuleBuilderSboxProtoForTests(t, libfoo.Output("aidl_library.sbox.textproto"))
+	aidlCommand := manifest.Commands[0].GetCommand()
+
+	expectedAidlFlags := "-Ipackage_foo/a -Ipackage_bar/x"
+	if !strings.Contains(aidlCommand, expectedAidlFlags) {
+		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlags)
+	}
+
+	outputs := strings.Join(libfoo.AllOutputs(), " ")
+
+	android.AssertStringDoesContain(t, "aidl-generated header", outputs, "gen/aidl_library/b/BpFoo.h")
+	android.AssertStringDoesContain(t, "aidl-generated header", outputs, "gen/aidl_library/b/BnFoo.h")
+	android.AssertStringDoesContain(t, "aidl-generated header", outputs, "gen/aidl_library/b/Foo.h")
+	android.AssertStringDoesContain(t, "aidl-generated cpp", outputs, "b/Foo.cpp")
+	// Confirm that the aidl header doesn't get compiled to cpp and h files
+	android.AssertStringDoesNotContain(t, "aidl-generated header", outputs, "gen/aidl_library/y/BpBar.h")
+	android.AssertStringDoesNotContain(t, "aidl-generated header", outputs, "gen/aidl_library/y/BnBar.h")
+	android.AssertStringDoesNotContain(t, "aidl-generated header", outputs, "gen/aidl_library/y/Bar.h")
+	android.AssertStringDoesNotContain(t, "aidl-generated cpp", outputs, "y/Bar.cpp")
+}
+
 func TestAidlFlagsPassedToTheAidlCompiler(t *testing.T) {
 	t.Parallel()
-	ctx := testCc(t, `
+	ctx := android.GroupFixturePreparers(
+		prepareForCcTest,
+		aidl_library.PrepareForTestWithAidlLibrary,
+	).RunTestWithBp(t, `
 		cc_library {
 			name: "libfoo",
 			srcs: ["a/Foo.aidl"],
@@ -4491,6 +4563,55 @@
 	}
 }
 
+func TestInvalidAidlProp(t *testing.T) {
+	t.Parallel()
+
+	testCases := []struct {
+		description string
+		bp          string
+	}{
+		{
+			description: "Invalid use of aidl.libs and aidl.include_dirs",
+			bp: `
+			cc_library {
+				name: "foo",
+				aidl: {
+					libs: ["foo_aidl"],
+					include_dirs: ["bar/include"],
+				}
+			}
+			`,
+		},
+		{
+			description: "Invalid use of aidl.libs and aidl.local_include_dirs",
+			bp: `
+			cc_library {
+				name: "foo",
+				aidl: {
+					libs: ["foo_aidl"],
+					local_include_dirs: ["include"],
+				}
+			}
+			`,
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.description, func(t *testing.T) {
+			bp := `
+			aidl_library {
+				name: "foo_aidl",
+				srcs: ["Foo.aidl"],
+			} ` + testCase.bp
+			android.GroupFixturePreparers(
+				prepareForCcTest,
+				aidl_library.PrepareForTestWithAidlLibrary.
+					ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("For aidl headers, please only use aidl.libs prop")),
+			).RunTestWithBp(t, bp)
+		})
+	}
+}
+
 func TestMinSdkVersionInClangTriple(t *testing.T) {
 	t.Parallel()
 	ctx := testCc(t, `
@@ -4706,7 +4827,15 @@
 	})
 
 	t.Run("ensure only aidl headers are exported", func(t *testing.T) {
-		ctx := testCc(t, genRuleModules+`
+		ctx := android.GroupFixturePreparers(
+			prepareForCcTest,
+			aidl_library.PrepareForTestWithAidlLibrary,
+		).RunTestWithBp(t, `
+		aidl_library {
+			name: "libfoo_aidl",
+			srcs: ["x/y/Bar.aidl"],
+			strip_import_prefix: "x",
+		}
 		cc_library_shared {
 			name: "libfoo",
 			srcs: [
@@ -4715,25 +4844,33 @@
 				"a.proto",
 			],
 			aidl: {
+				libs: ["libfoo_aidl"],
 				export_aidl_headers: true,
 			}
 		}
-		`)
+		`).TestContext
 		foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
 		checkIncludeDirs(t, ctx, foo,
 			expectedIncludeDirs(`
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library
 			`),
 			expectedSystemIncludeDirs(``),
 			expectedGeneratedHeaders(`
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/b.h
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bnb.h
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bpb.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/Bar.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/BnBar.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/BpBar.h
 			`),
 			expectedOrderOnlyDeps(`
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/b.h
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bnb.h
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bpb.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/Bar.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/BnBar.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/BpBar.h
 			`),
 		)
 	})
diff --git a/cc/compiler.go b/cc/compiler.go
index 88985b6..16f4a6e 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -120,6 +120,9 @@
 	Lex  *LexProperties
 
 	Aidl struct {
+		// List of aidl_library modules
+		Libs []string
+
 		// list of directories that will be added to the aidl include paths.
 		Include_dirs []string
 
@@ -272,6 +275,7 @@
 	deps.GeneratedSources = append(deps.GeneratedSources, compiler.Properties.Generated_sources...)
 	deps.GeneratedSources = removeListFromList(deps.GeneratedSources, compiler.Properties.Exclude_generated_sources)
 	deps.GeneratedHeaders = append(deps.GeneratedHeaders, compiler.Properties.Generated_headers...)
+	deps.AidlLibs = append(deps.AidlLibs, compiler.Properties.Aidl.Libs...)
 
 	android.ProtoDeps(ctx, &compiler.Proto)
 	if compiler.hasSrcExt(".proto") {
@@ -561,7 +565,12 @@
 			"-I"+android.PathForModuleGen(ctx, "yacc", ctx.ModuleDir()).String())
 	}
 
-	if compiler.hasSrcExt(".aidl") {
+	if len(compiler.Properties.Aidl.Libs) > 0 &&
+		(len(compiler.Properties.Aidl.Include_dirs) > 0 || len(compiler.Properties.Aidl.Local_include_dirs) > 0) {
+		ctx.ModuleErrorf("aidl.libs and (aidl.include_dirs or aidl.local_include_dirs) can't be set at the same time. For aidl headers, please only use aidl.libs prop")
+	}
+
+	if compiler.hasAidl(deps) {
 		flags.aidlFlags = append(flags.aidlFlags, compiler.Properties.Aidl.Flags...)
 		if len(compiler.Properties.Aidl.Local_include_dirs) > 0 {
 			localAidlIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Aidl.Local_include_dirs)
@@ -572,6 +581,14 @@
 			flags.aidlFlags = append(flags.aidlFlags, includeDirsToFlags(rootAidlIncludeDirs))
 		}
 
+		var rootAidlIncludeDirs android.Paths
+		for _, aidlLibraryInfo := range deps.AidlLibraryInfos {
+			rootAidlIncludeDirs = append(rootAidlIncludeDirs, aidlLibraryInfo.IncludeDirs.ToList()...)
+		}
+		if len(rootAidlIncludeDirs) > 0 {
+			flags.aidlFlags = append(flags.aidlFlags, includeDirsToFlags(rootAidlIncludeDirs))
+		}
+
 		if proptools.BoolDefault(compiler.Properties.Aidl.Generate_traces, true) {
 			flags.aidlFlags = append(flags.aidlFlags, "-t")
 		}
@@ -582,8 +599,14 @@
 		}
 		flags.aidlFlags = append(flags.aidlFlags, "--min_sdk_version="+aidlMinSdkVersion)
 
-		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
-			"-I"+android.PathForModuleGen(ctx, "aidl").String())
+		if compiler.hasSrcExt(".aidl") {
+			flags.Local.CommonFlags = append(flags.Local.CommonFlags,
+				"-I"+android.PathForModuleGen(ctx, "aidl").String())
+		}
+		if len(deps.AidlLibraryInfos) > 0 {
+			flags.Local.CommonFlags = append(flags.Local.CommonFlags,
+				"-I"+android.PathForModuleGen(ctx, "aidl_library").String())
+		}
 	}
 
 	if compiler.hasSrcExt(".rscript") || compiler.hasSrcExt(".fs") {
@@ -660,6 +683,10 @@
 	return nil
 }
 
+func (compiler *baseCompiler) hasAidl(deps PathDeps) bool {
+	return len(deps.AidlLibraryInfos) > 0 || compiler.hasSrcExt(".aidl")
+}
+
 func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
 	pathDeps := deps.GeneratedDeps
 	pathDeps = append(pathDeps, ndkPathDeps(ctx)...)
@@ -668,7 +695,7 @@
 
 	srcs := append(android.Paths(nil), compiler.srcsBeforeGen...)
 
-	srcs, genDeps, info := genSources(ctx, srcs, buildFlags)
+	srcs, genDeps, info := genSources(ctx, deps.AidlLibraryInfos, srcs, buildFlags)
 	pathDeps = append(pathDeps, genDeps...)
 
 	compiler.pathDeps = pathDeps
diff --git a/cc/gen.go b/cc/gen.go
index dfbb177..b15f164 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -18,7 +18,9 @@
 	"path/filepath"
 	"strings"
 
+	"android/soong/aidl_library"
 	"android/soong/bazel"
+
 	"github.com/google/blueprint"
 
 	"android/soong/android"
@@ -105,7 +107,14 @@
 	return ret
 }
 
-func genAidl(ctx android.ModuleContext, rule *android.RuleBuilder, aidlFile android.Path, aidlFlags string) (cppFile android.OutputPath, headerFiles android.Paths) {
+func genAidl(
+	ctx android.ModuleContext,
+	rule *android.RuleBuilder,
+	outDirBase string,
+	aidlFile android.Path,
+	aidlHdrs android.Paths,
+	aidlFlags string,
+) (cppFile android.OutputPath, headerFiles android.Paths) {
 	aidlPackage := strings.TrimSuffix(aidlFile.Rel(), aidlFile.Base())
 	baseName := strings.TrimSuffix(aidlFile.Base(), aidlFile.Ext())
 	shortName := baseName
@@ -117,20 +126,17 @@
 		shortName = strings.TrimPrefix(baseName, "I")
 	}
 
-	outDir := android.PathForModuleGen(ctx, "aidl")
+	outDir := android.PathForModuleGen(ctx, outDirBase)
 	cppFile = outDir.Join(ctx, aidlPackage, baseName+".cpp")
 	depFile := outDir.Join(ctx, aidlPackage, baseName+".cpp.d")
 	headerI := outDir.Join(ctx, aidlPackage, baseName+".h")
 	headerBn := outDir.Join(ctx, aidlPackage, "Bn"+shortName+".h")
 	headerBp := outDir.Join(ctx, aidlPackage, "Bp"+shortName+".h")
 
-	baseDir := strings.TrimSuffix(aidlFile.String(), aidlFile.Rel())
-	if baseDir != "" {
-		aidlFlags += " -I" + baseDir
-	}
-
 	cmd := rule.Command()
 	cmd.BuiltTool("aidl-cpp").
+		// libc++ is default stl for aidl-cpp (a cc_binary_host module)
+		ImplicitTool(ctx.Config().HostCcSharedLibPath(ctx, "libc++")).
 		FlagWithDepFile("-d", depFile).
 		Flag("--ninja").
 		Flag(aidlFlags).
@@ -143,6 +149,10 @@
 			headerBp,
 		})
 
+	if aidlHdrs != nil {
+		cmd.Implicits(aidlHdrs)
+	}
+
 	return cppFile, android.Paths{
 		headerI,
 		headerBn,
@@ -282,15 +292,23 @@
 	syspropOrderOnlyDeps android.Paths
 }
 
-func genSources(ctx android.ModuleContext, srcFiles android.Paths,
-	buildFlags builderFlags) (android.Paths, android.Paths, generatedSourceInfo) {
+func genSources(
+	ctx android.ModuleContext,
+	aidlLibraryInfos []aidl_library.AidlLibraryInfo,
+	srcFiles android.Paths,
+	buildFlags builderFlags,
+) (android.Paths, android.Paths, generatedSourceInfo) {
 
 	var info generatedSourceInfo
 
 	var deps android.Paths
 	var rsFiles android.Paths
 
+	// aidlRule supports compiling aidl files from srcs prop while aidlLibraryRule supports
+	// compiling aidl files from aidl_library modules specified in aidl.libs prop.
+	// The rules are separated so that they don't wipe out the other's outputDir
 	var aidlRule *android.RuleBuilder
+	var aidlLibraryRule *android.RuleBuilder
 
 	var yaccRule_ *android.RuleBuilder
 	yaccRule := func() *android.RuleBuilder {
@@ -330,7 +348,15 @@
 				aidlRule = android.NewRuleBuilder(pctx, ctx).Sbox(android.PathForModuleGen(ctx, "aidl"),
 					android.PathForModuleGen(ctx, "aidl.sbox.textproto"))
 			}
-			cppFile, aidlHeaders := genAidl(ctx, aidlRule, srcFile, buildFlags.aidlFlags)
+			baseDir := strings.TrimSuffix(srcFile.String(), srcFile.Rel())
+			cppFile, aidlHeaders := genAidl(
+				ctx,
+				aidlRule,
+				"aidl",
+				srcFile,
+				nil,
+				buildFlags.aidlFlags+" -I"+baseDir,
+			)
 			srcFiles[i] = cppFile
 
 			info.aidlHeaders = append(info.aidlHeaders, aidlHeaders...)
@@ -352,10 +378,40 @@
 		}
 	}
 
+	for _, aidlLibraryInfo := range aidlLibraryInfos {
+		if aidlLibraryRule == nil {
+			aidlLibraryRule = android.NewRuleBuilder(pctx, ctx).Sbox(
+				android.PathForModuleGen(ctx, "aidl_library"),
+				android.PathForModuleGen(ctx, "aidl_library.sbox.textproto"),
+			).SandboxInputs()
+		}
+		for _, aidlSrc := range aidlLibraryInfo.Srcs {
+			cppFile, aidlHeaders := genAidl(
+				ctx,
+				aidlLibraryRule,
+				"aidl_library",
+				aidlSrc,
+				aidlLibraryInfo.Hdrs.ToList(),
+				buildFlags.aidlFlags,
+			)
+
+			srcFiles = append(srcFiles, cppFile)
+			info.aidlHeaders = append(info.aidlHeaders, aidlHeaders...)
+			// Use the generated headers as order only deps to ensure that they are up to date when
+			// needed.
+			// TODO: Reduce the size of the ninja file by using one order only dep for the whole rule
+			info.aidlOrderOnlyDeps = append(info.aidlOrderOnlyDeps, aidlHeaders...)
+		}
+	}
+
 	if aidlRule != nil {
 		aidlRule.Build("aidl", "gen aidl")
 	}
 
+	if aidlLibraryRule != nil {
+		aidlLibraryRule.Build("aidl_library", "gen aidl_library")
+	}
+
 	if yaccRule_ != nil {
 		yaccRule_.Build("yacc", "gen yacc")
 	}
diff --git a/cc/library.go b/cc/library.go
index 7051f72..d5f5b40 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -271,7 +271,9 @@
 	Implementation_deps         bazel.LabelListAttribute
 	Implementation_dynamic_deps bazel.LabelListAttribute
 	Tags                        bazel.StringListAttribute
+
 	sdkAttributes
+	includesAttributes
 }
 
 type stripAttributes struct {
@@ -330,6 +332,14 @@
 		Native_coverage:                   baseAttributes.Native_coverage,
 	}
 
+	includeAttrs := includesAttributes{
+		Export_includes:          exportedIncludes.Includes,
+		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+		Export_system_includes:   exportedIncludes.SystemIncludes,
+		Local_includes:           compilerAttrs.localIncludes,
+		Absolute_includes:        compilerAttrs.absoluteIncludes,
+	}
+
 	sharedCommonAttrs := staticOrSharedAttributes{
 		Srcs:    *srcs.Clone().Append(sharedAttrs.Srcs),
 		Srcs_c:  *compilerAttrs.cSrcs.Clone().Append(sharedAttrs.Srcs_c),
@@ -351,41 +361,34 @@
 
 	staticTargetAttrs := &bazelCcLibraryStaticAttributes{
 		staticOrSharedAttributes: staticCommonAttrs,
+		includesAttributes:       includeAttrs,
 
 		Cppflags:   compilerAttrs.cppFlags,
 		Conlyflags: compilerAttrs.conlyFlags,
 		Asflags:    asFlags,
 
-		Export_includes:          exportedIncludes.Includes,
-		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
-		Export_system_includes:   exportedIncludes.SystemIncludes,
-		Local_includes:           compilerAttrs.localIncludes,
-		Absolute_includes:        compilerAttrs.absoluteIncludes,
-		Rtti:                     compilerAttrs.rtti,
-		Stl:                      compilerAttrs.stl,
-		Cpp_std:                  compilerAttrs.cppStd,
-		C_std:                    compilerAttrs.cStd,
+		Rtti:    compilerAttrs.rtti,
+		Stl:     compilerAttrs.stl,
+		Cpp_std: compilerAttrs.cppStd,
+		C_std:   compilerAttrs.cStd,
 
 		Features: baseAttributes.features,
 	}
 
 	sharedTargetAttrs := &bazelCcLibrarySharedAttributes{
 		staticOrSharedAttributes: sharedCommonAttrs,
-		Cppflags:                 compilerAttrs.cppFlags,
-		Conlyflags:               compilerAttrs.conlyFlags,
-		Asflags:                  asFlags,
+		includesAttributes:       includeAttrs,
 
-		Export_includes:          exportedIncludes.Includes,
-		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
-		Export_system_includes:   exportedIncludes.SystemIncludes,
-		Local_includes:           compilerAttrs.localIncludes,
-		Absolute_includes:        compilerAttrs.absoluteIncludes,
-		Linkopts:                 linkerAttrs.linkopts,
-		Rtti:                     compilerAttrs.rtti,
-		Stl:                      compilerAttrs.stl,
-		Cpp_std:                  compilerAttrs.cppStd,
-		C_std:                    compilerAttrs.cStd,
-		Use_version_lib:          linkerAttrs.useVersionLib,
+		Cppflags:   compilerAttrs.cppFlags,
+		Conlyflags: compilerAttrs.conlyFlags,
+		Asflags:    asFlags,
+
+		Linkopts:        linkerAttrs.linkopts,
+		Rtti:            compilerAttrs.rtti,
+		Stl:             compilerAttrs.stl,
+		Cpp_std:         compilerAttrs.cppStd,
+		C_std:           compilerAttrs.cStd,
+		Use_version_lib: linkerAttrs.useVersionLib,
 
 		Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,
 
@@ -428,11 +431,11 @@
 
 	var tagsForStaticVariant bazel.StringListAttribute
 	if compilerAttrs.stubsSymbolFile == nil && len(compilerAttrs.stubsVersions.Value) == 0 {
-		tagsForStaticVariant = android.ApexAvailableTags(m)
+		tagsForStaticVariant = android.ApexAvailableTagsWithoutTestApexes(ctx, m)
 	}
 	tagsForStaticVariant.Append(bazel.StringListAttribute{Value: staticAttrs.Apex_available})
 
-	tagsForSharedVariant := android.ApexAvailableTags(m)
+	tagsForSharedVariant := android.ApexAvailableTagsWithoutTestApexes(ctx, m)
 	tagsForSharedVariant.Append(bazel.StringListAttribute{Value: sharedAttrs.Apex_available})
 
 	ctx.CreateBazelTargetModuleWithRestrictions(staticProps,
@@ -2109,9 +2112,15 @@
 
 	// Optionally export aidl headers.
 	if Bool(library.Properties.Aidl.Export_aidl_headers) {
-		if library.baseCompiler.hasSrcExt(".aidl") {
-			dir := android.PathForModuleGen(ctx, "aidl")
-			library.reexportDirs(dir)
+		if library.baseCompiler.hasAidl(deps) {
+			if library.baseCompiler.hasSrcExt(".aidl") {
+				dir := android.PathForModuleGen(ctx, "aidl")
+				library.reexportDirs(dir)
+			}
+			if len(deps.AidlLibraryInfos) > 0 {
+				dir := android.PathForModuleGen(ctx, "aidl_library")
+				library.reexportDirs(dir)
+			}
 
 			library.reexportDeps(library.baseCompiler.aidlOrderOnlyDeps...)
 			library.addExportedGeneratedHeaders(library.baseCompiler.aidlHeaders...)
@@ -2875,6 +2884,13 @@
 	linkerAttrs := baseAttributes.linkerAttributes
 
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, &compilerAttrs.includes)
+	includeAttrs := includesAttributes{
+		Export_includes:          exportedIncludes.Includes,
+		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+		Export_system_includes:   exportedIncludes.SystemIncludes,
+		Local_includes:           compilerAttrs.localIncludes,
+		Absolute_includes:        compilerAttrs.absoluteIncludes,
+	}
 
 	// Append shared/static{} stanza properties. These won't be specified on
 	// cc_library_* itself, but may be specified in cc_defaults that this module
@@ -2929,11 +2945,7 @@
 			Cpp_std:                  compilerAttrs.cppStd,
 			C_std:                    compilerAttrs.cStd,
 
-			Export_includes:          exportedIncludes.Includes,
-			Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
-			Export_system_includes:   exportedIncludes.SystemIncludes,
-			Local_includes:           compilerAttrs.localIncludes,
-			Absolute_includes:        compilerAttrs.absoluteIncludes,
+			includesAttributes: includeAttrs,
 
 			Cppflags:   compilerAttrs.cppFlags,
 			Conlyflags: compilerAttrs.conlyFlags,
@@ -2959,11 +2971,8 @@
 			Cpp_std: compilerAttrs.cppStd,
 			C_std:   compilerAttrs.cStd,
 
-			Export_includes:          exportedIncludes.Includes,
-			Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
-			Export_system_includes:   exportedIncludes.SystemIncludes,
-			Local_includes:           compilerAttrs.localIncludes,
-			Absolute_includes:        compilerAttrs.absoluteIncludes,
+			includesAttributes: includeAttrs,
+
 			Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,
 
 			Strip: stripAttrsFromLinkerAttrs(&linkerAttrs),
@@ -2994,14 +3003,23 @@
 		Bzl_load_location: fmt.Sprintf("//build/bazel/rules/cc:%s.bzl", modType),
 	}
 
-	tags := android.ApexAvailableTags(module)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, module)
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name(), Tags: tags}, attrs)
 }
 
+type includesAttributes struct {
+	Export_includes          bazel.StringListAttribute
+	Export_absolute_includes bazel.StringListAttribute
+	Export_system_includes   bazel.StringListAttribute
+	Local_includes           bazel.StringListAttribute
+	Absolute_includes        bazel.StringListAttribute
+}
+
 // TODO(b/199902614): Can this be factored to share with the other Attributes?
 type bazelCcLibraryStaticAttributes struct {
 	staticOrSharedAttributes
+	includesAttributes
 
 	Use_version_lib bazel.BoolAttribute
 	Rtti            bazel.BoolAttribute
@@ -3009,12 +3027,7 @@
 	Cpp_std         *string
 	C_std           *string
 
-	Export_includes          bazel.StringListAttribute
-	Export_absolute_includes bazel.StringListAttribute
-	Export_system_includes   bazel.StringListAttribute
-	Local_includes           bazel.StringListAttribute
-	Absolute_includes        bazel.StringListAttribute
-	Hdrs                     bazel.LabelListAttribute
+	Hdrs bazel.LabelListAttribute
 
 	Cppflags   bazel.StringListAttribute
 	Conlyflags bazel.StringListAttribute
@@ -3026,6 +3039,7 @@
 // TODO(b/199902614): Can this be factored to share with the other Attributes?
 type bazelCcLibrarySharedAttributes struct {
 	staticOrSharedAttributes
+	includesAttributes
 
 	Linkopts        bazel.StringListAttribute
 	Use_version_lib bazel.BoolAttribute
@@ -3035,12 +3049,7 @@
 	Cpp_std *string
 	C_std   *string
 
-	Export_includes          bazel.StringListAttribute
-	Export_absolute_includes bazel.StringListAttribute
-	Export_system_includes   bazel.StringListAttribute
-	Local_includes           bazel.StringListAttribute
-	Absolute_includes        bazel.StringListAttribute
-	Hdrs                     bazel.LabelListAttribute
+	Hdrs bazel.LabelListAttribute
 
 	Strip                    stripAttributes
 	Additional_linker_inputs bazel.LabelListAttribute
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 1dee726..ce9c4aa 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -151,7 +151,7 @@
 		Bzl_load_location: "//build/bazel/rules/cc:cc_library_headers.bzl",
 	}
 
-	tags := android.ApexAvailableTags(module)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, module)
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
 		Name: module.Name(),
diff --git a/cc/object.go b/cc/object.go
index d65cdea..5d61872 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -226,7 +226,7 @@
 		Bzl_load_location: "//build/bazel/rules/cc:cc_object.bzl",
 	}
 
-	tags := android.ApexAvailableTags(m)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, m)
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
 		Name: m.Name(),
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 0b5841e..44cd0d7 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -389,7 +389,7 @@
 		name += "_bp2build_cc_library_static"
 	}
 
-	tags := android.ApexAvailableTags(module)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, module)
 	ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name, Tags: tags}, attrs, prebuiltAttrs.Enabled)
 
 	_true := true
@@ -420,7 +420,7 @@
 	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
-	tags := android.ApexAvailableTags(module)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, module)
 	ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name, Tags: tags}, attrs, prebuiltAttrs.Enabled)
 }
 
@@ -650,7 +650,7 @@
 	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
-	tags := android.ApexAvailableTags(module)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, module)
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name, Tags: tags}, attrs)
 }
 
@@ -813,7 +813,7 @@
 	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
-	tags := android.ApexAvailableTags(module)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, module)
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name, Tags: tags}, attrs)
 }
 
diff --git a/cc/proto.go b/cc/proto.go
index 97470e5..5d9aef6 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -207,7 +207,7 @@
 	protoAttrs.Min_sdk_version = m.Properties.Min_sdk_version
 
 	name := m.Name() + suffix
-	tags := android.ApexAvailableTags(m)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx.(android.TopDownMutatorContext), m)
 	ctx.CreateBazelTargetModule(
 		bazel.BazelTargetModuleProperties{
 			Rule_class:        rule_class,
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 7fddc1b..e28f537 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -99,6 +99,7 @@
 	Fuzzer
 	Memtag_heap
 	Memtag_stack
+	Memtag_globals
 	cfi // cfi is last to prevent it running before incompatible mutators
 )
 
@@ -111,6 +112,7 @@
 	Fuzzer,
 	Memtag_heap,
 	Memtag_stack,
+	Memtag_globals,
 	cfi, // cfi is last to prevent it running before incompatible mutators
 }
 
@@ -133,6 +135,8 @@
 		return "memtag_heap"
 	case Memtag_stack:
 		return "memtag_stack"
+	case Memtag_globals:
+		return "memtag_globals"
 	case Fuzzer:
 		return "fuzzer"
 	default:
@@ -151,6 +155,8 @@
 		return "memtag_heap"
 	case Memtag_stack:
 		return "memtag_stack"
+	case Memtag_globals:
+		return "memtag_globals"
 	case tsan:
 		return "thread"
 	case intOverflow:
@@ -172,7 +178,7 @@
 		sanitizer := &sanitizerSplitMutator{t}
 		ctx.TopDown(t.variationName()+"_markapexes", sanitizer.markSanitizableApexesMutator)
 		ctx.Transition(t.variationName(), sanitizer)
-	case Memtag_heap, Memtag_stack, intOverflow:
+	case Memtag_heap, Memtag_stack, Memtag_globals, intOverflow:
 		// do nothing
 	default:
 		panic(fmt.Errorf("unknown SanitizerType %d", t))
@@ -213,6 +219,8 @@
 		return true
 	case Memtag_stack:
 		return true
+	case Memtag_globals:
+		return true
 	default:
 		return false
 	}
@@ -267,6 +275,9 @@
 	// Memory-tagging stack instrumentation, only available on arm64
 	// Adds instrumentation to detect stack buffer overflows and use-after-scope using MTE.
 	Memtag_stack *bool `android:"arch_variant"`
+	// Memory-tagging globals instrumentation, only available on arm64
+	// Adds instrumentation to detect global buffer overflows using MTE.
+	Memtag_globals *bool `android:"arch_variant"`
 
 	// A modifier for ASAN and HWASAN for write only instrumentation
 	Writeonly *bool `android:"arch_variant"`
@@ -342,6 +353,8 @@
 	Memtag_heap *bool `blueprint:"mutated"`
 	// Whether Memory-tagging stack instrumentation is enabled for this module
 	Memtag_stack *bool `blueprint:"mutated"`
+	// Whether Memory-tagging globals instrumentation is enabled for this module
+	Memtag_globals *bool `android:"arch_variant"`
 
 	// Whether a modifier for ASAN and HWASAN for write only instrumentation is enabled for this
 	// module
@@ -425,6 +438,7 @@
 	p.Integer_overflow = userProps.Integer_overflow
 	p.Memtag_heap = userProps.Memtag_heap
 	p.Memtag_stack = userProps.Memtag_stack
+	p.Memtag_globals = userProps.Memtag_globals
 	p.Safestack = userProps.Safestack
 	p.Scs = userProps.Scs
 	p.Scudo = userProps.Scudo
@@ -552,6 +566,10 @@
 			s.Memtag_stack = proptools.BoolPtr(true)
 		}
 
+		if found, globalSanitizers = removeFromList("memtag_globals", globalSanitizers); found && s.Memtag_globals == nil {
+			s.Memtag_globals = proptools.BoolPtr(true)
+		}
+
 		if len(globalSanitizers) > 0 {
 			ctx.ModuleErrorf("unknown global sanitizer option %s", globalSanitizers[0])
 		}
@@ -632,6 +650,7 @@
 	if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() || ctx.Host() {
 		s.Memtag_heap = nil
 		s.Memtag_stack = nil
+		s.Memtag_globals = nil
 	}
 
 	// Also disable CFI if ASAN is enabled.
@@ -641,6 +660,7 @@
 		// HWASAN and ASAN win against MTE.
 		s.Memtag_heap = nil
 		s.Memtag_stack = nil
+		s.Memtag_globals = nil
 	}
 
 	// Disable sanitizers that depend on the UBSan runtime for windows/darwin builds.
@@ -696,7 +716,8 @@
 
 	if ctx.Os() != android.Windows && (Bool(s.All_undefined) || Bool(s.Undefined) || Bool(s.Address) || Bool(s.Thread) ||
 		Bool(s.Fuzzer) || Bool(s.Safestack) || Bool(s.Cfi) || Bool(s.Integer_overflow) || len(s.Misc_undefined) > 0 ||
-		Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap) || Bool(s.Memtag_stack)) {
+		Bool(s.Scudo) || Bool(s.Hwaddress) || Bool(s.Scs) || Bool(s.Memtag_heap) || Bool(s.Memtag_stack) ||
+		Bool(s.Memtag_globals)) {
 		sanitize.Properties.SanitizerEnabled = true
 	}
 
@@ -872,7 +893,7 @@
 		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-fatal-warnings")
 	}
 
-	if (Bool(sanProps.Memtag_heap) || Bool(sanProps.Memtag_stack)) && ctx.binary() {
+	if (Bool(sanProps.Memtag_heap) || Bool(sanProps.Memtag_stack) || Bool(sanProps.Memtag_globals)) && ctx.binary() {
 		if Bool(sanProps.Diag.Memtag_heap) {
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-fsanitize-memtag-mode=sync")
 		} else {
@@ -995,6 +1016,8 @@
 		return s.Properties.SanitizeMutated.Memtag_heap
 	case Memtag_stack:
 		return s.Properties.SanitizeMutated.Memtag_stack
+	case Memtag_globals:
+		return s.Properties.SanitizeMutated.Memtag_globals
 	case Fuzzer:
 		return s.Properties.SanitizeMutated.Fuzzer
 	default:
@@ -1011,6 +1034,7 @@
 		!sanitize.isSanitizerEnabled(scs) &&
 		!sanitize.isSanitizerEnabled(Memtag_heap) &&
 		!sanitize.isSanitizerEnabled(Memtag_stack) &&
+		!sanitize.isSanitizerEnabled(Memtag_globals) &&
 		!sanitize.isSanitizerEnabled(Fuzzer)
 }
 
@@ -1032,10 +1056,12 @@
 		sanitize.Properties.SanitizeMutated.Address = bPtr
 		// For ASAN variant, we need to disable Memtag_stack
 		sanitize.Properties.SanitizeMutated.Memtag_stack = nil
+		sanitize.Properties.SanitizeMutated.Memtag_globals = nil
 	case Hwasan:
 		sanitize.Properties.SanitizeMutated.Hwaddress = bPtr
 		// For HWAsan variant, we need to disable Memtag_stack
 		sanitize.Properties.SanitizeMutated.Memtag_stack = nil
+		sanitize.Properties.SanitizeMutated.Memtag_globals = nil
 	case tsan:
 		sanitize.Properties.SanitizeMutated.Thread = bPtr
 	case intOverflow:
@@ -1049,6 +1075,8 @@
 	case Memtag_stack:
 		sanitize.Properties.SanitizeMutated.Memtag_stack = bPtr
 		// We do not need to disable ASAN or HWASan here, as there is no Memtag_stack variant.
+	case Memtag_globals:
+		sanitize.Properties.Sanitize.Memtag_globals = bPtr
 	case Fuzzer:
 		sanitize.Properties.SanitizeMutated.Fuzzer = bPtr
 	default:
@@ -1536,6 +1564,13 @@
 			sanitizers = append(sanitizers, "memtag-stack")
 		}
 
+		if Bool(sanProps.Memtag_globals) {
+			sanitizers = append(sanitizers, "memtag-globals")
+			// TODO(mitchp): For now, enable memtag-heap with memtag-globals because the linker
+			// isn't new enough (https://reviews.llvm.org/differential/changeset/?ref=4243566).
+			sanitizers = append(sanitizers, "memtag-heap")
+		}
+
 		if Bool(sanProps.Fuzzer) {
 			sanitizers = append(sanitizers, "fuzzer-no-link")
 		}
diff --git a/cc/sysprop.go b/cc/sysprop.go
index 0df290a..7ddd476 100644
--- a/cc/sysprop.go
+++ b/cc/sysprop.go
@@ -38,7 +38,7 @@
 }
 
 func Bp2buildSysprop(ctx android.Bp2buildMutatorContext, labels SyspropLibraryLabels, srcs bazel.LabelListAttribute, minSdkVersion *string) {
-	apexAvailableTags := android.ApexAvailableTags(ctx.Module())
+	apexAvailableTags := android.ApexAvailableTagsWithoutTestApexes(ctx.(android.TopDownMutatorContext), ctx.Module())
 	ctx.CreateBazelTargetModule(
 		bazel.BazelTargetModuleProperties{
 			Rule_class:        "sysprop_library",
diff --git a/genrule/genrule.go b/genrule/genrule.go
index f5da50e..00adb70 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -940,7 +940,7 @@
 		}
 	}
 
-	tags := android.ApexAvailableTags(m)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, m)
 
 	if ctx.ModuleType() == "gensrcs" {
 		// The Output_extension prop is not in an immediately accessible field
diff --git a/java/app.go b/java/app.go
index 706f99a..8d96557 100755
--- a/java/app.go
+++ b/java/app.go
@@ -33,8 +33,17 @@
 
 func init() {
 	RegisterAppBuildComponents(android.InitRegistrationContext)
+	pctx.HostBinToolVariable("ModifyAllowlistCmd", "modify_permissions_allowlist")
 }
 
+var (
+	modifyAllowlist = pctx.AndroidStaticRule("modifyAllowlist",
+		blueprint.RuleParams{
+			Command:     "${ModifyAllowlistCmd} $in $packageName $out",
+			CommandDeps: []string{"${ModifyAllowlistCmd}"},
+		}, "packageName")
+)
+
 func RegisterAppBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("android_app", AndroidAppFactory)
 	ctx.RegisterModuleType("android_test", AndroidTestFactory)
@@ -115,6 +124,9 @@
 	// Prefer using other specific properties if build behaviour must be changed; avoid using this
 	// flag for anything but neverallow rules (unless the behaviour change is invisible to owners).
 	Updatable *bool
+
+	// Specifies the file that contains the allowlist for this app.
+	Privapp_allowlist *string `android:"path"`
 }
 
 // android_app properties that can be overridden by override_android_app
@@ -179,6 +191,8 @@
 	android.ApexBundleDepsInfo
 
 	javaApiUsedByOutputFile android.ModuleOutPath
+
+	privAppAllowlist android.OptionalPath
 }
 
 func (a *AndroidApp) IsInstallable() bool {
@@ -205,6 +219,10 @@
 	return a.jniCoverageOutputs
 }
 
+func (a *AndroidApp) PrivAppAllowlist() android.OptionalPath {
+	return a.privAppAllowlist
+}
+
 var _ AndroidLibraryDependency = (*AndroidApp)(nil)
 
 type Certificate struct {
@@ -269,6 +287,16 @@
 		ctx.AddDependency(ctx.Module(), certificateTag, cert)
 	}
 
+	if a.appProperties.Privapp_allowlist != nil && !Bool(a.appProperties.Privileged) {
+		// There are a few uids that are explicitly considered privileged regardless of their
+		// app's location. Bluetooth is one such app. It should arguably be moved to priv-app,
+		// but for now, allow it not to be in priv-app.
+		privilegedBecauseOfUid := ctx.ModuleName() == "Bluetooth"
+		if !privilegedBecauseOfUid {
+			ctx.PropertyErrorf("privapp_allowlist", "privileged must be set in order to use privapp_allowlist (with a few exceptions)")
+		}
+	}
+
 	for _, cert := range a.appProperties.Additional_certificates {
 		cert = android.SrcIsModule(cert)
 		if cert != "" {
@@ -591,7 +619,6 @@
 		}
 	}
 
-
 	return mainCertificate, certificates
 }
 
@@ -599,6 +626,35 @@
 	return a.installApkName
 }
 
+func (a *AndroidApp) createPrivappAllowlist(ctx android.ModuleContext) android.Path {
+	if a.appProperties.Privapp_allowlist == nil {
+		return nil
+	}
+
+	isOverrideApp := a.GetOverriddenBy() != ""
+	if !isOverrideApp {
+		// if this is not an override, we don't need to rewrite the existing privapp allowlist
+		return android.PathForModuleSrc(ctx, *a.appProperties.Privapp_allowlist)
+	}
+
+	if a.overridableAppProperties.Package_name == nil {
+		ctx.PropertyErrorf("privapp_allowlist", "package_name must be set to use privapp_allowlist")
+	}
+
+	packageName := *a.overridableAppProperties.Package_name
+	fileName := "privapp_allowlist_" + packageName + ".xml"
+	outPath := android.PathForModuleOut(ctx, fileName).OutputPath
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   modifyAllowlist,
+		Input:  android.PathForModuleSrc(ctx, *a.appProperties.Privapp_allowlist),
+		Output: outPath,
+		Args: map[string]string{
+			"packageName": packageName,
+		},
+	})
+	return &outPath
+}
+
 func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
 	var apkDeps android.Paths
 
@@ -734,11 +790,21 @@
 	BuildBundleModule(ctx, bundleFile, a.exportPackage, jniJarFile, dexJarFile)
 	a.bundleFile = bundleFile
 
+	allowlist := a.createPrivappAllowlist(ctx)
+	if allowlist != nil {
+		a.privAppAllowlist = android.OptionalPathForPath(allowlist)
+	}
+
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 
 	// Install the app package.
-	if (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() &&
-		!a.appProperties.PreventInstall {
+	shouldInstallAppPackage := (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() && !a.appProperties.PreventInstall
+	if shouldInstallAppPackage {
+		if a.privAppAllowlist.Valid() {
+			allowlistInstallPath := android.PathForModuleInstall(ctx, "etc", "permissions")
+			allowlistInstallFilename := a.installApkName + ".xml"
+			ctx.InstallFile(allowlistInstallPath, allowlistInstallFilename, a.privAppAllowlist.Path())
+		}
 
 		var extraInstalledPaths android.Paths
 		for _, extra := range a.extraOutputFiles {
@@ -925,6 +991,10 @@
 // For OutputFileProducer interface
 func (a *AndroidApp) OutputFiles(tag string) (android.Paths, error) {
 	switch tag {
+	// In some instances, it can be useful to reference the aapt-generated flags from another
+	// target, e.g., system server implements services declared in the framework-res manifest.
+	case ".aapt.proguardOptionsFile":
+		return []android.Path{a.proguardOptionsFile}, nil
 	case ".aapt.srcjar":
 		return []android.Path{a.aaptSrcJar}, nil
 	case ".export-package.apk":
diff --git a/java/app_import.go b/java/app_import.go
index 85b35eb..3097d7f 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -376,6 +376,10 @@
 	return a.provenanceMetaDataFile
 }
 
+func (a *AndroidAppImport) PrivAppAllowlist() android.OptionalPath {
+	return android.OptionalPath{}
+}
+
 var dpiVariantGroupType reflect.Type
 var archVariantGroupType reflect.Type
 var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"}
diff --git a/java/app_test.go b/java/app_test.go
index b154bc9..746ab75 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -3547,3 +3547,124 @@
 		android.AssertStringDoesContain(t, testCase.desc, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
 	}
 }
+
+func TestPrivappAllowlist(t *testing.T) {
+	testJavaError(t, "privileged must be set in order to use privapp_allowlist", `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			privapp_allowlist: "perms.xml",
+		}
+	`)
+
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
+		t,
+		`
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			privapp_allowlist: "privapp_allowlist_com.android.foo.xml",
+			privileged: true,
+			sdk_version: "current",
+		}
+		override_android_app {
+			name: "bar",
+			base: "foo",
+			package_name: "com.google.android.foo",
+		}
+		`,
+	)
+	app := result.ModuleForTests("foo", "android_common")
+	overrideApp := result.ModuleForTests("foo", "android_common_bar")
+
+	// verify that privapp allowlist is created for override apps
+	overrideApp.Output("out/soong/.intermediates/foo/android_common_bar/privapp_allowlist_com.google.android.foo.xml")
+	expectedAllowlistInput := "privapp_allowlist_com.android.foo.xml"
+	overrideActualAllowlistInput := overrideApp.Rule("modifyAllowlist").Input.String()
+	if expectedAllowlistInput != overrideActualAllowlistInput {
+		t.Errorf("expected override allowlist to be %q; got %q", expectedAllowlistInput, overrideActualAllowlistInput)
+	}
+
+	// verify that permissions are copied to device
+	app.Output("out/soong/target/product/test_device/system/etc/permissions/foo.xml")
+	overrideApp.Output("out/soong/target/product/test_device/system/etc/permissions/bar.xml")
+}
+
+func TestPrivappAllowlistAndroidMk(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		android.PrepareForTestWithAndroidMk,
+	).RunTestWithBp(
+		t,
+		`
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			privapp_allowlist: "privapp_allowlist_com.android.foo.xml",
+			privileged: true,
+			sdk_version: "current",
+		}
+		override_android_app {
+			name: "bar",
+			base: "foo",
+			package_name: "com.google.android.foo",
+		}
+		`,
+	)
+	baseApp := result.ModuleForTests("foo", "android_common")
+	overrideApp := result.ModuleForTests("foo", "android_common_bar")
+
+	baseAndroidApp := baseApp.Module().(*AndroidApp)
+	baseEntries := android.AndroidMkEntriesForTest(t, result.TestContext, baseAndroidApp)[0]
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALLED_MODULE; expected to find foo.apk",
+		baseEntries.EntryMap["LOCAL_SOONG_INSTALLED_MODULE"][0],
+		"\\S+foo.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include foo.apk",
+		baseEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"\\S+foo.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include app",
+		baseEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"\\S+foo.apk:\\S+/target/product/test_device/system/priv-app/foo/foo.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include privapp_allowlist",
+		baseEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"privapp_allowlist_com.android.foo.xml:\\S+/target/product/test_device/system/etc/permissions/foo.xml",
+	)
+
+	overrideAndroidApp := overrideApp.Module().(*AndroidApp)
+	overrideEntries := android.AndroidMkEntriesForTest(t, result.TestContext, overrideAndroidApp)[0]
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALLED_MODULE; expected to find bar.apk",
+		overrideEntries.EntryMap["LOCAL_SOONG_INSTALLED_MODULE"][0],
+		"\\S+bar.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include bar.apk",
+		overrideEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"\\S+bar.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include app",
+		overrideEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"\\S+bar.apk:\\S+/target/product/test_device/system/priv-app/bar/bar.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include privapp_allowlist",
+		overrideEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"\\S+soong/.intermediates/foo/android_common_bar/privapp_allowlist_com.google.android.foo.xml:\\S+/target/product/test_device/system/etc/permissions/bar.xml",
+	)
+}
diff --git a/java/java.go b/java/java.go
index 683e546..ccb1507 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2836,7 +2836,7 @@
 			return android.IsConvertedToAidlLibrary(ctx, src.OriginalModuleName)
 		})
 
-		apexAvailableTags := android.ApexAvailableTags(ctx.Module())
+		apexAvailableTags := android.ApexAvailableTagsWithoutTestApexes(ctx, ctx.Module())
 
 		if !aidlSrcs.IsEmpty() {
 			aidlLibName := m.Name() + "_aidl_library"
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 206d995..0740467 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -135,6 +135,19 @@
 	mctx.CreateModule(genrule.GenRuleFactory, &genruleProps)
 }
 
+func createLatestApiModuleExtensionVersionFile(mctx android.LoadHookContext, name string, version string) {
+	genruleProps := struct {
+		Name *string
+		Srcs []string
+		Out  []string
+		Cmd  *string
+	}{}
+	genruleProps.Name = proptools.StringPtr(name)
+	genruleProps.Out = []string{name}
+	genruleProps.Cmd = proptools.StringPtr("echo " + version + " > $(out)")
+	mctx.CreateModule(genrule.GenRuleFactory, &genruleProps)
+}
+
 func createEmptyFile(mctx android.LoadHookContext, name string) {
 	props := struct {
 		Name *string
@@ -233,9 +246,10 @@
 	type latestApiInfo struct {
 		module, scope, path string
 		version             int
+		isExtensionApiFile  bool
 	}
 
-	getLatest := func(files []string) map[string]latestApiInfo {
+	getLatest := func(files []string, isExtensionApiFile bool) map[string]latestApiInfo {
 		m := make(map[string]latestApiInfo)
 		for _, f := range files {
 			module, version, scope := parseFinalizedPrebuiltPath(mctx, f)
@@ -245,16 +259,16 @@
 			key := module + "." + scope
 			info, exists := m[key]
 			if !exists || version > info.version {
-				m[key] = latestApiInfo{module, scope, f, version}
+				m[key] = latestApiInfo{module, scope, f, version, isExtensionApiFile}
 			}
 		}
 		return m
 	}
 
-	latest := getLatest(apiLevelFiles)
+	latest := getLatest(apiLevelFiles, false)
 	if p.properties.Extensions_dir != nil {
 		extensionApiFiles := globExtensionDirs(mctx, p, "api/*.txt")
-		for k, v := range getLatest(extensionApiFiles) {
+		for k, v := range getLatest(extensionApiFiles, true) {
 			if _, exists := latest[k]; !exists {
 				mctx.ModuleErrorf("Module %v finalized for extension %d but never during an API level; likely error", v.module, v.version)
 			}
@@ -267,6 +281,12 @@
 	for _, k := range android.SortedKeys(latest) {
 		info := latest[k]
 		name := PrebuiltApiModuleName(info.module, info.scope, "latest")
+		latestExtensionVersionModuleName := PrebuiltApiModuleName(info.module, info.scope, "latest.extension_version")
+		if info.isExtensionApiFile {
+			createLatestApiModuleExtensionVersionFile(mctx, latestExtensionVersionModuleName, strconv.Itoa(info.version))
+		} else {
+			createLatestApiModuleExtensionVersionFile(mctx, latestExtensionVersionModuleName, "-1")
+		}
 		createApiModule(mctx, name, info.path)
 	}
 
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 9367ff0..97f6ab4 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -237,3 +237,20 @@
     name: "jars-to-module-info-java",
     src: "jars-to-module-info-java.sh",
 }
+
+python_binary_host {
+    name: "modify_permissions_allowlist",
+    main: "modify_permissions_allowlist.py",
+    srcs: [
+        "modify_permissions_allowlist.py",
+    ],
+}
+
+python_test_host {
+    name: "modify_permissions_allowlist_test",
+    main: "modify_permissions_allowlist_test.py",
+    srcs: [
+        "modify_permissions_allowlist_test.py",
+        "modify_permissions_allowlist.py",
+    ],
+}
diff --git a/scripts/modify_permissions_allowlist.py b/scripts/modify_permissions_allowlist.py
new file mode 100755
index 0000000..38ec7ec
--- /dev/null
+++ b/scripts/modify_permissions_allowlist.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""A tool for modifying privileged permission allowlists."""
+
+from __future__ import print_function
+
+import argparse
+import sys
+from xml.dom import minidom
+
+
+class InvalidRootNodeException(Exception):
+  pass
+
+
+class InvalidNumberOfPrivappPermissionChildren(Exception):
+  pass
+
+
+def modify_allowlist(allowlist_dom, package_name):
+  if allowlist_dom.documentElement.tagName != 'permissions':
+    raise InvalidRootNodeException
+  nodes = allowlist_dom.getElementsByTagName('privapp-permissions')
+  if nodes.length != 1:
+    raise InvalidNumberOfPrivappPermissionChildren
+  privapp_permissions = nodes[0]
+  privapp_permissions.setAttribute('package', package_name)
+
+
+def parse_args():
+  """Parse commandline arguments."""
+
+  parser = argparse.ArgumentParser()
+  parser.add_argument('input', help='input allowlist template file')
+  parser.add_argument(
+      'package_name', help='package name to use in the allowlist'
+  )
+  parser.add_argument('output', help='output allowlist file')
+
+  return parser.parse_args()
+
+
+def main():
+  try:
+    args = parse_args()
+    doc = minidom.parse(args.input)
+    modify_allowlist(doc, args.package_name)
+    with open(args.output, 'w') as output_file:
+      doc.writexml(output_file, encoding='utf-8')
+  except Exception as err:
+    print('error: ' + str(err), file=sys.stderr)
+    sys.exit(-1)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/scripts/modify_permissions_allowlist_test.py b/scripts/modify_permissions_allowlist_test.py
new file mode 100755
index 0000000..ee8b12c
--- /dev/null
+++ b/scripts/modify_permissions_allowlist_test.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Unit tests for modify_permissions_allowlist.py."""
+
+from __future__ import print_function
+
+import unittest
+
+from xml.dom import minidom
+
+from modify_permissions_allowlist import InvalidRootNodeException, InvalidNumberOfPrivappPermissionChildren, modify_allowlist
+
+
+class ModifyPermissionsAllowlistTest(unittest.TestCase):
+
+  def test_invalid_root(self):
+    xml_data = '<foo></foo>'
+    xml_dom = minidom.parseString(xml_data)
+    self.assertRaises(InvalidRootNodeException, modify_allowlist, xml_dom, 'x')
+
+  def test_no_packages(self):
+    xml_data = '<permissions></permissions>'
+    xml_dom = minidom.parseString(xml_data)
+    self.assertRaises(
+        InvalidNumberOfPrivappPermissionChildren, modify_allowlist, xml_dom, 'x'
+    )
+
+  def test_multiple_packages(self):
+    xml_data = (
+        '<permissions>'
+        '  <privapp-permissions package="foo.bar"></privapp-permissions>'
+        '  <privapp-permissions package="bar.baz"></privapp-permissions>'
+        '</permissions>'
+    )
+    xml_dom = minidom.parseString(xml_data)
+    self.assertRaises(
+        InvalidNumberOfPrivappPermissionChildren, modify_allowlist, xml_dom, 'x'
+    )
+
+  def test_modify_package_name(self):
+    xml_data = (
+        '<permissions>'
+        '  <privapp-permissions package="foo.bar">'
+        '    <permission name="myperm1"/>'
+        '  </privapp-permissions>'
+        '</permissions>'
+    )
+    xml_dom = minidom.parseString(xml_data)
+    modify_allowlist(xml_dom, 'bar.baz')
+    expected_data = (
+        '<?xml version="1.0" ?>'
+        '<permissions>'
+        '  <privapp-permissions package="bar.baz">'
+        '    <permission name="myperm1"/>'
+        '  </privapp-permissions>'
+        '</permissions>'
+    )
+    self.assertEqual(expected_data, xml_dom.toxml())
+
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)