Merge "Improve commenting for cc/builer.go, and kill dead code"
diff --git a/android/androidmk.go b/android/androidmk.go
index a670656..6ab4a24 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -12,6 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// This file offers AndroidMkEntriesProvider, which individual modules implement to output
+// Android.mk entries that contain information about the modules built through Soong. Kati reads
+// and combines them with the legacy Make-based module definitions to produce the complete view of
+// the source tree, which makes this a critical point of Make-Soong interoperability.
+//
+// Naturally, Soong-only builds do not rely on this mechanism.
+
 package android
 
 import (
@@ -36,8 +43,8 @@
 	ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
 }
 
-// Deprecated: consider using AndroidMkEntriesProvider instead, especially if you're not going to
-// use the Custom function.
+// Deprecated: Use AndroidMkEntriesProvider instead, especially if you're not going to use the
+// Custom function. It's easier to use and test.
 type AndroidMkDataProvider interface {
 	AndroidMk() AndroidMkData
 	BaseModuleName() string
@@ -63,37 +70,82 @@
 
 type AndroidMkExtraFunc func(w io.Writer, outputFile Path)
 
-// Allows modules to customize their Android*.mk output.
+// Interface for modules to declare their Android.mk outputs. Note that every module needs to
+// implement this in order to be included in the final Android-<product_name>.mk output, even if
+// they only need to output the common set of entries without any customizations.
 type AndroidMkEntriesProvider interface {
+	// Returns AndroidMkEntries objects that contain all basic info plus extra customization data
+	// if needed. This is the core func to implement.
+	// Note that one can return multiple objects. For example, java_library may return an additional
+	// AndroidMkEntries object for its hostdex sub-module.
 	AndroidMkEntries() []AndroidMkEntries
+	// Modules don't need to implement this as it's already implemented by ModuleBase.
+	// AndroidMkEntries uses BaseModuleName() instead of ModuleName() because certain modules
+	// e.g. Prebuilts, override the Name() func and return modified names.
+	// If a different name is preferred, use SubName or OverrideName in AndroidMkEntries.
 	BaseModuleName() string
 }
 
+// The core data struct that modules use to provide their Android.mk data.
 type AndroidMkEntries struct {
-	Class           string
-	SubName         string
-	OverrideName    string
-	DistFiles       TaggedDistFiles
-	OutputFile      OptionalPath
-	Disabled        bool
-	Include         string
-	Required        []string
-	Host_required   []string
+	// Android.mk class string, e.g EXECUTABLES, JAVA_LIBRARIES, ETC
+	Class string
+	// Optional suffix to append to the module name. Useful when a module wants to return multiple
+	// AndroidMkEntries objects. For example, when a java_library returns an additional entry for
+	// its hostdex sub-module, this SubName field is set to "-hostdex" so that it can have a
+	// different name than the parent's.
+	SubName string
+	// If set, this value overrides the base module name. SubName is still appended.
+	OverrideName string
+	// Dist files to output
+	DistFiles TaggedDistFiles
+	// The output file for Kati to process and/or install. If absent, the module is skipped.
+	OutputFile OptionalPath
+	// If true, the module is skipped and does not appear on the final Android-<product name>.mk
+	// file. Useful when a module needs to be skipped conditionally.
+	Disabled bool
+	// The postprocessing mk file to include, e.g. $(BUILD_SYSTEM)/soong_cc_prebuilt.mk
+	// If not set, $(BUILD_SYSTEM)/prebuilt.mk is used.
+	Include string
+	// Required modules that need to be built and included in the final build output when building
+	// this module.
+	Required []string
+	// Required host modules that need to be built and included in the final build output when
+	// building this module.
+	Host_required []string
+	// Required device modules that need to be built and included in the final build output when
+	// building this module.
 	Target_required []string
 
 	header bytes.Buffer
 	footer bytes.Buffer
 
+	// Funcs to append additional Android.mk entries or modify the common ones. Multiple funcs are
+	// accepted so that common logic can be factored out as a shared func.
 	ExtraEntries []AndroidMkExtraEntriesFunc
+	// Funcs to add extra lines to the module's Android.mk output. Unlike AndroidMkExtraEntriesFunc,
+	// which simply sets Make variable values, this can be used for anything since it can write any
+	// Make statements directly to the final Android-*.mk file.
+	// Primarily used to call macros or declare/update Make targets.
 	ExtraFooters []AndroidMkExtraFootersFunc
 
-	EntryMap   map[string][]string
+	// A map that holds the up-to-date Make variable values. Can be accessed from tests.
+	EntryMap map[string][]string
+	// A list of EntryMap keys in insertion order. This serves a few purposes:
+	// 1. Prevents churns. Golang map doesn't provide consistent iteration order, so without this,
+	// the outputted Android-*.mk file may change even though there have been no content changes.
+	// 2. Allows modules to refer to other variables, like LOCAL_BAR_VAR := $(LOCAL_FOO_VAR),
+	// without worrying about the variables being mixed up in the actual mk file.
+	// 3. Makes troubleshooting and spotting errors easier.
 	entryOrder []string
 }
 
 type AndroidMkExtraEntriesFunc func(entries *AndroidMkEntries)
 type AndroidMkExtraFootersFunc func(w io.Writer, name, prefix, moduleDir string, entries *AndroidMkEntries)
 
+// Utility funcs to manipulate Android.mk variable entries.
+
+// SetString sets a Make variable with the given name to the given value.
 func (a *AndroidMkEntries) SetString(name, value string) {
 	if _, ok := a.EntryMap[name]; !ok {
 		a.entryOrder = append(a.entryOrder, name)
@@ -101,6 +153,7 @@
 	a.EntryMap[name] = []string{value}
 }
 
+// SetPath sets a Make variable with the given name to the given path string.
 func (a *AndroidMkEntries) SetPath(name string, path Path) {
 	if _, ok := a.EntryMap[name]; !ok {
 		a.entryOrder = append(a.entryOrder, name)
@@ -108,12 +161,15 @@
 	a.EntryMap[name] = []string{path.String()}
 }
 
+// SetOptionalPath sets a Make variable with the given name to the given path string if it is valid.
+// It is a no-op if the given path is invalid.
 func (a *AndroidMkEntries) SetOptionalPath(name string, path OptionalPath) {
 	if path.Valid() {
 		a.SetPath(name, path.Path())
 	}
 }
 
+// AddPath appends the given path string to a Make variable with the given name.
 func (a *AndroidMkEntries) AddPath(name string, path Path) {
 	if _, ok := a.EntryMap[name]; !ok {
 		a.entryOrder = append(a.entryOrder, name)
@@ -121,12 +177,15 @@
 	a.EntryMap[name] = append(a.EntryMap[name], path.String())
 }
 
+// AddOptionalPath appends the given path string to a Make variable with the given name if it is
+// valid. It is a no-op if the given path is invalid.
 func (a *AndroidMkEntries) AddOptionalPath(name string, path OptionalPath) {
 	if path.Valid() {
 		a.AddPath(name, path.Path())
 	}
 }
 
+// SetPaths sets a Make variable with the given name to a slice of the given path strings.
 func (a *AndroidMkEntries) SetPaths(name string, paths Paths) {
 	if _, ok := a.EntryMap[name]; !ok {
 		a.entryOrder = append(a.entryOrder, name)
@@ -134,12 +193,15 @@
 	a.EntryMap[name] = paths.Strings()
 }
 
+// SetOptionalPaths sets a Make variable with the given name to a slice of the given path strings
+// only if there are a non-zero amount of paths.
 func (a *AndroidMkEntries) SetOptionalPaths(name string, paths Paths) {
 	if len(paths) > 0 {
 		a.SetPaths(name, paths)
 	}
 }
 
+// AddPaths appends the given path strings to a Make variable with the given name.
 func (a *AndroidMkEntries) AddPaths(name string, paths Paths) {
 	if _, ok := a.EntryMap[name]; !ok {
 		a.entryOrder = append(a.entryOrder, name)
@@ -147,6 +209,8 @@
 	a.EntryMap[name] = append(a.EntryMap[name], paths.Strings()...)
 }
 
+// SetBoolIfTrue sets a Make variable with the given name to true if the given flag is true.
+// It is a no-op if the given flag is false.
 func (a *AndroidMkEntries) SetBoolIfTrue(name string, flag bool) {
 	if flag {
 		if _, ok := a.EntryMap[name]; !ok {
@@ -156,6 +220,7 @@
 	}
 }
 
+// SetBool sets a Make variable with the given name to if the given bool flag value.
 func (a *AndroidMkEntries) SetBool(name string, flag bool) {
 	if _, ok := a.EntryMap[name]; !ok {
 		a.entryOrder = append(a.entryOrder, name)
@@ -167,6 +232,7 @@
 	}
 }
 
+// AddStrings appends the given strings to a Make variable with the given name.
 func (a *AndroidMkEntries) AddStrings(name string, value ...string) {
 	if len(value) == 0 {
 		return
@@ -368,6 +434,8 @@
 	return generateDistContributionsForMake(distContributions)
 }
 
+// fillInEntries goes through the common variable processing and calls the extra data funcs to
+// generate and fill in AndroidMkEntries's in-struct data, ready to be flushed to a file.
 func (a *AndroidMkEntries) fillInEntries(config Config, bpPath string, mod blueprint.Module) {
 	a.EntryMap = make(map[string][]string)
 	amod := mod.(Module).base()
@@ -488,6 +556,8 @@
 	}
 }
 
+// write  flushes the AndroidMkEntries's in-struct data populated by AndroidMkEntries into the
+// given Writer object.
 func (a *AndroidMkEntries) write(w io.Writer) {
 	if a.Disabled {
 		return
@@ -508,6 +578,8 @@
 	return strings.Split(string(a.footer.Bytes()), "\n")
 }
 
+// AndroidMkSingleton is a singleton to collect Android.mk data from all modules and dump them into
+// the final Android-<product_name>.mk file output.
 func AndroidMkSingleton() Singleton {
 	return &androidMkSingleton{}
 }
@@ -515,6 +587,7 @@
 type androidMkSingleton struct{}
 
 func (c *androidMkSingleton) GenerateBuildActions(ctx SingletonContext) {
+	// Skip if Soong wasn't invoked from Make.
 	if !ctx.Config().KatiEnabled() {
 		return
 	}
@@ -525,6 +598,8 @@
 		androidMkModulesList = append(androidMkModulesList, module)
 	})
 
+	// Sort the module list by the module names to eliminate random churns, which may erroneously
+	// invoke additional build processes.
 	sort.SliceStable(androidMkModulesList, func(i, j int) bool {
 		return ctx.ModuleName(androidMkModulesList[i]) < ctx.ModuleName(androidMkModulesList[j])
 	})
@@ -617,6 +692,8 @@
 	}
 }
 
+// A simple, special Android.mk entry output func to make it possible to build blueprint tools using
+// m by making them phony targets.
 func translateGoBinaryModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
 	goBinary bootstrap.GoBinaryTool) error {
 
@@ -649,6 +726,8 @@
 	data.Target_required = data.Entries.Target_required
 }
 
+// A support func for the deprecated AndroidMkDataProvider interface. Use AndroidMkEntryProvider
+// instead.
 func translateAndroidModule(ctx SingletonContext, w io.Writer, mod blueprint.Module,
 	provider AndroidMkDataProvider) error {
 
@@ -695,6 +774,8 @@
 	return nil
 }
 
+// A support func for the deprecated AndroidMkDataProvider interface. Use AndroidMkEntryProvider
+// instead.
 func WriteAndroidMkData(w io.Writer, data AndroidMkData) {
 	if data.Disabled {
 		return
@@ -742,11 +823,14 @@
 		module.Os() == LinuxBionic
 }
 
+// A utility func to format LOCAL_TEST_DATA outputs. See the comments on DataPath to understand how
+// to use this func.
 func AndroidMkDataPaths(data []DataPath) []string {
 	var testFiles []string
 	for _, d := range data {
 		rel := d.SrcPath.Rel()
 		path := d.SrcPath.String()
+		// LOCAL_TEST_DATA requires the rel portion of the path to be removed from the path.
 		if !strings.HasSuffix(path, rel) {
 			panic(fmt.Errorf("path %q does not end with %q", path, rel))
 		}
diff --git a/android/config.go b/android/config.go
index 04c9129..57ab70b 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1321,6 +1321,14 @@
 	return Bool(c.productVariables.EnforceProductPartitionInterface)
 }
 
+func (c *config) EnforceInterPartitionJavaSdkLibrary() bool {
+	return Bool(c.productVariables.EnforceInterPartitionJavaSdkLibrary)
+}
+
+func (c *config) InterPartitionJavaLibraryAllowList() []string {
+	return c.productVariables.InterPartitionJavaLibraryAllowList
+}
+
 func (c *config) InstallExtraFlattenedApexes() bool {
 	return Bool(c.productVariables.InstallExtraFlattenedApexes)
 }
diff --git a/android/packaging.go b/android/packaging.go
index f168fa6..3e42060 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -40,6 +40,15 @@
 	executable bool
 }
 
+// Get file name of installed package
+func (p *PackagingSpec) FileName() string {
+	if p.relPathInPackage != "" {
+		return filepath.Base(p.relPathInPackage)
+	}
+
+	return ""
+}
+
 type PackageModule interface {
 	Module
 	packagingBase() *PackagingBase
@@ -144,15 +153,9 @@
 
 // See PackageModule.CopyDepsToZip
 func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut OutputPath) (entries []string) {
-	var supportedArches []string
-	for _, t := range p.getSupportedTargets(ctx) {
-		supportedArches = append(supportedArches, t.Arch.ArchType.String())
-	}
 	m := make(map[string]PackagingSpec)
 	ctx.WalkDeps(func(child Module, parent Module) bool {
-		// Don't track modules with unsupported arch
-		// TODO(jiyong): remove this when aosp/1501613 lands.
-		if !InList(child.Target().Arch.ArchType.String(), supportedArches) {
+		if !IsInstallDepNeeded(ctx.OtherModuleDependencyTag(child)) {
 			return false
 		}
 		for _, ps := range child.PackagingSpecs() {
diff --git a/android/packaging_test.go b/android/packaging_test.go
index 2acd15c..7269bfb 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -29,6 +29,12 @@
 	}
 }
 
+// dep tag used in this test. All dependencies are considered as installable.
+type installDepTag struct {
+	blueprint.BaseDependencyTag
+	InstallAlwaysNeededDependencyTag
+}
+
 func componentTestModuleFactory() Module {
 	m := &componentTestModule{}
 	m.AddProperties(&m.props)
@@ -37,7 +43,7 @@
 }
 
 func (m *componentTestModule) DepsMutator(ctx BottomUpMutatorContext) {
-	ctx.AddDependency(ctx.Module(), nil, m.props.Deps...)
+	ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
 }
 
 func (m *componentTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
@@ -63,7 +69,7 @@
 }
 
 func (m *packageTestModule) DepsMutator(ctx BottomUpMutatorContext) {
-	m.AddDeps(ctx, struct{ blueprint.BaseDependencyTag }{})
+	m.AddDeps(ctx, installDepTag{})
 }
 
 func (m *packageTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
diff --git a/android/variable.go b/android/variable.go
index a9a9c87..0df5272 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -347,6 +347,9 @@
 
 	EnforceProductPartitionInterface *bool `json:",omitempty"`
 
+	EnforceInterPartitionJavaSdkLibrary *bool    `json:",omitempty"`
+	InterPartitionJavaLibraryAllowList  []string `json:",omitempty"`
+
 	InstallExtraFlattenedApexes *bool `json:",omitempty"`
 
 	BoardUsesRecoveryAsBoot *bool `json:",omitempty"`
diff --git a/cc/Android.bp b/cc/Android.bp
index ff2cdf3..88104e2 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -72,6 +72,8 @@
         "vendor_public_library.go",
 
         "testing.go",
+
+        "stub_library.go",
     ],
     testSrcs: [
         "cc_test.go",
diff --git a/cc/cc.go b/cc/cc.go
index 6deb1b4..1a7ccf2 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -584,7 +584,7 @@
 	genHeaderExportDepTag = dependencyTag{name: "gen header export"}
 	objDepTag             = dependencyTag{name: "obj"}
 	linkerFlagsDepTag     = dependencyTag{name: "linker flags file"}
-	dynamicLinkerDepTag   = dependencyTag{name: "dynamic linker"}
+	dynamicLinkerDepTag   = installDependencyTag{name: "dynamic linker"}
 	reuseObjTag           = dependencyTag{name: "reuse objects"}
 	staticVariantTag      = dependencyTag{name: "static variant"}
 	vndkExtDepTag         = dependencyTag{name: "vndk extends"}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index dbc52a5..22ee25f 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -169,14 +169,14 @@
 		Cfi              *bool    `android:"arch_variant"`
 		Integer_overflow *bool    `android:"arch_variant"`
 		Misc_undefined   []string `android:"arch_variant"`
-		No_recover       []string
-	}
+		No_recover       []string `android:"arch_variant"`
+	} `android:"arch_variant"`
 
 	// Sanitizers to run with flag configuration specified
 	Config struct {
 		// Enables CFI support flags for assembly-heavy libraries
 		Cfi_assembly_support *bool `android:"arch_variant"`
-	}
+	} `android:"arch_variant"`
 
 	// value to pass to -fsanitize-recover=
 	Recover []string
diff --git a/cc/stub_library.go b/cc/stub_library.go
new file mode 100644
index 0000000..76d236c
--- /dev/null
+++ b/cc/stub_library.go
@@ -0,0 +1,82 @@
+// 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 cc
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+func init() {
+	// Use singleton type to gather all generated soong modules.
+	android.RegisterSingletonType("stublibraries", stubLibrariesSingleton)
+}
+
+type stubLibraries struct {
+	stubLibraryMap map[string]bool
+}
+
+// Check if the module defines stub, or itself is stub
+func isStubTarget(m *Module) bool {
+	if m.IsStubs() || m.HasStubsVariants() {
+		return true
+	}
+
+	// Library which defines LLNDK Stub is also Stub target.
+	// Pure LLNDK Stub target would not contain any packaging
+	// with target file path.
+	if library, ok := m.linker.(*libraryDecorator); ok {
+		if library.Properties.Llndk_stubs != nil {
+			return true
+		}
+	}
+
+	return false
+}
+
+// Get target file name to be installed from this module
+func getInstalledFileName(m *Module) string {
+	for _, ps := range m.PackagingSpecs() {
+		if name := ps.FileName(); name != "" {
+			return name
+		}
+	}
+	return ""
+}
+
+func (s *stubLibraries) GenerateBuildActions(ctx android.SingletonContext) {
+	// Visit all generated soong modules and store stub library file names.
+	ctx.VisitAllModules(func(module android.Module) {
+		if m, ok := module.(*Module); ok {
+			if isStubTarget(m) {
+				if name := getInstalledFileName(m); name != "" {
+					s.stubLibraryMap[name] = true
+				}
+			}
+		}
+	})
+}
+
+func stubLibrariesSingleton() android.Singleton {
+	return &stubLibraries{
+		stubLibraryMap: make(map[string]bool),
+	}
+}
+
+func (s *stubLibraries) MakeVars(ctx android.MakeVarsContext) {
+	// Convert stub library file names into Makefile variable.
+	ctx.Strict("STUB_LIBRARIES", strings.Join(android.SortedStringKeys(s.stubLibraryMap), " "))
+}
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index ecbfbab..ac33b76 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -29,8 +29,16 @@
 type filesystem struct {
 	android.ModuleBase
 	android.PackagingBase
+
+	output     android.OutputPath
+	installDir android.InstallPath
 }
 
+// android_filesystem packages a set of modules and their transitive dependencies into a filesystem
+// image. The filesystem images are expected to be mounted in the target device, which means the
+// modules in the filesystem image are built for the target device (i.e. Android, not Linux host).
+// The modules are placed in the filesystem image just like they are installed to the ordinary
+// partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory.
 func filesystemFactory() android.Module {
 	module := &filesystem{}
 	android.InitPackageModule(module)
@@ -44,6 +52,10 @@
 	f.AddDeps(ctx, dependencyTag)
 }
 
+func (f *filesystem) installFileName() string {
+	return f.BaseModuleName() + ".img"
+}
+
 var pctx = android.NewPackageContext("android/soong/filesystem")
 
 func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -68,13 +80,32 @@
 		Text(">").Output(propFile).
 		Implicit(mkuserimg)
 
-	image := android.PathForModuleOut(ctx, "filesystem.img").OutputPath
+	f.output = android.PathForModuleOut(ctx, "filesystem.img").OutputPath
 	builder.Command().BuiltTool(ctx, "build_image").
 		Text(rootDir.String()). // input directory
 		Input(propFile).
-		Output(image).
+		Output(f.output).
 		Text(rootDir.String()) // directory where to find fs_config_files|dirs
 
 	// rootDir is not deleted. Might be useful for quick inspection.
 	builder.Build(pctx, ctx, "build_filesystem_image", fmt.Sprintf("Creating filesystem %s", f.BaseModuleName()))
+
+	f.installDir = android.PathForModuleInstall(ctx, "etc")
+	ctx.InstallFile(f.installDir, f.installFileName(), f.output)
+}
+
+var _ android.AndroidMkEntriesProvider = (*filesystem)(nil)
+
+// Implements android.AndroidMkEntriesProvider
+func (f *filesystem) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(f.output),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", f.installDir.ToMakePath().String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
+			},
+		},
+	}}
 }
