add aidl_interface_headers module type

As part of the go/roboleaf conversion, all inputs to a rule need to be
explicitly provided to Bazel to satisfy sandboxing requirements. The
include_dirs property of aidl_interfaces passes directory paths to the
AIDL compiler to locate imports in AIDL files, but since Soong
sandboxing allows access of the entire source tree at rule execution
time, the sources used in the include_dirs property are not explicity
provided to the build system.

The aidl_interface_headers module type acts like a filegroup and wraps
a collection of AIDL sources and their import directory to track the
inputs for AIDL compilation actions. These inputs are then recorded as
implicits to the Soong rule as well. This explicit file information
will be used to allow Bazel to perform fully sandboxed actions.

The include_dirs property will be removed in a future commit in Internal
since there are some modules there that still use it.

Bug: 229251008
Test: m networkstack-aidl-interfaces
Change-Id: I8742645dca1b1054f4e3df63bfc5cfebb2c2b181
(cherry picked from commit 2eb58bc2a848cc483badadda5ae08f3a18d72094)
Merged-In: I8742645dca1b1054f4e3df63bfc5cfebb2c2b181
diff --git a/build/Android.bp b/build/Android.bp
index 24b4a93..5fe0892 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -40,6 +40,7 @@
         "aidl_api.go",
         "aidl_gen_rule.go",
         "aidl_interface_backends.go",
+        "aidl_interface_headers.go",
         "aidl_interface_metadata_singleton.go",
         "aidl_mapping.go",
         "aidl_rust_source_provider.go",
diff --git a/build/aidl_api.go b/build/aidl_api.go
index 1fc4474..fe821a6 100644
--- a/build/aidl_api.go
+++ b/build/aidl_api.go
@@ -55,6 +55,7 @@
 	AidlRoot  string   // base directory for the input aidl file
 	Stability *string
 	Imports   []string
+	Headers   []string
 	Versions  []string
 	Dumpapi   DumpApiProperties
 }
@@ -340,6 +341,14 @@
 			// add imported module's checkapiTimestamps as implicits to make sure that imported apiDump is up-to-date
 			deps.implicits = append(deps.implicits, api.checkApiTimestamps.Paths()...)
 			deps.implicits = append(deps.implicits, api.checkHashTimestamps.Paths()...)
+		case interfaceHeadersDepTag:
+			headerInfo, ok := ctx.OtherModuleProvider(dep, AidlInterfaceHeadersProvider).(AidlInterfaceHeadersInfo)
+			if !ok {
+				ctx.PropertyErrorf("headers", "module %v does not provide AidlInterfaceHeadersInfo", dep.Name())
+				return
+			}
+			deps.implicits = append(deps.implicits, headerInfo.Srcs...)
+			deps.imports = append(deps.imports, headerInfo.IncludeDir)
 		}
 	})
 	return deps
@@ -578,6 +587,7 @@
 		AidlRoot:  aidlRoot,
 		Stability: i.properties.Stability,
 		Imports:   i.properties.Imports,
+		Headers:   i.properties.Headers,
 		Versions:  i.getVersions(),
 		Dumpapi:   i.properties.Dumpapi,
 	})
diff --git a/build/aidl_gen_rule.go b/build/aidl_gen_rule.go
index 3dd0fde..6a326d1 100644
--- a/build/aidl_gen_rule.go
+++ b/build/aidl_gen_rule.go
@@ -66,6 +66,7 @@
 	Srcs            []string `android:"path"`
 	AidlRoot        string   // base directory for the input aidl file
 	Imports         []string
+	Headers         []string
 	Stability       *string
 	Min_sdk_version *string
 	Platform_apis   bool
@@ -114,12 +115,12 @@
 func (g *aidlGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	srcs, imports := getPaths(ctx, g.properties.Srcs, g.properties.AidlRoot)
 
+	g.deps = getDeps(ctx, g.getImports(ctx))
+
 	if ctx.Failed() {
 		return
 	}
 
-	g.deps = getDeps(ctx, g.getImports(ctx))
-
 	genDirTimestamp := android.PathForModuleGen(ctx, "timestamp") // $out/gen/timestamp
 	g.implicitInputs = append(g.implicitInputs, genDirTimestamp)
 	g.implicitInputs = append(g.implicitInputs, g.deps.implicits...)
diff --git a/build/aidl_interface.go b/build/aidl_interface.go
index 36527e4..e853c24 100644
--- a/build/aidl_interface.go
+++ b/build/aidl_interface.go
@@ -406,6 +406,10 @@
 
 	// --dumpapi options
 	Dumpapi DumpApiProperties
+
+	// List of aidl_interface_headers modules that provide include dependencies
+	// for the AIDL tool.
+	Headers []string
 }
 
 type aidlInterface struct {
@@ -490,6 +494,10 @@
 	blueprint.BaseDependencyTag
 }
 
