Implement mixed builds for apex modules.

* Add ApexCqueryInfo to obtain apex artifacts used by the makefile
  generator and downstream modules
* Refactor code common to GenerateAndroidBuildActions and ProcessBazelQueryResponse
* Implement android.MixedBuildBuildable for modules
* Enable mixed build for apex modules with payload_type:"image"

Bug: 232085015
Test: treehugger
Change-Id: I9f1e03e5e7a5b5dde35a5db10c253069543ac973
diff --git a/bazel/aquery.go b/bazel/aquery.go
index ae2b107..418b143 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -574,7 +574,7 @@
 
 // expandTemplateContent substitutes the tokens in a template.
 func expandTemplateContent(actionEntry action) string {
-	replacerString := []string{}
+	var replacerString []string
 	for _, pair := range actionEntry.Substitutions {
 		value := pair.Value
 		if val, ok := templateActionOverriddenTokens[pair.Key]; ok {
@@ -647,7 +647,7 @@
 		}
 		labels = append([]string{currFragment.Label}, labels...)
 		if currId == currFragment.ParentId {
-			return "", fmt.Errorf("Fragment cannot refer to itself as parent %#v", currFragment)
+			return "", fmt.Errorf("fragment cannot refer to itself as parent %#v", currFragment)
 		}
 		currId = currFragment.ParentId
 	}
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index f5435f2..d32e619 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -1,6 +1,7 @@
 package cquery
 
 import (
+	"encoding/json"
 	"fmt"
 	"strings"
 )
@@ -9,6 +10,7 @@
 	GetOutputFiles  = &getOutputFilesRequestType{}
 	GetPythonBinary = &getPythonBinaryRequestType{}
 	GetCcInfo       = &getCcInfoType{}
+	GetApexInfo     = &getApexInfoType{}
 )
 
 type CcInfo struct {
@@ -179,7 +181,7 @@
 	const expectedLen = 10
 	splitString := strings.Split(rawString, "|")
 	if len(splitString) != expectedLen {
-		return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
+		return CcInfo{}, fmt.Errorf("expected %d items, got %q", expectedLen, splitString)
 	}
 	outputFilesString := splitString[0]
 	ccObjectsString := splitString[1]
@@ -215,6 +217,54 @@
 	}, nil
 }
 
+// Query Bazel for the artifacts generated by the apex modules.
+type getApexInfoType struct{}
+
+// Name returns a string name for this request type. Such request type names must be unique,
+// and must only consist of alphanumeric characters.
+func (g getApexInfoType) Name() string {
+	return "getApexInfo"
+}
+
+// StarlarkFunctionBody returns a starlark function body to process this request type.
+// The returned string is the body of a Starlark function which obtains
+// all request-relevant information about a target and returns a string containing
+// this information. The function should have the following properties:
+//   - `target` is the only parameter to this function (a configured target).
+//   - The return value must be a string.
+//   - The function body should not be indented outside of its own scope.
+func (g getApexInfoType) StarlarkFunctionBody() string {
+	return `info = providers(target)["//build/bazel/rules/apex:apex.bzl%ApexInfo"]
+return "{%s}" % ",".join([
+    json_for_file("signed_output", info.signed_output),
+    json_for_file("unsigned_output", info.unsigned_output),
+    json_for_labels("provides_native_libs", info.provides_native_libs),
+    json_for_labels("requires_native_libs", info.requires_native_libs),
+    json_for_files("bundle_key_pair", info.bundle_key_pair),
+    json_for_files("container_key_pair", info.container_key_pair)
+    ])`
+}
+
+type ApexCqueryInfo struct {
+	SignedOutput     string   `json:"signed_output"`
+	UnsignedOutput   string   `json:"unsigned_output"`
+	ProvidesLibs     []string `json:"provides_native_libs"`
+	RequiresLibs     []string `json:"requires_native_libs"`
+	BundleKeyPair    []string `json:"bundle_key_pair"`
+	ContainerKeyPair []string `json:"container_key_pair"`
+}
+
+// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
+// The given rawString must correspond to the string output which was created by evaluating the
+// Starlark given in StarlarkFunctionBody.
+func (g getApexInfoType) ParseResult(rawString string) ApexCqueryInfo {
+	var info ApexCqueryInfo
+	if err := json.Unmarshal([]byte(rawString), &info); err != nil {
+		panic(fmt.Errorf("cannot parse cquery result '%s': %s", rawString, err))
+	}
+	return info
+}
+
 // splitOrEmpty is a modification of strings.Split() that returns an empty list
 // if the given string is empty.
 func splitOrEmpty(s string, sep string) []string {
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 606e285..34248ce 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -148,13 +148,13 @@
 			description:          "too few result splits",
 			input:                "|",
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, []string{"", ""}),
+			expectedErrorMessage: fmt.Sprintf("expected %d items, got %q", expectedSplits, []string{"", ""}),
 		},
 		{
 			description:          "too many result splits",
 			input:                strings.Repeat("|", expectedSplits+1), // 2 too many
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, make([]string, expectedSplits+2)),
+			expectedErrorMessage: fmt.Sprintf("expected %d items, got %q", expectedSplits, make([]string, expectedSplits+2)),
 		},
 	}
 	for _, tc := range testCases {
@@ -167,3 +167,40 @@
 		}
 	}
 }
+
+func TestGetApexInfoParseResults(t *testing.T) {
+	testCases := []struct {
+		description    string
+		input          string
+		expectedOutput ApexCqueryInfo
+	}{
+		{
+			description:    "no result",
+			input:          "{}",
+			expectedOutput: ApexCqueryInfo{},
+		},
+		{
+			description: "one result",
+			input: `{"signed_output":"my.apex",` +
+				`"unsigned_output":"my.apex.unsigned",` +
+				`"requires_native_libs":["//bionic/libc:libc","//bionic/libdl:libdl"],` +
+				`"bundle_key_pair":["foo.pem","foo.privkey"],` +
+				`"container_key_pair":["foo.x509.pem", "foo.pk8"],` +
+				`"provides_native_libs":[]}`,
+			expectedOutput: ApexCqueryInfo{
+				SignedOutput:     "my.apex",
+				UnsignedOutput:   "my.apex.unsigned",
+				RequiresLibs:     []string{"//bionic/libc:libc", "//bionic/libdl:libdl"},
+				ProvidesLibs:     []string{},
+				BundleKeyPair:    []string{"foo.pem", "foo.privkey"},
+				ContainerKeyPair: []string{"foo.x509.pem", "foo.pk8"},
+			},
+		},
+	}
+	for _, tc := range testCases {
+		actualOutput := GetApexInfo.ParseResult(tc.input)
+		if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+			t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
+		}
+	}
+}