Merge "Fix non-determinism in tests."
diff --git a/android/Android.bp b/android/Android.bp
index 487372b..977345b 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -19,6 +19,7 @@
         "csuite_config.go",
         "defaults.go",
         "defs.go",
+        "depset.go",
         "expand.go",
         "filegroup.go",
         "hooks.go",
@@ -61,6 +62,7 @@
         "arch_test.go",
         "config_test.go",
         "csuite_config_test.go",
+        "depset_test.go",
         "expand_test.go",
         "module_test.go",
         "mutator_test.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 045cb59..dfc68c4 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -107,6 +107,25 @@
 	a.EntryMap[name] = []string{path.String()}
 }
 
+func (a *AndroidMkEntries) SetOptionalPath(name string, path OptionalPath) {
+	if path.Valid() {
+		a.SetPath(name, path.Path())
+	}
+}
+
+func (a *AndroidMkEntries) AddPath(name string, path Path) {
+	if _, ok := a.EntryMap[name]; !ok {
+		a.entryOrder = append(a.entryOrder, name)
+	}
+	a.EntryMap[name] = append(a.EntryMap[name], path.String())
+}
+
+func (a *AndroidMkEntries) AddOptionalPath(name string, path OptionalPath) {
+	if path.Valid() {
+		a.AddPath(name, path.Path())
+	}
+}
+
 func (a *AndroidMkEntries) SetBoolIfTrue(name string, flag bool) {
 	if flag {
 		if _, ok := a.EntryMap[name]; !ok {
diff --git a/android/depset.go b/android/depset.go
new file mode 100644
index 0000000..f707094
--- /dev/null
+++ b/android/depset.go
@@ -0,0 +1,190 @@
+// Copyright 2020 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 "fmt"
+
+// DepSet is designed to be conceptually compatible with Bazel's depsets:
+// https://docs.bazel.build/versions/master/skylark/depsets.html
+
+// A DepSet efficiently stores Paths from transitive dependencies without copying. It is stored
+// as a DAG of DepSet nodes, each of which has some direct contents and a list of dependency
+// DepSet nodes.
+//
+// A DepSet has an order that will be used to walk the DAG when ToList() is called.  The order
+// can be POSTORDER, PREORDER, or TOPOLOGICAL.  POSTORDER and PREORDER orders return a postordered
+// or preordered left to right flattened list.  TOPOLOGICAL returns a list that guarantees that
+// elements of children are listed after all of their parents (unless there are duplicate direct
+// elements in the DepSet or any of its transitive dependencies, in which case the ordering of the
+// duplicated element is not guaranteed).
+//
+// A DepSet is created by NewDepSet or NewDepSetBuilder.Build from the Paths for direct contents
+// and the *DepSets of dependencies. A DepSet is immutable once created.
+type DepSet struct {
+	preorder   bool
+	reverse    bool
+	order      DepSetOrder
+	direct     Paths
+	transitive []*DepSet
+}
+
+// DepSetBuilder is used to create an immutable DepSet.
+type DepSetBuilder struct {
+	order      DepSetOrder
+	direct     Paths
+	transitive []*DepSet
+}
+
+type DepSetOrder int
+
+const (
+	PREORDER DepSetOrder = iota
+	POSTORDER
+	TOPOLOGICAL
+)
+
+func (o DepSetOrder) String() string {
+	switch o {
+	case PREORDER:
+		return "PREORDER"
+	case POSTORDER:
+		return "POSTORDER"
+	case TOPOLOGICAL:
+		return "TOPOLOGICAL"
+	default:
+		panic(fmt.Errorf("Invalid DepSetOrder %d", o))
+	}
+}
+
+// NewDepSet returns an immutable DepSet with the given order, direct and transitive contents.
+func NewDepSet(order DepSetOrder, direct Paths, transitive []*DepSet) *DepSet {
+	var directCopy Paths
+	var transitiveCopy []*DepSet
+	if order == TOPOLOGICAL {
+		directCopy = ReversePaths(direct)
+		transitiveCopy = reverseDepSets(transitive)
+	} else {
+		// Use copy instead of append(nil, ...) to make a slice that is exactly the size of the input
+		// slice.  The DepSet is immutable, there is no need for additional capacity.
+		directCopy = make(Paths, len(direct))
+		copy(directCopy, direct)
+		transitiveCopy = make([]*DepSet, len(transitive))
+		copy(transitiveCopy, transitive)
+	}
+
+	for _, dep := range transitive {
+		if dep.order != order {
+			panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s",
+				order, dep.order))
+		}
+	}
+
+	return &DepSet{
+		preorder:   order == PREORDER,
+		reverse:    order == TOPOLOGICAL,
+		order:      order,
+		direct:     directCopy,
+		transitive: transitiveCopy,
+	}
+}
+
+// NewDepSetBuilder returns a DepSetBuilder to create an immutable DepSet with the given order.
+func NewDepSetBuilder(order DepSetOrder) *DepSetBuilder {
+	return &DepSetBuilder{order: order}
+}
+
+// Direct adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct
+// contents are to the right of any existing direct contents.
+func (b *DepSetBuilder) Direct(direct ...Path) *DepSetBuilder {
+	b.direct = append(b.direct, direct...)
+	return b
+}
+
+// Transitive adds transitive contents to the DepSet being built by a DepSetBuilder. Newly added
+// transitive contents are to the right of any existing transitive contents.
+func (b *DepSetBuilder) Transitive(transitive ...*DepSet) *DepSetBuilder {
+	b.transitive = append(b.transitive, transitive...)
+	return b
+}
+
+// Returns the DepSet being built by this DepSetBuilder.  The DepSetBuilder retains its contents
+// for creating more DepSets.
+func (b *DepSetBuilder) Build() *DepSet {
+	return NewDepSet(b.order, b.direct, b.transitive)
+}
+
+// walk calls the visit method in depth-first order on a DepSet, preordered if d.preorder is set,
+// otherwise postordered.
+func (d *DepSet) walk(visit func(Paths)) {
+	visited := make(map[*DepSet]bool)
+
+	var dfs func(d *DepSet)
+	dfs = func(d *DepSet) {
+		visited[d] = true
+		if d.preorder {
+			visit(d.direct)
+		}
+		for _, dep := range d.transitive {
+			if !visited[dep] {
+				dfs(dep)
+			}
+		}
+
+		if !d.preorder {
+			visit(d.direct)
+		}
+	}
+
+	dfs(d)
+}
+
+// ToList returns the DepSet flattened to a list.  The order in the list is based on the order
+// of the DepSet.  POSTORDER and PREORDER orders return a postordered or preordered left to right
+// flattened list.  TOPOLOGICAL returns a list that guarantees that elements of children are listed
+// after all of their parents (unless there are duplicate direct elements in the DepSet or any of
+// its transitive dependencies, in which case the ordering of the duplicated element is not
+// guaranteed).
+func (d *DepSet) ToList() Paths {
+	var list Paths
+	d.walk(func(paths Paths) {
+		list = append(list, paths...)
+	})
+	list = FirstUniquePaths(list)
+	if d.reverse {
+		reversePathsInPlace(list)
+	}
+	return list
+}
+
+// ToSortedList returns the direct and transitive contents of a DepSet in lexically sorted order
+// with duplicates removed.
+func (d *DepSet) ToSortedList() Paths {
+	list := d.ToList()
+	return SortedUniquePaths(list)
+}
+
+func reversePathsInPlace(list Paths) {
+	for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
+		list[i], list[j] = list[j], list[i]
+	}
+}
+
+func reverseDepSets(list []*DepSet) []*DepSet {
+	ret := make([]*DepSet, len(list))
+	for i := range list {
+		ret[i] = list[len(list)-1-i]
+	}
+	return ret
+}
diff --git a/android/depset_test.go b/android/depset_test.go
new file mode 100644
index 0000000..c328127
--- /dev/null
+++ b/android/depset_test.go
@@ -0,0 +1,304 @@
+// Copyright 2020 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 (
+	"fmt"
+	"reflect"
+	"strings"
+	"testing"
+)
+
+func ExampleDepSet_ToList_postordered() {
+	a := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("a")).Build()
+	b := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("b")).Transitive(a).Build()
+	c := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("c")).Transitive(a).Build()
+	d := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("d")).Transitive(b, c).Build()
+
+	fmt.Println(d.ToList().Strings())
+	// Output: [a b c d]
+}
+
+func ExampleDepSet_ToList_preordered() {
+	a := NewDepSetBuilder(PREORDER).Direct(PathForTesting("a")).Build()
+	b := NewDepSetBuilder(PREORDER).Direct(PathForTesting("b")).Transitive(a).Build()
+	c := NewDepSetBuilder(PREORDER).Direct(PathForTesting("c")).Transitive(a).Build()
+	d := NewDepSetBuilder(PREORDER).Direct(PathForTesting("d")).Transitive(b, c).Build()
+
+	fmt.Println(d.ToList().Strings())
+	// Output: [d b a c]
+}
+
+func ExampleDepSet_ToList_topological() {
+	a := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("a")).Build()
+	b := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("b")).Transitive(a).Build()
+	c := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("c")).Transitive(a).Build()
+	d := NewDepSetBuilder(TOPOLOGICAL).Direct(PathForTesting("d")).Transitive(b, c).Build()
+
+	fmt.Println(d.ToList().Strings())
+	// Output: [d b c a]
+}
+
+func ExampleDepSet_ToSortedList() {
+	a := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("a")).Build()
+	b := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("b")).Transitive(a).Build()
+	c := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("c")).Transitive(a).Build()
+	d := NewDepSetBuilder(POSTORDER).Direct(PathForTesting("d")).Transitive(b, c).Build()
+
+	fmt.Println(d.ToSortedList().Strings())
+	// Output: [a b c d]
+}
+
+// Tests based on Bazel's ExpanderTestBase.java to ensure compatibility
+// https://github.com/bazelbuild/bazel/blob/master/src/test/java/com/google/devtools/build/lib/collect/nestedset/ExpanderTestBase.java
+func TestDepSet(t *testing.T) {
+	a := PathForTesting("a")
+	b := PathForTesting("b")
+	c := PathForTesting("c")
+	c2 := PathForTesting("c2")
+	d := PathForTesting("d")
+	e := PathForTesting("e")
+
+	tests := []struct {
+		name                             string
+		depSet                           func(t *testing.T, order DepSetOrder) *DepSet
+		postorder, preorder, topological []string
+	}{
+		{
+			name: "simple",
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+				return NewDepSet(order, Paths{c, a, b}, nil)
+			},
+			postorder:   []string{"c", "a", "b"},
+			preorder:    []string{"c", "a", "b"},
+			topological: []string{"c", "a", "b"},
+		},
+		{
+			name: "simpleNoDuplicates",
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+				return NewDepSet(order, Paths{c, a, a, a, b}, nil)
+			},
+			postorder:   []string{"c", "a", "b"},
+			preorder:    []string{"c", "a", "b"},
+			topological: []string{"c", "a", "b"},
+		},
+		{
+			name: "nesting",
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+				subset := NewDepSet(order, Paths{c, a, e}, nil)
+				return NewDepSet(order, Paths{b, d}, []*DepSet{subset})
+			},
+			postorder:   []string{"c", "a", "e", "b", "d"},
+			preorder:    []string{"b", "d", "c", "a", "e"},
+			topological: []string{"b", "d", "c", "a", "e"},
+		},
+		{
+			name: "builderReuse",
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+				assertEquals := func(t *testing.T, w, g Paths) {
+					if !reflect.DeepEqual(w, g) {
+						t.Errorf("want %q, got %q", w, g)
+					}
+				}
+				builder := NewDepSetBuilder(order)
+				assertEquals(t, nil, builder.Build().ToList())
+
+				builder.Direct(b)
+				assertEquals(t, Paths{b}, builder.Build().ToList())
+
+				builder.Direct(d)
+				assertEquals(t, Paths{b, d}, builder.Build().ToList())
+
+				child := NewDepSetBuilder(order).Direct(c, a, e).Build()
+				builder.Transitive(child)
+				return builder.Build()
+			},
+			postorder:   []string{"c", "a", "e", "b", "d"},
+			preorder:    []string{"b", "d", "c", "a", "e"},
+			topological: []string{"b", "d", "c", "a", "e"},
+		},
+		{
+			name: "builderChaining",
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+				return NewDepSetBuilder(order).Direct(b).Direct(d).
+					Transitive(NewDepSetBuilder(order).Direct(c, a, e).Build()).Build()
+			},
+			postorder:   []string{"c", "a", "e", "b", "d"},
+			preorder:    []string{"b", "d", "c", "a", "e"},
+			topological: []string{"b", "d", "c", "a", "e"},
+		},
+		{
+			name: "transitiveDepsHandledSeparately",
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+				subset := NewDepSetBuilder(order).Direct(c, a, e).Build()
+				builder := NewDepSetBuilder(order)
+				// The fact that we add the transitive subset between the Direct(b) and Direct(d)
+				// calls should not change the result.
+				builder.Direct(b)
+				builder.Transitive(subset)
+				builder.Direct(d)
+				return builder.Build()
+			},
+			postorder:   []string{"c", "a", "e", "b", "d"},
+			preorder:    []string{"b", "d", "c", "a", "e"},
+			topological: []string{"b", "d", "c", "a", "e"},
+		},
+		{
+			name: "nestingNoDuplicates",
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+				subset := NewDepSetBuilder(order).Direct(c, a, e).Build()
+				return NewDepSetBuilder(order).Direct(b, d, e).Transitive(subset).Build()
+			},
+			postorder:   []string{"c", "a", "e", "b", "d"},
+			preorder:    []string{"b", "d", "e", "c", "a"},
+			topological: []string{"b", "d", "c", "a", "e"},
+		},
+		{
+			name: "chain",
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+				c := NewDepSetBuilder(order).Direct(c).Build()
+				b := NewDepSetBuilder(order).Direct(b).Transitive(c).Build()
+				a := NewDepSetBuilder(order).Direct(a).Transitive(b).Build()
+
+				return a
+			},
+			postorder:   []string{"c", "b", "a"},
+			preorder:    []string{"a", "b", "c"},
+			topological: []string{"a", "b", "c"},
+		},
+		{
+			name: "diamond",
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+				d := NewDepSetBuilder(order).Direct(d).Build()
+				c := NewDepSetBuilder(order).Direct(c).Transitive(d).Build()
+				b := NewDepSetBuilder(order).Direct(b).Transitive(d).Build()
+				a := NewDepSetBuilder(order).Direct(a).Transitive(b).Transitive(c).Build()
+
+				return a
+			},
+			postorder:   []string{"d", "b", "c", "a"},
+			preorder:    []string{"a", "b", "d", "c"},
+			topological: []string{"a", "b", "c", "d"},
+		},
+		{
+			name: "extendedDiamond",
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+				d := NewDepSetBuilder(order).Direct(d).Build()
+				e := NewDepSetBuilder(order).Direct(e).Build()
+				b := NewDepSetBuilder(order).Direct(b).Transitive(d).Transitive(e).Build()
+				c := NewDepSetBuilder(order).Direct(c).Transitive(e).Transitive(d).Build()
+				a := NewDepSetBuilder(order).Direct(a).Transitive(b).Transitive(c).Build()
+				return a
+			},
+			postorder:   []string{"d", "e", "b", "c", "a"},
+			preorder:    []string{"a", "b", "d", "e", "c"},
+			topological: []string{"a", "b", "c", "e", "d"},
+		},
+		{
+			name: "extendedDiamondRightArm",
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+				d := NewDepSetBuilder(order).Direct(d).Build()
+				e := NewDepSetBuilder(order).Direct(e).Build()
+				b := NewDepSetBuilder(order).Direct(b).Transitive(d).Transitive(e).Build()
+				c2 := NewDepSetBuilder(order).Direct(c2).Transitive(e).Transitive(d).Build()
+				c := NewDepSetBuilder(order).Direct(c).Transitive(c2).Build()
+				a := NewDepSetBuilder(order).Direct(a).Transitive(b).Transitive(c).Build()
+				return a
+			},
+			postorder:   []string{"d", "e", "b", "c2", "c", "a"},
+			preorder:    []string{"a", "b", "d", "e", "c", "c2"},
+			topological: []string{"a", "b", "c", "c2", "e", "d"},
+		},
+		{
+			name: "orderConflict",
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+				child1 := NewDepSetBuilder(order).Direct(a, b).Build()
+				child2 := NewDepSetBuilder(order).Direct(b, a).Build()
+				parent := NewDepSetBuilder(order).Transitive(child1).Transitive(child2).Build()
+				return parent
+			},
+			postorder:   []string{"a", "b"},
+			preorder:    []string{"a", "b"},
+			topological: []string{"b", "a"},
+		},
+		{
+			name: "orderConflictNested",
+			depSet: func(t *testing.T, order DepSetOrder) *DepSet {
+				a := NewDepSetBuilder(order).Direct(a).Build()
+				b := NewDepSetBuilder(order).Direct(b).Build()
+				child1 := NewDepSetBuilder(order).Transitive(a).Transitive(b).Build()
+				child2 := NewDepSetBuilder(order).Transitive(b).Transitive(a).Build()
+				parent := NewDepSetBuilder(order).Transitive(child1).Transitive(child2).Build()
+				return parent
+			},
+			postorder:   []string{"a", "b"},
+			preorder:    []string{"a", "b"},
+			topological: []string{"b", "a"},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			t.Run("postorder", func(t *testing.T) {
+				depSet := tt.depSet(t, POSTORDER)
+				if g, w := depSet.ToList().Strings(), tt.postorder; !reflect.DeepEqual(g, w) {
+					t.Errorf("expected ToList() = %q, got %q", w, g)
+				}
+			})
+			t.Run("preorder", func(t *testing.T) {
+				depSet := tt.depSet(t, PREORDER)
+				if g, w := depSet.ToList().Strings(), tt.preorder; !reflect.DeepEqual(g, w) {
+					t.Errorf("expected ToList() = %q, got %q", w, g)
+				}
+			})
+			t.Run("topological", func(t *testing.T) {
+				depSet := tt.depSet(t, TOPOLOGICAL)
+				if g, w := depSet.ToList().Strings(), tt.topological; !reflect.DeepEqual(g, w) {
+					t.Errorf("expected ToList() = %q, got %q", w, g)
+				}
+			})
+		})
+	}
+}
+
+func TestDepSetInvalidOrder(t *testing.T) {
+	orders := []DepSetOrder{POSTORDER, PREORDER, TOPOLOGICAL}
+
+	run := func(t *testing.T, order1, order2 DepSetOrder) {
+		defer func() {
+			if r := recover(); r != nil {
+				if err, ok := r.(error); !ok {
+					t.Fatalf("expected panic error, got %v", err)
+				} else if !strings.Contains(err.Error(), "incompatible order") {
+					t.Fatalf("expected incompatible order error, got %v", err)
+				}
+			}
+		}()
+		NewDepSet(order1, nil, []*DepSet{NewDepSet(order2, nil, nil)})
+		t.Fatal("expected panic")
+	}
+
+	for _, order1 := range orders {
+		t.Run(order1.String(), func(t *testing.T) {
+			for _, order2 := range orders {
+				t.Run(order2.String(), func(t *testing.T) {
+					if order1 != order2 {
+						run(t, order1, order2)
+					}
+				})
+			}
+		})
+	}
+}
diff --git a/android/paths.go b/android/paths.go
index d13b6d8..20ff55e 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -486,6 +486,10 @@
 	return ret
 }
 