+type interfaceHeadersDepTag struct {
+	blueprint.BaseDependencyTag
+}
+
 var (
 	// Dep from *-source (aidlGenRule) to *-api (aidlApi)
 	apiDep = apiDepTag{name: "api"}
@@ -497,6 +505,8 @@
 	importApiDep = apiDepTag{name: "imported-api"}
 	// Dep to original *-interface (aidlInterface)
 	interfaceDep = interfaceDepTag{}
+	// Dep for a header interface
+	interfaceHeadersDep = interfaceHeadersDepTag{}
 )
 
 func addImportedInterfaceDeps(ctx android.BottomUpMutatorContext, imports []string) {
@@ -529,6 +539,10 @@
 			return
 		}
 		addImportedInterfaceDeps(mctx, i.properties.Imports)
+
+		for _, header := range i.properties.Headers {
+			mctx.AddDependency(i, interfaceHeadersDep, header)
+		}
 	case *aidlImplementationGenerator:
 		mctx.AddDependency(i, interfaceDep, i.properties.AidlInterfaceName+aidlInterfaceSuffix)
 		addImportedInterfaceDeps(mctx, i.properties.Imports)
@@ -547,6 +561,9 @@
 			name, _ := parseModuleWithVersion(anImport)
 			mctx.AddDependency(i, importApiDep, name+aidlApiSuffix)
 		}
+		for _, header := range i.properties.Headers {
+			mctx.AddDependency(i, interfaceHeadersDep, header)
+		}
 	case *aidlGenRule:
 		mctx.AddDependency(i, interfaceDep, i.properties.BaseName+aidlInterfaceSuffix)
 		addImportedInterfaceDeps(mctx, i.properties.Imports)
@@ -554,6 +571,9 @@
 			// for checkapi timestamps
 			mctx.AddDependency(i, apiDep, i.properties.BaseName+aidlApiSuffix)
 		}
+		for _, header := range i.properties.Headers {
+			mctx.AddDependency(i, interfaceHeadersDep, header)
+		}
 	}
 }
 
@@ -936,6 +956,8 @@
 	}
 
 	paths, imports := getPaths(ctx, srcs, root_dir)
+	imports = append(imports, deps.imports...)
+	imports = append(imports, i.properties.Include_dirs...)
 
 	preprocessCommand := rb.Command().BuiltTool("aidl").
 		FlagWithOutput("--preprocess ", preprocessed).
@@ -944,7 +966,7 @@
 		preprocessCommand.FlagWithArg("--stability ", *i.properties.Stability)
 	}
 	preprocessCommand.FlagForEachInput("-p", deps.preprocessed)
-	preprocessCommand.FlagForEachArg("-I", concat(imports, i.properties.Include_dirs))
+	preprocessCommand.FlagForEachArg("-I", imports)
 	preprocessCommand.Inputs(paths)
 	name := i.BaseModuleName()
 	if version != "" {
diff --git a/build/aidl_interface_backends.go b/build/aidl_interface_backends.go
index 62a167c..54948ce 100644
--- a/build/aidl_interface_backends.go
+++ b/build/aidl_interface_backends.go
@@ -76,6 +76,7 @@
 		Srcs:            srcs,
 		AidlRoot:        aidlRoot,
 		Imports:         i.getImportsForVersion(version),
+		Headers:         i.properties.Headers,
 		Stability:       i.properties.Stability,
 		Min_sdk_version: i.minSdkVersion(lang),
 		Lang:            lang,
@@ -224,6 +225,7 @@
 		Srcs:            srcs,
 		AidlRoot:        aidlRoot,
 		Imports:         i.getImportsForVersion(version),
+		Headers:         i.properties.Headers,
 		Stability:       i.properties.Stability,
 		Min_sdk_version: minSdkVersion,
 		Platform_apis:   proptools.Bool(i.properties.Backend.Java.Platform_apis),
@@ -276,6 +278,7 @@
 		Srcs:            srcs,
 		AidlRoot:        aidlRoot,
 		Imports:         i.getImportsForVersion(version),
+		Headers:         i.properties.Headers,
 		Stability:       i.properties.Stability,
 		Min_sdk_version: i.minSdkVersion(langRust),
 		Lang:            langRust,
diff --git a/build/aidl_interface_headers.go b/build/aidl_interface_headers.go
new file mode 100644
index 0000000..0d59c4d
--- /dev/null
+++ b/build/aidl_interface_headers.go
@@ -0,0 +1,69 @@
+// 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.
+
+package aidl
+
+import (
+	"path/filepath"
+
+	"android/soong/android"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	android.RegisterModuleType("aidl_interface_headers", aidlInterfaceHeadersFactory)
+}
+
+type AidlInterfaceHeadersInfo struct {
+	Srcs       android.Paths
+	IncludeDir string
+}
+
+var AidlInterfaceHeadersProvider = blueprint.NewProvider(AidlInterfaceHeadersInfo{})
+
+type aidlInterfaceHeadersProperties struct {
+	// List of .aidl files which compose this interface.
+	Srcs []string `android:"path"`
+
+	// Relative path for includes. assumes AIDL path is relative to current directory.
+	Include_dir *string
+}
+
+type aidlInterfaceHeaders struct {
+	android.ModuleBase
+
+	properties aidlInterfaceHeadersProperties
+
+	srcs android.Paths
+}
+
+// Modules which provide AIDL sources that are only used to provide "-I" flags to the
+// aidl tool. No language bindings are generated from these modules. Typically this will
+// be used to provide includes for UnstructuredParcelable AIDL definitions such as those
+// coming from framework modules.
+func aidlInterfaceHeadersFactory() android.Module {
+	i := &aidlInterfaceHeaders{}
+	i.AddProperties(&i.properties)
+	android.InitAndroidModule(i)
+	return i
+}
+
+func (i *aidlInterfaceHeaders) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	ctx.SetProvider(AidlInterfaceHeadersProvider, AidlInterfaceHeadersInfo{
+		Srcs:       android.PathsForModuleSrc(ctx, i.properties.Srcs),
+		IncludeDir: filepath.Join(ctx.ModuleDir(), proptools.String(i.properties.Include_dir)),
+	})
+}
diff --git a/build/aidl_test.go b/build/aidl_test.go
index 8c258db..11f8dd9 100644
--- a/build/aidl_test.go
+++ b/build/aidl_test.go
@@ -175,6 +175,7 @@
 		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 			ctx.RegisterModuleType("aidl_interface", aidlInterfaceFactory)
 			ctx.RegisterModuleType("aidl_interfaces_metadata", aidlInterfacesMetadataSingletonFactory)
