checkapi works with imports

Without imports checkapi can't properly work with references to imported
types. We had to work around it till now because TypeSpecifiers and
ConstantReferences are not resolved,

Now, checkapi works with imports including:
- include_dirs
- "current" api dumps of imported modules

Bug: 188878102
Test: m aidl_unittests
Change-Id: If0bed7471ac7cbb944fa6e136b553dac0ce93612
diff --git a/build/aidl_api.go b/build/aidl_api.go
index 8f17fe8..1f3e933 100644
--- a/build/aidl_api.go
+++ b/build/aidl_api.go
@@ -36,11 +36,11 @@
 	}, "optionalFlags", "imports", "outDir", "hashFile", "latestVersion")
 
 	aidlCheckApiRule = pctx.StaticRule("aidlCheckApiRule", blueprint.RuleParams{
-		Command: `(${aidlCmd} ${optionalFlags} --checkapi=${checkApiLevel} ${old} ${new} && touch ${out}) || ` +
+		Command: `(${aidlCmd} ${optionalFlags} --checkapi=${checkApiLevel} ${imports} ${old} ${new} && touch ${out}) || ` +
 			`(cat ${messageFile} && exit 1)`,
 		CommandDeps: []string{"${aidlCmd}"},
 		Description: "AIDL CHECK API: ${new} against ${old}",
-	}, "optionalFlags", "old", "new", "messageFile", "checkApiLevel")
+	}, "optionalFlags", "imports", "old", "new", "messageFile", "checkApiLevel")
 
 	aidlVerifyHashRule = pctx.StaticRule("aidlVerifyHashRule", blueprint.RuleParams{
 		Command: `if [ $$(cd '${apiDir}' && { find ./ -name "*.aidl" -print0 | LC_ALL=C sort -z | xargs -0 sha1sum && echo ${version}; } | sha1sum | cut -d " " -f 1) = $$(read -r <'${hashFile}' hash extra; printf %s $$hash) ]; then ` +
@@ -165,6 +165,8 @@
 			if m.properties.Stability != nil {
 				equalityCheckCommand.FlagWithArg("--stability ", *m.properties.Stability)
 			}
+			importPaths, implicits := m.getImportsForCheckApi(ctx)
+			equalityCheckCommand.FlagForEachArg("-I", importPaths).Implicits(implicits)
 			equalityCheckCommand.
 				Text(latestVersionDump.dir.String()).Implicits(latestVersionDump.files).
 				Text(dump.dir.String()).Implicits(dump.files).
@@ -209,6 +211,32 @@
 	return timestampFile
 }
 
+// calculates "import" flags(-I) for --checkapi command. The list of imports differs from --dumpapi
+// or --compile because --checkapi works with "apiDump"s.
+// For example, local_include_dirs is not provided because apiDump has all .aidl files.
+func (m *aidlApi) getImportsForCheckApi(ctx android.ModuleContext) (importPaths []string, implicits android.Paths) {
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		if importedAidl, ok := dep.(*aidlInterface); ok {
+			switch ctx.OtherModuleDependencyTag(dep) {
+			case importDep:
+				if proptools.Bool(importedAidl.properties.Unstable) {
+					importPaths = append(importPaths, importedAidl.properties.Full_import_paths...)
+				} else {
+					// use "current" snapshot from stable "imported" modules
+					currentDir := filepath.Join(ctx.OtherModuleDir(dep), aidlApiDir, importedAidl.BaseModuleName(), currentVersion)
+					importPaths = append(importPaths, currentDir)
+				}
+			case interfaceDep:
+				importPaths = append(importPaths, importedAidl.properties.Include_dirs...)
+			}
+		} else if importedApi, ok := dep.(*aidlApi); ok {
+			// add imported module's checkapiTimestamps as implicits to make sure that imported apiDump is up-to-date
+			implicits = append(implicits, importedApi.checkApiTimestamps.Paths()...)
+		}
+	})
+	return
+}
+
 func (m *aidlApi) checkApi(ctx android.ModuleContext, oldDump, newDump apiDump, checkApiLevel string, messageFile android.Path) android.WritablePath {
 	newVersion := newDump.dir.Base()
 	timestampFile := android.PathForModuleOut(ctx, "checkapi_"+newVersion+".timestamp")
@@ -218,16 +246,18 @@
 		optionalFlags = append(optionalFlags, "--stability", *m.properties.Stability)
 	}
 
-	var implicits android.Paths
+	importPaths, implicits := m.getImportsForCheckApi(ctx)
 	implicits = append(implicits, oldDump.files...)
 	implicits = append(implicits, newDump.files...)
 	implicits = append(implicits, messageFile)
+
 	ctx.Build(pctx, android.BuildParams{
 		Rule:      aidlCheckApiRule,
 		Implicits: implicits,
 		Output:    timestampFile,
 		Args: map[string]string{
 			"optionalFlags": strings.Join(optionalFlags, " "),
+			"imports":       strings.Join(wrap("-I", importPaths, ""), " "),
 			"old":           oldDump.dir.String(),
 			"new":           newDump.dir.String(),
 			"messageFile":   messageFile.String(),
@@ -257,10 +287,6 @@
 	rb.Command().Text("sed").Flag(" s/%s/" + m.properties.BaseName + "/g ").Input(messageFile).Text(" > ").Output(formattedMessageFile)
 	rb.Build("format_message_"+m.properties.BaseName, "")
 
-	var implicits android.Paths
-	implicits = append(implicits, oldDump.files...)
-	implicits = append(implicits, newDump.files...)
-	implicits = append(implicits, formattedMessageFile)
 	return m.checkApi(ctx, oldDump, newDump, "equal", formattedMessageFile)
 }
 
@@ -386,8 +412,21 @@
 	}
 }
 
+type depTag struct {
+	blueprint.BaseDependencyTag
+	name string
+}
+
+var (
+	importDep    = depTag{name: "imported-interface"}
+	interfaceDep = depTag{name: "interface"}
+	importApiDep = depTag{name: "imported-api"}
+)
+
 func (m *aidlApi) DepsMutator(ctx android.BottomUpMutatorContext) {
-	ctx.AddDependency(ctx.Module(), nil, wrap("", m.properties.ImportsWithoutVersion, aidlInterfaceSuffix)...)
+	ctx.AddDependency(ctx.Module(), importApiDep, wrap("", m.properties.ImportsWithoutVersion, aidlApiSuffix)...)
+	ctx.AddDependency(ctx.Module(), importDep, wrap("", m.properties.ImportsWithoutVersion, aidlInterfaceSuffix)...)
+	ctx.AddDependency(ctx.Module(), interfaceDep, m.properties.BaseName+aidlInterfaceSuffix)
 }
 
 func aidlApiFactory() android.Module {
@@ -407,7 +446,7 @@
 		Srcs:                  srcs,
 		AidlRoot:              aidlRoot,
 		Stability:             i.properties.Stability,
-		ImportsWithoutVersion: concat(i.properties.ImportsWithoutVersion, []string{i.ModuleBase.Name()}),
+		ImportsWithoutVersion: i.properties.ImportsWithoutVersion,
 		Versions:              i.properties.Versions,
 		Dumpapi:               i.properties.Dumpapi,
 	})