Merge "Move hardcoded apex_available's for ART modules into ART blueprints."
diff --git a/android/apex.go b/android/apex.go
index 3039e79..e70ec4f 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -67,6 +67,15 @@
 	return i.ApexVariationName == ""
 }
 
+func (i ApexInfo) InApex(apex string) bool {
+	for _, a := range i.InApexes {
+		if a == apex {
+			return true
+		}
+	}
+	return false
+}
+
 // ApexTestForInfo stores the contents of APEXes for which this module is a test and thus has
 // access to APEX internals.
 type ApexTestForInfo struct {
diff --git a/android/config.go b/android/config.go
index dbae7f7..e87a4ac 100644
--- a/android/config.go
+++ b/android/config.go
@@ -793,6 +793,11 @@
 	return Bool(c.productVariables.Always_use_prebuilt_sdks)
 }
 
+// Returns true if the boot jars check should be skipped.
+func (c *config) SkipBootJarsCheck() bool {
+	return Bool(c.productVariables.Skip_boot_jars_check)
+}
+
 func (c *config) Fuchsia() bool {
 	return Bool(c.productVariables.Fuchsia)
 }
@@ -969,13 +974,21 @@
 	return c.productVariables.ModulesLoadedByPrivilegedModules
 }
 
-func (c *config) DexpreoptGlobalConfig(ctx PathContext) ([]byte, error) {
+func (c *config) DexpreoptGlobalConfigPath(ctx PathContext) OptionalPath {
 	if c.productVariables.DexpreoptGlobalConfig == nil {
+		return OptionalPathForPath(nil)
+	}
+	return OptionalPathForPath(
+		pathForBuildToolDep(ctx, *c.productVariables.DexpreoptGlobalConfig))
+}
+
+func (c *config) DexpreoptGlobalConfig(ctx PathContext) ([]byte, error) {
+	path := c.DexpreoptGlobalConfigPath(ctx)
+	if !path.Valid() {
 		return nil, nil
 	}
-	path := absolutePath(*c.productVariables.DexpreoptGlobalConfig)
-	ctx.AddNinjaFileDeps(path)
-	return ioutil.ReadFile(path)
+	ctx.AddNinjaFileDeps(path.String())
+	return ioutil.ReadFile(absolutePath(path.String()))
 }
 
 func (c *config) FrameworksBaseDirExists(ctx PathContext) bool {
@@ -1341,6 +1354,11 @@
 	return l.jars[idx]
 }
 
+// Apex component of idx-th pair on the list.
+func (l *ConfiguredJarList) Apex(idx int) string {
+	return l.apexes[idx]
+}
+
 // If the list contains a pair with the given jar.
 func (l *ConfiguredJarList) ContainsJar(jar string) bool {
 	return InList(jar, l.jars)
@@ -1538,3 +1556,11 @@
 		return list
 	}).([]string)
 }
+
+func (c *config) NonUpdatableBootJars() ConfiguredJarList {
+	return c.productVariables.BootJars
+}
+
+func (c *config) UpdatableBootJars() ConfiguredJarList {
+	return c.productVariables.UpdatableBootJars
+}
diff --git a/android/paths.go b/android/paths.go
index 2fb5f25..b13979d 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1557,6 +1557,72 @@
 	}
 }
 
+type testModuleInstallPathContext struct {
+	baseModuleContext
+
+	inData          bool
+	inTestcases     bool
+	inSanitizerDir  bool
+	inRamdisk       bool
+	inVendorRamdisk bool
+	inRecovery      bool
+	inRoot          bool
+	forceOS         *OsType
+	forceArch       *ArchType
+}
+
+func (m testModuleInstallPathContext) Config() Config {
+	return m.baseModuleContext.config
+}
+
+func (testModuleInstallPathContext) AddNinjaFileDeps(deps ...string) {}
+
+func (m testModuleInstallPathContext) InstallInData() bool {
+	return m.inData
+}
+
+func (m testModuleInstallPathContext) InstallInTestcases() bool {
+	return m.inTestcases
+}
+
+func (m testModuleInstallPathContext) InstallInSanitizerDir() bool {
+	return m.inSanitizerDir
+}
+
+func (m testModuleInstallPathContext) InstallInRamdisk() bool {
+	return m.inRamdisk
+}
+
+func (m testModuleInstallPathContext) InstallInVendorRamdisk() bool {
+	return m.inVendorRamdisk
+}
+
+func (m testModuleInstallPathContext) InstallInRecovery() bool {
+	return m.inRecovery
+}
+
+func (m testModuleInstallPathContext) InstallInRoot() bool {
+	return m.inRoot
+}
+
+func (m testModuleInstallPathContext) InstallBypassMake() bool {
+	return false
+}
+
+func (m testModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) {
+	return m.forceOS, m.forceArch
+}
+
+// Construct a minimal ModuleInstallPathContext for testing. Note that baseModuleContext is
+// default-initialized, which leaves blueprint.baseModuleContext set to nil, so methods that are
+// delegated to it will panic.
+func ModuleInstallPathContextForTesting(config Config) ModuleInstallPathContext {
+	ctx := &testModuleInstallPathContext{}
+	ctx.config = config
+	ctx.os = Android
+	return ctx
+}
+
 // Rel performs the same function as filepath.Rel, but reports errors to a PathContext, and reports an error if
 // targetPath is not inside basePath.
 func Rel(ctx PathContext, basePath string, targetPath string) string {
diff --git a/android/paths_test.go b/android/paths_test.go
index 51e4ba5..e7fd763 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -197,62 +197,6 @@
 	}
 }
 
-type moduleInstallPathContextImpl struct {
-	baseModuleContext
-
-	inData          bool
-	inTestcases     bool
-	inSanitizerDir  bool
-	inRamdisk       bool
-	inVendorRamdisk bool
-	inRecovery      bool
-	inRoot          bool
-	forceOS         *OsType
-	forceArch       *ArchType
-}
-
-func (m moduleInstallPathContextImpl) Config() Config {
-	return m.baseModuleContext.config
-}
-
-func (moduleInstallPathContextImpl) AddNinjaFileDeps(deps ...string) {}
-
-func (m moduleInstallPathContextImpl) InstallInData() bool {
-	return m.inData
-}
-
-func (m moduleInstallPathContextImpl) InstallInTestcases() bool {
-	return m.inTestcases
-}
-
-func (m moduleInstallPathContextImpl) InstallInSanitizerDir() bool {
-	return m.inSanitizerDir
-}
-
-func (m moduleInstallPathContextImpl) InstallInRamdisk() bool {
-	return m.inRamdisk
-}
-
-func (m moduleInstallPathContextImpl) InstallInVendorRamdisk() bool {
-	return m.inVendorRamdisk
-}
-
-func (m moduleInstallPathContextImpl) InstallInRecovery() bool {
-	return m.inRecovery
-}
-
-func (m moduleInstallPathContextImpl) InstallInRoot() bool {
-	return m.inRoot
-}
-
-func (m moduleInstallPathContextImpl) InstallBypassMake() bool {
-	return false
-}
-
-func (m moduleInstallPathContextImpl) InstallForceOS() (*OsType, *ArchType) {
-	return m.forceOS, m.forceArch
-}
-
 func pathTestConfig(buildDir string) Config {
 	return TestConfig(buildDir, nil, "", nil)
 }