+func CopyOfPaths(paths Paths) Paths {
+	return append(Paths(nil), paths...)
+}
+
 // FirstUniquePaths returns all unique elements of a Paths, keeping the first copy of each.  It
 // modifies the Paths slice contents in place, and returns a subslice of the original slice.
 func FirstUniquePaths(list Paths) Paths {
@@ -496,7 +500,8 @@
 	return firstUniquePathsList(list)
 }
 
-// SortedUniquePaths returns what its name says
+// SortedUniquePaths returns all unique elements of a Paths in sorted order.  It modifies the
+// Paths slice contents in place, and returns a subslice of the original slice.
 func SortedUniquePaths(list Paths) Paths {
 	unique := FirstUniquePaths(list)
 	sort.Slice(unique, func(i, j int) bool {
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 9f4df28..269ad5d 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -225,7 +225,7 @@
 // PrebuiltSourceDepsMutator adds dependencies to the prebuilt module from the
 // corresponding source module, if one exists for the same variant.
 func PrebuiltSourceDepsMutator(ctx BottomUpMutatorContext) {
-	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
+	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Enabled() && m.Prebuilt() != nil {
 		p := m.Prebuilt()
 		if !p.properties.PrebuiltRenamedToSource {
 			name := m.base().BaseModuleName()
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 8029b85..6c3cd9e 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -22,9 +22,10 @@
 )
 
 var prebuiltsTests = []struct {
-	name     string
-	modules  string
-	prebuilt []OsClass
+	name      string
+	replaceBp bool // modules is added to default bp boilerplate if false.
+	modules   string
+	prebuilt  []OsType
 }{
 	{
 		name: "no prebuilt",
@@ -42,7 +43,7 @@
 				prefer: false,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: []OsClass{Device, Host},
+		prebuilt: []OsType{Android, BuildOs},
 	},
 	{
 		name: "no source prebuilt preferred",
@@ -52,7 +53,7 @@
 				prefer: true,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: []OsClass{Device, Host},
+		prebuilt: []OsType{Android, BuildOs},
 	},
 	{
 		name: "prebuilt not preferred",
@@ -80,7 +81,7 @@
 				prefer: true,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: []OsClass{Device, Host},
+		prebuilt: []OsType{Android, BuildOs},
 	},
 	{
 		name: "prebuilt no file not preferred",
@@ -120,7 +121,7 @@
 				prefer: true,
 				srcs: [":fg"],
 			}`,
-		prebuilt: []OsClass{Device, Host},
+		prebuilt: []OsType{Android, BuildOs},
 	},
 	{
 		name: "prebuilt module for device only",
@@ -135,7 +136,7 @@
 				prefer: true,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: []OsClass{Device},
+		prebuilt: []OsType{Android},
 	},
 	{
 		name: "prebuilt file for host only",
@@ -153,7 +154,7 @@
 					},
 				},
 			}`,
-		prebuilt: []OsClass{Host},
+		prebuilt: []OsType{BuildOs},
 	},
 	{
 		name: "prebuilt override not preferred",
@@ -191,7 +192,72 @@
 				prefer: true,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: []OsClass{Device, Host},
+		prebuilt: []OsType{Android, BuildOs},
+	},
+	{
+		name:      "prebuilt including default-disabled OS",
+		replaceBp: true,
+		modules: `
+			source {
+				name: "foo",
+				deps: [":bar"],
+				target: {
+					windows: {
+						enabled: true,
+					},
+				},
+			}
+
+			source {
+				name: "bar",
+				target: {
+					windows: {
+						enabled: true,
+					},
+				},
+			}
+
+			prebuilt {
+				name: "bar",
+				prefer: true,
+				srcs: ["prebuilt_file"],
+				target: {
+					windows: {
+						enabled: true,
+					},
+				},
+			}`,
+		prebuilt: []OsType{Android, BuildOs, Windows},
+	},
+	{
+		name:      "fall back to source for default-disabled OS",
+		replaceBp: true,
+		modules: `
+			source {
+				name: "foo",
+				deps: [":bar"],
+				target: {
+					windows: {
+						enabled: true,
+					},
+				},
+			}
+
+			source {
+				name: "bar",
+				target: {
+					windows: {
+						enabled: true,
+					},
+				},
+			}
+
+			prebuilt {
+				name: "bar",
+				prefer: true,
+				srcs: ["prebuilt_file"],
+			}`,
+		prebuilt: []OsType{Android, BuildOs},
 	},
 }
 
@@ -203,14 +269,25 @@
 
 	for _, test := range prebuiltsTests {
 		t.Run(test.name, func(t *testing.T) {
-			bp := `
-				source {
-					name: "foo",
-					deps: [":bar"],
-				}
-				` + test.modules
+			bp := test.modules
+			if !test.replaceBp {
+				bp = bp + `
+					source {
+						name: "foo",
+						deps: [":bar"],
+					}`
+			}
 			config := TestArchConfig(buildDir, nil, bp, fs)
 
+			// Add windows to the target list to test the logic when a variant is
+			// disabled by default.
+			if !Windows.DefaultDisabled {
+				t.Errorf("windows is assumed to be disabled by default")
+			}
+			config.config.Targets[Windows] = []Target{
+				{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", ""},
+			}
+
 			ctx := NewTestArchContext()
 			registerTestPrebuiltBuildComponents(ctx)
 			ctx.RegisterModuleType("filegroup", FileGroupFactory)
@@ -223,7 +300,7 @@
 
 			for _, variant := range ctx.ModuleVariantsForTests("foo") {
 				foo := ctx.ModuleForTests("foo", variant)
-				t.Run(foo.Module().Target().Os.Class.String(), func(t *testing.T) {
+				t.Run(foo.Module().Target().Os.String(), func(t *testing.T) {
 					var dependsOnSourceModule, dependsOnPrebuiltModule bool
 					ctx.VisitDirectDeps(foo.Module(), func(m blueprint.Module) {
 						if _, ok := m.(*sourceModule); ok {
@@ -237,26 +314,38 @@
 						}
 					})
 
+					moduleIsDisabled := !foo.Module().Enabled()
 					deps := foo.Module().(*sourceModule).deps
-					if deps == nil || len(deps) != 1 {
-						t.Errorf("deps does not have single path, but is %v", deps)
+					if moduleIsDisabled {
+						if len(deps) > 0 {
+							t.Errorf("disabled module got deps: %v", deps)
+						}
+					} else {
+						if len(deps) != 1 {
+							t.Errorf("deps does not have single path, but is %v", deps)
+						}
 					}
+
 					var usingSourceFile, usingPrebuiltFile bool
-					if deps[0].String() == "source_file" {
+					if len(deps) > 0 && deps[0].String() == "source_file" {
 						usingSourceFile = true
 					}
-					if deps[0].String() == "prebuilt_file" {
+					if len(deps) > 0 && deps[0].String() == "prebuilt_file" {
 						usingPrebuiltFile = true
 					}
 
 					prebuilt := false
 					for _, os := range test.prebuilt {
-						if os == foo.Module().Target().Os.Class {
+						if os == foo.Module().Target().Os {
 							prebuilt = true
 						}
 					}
 
 					if prebuilt {
+						if moduleIsDisabled {
+							t.Errorf("dependent module for prebuilt is disabled")
+						}
+
 						if !dependsOnPrebuiltModule {
 							t.Errorf("doesn't depend on prebuilt module")
 						}
@@ -270,7 +359,7 @@
 						if usingSourceFile {
 							t.Errorf("using source_file")
 						}
-					} else {
+					} else if !moduleIsDisabled {
 						if dependsOnPrebuiltModule {
 							t.Errorf("depends on prebuilt module")
 						}
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 7595238..b9bcc3a 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -105,9 +105,10 @@
 		fmt.Fprintln(w, "LOCAL_MODULE :=", moduleName)
 		// /apex/<apex_name>/{lib|framework|...}
 		pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex", apexName, fi.installDir)
+		var modulePath string
 		if apexType == flattenedApex {
 			// /system/apex/<name>/{lib|framework|...}
-			modulePath := filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.installDir)
+			modulePath = filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.installDir)
 			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
 			if a.primaryApexType && !symbolFilesNotNeeded {
 				fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathWhenActivated)
@@ -131,6 +132,7 @@
 				fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", strings.Join(fi.module.NoticeFiles().Strings(), " "))
 			}
 		} else {
+			modulePath = pathWhenActivated
 			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", pathWhenActivated)
 
 			// For non-flattend APEXes, the merged notice file is attached to the APEX itself.
@@ -193,8 +195,13 @@
 			// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
 			// we will have foo.apk.apk
 			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.Stem(), ".apk"))
-			if app, ok := fi.module.(*java.AndroidApp); ok && len(app.JniCoverageOutputs()) > 0 {
-				fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", strings.Join(app.JniCoverageOutputs().Strings(), " "))
+			if app, ok := fi.module.(*java.AndroidApp); ok {
+				if jniCoverageOutputs := app.JniCoverageOutputs(); len(jniCoverageOutputs) > 0 {
+					fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", strings.Join(jniCoverageOutputs.Strings(), " "))
+				}
+				if jniLibSymbols := app.JNISymbolsInstalls(modulePath); len(jniLibSymbols) > 0 {
+					fmt.Fprintln(w, "LOCAL_SOONG_JNI_LIBS_SYMBOLS :=", jniLibSymbols.String())
+				}
 			}
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_app_prebuilt.mk")
 		case appSet:
@@ -203,6 +210,7 @@
 				panic(fmt.Sprintf("Expected %s to be AndroidAppSet", fi.module))
 			}
 			fmt.Fprintln(w, "LOCAL_APK_SET_MASTER_FILE :=", as.MasterFile())
+			fmt.Fprintln(w, "LOCAL_APKCERTS_FILE :=", as.APKCertsFile().String())
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_android_app_set.mk")
 		case nativeSharedLib, nativeExecutable, nativeTest:
 			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.Stem())
diff --git a/apex/builder.go b/apex/builder.go
index 5fb9a5f..49e4642 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -390,7 +390,7 @@
 		} else {
 			if fi.class == appSet {
 				copyCommands = append(copyCommands,
-					fmt.Sprintf("unzip -q -d %s %s", destPathDir, fi.builtFile.String()))
+					fmt.Sprintf("unzip -qDD -d %s %s", destPathDir, fi.builtFile.String()))
 			} else {
 				copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath)
 			}
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 7ff20f4..71c9204 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -225,6 +225,8 @@
 func llndkHeadersFactory() android.Module {
 	module, library := NewLibrary(android.DeviceSupported)
 	library.HeaderOnly()
+	module.stl = nil
+	module.sanitize = nil
 
 	decorator := &llndkHeadersDecorator{
 		libraryDecorator: library,
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index d3802f9..e485c60 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -117,7 +117,7 @@
 // Command is the type of soong_ui execution. Only one type of
 // execution is specified. The args are specific to the command.
 func main() {
-	buildStartedMilli := time.Now().UnixNano() / int64(time.Millisecond)
+	buildStarted := time.Now()
 
 	c, args := getCommand(os.Args)
 	if c == nil {
@@ -138,6 +138,7 @@
 	defer trace.Close()
 
 	met := metrics.New()
+	met.SetBuildDateTime(buildStarted)
 
 	stat := &status.Status{}
 	defer stat.Finish()
@@ -171,7 +172,7 @@
 	buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
 	rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
 	soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
-	defer build.UploadMetrics(buildCtx, config, c.forceDumbOutput, buildStartedMilli, buildErrorFile, rbeMetricsFile, soongMetricsFile)
+	defer build.UploadMetrics(buildCtx, config, c.forceDumbOutput, buildStarted, buildErrorFile, rbeMetricsFile, soongMetricsFile)
 
 	os.MkdirAll(logsDir, 0777)
 	log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
diff --git a/java/aar.go b/java/aar.go
index 500788f..ad9b5e7 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -641,7 +641,7 @@
 var unzipAAR = pctx.AndroidStaticRule("unzipAAR",
 	blueprint.RuleParams{
 		Command: `rm -rf $outDir && mkdir -p $outDir && ` +
-			`unzip -qo -d $outDir $in && rm -rf $outDir/res && touch $out`,
+			`unzip -qoDD -d $outDir $in && rm -rf $outDir/res && touch $out`,
 	},
 	"outDir")
 
diff --git a/java/androidmk.go b/java/androidmk.go
index 618e15d..03994bf 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -131,6 +131,10 @@
 						entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", library.proguardDictionary)
 					}
 					entries.SetString("LOCAL_MODULE_STEM", library.Stem())
+
+					entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", library.linter.outputs.transitiveHTMLZip)
+					entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", library.linter.outputs.transitiveTextZip)
+					entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", library.linter.outputs.transitiveXMLZip)
 				},
 			},
 		}
@@ -370,9 +374,15 @@
 				entries.SetString("LOCAL_CERTIFICATE", app.certificate.AndroidMkString())
 				entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", app.getOverriddenPackages()...)
 
-				for _, jniLib := range app.installJniLibs {
-					entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
+				if app.embeddedJniLibs {
+					jniSymbols := app.JNISymbolsInstalls(app.installPathForJNISymbols.String())
+					entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", jniSymbols.String())
+				} else {
+					for _, jniLib := range app.jniLibs {
+						entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
+					}
 				}
+
 				if len(app.jniCoverageOutputs) > 0 {
 					entries.AddStrings("LOCAL_PREBUILT_COVERAGE_ARCHIVE", app.jniCoverageOutputs.Strings()...)
 				}
@@ -383,6 +393,10 @@
 					install := app.onDeviceDir + "/" + extra.Base()
 					entries.AddStrings("LOCAL_SOONG_BUILT_INSTALLED", extra.String()+":"+install)
 				}
+
+				entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", app.linter.outputs.transitiveHTMLZip)
+				entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", app.linter.outputs.transitiveTextZip)
+				entries.AddOptionalPath("LOCAL_SOONG_LINT_REPORTS", app.linter.outputs.transitiveXMLZip)
 			},
 		},
 		ExtraFooters: []android.AndroidMkExtraFootersFunc{
diff --git a/java/app.go b/java/app.go
index 98bce94..4031cfe 100755
--- a/java/app.go
+++ b/java/app.go
@@ -106,6 +106,10 @@
 	return as.masterFile
 }
 
+func (as *AndroidAppSet) APKCertsFile() android.Path {
+	return as.apkcertsFile
+}
+
 var TargetCpuAbi = map[string]string{
 	"arm":    "ARMEABI_V7A",
 	"arm64":  "ARM64_V8A",
@@ -288,8 +292,10 @@
 
 	overridableAppProperties overridableAppProperties
 
-	installJniLibs     []jniLib
-	jniCoverageOutputs android.Paths
+	jniLibs                  []jniLib
+	installPathForJNISymbols android.Path
+	embeddedJniLibs          bool
+	jniCoverageOutputs       android.Paths
 
 	bundleFile android.Path
 
@@ -566,8 +572,7 @@
 	a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, a.proguardOptionsFile)
 }
 
-func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
-
+func (a *AndroidApp) installPath(ctx android.ModuleContext) android.InstallPath {
 	var installDir string
 	if ctx.ModuleName() == "framework-res" {
 		// framework-res.apk is installed as system/framework/framework-res.apk
@@ -577,7 +582,12 @@
 	} else {
 		installDir = filepath.Join("app", a.installApkName)
 	}
-	a.dexpreopter.installPath = android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
+
+	return android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
+}
+
+func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
+	a.dexpreopter.installPath = a.installPath(ctx)
 	if a.deviceProperties.Uncompress_dex == nil {
 		// If the value was not force-set by the user, use reasonable default based on the module.
 		a.deviceProperties.Uncompress_dex = proptools.BoolPtr(a.shouldUncompressDex(ctx))
@@ -599,8 +609,10 @@
 func (a *AndroidApp) jniBuildActions(jniLibs []jniLib, ctx android.ModuleContext) android.WritablePath {
 	var jniJarFile android.WritablePath
 	if len(jniLibs) > 0 {
+		a.jniLibs = jniLibs
 		if a.shouldEmbedJnis(ctx) {
 			jniJarFile = android.PathForModuleOut(ctx, "jnilibs.zip")
+			a.installPathForJNISymbols = a.installPath(ctx).ToMakePath()
 			TransformJniLibsToJar(ctx, jniJarFile, jniLibs, a.useEmbeddedNativeLibs(ctx))
 			for _, jni := range jniLibs {
 				if jni.coverageFile.Valid() {
@@ -618,13 +630,25 @@
 					}
 				}
 			}
-		} else {
-			a.installJniLibs = jniLibs
+			a.embeddedJniLibs = true
 		}
 	}
 	return jniJarFile
 }
 
+func (a *AndroidApp) JNISymbolsInstalls(installPath string) android.RuleBuilderInstalls {
+	var jniSymbols android.RuleBuilderInstalls
+	for _, jniLib := range a.jniLibs {
+		if jniLib.unstrippedFile != nil {
+			jniSymbols = append(jniSymbols, android.RuleBuilderInstall{
+				From: jniLib.unstrippedFile,
+				To:   filepath.Join(installPath, targetToJniDir(jniLib.target), jniLib.unstrippedFile.Base()),
+			})
+		}
+	}
+	return jniSymbols
+}
+
 func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext) {
 	// Collect NOTICE files from all dependencies.
 	seenModules := make(map[android.Module]bool)
@@ -752,6 +776,7 @@
 	a.linter.mergedManifest = a.aapt.mergedManifestFile
 	a.linter.manifest = a.aapt.manifestPath
 	a.linter.resources = a.aapt.resourceFiles
+	a.linter.buildModuleReportZip = ctx.Config().UnbundledBuildApps()
 
 	dexJarFile := a.dexBuildActions(ctx)
 
@@ -840,10 +865,11 @@
 
 				if lib.Valid() {
 					jniLibs = append(jniLibs, jniLib{
-						name:         ctx.OtherModuleName(module),
-						path:         path,
-						target:       module.Target(),
-						coverageFile: dep.CoverageOutputFile(),
+						name:           ctx.OtherModuleName(module),
+						path:           path,
+						target:         module.Target(),
+						coverageFile:   dep.CoverageOutputFile(),
+						unstrippedFile: dep.UnstrippedOutputFile(),
 					})
 				} else {
 					ctx.ModuleErrorf("dependency %q missing output file", otherName)
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 99bfb6d..08865b6 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -112,13 +112,20 @@
 	// local files that are used within user customized droiddoc options.
 	Arg_files []string `android:"path"`
 
-	// user customized droiddoc args.
+	// user customized droiddoc args. Deprecated, use flags instead.
 	// Available variables for substitution:
 	//
 	//  $(location <label>): the path to the arg_files with name <label>
 	//  $$: a literal $
 	Args *string
 
+	// user customized droiddoc args. Not compatible with property args.
+	// Available variables for substitution:
+	//
+	//  $(location <label>): the path to the arg_files with name <label>
+	//  $$: a literal $
+	Flags []string
+
 	// names of the output files used in args that will be generated
 	Out []string
 
@@ -382,7 +389,7 @@
 	argFiles    android.Paths
 	implicits   android.Paths
 
-	args string
+	args []string
 
 	docZip      android.WritablePath
 	stubsSrcJar android.WritablePath
@@ -619,8 +626,8 @@
 	}
 	srcFiles = filterHtml(srcFiles)
 
-	flags := j.collectAidlFlags(ctx, deps)
-	srcFiles = j.genSources(ctx, srcFiles, flags)
+	aidlFlags := j.collectAidlFlags(ctx, deps)
+	srcFiles = j.genSources(ctx, srcFiles, aidlFlags)
 
 	// srcs may depend on some genrule output.
 	j.srcJars = srcFiles.FilterByExt(".srcjar")
@@ -649,24 +656,38 @@
 		}
 	}
 
-	var err error
-	j.args, err = android.Expand(String(j.properties.Args), func(name string) (string, error) {
-		if strings.HasPrefix(name, "location ") {
-			label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
-			if paths, ok := argFilesMap[label]; ok {
-				return paths, nil
-			} else {
-				return "", fmt.Errorf("unknown location label %q, expecting one of %q",
-					label, strings.Join(argFileLabels, ", "))
-			}
-		} else if name == "genDir" {
-			return android.PathForModuleGen(ctx).String(), nil
-		}
-		return "", fmt.Errorf("unknown variable '$(%s)'", name)
-	})
+	var argsPropertyName string
+	flags := make([]string, 0)
+	if j.properties.Args != nil && j.properties.Flags != nil {
+		ctx.PropertyErrorf("args", "flags is set. Cannot set args")
+	} else if args := proptools.String(j.properties.Args); args != "" {
+		flags = append(flags, args)
+		argsPropertyName = "args"
+	} else {
+		flags = append(flags, j.properties.Flags...)
+		argsPropertyName = "flags"
+	}
 
-	if err != nil {
-		ctx.PropertyErrorf("args", "%s", err.Error())
+	for _, flag := range flags {
+		args, err := android.Expand(flag, func(name string) (string, error) {
+			if strings.HasPrefix(name, "location ") {
+				label := strings.TrimSpace(strings.TrimPrefix(name, "location "))
+				if paths, ok := argFilesMap[label]; ok {
+					return paths, nil
+				} else {
+					return "", fmt.Errorf("unknown location label %q, expecting one of %q",
+						label, strings.Join(argFileLabels, ", "))
+				}
+			} else if name == "genDir" {
+				return android.PathForModuleGen(ctx).String(), nil
+			}
+			return "", fmt.Errorf("unknown variable '$(%s)'", name)
+		})
+
+		if err != nil {
+			ctx.PropertyErrorf(argsPropertyName, "%s", err.Error())
+		}
+		j.args = append(j.args, args)
 	}
 
 	return deps
@@ -1010,7 +1031,7 @@
 
 	d.stubsFlags(ctx, cmd, stubsDir)
 
-	cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles)
+	cmd.Flag(strings.Join(d.Javadoc.args, " ")).Implicits(d.Javadoc.argFiles)
 
 	if d.properties.Compat_config != nil {
 		compatConfig := android.PathForModuleSrc(ctx, String(d.properties.Compat_config))
@@ -1327,7 +1348,7 @@
 		cmd.Flag("--include-annotations")
 
 		validatingNullability :=
-			strings.Contains(d.Javadoc.args, "--validate-nullability-from-merged-stubs") ||
+			android.InList("--validate-nullability-from-merged-stubs", d.Javadoc.args) ||
 				String(d.properties.Validate_nullability_from_list) != ""
 
 		migratingNullability := String(d.properties.Previous_api) != ""
@@ -1539,14 +1560,14 @@
 	d.apiLevelsAnnotationsFlags(ctx, cmd)
 	d.apiToXmlFlags(ctx, cmd)
 
-	if strings.Contains(d.Javadoc.args, "--generate-documentation") {
+	if android.InList("--generate-documentation", d.Javadoc.args) {
 		// Currently Metalava have the ability to invoke Javadoc in a seperate process.
 		// Pass "-nodocs" to suppress the Javadoc invocation when Metalava receives
 		// "--generate-documentation" arg. This is not needed when Metalava removes this feature.
-		d.Javadoc.args = d.Javadoc.args + " -nodocs "
+		d.Javadoc.args = append(d.Javadoc.args, "-nodocs")
 	}
 
-	cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles)
+	cmd.Flag(strings.Join(d.Javadoc.args, " ")).Implicits(d.Javadoc.argFiles)
 	for _, o := range d.Javadoc.properties.Out {
 		cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
 	}
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 130b634..b5a0217 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -146,7 +146,7 @@
 
 var hiddenAPIEncodeDexRule = pctx.AndroidStaticRule("hiddenAPIEncodeDex", blueprint.RuleParams{
 	Command: `rm -rf $tmpDir && mkdir -p $tmpDir && mkdir $tmpDir/dex-input && mkdir $tmpDir/dex-output &&
-		unzip -o -q $in 'classes*.dex' -d $tmpDir/dex-input &&
+		unzip -qoDD $in 'classes*.dex' -d $tmpDir/dex-input &&
 		for INPUT_DEX in $$(find $tmpDir/dex-input -maxdepth 1 -name 'classes*.dex' | sort); do
 		  echo "--input-dex=$${INPUT_DEX}";
 		  echo "--output-dex=$tmpDir/dex-output/$$(basename $${INPUT_DEX})";
diff --git a/java/java.go b/java/java.go
index 46ef98b..367b09c 100644
--- a/java/java.go
+++ b/java/java.go
@@ -635,10 +635,11 @@
 }
 
 type jniLib struct {
-	name         string
-	path         android.Path
-	target       android.Target
-	coverageFile android.OptionalPath
+	name           string
+	path           android.Path
+	target         android.Target
+	coverageFile   android.OptionalPath
+	unstrippedFile android.Path
 }
 
 func (j *Module) shouldInstrument(ctx android.BaseModuleContext) bool {
diff --git a/java/java_test.go b/java/java_test.go
index fb00361..db3f187 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1097,7 +1097,7 @@
 		    ],
 		    proofread_file: "libcore-proofread.txt",
 		    todo_file: "libcore-docs-todo.html",
-		    args: "-offlinemode -title \"libcore\"",
+		    flags: ["-offlinemode -title \"libcore\""],
 		}
 		`,
 		map[string][]byte{
@@ -1124,6 +1124,42 @@
 	}
 }
 
+func TestDroiddocArgsAndFlagsCausesError(t *testing.T) {
+	testJavaError(t, "flags is set. Cannot set args", `
+		droiddoc_exported_dir {
+		    name: "droiddoc-templates-sdk",
+		    path: ".",
+		}
+		filegroup {
+		    name: "bar-doc-aidl-srcs",
+		    srcs: ["bar-doc/IBar.aidl"],
+		    path: "bar-doc",
+		}
+		droiddoc {
+		    name: "bar-doc",
+		    srcs: [
+		        "bar-doc/a.java",
+		        "bar-doc/IFoo.aidl",
+		        ":bar-doc-aidl-srcs",
+		    ],
+		    exclude_srcs: [
+		        "bar-doc/b.java"
+		    ],
+		    custom_template: "droiddoc-templates-sdk",
+		    hdf: [
+		        "android.whichdoc offline",
+		    ],
+		    knowntags: [
+		        "bar-doc/known_oj_tags.txt",
+		    ],
+		    proofread_file: "libcore-proofread.txt",
+		    todo_file: "libcore-docs-todo.html",
+		    flags: ["-offlinemode -title \"libcore\""],
+		    args: "-offlinemode -title \"libcore\"",
+		}
+		`)
+}
+
 func TestDroidstubsWithSystemModules(t *testing.T) {
 	ctx, _ := testJava(t, `
 		droidstubs {
diff --git a/java/lint.go b/java/lint.go
index b73d6a5..20a7dc4 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -67,12 +67,32 @@
 	kotlinLanguageLevel string
 	outputs             lintOutputs
 	properties          LintProperties
+
+	buildModuleReportZip bool
 }
 
 type lintOutputs struct {
 	html android.ModuleOutPath
 	text android.ModuleOutPath
 	xml  android.ModuleOutPath
+
+	transitiveHTML *android.DepSet
+	transitiveText *android.DepSet
+	transitiveXML  *android.DepSet
+
+	transitiveHTMLZip android.OptionalPath
+	transitiveTextZip android.OptionalPath
+	transitiveXMLZip  android.OptionalPath
+}
+
+type lintOutputIntf interface {
+	lintOutputs() *lintOutputs
+}
+
+var _ lintOutputIntf = (*linter)(nil)
+
+func (l *linter) lintOutputs() *lintOutputs {
+	return &l.outputs
 }
 
 func (l *linter) enabled() bool {
@@ -213,27 +233,49 @@
 
 	projectXML, lintXML, cacheDir, homeDir, deps := l.writeLintProjectXML(ctx, rule)
 
-	l.outputs.html = android.PathForModuleOut(ctx, "lint-report.html")
-	l.outputs.text = android.PathForModuleOut(ctx, "lint-report.txt")
-	l.outputs.xml = android.PathForModuleOut(ctx, "lint-report.xml")
+	html := android.PathForModuleOut(ctx, "lint-report.html")
+	text := android.PathForModuleOut(ctx, "lint-report.txt")
+	xml := android.PathForModuleOut(ctx, "lint-report.xml")
+
+	htmlDeps := android.NewDepSetBuilder(android.POSTORDER).Direct(html)
+	textDeps := android.NewDepSetBuilder(android.POSTORDER).Direct(text)
+	xmlDeps := android.NewDepSetBuilder(android.POSTORDER).Direct(xml)
+
+	ctx.VisitDirectDepsWithTag(staticLibTag, func(dep android.Module) {
+		if depLint, ok := dep.(lintOutputIntf); ok {
+			depLintOutputs := depLint.lintOutputs()
+			htmlDeps.Transitive(depLintOutputs.transitiveHTML)
+			textDeps.Transitive(depLintOutputs.transitiveText)
+			xmlDeps.Transitive(depLintOutputs.transitiveXML)
+		}
+	})
 
 	rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
 	rule.Command().Text("mkdir -p").Flag(cacheDir.String()).Flag(homeDir.String())
 
+	var annotationsZipPath, apiVersionsXMLPath android.Path
+	if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
+		annotationsZipPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/annotations.zip")
+		apiVersionsXMLPath = android.PathForSource(ctx, "prebuilts/sdk/current/public/data/api-versions.xml")
+	} else {
+		annotationsZipPath = copiedAnnotationsZipPath(ctx)
+		apiVersionsXMLPath = copiedAPIVersionsXmlPath(ctx)
+	}
+
 	rule.Command().
 		Text("(").
 		Flag("JAVA_OPTS=-Xmx2048m").
 		FlagWithArg("ANDROID_SDK_HOME=", homeDir.String()).
-		FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath(ctx)).
-		FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXmlPath(ctx)).
+		FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath).
+		FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXMLPath).
 		Tool(android.PathForSource(ctx, "prebuilts/cmdline-tools/tools/bin/lint")).
 		Implicit(android.PathForSource(ctx, "prebuilts/cmdline-tools/tools/lib/lint-classpath.jar")).
 		Flag("--quiet").
 		FlagWithInput("--project ", projectXML).
 		FlagWithInput("--config ", lintXML).
-		FlagWithOutput("--html ", l.outputs.html).
-		FlagWithOutput("--text ", l.outputs.text).
-		FlagWithOutput("--xml ", l.outputs.xml).
+		FlagWithOutput("--html ", html).
+		FlagWithOutput("--text ", text).
+		FlagWithOutput("--xml ", xml).
 		FlagWithArg("--compile-sdk-version ", l.compileSdkVersion).
 		FlagWithArg("--java-language-level ", l.javaLanguageLevel).
 		FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
@@ -241,23 +283,37 @@
 		Flag("--exitcode").
 		Flags(l.properties.Lint.Flags).
 		Implicits(deps).
-		Text("|| (").Text("cat").Input(l.outputs.text).Text("; exit 7)").
+		Text("|| (").Text("cat").Input(text).Text("; exit 7)").
 		Text(")")
 
 	rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
 
 	rule.Build(pctx, ctx, "lint", "lint")
-}
 
-func (l *linter) lintOutputs() *lintOutputs {
-	return &l.outputs
-}
+	l.outputs = lintOutputs{
+		html: html,
+		text: text,
+		xml:  xml,
 
-type lintOutputIntf interface {
-	lintOutputs() *lintOutputs
-}
+		transitiveHTML: htmlDeps.Build(),
+		transitiveText: textDeps.Build(),
+		transitiveXML:  xmlDeps.Build(),
+	}
 
-var _ lintOutputIntf = (*linter)(nil)
+	if l.buildModuleReportZip {
+		htmlZip := android.PathForModuleOut(ctx, "lint-report-html.zip")
+		l.outputs.transitiveHTMLZip = android.OptionalPathForPath(htmlZip)
+		lintZip(ctx, l.outputs.transitiveHTML.ToSortedList(), htmlZip)
+
+		textZip := android.PathForModuleOut(ctx, "lint-report-text.zip")
+		l.outputs.transitiveTextZip = android.OptionalPathForPath(textZip)
+		lintZip(ctx, l.outputs.transitiveText.ToSortedList(), textZip)
+
+		xmlZip := android.PathForModuleOut(ctx, "lint-report-xml.zip")
+		l.outputs.transitiveXMLZip = android.OptionalPathForPath(xmlZip)
+		lintZip(ctx, l.outputs.transitiveXML.ToSortedList(), xmlZip)
+	}
+}
 
 type lintSingleton struct {
 	htmlZip android.WritablePath
@@ -271,7 +327,7 @@
 }
 
 func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
-	if ctx.Config().UnbundledBuild() {
+	if ctx.Config().UnbundledBuildUsePrebuiltSdks() {
 		return
 	}
 
@@ -297,25 +353,29 @@
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   android.Cp,
 		Input:  android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
-		Output: annotationsZipPath(ctx),
+		Output: copiedAnnotationsZipPath(ctx),
 	})
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   android.Cp,
 		Input:  android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"),
-		Output: apiVersionsXmlPath(ctx),
+		Output: copiedAPIVersionsXmlPath(ctx),
 	})
 }
 
-func annotationsZipPath(ctx android.PathContext) android.WritablePath {
+func copiedAnnotationsZipPath(ctx android.PathContext) android.WritablePath {
 	return android.PathForOutput(ctx, "lint", "annotations.zip")
 }
 
-func apiVersionsXmlPath(ctx android.PathContext) android.WritablePath {
+func copiedAPIVersionsXmlPath(ctx android.PathContext) android.WritablePath {
 	return android.PathForOutput(ctx, "lint", "api_versions.xml")
 }
 
 func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) {
+	if ctx.Config().UnbundledBuild() {
+		return
+	}
+
 	var outputs []*lintOutputs
 	var dirs []string
 	ctx.VisitAllModules(func(m android.Module) {
@@ -343,18 +403,7 @@
 			paths = append(paths, get(output))
 		}
 
-		sort.Slice(paths, func(i, j int) bool {
-			return paths[i].String() < paths[j].String()
-		})
-
-		rule := android.NewRuleBuilder()
-
-		rule.Command().BuiltTool(ctx, "soong_zip").
-			FlagWithOutput("-o ", outputPath).
-			FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
-			FlagWithRspFileInputList("-l ", paths)
-
-		rule.Build(pctx, ctx, outputPath.Base(), outputPath.Base())
+		lintZip(ctx, paths, outputPath)
 	}
 
 	l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip")
@@ -370,7 +419,9 @@
 }
 
 func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
-	ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
+	if !ctx.Config().UnbundledBuild() {
+		ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
+	}
 }
 
 var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
@@ -379,3 +430,20 @@
 	android.RegisterSingletonType("lint",
 		func() android.Singleton { return &lintSingleton{} })
 }
+
+func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android.WritablePath) {
+	paths = android.SortedUniquePaths(android.CopyOfPaths(paths))
+
+	sort.Slice(paths, func(i, j int) bool {
+		return paths[i].String() < paths[j].String()
+	})
+
+	rule := android.NewRuleBuilder()
+
+	rule.Command().BuiltTool(ctx, "soong_zip").
+		FlagWithOutput("-o ", outputPath).
+		FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
+		FlagWithRspFileInputList("-l ", paths)
+
+	rule.Build(pctx, ctx, outputPath.Base(), outputPath.Base())
+}
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 999c72f..b10e6c7 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -83,8 +83,7 @@
 	}{}
 	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, apiver))
 	props.Jars = append(props.Jars, path)
-	// TODO(hansson): change to scope after migration is done.
-	props.Sdk_version = proptools.StringPtr("current")
+	props.Sdk_version = proptools.StringPtr(scope)
 	props.Installable = proptools.BoolPtr(false)
 
 	mctx.CreateModule(ImportFactory, &props)
diff --git a/java/sdk_library.go b/java/sdk_library.go
index a5aa328..58e05e5 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -143,6 +143,7 @@
 	droidstubsArgsForGeneratingApi []string
 
 	// True if the stubs source and api can be created by the same metalava invocation.
+	// TODO(b/146727827) Now that metalava supports "API hierarchy", do we still need it?
 	createStubsSourceAndApiTogether bool
 
 	// Whether the api scope can be treated as unstable, and should skip compat checks.
@@ -284,6 +285,7 @@
 		sdkVersion:    "module_current",
 		droidstubsArgs: []string{
 			"--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\)",
+			"--show-for-stub-purposes-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)",
 		},
 	})
 	apiScopeSystemServer = initApiScope(&apiScope{
diff --git a/rust/config/global.go b/rust/config/global.go
index e1b1775..2020f46 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -54,6 +54,9 @@
 		"-Wl,--pack-dyn-relocs=android+relr",
 		"-Wl,--no-undefined",
 		"-Wl,--hash-style=gnu",
+
+		"-B${ccConfig.ClangBin}",
+		"-fuse-ld=lld",
 	}
 )
 
@@ -80,7 +83,7 @@
 
 	pctx.ImportAs("ccConfig", "android/soong/cc/config")
 	pctx.StaticVariable("RustLinker", "${ccConfig.ClangBin}/clang++")
-	pctx.StaticVariable("RustLinkerArgs", "-B ${ccConfig.ClangBin} -fuse-ld=lld")
+	pctx.StaticVariable("RustLinkerArgs", "")
 
 	pctx.StaticVariable("DeviceGlobalLinkFlags", strings.Join(deviceGlobalLinkFlags, " "))
 
diff --git a/rust/config/x86_darwin_host.go b/rust/config/x86_darwin_host.go
index 4c16693..4104400 100644
--- a/rust/config/x86_darwin_host.go
+++ b/rust/config/x86_darwin_host.go
@@ -21,8 +21,10 @@
 )
 
 var (
-	DarwinRustFlags      = []string{}
-	DarwinRustLinkFlags  = []string{}
+	DarwinRustFlags     = []string{}
+	DarwinRustLinkFlags = []string{
+		"-B${ccConfig.MacToolPath}",
+	}
 	darwinX8664Rustflags = []string{}
 	darwinX8664Linkflags = []string{}
 )
@@ -66,6 +68,10 @@
 	return ".dylib"
 }
 
+func (t *toolchainDarwin) DylibSuffix() string {
+	return ".rustlib.dylib"
+}
+
 func (t *toolchainDarwin) ProcMacroSuffix() string {
 	return ".dylib"
 }
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
index 5376e5b..acc99e1 100644
--- a/rust/config/x86_linux_host.go
+++ b/rust/config/x86_linux_host.go
@@ -21,8 +21,11 @@
 )
 
 var (
-	LinuxRustFlags      = []string{}
-	LinuxRustLinkFlags  = []string{}
+	LinuxRustFlags     = []string{}
+	LinuxRustLinkFlags = []string{
+		"-B${ccConfig.ClangBin}",
+		"-fuse-ld=lld",
+	}
 	linuxX86Rustflags   = []string{}
 	linuxX86Linkflags   = []string{}
 	linuxX8664Rustflags = []string{}
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index b168cd0..2dd23c7 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -481,9 +481,6 @@
 }
 
 func TestMultipleHostOsTypesSnapshotWithCcBinary(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
 	result := testSdkWithCc(t, `
 		module_exports {
 			name: "myexports",
@@ -592,9 +589,6 @@
 // Test that we support the necessary flags for the linker binary, which is
 // special in several ways.
 func TestSnapshotWithCcStaticNocrtBinary(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
 	result := testSdkWithCc(t, `
 		module_exports {
 			name: "mymodule_exports",
@@ -943,9 +937,6 @@
 }
 
 func TestHostSnapshotWithCcSharedLibrary(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -1038,9 +1029,6 @@
 }
 
 func TestMultipleHostOsTypesSnapshotWithCcSharedLibrary(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -1213,9 +1201,6 @@
 }
 
 func TestHostSnapshotWithCcStaticLibrary(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
 	result := testSdkWithCc(t, `
 		module_exports {
 			name: "myexports",
@@ -1391,9 +1376,6 @@
 }
 
 func TestHostSnapshotWithMultiLib64(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
 	result := testSdkWithCc(t, `
 		module_exports {
 			name: "myexports",
@@ -1523,9 +1505,6 @@
 }
 
 func TestHostSnapshotWithCcHeadersLibrary(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -1579,9 +1558,6 @@
 }
 
 func TestDeviceAndHostSnapshotWithCcHeadersLibrary(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -1656,9 +1632,6 @@
 }
 
 func TestSystemSharedLibPropagation(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -1923,9 +1896,6 @@
 }
 
 func TestDeviceAndHostSnapshotWithStubsLibrary(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
@@ -2009,9 +1979,6 @@
 }
 
 func TestUniqueHostSoname(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
 	result := testSdkWithCc(t, `
 		sdk {
 			name: "mysdk",
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 79da3f0..aee04a1 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -265,9 +265,6 @@
 }
 
 func TestHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
 	result := testSdkWithJava(t, `
 		sdk {
 			name: "mysdk",
@@ -325,9 +322,6 @@
 }
 
 func TestDeviceAndHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
 	result := testSdkWithJava(t, `
 		sdk {
 			name: "mysdk",
@@ -441,9 +435,6 @@
 }
 
 func TestHostSnapshotWithJavaImplLibrary(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",
@@ -548,9 +539,6 @@
 }
 
 func TestHostSnapshotWithJavaTest(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",
@@ -692,9 +680,6 @@
 }
 
 func TestHostSnapshotWithDroidstubs(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
 	result := testSdkWithDroidstubs(t, `
 		module_exports {
 			name: "myexports",
@@ -835,9 +820,6 @@
 }
 
 func TestHostSnapshotWithJavaSystemModules(t *testing.T) {
-	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-	SkipIfNotLinux(t)
-
 	result := testSdkWithJava(t, `
 		sdk {
 			name: "mysdk",
@@ -913,9 +895,6 @@
 }
 
 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",
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index 56be741..ef62b79 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -15,6 +15,9 @@
 package sdk
 
 import (
+	"android/soong/android"
+	"log"
+	"os"
 	"testing"
 
 	"github.com/google/blueprint/proptools"
@@ -22,6 +25,12 @@
 
 // Needed in an _test.go file in this package to ensure tests run correctly, particularly in IDE.
 func TestMain(m *testing.M) {
+	if android.BuildOs != android.Linux {
+		// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+		log.Printf("Skipping as sdk snapshot generation is only supported on %s not %s", android.Linux, android.BuildOs)
+		os.Exit(0)
+	}
+
 	runTestWithBuildDir(m)
 }
 
diff --git a/sdk/testing.go b/sdk/testing.go
index 4d029e4..378ce1f 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -422,10 +422,3 @@
 
 	os.Exit(run())
 }
-
-func SkipIfNotLinux(t *testing.T) {
-	t.Helper()
-	if android.BuildOs != android.Linux {
-		t.Skipf("Skipping as sdk snapshot generation is only supported on %s not %s", android.Linux, android.BuildOs)
-	}
-}
diff --git a/sdk/update.go b/sdk/update.go
index 8241151..cf25008 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -911,7 +911,7 @@
 
 	if commonVariants, ok := variantsByArchName["common"]; ok {
 		if len(osTypeVariants) != 1 {
-			panic("Expected to only have 1 variant when arch type is common but found " + string(len(osTypeVariants)))
+			panic(fmt.Errorf("Expected to only have 1 variant when arch type is common but found %d", len(osTypeVariants)))
 		}
 
 		// A common arch type only has one variant and its properties should be treated
diff --git a/ui/build/config.go b/ui/build/config.go
index e567e40..a60d70e 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -256,9 +256,6 @@
 		ret.buildDateTime = strconv.FormatInt(time.Now().Unix(), 10)
 	}
 
-	if ctx.Metrics != nil {
-		ctx.Metrics.SetBuildDateTime(ret.buildDateTime)
-	}
 	ret.environ.Set("BUILD_DATETIME_FILE", buildDateTimeFile)
 
 	return Config{ret}
diff --git a/ui/build/upload.go b/ui/build/upload.go
index 3a23a80..1cc2e94 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -44,7 +44,7 @@
 // environment variable. The metrics files are copied to a temporary directory
 // and the uploader is then executed in the background to allow the user to continue
 // working.
-func UploadMetrics(ctx Context, config Config, forceDumbOutput bool, buildStartedMilli int64, files ...string) {
+func UploadMetrics(ctx Context, config Config, forceDumbOutput bool, buildStarted time.Time, files ...string) {
 	ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
 	defer ctx.EndTrace()
 
@@ -86,7 +86,7 @@
 	// For platform builds, the branch and target name is hardcoded to specific
 	// values for later extraction of the metrics in the data metrics pipeline.
 	data, err := proto.Marshal(&upload_proto.Upload{
-		CreationTimestampMs:   proto.Uint64(uint64(buildStartedMilli)),
+		CreationTimestampMs:   proto.Uint64(uint64(buildStarted.UnixNano() / int64(time.Millisecond))),
 		CompletionTimestampMs: proto.Uint64(uint64(time.Now().UnixNano() / int64(time.Millisecond))),
 		BranchName:            proto.String("developer-metrics"),
 		TargetName:            proto.String("platform-build-systems-metrics"),
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
index c812730..dccf156 100644
--- a/ui/build/upload_test.go
+++ b/ui/build/upload_test.go
@@ -97,7 +97,7 @@
 				buildDateTime: strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10),
 			}}
 
-			UploadMetrics(ctx, config, false, 1591031903, metricsFiles...)
+			UploadMetrics(ctx, config, false, time.Now(), metricsFiles...)
 		})
 	}
 }
@@ -151,7 +151,7 @@
 					"OUT_DIR=/bad",
 				}}}
 
-			UploadMetrics(ctx, config, true, 1591031903, metricsFile)
+			UploadMetrics(ctx, config, true, time.Now(), metricsFile)
 			t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
 		})
 	}
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 3596e10..8188a69 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -25,6 +25,9 @@
         "metrics.go",
         "time.go",
     ],
+    testSrcs: [
+        "time_test.go",
+    ],
 }
 
 bootstrap_go_package {
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index e055b76..2b5c4c3 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -17,7 +17,7 @@
 import (
 	"io/ioutil"
 	"os"
-	"strconv"
+	"time"
 
 	"github.com/golang/protobuf/proto"
 
@@ -131,14 +131,8 @@
 	}
 }
 
-func (m *Metrics) SetBuildDateTime(date_time string) {
-	if date_time != "" {
-		date_time_timestamp, err := strconv.ParseInt(date_time, 10, 64)
-		if err != nil {
-			panic(err)
-		}
-		m.metrics.BuildDateTimestamp = &date_time_timestamp
-	}
+func (m *Metrics) SetBuildDateTime(buildTimestamp time.Time) {
+	m.metrics.BuildDateTimestamp = proto.Int64(buildTimestamp.UnixNano() / int64(time.Second))
 }
 
 // exports the output to the file at outputPath
diff --git a/ui/metrics/time.go b/ui/metrics/time.go
index b8baf16..4016563 100644
--- a/ui/metrics/time.go
+++ b/ui/metrics/time.go
@@ -19,13 +19,18 @@
 
 	"android/soong/ui/metrics/metrics_proto"
 	"android/soong/ui/tracer"
+	"github.com/golang/protobuf/proto"
 )
 
+// for testing purpose only
+var _now = now
+
 type timeEvent struct {
 	desc string
 	name string
 
-	atNanos uint64 // timestamp measured in nanoseconds since the reference date
+	// the time that the event started to occur.
+	start time.Time
 }
 
 type TimeTracer interface {
@@ -39,33 +44,26 @@
 
 var _ TimeTracer = &timeTracerImpl{}
 
-func (t *timeTracerImpl) now() uint64 {
-	return uint64(time.Now().UnixNano())
+func now() time.Time {
+	return time.Now()
 }
 
-func (t *timeTracerImpl) Begin(name, desc string, thread tracer.Thread) {
-	t.beginAt(name, desc, t.now())
+func (t *timeTracerImpl) Begin(name, desc string, _ tracer.Thread) {
+	t.activeEvents = append(t.activeEvents, timeEvent{name: name, desc: desc, start: _now()})
 }
 
-func (t *timeTracerImpl) beginAt(name, desc string, atNanos uint64) {
-	t.activeEvents = append(t.activeEvents, timeEvent{name: name, desc: desc, atNanos: atNanos})
-}
-
-func (t *timeTracerImpl) End(thread tracer.Thread) soong_metrics_proto.PerfInfo {
-	return t.endAt(t.now())
-}
-
-func (t *timeTracerImpl) endAt(atNanos uint64) soong_metrics_proto.PerfInfo {
+func (t *timeTracerImpl) End(tracer.Thread) soong_metrics_proto.PerfInfo {
 	if len(t.activeEvents) < 1 {
 		panic("Internal error: No pending events for endAt to end!")
 	}
 	lastEvent := t.activeEvents[len(t.activeEvents)-1]
 	t.activeEvents = t.activeEvents[:len(t.activeEvents)-1]
-	realTime := atNanos - lastEvent.atNanos
+	realTime := uint64(_now().Sub(lastEvent.start).Nanoseconds())
 
 	return soong_metrics_proto.PerfInfo{
-		Desc:      &lastEvent.desc,
-		Name:      &lastEvent.name,
-		StartTime: &lastEvent.atNanos,
-		RealTime:  &realTime}
+		Desc:      proto.String(lastEvent.desc),
+		Name:      proto.String(lastEvent.name),
+		StartTime: proto.Uint64(uint64(lastEvent.start.UnixNano())),
+		RealTime:  proto.Uint64(realTime),
+	}
 }
diff --git a/ui/metrics/time_test.go b/ui/metrics/time_test.go
new file mode 100644
index 0000000..d73080a
--- /dev/null
+++ b/ui/metrics/time_test.go
@@ -0,0 +1,42 @@
+// Copyright 2020 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 metrics
+
+import (
+	"testing"
+	"time"
+
+	"android/soong/ui/tracer"
+)
+
+func TestEnd(t *testing.T) {
+	startTime := time.Date(2020, time.July, 13, 13, 0, 0, 0, time.UTC)
+	dur := time.Nanosecond * 10
+	initialNow := _now
+	_now = func() time.Time { return startTime.Add(dur) }
+	defer func() { _now = initialNow }()
+
+	timeTracer := &timeTracerImpl{}
+	timeTracer.activeEvents = append(timeTracer.activeEvents, timeEvent{
+		desc:  "test",
+		name:  "test",
+		start: startTime,
+	})
+
+	perf := timeTracer.End(tracer.Thread(0))
+	if perf.GetRealTime() != uint64(dur.Nanoseconds()) {
+		t.Errorf("got %d, want %d nanoseconds for event duration", perf.GetRealTime(), dur.Nanoseconds())
+	}
+}