Merge "Fix data race on cfiExportsMap" into pi-dev
diff --git a/Android.bp b/Android.bp
index 1d2c516..00db8f2d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -71,6 +71,7 @@
         "android/config_test.go",
         "android/expand_test.go",
         "android/namespace_test.go",
+        "android/neverallow_test.go",
         "android/paths_test.go",
         "android/prebuilt_test.go",
         "android/util_test.go",
diff --git a/android/arch.go b/android/arch.go
index 6ab184f..fd80eec 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -784,14 +784,18 @@
 				a.appendProperties(ctx, genProps, targetProp, field, prefix)
 			}
 
-			if arch.ArchType == X86 && (hasArmAbi(arch) ||
-				hasArmAndroidArch(ctx.Config().Targets[Device])) {
+			if (arch.ArchType == X86 && (hasArmAbi(arch) ||
+				hasArmAndroidArch(ctx.Config().Targets[Device]))) ||
+				(arch.ArchType == Arm &&
+					hasX86AndroidArch(ctx.Config().Targets[Device])) {
 				field := "Arm_on_x86"
 				prefix := "target.arm_on_x86"
 				a.appendProperties(ctx, genProps, targetProp, field, prefix)
 			}
-			if arch.ArchType == X86_64 && (hasArmAbi(arch) ||
-				hasArmAndroidArch(ctx.Config().Targets[Device])) {
+			if (arch.ArchType == X86_64 && (hasArmAbi(arch) ||
+				hasArmAndroidArch(ctx.Config().Targets[Device]))) ||
+				(arch.ArchType == Arm &&
+					hasX8664AndroidArch(ctx.Config().Targets[Device])) {
 				field := "Arm_on_x86_64"
 				prefix := "target.arm_on_x86_64"
 				a.appendProperties(ctx, genProps, targetProp, field, prefix)
@@ -914,6 +918,26 @@
 	return false
 }
 
+// hasX86Arch returns true if targets has at least x86 Android arch
+func hasX86AndroidArch(targets []Target) bool {
+	for _, target := range targets {
+		if target.Os == Android && target.Arch.ArchType == X86 {
+			return true
+		}
+	}
+	return false
+}
+
+// hasX8664Arch returns true if targets has at least x86_64 Android arch
+func hasX8664AndroidArch(targets []Target) bool {
+	for _, target := range targets {
+		if target.Os == Android && target.Arch.ArchType == X86_64 {
+			return true
+		}
+	}
+	return false
+}
+
 type archConfig struct {
 	arch        string
 	archVariant string
diff --git a/android/namespace_test.go b/android/namespace_test.go
index a6fc9d5..8bec0ad 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -628,7 +628,7 @@
 
 func setupTest(t *testing.T, bps map[string]string) (ctx *TestContext) {
 	ctx, errs := setupTestExpectErrs(bps)
-	failIfErrored(t, errs)
+	FailIfErrored(t, errs)
 	return ctx
 }
 
@@ -692,12 +692,3 @@
 	InitAndroidModule(m)
 	return m
 }
-
-func failIfErrored(t *testing.T, errs []error) {
-	if len(errs) > 0 {
-		for _, err := range errs {
-			t.Error(err)
-		}
-		t.FailNow()
-	}
-}
diff --git a/android/neverallow.go b/android/neverallow.go
index 261f2ee..8fba4b9 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -46,9 +46,15 @@
 }
 
 var neverallows = []*rule{
-	neverallow().in("vendor", "device").with("vndk.enabled", "true").
+	neverallow().
+		in("vendor", "device").
+		with("vndk.enabled", "true").
+		without("vendor", "true").
 		because("the VNDK can never contain a library that is device dependent."),
-	neverallow().with("vndk.enabled", "true").without("owner", "").
+	neverallow().
+		with("vndk.enabled", "true").
+		without("vendor", "true").
+		without("owner", "").
 		because("a VNDK module can never have an owner."),
 	neverallow().notIn("libcore").with("no_standard_libs", "true"),
 
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
new file mode 100644
index 0000000..a278365
--- /dev/null
+++ b/android/neverallow_test.go
@@ -0,0 +1,217 @@
+// Copyright 2018 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 android
+
+import (
+	"io/ioutil"
+	"os"
+	"testing"
+)
+
+var neverallowTests = []struct {
+	name          string
+	fs            map[string][]byte
+	expectedError string
+}{
+	{
+		name: "no vndk.enabled under vendor directory",
+		fs: map[string][]byte{
+			"vendor/Blueprints": []byte(`
+				cc_library {
+					name: "libvndk",
+					vendor_available: true,
+					vndk: {
+						enabled: true,
+					},
+				}`),
+		},
+		expectedError: "VNDK can never contain a library that is device dependent",
+	},
+	{
+		name: "no vndk.enabled under device directory",
+		fs: map[string][]byte{
+			"device/Blueprints": []byte(`
+				cc_library {
+					name: "libvndk",
+					vendor_available: true,
+					vndk: {
+						enabled: true,
+					},
+				}`),
+		},
+		expectedError: "VNDK can never contain a library that is device dependent",
+	},
+	{
+		name: "vndk-ext under vendor or device directory",
+		fs: map[string][]byte{
+			"device/Blueprints": []byte(`
+				cc_library {
+					name: "libvndk1_ext",
+					vendor: true,
+					vndk: {
+						enabled: true,
+					},
+				}`),
+			"vendor/Blueprints": []byte(`
+				cc_library {
+					name: "libvndk2_ext",
+					vendor: true,
+					vndk: {
+						enabled: true,
+					},
+				}`),
+		},
+		expectedError: "",
+	},
+
+	{
+		name: "no enforce_vintf_manifest.cflags",
+		fs: map[string][]byte{
+			"Blueprints": []byte(`
+				cc_library {
+					name: "libexample",
+					product_variables: {
+						enforce_vintf_manifest: {
+							cflags: ["-DSHOULD_NOT_EXIST"],
+						},
+					},
+				}`),
+		},
+		expectedError: "manifest enforcement should be independent",
+	},
+	{
+		name: "libhidltransport enforce_vintf_manifest.cflags",
+		fs: map[string][]byte{
+			"Blueprints": []byte(`
+				cc_library {
+					name: "libhidltransport",
+					product_variables: {
+						enforce_vintf_manifest: {
+							cflags: ["-DSHOULD_NOT_EXIST"],
+						},
+					},
+				}`),
+		},
+		expectedError: "",
+	},
+
+	{
+		name: "no treble_linker_namespaces.cflags",
+		fs: map[string][]byte{
+			"Blueprints": []byte(`
+				cc_library {
+					name: "libexample",
+					product_variables: {
+						treble_linker_namespaces: {
+							cflags: ["-DSHOULD_NOT_EXIST"],
+						},
+					},
+				}`),
+		},
+		expectedError: "nothing should care if linker namespaces are enabled or not",
+	},
+	{
+		name: "libc_bionic_ndk treble_linker_namespaces.cflags",
+		fs: map[string][]byte{
+			"Blueprints": []byte(`
+				cc_library {
+					name: "libc_bionic_ndk",
+					product_variables: {
+						treble_linker_namespaces: {
+							cflags: ["-DSHOULD_NOT_EXIST"],
+						},
+					},
+				}`),
+		},
+		expectedError: "",
+	},
+}
+
+func TestNeverallow(t *testing.T) {
+	buildDir, err := ioutil.TempDir("", "soong_neverallow_test")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(buildDir)
+
+	config := TestConfig(buildDir, nil)
+
+	for _, test := range neverallowTests {
+		t.Run(test.name, func(t *testing.T) {
+			_, errs := testNeverallow(t, config, test.fs)
+
+			if test.expectedError == "" {
+				FailIfErrored(t, errs)
+			} else {
+				FailIfNoMatchingErrors(t, test.expectedError, errs)
+			}
+		})
+	}
+}
+
+func testNeverallow(t *testing.T, config Config, fs map[string][]byte) (*TestContext, []error) {
+	ctx := NewTestContext()
+	ctx.RegisterModuleType("cc_library", ModuleFactoryAdaptor(newMockCcLibraryModule))
+	ctx.PostDepsMutators(registerNeverallowMutator)
+	ctx.Register()
+
+	ctx.MockFileSystem(fs)
+
+	_, errs := ctx.ParseBlueprintsFiles("Blueprints")
+	if len(errs) > 0 {
+		return ctx, errs
+	}
+
+	_, errs = ctx.PrepareBuildActions(config)
+	return ctx, errs
+}
+
+type mockProperties struct {
+	Vendor_available *bool
+
+	Vndk struct {
+		Enabled                *bool
+		Support_system_process *bool
+		Extends                *string
+	}
+
+	Product_variables struct {
+		Enforce_vintf_manifest struct {
+			Cflags []string
+		}
+
+		Treble_linker_namespaces struct {
+			Cflags []string
+		}
+	}
+}
+
+type mockCcLibraryModule struct {
+	ModuleBase
+	properties mockProperties
+}
+
+func newMockCcLibraryModule() Module {
+	m := &mockCcLibraryModule{}
+	m.AddProperties(&m.properties)
+	InitAndroidModule(m)
+	return m
+}
+
+func (p *mockCcLibraryModule) DepsMutator(ctx BottomUpMutatorContext) {
+}
+
+func (p *mockCcLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
+}
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 93f5805..69ce16a 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -138,9 +138,9 @@
 			})
 
 			_, errs := ctx.ParseBlueprintsFiles("Blueprints")