@@ -265,14 +209,14 @@
 
 	testCases := []struct {
 		name         string
-		ctx          *moduleInstallPathContextImpl
+		ctx          *testModuleInstallPathContext
 		in           []string
 		out          string
 		partitionDir string
 	}{
 		{
 			name: "host binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     hostTarget.Os,
 					target: hostTarget,
@@ -285,7 +229,7 @@
 
 		{
 			name: "system binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -297,7 +241,7 @@
 		},
 		{
 			name: "vendor binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -312,7 +256,7 @@
 		},
 		{
 			name: "odm binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -327,7 +271,7 @@
 		},
 		{
 			name: "product binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -342,7 +286,7 @@
 		},
 		{
 			name: "system_ext binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -357,7 +301,7 @@
 		},
 		{
 			name: "root binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -370,7 +314,7 @@
 		},
 		{
 			name: "recovery binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -383,7 +327,7 @@
 		},
 		{
 			name: "recovery root binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -398,7 +342,7 @@
 
 		{
 			name: "system native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -411,7 +355,7 @@
 		},
 		{
 			name: "vendor native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -427,7 +371,7 @@
 		},
 		{
 			name: "odm native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -443,7 +387,7 @@
 		},
 		{
 			name: "product native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -460,7 +404,7 @@
 
 		{
 			name: "system_ext native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -477,7 +421,7 @@
 
 		{
 			name: "sanitized system binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -490,7 +434,7 @@
 		},
 		{
 			name: "sanitized vendor binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -506,7 +450,7 @@
 		},
 		{
 			name: "sanitized odm binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -522,7 +466,7 @@
 		},
 		{
 			name: "sanitized product binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -539,7 +483,7 @@
 
 		{
 			name: "sanitized system_ext binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -556,7 +500,7 @@
 
 		{
 			name: "sanitized system native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -570,7 +514,7 @@
 		},
 		{
 			name: "sanitized vendor native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -587,7 +531,7 @@
 		},
 		{
 			name: "sanitized odm native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -604,7 +548,7 @@
 		},
 		{
 			name: "sanitized product native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -621,7 +565,7 @@
 		},
 		{
 			name: "sanitized system_ext native test binary",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -637,7 +581,7 @@
 			partitionDir: "target/product/test_device/data/asan/data",
 		}, {
 			name: "device testcases",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -649,7 +593,7 @@
 			partitionDir: "target/product/test_device/testcases",
 		}, {
 			name: "host testcases",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     hostTarget.Os,
 					target: hostTarget,
@@ -661,7 +605,7 @@
 			partitionDir: "host/linux-x86/testcases",
 		}, {
 			name: "forced host testcases",
-			ctx: &moduleInstallPathContextImpl{
+			ctx: &testModuleInstallPathContext{
 				baseModuleContext: baseModuleContext{
 					os:     deviceTarget.Os,
 					target: deviceTarget,
@@ -697,7 +641,7 @@
 	testConfig := pathTestConfig("")
 	deviceTarget := Target{Os: Android, Arch: Arch{ArchType: Arm64}}
 
-	ctx := &moduleInstallPathContextImpl{
+	ctx := &testModuleInstallPathContext{
 		baseModuleContext: baseModuleContext{
 			os:     deviceTarget.Os,
 			target: deviceTarget,
diff --git a/android/variable.go b/android/variable.go
index 7999f0f..a9a9c87 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -224,6 +224,7 @@
 	Unbundled_build              *bool `json:",omitempty"`
 	Unbundled_build_apps         *bool `json:",omitempty"`
 	Always_use_prebuilt_sdks     *bool `json:",omitempty"`
+	Skip_boot_jars_check         *bool `json:",omitempty"`
 	Malloc_not_svelte            *bool `json:",omitempty"`
 	Malloc_zero_contents         *bool `json:",omitempty"`
 	Malloc_pattern_fill_contents *bool `json:",omitempty"`
diff --git a/android/writedocs.go b/android/writedocs.go
index 4eb15e6..91c2318 100644
--- a/android/writedocs.go
+++ b/android/writedocs.go
@@ -48,6 +48,13 @@
 	deps = append(deps, pathForBuildToolDep(ctx, ctx.Config().moduleListFile))
 	deps = append(deps, pathForBuildToolDep(ctx, ctx.Config().ProductVariablesFileName))
 
+	// The dexpreopt configuration may not exist, but if it does, it's a dependency
+	// of soong_build.
+	dexpreoptConfigPath := ctx.Config().DexpreoptGlobalConfigPath(ctx)
+	if dexpreoptConfigPath.Valid() {
+		deps = append(deps, dexpreoptConfigPath.Path())
+	}
+
 	// Generate build system docs for the primary builder.  Generating docs reads the source
 	// files used to build the primary builder, but that dependency will be picked up through
 	// the dependency on the primary builder itself.  There are no dependencies on the
diff --git a/apex/allowed_deps.txt b/apex/allowed_deps.txt
index 3fa3a10..e5b91e5 100644
--- a/apex/allowed_deps.txt
+++ b/apex/allowed_deps.txt
@@ -17,12 +17,14 @@
 android.hardware.cas.native@1.0(minSdkVersion:29)
 android.hardware.cas@1.0(minSdkVersion:29)
 android.hardware.common-ndk_platform(minSdkVersion:29)
+android.hardware.common-unstable-ndk_platform(minSdkVersion:29)
 android.hardware.graphics.allocator@2.0(minSdkVersion:29)
 android.hardware.graphics.allocator@3.0(minSdkVersion:29)
 android.hardware.graphics.allocator@4.0(minSdkVersion:29)
 android.hardware.graphics.bufferqueue@1.0(minSdkVersion:29)
 android.hardware.graphics.bufferqueue@2.0(minSdkVersion:29)
 android.hardware.graphics.common-ndk_platform(minSdkVersion:29)
+android.hardware.graphics.common-unstable-ndk_platform(minSdkVersion:29)
 android.hardware.graphics.common@1.0(minSdkVersion:29)
 android.hardware.graphics.common@1.1(minSdkVersion:29)
 android.hardware.graphics.common@1.2(minSdkVersion:29)
@@ -423,15 +425,23 @@
 ndk_libc++abi(minSdkVersion:(no version))
 ndk_libc++abi(minSdkVersion:16)
 ndk_libunwind(minSdkVersion:16)
+net-utils-device-common(minSdkVersion:29)
 net-utils-framework-common(minSdkVersion:current)
 netd_aidl_interface-unstable-java(minSdkVersion:29)
 netd_event_listener_interface-ndk_platform(minSdkVersion:29)
+netd_event_listener_interface-unstable-ndk_platform(minSdkVersion:29)
 netlink-client(minSdkVersion:29)
 networkstack-aidl-interfaces-unstable-java(minSdkVersion:29)
 networkstack-client(minSdkVersion:29)
 NetworkStackApiStableDependencies(minSdkVersion:29)
 NetworkStackApiStableLib(minSdkVersion:29)
 networkstackprotos(minSdkVersion:29)
+neuralnetworks_types(minSdkVersion:30)
+neuralnetworks_utils_hal_1_0(minSdkVersion:30)
+neuralnetworks_utils_hal_1_1(minSdkVersion:30)
+neuralnetworks_utils_hal_1_2(minSdkVersion:30)
+neuralnetworks_utils_hal_1_3(minSdkVersion:30)
+neuralnetworks_utils_hal_common(minSdkVersion:30)
 PermissionController(minSdkVersion:28)
 permissioncontroller-statsd(minSdkVersion:current)
 philox_random(minSdkVersion:(no version))
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 7e83070..8b1f40d 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -825,7 +825,7 @@
 	ensureNotContains(t, mylib2Cflags, "-include ")
 
 	// Ensure that genstub is invoked with --apex
-	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_3").Rule("genStubSrc").Args["flags"])
+	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"])
 
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
 		"lib64/mylib.so",
@@ -920,11 +920,11 @@
 	ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_29/mylib3.so")
 
 	// Ensure that stubs libs are built without -include flags
-	mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+	mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("cc").Args["cFlags"]
 	ensureNotContains(t, mylib2Cflags, "-include ")
 
 	// Ensure that genstub is invoked with --apex
-	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_29").Rule("genStubSrc").Args["flags"])
+	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("genStubSrc").Args["flags"])
 
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
 		"lib64/mylib.so",
diff --git a/cc/cc.go b/cc/cc.go
index 3b01fb2..3297e41 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -377,8 +377,6 @@
 	isForPlatform() bool
 	apexVariationName() string
 	apexSdkVersion() android.ApiLevel
-	hasStubsVariants() bool
-	isStubs() bool
 	bootstrap() bool
 	mustUseVendorVariant() bool
 	nativeCoverage() bool
@@ -623,6 +621,8 @@
 	lto       *lto
 	pgo       *pgo
 
+	library libraryInterface
+
 	outputFile android.OptionalPath
 
 	cachedToolchain config.Toolchain
@@ -731,13 +731,6 @@
 	return c.Properties.AlwaysSdk || Bool(c.Properties.Sdk_variant_only)
 }
 
-func (c *Module) StubsVersions(ctx android.BaseMutatorContext) []string {
-	if versioned, ok := c.linker.(versionedInterface); ok {
-		return versioned.stubsVersions(ctx)
-	}
-	panic(fmt.Errorf("StubsVersions called on non-library module: %q", c.BaseModuleName()))
-}
-
 func (c *Module) CcLibrary() bool {
 	if c.linker != nil {
 		if _, ok := c.linker.(*libraryDecorator); ok {
@@ -761,53 +754,6 @@
 	return false
 }
 
-func (c *Module) SetBuildStubs() {
-	if versioned, ok := c.linker.(versionedInterface); ok {
-		versioned.setBuildStubs()
-		c.Properties.HideFromMake = true
-		c.sanitize = nil
-		c.stl = nil
-		c.Properties.PreventInstall = true
-		return
-	}
-	panic(fmt.Errorf("SetBuildStubs called on non-library module: %q", c.BaseModuleName()))
-}
-
-func (c *Module) BuildStubs() bool {
-	if versioned, ok := c.linker.(versionedInterface); ok {
-		return versioned.buildStubs()
-	}
-	panic(fmt.Errorf("BuildStubs called on non-library module: %q", c.BaseModuleName()))
-}
-
-func (c *Module) SetAllStubsVersions(versions []string) {
-	if versioned, ok := c.linker.(versionedInterface); ok {
-		versioned.setAllStubsVersions(versions)
-	}
-}
-
-func (c *Module) AllStubsVersions() []string {
-	if versioned, ok := c.linker.(versionedInterface); ok {
-		return versioned.allStubsVersions()
-	}
-	return nil
-}
-
-func (c *Module) SetStubsVersion(version string) {
-	if versioned, ok := c.linker.(versionedInterface); ok {
-		versioned.setStubsVersion(version)
-		return
-	}
-	panic(fmt.Errorf("SetStubsVersion called on non-library module: %q", c.BaseModuleName()))
-}
-
-func (c *Module) StubsVersion() string {
-	if versioned, ok := c.linker.(versionedInterface); ok {
-		return versioned.stubsVersion()
-	}
-	panic(fmt.Errorf("StubsVersion called on non-library module: %q", c.BaseModuleName()))
-}
-
 func (c *Module) SetStatic() {
 	if c.linker != nil {
 		if library, ok := c.linker.(libraryInterface); ok {
@@ -1041,15 +987,15 @@
 }
 
 func (c *Module) IsStubs() bool {
-	if versioned, ok := c.linker.(versionedInterface); ok {
-		return versioned.buildStubs()
+	if lib := c.library; lib != nil {
+		return lib.buildStubs()
 	}
 	return false
 }
 
 func (c *Module) HasStubsVariants() bool {
-	if versioned, ok := c.linker.(versionedInterface); ok {
-		return versioned.hasStubsVariants()
+	if lib := c.library; lib != nil {
+		return lib.hasStubsVariants()
 	}
 	return false
 }
@@ -1240,10 +1186,15 @@
 		// Host modules do not need ABI dumps.
 		return false
 	}
-	if ctx.isStubs() || ctx.isNDKStubLibrary() {
+	if ctx.isNDKStubLibrary() {
 		// Stubs do not need ABI dumps.
 		return false
 	}
+	if lib := ctx.mod.library; lib != nil && lib.buildStubs() {
+		// Stubs do not need ABI dumps.
+		return false
+	}
+
 	return true
 }
 
@@ -1278,14 +1229,6 @@
 	return ctx.mod.apexSdkVersion
 }
 
-func (ctx *moduleContextImpl) hasStubsVariants() bool {
-	return ctx.mod.HasStubsVariants()
-}
-
-func (ctx *moduleContextImpl) isStubs() bool {
-	return ctx.mod.IsStubs()
-}
-
 func (ctx *moduleContextImpl) bootstrap() bool {
 	return ctx.mod.bootstrap()
 }
@@ -2078,18 +2021,20 @@
 		// Recovery code is not NDK
 		return
 	}
-	if to.ToolchainLibrary() {
-		// These are always allowed
-		return
-	}
-	if to.NdkPrebuiltStl() {
-		// These are allowed, but they don't set sdk_version
-		return
-	}
-	if to.StubDecorator() {
-		// These aren't real libraries, but are the stub shared libraries that are included in
-		// the NDK.
-		return
+	if c, ok := to.(*Module); ok {
+		if c.ToolchainLibrary() {
+			// These are always allowed
+			return
+		}
+		if c.NdkPrebuiltStl() {
+			// These are allowed, but they don't set sdk_version
+			return
+		}
+		if c.StubDecorator() {
+			// These aren't real libraries, but are the stub shared libraries that are included in
+			// the NDK.
+			return
+		}
 	}
 
 	if strings.HasPrefix(ctx.ModuleName(), "libclang_rt.") && to.Module().Name() == "libc++" {
@@ -2337,12 +2282,17 @@
 		}
 
 		if depTag == reuseObjTag {
-			// reusing objects only make sense for cc.Modules.
-			staticAnalogue := ctx.OtherModuleProvider(dep, StaticLibraryInfoProvider).(StaticLibraryInfo)
-			objs := staticAnalogue.ReuseObjects
-			depPaths.Objs = depPaths.Objs.Append(objs)
-			depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
-			reexportExporter(depExporterInfo)
+			// Skip reused objects for stub libraries, they use their own stub object file instead.
+			// The reuseObjTag dependency still exists because the LinkageMutator runs before the
+			// version mutator, so the stubs variant is created from the shared variant that
+			// already has the reuseObjTag dependency on the static variant.
+			if !c.library.buildStubs() {
+				staticAnalogue := ctx.OtherModuleProvider(dep, StaticLibraryInfoProvider).(StaticLibraryInfo)
+				objs := staticAnalogue.ReuseObjects
+				depPaths.Objs = depPaths.Objs.Append(objs)
+				depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
+				reexportExporter(depExporterInfo)
+			}
 			return
 		}
 
@@ -2378,7 +2328,8 @@
 
 				if !libDepTag.explicitlyVersioned && len(sharedLibraryStubsInfo.SharedLibraryStubsInfos) > 0 {
 					useStubs := false
-					if m, ok := ccDep.(*Module); ok && m.IsStubs() && c.UseVndk() { // LLNDK
+
+					if lib := moduleLibraryInterface(dep); lib.buildStubs() && c.UseVndk() { // LLNDK
 						if !apexInfo.IsForPlatform() {
 							// For platform libraries, use current version of LLNDK
 							// If this is for use_vendor apex we will apply the same rules
@@ -2547,8 +2498,8 @@
 				c.Properties.AndroidMkHeaderLibs = append(
 					c.Properties.AndroidMkHeaderLibs, makeLibName)
 			case libDepTag.shared():
-				if ccDep.CcLibrary() {
-					if ccDep.BuildStubs() && dep.(android.ApexModule).InAnyApex() {
+				if lib := moduleLibraryInterface(dep); lib != nil {
+					if lib.buildStubs() && dep.(android.ApexModule).InAnyApex() {
 						// Add the dependency to the APEX(es) providing the library so that
 						// m <module> can trigger building the APEXes as well.
 						depApexInfo := ctx.OtherModuleProvider(dep, android.ApexInfoProvider).(android.ApexInfo)
@@ -2850,12 +2801,10 @@
 // Overrides ApexModule.IsInstallabeToApex()
 // Only shared/runtime libraries and "test_per_src" tests are installable to APEX.
 func (c *Module) IsInstallableToApex() bool {
-	if shared, ok := c.linker.(interface {
-		shared() bool
-	}); ok {
+	if lib := c.library; lib != nil {
 		// Stub libs and prebuilt libs in a versioned SDK are not
 		// installable to APEX even though they are shared libs.
-		return shared.shared() && !c.IsStubs() && c.ContainingSdk().Unversioned()
+		return lib.shared() && !lib.buildStubs() && c.ContainingSdk().Unversioned()
 	} else if _, ok := c.linker.(testPerSrc); ok {
 		return true
 	}
diff --git a/cc/config/global.go b/cc/config/global.go
index a170652..e5cb7ee 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -46,6 +46,7 @@
 
 		"-O2",
 		"-g",
+		"-fdebug-info-for-profiling",
 
 		"-fno-strict-aliasing",
 
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 681e3bc..fe3c12b 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -174,12 +174,25 @@
 	// TODO(b/144090547): We should be parsing these modules using
 	// ModuleDependencyTag instead of the current brute-force checking.
 
-	if linkable, ok := dependency.(LinkableInterface); !ok || // Discard non-linkables.
-		!linkable.CcLibraryInterface() || !linkable.Shared() || // Discard static libs.
-		linkable.UseVndk() || // Discard vendor linked libraries.
+	linkable, ok := dependency.(LinkableInterface)
+	if !ok || !linkable.CcLibraryInterface() {
+		// Discard non-linkables.
+		return false
+	}
+
+	if !linkable.Shared() {
+		// Discard static libs.
+		return false
+	}
+
+	if linkable.UseVndk() {
+		// Discard vendor linked libraries.
+		return false
+	}
+
+	if lib := moduleLibraryInterface(dependency); lib != nil && lib.buildStubs() && linkable.CcLibrary() {
 		// Discard stubs libs (only CCLibrary variants). Prebuilt libraries should not
 		// be excluded on the basis of they're not CCLibrary()'s.
-		(linkable.CcLibrary() && linkable.BuildStubs()) {
 		return false
 	}
 
diff --git a/cc/library.go b/cc/library.go
index 910fc31..d946629 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -587,7 +587,7 @@
 			}
 		}
 	}
-	if Bool(enabled) || ctx.hasStubsVariants() {
+	if Bool(enabled) || library.hasStubsVariants() {
 		return "PLATFORM"
 	}
 	return ""
@@ -598,7 +598,7 @@
 		return false
 	}
 	if !ctx.isForPlatform() {
-		if !ctx.hasStubsVariants() {
+		if !library.hasStubsVariants() {
 			// Skip ABI checks if this library is for APEX but isn't exported.
 			return false
 		}
@@ -1060,7 +1060,7 @@
 			stubInfo := ctx.OtherModuleProvider(stub, SharedLibraryInfoProvider).(SharedLibraryInfo)
 			flagInfo := ctx.OtherModuleProvider(stub, FlagExporterInfoProvider).(FlagExporterInfo)
 			stubsInfo = append(stubsInfo, SharedLibraryStubsInfo{
-				Version:           stub.(*Module).StubsVersion(),
+				Version:           moduleLibraryInterface(stub).stubsVersion(),
 				SharedLibraryInfo: stubInfo,
 				FlagExporterInfo:  flagInfo,
 			})
@@ -1384,7 +1384,7 @@
 	if library.Properties.Header_abi_checker.Symbol_file != nil {
 		return library.Properties.Header_abi_checker.Symbol_file
 	}
-	if ctx.hasStubsVariants() && library.Properties.Stubs.Symbol_file != nil {
+	if library.hasStubsVariants() && library.Properties.Stubs.Symbol_file != nil {
 		return library.Properties.Stubs.Symbol_file
 	}
 	return nil
@@ -1483,6 +1483,7 @@
 	module.compiler = library
 	module.linker = library
 	module.installer = library
+	module.library = library
 
 	return module, library
 }
@@ -1620,8 +1621,14 @@
 	modules := mctx.CreateLocalVariations(variants...)
 	for i, m := range modules {
 		if variants[i] != "" {
-			m.(LinkableInterface).SetBuildStubs()
-			m.(LinkableInterface).SetStubsVersion(variants[i])
+			c := m.(*Module)
+			c.Properties.HideFromMake = true
+			c.sanitize = nil
+			c.stl = nil
+			c.Properties.PreventInstall = true
+			lib := moduleLibraryInterface(m)
+			lib.setBuildStubs()
+			lib.setStubsVersion(variants[i])
 			// The implementation depends on the stubs
 			mctx.AddInterVariantDependency(stubImplDepTag, modules[len(modules)-1], modules[i])
 		}
@@ -1665,18 +1672,24 @@
 	InRecovery() bool
 	CcLibraryInterface() bool
 	Shared() bool
-	Static() bool
 }) bool {
 	return CanBeOrLinkAgainstVersionVariants(module) &&
-		module.CcLibraryInterface() && (module.Shared() || module.Static())
+		module.CcLibraryInterface() && module.Shared()
+}
+
+func moduleLibraryInterface(module android.Module) libraryInterface {
+	if m, ok := module.(*Module); ok {
+		return m.library
+	}
+	return nil
 }
 
 // versionSelector normalizes the versions in the Stubs.Versions property into MutatedProperties.AllStubsVersions,
 // and propagates the value from implementation libraries to llndk libraries with the same name.
 func versionSelectorMutator(mctx android.BottomUpMutatorContext) {
-	if library, ok := mctx.Module().(LinkableInterface); ok && CanBeVersionVariant(library) {
-		if library.CcLibraryInterface() && library.BuildSharedVariant() {
-			versions := library.StubsVersions(mctx)
+	if library := moduleLibraryInterface(mctx.Module()); library != nil && CanBeVersionVariant(mctx.Module().(*Module)) {
+		if library.buildShared() {
+			versions := library.stubsVersions(mctx)
 			if len(versions) > 0 {
 				normalizeVersions(mctx, versions)
 				if mctx.Failed() {
@@ -1684,7 +1697,7 @@
 				}
 				// Set the versions on the pre-mutated module so they can be read by any llndk modules that
 				// depend on the implementation library and haven't been mutated yet.
-				library.SetAllStubsVersions(versions)
+				library.setAllStubsVersions(versions)
 				return
 			}
 		}
@@ -1694,8 +1707,8 @@
 // versionMutator splits a module into the mandatory non-stubs variant
 // (which is unnamed) and zero or more stubs variants.
 func versionMutator(mctx android.BottomUpMutatorContext) {
-	if library, ok := mctx.Module().(LinkableInterface); ok && CanBeVersionVariant(library) {
-		createVersionVariations(mctx, library.AllStubsVersions())
+	if library := moduleLibraryInterface(mctx.Module()); library != nil && CanBeVersionVariant(mctx.Module().(*Module)) {
+		createVersionVariations(mctx, library.allStubsVersions())
 		return
 	}
 
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index fa6b326..106b2eb 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -86,10 +86,6 @@
 			if mctx.Device() {
 				variations = append(variations,
 					blueprint.Variation{Mutator: "image", Variation: android.CoreVariation})
-				if mt.linkTypes != nil {
-					variations = append(variations,
-						blueprint.Variation{Mutator: "version", Variation: version})
-				}
 			}
 			if mt.linkTypes == nil {
 				mctx.AddFarVariationDependencies(variations, dependencyTag, name)
@@ -97,6 +93,10 @@
 				for _, linkType := range mt.linkTypes {
 					libVariations := append(variations,
 						blueprint.Variation{Mutator: "link", Variation: linkType})
+					if mctx.Device() && linkType == "shared" {
+						libVariations = append(libVariations,
+							blueprint.Variation{Mutator: "version", Variation: version})
+					}
 					mctx.AddFarVariationDependencies(libVariations, dependencyTag, name)
 				}
 			}
@@ -428,22 +428,22 @@
 		specifiedDeps := specifiedDeps{}
 		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(specifiedDeps)
 
-		if !ccModule.HasStubsVariants() {
-			// Propagate dynamic dependencies for implementation libs, but not stubs.
-			p.SharedLibs = specifiedDeps.sharedLibs
+		if lib := ccModule.library; lib != nil {
+			if !lib.hasStubsVariants() {
+				// Propagate dynamic dependencies for implementation libs, but not stubs.
+				p.SharedLibs = specifiedDeps.sharedLibs
+			} else {
+				// TODO(b/169373910): 1. Only output the specific version (from
+				// ccModule.StubsVersion()) if the module is versioned. 2. Ensure that all
+				// the versioned stub libs are retained in the prebuilt tree; currently only
+				// the stub corresponding to ccModule.StubsVersion() is.
+				p.StubsVersions = lib.allStubsVersions()
+			}
 		}
 		p.SystemSharedLibs = specifiedDeps.systemSharedLibs
 	}
 	p.exportedGeneratedHeaders = exportedInfo.GeneratedHeaders
 
-	if ccModule.HasStubsVariants() {
-		// TODO(b/169373910): 1. Only output the specific version (from
-		// ccModule.StubsVersion()) if the module is versioned. 2. Ensure that all
-		// the versioned stub libs are retained in the prebuilt tree; currently only
-		// the stub corresponding to ccModule.StubsVersion() is.
-		p.StubsVersions = ccModule.AllStubsVersions()
-	}
-
 	if !p.memberType.noOutputFiles && addOutputFile {
 		p.outputFile = getRequiredMemberOutputFile(ctx, ccModule)
 	}
diff --git a/cc/linkable.go b/cc/linkable.go
index 177e0c4..60ab6df 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -16,16 +16,7 @@
 
 	NonCcVariants() bool
 
-	StubsVersions(android.BaseMutatorContext) []string
-	BuildStubs() bool
-	SetBuildStubs()
-	SetStubsVersion(string)
-	StubsVersion() string
-	SetAllStubsVersions([]string)
-	AllStubsVersions() []string
-	HasStubsVariants() bool
 	SelectedStl() string
-	ApiLevel() string
 
 	BuildStaticVariant() bool
 	BuildSharedVariant() bool
@@ -56,10 +47,6 @@
 	AlwaysSdk() bool
 	IsSdkVariant() bool
 
-	ToolchainLibrary() bool
-	NdkPrebuiltStl() bool
-	StubDecorator() bool
-
 	SplitPerApiLevel() bool
 }
 
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 9f4444d..3b1d2f4 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -185,7 +185,7 @@
 	if len(impls) > 1 {
 		panic(fmt.Errorf("Expected single implmenetation library, got %d", len(impls)))
 	} else if len(impls) == 1 {
-		return impls[0].(*Module).AllStubsVersions()
+		return moduleLibraryInterface(impls[0]).allStubsVersions()
 	}
 	return nil
 }
@@ -204,6 +204,7 @@
 	module.compiler = stub
 	module.linker = stub
 	module.installer = nil
+	module.library = stub
 
 	module.AddProperties(
 		&module.Properties,
@@ -251,6 +252,7 @@
 	module.compiler = nil
 	module.linker = decorator
 	module.installer = nil
+	module.library = decorator
 
 	module.AddProperties(
 		&module.Properties,
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 02b38a6..9097e7b 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -333,6 +333,7 @@
 	module.compiler = stub
 	module.linker = stub
 	module.installer = stub
+	module.library = stub
 
 	module.Properties.AlwaysSdk = true
 	module.Properties.Sdk_version = StringPtr("current")
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index b6733c2..70b15c1 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -131,7 +131,7 @@
 		}
 
 		if m, ok := module.(*Module); ok {
-			if installer, ok := m.installer.(*stubDecorator); ok && m.BuildStubs() {
+			if installer, ok := m.installer.(*stubDecorator); ok && m.library.buildStubs() {
 				if ctx.Config().ExcludeDraftNdkApis() &&
 					installer.properties.Draft {
 					return
diff --git a/cc/pgo.go b/cc/pgo.go
index 439d2f7..3cf550a 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -41,7 +41,6 @@
 var pgoProfileProjectsConfigKey = android.NewOnceKey("PgoProfileProjects")
 
 const profileInstrumentFlag = "-fprofile-generate=/data/local/tmp"
-const profileSamplingFlag = "-gmlt -fdebug-info-for-profiling"
 const profileUseInstrumentFormat = "-fprofile-use=%s"
 const profileUseSamplingFormat = "-fprofile-sample-accurate -fprofile-sample-use=%s"
 
@@ -100,9 +99,6 @@
 }
 func (props *PgoProperties) addSamplingProfileGatherFlags(ctx ModuleContext, flags Flags) Flags {
 	flags.Local.CFlags = append(flags.Local.CFlags, props.Pgo.Cflags...)
-
-	flags.Local.CFlags = append(flags.Local.CFlags, profileSamplingFlag)
-	flags.Local.LdFlags = append(flags.Local.LdFlags, profileSamplingFlag)
 	return flags
 }
 
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 45d3eb1..8873883 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -228,6 +228,7 @@
 		libraryDecorator: library,
 	}
 	module.linker = prebuilt
+	module.library = prebuilt
 
 	module.AddProperties(&prebuilt.properties)
 
diff --git a/cc/toolchain_library.go b/cc/toolchain_library.go
index 8c546c5..0c934ad 100644
--- a/cc/toolchain_library.go
+++ b/cc/toolchain_library.go
@@ -66,6 +66,7 @@
 	module.stl = nil
 	module.sanitize = nil
 	module.installer = nil
+	module.library = toolchainLibrary
 	module.Properties.Sdk_version = StringPtr("current")
 	return module.Init()
 }
diff --git a/cc/vndk.go b/cc/vndk.go
index 7d8777c..b2614c5 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -313,7 +313,7 @@
 		panic(err)
 	}
 
-	if m.HasStubsVariants() && name != "libz" {
+	if lib := m.library; lib != nil && lib.hasStubsVariants() && name != "libz" {
 		// b/155456180 libz is the ONLY exception here. We don't want to make
 		// libz an LLNDK library because we in general can't guarantee that
 		// libz will behave consistently especially about the compression.
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 7329aee..0a9b156 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -412,7 +412,9 @@
 	ctx.Status.AddOutput(terminal.NewStatusOutput(ctx.Writer, "", false,
 		build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD")))
 
-	config := build.NewConfig(ctx, flag.Args()...)
+	args := append([]string(nil), flag.Args()...)
+	args = append(args, "--skip-soong-tests")
+	config := build.NewConfig(ctx, args...)
 	config.Environment().Set("OUT_DIR", outDir)
 	if !*keepArtifacts {
 		config.Environment().Set("EMPTY_NINJA_FILE", "true")
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index a1b0c8e..e778bd6 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -139,6 +139,7 @@
 
 	met := metrics.New()
 	met.SetBuildDateTime(buildStarted)
+	met.SetBuildCommand(os.Args)
 
 	stat := &status.Status{}
 	defer stat.Finish()
diff --git a/dexpreopt/Android.bp b/dexpreopt/Android.bp
index 94c0361..35f90df 100644
--- a/dexpreopt/Android.bp
+++ b/dexpreopt/Android.bp
@@ -8,6 +8,7 @@
         "testing.go",
     ],
     testSrcs: [
+        "class_loader_context_test.go",
         "dexpreopt_test.go",
     ],
     deps: [
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index c8bab2f..6d77812 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -80,8 +80,8 @@
 
 // Add a new library path to the map, unless a path for this library already exists.
 // If necessary, check that the build and install paths exist.
-func (libPaths LibraryPaths) addLibraryPath(ctx android.ModuleContext, lib string,
-	hostPath, installPath android.Path, strict bool) {
+func (libPaths LibraryPaths) addLibraryPath(ctx android.ModuleInstallPathContext, lib string,
+	hostPath, installPath android.Path, strict bool) error {
 
 	// If missing dependencies are allowed, the build shouldn't fail when a <uses-library> is
 	// not found. However, this is likely to result is disabling dexpreopt, as it won't be
@@ -89,7 +89,7 @@
 	strict = strict && !ctx.Config().AllowMissingDependencies()
 
 	if hostPath == nil && strict {
-		android.ReportPathErrorf(ctx, "unknown build path to <uses-library> '%s'", lib)
+		return fmt.Errorf("unknown build path to <uses-library> '%s'", lib)
 	}
 
 	if installPath == nil {
@@ -97,7 +97,7 @@
 			// Assume that compatibility libraries are installed in /system/framework.
 			installPath = android.PathForModuleInstall(ctx, "framework", lib+".jar")
 		} else if strict {
-			android.ReportPathErrorf(ctx, "unknown install path to <uses-library> '%s'", lib)
+			return fmt.Errorf("unknown install path to <uses-library> '%s'", lib)
 		}
 	}
 
@@ -115,19 +115,30 @@
 		}
 		libPaths[lib] = &LibraryPath{hostPath, devicePath}
 	}
+	return nil
+}
+
+// Wrapper around addLibraryPath that does error reporting.
+func (libPaths LibraryPaths) addLibraryPathOrReportError(ctx android.ModuleInstallPathContext, lib string,
+	hostPath, installPath android.Path, strict bool) {
+
+	err := libPaths.addLibraryPath(ctx, lib, hostPath, installPath, strict)
+	if err != nil {
+		android.ReportPathErrorf(ctx, err.Error())
+	}
 }
 
 // Add a new library path to the map. Enforce checks that the library paths exist.
-func (libPaths LibraryPaths) AddLibraryPath(ctx android.ModuleContext, lib string, hostPath, installPath android.Path) {
-	libPaths.addLibraryPath(ctx, lib, hostPath, installPath, true)
+func (libPaths LibraryPaths) AddLibraryPath(ctx android.ModuleInstallPathContext, lib string, hostPath, installPath android.Path) {
+	libPaths.addLibraryPathOrReportError(ctx, lib, hostPath, installPath, true)
 }
 
 // Add a new library path to the map, if the library exists (name is not nil).
 // Don't enforce checks that the library paths exist. Some libraries may be missing from the build,
 // but their names still need to be added to <uses-library> tags in the manifest.
-func (libPaths LibraryPaths) MaybeAddLibraryPath(ctx android.ModuleContext, lib *string, hostPath, installPath android.Path) {
+func (libPaths LibraryPaths) MaybeAddLibraryPath(ctx android.ModuleInstallPathContext, lib *string, hostPath, installPath android.Path) {
 	if lib != nil {
-		libPaths.addLibraryPath(ctx, *lib, hostPath, installPath, false)
+		libPaths.addLibraryPathOrReportError(ctx, *lib, hostPath, installPath, false)
 	}
 }
 
@@ -153,7 +164,9 @@
 	clc.Target = append(clc.Target, targetPath)
 }
 
-func (m classLoaderContextMap) addLibs(ctx android.PathContext, sdkVer int, module *ModuleConfig, libs ...string) bool {
+func (m classLoaderContextMap) addLibs(ctx android.PathContext, sdkVer int, module *ModuleConfig,
+	libs ...string) (bool, error) {
+
 	clc := m.getValue(sdkVer)
 	for _, lib := range libs {
 		if p, ok := module.LibraryPaths[lib]; ok && p.Host != nil && p.Device != UnknownInstallLibraryPath {
@@ -162,15 +175,15 @@
 			if sdkVer == AnySdkVersion {
 				// Fail the build if dexpreopt doesn't know paths to one of the <uses-library>
 				// dependencies. In the future we may need to relax this and just disable dexpreopt.
-				android.ReportPathErrorf(ctx, "dexpreopt cannot find path for <uses-library> '%s'", lib)
+				return false, fmt.Errorf("dexpreopt cannot find path for <uses-library> '%s'", lib)
 			} else {
 				// No error for compatibility libraries, as Soong doesn't know if they are needed
 				// (this depends on the targetSdkVersion in the manifest).
+				return false, nil
 			}
-			return false
 		}
 	}
-	return true
+	return true, nil
 }
 
 func (m classLoaderContextMap) addSystemServerLibs(sdkVer int, ctx android.PathContext, module *ModuleConfig, libs ...string) {
@@ -206,7 +219,7 @@
 //    check that the class loader context provided by the PackageManager agrees with the stored
 //    class loader context recorded in the .odex file.
 //
-func genClassLoaderContext(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) *classLoaderContextMap {
+func genClassLoaderContext(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) (*classLoaderContextMap, error) {
 	classLoaderContexts := make(classLoaderContextMap)
 	systemServerJars := NonUpdatableSystemServerJars(ctx, global)
 
@@ -218,14 +231,14 @@
 	} else if module.EnforceUsesLibraries {
 		// Unconditional class loader context.
 		usesLibs := append(copyOf(module.UsesLibraries), module.OptionalUsesLibraries...)
-		if !classLoaderContexts.addLibs(ctx, AnySdkVersion, module, usesLibs...) {
-			return nil
+		if ok, err := classLoaderContexts.addLibs(ctx, AnySdkVersion, module, usesLibs...); !ok {
+			return nil, err
 		}
 
 		// Conditional class loader context for API version < 28.
 		const httpLegacy = "org.apache.http.legacy"
-		if !classLoaderContexts.addLibs(ctx, 28, module, httpLegacy) {
-			return nil
+		if ok, err := classLoaderContexts.addLibs(ctx, 28, module, httpLegacy); !ok {
+			return nil, err
 		}
 
 		// Conditional class loader context for API version < 29.
@@ -233,13 +246,13 @@
 			"android.hidl.base-V1.0-java",
 			"android.hidl.manager-V1.0-java",
 		}
-		if !classLoaderContexts.addLibs(ctx, 29, module, usesLibs29...) {
-			return nil
+		if ok, err := classLoaderContexts.addLibs(ctx, 29, module, usesLibs29...); !ok {
+			return nil, err
 		}
 
 		// Conditional class loader context for API version < 30.
-		if !classLoaderContexts.addLibs(ctx, 30, module, OptionalCompatUsesLibs30...) {
-			return nil
+		if ok, err := classLoaderContexts.addLibs(ctx, 30, module, OptionalCompatUsesLibs30...); !ok {
+			return nil, err
 		}
 
 	} else {
@@ -251,7 +264,7 @@
 
 	fixConditionalClassLoaderContext(classLoaderContexts)
 
-	return &classLoaderContexts
+	return &classLoaderContexts, nil
 }
 
 // Now that the full unconditional context is known, reconstruct conditional context.
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
new file mode 100644
index 0000000..51c1a0a
--- /dev/null
+++ b/dexpreopt/class_loader_context_test.go
@@ -0,0 +1,225 @@
+// 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 dexpreopt
+
+// This file contains unit tests for class loader context structure.
+// For class loader context tests involving .bp files, see TestUsesLibraries in java package.
+
+import (
+	"reflect"
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestCLC(t *testing.T) {
+	// Construct class loader context with the following structure:
+	// .
+	// ├── 29
+	// │   ├── android.hidl.manager
+	// │   └── android.hidl.base
+	// │
+	// └── any
+	//     ├── a
+	//     ├── b
+	//     ├── c
+	//     ├── d
+	//     ├── a2
+	//     ├── b2
+	//     ├── c2
+	//     ├── a1
+	//     ├── b1
+	//     ├── f
+	//     ├── a3
+	//     └── b3
+	//
+	ctx := testContext()
+
+	lp := make(LibraryPaths)
+
+	lp.AddLibraryPath(ctx, "a", buildPath(ctx, "a"), installPath(ctx, "a"))
+	lp.AddLibraryPath(ctx, "b", buildPath(ctx, "b"), installPath(ctx, "b"))
+
+	// "Maybe" variant in the good case: add as usual.
+	c := "c"
+	lp.MaybeAddLibraryPath(ctx, &c, buildPath(ctx, "c"), installPath(ctx, "c"))
+
+	// "Maybe" variant in the bad case: don't add library with unknown name, keep going.
+	lp.MaybeAddLibraryPath(ctx, nil, nil, nil)
+
+	// Add some libraries with nested subcontexts.
+
+	lp1 := make(LibraryPaths)
+	lp1.AddLibraryPath(ctx, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"))
+	lp1.AddLibraryPath(ctx, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"))
+
+	lp2 := make(LibraryPaths)
+	lp2.AddLibraryPath(ctx, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"))
+	lp2.AddLibraryPath(ctx, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"))
+	lp2.AddLibraryPath(ctx, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"))
+	lp2.AddLibraryPaths(lp1)
+
+	lp.AddLibraryPath(ctx, "d", buildPath(ctx, "d"), installPath(ctx, "d"))
+	lp.AddLibraryPaths(lp2)
+
+	lp3 := make(LibraryPaths)
+	lp3.AddLibraryPath(ctx, "f", buildPath(ctx, "f"), installPath(ctx, "f"))
+	lp3.AddLibraryPath(ctx, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"))
+	lp3.AddLibraryPath(ctx, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"))
+	lp.AddLibraryPaths(lp3)
+
+	// Compatibility libraries with unknown install paths get default paths.
+	lp.AddLibraryPath(ctx, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil)
+	lp.AddLibraryPath(ctx, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil)
+	lp.AddLibraryPath(ctx, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil)
+
+	module := testSystemModuleConfig(ctx, "test")
+	module.LibraryPaths = lp
+
+	m := make(classLoaderContextMap)
+	valid := true
+
+	ok, err := m.addLibs(ctx, AnySdkVersion, module, "a", "b", "c", "d", "a2", "b2", "c2", "a1", "b1", "f", "a3", "b3")
+	valid = valid && ok && err == nil
+
+	// Add compatibility libraries to conditional CLC for SDK level 29.
+	ok, err = m.addLibs(ctx, 29, module, AndroidHidlManager, AndroidHidlBase)
+	valid = valid && ok && err == nil
+
+	// Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only
+	// needed as a compatibility library if "android.test.runner" is in CLC as well.
+	ok, err = m.addLibs(ctx, 30, module, AndroidTestMock)
+	valid = valid && ok && err == nil
+
+	// When the same library is both in conditional and unconditional context, it should be removed
+	// from conditional context.
+	ok, err = m.addLibs(ctx, 42, module, "f")
+	valid = valid && ok && err == nil
+
+	fixConditionalClassLoaderContext(m)
+
+	var haveStr string
+	var havePaths android.Paths
+	var haveUsesLibs []string
+	if valid {
+		haveStr, havePaths = computeClassLoaderContext(ctx, m)
+		haveUsesLibs = m.usesLibs()
+	}
+
+	// Test that validation is successful (all paths are known).
+	t.Run("validate", func(t *testing.T) {
+		if !valid {
+			t.Errorf("invalid class loader context")
+		}
+	})
+
+	// Test that class loader context structure is correct.
+	t.Run("string", func(t *testing.T) {
+		wantStr := " --host-context-for-sdk 29 " +
+			"PCL[out/" + AndroidHidlManager + ".jar]#" +
+			"PCL[out/" + AndroidHidlBase + ".jar]" +
+			" --target-context-for-sdk 29 " +
+			"PCL[/system/framework/" + AndroidHidlManager + ".jar]#" +
+			"PCL[/system/framework/" + AndroidHidlBase + ".jar]" +
+			" --host-context-for-sdk any " +
+			"PCL[out/a.jar]#PCL[out/b.jar]#PCL[out/c.jar]#PCL[out/d.jar]#" +
+			"PCL[out/a2.jar]#PCL[out/b2.jar]#PCL[out/c2.jar]#" +
+			"PCL[out/a1.jar]#PCL[out/b1.jar]#" +
+			"PCL[out/f.jar]#PCL[out/a3.jar]#PCL[out/b3.jar]" +
+			" --target-context-for-sdk any " +
+			"PCL[/system/a.jar]#PCL[/system/b.jar]#PCL[/system/c.jar]#PCL[/system/d.jar]#" +
+			"PCL[/system/a2.jar]#PCL[/system/b2.jar]#PCL[/system/c2.jar]#" +
+			"PCL[/system/a1.jar]#PCL[/system/b1.jar]#" +
+			"PCL[/system/f.jar]#PCL[/system/a3.jar]#PCL[/system/b3.jar]"
+		if wantStr != haveStr {
+			t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr)
+		}
+	})
+
+	// Test that all expected build paths are gathered.
+	t.Run("paths", func(t *testing.T) {
+		wantPaths := []string{
+			"out/android.hidl.manager-V1.0-java.jar", "out/android.hidl.base-V1.0-java.jar",
+			"out/a.jar", "out/b.jar", "out/c.jar", "out/d.jar",
+			"out/a2.jar", "out/b2.jar", "out/c2.jar",
+			"out/a1.jar", "out/b1.jar",
+			"out/f.jar", "out/a3.jar", "out/b3.jar",
+		}
+		if !reflect.DeepEqual(wantPaths, havePaths.Strings()) {
+			t.Errorf("\nwant paths: %s\nhave paths: %s", wantPaths, havePaths)
+		}
+	})
+
+	// Test for libraries that are added by the manifest_fixer.
+	t.Run("uses libs", func(t *testing.T) {
+		wantUsesLibs := []string{"a", "b", "c", "d", "a2", "b2", "c2", "a1", "b1", "f", "a3", "b3"}
+		if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
+			t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs)
+		}
+	})
+}
+
+// Test that an unexpected unknown build path causes immediate error.
+func TestCLCUnknownBuildPath(t *testing.T) {
+	ctx := testContext()
+	lp := make(LibraryPaths)
+	err := lp.addLibraryPath(ctx, "a", nil, nil, true)
+	checkError(t, err, "unknown build path to <uses-library> 'a'")
+}
+
+// Test that an unexpected unknown install path causes immediate error.
+func TestCLCUnknownInstallPath(t *testing.T) {
+	ctx := testContext()
+	lp := make(LibraryPaths)
+	err := lp.addLibraryPath(ctx, "a", buildPath(ctx, "a"), nil, true)
+	checkError(t, err, "unknown install path to <uses-library> 'a'")
+}
+
+func TestCLCMaybeAdd(t *testing.T) {
+	ctx := testContext()
+
+	lp := make(LibraryPaths)
+	a := "a"
+	lp.MaybeAddLibraryPath(ctx, &a, nil, nil)
+
+	module := testSystemModuleConfig(ctx, "test")
+	module.LibraryPaths = lp
+
+	m := make(classLoaderContextMap)
+	_, err := m.addLibs(ctx, AnySdkVersion, module, "a")
+	checkError(t, err, "dexpreopt cannot find path for <uses-library> 'a'")
+}
+
+func checkError(t *testing.T, have error, want string) {
+	if have == nil {
+		t.Errorf("\nwant error: '%s'\nhave: none", want)
+	} else if msg := have.Error(); !strings.HasPrefix(msg, want) {
+		t.Errorf("\nwant error: '%s'\nhave error: '%s'\n", want, msg)
+	}
+}
+
+func testContext() android.ModuleInstallPathContext {
+	config := android.TestConfig("out", nil, "", nil)
+	return android.ModuleInstallPathContextForTesting(config)
+}
+
+func buildPath(ctx android.PathContext, lib string) android.Path {
+	return android.PathForOutput(ctx, lib+".jar")
+}
+
+func installPath(ctx android.ModuleInstallPathContext, lib string) android.InstallPath {
+	return android.PathForModuleInstall(ctx, lib+".jar")
+}
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 51d1157..a07f1fa 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -81,7 +81,9 @@
 	}
 
 	if !dexpreoptDisabled(ctx, global, module) {
-		if clc := genClassLoaderContext(ctx, global, module); clc != nil {
+		if clc, err := genClassLoaderContext(ctx, global, module); err != nil {
+			android.ReportPathErrorf(ctx, err.Error())
+		} else if clc != nil {
 			appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
 				!module.NoCreateAppImage
 
diff --git a/genrule/genrule.go b/genrule/genrule.go
index b09c195..53b9dbe 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -561,13 +561,12 @@
 
 func (g *Module) AndroidMk() android.AndroidMkData {
 	return android.AndroidMkData{
-		Include:    "$(BUILD_PHONY_PACKAGE)",
-		Class:      "FAKE",
+		Class:      "ETC",
 		OutputFile: android.OptionalPathForPath(g.outputFiles[0]),
 		SubName:    g.subName,
 		Extra: []android.AndroidMkExtraFunc{
 			func(w io.Writer, outputFile android.Path) {
-				fmt.Fprintln(w, "LOCAL_ADDITIONAL_DEPENDENCIES :=", strings.Join(g.outputDeps.Strings(), " "))
+				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
 			},
 		},
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
diff --git a/java/Android.bp b/java/Android.bp
index 92e8ca4..9e8dc78 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -22,6 +22,7 @@
         "androidmk.go",
         "app_builder.go",
         "app.go",
+        "boot_jars.go",
         "builder.go",
         "device_host_converter.go",
         "dex.go",
diff --git a/java/androidmk.go b/java/androidmk.go
index c21c83a..e1a661f 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -217,10 +217,6 @@
 			func(entries *android.AndroidMkEntries) {
 				if prebuilt.dexJarFile != nil {
 					entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile)
-					// TODO(b/125517186): export the dex jar as a classes jar to match some mis-uses in Make until
-					// boot_jars_package_check.mk can check dex jars.
-					entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.dexJarFile)
-					entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.dexJarFile)
 				}
 				if len(prebuilt.dexpreopter.builtInstalled) > 0 {
 					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", prebuilt.dexpreopter.builtInstalled)
diff --git a/java/boot_jars.go b/java/boot_jars.go
new file mode 100644
index 0000000..900eb7a
--- /dev/null
+++ b/java/boot_jars.go
@@ -0,0 +1,123 @@
+// 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 java
+
+import (
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterSingletonType("boot_jars", bootJarsSingletonFactory)
+}
+
+func bootJarsSingletonFactory() android.Singleton {
+	return &bootJarsSingleton{}
+}
+
+type bootJarsSingleton struct{}
+
+func populateMapFromConfiguredJarList(ctx android.SingletonContext, moduleToApex map[string]string, list android.ConfiguredJarList, name string) bool {
+	for i := 0; i < list.Len(); i++ {
+		module := list.Jar(i)
+		// Ignore jacocoagent it is only added when instrumenting and so has no impact on
+		// app compatibility.
+		if module == "jacocoagent" {
+			continue
+		}
+		apex := list.Apex(i)
+		if existing, ok := moduleToApex[module]; ok {
+			ctx.Errorf("Configuration property %q is invalid as it contains multiple references to module (%s) in APEXes (%s and %s)",
+				module, existing, apex)
+			return false
+		}
+
+		moduleToApex[module] = apex
+	}
+
+	return true
+}
+
+func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	config := ctx.Config()
+	if config.SkipBootJarsCheck() {
+		return
+	}
+
+	// Populate a map from module name to APEX from the boot jars. If there is a problem
+	// such as duplicate modules then fail and return immediately.
+	moduleToApex := make(map[string]string)
+	if !populateMapFromConfiguredJarList(ctx, moduleToApex, config.NonUpdatableBootJars(), "BootJars") ||
+		!populateMapFromConfiguredJarList(ctx, moduleToApex, config.UpdatableBootJars(), "UpdatableBootJars") {
+		return
+	}
+
+	// Map from module name to the correct apex variant.
+	nameToApexVariant := make(map[string]android.Module)
+
+	// Scan all the modules looking for the module/apex variants corresponding to the
+	// boot jars.
+	ctx.VisitAllModules(func(module android.Module) {
+		name := ctx.ModuleName(module)
+		if apex, ok := moduleToApex[name]; ok {
+			apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
+			if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApex(apex) {
+				// The module name/apex variant should be unique in the system but double check
+				// just in case something has gone wrong.
+				if existing, ok := nameToApexVariant[name]; ok {
+					ctx.Errorf("found multiple variants matching %s:%s: %q and %q", apex, name, existing, module)
+				}
+				nameToApexVariant[name] = module
+			}
+		}
+	})
+
+	timestamp := android.PathForOutput(ctx, "boot-jars-package-check/stamp")
+
+	rule := android.NewRuleBuilder()
+	checkBootJars := rule.Command().BuiltTool(ctx, "check_boot_jars").
+		Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt"))
+
+	// If this is not an unbundled build and missing dependencies are not allowed
+	// then all the boot jars listed must have been found.
+	strict := !config.UnbundledBuild() && !config.AllowMissingDependencies()
+
+	// Iterate over the module names on the boot classpath in order
+	for _, name := range android.SortedStringKeys(moduleToApex) {
+		if apexVariant, ok := nameToApexVariant[name]; ok {
+			if dep, ok := apexVariant.(Dependency); ok {
+				// Add the implementation jars for the module to be checked. This uses implementation
+				// and resources jar as that is what the previous make based check uses.
+				for _, jar := range dep.ImplementationAndResourcesJars() {
+					checkBootJars.Input(jar)
+				}
+			} else if _, ok := apexVariant.(*DexImport); ok {
+				// TODO(b/171479578): ignore deximport when doing package check until boot_jars.go can check dex jars.
+			} else {
+				ctx.Errorf("module %q is of type %q which is not supported as a boot jar", name, ctx.ModuleType(apexVariant))
+			}
+		} else if strict {
+			ctx.Errorf("boot jars package check failed as it could not find module %q for apex %q", name, moduleToApex[name])
+		}
+	}
+
+	checkBootJars.Text("&& touch").Output(timestamp)
+	rule.Build(pctx, ctx, "boot_jars_package_check", "check boot jar packages")
+
+	// The check-boot-jars phony target depends on the timestamp created if the check succeeds.
+	ctx.Phony("check-boot-jars", timestamp)
+
+	// The droidcore phony target depends on the check-boot-jars phony target
+	ctx.Phony("droidcore", android.PathForPhony(ctx, "check-boot-jars"))
+}
diff --git a/java/java.go b/java/java.go
index 4b48a15..9f09051 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1187,18 +1187,6 @@
 	// javaVersion flag.
 	flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), sdkContext(j))
 
-	// javac flags.
-	javacFlags := j.properties.Javacflags
-	if flags.javaVersion.usesJavaModules() {
-		javacFlags = append(javacFlags, j.properties.Openjdk9.Javacflags...)
-	}
-	if ctx.Config().MinimizeJavaDebugInfo() && !ctx.Host() {
-		// For non-host binaries, override the -g flag passed globally to remove
-		// local variable debug info to reduce disk and memory usage.
-		javacFlags = append(javacFlags, "-g:source,lines")
-	}
-	javacFlags = append(javacFlags, "-Xlint:-dep-ann")
-
 	if ctx.Config().RunErrorProne() {
 		if config.ErrorProneClasspath == nil {
 			ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
@@ -1249,24 +1237,77 @@
 		}
 	}
 
-	if j.properties.Patch_module != nil && flags.javaVersion.usesJavaModules() {
-		// Manually specify build directory in case it is not under the repo root.
-		// (javac doesn't seem to expand into symbolc links when searching for patch-module targets, so
-		// just adding a symlink under the root doesn't help.)
-		patchPaths := ".:" + ctx.Config().BuildDir()
-		classPath := flags.classpath.FormJavaClassPath("")
-		if classPath != "" {
-			patchPaths += ":" + classPath
-		}
-		javacFlags = append(javacFlags, "--patch-module="+String(j.properties.Patch_module)+"="+patchPaths)
-	}
-
 	// systemModules
 	flags.systemModules = deps.systemModules
 
 	// aidl flags.
 	flags.aidlFlags, flags.aidlDeps = j.aidlFlags(ctx, deps.aidlPreprocess, deps.aidlIncludeDirs)
 
+	return flags
+}
+
+func (j *Module) collectJavacFlags(
+	ctx android.ModuleContext, flags javaBuilderFlags, srcFiles android.Paths) javaBuilderFlags {
+	// javac flags.
+	javacFlags := j.properties.Javacflags
+
+	if ctx.Config().MinimizeJavaDebugInfo() && !ctx.Host() {
+		// For non-host binaries, override the -g flag passed globally to remove
+		// local variable debug info to reduce disk and memory usage.
+		javacFlags = append(javacFlags, "-g:source,lines")
+	}
+	javacFlags = append(javacFlags, "-Xlint:-dep-ann")
+
+	if flags.javaVersion.usesJavaModules() {
+		javacFlags = append(javacFlags, j.properties.Openjdk9.Javacflags...)
+
+		if j.properties.Patch_module != nil {
+			// Manually specify build directory in case it is not under the repo root.
+			// (javac doesn't seem to expand into symbolic links when searching for patch-module targets, so
+			// just adding a symlink under the root doesn't help.)
+			patchPaths := []string{".", ctx.Config().BuildDir()}
+
+			// b/150878007
+			//
+			// Workaround to support *Bazel-executed* JDK9 javac in Bazel's
+			// execution root for --patch-module. If this javac command line is
+			// invoked within Bazel's execution root working directory, the top
+			// level directories (e.g. libcore/, tools/, frameworks/) are all
+			// symlinks. JDK9 javac does not traverse into symlinks, which causes
+			// --patch-module to fail source file lookups when invoked in the
+			// execution root.
+			//
+			// Short of patching javac or enumerating *all* directories as possible
+			// input dirs, manually add the top level dir of the source files to be
+			// compiled.
+			topLevelDirs := map[string]bool{}
+			for _, srcFilePath := range srcFiles {
+				srcFileParts := strings.Split(srcFilePath.String(), "/")
+				// Ignore source files that are already in the top level directory
+				// as well as generated files in the out directory. The out
+				// directory may be an absolute path, which means srcFileParts[0] is the
+				// empty string, so check that as well. Note that "out" in Bazel's execution
+				// root is *not* a symlink, which doesn't cause problems for --patch-modules
+				// anyway, so it's fine to not apply this workaround for generated
+				// source files.
+				if len(srcFileParts) > 1 &&
+					srcFileParts[0] != "" &&
+					srcFileParts[0] != "out" {
+					topLevelDirs[srcFileParts[0]] = true
+				}
+			}
+			patchPaths = append(patchPaths, android.SortedStringKeys(topLevelDirs)...)
+
+			classPath := flags.classpath.FormJavaClassPath("")
+			if classPath != "" {
+				patchPaths = append(patchPaths, classPath)
+			}
+			javacFlags = append(
+				javacFlags,
+				"--patch-module="+String(j.properties.Patch_module)+"="+strings.Join(patchPaths, ":"))
+		}
+	}
+
 	if len(javacFlags) > 0 {
 		// optimization.
 		ctx.Variable(pctx, "javacFlags", strings.Join(javacFlags, " "))
@@ -1297,6 +1338,10 @@
 
 	srcFiles = j.genSources(ctx, srcFiles, flags)
 
+	// Collect javac flags only after computing the full set of srcFiles to
+	// ensure that the --patch-module lookup paths are complete.
+	flags = j.collectJavacFlags(ctx, flags, srcFiles)
+
 	srcJars := srcFiles.FilterByExt(".srcjar")
 	srcJars = append(srcJars, deps.srcJars...)
 	if aaptSrcJar != nil {
diff --git a/java/java_test.go b/java/java_test.go
index cdb4375..6c0a908 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -2046,7 +2046,14 @@
 
 			java_library {
 				name: "baz",
-				srcs: ["c.java"],
+				srcs: [
+					"c.java",
+					// Tests for b/150878007
+					"dir/d.java",
+					"dir2/e.java",
+					"dir2/f.java",
+					"nested/dir/g.java"
+				],
 				patch_module: "java.base",
 			}
 		`
@@ -2055,7 +2062,8 @@
 		checkPatchModuleFlag(t, ctx, "foo", "")
 		expected := "java.base=.:" + buildDir
 		checkPatchModuleFlag(t, ctx, "bar", expected)
-		expected = "java.base=" + strings.Join([]string{".", buildDir, moduleToPath("ext"), moduleToPath("framework")}, ":")
+		expected = "java.base=" + strings.Join([]string{
+			".", buildDir, "dir", "dir2", "nested", moduleToPath("ext"), moduleToPath("framework")}, ":")
 		checkPatchModuleFlag(t, ctx, "baz", expected)
 	})
 }
diff --git a/rust/rust.go b/rust/rust.go
index 77d9a90..e65b90e 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -133,14 +133,6 @@
 func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
 }
 
-func (mod *Module) BuildStubs() bool {
-	return false
-}
-
-func (mod *Module) HasStubsVariants() bool {
-	return false
-}
-
 func (mod *Module) SelectedStl() string {
 	return ""
 }
@@ -154,10 +146,6 @@
 	panic(fmt.Errorf("NonCcVariants called on non-library module: %q", mod.BaseModuleName()))
 }
 
-func (mod *Module) ApiLevel() string {
-	panic(fmt.Errorf("Called ApiLevel on Rust module %q; stubs libraries are not yet supported.", mod.BaseModuleName()))
-}
-
 func (mod *Module) Static() bool {
 	if mod.compiler != nil {
 		if library, ok := mod.compiler.(libraryInterface); ok {
@@ -233,18 +221,6 @@
 	return false
 }
 
-func (mod *Module) ToolchainLibrary() bool {
-	return false
-}
-
-func (mod *Module) NdkPrebuiltStl() bool {
-	return false
-}
-
-func (mod *Module) StubDecorator() bool {
-	return false
-}
-
 type Deps struct {
 	Dylibs     []string
 	Rlibs      []string
@@ -466,26 +442,6 @@
 	panic(fmt.Errorf("SetShared called on non-library module: %q", mod.BaseModuleName()))
 }
 
-func (mod *Module) SetBuildStubs() {
-	panic("SetBuildStubs not yet implemented for rust modules")
-}
-
-func (mod *Module) SetStubsVersion(string) {
-	panic("SetStubsVersion not yet implemented for rust modules")
-}
-
-func (mod *Module) StubsVersion() string {
-	panic("StubsVersion not yet implemented for rust modules")
-}
-
-func (mod *Module) SetAllStubsVersions([]string) {
-	panic("SetAllStubsVersions not yet implemented for rust modules")
-}
-
-func (mod *Module) AllStubsVersions() []string {
-	return nil
-}
-
 func (mod *Module) BuildStaticVariant() bool {
 	if mod.compiler != nil {
 		if library, ok := mod.compiler.(libraryInterface); ok {
@@ -508,16 +464,6 @@
 	return mod
 }
 
-func (mod *Module) StubsVersions(ctx android.BaseMutatorContext) []string {
-	// For now, Rust has no stubs versions.
-	if mod.compiler != nil {
-		if _, ok := mod.compiler.(libraryInterface); ok {
-			return []string{}
-		}
-	}
-	panic(fmt.Errorf("StubsVersions called on non-library module: %q", mod.BaseModuleName()))
-}
-
 func (mod *Module) OutputFile() android.OptionalPath {
 	return mod.outputFile
 }
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 92f5c53..dd03f28 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -1,4 +1,20 @@
 python_binary_host {
+    name: "check_boot_jars",
+    main: "check_boot_jars/check_boot_jars.py",
+    srcs: [
+        "check_boot_jars/check_boot_jars.py",
+    ],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    },
+}
+
+python_binary_host {
     name: "manifest_fixer",
     main: "manifest_fixer.py",
     srcs: [
diff --git a/scripts/check_boot_jars/check_boot_jars.py b/scripts/check_boot_jars/check_boot_jars.py
new file mode 100755
index 0000000..cf4ef27
--- /dev/null
+++ b/scripts/check_boot_jars/check_boot_jars.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python
+
+"""
+Check boot jars.
+
+Usage: check_boot_jars.py <package_allow_list_file> <jar1> <jar2> ...
+"""
+import logging
+import os.path
+import re
+import subprocess
+import sys
+
+
+# The compiled allow list RE.
+allow_list_re = None
+
+
+def LoadAllowList(filename):
+  """ Load and compile allow list regular expressions from filename.
+  """
+  lines = []
+  with open(filename, 'r') as f:
+    for line in f:
+      line = line.strip()
+      if not line or line.startswith('#'):
+        continue
+      lines.append(line)
+  combined_re = r'^(%s)$' % '|'.join(lines)
+  global allow_list_re
+  try:
+    allow_list_re = re.compile(combined_re)
+  except re.error:
+    logging.exception(
+        'Cannot compile package allow list regular expression: %r',
+        combined_re)
+    allow_list_re = None
+    return False
+  return True
+
+
+def CheckJar(allow_list_path, jar):
+  """Check a jar file.
+  """
+  # Get the list of files inside the jar file.
+  p = subprocess.Popen(args='jar tf %s' % jar,
+      stdout=subprocess.PIPE, shell=True)
+  stdout, _ = p.communicate()
+  if p.returncode != 0:
+    return False
+  items = stdout.split()
+  classes = 0
+  for f in items:
+    if f.endswith('.class'):
+      classes += 1
+      package_name = os.path.dirname(f)
+      package_name = package_name.replace('/', '.')
+      if not package_name or not allow_list_re.match(package_name):
+        print >> sys.stderr, ('Error: %s contains class file %s, whose package name %s is empty or'
+                              ' not in the allow list %s of packages allowed on the bootclasspath.'
+                              % (jar, f, package_name, allow_list_path))
+        return False
+  if classes == 0:
+    print >> sys.stderr, ('Error: %s does not contain any class files.' % jar)
+    return False
+  return True
+
+
+def main(argv):
+  if len(argv) < 2:
+    print __doc__
+    return 1
+  allow_list_path = argv[0]
+
+  if not LoadAllowList(allow_list_path):
+    return 1
+
+  passed = True
+  for jar in argv[1:]:
+    if not CheckJar(allow_list_path, jar):
+      passed = False
+  if not passed:
+    return 1
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/scripts/check_boot_jars/package_allowed_list.txt b/scripts/check_boot_jars/package_allowed_list.txt
new file mode 100644
index 0000000..18ab427
--- /dev/null
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -0,0 +1,248 @@
+# Boot jar package name allowed list.
+# Each line is interpreted as a regular expression.
+
+###################################################
+# core-libart.jar & core-oj.jar
+java\.awt\.font
+java\.beans
+java\.io
+java\.lang
+java\.lang\.annotation
+java\.lang\.invoke
+java\.lang\.ref
+java\.lang\.reflect
+java\.math
+java\.net
+java\.nio
+java\.nio\.file
+java\.nio\.file\.spi
+java\.nio\.file\.attribute
+java\.nio\.channels
+java\.nio\.channels\.spi
+java\.nio\.charset
+java\.nio\.charset\.spi
+java\.security
+java\.security\.acl
+java\.security\.cert
+java\.security\.interfaces
+java\.security\.spec
+java\.sql
+java\.text
+java\.text\.spi
+java\.time
+java\.time\.chrono
+java\.time\.format
+java\.time\.temporal
+java\.time\.zone
+java\.util
+java\.util\.concurrent
+java\.util\.concurrent\.atomic
+java\.util\.concurrent\.locks
+java\.util\.function
+java\.util\.jar
+java\.util\.logging
+java\.util\.prefs
+java\.util\.regex
+java\.util\.spi
+java\.util\.stream
+java\.util\.zip
+# TODO: Remove javax.annotation.processing if possible, see http://b/132338110:
+javax\.annotation\.processing
+javax\.crypto
+javax\.crypto\.interfaces
+javax\.crypto\.spec
+javax\.net
+javax\.net\.ssl
+javax\.security\.auth
+javax\.security\.auth\.callback
+javax\.security\.auth\.login
+javax\.security\.auth\.x500
+javax\.security\.cert
+javax\.sql
+javax\.xml
+javax\.xml\.datatype
+javax\.xml\.namespace
+javax\.xml\.parsers
+javax\.xml\.transform
+javax\.xml\.transform\.dom
+javax\.xml\.transform\.sax
+javax\.xml\.transform\.stream
+javax\.xml\.validation
+javax\.xml\.xpath
+jdk\.internal\.util
+jdk\.internal\.vm\.annotation
+jdk\.net
+org\.w3c\.dom
+org\.w3c\.dom\.ls
+org\.w3c\.dom\.traversal
+# OpenJdk internal implementation.
+sun\.invoke\.util
+sun\.invoke\.empty
+sun\.misc
+sun\.util.*
+sun\.text.*
+sun\.security.*
+sun\.reflect.*
+sun\.nio.*
+sun\.net.*
+com\.sun\..*
+
+# TODO: Move these internal org.apache.harmony classes to libcore.*
+org\.apache\.harmony\.crypto\.internal
+org\.apache\.harmony\.dalvik
+org\.apache\.harmony\.dalvik\.ddmc
+org\.apache\.harmony\.luni\.internal\.util
+org\.apache\.harmony\.security
+org\.apache\.harmony\.security\.asn1
+org\.apache\.harmony\.security\.fortress
+org\.apache\.harmony\.security\.pkcs10
+org\.apache\.harmony\.security\.pkcs7
+org\.apache\.harmony\.security\.pkcs8
+org\.apache\.harmony\.security\.provider\.crypto
+org\.apache\.harmony\.security\.utils
+org\.apache\.harmony\.security\.x501
+org\.apache\.harmony\.security\.x509
+org\.apache\.harmony\.security\.x509\.tsp
+org\.apache\.harmony\.xml
+org\.apache\.harmony\.xml\.dom
+org\.apache\.harmony\.xml\.parsers
+
+org\.json
+org\.xmlpull\.v1
+org\.xmlpull\.v1\.sax2
+
+# TODO:  jarjar org.kxml2.io to com.android org\.kxml2\.io
+org\.kxml2\.io
+org\.xml
+org\.xml\.sax
+org\.xml\.sax\.ext
+org\.xml\.sax\.helpers
+
+dalvik\..*
+libcore\..*
+android\..*
+com\.android\..*
+###################################################
+# android.test.base.jar
+junit\.extensions
+junit\.framework
+android\.test
+android\.test\.suitebuilder\.annotation
+
+
+###################################################
+# ext.jar
+# TODO: jarjar javax.sip to com.android
+javax\.sip
+javax\.sip\.address
+javax\.sip\.header
+javax\.sip\.message
+
+# TODO: jarjar org.apache.commons to com.android
+org\.apache\.commons\.codec
+org\.apache\.commons\.codec\.binary
+org\.apache\.commons\.codec\.language
+org\.apache\.commons\.codec\.net
+org\.apache\.commons\.logging
+org\.apache\.commons\.logging\.impl
+org\.apache\.http
+org\.apache\.http\.auth
+org\.apache\.http\.auth\.params
+org\.apache\.http\.client
+org\.apache\.http\.client\.entity
+org\.apache\.http\.client\.methods
+org\.apache\.http\.client\.params
+org\.apache\.http\.client\.protocol
+org\.apache\.http\.client\.utils
+org\.apache\.http\.conn
+org\.apache\.http\.conn\.params
+org\.apache\.http\.conn\.routing
+org\.apache\.http\.conn\.scheme
+org\.apache\.http\.conn\.ssl
+org\.apache\.http\.conn\.util
+org\.apache\.http\.cookie
+org\.apache\.http\.cookie\.params
+org\.apache\.http\.entity
+org\.apache\.http\.impl
+org\.apache\.http\.impl\.auth
+org\.apache\.http\.impl\.client
+org\.apache\.http\.impl\.client
+org\.apache\.http\.impl\.conn
+org\.apache\.http\.impl\.conn\.tsccm
+org\.apache\.http\.impl\.cookie
+org\.apache\.http\.impl\.entity
+org\.apache\.http\.impl\.io
+org\.apache\.http\.impl\.io
+org\.apache\.http\.io
+org\.apache\.http\.message
+org\.apache\.http\.params
+org\.apache\.http\.protocol
+org\.apache\.http\.util
+
+# TODO: jarjar gov.nist to com.android
+gov\.nist\.core
+gov\.nist\.core\.net
+gov\.nist\.javax\.sip
+gov\.nist\.javax\.sip\.address
+gov\.nist\.javax\.sip\.clientauthutils
+gov\.nist\.javax\.sip\.header
+gov\.nist\.javax\.sip\.header\.extensions
+gov\.nist\.javax\.sip\.header\.ims
+gov\.nist\.javax\.sip\.message
+gov\.nist\.javax\.sip\.parser
+gov\.nist\.javax\.sip\.parser\.extensions
+gov\.nist\.javax\.sip\.parser\.ims
+gov\.nist\.javax\.sip\.stack
+
+org\.ccil\.cowan\.tagsoup
+org\.ccil\.cowan\.tagsoup\.jaxp
+
+###################################################
+# framework.jar
+javax\.microedition\.khronos\.opengles
+javax\.microedition\.khronos\.egl
+
+android
+
+###################################################
+# apache-xml.jar
+org\.apache\.xml\.res
+org\.apache\.xml\.utils
+org\.apache\.xml\.utils\.res
+org\.apache\.xml\.dtm
+org\.apache\.xml\.dtm\.ref
+org\.apache\.xml\.dtm\.ref\.dom2dtm
+org\.apache\.xml\.dtm\.ref\.sax2dtm
+org\.apache\.xml\.serializer
+org\.apache\.xml\.serializer\.utils
+org\.apache\.xml\.serializer\.dom3
+org\.apache\.xpath
+org\.apache\.xpath\.operations
+org\.apache\.xpath\.domapi
+org\.apache\.xpath\.functions
+org\.apache\.xpath\.res
+org\.apache\.xpath\.axes
+org\.apache\.xpath\.objects
+org\.apache\.xpath\.patterns
+org\.apache\.xpath\.jaxp
+org\.apache\.xpath\.compiler
+org\.apache\.xalan
+org\.apache\.xalan\.res
+org\.apache\.xalan\.templates
+org\.apache\.xalan\.serialize
+org\.apache\.xalan\.extensions
+org\.apache\.xalan\.processor
+org\.apache\.xalan\.transformer
+org\.apache\.xalan\.xslt
+
+###################################################
+# Packages in the google namespace across all bootclasspath jars.
+com\.google\.android\..*
+com\.google\.vr\.platform.*
+com\.google\.i18n\.phonenumbers\..*
+com\.google\.i18n\.phonenumbers
+
+###################################################
+# Packages used for Android in Chrome OS
+org\.chromium\.arc
+org\.chromium\.arc\..*
diff --git a/scripts/conv_linker_config.py b/scripts/conv_linker_config.py
index 86f788d..81425fb 100644
--- a/scripts/conv_linker_config.py
+++ b/scripts/conv_linker_config.py
@@ -25,8 +25,12 @@
 
 
 def Proto(args):
+  json_content = ''
   with open(args.source) as f:
-    obj = json.load(f, object_pairs_hook=collections.OrderedDict)
+    for line in f:
+      if not line.lstrip().startswith('//'):
+        json_content += line
+  obj = json.loads(json_content, object_pairs_hook=collections.OrderedDict)
   pb = ParseDict(obj, linker_config_pb2.LinkerConfig())
   with open(args.output, 'wb') as f:
     f.write(pb.SerializeToString())
diff --git a/ui/build/config.go b/ui/build/config.go
index 82df8a0..e57c730 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -41,12 +41,13 @@
 	buildDateTime string
 
 	// From the arguments
-	parallel   int
-	keepGoing  int
-	verbose    bool
-	checkbuild bool
-	dist       bool
-	skipMake   bool
+	parallel       int
+	keepGoing      int
+	verbose        bool
+	checkbuild     bool
+	dist           bool
+	skipMake       bool
+	skipSoongTests bool
 
 	// From the product config
 	katiArgs        []string
@@ -526,6 +527,8 @@
 			c.verbose = true
 		} else if arg == "--skip-make" {
 			c.skipMake = true
+		} else if arg == "--skip-soong-tests" {
+			c.skipSoongTests = true
 		} else if len(arg) > 0 && arg[0] == '-' {
 			parseArgNum := func(def int) int {
 				if len(arg) > 2 {
diff --git a/ui/build/soong.go b/ui/build/soong.go
index fb21430..b20237c 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -38,7 +38,12 @@
 		ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
 		defer ctx.EndTrace()
 
-		cmd := Command(ctx, config, "blueprint bootstrap", "build/blueprint/bootstrap.bash", "-t", "-n")
+		args := []string{"-n"}
+		if !config.skipSoongTests {
+			args = append(args, "-t")
+		}
+
+		cmd := Command(ctx, config, "blueprint bootstrap", "build/blueprint/bootstrap.bash", args...)
 		cmd.Environment.Set("BLUEPRINTDIR", "./build/blueprint")
 		cmd.Environment.Set("BOOTSTRAP", "./build/blueprint/bootstrap.bash")
 		cmd.Environment.Set("BUILDDIR", config.SoongOutDir())
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index 83b3807..3164680 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -68,6 +68,12 @@
 	miniBootstrapDir := filepath.Join(outDir, "soong", ".minibootstrap")
 	modulePathsDir := filepath.Join(outDir, ".module_paths")
 	variablesFilePath := filepath.Join(outDir, "soong", "soong.variables")
+	// dexpreopt.config is an input to the soong_docs action, which runs the
+	// soong_build primary builder. However, this file is created from $(shell)
+	// invocation at Kati parse time, so it's not an explicit output of any
+	// Ninja action, but it is present during the build itself and can be
+	// treated as an source file.
+	dexpreoptConfigFilePath := filepath.Join(outDir, "soong", "dexpreopt.config")
 
 	danglingRules := make(map[string]bool)
 
@@ -81,7 +87,8 @@
 		if strings.HasPrefix(line, bootstrapDir) ||
 			strings.HasPrefix(line, miniBootstrapDir) ||
 			strings.HasPrefix(line, modulePathsDir) ||
-			line == variablesFilePath {
+			line == variablesFilePath ||
+			line == dexpreoptConfigFilePath {
 			// Leaf node is in one of Soong's bootstrap directories, which do not have
 			// full build rules in the primary build.ninja file.
 			continue
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 7a3726b..9434924 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -18,6 +18,7 @@
 	"io/ioutil"
 	"os"
 	"runtime"
+	"strings"
 	"time"
 
 	"github.com/golang/protobuf/proto"
@@ -142,6 +143,10 @@
 	m.metrics.BuildDateTimestamp = proto.Int64(buildTimestamp.UnixNano() / int64(time.Second))
 }
 
+func (m *Metrics) SetBuildCommand(cmd []string) {
+	m.metrics.BuildCommand = proto.String(strings.Join(cmd, " "))
+}
+
 // exports the output to the file at outputPath
 func (m *Metrics) Dump(outputPath string) error {
 	// ignore the error if the hostname could not be retrieved as it
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index b8c5775..5db5ae5 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -203,10 +203,12 @@
 	// The hostname of the machine.
 	Hostname *string `protobuf:"bytes,24,opt,name=hostname" json:"hostname,omitempty"`
 	// The system resource information such as total physical memory.
-	SystemResourceInfo   *SystemResourceInfo `protobuf:"bytes,25,opt,name=system_resource_info,json=systemResourceInfo" json:"system_resource_info,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}            `json:"-"`
-	XXX_unrecognized     []byte              `json:"-"`
-	XXX_sizecache        int32               `json:"-"`
+	SystemResourceInfo *SystemResourceInfo `protobuf:"bytes,25,opt,name=system_resource_info,json=systemResourceInfo" json:"system_resource_info,omitempty"`
+	// The build command that the user entered to the build system.
+	BuildCommand         *string  `protobuf:"bytes,26,opt,name=build_command,json=buildCommand" json:"build_command,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
 }
 
 func (m *MetricsBase) Reset()         { *m = MetricsBase{} }
@@ -414,6 +416,13 @@
 	return nil
 }
 
+func (m *MetricsBase) GetBuildCommand() string {
+	if m != nil && m.BuildCommand != nil {
+		return *m.BuildCommand
+	}
+	return ""
+}
+
 type BuildConfig struct {
 	UseGoma              *bool    `protobuf:"varint,1,opt,name=use_goma,json=useGoma" json:"use_goma,omitempty"`
 	UseRbe               *bool    `protobuf:"varint,2,opt,name=use_rbe,json=useRbe" json:"use_rbe,omitempty"`
@@ -972,90 +981,91 @@
 }
 
 var fileDescriptor_6039342a2ba47b72 = []byte{
-	// 1347 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x6f, 0x4f, 0x1b, 0xc7,
-	0x13, 0x8e, 0xc1, 0x60, 0x7b, 0x8c, 0x8d, 0x59, 0xe0, 0xc7, 0x85, 0x24, 0xbf, 0x52, 0xab, 0x49,
-	0x51, 0xd5, 0x90, 0x88, 0x46, 0x28, 0x42, 0x51, 0x55, 0x70, 0x68, 0x9a, 0x22, 0x30, 0x5a, 0x42,
-	0x9a, 0xb6, 0x2f, 0xb6, 0xeb, 0xf3, 0x1a, 0x2e, 0xb9, 0xbb, 0x3d, 0xed, 0xee, 0x51, 0x9c, 0x6f,
-	0xd6, 0xd7, 0xfd, 0x2c, 0xfd, 0x04, 0x55, 0xdf, 0x57, 0x3b, 0x7b, 0x77, 0x1c, 0xc4, 0x4d, 0xa2,
-	0xbc, 0xbb, 0x9b, 0xe7, 0x79, 0x66, 0x67, 0xe7, 0xe6, 0x8f, 0x0d, 0xad, 0x48, 0x18, 0x15, 0xf8,
-	0x7a, 0x23, 0x51, 0xd2, 0x48, 0xb2, 0xa8, 0xa5, 0x8c, 0x4f, 0xd9, 0x20, 0x0d, 0xc2, 0x21, 0xcb,
-	0xa0, 0xee, 0x3f, 0x4d, 0x68, 0x1e, 0xb8, 0xe7, 0x5d, 0xae, 0x05, 0x79, 0x08, 0x4b, 0x8e, 0x30,
-	0xe4, 0x46, 0x30, 0x13, 0x44, 0x42, 0x1b, 0x1e, 0x25, 0x5e, 0x65, 0xad, 0xb2, 0x3e, 0x4d, 0x09,
-	0x62, 0x4f, 0xb9, 0x11, 0x2f, 0x72, 0x84, 0xdc, 0x84, 0xba, 0x53, 0x04, 0x43, 0x6f, 0x6a, 0xad,
-	0xb2, 0xde, 0xa0, 0x35, 0x7c, 0x7f, 0x3e, 0x24, 0xdb, 0x70, 0x33, 0x09, 0xb9, 0x19, 0x49, 0x15,
-	0xb1, 0x73, 0xa1, 0x74, 0x20, 0x63, 0xe6, 0xcb, 0xa1, 0x88, 0x79, 0x24, 0xbc, 0x69, 0xe4, 0xae,
-	0xe4, 0x84, 0x97, 0x0e, 0xef, 0x65, 0x30, 0xb9, 0x0b, 0x6d, 0xc3, 0xd5, 0xa9, 0x30, 0x2c, 0x51,
-	0x72, 0x98, 0xfa, 0xc6, 0xab, 0xa2, 0xa0, 0xe5, 0xac, 0x47, 0xce, 0x48, 0x86, 0xb0, 0x94, 0xd1,
-	0x5c, 0x10, 0xe7, 0x5c, 0x05, 0x3c, 0x36, 0xde, 0xcc, 0x5a, 0x65, 0xbd, 0xbd, 0x79, 0x7f, 0x63,
-	0xc2, 0x9d, 0x37, 0x4a, 0xf7, 0xdd, 0xd8, 0xb5, 0xc8, 0x4b, 0x27, 0xda, 0x9e, 0xde, 0x3b, 0x7c,
-	0x46, 0x89, 0xf3, 0x57, 0x06, 0x48, 0x1f, 0x9a, 0xd9, 0x29, 0x5c, 0xf9, 0x67, 0xde, 0x2c, 0x3a,
-	0xbf, 0xfb, 0x41, 0xe7, 0x3b, 0xca, 0x3f, 0xdb, 0xae, 0x9d, 0x1c, 0xee, 0x1f, 0xf6, 0x7f, 0x3a,
-	0xa4, 0xe0, 0x5c, 0x58, 0x23, 0xd9, 0x80, 0xc5, 0x92, 0xc3, 0x22, 0xea, 0x1a, 0x5e, 0x71, 0xe1,
-	0x92, 0x98, 0x07, 0xf0, 0x35, 0x64, 0x61, 0x31, 0x3f, 0x49, 0x0b, 0x7a, 0x1d, 0xe9, 0x1d, 0x87,
-	0xf4, 0x92, 0x34, 0x67, 0xef, 0x43, 0xe3, 0x4c, 0xea, 0x2c, 0xd8, 0xc6, 0x27, 0x05, 0x5b, 0xb7,
-	0x0e, 0x30, 0x54, 0x0a, 0x2d, 0x74, 0xb6, 0x19, 0x0f, 0x9d, 0x43, 0xf8, 0x24, 0x87, 0x4d, 0xeb,
-	0x64, 0x33, 0x1e, 0xa2, 0xcf, 0x15, 0xa8, 0xa1, 0x4f, 0xa9, 0xbd, 0x26, 0xde, 0x61, 0xd6, 0xbe,
-	0xf6, 0x35, 0xe9, 0x66, 0x87, 0x49, 0xcd, 0xc4, 0x85, 0x51, 0xdc, 0x9b, 0x43, 0xb8, 0xe9, 0xe0,
-	0x3d, 0x6b, 0x2a, 0x38, 0xbe, 0x92, 0x5a, 0x5b, 0x17, 0xad, 0x4b, 0x4e, 0xcf, 0xda, 0xfa, 0x9a,
-	0xdc, 0x83, 0xf9, 0x12, 0x07, 0xc3, 0x6e, 0xbb, 0xf2, 0x29, 0x58, 0x18, 0xc8, 0x7d, 0x58, 0x2c,
-	0xf1, 0x8a, 0x2b, 0xce, 0xbb, 0xc4, 0x16, 0xdc, 0x52, 0xdc, 0x32, 0x35, 0x6c, 0x18, 0x28, 0xaf,
-	0xe3, 0xe2, 0x96, 0xa9, 0x79, 0x1a, 0x28, 0xf2, 0x2d, 0x34, 0xb5, 0x30, 0x69, 0xc2, 0x8c, 0x94,
-	0xa1, 0xf6, 0x16, 0xd6, 0xa6, 0xd7, 0x9b, 0x9b, 0x77, 0x26, 0xa6, 0xe8, 0x48, 0xa8, 0xd1, 0xf3,
-	0x78, 0x24, 0x29, 0xa0, 0xe2, 0x85, 0x15, 0x90, 0x6d, 0x68, 0xbc, 0xe1, 0x26, 0x60, 0x2a, 0x8d,
-	0xb5, 0x47, 0x3e, 0x46, 0x5d, 0xb7, 0x7c, 0x9a, 0xc6, 0x9a, 0x3c, 0x01, 0x70, 0x4c, 0x14, 0x2f,
-	0x7e, 0x8c, 0xb8, 0x81, 0x68, 0xae, 0x8e, 0x83, 0xf8, 0x35, 0x77, 0xea, 0xa5, 0x8f, 0x52, 0xa3,
-	0x00, 0xd5, 0xdf, 0xc0, 0x8c, 0x91, 0x86, 0x87, 0xde, 0xf2, 0x5a, 0xe5, 0xc3, 0x42, 0xc7, 0x25,
-	0x2f, 0x61, 0xd2, 0x28, 0xf2, 0xfe, 0x87, 0x2e, 0xee, 0x4d, 0x74, 0x71, 0x6c, 0x6d, 0xd8, 0x92,
-	0x59, 0x85, 0xd1, 0x05, 0x7d, 0xdd, 0x44, 0x7a, 0x30, 0xe7, 0x54, 0xbe, 0x8c, 0x47, 0xc1, 0xa9,
-	0xb7, 0x82, 0x0e, 0xd7, 0x26, 0x3a, 0x44, 0x61, 0x0f, 0x79, 0xb4, 0x39, 0xb8, 0x7c, 0x21, 0xab,
-	0x80, 0xa5, 0x8f, 0x23, 0xca, 0xc3, 0x6f, 0x5c, 0xbc, 0x93, 0x9f, 0x61, 0x49, 0x8f, 0xb5, 0x11,
-	0x11, 0x53, 0x42, 0xcb, 0x54, 0xf9, 0x82, 0x05, 0xf1, 0x48, 0x7a, 0x37, 0xf1, 0xa0, 0x2f, 0x27,
-	0x47, 0x8e, 0x02, 0x9a, 0xf1, 0x31, 0x0d, 0x44, 0xbf, 0x63, 0xeb, 0x3e, 0x84, 0xb9, 0x2b, 0x13,
-	0xa7, 0x0e, 0xd5, 0x93, 0xe3, 0x3d, 0xda, 0xb9, 0x41, 0x5a, 0xd0, 0xb0, 0x4f, 0x4f, 0xf7, 0x76,
-	0x4f, 0x9e, 0x75, 0x2a, 0xa4, 0x06, 0x76, 0x4a, 0x75, 0xa6, 0xba, 0x4f, 0xa0, 0x8a, 0x35, 0xd9,
-	0x84, 0xbc, 0xc7, 0x3a, 0x37, 0x2c, 0xba, 0x43, 0x0f, 0x3a, 0x15, 0xd2, 0x80, 0x99, 0x1d, 0x7a,
-	0xb0, 0xf5, 0xa8, 0x33, 0x65, 0x6d, 0xaf, 0x1e, 0x6f, 0x75, 0xa6, 0x09, 0xc0, 0xec, 0xab, 0xc7,
-	0x5b, 0x6c, 0xeb, 0x51, 0xa7, 0xda, 0x3d, 0x85, 0x66, 0x29, 0x05, 0x76, 0x88, 0xa7, 0x5a, 0xb0,
-	0x53, 0x19, 0x71, 0x1c, 0xf5, 0x75, 0x5a, 0x4b, 0xb5, 0x78, 0x26, 0x23, 0x6e, 0x6b, 0xde, 0x42,
-	0x6a, 0x20, 0x70, 0xbc, 0xd7, 0xe9, 0x6c, 0xaa, 0x05, 0x1d, 0x08, 0xf2, 0x05, 0xb4, 0x47, 0xd2,
-	0xe6, 0xa0, 0x50, 0x4e, 0x23, 0x3e, 0x87, 0xd6, 0x13, 0x27, 0xef, 0x4a, 0x20, 0xef, 0xa6, 0x80,
-	0x6c, 0xc2, 0x32, 0xd6, 0x02, 0x4b, 0xce, 0xc6, 0x3a, 0xf0, 0x79, 0xc8, 0x22, 0x11, 0x49, 0x35,
-	0xc6, 0xc3, 0xab, 0x74, 0x11, 0xc1, 0xa3, 0x0c, 0x3b, 0x40, 0xc8, 0x6e, 0x04, 0x7e, 0xce, 0x83,
-	0x90, 0x0f, 0x42, 0x61, 0xc7, 0xa0, 0xc6, 0x78, 0x66, 0x68, 0xab, 0xb0, 0xf6, 0x92, 0x54, 0x77,
-	0xff, 0xae, 0x40, 0x3d, 0xaf, 0x38, 0x42, 0xa0, 0x3a, 0x14, 0xda, 0x47, 0xb7, 0x0d, 0x8a, 0xcf,
-	0xd6, 0x86, 0x5f, 0xd7, 0x2d, 0x2b, 0x7c, 0x26, 0x77, 0x00, 0xb4, 0xe1, 0xca, 0xe0, 0xc6, 0xc3,
-	0x7b, 0x54, 0x69, 0x03, 0x2d, 0x76, 0xd1, 0x91, 0x5b, 0xd0, 0x50, 0x82, 0x87, 0x0e, 0xad, 0x22,
-	0x5a, 0xb7, 0x06, 0x04, 0x3f, 0x07, 0x70, 0xc1, 0xdb, 0x44, 0xe0, 0xe2, 0xa9, 0xee, 0x4e, 0x79,
-	0x15, 0xda, 0x70, 0xd6, 0x13, 0x2d, 0xc8, 0x6f, 0xb0, 0x92, 0x28, 0xe9, 0x0b, 0xad, 0x85, 0xbe,
-	0x56, 0x3b, 0xb3, 0xd8, 0x71, 0xeb, 0x93, 0x1b, 0xc7, 0x69, 0xae, 0x14, 0xcf, 0x72, 0xe1, 0xe8,
-	0x4a, 0xfd, 0xfc, 0x31, 0x0d, 0x8b, 0x13, 0xe8, 0xc5, 0x65, 0x2b, 0xa5, 0xcb, 0xae, 0x43, 0x27,
-	0xd5, 0x42, 0xe1, 0x6d, 0x58, 0x14, 0xd8, 0xd9, 0x87, 0xc9, 0xa8, 0xd2, 0xb6, 0xb5, 0xdb, 0x4b,
-	0x1d, 0xa0, 0xd5, 0xae, 0x9d, 0xac, 0xe0, 0xcb, 0x5c, 0x97, 0x9e, 0x8e, 0x43, 0x4a, 0xec, 0xdb,
-	0x00, 0x11, 0xbf, 0x60, 0x4a, 0x6b, 0xf6, 0x66, 0x90, 0xa7, 0x29, 0xe2, 0x17, 0x54, 0xeb, 0xfd,
-	0x01, 0xf9, 0x0a, 0x16, 0xa2, 0x20, 0x96, 0x8a, 0x25, 0xfc, 0x54, 0xb0, 0x11, 0x4f, 0x43, 0xa3,
-	0x5d, 0xb6, 0xe8, 0x3c, 0x02, 0x47, 0xfc, 0x54, 0x7c, 0x8f, 0x66, 0xe4, 0xf2, 0xd7, 0xd7, 0xb8,
-	0xb3, 0x19, 0xd7, 0x02, 0x25, 0xee, 0xff, 0xa1, 0x19, 0x48, 0x16, 0xc4, 0x49, 0x6a, 0xec, 0xb1,
-	0x35, 0xf7, 0xed, 0x02, 0xf9, 0xdc, 0x5a, 0xf6, 0x07, 0x64, 0x0d, 0xe6, 0x02, 0xc9, 0x64, 0x6a,
-	0x32, 0x42, 0x1d, 0x09, 0x10, 0xc8, 0x3e, 0x9a, 0xf6, 0x07, 0xe4, 0x09, 0xac, 0x9e, 0xcb, 0x30,
-	0x8d, 0x0d, 0x57, 0x63, 0x3b, 0x3b, 0x8c, 0xb8, 0x30, 0x4c, 0xff, 0x1e, 0x18, 0xff, 0x4c, 0x68,
-	0xdc, 0x9f, 0x55, 0xea, 0x15, 0x8c, 0x9e, 0x23, 0x1c, 0x67, 0x38, 0xf9, 0x0e, 0x6e, 0x07, 0xf1,
-	0x7b, 0xf4, 0x80, 0xfa, 0xd5, 0x12, 0xe7, 0x9a, 0x87, 0xee, 0x5f, 0x15, 0x68, 0x1f, 0xc8, 0x61,
-	0x1a, 0x8a, 0x17, 0xe3, 0xc4, 0x7d, 0xb6, 0x5f, 0xf3, 0x51, 0xe6, 0x92, 0x8c, 0x9f, 0xaf, 0xbd,
-	0xf9, 0x60, 0xf2, 0xce, 0xbd, 0x22, 0x75, 0x93, 0xcd, 0xb5, 0x5c, 0x69, 0xfb, 0x0e, 0x2e, 0xad,
-	0xe4, 0x33, 0x68, 0x46, 0xa8, 0x61, 0x66, 0x9c, 0xe4, 0x7d, 0x00, 0x51, 0xe1, 0xc6, 0x76, 0x76,
-	0x9c, 0x46, 0x4c, 0x8e, 0x98, 0x33, 0xba, 0x4f, 0xde, 0xa2, 0x73, 0x71, 0x1a, 0xf5, 0x47, 0xee,
-	0x3c, 0xdd, 0x7d, 0x90, 0x8d, 0x90, 0xcc, 0xeb, 0x95, 0x39, 0xd4, 0x80, 0x99, 0xe3, 0x7e, 0xff,
-	0xd0, 0x0e, 0xac, 0x3a, 0x54, 0x0f, 0x76, 0xf6, 0xf7, 0x3a, 0x53, 0xdd, 0x10, 0x56, 0x7b, 0x2a,
-	0x30, 0xb6, 0xa5, 0x4f, 0xb4, 0x50, 0x3f, 0xca, 0x54, 0xc5, 0x62, 0x9c, 0x4f, 0xef, 0x49, 0x95,
-	0xba, 0x0d, 0xb5, 0x7c, 0x3b, 0x4c, 0xbd, 0x67, 0x98, 0x97, 0x7e, 0x75, 0xd0, 0x5c, 0xd0, 0x1d,
-	0xc0, 0xad, 0x09, 0xa7, 0xe9, 0xcb, 0x65, 0x51, 0xf5, 0xd3, 0xd7, 0xda, 0xab, 0x60, 0xff, 0x4d,
-	0xce, 0xec, 0x7f, 0x47, 0x4b, 0x51, 0xdc, 0xfd, 0xb3, 0x02, 0x0b, 0xef, 0xac, 0x26, 0xe2, 0x41,
-	0x2d, 0xcf, 0x5b, 0x05, 0xf3, 0x96, 0xbf, 0xda, 0xe5, 0x92, 0xfd, 0x76, 0x73, 0x17, 0x6a, 0xd1,
-	0xe2, 0xdd, 0xd6, 0xbc, 0x1b, 0x89, 0x3c, 0x0c, 0xa5, 0xcf, 0x7c, 0x99, 0xc6, 0x26, 0x6b, 0xb5,
-	0x79, 0x04, 0x76, 0xac, 0xbd, 0x67, 0xcd, 0xb6, 0x83, 0xcb, 0x5c, 0x1d, 0xbc, 0xcd, 0xc7, 0x52,
-	0xfb, 0x92, 0x7a, 0x1c, 0xbc, 0x15, 0xf6, 0xc7, 0x92, 0xed, 0xc9, 0x33, 0xc1, 0x13, 0x47, 0x73,
-	0x1d, 0xd7, 0x8c, 0xf8, 0xc5, 0x0f, 0x82, 0x27, 0x96, 0xb3, 0xbb, 0xfc, 0x4b, 0xb6, 0x8f, 0xb3,
-	0x7b, 0x33, 0xfc, 0xbf, 0xf0, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfd, 0xc2, 0xdd, 0xb5, 0x3f,
-	0x0c, 0x00, 0x00,
+	// 1366 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xdd, 0x52, 0x1b, 0xc7,
+	0x12, 0xb6, 0x40, 0x20, 0xa9, 0xf5, 0x83, 0x18, 0xe0, 0xb0, 0xc6, 0xf6, 0x39, 0x1c, 0x25, 0x76,
+	0xa8, 0x54, 0x8c, 0x5d, 0xc4, 0x45, 0xb9, 0x28, 0x57, 0x2a, 0x20, 0x13, 0xc7, 0xa1, 0x40, 0xd4,
+	0x60, 0x1c, 0x27, 0xb9, 0x98, 0x8c, 0x56, 0x23, 0x58, 0x7b, 0x77, 0x67, 0x6b, 0x66, 0x96, 0x80,
+	0x1f, 0x25, 0x6f, 0x92, 0xeb, 0x3c, 0x4b, 0x9e, 0x20, 0x2f, 0x90, 0x9a, 0x9e, 0xdd, 0x65, 0xc1,
+	0x8a, 0xed, 0xf2, 0xdd, 0xee, 0xd7, 0xdf, 0xd7, 0xd3, 0xdd, 0x3b, 0xdd, 0x2d, 0x41, 0x3b, 0x12,
+	0x46, 0x05, 0xbe, 0x5e, 0x4f, 0x94, 0x34, 0x92, 0x2c, 0x68, 0x29, 0xe3, 0x13, 0x36, 0x4c, 0x83,
+	0x70, 0xc4, 0x32, 0x53, 0xef, 0xf7, 0x16, 0x34, 0xf7, 0xdd, 0xf3, 0x0e, 0xd7, 0x82, 0x3c, 0x84,
+	0x45, 0x47, 0x18, 0x71, 0x23, 0x98, 0x09, 0x22, 0xa1, 0x0d, 0x8f, 0x12, 0xaf, 0xb2, 0x5a, 0x59,
+	0x9b, 0xa6, 0x04, 0x6d, 0x4f, 0xb9, 0x11, 0x2f, 0x72, 0x0b, 0xb9, 0x09, 0x75, 0xa7, 0x08, 0x46,
+	0xde, 0xd4, 0x6a, 0x65, 0xad, 0x41, 0x6b, 0xf8, 0xfe, 0x7c, 0x44, 0xb6, 0xe0, 0x66, 0x12, 0x72,
+	0x33, 0x96, 0x2a, 0x62, 0x67, 0x42, 0xe9, 0x40, 0xc6, 0xcc, 0x97, 0x23, 0x11, 0xf3, 0x48, 0x78,
+	0xd3, 0xc8, 0x5d, 0xce, 0x09, 0x2f, 0x9d, 0xbd, 0x9f, 0x99, 0xc9, 0x5d, 0xe8, 0x18, 0xae, 0x4e,
+	0x84, 0x61, 0x89, 0x92, 0xa3, 0xd4, 0x37, 0x5e, 0x15, 0x05, 0x6d, 0x87, 0x1e, 0x3a, 0x90, 0x8c,
+	0x60, 0x31, 0xa3, 0xb9, 0x20, 0xce, 0xb8, 0x0a, 0x78, 0x6c, 0xbc, 0x99, 0xd5, 0xca, 0x5a, 0x67,
+	0xe3, 0xfe, 0xfa, 0x84, 0x9c, 0xd7, 0x4b, 0xf9, 0xae, 0xef, 0x58, 0xcb, 0x4b, 0x27, 0xda, 0x9a,
+	0xde, 0x3d, 0x78, 0x46, 0x89, 0xf3, 0x57, 0x36, 0x90, 0x01, 0x34, 0xb3, 0x53, 0xb8, 0xf2, 0x4f,
+	0xbd, 0x59, 0x74, 0x7e, 0xf7, 0x83, 0xce, 0xb7, 0x95, 0x7f, 0xba, 0x55, 0x3b, 0x3e, 0xd8, 0x3b,
+	0x18, 0xfc, 0x78, 0x40, 0xc1, 0xb9, 0xb0, 0x20, 0x59, 0x87, 0x85, 0x92, 0xc3, 0x22, 0xea, 0x1a,
+	0xa6, 0x38, 0x7f, 0x49, 0xcc, 0x03, 0xf8, 0x0a, 0xb2, 0xb0, 0x98, 0x9f, 0xa4, 0x05, 0xbd, 0x8e,
+	0xf4, 0xae, 0xb3, 0xf4, 0x93, 0x34, 0x67, 0xef, 0x41, 0xe3, 0x54, 0xea, 0x2c, 0xd8, 0xc6, 0x27,
+	0x05, 0x5b, 0xb7, 0x0e, 0x30, 0x54, 0x0a, 0x6d, 0x74, 0xb6, 0x11, 0x8f, 0x9c, 0x43, 0xf8, 0x24,
+	0x87, 0x4d, 0xeb, 0x64, 0x23, 0x1e, 0xa1, 0xcf, 0x65, 0xa8, 0xa1, 0x4f, 0xa9, 0xbd, 0x26, 0xe6,
+	0x30, 0x6b, 0x5f, 0x07, 0x9a, 0xf4, 0xb2, 0xc3, 0xa4, 0x66, 0xe2, 0xdc, 0x28, 0xee, 0xb5, 0xd0,
+	0xdc, 0x74, 0xe6, 0x5d, 0x0b, 0x15, 0x1c, 0x5f, 0x49, 0xad, 0xad, 0x8b, 0xf6, 0x25, 0xa7, 0x6f,
+	0xb1, 0x81, 0x26, 0xf7, 0x60, 0xae, 0xc4, 0xc1, 0xb0, 0x3b, 0xee, 0xfa, 0x14, 0x2c, 0x0c, 0xe4,
+	0x3e, 0x2c, 0x94, 0x78, 0x45, 0x8a, 0x73, 0xae, 0xb0, 0x05, 0xb7, 0x14, 0xb7, 0x4c, 0x0d, 0x1b,
+	0x05, 0xca, 0xeb, 0xba, 0xb8, 0x65, 0x6a, 0x9e, 0x06, 0x8a, 0x7c, 0x03, 0x4d, 0x2d, 0x4c, 0x9a,
+	0x30, 0x23, 0x65, 0xa8, 0xbd, 0xf9, 0xd5, 0xe9, 0xb5, 0xe6, 0xc6, 0x9d, 0x89, 0x25, 0x3a, 0x14,
+	0x6a, 0xfc, 0x3c, 0x1e, 0x4b, 0x0a, 0xa8, 0x78, 0x61, 0x05, 0x64, 0x0b, 0x1a, 0x6f, 0xb8, 0x09,
+	0x98, 0x4a, 0x63, 0xed, 0x91, 0x8f, 0x51, 0xd7, 0x2d, 0x9f, 0xa6, 0xb1, 0x26, 0x4f, 0x00, 0x1c,
+	0x13, 0xc5, 0x0b, 0x1f, 0x23, 0x6e, 0xa0, 0x35, 0x57, 0xc7, 0x41, 0xfc, 0x9a, 0x3b, 0xf5, 0xe2,
+	0x47, 0xa9, 0x51, 0x80, 0xea, 0xaf, 0x61, 0xc6, 0x48, 0xc3, 0x43, 0x6f, 0x69, 0xb5, 0xf2, 0x61,
+	0xa1, 0xe3, 0x92, 0x97, 0x30, 0x69, 0x14, 0x79, 0xff, 0x41, 0x17, 0xf7, 0x26, 0xba, 0x38, 0xb2,
+	0x18, 0xb6, 0x64, 0x76, 0xc3, 0xe8, 0xbc, 0xbe, 0x0e, 0x91, 0x3e, 0xb4, 0x9c, 0xca, 0x97, 0xf1,
+	0x38, 0x38, 0xf1, 0x96, 0xd1, 0xe1, 0xea, 0x44, 0x87, 0x28, 0xec, 0x23, 0x8f, 0x36, 0x87, 0x97,
+	0x2f, 0x64, 0x05, 0xf0, 0xea, 0xe3, 0x88, 0xf2, 0xf0, 0x1b, 0x17, 0xef, 0xe4, 0x27, 0x58, 0xd4,
+	0x17, 0xda, 0x88, 0x88, 0x29, 0xa1, 0x65, 0xaa, 0x7c, 0xc1, 0x82, 0x78, 0x2c, 0xbd, 0x9b, 0x78,
+	0xd0, 0x17, 0x93, 0x23, 0x47, 0x01, 0xcd, 0xf8, 0x58, 0x06, 0xa2, 0xdf, 0xc1, 0xc8, 0x67, 0xd0,
+	0xce, 0x63, 0x8f, 0x22, 0x1e, 0x8f, 0xbc, 0x15, 0x3c, 0xbb, 0x95, 0x85, 0x86, 0x58, 0xef, 0x21,
+	0xb4, 0xae, 0x8c, 0xa5, 0x3a, 0x54, 0x8f, 0x8f, 0x76, 0x69, 0xf7, 0x06, 0x69, 0x43, 0xc3, 0x3e,
+	0x3d, 0xdd, 0xdd, 0x39, 0x7e, 0xd6, 0xad, 0x90, 0x1a, 0xd8, 0x51, 0xd6, 0x9d, 0xea, 0x3d, 0x81,
+	0x2a, 0x5e, 0xdc, 0x26, 0xe4, 0x8d, 0xd8, 0xbd, 0x61, 0xad, 0xdb, 0x74, 0xbf, 0x5b, 0x21, 0x0d,
+	0x98, 0xd9, 0xa6, 0xfb, 0x9b, 0x8f, 0xba, 0x53, 0x16, 0x7b, 0xf5, 0x78, 0xb3, 0x3b, 0x4d, 0x00,
+	0x66, 0x5f, 0x3d, 0xde, 0x64, 0x9b, 0x8f, 0xba, 0xd5, 0xde, 0x09, 0x34, 0x4b, 0x75, 0xb2, 0x93,
+	0x3e, 0xd5, 0x82, 0x9d, 0xc8, 0x88, 0xe3, 0x3e, 0xa8, 0xd3, 0x5a, 0xaa, 0xc5, 0x33, 0x19, 0x71,
+	0xdb, 0x18, 0xd6, 0xa4, 0x86, 0x02, 0x77, 0x40, 0x9d, 0xce, 0xa6, 0x5a, 0xd0, 0xa1, 0x20, 0x9f,
+	0x43, 0x67, 0x2c, 0x6d, 0xa1, 0x0a, 0xe5, 0x34, 0xda, 0x5b, 0x88, 0x1e, 0x3b, 0x79, 0x4f, 0x02,
+	0x79, 0xb7, 0x4e, 0x64, 0x03, 0x96, 0xf0, 0xc2, 0xb0, 0xe4, 0xf4, 0x42, 0x07, 0x3e, 0x0f, 0x59,
+	0x24, 0x22, 0xa9, 0x2e, 0xf0, 0xf0, 0x2a, 0x5d, 0x40, 0xe3, 0x61, 0x66, 0xdb, 0x47, 0x93, 0x5d,
+	0x1b, 0xfc, 0x8c, 0x07, 0x21, 0x1f, 0x86, 0xc2, 0xce, 0x4a, 0x8d, 0xf1, 0xcc, 0xd0, 0x76, 0x81,
+	0xf6, 0x93, 0x54, 0xf7, 0xfe, 0xae, 0x40, 0x3d, 0xbf, 0x96, 0x84, 0x40, 0x75, 0x24, 0xb4, 0x8f,
+	0x6e, 0x1b, 0x14, 0x9f, 0x2d, 0x86, 0x57, 0xc0, 0x6d, 0x34, 0x7c, 0x26, 0x77, 0x00, 0xb4, 0xe1,
+	0xca, 0xe0, 0x5a, 0xc4, 0x3c, 0xaa, 0xb4, 0x81, 0x88, 0xdd, 0x86, 0xe4, 0x16, 0x34, 0x94, 0xe0,
+	0xa1, 0xb3, 0x56, 0xd1, 0x5a, 0xb7, 0x00, 0x1a, 0xff, 0x0f, 0xe0, 0x82, 0xb7, 0x85, 0xc0, 0xed,
+	0x54, 0xdd, 0x99, 0xf2, 0x2a, 0xb4, 0xe1, 0xd0, 0x63, 0x2d, 0xc8, 0xaf, 0xb0, 0x9c, 0x28, 0xe9,
+	0x0b, 0xad, 0x85, 0xbe, 0x76, 0xc1, 0x66, 0xb1, 0x2d, 0xd7, 0x26, 0x77, 0x97, 0xd3, 0x5c, 0xb9,
+	0x61, 0x4b, 0x85, 0xa3, 0x32, 0xdc, 0xfb, 0x63, 0x1a, 0x16, 0x26, 0xd0, 0x8b, 0x64, 0x2b, 0xa5,
+	0x64, 0xd7, 0xa0, 0x9b, 0x6a, 0xa1, 0x30, 0x1b, 0x16, 0x05, 0x76, 0x40, 0x62, 0x31, 0xaa, 0xb4,
+	0x63, 0x71, 0x9b, 0xd4, 0x3e, 0xa2, 0x76, 0x37, 0x65, 0x5d, 0x51, 0xe6, 0xba, 0xf2, 0x74, 0x9d,
+	0xa5, 0xc4, 0xbe, 0x0d, 0x10, 0xf1, 0x73, 0xa6, 0xb4, 0x66, 0x6f, 0x86, 0x79, 0x99, 0x22, 0x7e,
+	0x4e, 0xb5, 0xde, 0x1b, 0x92, 0x2f, 0x61, 0x3e, 0x0a, 0x62, 0xa9, 0x58, 0xc2, 0x4f, 0x04, 0x1b,
+	0xf3, 0x34, 0x34, 0xda, 0x55, 0x8b, 0xce, 0xa1, 0xe1, 0x90, 0x9f, 0x88, 0xef, 0x10, 0x46, 0x2e,
+	0x7f, 0x7d, 0x8d, 0x3b, 0x9b, 0x71, 0xad, 0xa1, 0xc4, 0xfd, 0x2f, 0x34, 0x03, 0xc9, 0x82, 0x38,
+	0x49, 0x8d, 0x3d, 0xb6, 0xe6, 0xbe, 0x5d, 0x20, 0x9f, 0x5b, 0x64, 0x6f, 0x48, 0x56, 0xa1, 0x15,
+	0x48, 0x26, 0x53, 0x93, 0x11, 0xea, 0x48, 0x80, 0x40, 0x0e, 0x10, 0xda, 0x1b, 0x92, 0x27, 0xb0,
+	0x72, 0x26, 0xc3, 0x34, 0x36, 0x5c, 0x5d, 0xd8, 0x01, 0x63, 0xc4, 0xb9, 0x61, 0xfa, 0xb7, 0xc0,
+	0xf8, 0xa7, 0x42, 0xe3, 0x92, 0xad, 0x52, 0xaf, 0x60, 0xf4, 0x1d, 0xe1, 0x28, 0xb3, 0x93, 0x6f,
+	0xe1, 0x76, 0x10, 0xbf, 0x47, 0x0f, 0xa8, 0x5f, 0x29, 0x71, 0xae, 0x79, 0xe8, 0xfd, 0x55, 0x81,
+	0xce, 0xbe, 0x1c, 0xa5, 0xa1, 0x78, 0x71, 0x91, 0xb8, 0xcf, 0xf6, 0x4b, 0x3e, 0xef, 0x5c, 0x91,
+	0xf1, 0xf3, 0x75, 0x36, 0x1e, 0x4c, 0x5e, 0xcc, 0x57, 0xa4, 0x6e, 0xfc, 0xb9, 0x96, 0x2b, 0xad,
+	0xe8, 0xe1, 0x25, 0x4a, 0xfe, 0x07, 0xcd, 0x08, 0x35, 0xcc, 0x5c, 0x24, 0x79, 0x1f, 0x40, 0x54,
+	0xb8, 0xb1, 0x9d, 0x1d, 0xa7, 0x11, 0x93, 0x63, 0xe6, 0x40, 0xf7, 0xc9, 0xdb, 0xb4, 0x15, 0xa7,
+	0xd1, 0x60, 0xec, 0xce, 0xd3, 0xbd, 0x07, 0xd9, 0x08, 0xc9, 0xbc, 0x5e, 0x99, 0x43, 0x0d, 0x98,
+	0x39, 0x1a, 0x0c, 0x0e, 0xec, 0xc0, 0xaa, 0x43, 0x75, 0x7f, 0x7b, 0x6f, 0xb7, 0x3b, 0xd5, 0x0b,
+	0x61, 0xa5, 0xaf, 0x02, 0x63, 0x5b, 0xfa, 0x58, 0x0b, 0xf5, 0x83, 0x4c, 0x55, 0x2c, 0x2e, 0xf2,
+	0x11, 0x3f, 0xe9, 0xa6, 0x6e, 0x41, 0x2d, 0x5f, 0x21, 0x53, 0xef, 0x99, 0xf8, 0xa5, 0x9f, 0x26,
+	0x34, 0x17, 0xf4, 0x86, 0x70, 0x6b, 0xc2, 0x69, 0xfa, 0x72, 0xa3, 0x54, 0xfd, 0xf4, 0xb5, 0xf6,
+	0x2a, 0xd8, 0x7f, 0x93, 0x2b, 0xfb, 0xef, 0xd1, 0x52, 0x14, 0xf7, 0xfe, 0xac, 0xc0, 0xfc, 0x3b,
+	0xfb, 0x8b, 0x78, 0x50, 0xcb, 0xeb, 0x56, 0xc1, 0xba, 0xe5, 0xaf, 0x76, 0x03, 0x65, 0x3f, 0xf0,
+	0x5c, 0x42, 0x6d, 0x5a, 0xbc, 0xdb, 0x3b, 0xef, 0x46, 0x22, 0x0f, 0x43, 0xe9, 0x33, 0x5f, 0xa6,
+	0xb1, 0xc9, 0x5a, 0x6d, 0x0e, 0x0d, 0xdb, 0x16, 0xef, 0x5b, 0xd8, 0x76, 0x70, 0x99, 0xab, 0x83,
+	0xb7, 0xf9, 0x58, 0xea, 0x5c, 0x52, 0x8f, 0x82, 0xb7, 0xc2, 0xfe, 0xa2, 0xb2, 0x3d, 0x79, 0x2a,
+	0x78, 0xe2, 0x68, 0xae, 0xe3, 0x9a, 0x11, 0x3f, 0xff, 0x5e, 0xf0, 0xc4, 0x72, 0x76, 0x96, 0x7e,
+	0xce, 0x96, 0x76, 0x96, 0x37, 0xc3, 0x3f, 0x15, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0xb9, 0x86,
+	0x34, 0xa9, 0x64, 0x0c, 0x00, 0x00,
 }
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 44f8859..8d3933f 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -102,6 +102,9 @@
 
   // The system resource information such as total physical memory.
   optional SystemResourceInfo system_resource_info = 25;
+
+  // The build command that the user entered to the build system.
+  optional string build_command = 26;
 }
 
 message BuildConfig {