diff --git a/java/Android.bp b/java/Android.bp
index 9e8dc78..39502b3 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -48,6 +48,7 @@
         "robolectric.go",
         "sdk.go",
         "sdk_library.go",
+        "sdk_library_external.go",
         "support_libraries.go",
         "sysprop.go",
         "system_modules.go",
diff --git a/java/aapt2.go b/java/aapt2.go
index 04e4de5..5346ddf 100644
--- a/java/aapt2.go
+++ b/java/aapt2.go
@@ -25,12 +25,9 @@
 	"android/soong/android"
 )
 
-const AAPT2_SHARD_SIZE = 100
-
 // Convert input resource file path to output file path.
 // values-[config]/<file>.xml -> values-[config]_<file>.arsc.flat;
-// For other resource file, just replace the last "/" with "_" and
-// add .flat extension.
+// For other resource file, just replace the last "/" with "_" and add .flat extension.
 func pathToAapt2Path(ctx android.ModuleContext, res android.Path) android.WritablePath {
 
 	name := res.Base()
@@ -43,6 +40,7 @@
 	return android.PathForModuleOut(ctx, "aapt2", subDir, name)
 }
 
+// pathsToAapt2Paths Calls pathToAapt2Path on each entry of the given Paths, i.e. []Path.
 func pathsToAapt2Paths(ctx android.ModuleContext, resPaths android.Paths) android.WritablePaths {
 	outPaths := make(android.WritablePaths, len(resPaths))
 
@@ -53,6 +51,9 @@
 	return outPaths
 }
 