-			fail(t, errs)
+			FailIfErrored(t, errs)
 			_, errs = ctx.PrepareBuildActions(config)
-			fail(t, errs)
+			FailIfErrored(t, errs)
 
 			foo := ctx.ModuleForTests("foo", "")
 
@@ -231,12 +231,3 @@
 
 func (s *sourceModule) GenerateAndroidBuildActions(ctx ModuleContext) {
 }
-
-func fail(t *testing.T, errs []error) {
-	if len(errs) > 0 {
-		for _, err := range errs {
-			t.Error(err)
-		}
-		t.FailNow()
-	}
-}
diff --git a/android/testing.go b/android/testing.go
index ae012b0..f5d33e1 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -17,7 +17,9 @@
 import (
 	"fmt"
 	"path/filepath"
+	"regexp"
 	"strings"
+	"testing"
 
 	"github.com/google/blueprint"
 )
@@ -152,3 +154,36 @@
 	panic(fmt.Errorf("couldn't find output %q.\nall outputs: %v",
 		file, searchedOutputs))
 }
+
+func FailIfErrored(t *testing.T, errs []error) {
+	t.Helper()
+	if len(errs) > 0 {
+		for _, err := range errs {
+			t.Error(err)
+		}
+		t.FailNow()
+	}
+}
+
+func FailIfNoMatchingErrors(t *testing.T, pattern string, errs []error) {
+	t.Helper()
+
+	matcher, err := regexp.Compile(pattern)
+	if err != nil {
+		t.Errorf("failed to compile regular expression %q because %s", pattern, err)
+	}
+
+	found := false
+	for _, err := range errs {
+		if matcher.FindStringIndex(err.Error()) != nil {
+			found = true
+			break
+		}
+	}
+	if !found {
+		t.Errorf("missing the expected error %q (checked %d error(s))", pattern, len(errs))
+		for i, err := range errs {
+			t.Errorf("errs[%d] = %s", i, err)
+		}
+	}
+}
diff --git a/android/util.go b/android/util.go
index 854d782..e9b9764 100644
--- a/android/util.go
+++ b/android/util.go
@@ -101,11 +101,18 @@
 
 func RemoveFromList(s string, list []string) (bool, []string) {
 	i := IndexList(s, list)
-	if i != -1 {
-		return true, append(list[:i], list[i+1:]...)
-	} else {
+	if i == -1 {
 		return false, list
 	}
+
+	result := make([]string, 0, len(list)-1)
+	result = append(result, list[:i]...)
+	for _, l := range list[i+1:] {
+		if l != s {
+			result = append(result, l)
+		}
+	}
+	return true, result
 }
 
 // FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of
diff --git a/android/util_test.go b/android/util_test.go
index 32f92b4..1c791b2 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -118,3 +118,244 @@
 		}
 	}
 }
