Allow sdk members to vary by os type

Adds support for specifying separate members to an sdk/module_exports
for different os types, e.g. separate ones for android and host.

Adds 'android:"arch_variant"' tag to the dynamically generated fields
for the sdk member types.

Merges the exported members from all variants together.

Determines the device/host_supported flags of the member snapshots by
whether the OsClasses used by their variants rather than the sdk's
host/device supported properties as they may be different.

Bug: 150451422
Test: m nothing
Change-Id: I41fbbcd8723aafd54826aad9b78eced9f66ef51c
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 45da1f9..c60002b 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -816,3 +816,126 @@
 		checkAllCopyRules(".intermediates/system-module/linux_glibc_common/javac/system-module.jar -> java/system-module.jar"),
 	)
 }
+
+func TestDeviceAndHostSnapshotWithOsSpecificMembers(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithJava(t, `
+		module_exports {
+			name: "myexports",
+			host_supported: true,
+			java_libs: ["myjavalib"],
+			target: {
+				android: {
+					java_header_libs: ["androidjavalib"],
+				},
+				host: {
+					java_header_libs: ["hostjavalib"],
+				},
+			},
+		}
+
+		java_library {
+			name: "myjavalib",
+			host_supported: true,
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+		}
+
+		java_library {
+			name: "androidjavalib",
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+		}
+
+		java_library_host {
+			name: "hostjavalib",
+			srcs: ["Test.java"],
+		}
+	`)
+
+	result.CheckSnapshot("myexports", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "myexports_hostjavalib@current",
+    sdk_member_name: "hostjavalib",
+    device_supported: false,
+    host_supported: true,
+    jars: ["java/hostjavalib.jar"],
+}
+
+java_import {
+    name: "hostjavalib",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    jars: ["java/hostjavalib.jar"],
+}
+
+java_import {
+    name: "myexports_androidjavalib@current",
+    sdk_member_name: "androidjavalib",
+    jars: ["java/androidjavalib.jar"],
+}
+
+java_import {
+    name: "androidjavalib",
+    prefer: false,
+    jars: ["java/androidjavalib.jar"],
+}
+
+java_import {
+    name: "myexports_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    host_supported: true,
+    target: {
+        android: {
+            jars: ["java/android/myjavalib.jar"],
+        },
+        linux_glibc: {
+            jars: ["java/linux_glibc/myjavalib.jar"],
+        },
+    },
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    host_supported: true,
+    target: {
+        android: {
+            jars: ["java/android/myjavalib.jar"],
+        },
+        linux_glibc: {
+            jars: ["java/linux_glibc/myjavalib.jar"],
+        },
+    },
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    host_supported: true,
+    target: {
+        android: {
+            java_header_libs: ["myexports_androidjavalib@current"],
+        },
+        linux_glibc: {
+            java_header_libs: ["myexports_hostjavalib@current"],
+        },
+    },
+    java_libs: ["myexports_myjavalib@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/hostjavalib/linux_glibc_common/javac/hostjavalib.jar -> java/hostjavalib.jar
+.intermediates/androidjavalib/android_common/turbine-combined/androidjavalib.jar -> java/androidjavalib.jar
+.intermediates/myjavalib/android_common/javac/myjavalib.jar -> java/android/myjavalib.jar
+.intermediates/myjavalib/linux_glibc_common/javac/myjavalib.jar -> java/linux_glibc/myjavalib.jar
+`),
+	)
+}
diff --git a/sdk/sdk.go b/sdk/sdk.go
index db71575..8b9d5bc 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -150,6 +150,7 @@
 		fields = append(fields, reflect.StructField{
 			Name: proptools.FieldNameForProperty(p),
 			Type: reflect.TypeOf([]string{}),
+			Tag:  `android:"arch_variant"`,
 		})
 
 		// Copy the field index for use in the getter func as using the loop variable directly will
diff --git a/sdk/update.go b/sdk/update.go
index 282a7a4..a43a14b 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -225,10 +225,17 @@
 // the contents (header files, stub libraries, etc) into the zip file.
 func (s *sdk) buildSnapshot(ctx android.ModuleContext, sdkVariants []*sdk) android.OutputPath {
 
+	exportedMembers := make(map[string]struct{})
 	var memberRefs []sdkMemberRef
 	for _, sdkVariant := range sdkVariants {
 		memberRefs = append(memberRefs, sdkVariant.memberRefs...)
+
+		// Merge the exported member sets from all sdk variants.
+		for key, _ := range sdkVariant.getExportedMembers() {
+			exportedMembers[key] = struct{}{}
+		}
 	}
+	s.exportedMembers = exportedMembers
 
 	snapshotDir := android.PathForModuleOut(ctx, "snapshot")
 
@@ -302,23 +309,43 @@
 		snapshotModule.AddProperty("visibility", visibility)
 	}
 
-	addHostDeviceSupportedProperties(&s.ModuleBase, snapshotModule)
+	addHostDeviceSupportedProperties(s.ModuleBase.DeviceSupported(), s.ModuleBase.HostSupported(), snapshotModule)
 
 	// Compile_multilib defaults to both and must always be set to both on the
 	// device and so only needs to be set when targeted at the host and is neither
 	// unspecified or both.
+	targetPropertySet := snapshotModule.AddPropertySet("target")
 	if s.HostSupported() && multilib != "" && multilib != "both" {
-		targetSet := snapshotModule.AddPropertySet("target")
-		hostSet := targetSet.AddPropertySet("host")
+		hostSet := targetPropertySet.AddPropertySet("host")
 		hostSet.AddProperty("compile_multilib", multilib)
 	}
 
-	for _, memberListProperty := range s.memberListProperties() {
-		names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
-		if len(names) > 0 {
-			snapshotModule.AddProperty(memberListProperty.propertyName(), builder.versionedSdkMemberNames(names))
+	var dynamicMemberPropertiesList []interface{}
+	osTypeToMemberProperties := make(map[android.OsType]*sdk)
+	for _, sdkVariant := range sdkVariants {
+		properties := sdkVariant.dynamicMemberTypeListProperties
+		osTypeToMemberProperties[sdkVariant.Target().Os] = sdkVariant
+		dynamicMemberPropertiesList = append(dynamicMemberPropertiesList, properties)
+	}
+
+	// Extract the common lists of members into a separate struct.
+	commonDynamicMemberProperties := s.dynamicSdkMemberTypes.createMemberListProperties()
+	extractCommonProperties(commonDynamicMemberProperties, dynamicMemberPropertiesList)
+
+	// Add properties common to all os types.
+	s.addMemberPropertiesToPropertySet(builder, snapshotModule, commonDynamicMemberProperties)
+
+	// Iterate over the os types in a fixed order.
+	for _, osType := range s.getPossibleOsTypes() {
+		if sdkVariant, ok := osTypeToMemberProperties[osType]; ok {
+			osPropertySet := targetPropertySet.AddPropertySet(sdkVariant.Target().Os.Name)
+			s.addMemberPropertiesToPropertySet(builder, osPropertySet, sdkVariant.dynamicMemberTypeListProperties)
 		}
 	}
+
+	// Prune any empty property sets.
+	snapshotModule.transform(pruneEmptySetTransformer{})
+
 	bpFile.AddModule(snapshotModule)
 
 	// generate Android.bp
@@ -369,6 +396,15 @@
 	return outputZipFile
 }
 
+func (s *sdk) addMemberPropertiesToPropertySet(builder *snapshotBuilder, propertySet android.BpPropertySet, dynamicMemberTypeListProperties interface{}) {
+	for _, memberListProperty := range s.memberListProperties() {
+		names := memberListProperty.getter(dynamicMemberTypeListProperties)
+		if len(names) > 0 {
+			propertySet.AddProperty(memberListProperty.propertyName(), builder.versionedSdkMemberNames(names))
+		}
+	}
+}
+
 type propertyTag struct {
 	name string
 }
@@ -573,7 +609,19 @@
 		}
 	}
 
-	addHostDeviceSupportedProperties(&s.sdk.ModuleBase, m)
+	deviceSupported := false
+	hostSupported := false
+
+	for _, variant := range member.Variants() {
+		osClass := variant.Target().Os.Class
+		if osClass == android.Host || osClass == android.HostCross {
+			hostSupported = true
+		} else if osClass == android.Device {
+			deviceSupported = true
+		}
+	}
+
+	addHostDeviceSupportedProperties(deviceSupported, hostSupported, m)
 
 	// Where available copy apex_available properties from the member.
 	if apexAware, ok := variant.(interface{ ApexAvailable() []string }); ok {
@@ -588,11 +636,11 @@
 	return m
 }
 
-func addHostDeviceSupportedProperties(module *android.ModuleBase, bpModule *bpModule) {
-	if !module.DeviceSupported() {
+func addHostDeviceSupportedProperties(deviceSupported bool, hostSupported bool, bpModule *bpModule) {
+	if !deviceSupported {
 		bpModule.AddProperty("device_supported", false)
 	}
-	if module.HostSupported() {
+	if hostSupported {
 		bpModule.AddProperty("host_supported", true)
 	}
 }