+// Shard resource files for efficiency. See aapt2Compile for details.
+const AAPT2_SHARD_SIZE = 100
+
 var aapt2CompileRule = pctx.AndroidStaticRule("aapt2Compile",
 	blueprint.RuleParams{
 		Command:     `${config.Aapt2Cmd} compile -o $outDir $cFlags $in`,
@@ -60,14 +61,26 @@
 	},
 	"outDir", "cFlags")
 
+// aapt2Compile compiles resources and puts the results in the requested directory.
 func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Paths,
 	flags []string) android.WritablePaths {
 
+	// Shard the input paths so that they can be processed in parallel. If we shard them into too
+	// small chunks, the additional cost of spinning up aapt2 outweighs the performance gain. The
+	// current shard size, 100, seems to be a good balance between the added cost and the gain.
+	// The aapt2 compile actions are trivially short, but each action in ninja takes on the order of
+	// ~10 ms to run. frameworks/base/core/res/res has >10k resource files, so compiling each one
+	// with an individual action could take 100 CPU seconds. Sharding them reduces the overhead of
+	// starting actions by a factor of 100, at the expense of recompiling more files when one
+	// changes.  Since the individual compiles are trivial it's a good tradeoff.
 	shards := android.ShardPaths(paths, AAPT2_SHARD_SIZE)
 
 	ret := make(android.WritablePaths, 0, len(paths))
 
 	for i, shard := range shards {
+		// This should be kept in sync with pathToAapt2Path. The aapt2 compile command takes an
+		// output directory path, but not output file paths. So, outPaths is just where we expect
+		// the output files will be located.
 		outPaths := pathsToAapt2Paths(ctx, shard)
 		ret = append(ret, outPaths...)
 
@@ -82,6 +95,12 @@
 			Inputs:      shard,
 			Outputs:     outPaths,
 			Args: map[string]string{
+				// The aapt2 compile command takes an output directory path, but not output file paths.
+				// outPaths specified above is only used for dependency management purposes. In order for
+				// the outPaths values to match the actual outputs from aapt2, the dir parameter value
+				// must be a common prefix path of the paths values, and the top-level path segment used
+				// below, "aapt2", must always be kept in sync with the one in pathToAapt2Path.
+				// TODO(b/174505750): Make this easier and robust to use.
 				"outDir": android.PathForModuleOut(ctx, "aapt2", dir.String()).String(),
 				"cFlags": strings.Join(flags, " "),
 			},
@@ -104,6 +123,8 @@
 		},
 	}, "cFlags", "resZipDir", "zipSyncFlags")
 