+
+func TestJoinWithPrefix(t *testing.T) {
+	testcases := []struct {
+		name     string
+		input    []string
+		expected string
+	}{
+		{
+			name:     "zero_inputs",
+			input:    []string{},
+			expected: "",
+		},
+		{
+			name:     "one_input",
+			input:    []string{"a"},
+			expected: "prefix:a",
+		},
+		{
+			name:     "two_inputs",
+			input:    []string{"a", "b"},
+			expected: "prefix:a prefix:b",
+		},
+	}
+
+	prefix := "prefix:"
+
+	for _, testCase := range testcases {
+		t.Run(testCase.name, func(t *testing.T) {
+			out := JoinWithPrefix(testCase.input, prefix)
+			if out != testCase.expected {
+				t.Errorf("incorrect output:")
+				t.Errorf("     input: %#v", testCase.input)
+				t.Errorf("    prefix: %#v", prefix)
+				t.Errorf("  expected: %#v", testCase.expected)
+				t.Errorf("       got: %#v", out)
+			}
+		})
+	}
+}
+
+func TestIndexList(t *testing.T) {
+	input := []string{"a", "b", "c"}
+
+	testcases := []struct {
+		key      string
+		expected int
+	}{
+		{
+			key:      "a",
+			expected: 0,
+		},
+		{
+			key:      "b",
+			expected: 1,
+		},
+		{
+			key:      "c",
+			expected: 2,
+		},
+		{
+			key:      "X",
+			expected: -1,
+		},
+	}
+
+	for _, testCase := range testcases {
+		t.Run(testCase.key, func(t *testing.T) {
+			out := IndexList(testCase.key, input)
+			if out != testCase.expected {
+				t.Errorf("incorrect output:")
+				t.Errorf("       key: %#v", testCase.key)
+				t.Errorf("     input: %#v", input)
+				t.Errorf("  expected: %#v", testCase.expected)
+				t.Errorf("       got: %#v", out)
+			}
+		})
+	}
+}
+
+func TestInList(t *testing.T) {
+	input := []string{"a"}
+
+	testcases := []struct {
+		key      string
+		expected bool
+	}{
+		{
+			key:      "a",
+			expected: true,
+		},
+		{
+			key:      "X",
+			expected: false,
+		},
+	}
+
+	for _, testCase := range testcases {
+		t.Run(testCase.key, func(t *testing.T) {
+			out := InList(testCase.key, input)
+			if out != testCase.expected {
+				t.Errorf("incorrect output:")
+				t.Errorf("       key: %#v", testCase.key)
+				t.Errorf("     input: %#v", input)
+				t.Errorf("  expected: %#v", testCase.expected)
+				t.Errorf("       got: %#v", out)
+			}
+		})
+	}
+}
+
+func TestPrefixInList(t *testing.T) {
+	prefixes := []string{"a", "b"}
+
+	testcases := []struct {
+		str      string
+		expected bool
+	}{
+		{
+			str:      "a-example",
+			expected: true,
+		},
+		{
+			str:      "b-example",
+			expected: true,
+		},
+		{
+			str:      "X-example",
+			expected: false,
+		},
+	}
+
+	for _, testCase := range testcases {
+		t.Run(testCase.str, func(t *testing.T) {
+			out := PrefixInList(testCase.str, prefixes)
+			if out != testCase.expected {
+				t.Errorf("incorrect output:")
+				t.Errorf("       str: %#v", testCase.str)
+				t.Errorf("  prefixes: %#v", prefixes)
+				t.Errorf("  expected: %#v", testCase.expected)
+				t.Errorf("       got: %#v", out)
+			}
+		})
+	}
+}
+
+func TestFilterList(t *testing.T) {
+	input := []string{"a", "b", "c", "c", "b", "d", "a"}
+	filter := []string{"a", "c"}
+	remainder, filtered := FilterList(input, filter)
+
+	expected := []string{"b", "b", "d"}
+	if !reflect.DeepEqual(remainder, expected) {
+		t.Errorf("incorrect remainder output:")
+		t.Errorf("     input: %#v", input)
+		t.Errorf("    filter: %#v", filter)
+		t.Errorf("  expected: %#v", expected)
+		t.Errorf("       got: %#v", remainder)
+	}
+
+	expected = []string{"a", "c", "c", "a"}
+	if !reflect.DeepEqual(filtered, expected) {
+		t.Errorf("incorrect filtered output:")
+		t.Errorf("     input: %#v", input)
+		t.Errorf("    filter: %#v", filter)
+		t.Errorf("  expected: %#v", expected)
+		t.Errorf("       got: %#v", filtered)
+	}
+}
+
+func TestRemoveListFromList(t *testing.T) {
+	input := []string{"a", "b", "c", "d", "a", "c", "d"}
+	filter := []string{"a", "c"}
+	expected := []string{"b", "d", "d"}
+	out := RemoveListFromList(input, filter)
+	if !reflect.DeepEqual(out, expected) {
+		t.Errorf("incorrect output:")
+		t.Errorf("     input: %#v", input)
+		t.Errorf("    filter: %#v", filter)
+		t.Errorf("  expected: %#v", expected)
+		t.Errorf("       got: %#v", out)
+	}
+}
+
+func TestRemoveFromList(t *testing.T) {
+	testcases := []struct {
+		name          string
+		key           string
+		input         []string
+		expectedFound bool
+		expectedOut   []string
+	}{
+		{
+			name:          "remove_one_match",
+			key:           "a",
+			input:         []string{"a", "b", "c"},
+			expectedFound: true,
+			expectedOut:   []string{"b", "c"},
+		},
+		{
+			name:          "remove_three_matches",
+			key:           "a",
+			input:         []string{"a", "b", "a", "c", "a"},
+			expectedFound: true,
+			expectedOut:   []string{"b", "c"},
+		},
+		{
+			name:          "remove_zero_matches",
+			key:           "X",
+			input:         []string{"a", "b", "a", "c", "a"},
+			expectedFound: false,
+			expectedOut:   []string{"a", "b", "a", "c", "a"},
+		},
+		{
+			name:          "remove_all_matches",
+			key:           "a",
+			input:         []string{"a", "a", "a", "a"},
+			expectedFound: true,
+			expectedOut:   []string{},
+		},
+	}
+
+	for _, testCase := range testcases {
+		t.Run(testCase.name, func(t *testing.T) {
+			found, out := RemoveFromList(testCase.key, testCase.input)
+			if found != testCase.expectedFound {
+				t.Errorf("incorrect output:")
+				t.Errorf("       key: %#v", testCase.key)
+				t.Errorf("     input: %#v", testCase.input)
+				t.Errorf("  expected: %#v", testCase.expectedFound)
+				t.Errorf("       got: %#v", found)
+			}
+			if !reflect.DeepEqual(out, testCase.expectedOut) {
+				t.Errorf("incorrect output:")
+				t.Errorf("       key: %#v", testCase.key)
+				t.Errorf("     input: %#v", testCase.input)
+				t.Errorf("  expected: %#v", testCase.expectedOut)
+				t.Errorf("       got: %#v", out)
+			}
+		})
+	}
+}
diff --git a/android/variable.go b/android/variable.go
index e45cebd..01a8122 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -102,6 +102,10 @@
 			Cppflags []string
 		}
 