+			ctx.RegisterModuleType("aidl_interface_headers", aidlInterfaceHeadersFactory)
 			ctx.RegisterModuleType("rust_defaults", func() android.Module {
 				return rust.DefaultsFactory()
 			})
@@ -1097,6 +1098,7 @@
 				srcs: ["a/Foo.aidl"],
 				imports: ["bar-iface-V2"],
 				versions: ["1"],
+				headers: ["boq-iface-headers"],
 			}
 		`),
 		"foo/a/Foo.aidl": nil,
@@ -1129,13 +1131,21 @@
 		"baz/aidl_api/baz-iface/current/b/Baz.aidl": nil,
 		"baz/aidl_api/baz-iface/1/b/Baz.aidl":       nil,
 		"baz/aidl_api/baz-iface/1/.hash":            nil,
+
+		"boq/Android.bp": []byte(`
+			aidl_interface_headers {
+				name: "boq-iface-headers",
+				srcs: ["b/Boq.aidl"],
+			}
+		`),
+		"boq/b/Baz.aidl": nil,
 	})
 	ctx, _ := testAidl(t, ``, customizer)
 
 	// checkapidump rule is to compare "compatibility" between ToT(dump) and "current"
 	{
 		rule := ctx.ModuleForTests("foo-iface-api", "").Output("checkapi_dump.timestamp")
-		android.AssertStringEquals(t, "checkapi(dump == current) imports", "", rule.Args["imports"])
+		android.AssertStringEquals(t, "checkapi(dump == current) imports", "-Iboq", rule.Args["imports"])
 		android.AssertStringDoesContain(t, "checkapi(dump == current) optionalFlags",
 			rule.Args["optionalFlags"],
 			"-pout/soong/.intermediates/bar/bar-iface_interface/2/preprocessed.aidl")
@@ -1153,7 +1163,9 @@
 	// compile (v1)
 	{
 		rule := ctx.ModuleForTests("foo-iface-V1-cpp-source", "").Output("a/Foo.cpp")
-		android.AssertStringEquals(t, "compile(old=1) should import aidl_api/1", "-Ifoo/aidl_api/foo-iface/1", rule.Args["imports"])
+		android.AssertStringEquals(t, "compile(old=1) should import aidl_api/1",
+			"-Ifoo/aidl_api/foo-iface/1 -Iboq",
+			rule.Args["imports"])
 		android.AssertStringDoesContain(t, "compile(old=1) should import bar.preprocessed",
 			rule.Args["optionalFlags"],
 			"-pout/soong/.intermediates/bar/bar-iface_interface/2/preprocessed.aidl")
@@ -1161,7 +1173,7 @@
 	// compile ToT(v2)
 	{
 		rule := ctx.ModuleForTests("foo-iface-V2-cpp-source", "").Output("a/Foo.cpp")
-		android.AssertStringEquals(t, "compile(tot=2) should import base dirs of srcs", "-Ifoo", rule.Args["imports"])
+		android.AssertStringEquals(t, "compile(tot=2) should import base dirs of srcs", "-Ifoo -Iboq", rule.Args["imports"])
 		android.AssertStringDoesContain(t, "compile(tot=2) should import bar.preprocessed",
 			rule.Args["optionalFlags"],
 			"-pout/soong/.intermediates/bar/bar-iface_interface/2/preprocessed.aidl")