+// Unzips the given compressed file and compiles the resource source files in it. The zipPrefix
+// parameter points to the subdirectory in the zip file where the resource files are located.
 func aapt2CompileZip(ctx android.ModuleContext, flata android.WritablePath, zip android.Path, zipPrefix string,
 	flags []string) {
 
@@ -163,6 +184,7 @@
 	var inFlags []string
 
 	if len(compiledRes) > 0 {
+		// Create a file that contains the list of all compiled resource file paths.
 		resFileList := android.PathForModuleOut(ctx, "aapt2", "res.list")
 		// Write out file lists to files
 		ctx.Build(pctx, android.BuildParams{
@@ -174,10 +196,12 @@
 
 		deps = append(deps, compiledRes...)
 		deps = append(deps, resFileList)
+		// aapt2 filepath arguments that start with "@" mean file-list files.
 		inFlags = append(inFlags, "@"+resFileList.String())
 	}
 
 	if len(compiledOverlay) > 0 {
+		// Compiled overlay files are processed the same way as compiled resources.
 		overlayFileList := android.PathForModuleOut(ctx, "aapt2", "overlay.list")
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        fileListToFileRule,
@@ -188,9 +212,11 @@
 
 		deps = append(deps, compiledOverlay...)
 		deps = append(deps, overlayFileList)
+		// Compiled overlay files are passed over to aapt2 using -R option.
 		inFlags = append(inFlags, "-R", "@"+overlayFileList.String())
 	}
 
+	// Set auxiliary outputs as implicit outputs to establish correct dependency chains.
 	implicitOutputs := append(splitPackages, proguardOptions, genJar, rTxt, extraPackages)
 	linkOutput := packageRes
 
@@ -212,6 +238,10 @@
 		Implicits:       deps,
 		Output:          linkOutput,
 		ImplicitOutputs: implicitOutputs,
+		// Note the absence of splitPackages. The caller is supposed to compose and provide --split flag
+		// values via the flags parameter when it wants to split outputs.
+		// TODO(b/174509108): Perhaps we can process it in this func while keeping the code reasonably
+		// tidy.
 		Args: map[string]string{
 			"flags":           strings.Join(flags, " "),
 			"inFlags":         strings.Join(inFlags, " "),
@@ -230,6 +260,8 @@
 		CommandDeps: []string{"${config.Aapt2Cmd}"},
 	})
 
+// Converts xml files and resource tables (resources.arsc) in the given jar/apk file to a proto
+// format. The proto definition is available at frameworks/base/tools/aapt2/Resources.proto.
 func aapt2Convert(ctx android.ModuleContext, out android.WritablePath, in android.Path) {
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        aapt2ConvertRule,
diff --git a/java/droiddoc.go b/java/droiddoc.go
index da8489a..64e8c59 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -1564,7 +1564,7 @@
 			`   1. You can add '@hide' javadoc comments (and remove @SystemApi/@TestApi/etc)\n`+
 			`      to the new methods, etc. shown in the above diff.\n\n`+
 			`   2. You can update current.txt and/or removed.txt by executing the following command:\n`+
-			`         make %s-update-current-api\n\n`+
+			`         m %s-update-current-api\n\n`+
 			`      To submit the revised current.txt to the main Android repository,\n`+
 			`      you will need approval.\n`+
 			`******************************\n`, ctx.ModuleName())
diff --git a/java/java.go b/java/java.go
index 8738e00..9000f74 100644
--- a/java/java.go
+++ b/java/java.go
@@ -772,6 +772,37 @@
 	libDeps := ctx.AddVariationDependencies(nil, libTag, rewriteSyspropLibs(j.properties.Libs, "libs")...)
 	ctx.AddVariationDependencies(nil, staticLibTag, rewriteSyspropLibs(j.properties.Static_libs, "static_libs")...)
 
+	if ctx.DeviceConfig().VndkVersion() != "" && ctx.Config().EnforceInterPartitionJavaSdkLibrary() {
+		// Require java_sdk_library at inter-partition java dependency to ensure stable
+		// interface between partitions. If inter-partition java_library dependency is detected,
+		// raise build error because java_library doesn't have a stable interface.
+		//
+		// Inputs:
+		//    PRODUCT_ENFORCE_INTER_PARTITION_JAVA_SDK_LIBRARY
+		//      if true, enable enforcement
+		//    PRODUCT_INTER_PARTITION_JAVA_LIBRARY_ALLOWLIST
+		//      exception list of java_library names to allow inter-partition dependency
+		for idx, lib := range j.properties.Libs {
+			if libDeps[idx] == nil {
+				continue
+			}
+
+			if _, ok := syspropPublicStubs[lib]; ok {
+				continue
+			}
+
+			if javaDep, ok := libDeps[idx].(javaSdkLibraryEnforceContext); ok {
+				// java_sdk_library is always allowed at inter-partition dependency.
+				// So, skip check.
+				if _, ok := javaDep.(*SdkLibrary); ok {
+					continue
+				}
+
+				j.checkPartitionsForJavaDependency(ctx, "libs", javaDep)
+			}
+		}
+	}
+
 	// For library dependencies that are component libraries (like stubs), add the implementation
 	// as a dependency (dexpreopt needs to be against the implementation library, not stubs).
 	for _, dep := range libDeps {
diff --git a/java/java_test.go b/java/java_test.go
index 83db443..f7cf03f 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -805,6 +805,165 @@
 	})
 }
 