+		Use_lmkd_stats_log struct {
+			Cflags []string
+		}
+
 		Arc struct {
 			Cflags       []string
 			Exclude_srcs []string
@@ -179,6 +183,7 @@
 	Enforce_vintf_manifest     *bool `json:",omitempty"`
 	Pdk                        *bool `json:",omitempty"`
 	Uml                        *bool `json:",omitempty"`
+	Use_lmkd_stats_log         *bool `json:",omitempty"`
 	Arc                        *bool `json:",omitempty"`
 	MinimizeJavaDebugInfo      *bool `json:",omitempty"`
 
diff --git a/cc/cc.go b/cc/cc.go
index 91bf9a6..b9c589a 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -52,6 +52,8 @@
 		ctx.TopDown("tsan_deps", sanitizerDepsMutator(tsan))
 		ctx.BottomUp("tsan", sanitizerMutator(tsan)).Parallel()
 
+		ctx.TopDown("minimal_runtime_deps", minimalRuntimeDepsMutator())
+
 		ctx.BottomUp("coverage", coverageLinkingMutator).Parallel()
 		ctx.TopDown("vndk_deps", sabiDepsMutator)
 
@@ -500,10 +502,17 @@
 func (ctx *moduleContextImpl) sdkVersion() string {
 	if ctx.ctx.Device() {
 		if ctx.useVndk() {
-			return "current"
-		} else {
-			return String(ctx.mod.Properties.Sdk_version)
+			vndk_ver := ctx.ctx.DeviceConfig().VndkVersion()
+			if vndk_ver == "current" {
+				platform_vndk_ver := ctx.ctx.DeviceConfig().PlatformVndkVersion()
+				if inList(platform_vndk_ver, ctx.ctx.Config().PlatformVersionCombinedCodenames()) {
+					return "current"
+				}
+				return platform_vndk_ver
+			}
+			return vndk_ver
 		}
+		return String(ctx.mod.Properties.Sdk_version)
 	}
 	return ""
 }
@@ -536,7 +545,7 @@
 		isUnsanitizedVariant = sanitize.isUnsanitizedVariant()
 	}
 	vendorAvailable := Bool(ctx.mod.VendorProperties.Vendor_available)