+func TestJavaSdkLibraryEnforce(t *testing.T) {
+	partitionToBpOption := func(partition string) string {
+		switch partition {
+		case "system":
+			return ""
+		case "vendor":
+			return "soc_specific: true,"
+		case "product":
+			return "product_specific: true,"
+		default:
+			panic("Invalid partition group name: " + partition)
+		}
+	}
+
+	type testConfigInfo struct {
+		libraryType                string
+		fromPartition              string
+		toPartition                string
+		enforceVendorInterface     bool
+		enforceProductInterface    bool
+		enforceJavaSdkLibraryCheck bool
+		allowList                  []string
+	}
+
+	createTestConfig := func(info testConfigInfo) android.Config {
+		bpFileTemplate := `
+			java_library {
+				name: "foo",
+				srcs: ["foo.java"],
+				libs: ["bar"],
+				sdk_version: "current",
+				%s
+			}
+
+			%s {
+				name: "bar",
+				srcs: ["bar.java"],
+				sdk_version: "current",
+				%s
+			}
+		`
+
+		bpFile := fmt.Sprintf(bpFileTemplate,
+			partitionToBpOption(info.fromPartition),
+			info.libraryType,
+			partitionToBpOption(info.toPartition))
+
+		config := testConfig(nil, bpFile, nil)
+		configVariables := config.TestProductVariables
+
+		configVariables.EnforceProductPartitionInterface = proptools.BoolPtr(info.enforceProductInterface)
+		if info.enforceVendorInterface {
+			configVariables.DeviceVndkVersion = proptools.StringPtr("current")
+		}
+		configVariables.EnforceInterPartitionJavaSdkLibrary = proptools.BoolPtr(info.enforceJavaSdkLibraryCheck)
+		configVariables.InterPartitionJavaLibraryAllowList = info.allowList
+
+		return config
+	}
+
+	isValidDependency := func(configInfo testConfigInfo) bool {
+		if configInfo.enforceVendorInterface == false {
+			return true
+		}
+
+		if configInfo.enforceJavaSdkLibraryCheck == false {
+			return true
+		}
+
+		if inList("bar", configInfo.allowList) {
+			return true
+		}
+
+		if configInfo.libraryType == "java_library" {
+			if configInfo.fromPartition != configInfo.toPartition {
+				if !configInfo.enforceProductInterface &&
+					((configInfo.fromPartition == "system" && configInfo.toPartition == "product") ||
+						(configInfo.fromPartition == "product" && configInfo.toPartition == "system")) {
+					return true
+				}
+				return false
+			}
+		}
+
+		return true
+	}
+
+	errorMessage := "is not allowed across the partitions"
+
+	allPartitionCombinations := func() [][2]string {
+		var result [][2]string
+		partitions := []string{"system", "vendor", "product"}
+
+		for _, fromPartition := range partitions {
+			for _, toPartition := range partitions {
+				result = append(result, [2]string{fromPartition, toPartition})
+			}
+		}
+
+		return result
+	}
+
+	allFlagCombinations := func() [][3]bool {
+		var result [][3]bool
+		flagValues := [2]bool{false, true}
+
+		for _, vendorInterface := range flagValues {
+			for _, productInterface := range flagValues {
+				for _, enableEnforce := range flagValues {
+					result = append(result, [3]bool{vendorInterface, productInterface, enableEnforce})
+				}
+			}
+		}
+
+		return result
+	}
+
+	for _, libraryType := range []string{"java_library", "java_sdk_library"} {
+		for _, partitionValues := range allPartitionCombinations() {
+			for _, flagValues := range allFlagCombinations() {
+				testInfo := testConfigInfo{
+					libraryType:                libraryType,
+					fromPartition:              partitionValues[0],
+					toPartition:                partitionValues[1],
+					enforceVendorInterface:     flagValues[0],
+					enforceProductInterface:    flagValues[1],
+					enforceJavaSdkLibraryCheck: flagValues[2],
+				}
+
+				if isValidDependency(testInfo) {
+					testJavaWithConfig(t, createTestConfig(testInfo))
+				} else {
+					testJavaErrorWithConfig(t, errorMessage, createTestConfig(testInfo))
+				}
+			}
+		}
+	}
+
+	testJavaWithConfig(t, createTestConfig(testConfigInfo{
+		libraryType:                "java_library",
+		fromPartition:              "vendor",
+		toPartition:                "system",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: true,
+		allowList:                  []string{"bar"},
+	}))
+
+	testJavaErrorWithConfig(t, errorMessage, createTestConfig(testConfigInfo{
+		libraryType:                "java_library",
+		fromPartition:              "vendor",
+		toPartition:                "system",
+		enforceVendorInterface:     true,
+		enforceProductInterface:    true,
+		enforceJavaSdkLibraryCheck: true,
+		allowList:                  []string{"foo"},
+	}))
+}
+
 func TestDefaults(t *testing.T) {
 	ctx, _ := testJava(t, `
 		java_defaults {
diff --git a/java/sdk_library_external.go b/java/sdk_library_external.go
new file mode 100644
index 0000000..2934936
--- /dev/null
+++ b/java/sdk_library_external.go
@@ -0,0 +1,109 @@
+// 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"
+)
+
+type partitionGroup int
+
+// Representation of partition group for checking inter-partition library dependencies.
+// Between system and system_ext, there are no restrictions of dependencies,
+// so we can treat these partitions as the same in terms of inter-partition dependency.
+// Same policy is applied between vendor and odm partiton.
+const (
+	partitionGroupNone partitionGroup = iota
+	// group for system, and system_ext partition
+	partitionGroupSystem
+	// group for vendor and odm partition
+	partitionGroupVendor
+	// product partition
+	partitionGroupProduct
+)
+
+func (g partitionGroup) String() string {
+	switch g {
+	case partitionGroupSystem:
+		return "system"
+	case partitionGroupVendor:
+		return "vendor"
+	case partitionGroupProduct:
+		return "product"
+	}
+
+	return ""
+}
+
+// Get partition group of java module that can be used at inter-partition dependency check.
+// We currently have three groups
+//   (system, system_ext) => system partition group
+//   (vendor, odm) => vendor partition group
+//   (product) => product partition group
+func (j *Module) partitionGroup(ctx android.EarlyModuleContext) partitionGroup {
+	// system and system_ext partition can be treated as the same in terms of inter-partition dependency.
+	if j.Platform() || j.SystemExtSpecific() {
+		return partitionGroupSystem
+	}
+
+	// vendor and odm partition can be treated as the same in terms of inter-partition dependency.
+	if j.SocSpecific() || j.DeviceSpecific() {
+		return partitionGroupVendor
+	}
+
+	// product partition is independent.
+	if j.ProductSpecific() {
+		return partitionGroupProduct
+	}
+
+	panic("Cannot determine partition type")
+}
+
+func (j *Module) allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool {
+	return inList(j.Name(), ctx.Config().InterPartitionJavaLibraryAllowList())
+}
+
+type javaSdkLibraryEnforceContext interface {
+	Name() string
+	allowListedInterPartitionJavaLibrary(ctx android.EarlyModuleContext) bool
+	partitionGroup(ctx android.EarlyModuleContext) partitionGroup
+}
+
+var _ javaSdkLibraryEnforceContext = (*Module)(nil)
+
+func (j *Module) checkPartitionsForJavaDependency(ctx android.EarlyModuleContext, propName string, dep javaSdkLibraryEnforceContext) {
+	if dep.allowListedInterPartitionJavaLibrary(ctx) {
+		return
+	}
+
+	// If product interface is not enforced, skip check between system and product partition.
+	// But still need to check between product and vendor partition because product interface flag
+	// just represents enforcement between product and system, and vendor interface enforcement
+	// that is enforced here by precondition is representing enforcement between vendor and other partitions.
+	if !ctx.Config().EnforceProductPartitionInterface() {
+		productToSystem := j.partitionGroup(ctx) == partitionGroupProduct && dep.partitionGroup(ctx) == partitionGroupSystem
+		systemToProduct := j.partitionGroup(ctx) == partitionGroupSystem && dep.partitionGroup(ctx) == partitionGroupProduct
+
+		if productToSystem || systemToProduct {
+			return
+		}
+	}
+
+	// If module and dependency library is inter-partition
+	if j.partitionGroup(ctx) != dep.partitionGroup(ctx) {
+		errorFormat := "dependency on java_library (%q) is not allowed across the partitions (%s -> %s), use java_sdk_library instead"
+		ctx.PropertyErrorf(propName, errorFormat, dep.Name(), j.partitionGroup(ctx), dep.partitionGroup(ctx))
+	}
+}
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index 1c44c74..623c9dd 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -102,6 +102,7 @@
 				entries.SetString("LOCAL_MODULE_PATH", l.installDirPath.ToMakePath().String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.outputFilePath.Base())
 				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !installable)
+				entries.SetString("LINKER_CONFIG_PATH_"+l.Name(), l.OutputFile().String())
 			},
 		},
 	}}
diff --git a/scripts/conv_linker_config.py b/scripts/conv_linker_config.py
index 81425fb..fca71ad 100644
--- a/scripts/conv_linker_config.py
+++ b/scripts/conv_linker_config.py
@@ -18,8 +18,10 @@
 import argparse
 import collections
 import json
+import os
 
 import linker_config_pb2
+from google.protobuf.descriptor import FieldDescriptor
 from google.protobuf.json_format import ParseDict
 from google.protobuf.text_format import MessageToString
 
@@ -43,6 +45,25 @@
   print(MessageToString(pb))
 
 
+def SystemProvide(args):
+  pb = linker_config_pb2.LinkerConfig()
+  with open(args.source, 'rb') as f:
+    pb.ParseFromString(f.read())
+  libraries = args.value.split()
+
+  def IsInLibPath(lib_name):
+    lib_path = os.path.join(args.system, 'lib', lib_name)
+    lib64_path = os.path.join(args.system, 'lib64', lib_name)
+    return os.path.exists(lib_path) or os.path.islink(lib_path) or os.path.exists(lib64_path) or os.path.islink(lib64_path)
+
+  installed_libraries = list(filter(IsInLibPath, libraries))
+  for item in installed_libraries:
+    if item not in getattr(pb, 'provideLibs'):
+      getattr(pb, 'provideLibs').append(item)
+  with open(args.output, 'wb') as f:
+    f.write(pb.SerializeToString())
+
+
 def GetArgParser():
   parser = argparse.ArgumentParser()
   subparsers = parser.add_subparsers()
@@ -73,6 +94,32 @@
       help='Source linker configuration file in protobuf.')
   print_proto.set_defaults(func=Print)
 
+  system_provide_libs = subparsers.add_parser(
+      'systemprovide', help='Append system provide libraries into the configuration.')
+  system_provide_libs.add_argument(
+      '-s',
+      '--source',
+      required=True,
+      type=str,
+      help='Source linker configuration file in protobuf.')
+  system_provide_libs.add_argument(
+      '-o',
+      '--output',
+      required=True,
+      type=str,
+      help='Target linker configuration file to write in protobuf.')
+  system_provide_libs.add_argument(
+      '--value',
+      required=True,
+      type=str,
+      help='Values of the libraries to append. If there are more than one it should be separated by empty space')
+  system_provide_libs.add_argument(
+      '--system',
+      required=True,
+      type=str,
+      help='Path of the system image.')
+  system_provide_libs.set_defaults(func=SystemProvide)
+
   return parser
 
 
diff --git a/ui/build/build.go b/ui/build/build.go
index cfd0b83..e8f0fc4 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -28,7 +28,7 @@
 func SetupOutDir(ctx Context, config Config) {
 	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "Android.mk"))
 	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "CleanSpec.mk"))
-	if !config.SkipMake() {
+	if !config.SkipKati() {
 		// Run soong_build with Kati for a hybrid build, e.g. running the
 		// AndroidMk singleton and postinstall commands. Communicate this to
 		// soong_build by writing an empty .soong.kati_enabled marker file in the
@@ -44,7 +44,7 @@
 	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), ".out-dir"))
 
 	if buildDateTimeFile, ok := config.environ.Get("BUILD_DATETIME_FILE"); ok {
-		err := ioutil.WriteFile(buildDateTimeFile, []byte(config.buildDateTime), 0777)
+		err := ioutil.WriteFile(buildDateTimeFile, []byte(config.buildDateTime), 0666) // a+rw
 		if err != nil {
 			ctx.Fatalln("Failed to write BUILD_DATETIME to file:", err)
 		}
@@ -67,8 +67,8 @@
 `))
 
 func createCombinedBuildNinjaFile(ctx Context, config Config) {
-	// If we're in SkipMake mode, skip creating this file if it already exists
-	if config.SkipMake() {
+	// If we're in SkipKati mode, skip creating this file if it already exists
+	if config.SkipKati() {
 		if _, err := os.Stat(config.CombinedNinjaFile()); err == nil || !os.IsNotExist(err) {
 			return
 		}
@@ -85,6 +85,7 @@
 	}
 }
 
+// These are bitmasks which can be used to check whether various flags are set e.g. whether to use Bazel.
 const (
 	BuildNone          = iota
 	BuildProductConfig = 1 << iota
@@ -97,6 +98,7 @@
 	BuildAllWithBazel  = BuildProductConfig | BuildSoong | BuildKati | BuildBazel
 )
 
+// checkProblematicFiles fails the build if existing Android.mk or CleanSpec.mk files are found at the root of the tree.
 func checkProblematicFiles(ctx Context) {
 	files := []string{"Android.mk", "CleanSpec.mk"}
 	for _, file := range files {
@@ -108,6 +110,7 @@
 	}
 }
 
+// checkCaseSensitivity issues a warning if a case-insensitive file system is being used.
 func checkCaseSensitivity(ctx Context, config Config) {
 	outDir := config.OutDir()
 	lowerCase := filepath.Join(outDir, "casecheck.txt")
@@ -115,13 +118,11 @@
 	lowerData := "a"
 	upperData := "B"
 
-	err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0777)
-	if err != nil {
+	if err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0666); err != nil { // a+rw
 		ctx.Fatalln("Failed to check case sensitivity:", err)
 	}
 
-	err = ioutil.WriteFile(upperCase, []byte(upperData), 0777)
-	if err != nil {
+	if err := ioutil.WriteFile(upperCase, []byte(upperData), 0666); err != nil { // a+rw
 		ctx.Fatalln("Failed to check case sensitivity:", err)
 	}
 
@@ -139,18 +140,15 @@
 	}
 }
 
-func help(ctx Context, config Config, what int) {
+// help prints a help/usage message, via the build/make/help.sh script.
+func help(ctx Context, config Config) {
 	cmd := Command(ctx, config, "help.sh", "build/make/help.sh")
 	cmd.Sandbox = dumpvarsSandbox
 	cmd.RunAndPrintOrFatal()
 }
 
-// Build the tree. The 'what' argument can be used to chose which components of
-// the build to run.
-func Build(ctx Context, config Config, what int) {
-	ctx.Verboseln("Starting build with args:", config.Arguments())
-	ctx.Verboseln("Environment:", config.Environment().Environ())
-
+// checkRAM warns if there probably isn't enough RAM to complete a build.
+func checkRAM(ctx Context, config Config) {
 	if totalRAM := config.TotalRAM(); totalRAM != 0 {
 		ram := float32(totalRAM) / (1024 * 1024 * 1024)
 		ctx.Verbosef("Total RAM: %.3vGB", ram)
@@ -166,24 +164,24 @@
 			ctx.Println("-j value.")
 			ctx.Println("************************************************************")
 		} else if ram <= float32(config.Parallel()) {
+			// Want at least 1GB of RAM per job.
 			ctx.Printf("Warning: high -j%d count compared to %.3vGB of RAM", config.Parallel(), ram)
 			ctx.Println("If you run into segfaults or other errors, try a lower -j value")
 		}
 	}
+}
+
+// Build the tree. The 'what' argument can be used to chose which components of
+// the build to run, via checking various bitmasks.
+func Build(ctx Context, config Config, what int) {
+	ctx.Verboseln("Starting build with args:", config.Arguments())
+	ctx.Verboseln("Environment:", config.Environment().Environ())
 
 	ctx.BeginTrace(metrics.Total, "total")
 	defer ctx.EndTrace()
 
-	if config.SkipMake() {
-		ctx.Verboseln("Skipping Make/Kati as requested")
-		what = what & (BuildSoong | BuildNinja)
-	}
-
 	if inList("help", config.Arguments()) {
-		help(ctx, config, what)
-		return
-	} else if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
-		clean(ctx, config)
+		help(ctx, config)
 		return
 	}
 
@@ -191,16 +189,35 @@
 	buildLock := BecomeSingletonOrFail(ctx, config)
 	defer buildLock.Unlock()
 
+	if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
+		clean(ctx, config)
+		return
+	}
+
+	// checkProblematicFiles aborts the build if Android.mk or CleanSpec.mk are found at the root of the tree.
 	checkProblematicFiles(ctx)
 