-	return vendorAvailable && isUnsanitizedVariant && ctx.ctx.Device() && ((ctx.useVndk() && ctx.isVndk()) || inList(ctx.baseModuleName(), llndkLibraries))
+	return isUnsanitizedVariant && ctx.ctx.Device() && ((ctx.useVndk() && ctx.isVndk() && vendorAvailable) || inList(ctx.baseModuleName(), llndkLibraries))
 }
 
 func (ctx *moduleContextImpl) selectedStl() string {
@@ -1421,6 +1430,7 @@
 		&VndkProperties{},
 		&LTOProperties{},
 		&PgoProperties{},
+		&android.ProtoProperties{},
 	)
 
 	android.InitDefaultsModule(module)
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 19e4703..2efee59 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -22,7 +22,6 @@
 	"io/ioutil"
 	"os"
 	"reflect"
-	"regexp"
 	"sort"
 	"strings"
 	"testing"
@@ -147,9 +146,9 @@
 	ctx := createTestContext(t, config, bp)
 
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	failIfErrored(t, errs)
+	android.FailIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
-	failIfErrored(t, errs)
+	android.FailIfErrored(t, errs)
 
 	return ctx
 }
@@ -178,13 +177,13 @@
 
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
 	if len(errs) > 0 {
-		failIfNoMatchingErrors(t, pattern, errs)
+		android.FailIfNoMatchingErrors(t, pattern, errs)
 		return
 	}
 
 	_, errs = ctx.PrepareBuildActions(config)
 	if len(errs) > 0 {
-		failIfNoMatchingErrors(t, pattern, errs)
+		android.FailIfNoMatchingErrors(t, pattern, errs)
 		return
 	}
 
@@ -1064,38 +1063,6 @@
 	}
 }
 
-func failIfErrored(t *testing.T, errs []error) {
-	if len(errs) > 0 {
-		for _, err := range errs {
-			t.Error(err)
-		}
-		t.FailNow()
-	}
-}
-
-func failIfNoMatchingErrors(t *testing.T, pattern string, errs []error) {
-	matcher, err := regexp.Compile(pattern)
-	if err != nil {
-		t.Errorf("failed to compile regular expression %q because %s", pattern, err)
-	}
-
-	found := false
-
-	for _, err := range errs {
-		if matcher.FindStringIndex(err.Error()) != nil {
-			found = true
-			break
-		}
-	}
-
-	if !found {
-		t.Errorf("missing the expected error %q (checked %d error(s))", pattern, len(errs))
-		for i, err := range errs {
-			t.Errorf("errs[%d] = %s", i, err)
-		}
-	}
-}
-
 func getOutputPaths(ctx *android.TestContext, variant string, moduleNames []string) (paths android.Paths) {
 	for _, moduleName := range moduleNames {
 		module := ctx.ModuleForTests(moduleName, variant).Module().(*Module)
diff --git a/cc/compiler.go b/cc/compiler.go
index 83bb331..6154758 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -106,6 +106,9 @@
 		// list of directories relative to the Blueprints file that will
 		// be added to the aidl include paths.
 		Local_include_dirs []string
+
+		// whether to generate traces (for systrace) for this interface
+		Generate_traces *bool
 	}
 
 	Renderscript struct {
@@ -305,8 +308,13 @@
 	}
 
 	if ctx.useVndk() {
+		// sdkVersion() returns VNDK version for vendor modules.
+		version := ctx.sdkVersion()
+		if version == "current" {
+			version = "__ANDROID_API_FUTURE__"
+		}
 		flags.GlobalFlags = append(flags.GlobalFlags,
-			"-D__ANDROID_API__=__ANDROID_API_FUTURE__", "-D__ANDROID_VNDK__")
+			"-D__ANDROID_API__="+version, "-D__ANDROID_VNDK__")
 	}
 
 	instructionSet := String(compiler.Properties.Instruction_set)
@@ -477,6 +485,10 @@
 			flags.aidlFlags = append(flags.aidlFlags, includeDirsToFlags(rootAidlIncludeDirs))
 		}
 
+		if Bool(compiler.Properties.Aidl.Generate_traces) {
+			flags.aidlFlags = append(flags.aidlFlags, "-t")
+		}
+
 		flags.GlobalFlags = append(flags.GlobalFlags,
 			"-I"+android.PathForModuleGen(ctx, "aidl").String())
 	}
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 471db1d..279ceef 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -215,6 +215,10 @@
 	return SanitizerRuntimeLibrary(t, "ubsan_standalone")
 }
 
+func UndefinedBehaviorSanitizerMinimalRuntimeLibrary(t Toolchain) string {
+	return SanitizerRuntimeLibrary(t, "ubsan_minimal")
+}
+
 func ThreadSanitizerRuntimeLibrary(t Toolchain) string {
 	return SanitizerRuntimeLibrary(t, "tsan")
 }