+	checkRAM(ctx, config)
+
 	SetupOutDir(ctx, config)
 
+	// checkCaseSensitivity issues a warning if a case-insensitive file system is being used.
 	checkCaseSensitivity(ctx, config)
 
 	ensureEmptyDirectoriesExist(ctx, config.TempDir())
 
 	SetupPath(ctx, config)
 
+	if config.SkipConfig() {
+		ctx.Verboseln("Skipping Config as requested")
+		what = what &^ BuildProductConfig
+	}
+
+	if config.SkipKati() {
+		ctx.Verboseln("Skipping Kati as requested")
+		what = what &^ BuildKati
+	}
+
 	if config.StartGoma() {
 		// Ensure start Goma compiler_proxy
 		startGoma(ctx, config)
@@ -216,12 +233,16 @@
 		runMakeProductConfig(ctx, config)
 	}
 
+	// Everything below here depends on product config.
+
 	if inList("installclean", config.Arguments()) ||
 		inList("install-clean", config.Arguments()) {
 		installClean(ctx, config)
 		ctx.Println("Deleted images and staging directories.")
 		return
-	} else if inList("dataclean", config.Arguments()) ||
+	}
+
+	if inList("dataclean", config.Arguments()) ||
 		inList("data-clean", config.Arguments()) {
 		dataClean(ctx, config)
 		ctx.Println("Deleted data files.")
@@ -240,7 +261,7 @@
 		runKatiBuild(ctx, config)
 		runKatiPackage(ctx, config)
 
-		ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0777)
+		ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0666) // a+rw
 	} else {
 		// Load last Kati Suffix if it exists
 		if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil {
@@ -259,7 +280,7 @@
 	}
 
 	if what&BuildNinja != 0 {
-		if !config.SkipMake() {
+		if what&BuildKati != 0 {
 			installCleanIfNecessary(ctx, config)
 		}
 
@@ -267,6 +288,7 @@
 		runNinja(ctx, config)
 	}
 
+	// Currently, using Bazel requires Kati and Soong to run first, so check whether to run Bazel last.
 	if what&BuildBazel != 0 {
 		runBazel(ctx, config)
 	}
@@ -282,14 +304,11 @@
 	subDir := filepath.Join(subDirs...)
 	destDir := filepath.Join(config.DistDir(), "soong_ui", subDir)
 
-	err := os.MkdirAll(destDir, 0777)
-	if err != nil {
+	if err := os.MkdirAll(destDir, 0777); err != nil { // a+rwx
 		ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
-
 	}
 
-	err = gzipFileToDir(src, destDir)
-	if err != nil {
+	if err := gzipFileToDir(src, destDir); err != nil {
 		ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
 	}
 }
@@ -304,14 +323,11 @@
 	subDir := filepath.Join(subDirs...)
 	destDir := filepath.Join(config.DistDir(), "soong_ui", subDir)
 
-	err := os.MkdirAll(destDir, 0777)
-	if err != nil {
+	if err := os.MkdirAll(destDir, 0777); err != nil { // a+rwx
 		ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
-
 	}
 
-	_, err = copyFile(src, filepath.Join(destDir, filepath.Base(src)))
-	if err != nil {
+	if _, err := copyFile(src, filepath.Join(destDir, filepath.Base(src))); err != nil {
 		ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
 	}
 }
diff --git a/ui/build/config.go b/ui/build/config.go
index 229bd5c..c9911f3 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -46,7 +46,8 @@
 	verbose        bool
 	checkbuild     bool
 	dist           bool
-	skipMake       bool
+	skipConfig     bool
+	skipKati       bool
 	skipSoongTests bool
 
 	// From the product config
@@ -536,7 +537,10 @@
 		} else if arg == "showcommands" {
 			c.verbose = true
 		} else if arg == "--skip-make" {
-			c.skipMake = true
+			c.skipConfig = true
+			c.skipKati = true
+		} else if arg == "--skip-kati" {
+			c.skipKati = true
 		} else if arg == "--skip-soong-tests" {
 			c.skipSoongTests = true
 		} else if len(arg) > 0 && arg[0] == '-' {
@@ -697,7 +701,7 @@
 }
 
 func (c *configImpl) NinjaArgs() []string {
-	if c.skipMake {
+	if c.skipKati {
 		return c.arguments
 	}
 	return c.ninjaArgs
@@ -740,8 +744,12 @@
 	return c.verbose
 }
 
-func (c *configImpl) SkipMake() bool {
-	return c.skipMake
+func (c *configImpl) SkipKati() bool {
+	return c.skipKati
+}
+
+func (c *configImpl) SkipConfig() bool {
+	return c.skipConfig
 }
 
 func (c *configImpl) TargetProduct() string {
diff --git a/ui/build/soong.go b/ui/build/soong.go
index bb5cbf0..08c2ee1 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -30,16 +30,33 @@
 	"android/soong/ui/status"
 )
 
+// This uses Android.bp files and various tools to generate <builddir>/build.ninja.
+//
+// However, the execution of <builddir>/build.ninja happens later in build/soong/ui/build/build.go#Build()
+//
+// We want to rely on as few prebuilts as possible, so there is some bootstrapping here.
+//
+// "Microfactory" is a tool for compiling Go code. We use it to build two other tools:
+// - minibp, used to generate build.ninja files. This is really build/blueprint/bootstrap/command.go#Main()
+// - bpglob, used during incremental builds to identify files in a glob that have changed
+//
+// In reality, several build.ninja files are generated and/or used during the bootstrapping and build process.
+// See build/blueprint/bootstrap/doc.go for more information.
+//
 func runSoong(ctx Context, config Config) {
 	ctx.BeginTrace(metrics.RunSoong, "soong")
 	defer ctx.EndTrace()
 
+	// Use an anonymous inline function for tracing purposes (this pattern is used several times below).
 	func() {
 		ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
 		defer ctx.EndTrace()
 
+		// Use validations to depend on tests.
 		args := []string{"-n"}
+
 		if !config.skipSoongTests {
+			// Run tests.
 			args = append(args, "-t")
 		}
 
@@ -145,7 +162,10 @@
 		cmd.RunAndStreamOrFatal()
 	}
 
+	// This build generates .bootstrap/build.ninja, which is used in the next step.
 	ninja("minibootstrap", ".minibootstrap/build.ninja")
+
+	// This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
 	ninja("bootstrap", ".bootstrap/build.ninja")
 
 	soongBuildMetrics := loadSoongBuildMetrics(ctx, config)
@@ -153,7 +173,7 @@
 
 	distGzipFile(ctx, config, config.SoongNinjaFile(), "soong")
 
-	if !config.SkipMake() {
+	if !config.SkipKati() {
 		distGzipFile(ctx, config, config.SoongAndroidMk(), "soong")
 		distGzipFile(ctx, config, config.SoongMakeVarsMk(), "soong")
 	}