diff --git a/cc/library.go b/cc/library.go
index 4b2abaf..76f8a8c 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -643,8 +643,8 @@
 func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
 	//Also take into account object re-use.
 	if len(objs.sAbiDumpFiles) > 0 && ctx.createVndkSourceAbiDump() {
-		vndkVersion := "current"
-		if ver := ctx.DeviceConfig().VndkVersion(); ver != "" {
+		vndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
+		if ver := ctx.DeviceConfig().VndkVersion(); ver != "" && ver != "current" {
 			vndkVersion = ver
 		}
 
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index b573c2e..6e64acf 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -76,7 +76,17 @@
 }
 
 func (stub *llndkStubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
-	objs, versionScript := compileStubLibrary(ctx, flags, String(stub.Properties.Symbol_file), "current", "--vndk")
+	vndk_ver := ctx.DeviceConfig().VndkVersion()
+	if vndk_ver == "current" {
+		platform_vndk_ver := ctx.DeviceConfig().PlatformVndkVersion()
+		if !inList(platform_vndk_ver, ctx.Config().PlatformVersionCombinedCodenames()) {
+			vndk_ver = platform_vndk_ver
+		}
+	} else if vndk_ver == "" {
+		// For non-enforcing devices, use "current"
+		vndk_ver = "current"
+	}
+	objs, versionScript := compileStubLibrary(ctx, flags, String(stub.Properties.Symbol_file), vndk_ver, "--vndk")
 	stub.versionScriptPath = versionScript
 	return objs
 }
diff --git a/cc/makevars.go b/cc/makevars.go
index 23910d3..0386f93 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -276,6 +276,7 @@
 		if target.Os.Class == android.Device {
 			ctx.Strict(secondPrefix+"ADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.AddressSanitizerRuntimeLibrary(toolchain), ".so"))
 			ctx.Strict(secondPrefix+"UBSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain), ".so"))
+			ctx.Strict(secondPrefix+"UBSAN_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain), ".a"))
 			ctx.Strict(secondPrefix+"TSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.ThreadSanitizerRuntimeLibrary(toolchain), ".so"))
 		}
 
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 0bcbb12..ee549bc 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -41,7 +41,8 @@
 	cfiExportsMapPath  = "build/soong/cc/config/cfi_exports.map"
 	cfiStaticLibsMutex sync.Mutex
 
-	intOverflowCflags = []string{"-fsanitize-blacklist=build/soong/cc/config/integer_overflow_blacklist.txt"}
+	intOverflowCflags   = []string{"-fsanitize-blacklist=build/soong/cc/config/integer_overflow_blacklist.txt"}
+	minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer", "-fno-sanitize-recover=integer"}
 )
 
 type sanitizerType int
@@ -111,9 +112,10 @@
 		Blacklist *string
 	} `android:"arch_variant"`
 
-	SanitizerEnabled bool `blueprint:"mutated"`
-	SanitizeDep      bool `blueprint:"mutated"`
-	InSanitizerDir   bool `blueprint:"mutated"`
+	SanitizerEnabled  bool `blueprint:"mutated"`
+	SanitizeDep       bool `blueprint:"mutated"`
+	MinimalRuntimeDep bool `blueprint:"mutated"`
+	InSanitizerDir    bool `blueprint:"mutated"`
 }
 
 type sanitize struct {
@@ -298,6 +300,11 @@
 }
 
 func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
+	minimalRuntimePath := "${config.ClangAsanLibDir}/" + config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(ctx.toolchain()) + ".a"
+
+	if ctx.Device() && sanitize.Properties.MinimalRuntimeDep {
+		flags.LdFlags = append(flags.LdFlags, minimalRuntimePath)
+	}
 	if !sanitize.Properties.SanitizerEnabled {
 		return flags
 	}
@@ -428,6 +435,7 @@
 
 	if len(sanitizers) > 0 {
 		sanitizeArg := "-fsanitize=" + strings.Join(sanitizers, ",")
+
 		flags.CFlags = append(flags.CFlags, sanitizeArg)
 		if ctx.Host() {
 			flags.CFlags = append(flags.CFlags, "-fno-sanitize-recover=all")
@@ -437,6 +445,11 @@
 			_, flags.LdFlags = removeFromList("-Wl,--no-undefined", flags.LdFlags)
 		} else {
 			flags.CFlags = append(flags.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
+
+			if enableMinimalRuntime(sanitize) {
+				flags.CFlags = append(flags.CFlags, strings.Join(minimalRuntimeFlags, " "))
+				flags.libFlags = append([]string{minimalRuntimePath}, flags.libFlags...)
+			}
 		}
 	}
 
@@ -586,6 +599,24 @@
 	}
 }
 
+// Propagate the ubsan minimal runtime dependency when there are integer overflow sanitized static dependencies.
+func minimalRuntimeDepsMutator() func(android.TopDownMutatorContext) {
+	return func(mctx android.TopDownMutatorContext) {
+		if c, ok := mctx.Module().(*Module); ok && c.sanitize != nil {
+			mctx.VisitDepsDepthFirst(func(module android.Module) {
+				if d, ok := module.(*Module); ok && d.static() && d.sanitize != nil {
+
+					// If a static dependency will be built with the minimal runtime,
+					// make sure we include the ubsan minimal runtime.
+					if enableMinimalRuntime(d.sanitize) {
+						c.sanitize.Properties.MinimalRuntimeDep = true
+					}
+				}
+			})
+		}
+	}
+}
+
 // Create sanitized variants for modules that need them
 func sanitizerMutator(t sanitizerType) func(android.BottomUpMutatorContext) {
 	return func(mctx android.BottomUpMutatorContext) {
@@ -656,6 +687,18 @@
 	}).(*[]string)
 }
 
+func enableMinimalRuntime(sanitize *sanitize) bool {
+	if !Bool(sanitize.Properties.Sanitize.Address) &&
+		(Bool(sanitize.Properties.Sanitize.Integer_overflow) ||
+			len(sanitize.Properties.Sanitize.Misc_undefined) > 0) &&
+		!(Bool(sanitize.Properties.Sanitize.Diag.Integer_overflow) ||
+			Bool(sanitize.Properties.Sanitize.Diag.Cfi) ||
+			len(sanitize.Properties.Sanitize.Diag.Misc_undefined) > 0) {
+		return true
+	}
+	return false
+}
+
 func cfiMakeVarsProvider(ctx android.MakeVarsContext) {
 	cfiStaticLibs := cfiStaticLibs(ctx.Config())
 	sort.Strings(*cfiStaticLibs)
diff --git a/cc/test_data_test.go b/cc/test_data_test.go
index 434edcd..4a7b0f7 100644
--- a/cc/test_data_test.go
+++ b/cc/test_data_test.go
@@ -135,9 +135,9 @@
 			ctx.Register()
 
 			_, errs := ctx.ParseBlueprintsFiles("Blueprints")
-			fail(t, errs)
+			android.FailIfErrored(t, errs)
 			_, errs = ctx.PrepareBuildActions(config)
-			fail(t, errs)
+			android.FailIfErrored(t, errs)
 
 			foo := ctx.ModuleForTests("foo", "")
 
@@ -186,12 +186,3 @@
 func (test *testDataTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	test.data = ctx.ExpandSources(test.Properties.Data, nil)
 }
-
-func fail(t *testing.T, errs []error) {
-	if len(errs) > 0 {
-		for _, err := range errs {
-			t.Error(err)
-		}
-		t.FailNow()
-	}
-}
diff --git a/java/java.go b/java/java.go
index ae48bd6..d4db715 100644
--- a/java/java.go
+++ b/java/java.go
@@ -175,6 +175,9 @@
 		// directories that should be added as include directories for any aidl sources of modules
 		// that depend on this module, as well as to aidl for this module.
 		Export_include_dirs []string
+
+		// whether to generate traces (for systrace) for this interface
+		Generate_traces *bool
 	}
 
 	// If true, export a copy of the module as a -hostdex module for host testing.
@@ -534,6 +537,10 @@
 		flags = append(flags, "-I"+src.String())
 	}
 
+	if Bool(j.deviceProperties.Aidl.Generate_traces) {
+		flags = append(flags, "-t")
+	}
+
 	return flags
 }
 
@@ -1277,6 +1284,7 @@
 	module.AddProperties(
 		&CompilerProperties{},
 		&CompilerDeviceProperties{},
+		&android.ProtoProperties{},
 	)
 
 	android.InitDefaultsModule(module)
diff --git a/java/java_test.go b/java/java_test.go
index 5d6a6e0..6ef406f 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -190,9 +190,9 @@
 func run(t *testing.T, ctx *android.TestContext, config android.Config) {
 	t.Helper()
 	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	fail(t, errs)
+	android.FailIfErrored(t, errs)
 	_, errs = ctx.PrepareBuildActions(config)
-	fail(t, errs)
+	android.FailIfErrored(t, errs)
 }
 
 func testJava(t *testing.T, bp string) *android.TestContext {
@@ -977,13 +977,3 @@
 		t.Errorf(`foo inputs %v != ["java-fg/c.java"]`, javac.Inputs)
 	}
 }
-
-func fail(t *testing.T, errs []error) {
-	t.Helper()
-	if len(errs) > 0 {
-		for _, err := range errs {
-			t.Error(err)
-		}
-		t.FailNow()
-	}
-}
diff --git a/python/python_test.go b/python/python_test.go
index 176c6ec..ad80167 100644
--- a/python/python_test.go
+++ b/python/python_test.go
@@ -347,7 +347,7 @@
 			ctx.Register()
 			ctx.MockFileSystem(d.mockFiles)
 			_, testErrs := ctx.ParseBlueprintsFiles(bpFile)
-			fail(t, testErrs)
+			android.FailIfErrored(t, testErrs)
 			_, actErrs := ctx.PrepareBuildActions(config)
 			if len(actErrs) > 0 {
 				testErrs = append(testErrs, expectErrors(t, actErrs, d.errors)...)
@@ -360,7 +360,7 @@
 							e.parSpec, e.depsParSpecs)...)
 				}
 			}
-			fail(t, testErrs)
+			android.FailIfErrored(t, testErrs)
 		})
 	}
 }
@@ -458,12 +458,3 @@
 func tearDownBuildEnv(buildDir string) {
 	os.RemoveAll(buildDir)
 }
-
-func fail(t *testing.T, errs []error) {
-	if len(errs) > 0 {
-		for _, err := range errs {
-			t.Error(err)
-		}
-		t.FailNow()
-	}
-}