Merge "Update rustc version to 1.40.0"
diff --git a/Android.bp b/Android.bp
index 7576102..9403b26 100644
--- a/Android.bp
+++ b/Android.bp
@@ -155,6 +155,7 @@
         "cc/androidmk.go",
         "cc/builder.go",
         "cc/cc.go",
+        "cc/ccdeps.go",
         "cc/check.go",
         "cc/coverage.go",
         "cc/gen.go",
@@ -507,11 +508,13 @@
     ],
     srcs: [
         "sdk/bp.go",
+        "sdk/exports.go",
         "sdk/sdk.go",
         "sdk/update.go",
     ],
     testSrcs: [
         "sdk/cc_sdk_test.go",
+        "sdk/exports_test.go",
         "sdk/java_sdk_test.go",
         "sdk/sdk_test.go",
         "sdk/testing.go",
diff --git a/android/apex.go b/android/apex.go
index 1b0412b..8482dc2 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -185,6 +185,8 @@
 		}
 		variations = append(variations, m.apexVariations...)
 
+		defaultVariation := ""
+		mctx.SetDefaultDependencyVariation(&defaultVariation)
 		modules := mctx.CreateVariations(variations...)
 		for i, m := range modules {
 			if availableForPlatform && i == 0 {
diff --git a/android/hooks.go b/android/hooks.go
index 604cb9c..0e0f5a4 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -33,24 +33,11 @@
 	CreateModule(ModuleFactory, ...interface{}) Module
 }
 
-// Arch hooks are run after the module has been split into architecture variants, and can be used
-// to add architecture-specific properties.
-type ArchHookContext interface {
-	BaseModuleContext
-	AppendProperties(...interface{})
-	PrependProperties(...interface{})
-}
-
 func AddLoadHook(m blueprint.Module, hook func(LoadHookContext)) {
 	h := &m.(Module).base().hooks
 	h.load = append(h.load, hook)
 }
 
-func AddArchHook(m blueprint.Module, hook func(ArchHookContext)) {
-	h := &m.(Module).base().hooks
-	h.arch = append(h.arch, hook)
-}
-
 func (x *hooks) runLoadHooks(ctx LoadHookContext, m *ModuleBase) {
 	if len(x.load) > 0 {
 		for _, x := range x.load {
@@ -62,17 +49,6 @@
 	}
 }
 
-func (x *hooks) runArchHooks(ctx ArchHookContext, m *ModuleBase) {
-	if len(x.arch) > 0 {
-		for _, x := range x.arch {
-			x(ctx)
-			if ctx.Failed() {
-				return
-			}
-		}
-	}
-}
-
 type InstallHookContext interface {
 	ModuleContext
 	Path() InstallPath
@@ -119,7 +95,6 @@
 
 type hooks struct {
 	load    []func(LoadHookContext)
-	arch    []func(ArchHookContext)
 	install []func(InstallHookContext)
 }
 
@@ -137,12 +112,3 @@
 		m.base().hooks.runLoadHooks(loadHookCtx, m.base())
 	}
 }
-
-func archHookMutator(ctx TopDownMutatorContext) {
-	if m, ok := ctx.Module().(Module); ok {
-		// Cast through *topDownMutatorContext because AppendProperties is implemented
-		// on *topDownMutatorContext but not exposed through TopDownMutatorContext
-		var archHookCtx ArchHookContext = ctx.(*topDownMutatorContext)
-		m.base().hooks.runArchHooks(archHookCtx, m.base())
-	}
-}
diff --git a/android/mutator.go b/android/mutator.go
index c2bae44..709d9c0 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -89,7 +89,6 @@
 	ctx.BottomUp("os", osMutator).Parallel()
 	ctx.BottomUp("image", imageMutator).Parallel()
 	ctx.BottomUp("arch", archMutator).Parallel()
-	ctx.TopDown("arch_hooks", archHookMutator).Parallel()
 }
 
 var preDeps = []RegisterMutatorFunc{
diff --git a/android/sdk.go b/android/sdk.go
index 7956434..9e6ad16 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -237,6 +237,9 @@
 	// The name of the member type property on an sdk module.
 	SdkPropertyName() string
 
+	// True if the member type supports the sdk/sdk_snapshot, false otherwise.
+	UsableWithSdkAndSdkSnapshot() bool
+
 	// Add dependencies from the SDK module to all the variants the member
 	// contributes to the SDK. The exact set of variants required is determined
 	// by the SDK and its properties. The dependencies must be added with the
@@ -262,14 +265,20 @@
 	BuildSnapshot(sdkModuleContext ModuleContext, builder SnapshotBuilder, member SdkMember)
 }
 
+// Base type for SdkMemberType implementations.
 type SdkMemberTypeBase struct {
 	PropertyName string
+	SupportsSdk  bool
 }
 
 func (b *SdkMemberTypeBase) SdkPropertyName() string {
 	return b.PropertyName
 }
 
+func (b *SdkMemberTypeBase) UsableWithSdkAndSdkSnapshot() bool {
+	return b.SupportsSdk
+}
+
 // Encapsulates the information about registered SdkMemberTypes.
 type SdkMemberTypesRegistry struct {
 	// The list of types sorted by property name.
@@ -279,22 +288,8 @@
 	key OnceKey
 }
 
-func (r *SdkMemberTypesRegistry) RegisteredTypes() []SdkMemberType {
-	return r.list
-}
-
-func (r *SdkMemberTypesRegistry) UniqueOnceKey() OnceKey {
-	// Use the pointer to the registry as the unique key.
-	return NewCustomOnceKey(r)
-}
-
-// The set of registered SdkMemberTypes.
-var SdkMemberTypes = &SdkMemberTypesRegistry{}
-
-// Register an SdkMemberType object to allow them to be used in the sdk and sdk_snapshot module
-// types.
-func RegisterSdkMemberType(memberType SdkMemberType) {
-	oldList := SdkMemberTypes.list
+func (r *SdkMemberTypesRegistry) copyAndAppend(memberType SdkMemberType) *SdkMemberTypesRegistry {
+	oldList := r.list
 
 	// Copy the slice just in case this is being read while being modified, e.g. when testing.
 	list := make([]SdkMemberType, 0, len(oldList)+1)
@@ -319,8 +314,33 @@
 	key := NewOnceKey(strings.Join(properties, "|"))
 
 	// Create a new registry so the pointer uniquely identifies the set of registered types.
-	SdkMemberTypes = &SdkMemberTypesRegistry{
+	return &SdkMemberTypesRegistry{
 		list: list,
 		key:  key,
 	}
 }
+
+func (r *SdkMemberTypesRegistry) RegisteredTypes() []SdkMemberType {
+	return r.list
+}
+
+func (r *SdkMemberTypesRegistry) UniqueOnceKey() OnceKey {
+	// Use the pointer to the registry as the unique key.
+	return NewCustomOnceKey(r)
+}
+
+// The set of registered SdkMemberTypes, one for sdk module and one for module_exports.
+var ModuleExportsMemberTypes = &SdkMemberTypesRegistry{}
+var SdkMemberTypes = &SdkMemberTypesRegistry{}
+
+// Register an SdkMemberType object to allow them to be used in the sdk and sdk_snapshot module
+// types.
+func RegisterSdkMemberType(memberType SdkMemberType) {
+	// All member types are usable with module_exports.
+	ModuleExportsMemberTypes = ModuleExportsMemberTypes.copyAndAppend(memberType)
+
+	// Only those that explicitly indicate it are usable with sdk.
+	if memberType.UsableWithSdkAndSdkSnapshot() {
+		SdkMemberTypes = SdkMemberTypes.copyAndAppend(memberType)
+	}
+}
diff --git a/apex/apex.go b/apex/apex.go
index e0ee1ea..b27b54e 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -105,7 +105,8 @@
 				android.UpdateApexDependency(apexBundleName, depName, directDep)
 			}
 
-			if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() {
+			if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() &&
+				(directDep || am.DepIsInSameApex(mctx, child)) {
 				am.BuildForApex(apexBundleName)
 				return true
 			} else {
@@ -1024,12 +1025,12 @@
 					}
 					filesInfo = append(filesInfo, af)
 
-					pf := sdkLib.PermissionFile()
-					if pf == nil {
+					pf, _ := sdkLib.OutputFiles(".xml")
+					if len(pf) != 1 {
 						ctx.PropertyErrorf("java_libs", "%q failed to generate permission XML", depName)
 						return false
 					}
-					filesInfo = append(filesInfo, newApexFile(ctx, pf, pf.Base(), "etc/permissions", etc, nil))
+					filesInfo = append(filesInfo, newApexFile(ctx, pf[0], pf[0].Base(), "etc/permissions", etc, nil))
 					return true // track transitive dependencies
 				} else {
 					ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 97600b6..0d929ed 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -438,6 +438,7 @@
 			system_modules: "none",
 			compile_dex: true,
 			static_libs: ["myotherjar"],
+			libs: ["mysharedjar"],
 		}
 
 		java_library {
@@ -447,6 +448,14 @@
 			system_modules: "none",
 			compile_dex: true,
 		}
+
+		java_library {
+			name: "mysharedjar",
+			srcs: ["foo/bar/MyClass.java"],
+			sdk_version: "none",
+			system_modules: "none",
+			compile_dex: true,
+		}
 	`)
 
 	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
@@ -475,12 +484,17 @@
 	ensureContains(t, copyCmds, "image.apex/javalib/myjar.jar")
 	// .. but not for java libs
 	ensureNotContains(t, copyCmds, "image.apex/javalib/myotherjar.jar")
+	ensureNotContains(t, copyCmds, "image.apex/javalib/msharedjar.jar")
 
 	// Ensure that the platform variant ends with _shared or _common
 	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared")
 	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared")
 	ensureListContains(t, ctx.ModuleVariantsForTests("myjar"), "android_common")
 	ensureListContains(t, ctx.ModuleVariantsForTests("myotherjar"), "android_common")
+	ensureListContains(t, ctx.ModuleVariantsForTests("mysharedjar"), "android_common")
+
+	// Ensure that dynamic dependency to java libs are not included
+	ensureListNotContains(t, ctx.ModuleVariantsForTests("mysharedjar"), "android_common_myapex")
 
 	// Ensure that all symlinks are present.
 	found_foo_link_64 := false
@@ -700,9 +714,9 @@
 	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_myapex").Rule("ld").Args["libFlags"]
 
 	// Ensure that mylib is linking with the latest version of stubs for mylib2
-	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_3_myapex/mylib2.so")
+	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_3/mylib2.so")
 	// ... and not linking to the non-stub (impl) variant of mylib2
-	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_myapex/mylib2.so")
+	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
 
 	// Ensure that mylib is linking with the non-stub (impl) of mylib3 (because mylib3 is in the same apex)
 	ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_myapex/mylib3.so")
@@ -714,7 +728,7 @@
 	ensureNotContains(t, mylib2Cflags, "-include ")
 
 	// Ensure that genstub is invoked with --apex
-	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_3_myapex").Rule("genStubSrc").Args["flags"])
+	ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_3").Rule("genStubSrc").Args["flags"])
 
 	ensureExactContents(t, ctx, "myapex", []string{
 		"lib64/mylib.so",
@@ -780,11 +794,11 @@
 	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_myapex").Rule("ld").Args["libFlags"]
 
 	// Ensure that mylib is linking with version 10 of libfoo
-	ensureContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_shared_10_myapex/libfoo.so")
+	ensureContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_shared_10/libfoo.so")
 	// ... and not linking to the non-stub (impl) variant of libfoo
-	ensureNotContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_shared_myapex/libfoo.so")
+	ensureNotContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_shared/libfoo.so")
 
-	libFooStubsLdFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared_10_myapex").Rule("ld").Args["libFlags"]
+	libFooStubsLdFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared_10").Rule("ld").Args["libFlags"]
 
 	// Ensure that libfoo stubs is not linking to libbar (since it is a stubs)
 	ensureNotContains(t, libFooStubsLdFlags, "libbar.so")
@@ -996,9 +1010,9 @@
 
 	// For dependency to libc
 	// Ensure that mylib is linking with the latest version of stubs
-	ensureContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared_29_myapex/libc.so")
+	ensureContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared_29/libc.so")
 	// ... and not linking to the non-stub (impl) variant
-	ensureNotContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared_myapex/libc.so")
+	ensureNotContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared/libc.so")
 	// ... Cflags from stub is correctly exported to mylib
 	ensureContains(t, mylibCFlags, "__LIBC_API__=29")
 	ensureContains(t, mylibSharedCFlags, "__LIBC_API__=29")
@@ -1007,17 +1021,17 @@
 	// Ensure that mylib is linking with the non-stub (impl) variant
 	ensureContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_shared_myapex/libm.so")
 	// ... and not linking to the stub variant
-	ensureNotContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_shared_29_myapex/libm.so")
+	ensureNotContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_shared_29/libm.so")
 	// ... and is not compiling with the stub
 	ensureNotContains(t, mylibCFlags, "__LIBM_API__=29")
 	ensureNotContains(t, mylibSharedCFlags, "__LIBM_API__=29")
 
 	// For dependency to libdl
 	// Ensure that mylib is linking with the specified version of stubs
-	ensureContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_27_myapex/libdl.so")
+	ensureContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_27/libdl.so")
 	// ... and not linking to the other versions of stubs
-	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_28_myapex/libdl.so")
-	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_29_myapex/libdl.so")
+	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_28/libdl.so")
+	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_29/libdl.so")
 	// ... and not linking to the non-stub (impl) variant
 	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_myapex/libdl.so")
 	// ... Cflags from stub is correctly exported to mylib
@@ -1626,7 +1640,6 @@
 		"etc/vndkcore.libraries.VER.txt",
 		"etc/vndksp.libraries.VER.txt",
 		"etc/vndkprivate.libraries.VER.txt",
-		"etc/vndkcorevariant.libraries.VER.txt",
 	})
 }
 
@@ -1687,7 +1700,7 @@
 func vndkLibrariesTxtFiles(vers ...string) (result string) {
 	for _, v := range vers {
 		if v == "current" {
-			for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate", "vndkcorevariant"} {
+			for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate"} {
 				result += `
 					vndk_libraries_txt {
 						name: "` + txt + `.libraries.txt",
@@ -3288,8 +3301,8 @@
 		"etc/permissions/foo.xml",
 	})
 	// Permission XML should point to the activated path of impl jar of java_sdk_library
-	genXMLCommand := ctx.ModuleForTests("foo", "android_common_myapex").Output("foo.xml").RuleParams.Command
-	ensureContains(t, genXMLCommand, `<library name="foo" file="/apex/myapex/javalib/foo.jar"`)
+	xml := ctx.ModuleForTests("foo", "android_common_myapex").Output("foo.xml")
+	ensureContains(t, xml.Args["content"], `<library name="foo" file="/apex/myapex/javalib/foo.jar"`)
 }
 
 func TestRejectNonInstallableJavaLibrary(t *testing.T) {
diff --git a/apex/builder.go b/apex/builder.go
index fe465f5..4a760b9 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -105,6 +105,7 @@
 			`${apexer} --force --manifest ${manifest} ` +
 			`--file_contexts ${file_contexts} ` +
 			`--canned_fs_config ${canned_fs_config} ` +
+			`--include_build_info ` +
 			`--payload_type image ` +
 			`--key ${key} ${opt_flags} ${image_dir} ${out} `,
 		CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}",
@@ -378,7 +379,7 @@
 			optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeFile.String()))
 		}
 
-		if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldSkipHashtreeGeneration() {
+		if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && ctx.ModuleDir() != "system/apex/shim/build" && a.testOnlyShouldSkipHashtreeGeneration() {
 			ctx.PropertyErrorf("test_only_no_hashtree", "not available")
 			return
 		}
diff --git a/build_kzip.bash b/build_kzip.bash
index ccd6bad..02b346d 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -19,12 +19,13 @@
 # Build extraction files for C++ and Java. Build `merge_zips` which we use later.
 build/soong/soong_ui.bash --build-mode --all-modules --dir=$PWD -k merge_zips xref_cxx xref_java
 #Build extraction file for Go files in build/soong directory.
+declare -r abspath_out=$(realpath "${out}")
 (cd build/soong;
  ../../prebuilts/build-tools/linux-x86/bin/go_extractor \
     --goroot="${PWD}/../../prebuilts/go/linux-x86" \
     --rules=vnames.go.json \
     --canonicalize_package_corpus \
-    --output "${out}/soong/all.go.kzip" \
+    --output "${abspath_out}/soong/all.go.kzip" \
     ./... )
 
 declare -r kzip_count=$(find "$out" -name '*.kzip' | wc -l)
diff --git a/cc/cc.go b/cc/cc.go
index f16bb12..0c32225 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -2463,14 +2463,6 @@
 	return c.installer != nil && !c.Properties.PreventInstall && c.IsForPlatform() && c.outputFile.Valid()
 }
 
-func (c *Module) IDEInfo(dpInfo *android.IdeInfo) {
-	outputFiles, err := c.OutputFiles("")
-	if err != nil {
-		panic(err)
-	}
-	dpInfo.Srcs = append(dpInfo.Srcs, outputFiles.Strings()...)
-}
-
 func (c *Module) AndroidMkWriteAdditionalDependenciesForSourceAbiDiff(w io.Writer) {
 	if c.linker != nil {
 		if library, ok := c.linker.(*libraryDecorator); ok {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 1e70529..4d02f4f 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -295,7 +295,14 @@
 func checkVndkLibrariesOutput(t *testing.T, ctx *android.TestContext, module string, expected []string) {
 	t.Helper()
 	vndkLibraries := ctx.ModuleForTests(module, "")
-	output := insertVndkVersion(module, "VER")
+
+	var output string
+	if module != "vndkcorevariant.libraries.txt" {
+		output = insertVndkVersion(module, "VER")
+	} else {
+		output = module
+	}
+
 	checkWriteFileOutput(t, vndkLibraries.Output(output), expected)
 }
 
diff --git a/cc/ccdeps.go b/cc/ccdeps.go
new file mode 100644
index 0000000..9b89110
--- /dev/null
+++ b/cc/ccdeps.go
@@ -0,0 +1,252 @@
+// Copyright 2019 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 (
+	"encoding/json"
+	"fmt"
+	"os"
+	"path"
+	"sort"
+	"strings"
+
+	"android/soong/android"
+)
+
+// This singleton collects cc modules' source and flags into to a json file.
+// It does so for generating CMakeLists.txt project files needed data when
+// either make, mm, mma, mmm or mmma is called.
+// The info file is generated in $OUT/module_bp_cc_depend.json.
+
+func init() {
+	android.RegisterSingletonType("ccdeps_generator", ccDepsGeneratorSingleton)
+}
+
+func ccDepsGeneratorSingleton() android.Singleton {
+	return &ccdepsGeneratorSingleton{}
+}
+
+type ccdepsGeneratorSingleton struct {
+}
+
+const (
+	// Environment variables used to control the behavior of this singleton.
+	envVariableCollectCCDeps = "SOONG_COLLECT_CC_DEPS"
+	ccdepsJsonFileName       = "module_bp_cc_deps.json"
+	cClang                   = "clang"
+	cppClang                 = "clang++"
+)
+
+type ccIdeInfo struct {
+	Path                 []string     `json:"path,omitempty"`
+	Srcs                 []string     `json:"srcs,omitempty"`
+	Global_Common_Flags  ccParameters `json:"global_common_flags,omitempty"`
+	Local_Common_Flags   ccParameters `json:"local_common_flags,omitempty"`
+	Global_C_flags       ccParameters `json:"global_c_flags,omitempty"`
+	Local_C_flags        ccParameters `json:"local_c_flags,omitempty"`
+	Global_C_only_flags  ccParameters `json:"global_c_only_flags,omitempty"`
+	Local_C_only_flags   ccParameters `json:"local_c_only_flags,omitempty"`
+	Global_Cpp_flags     ccParameters `json:"global_cpp_flags,omitempty"`
+	Local_Cpp_flags      ccParameters `json:"local_cpp_flags,omitempty"`
+	System_include_flags ccParameters `json:"system_include_flags,omitempty"`
+	Module_name          string       `json:"module_name,omitempty"`
+}
+
+type ccParameters struct {
+	HeaderSearchPath       []string          `json:"header_search_path,omitempty"`
+	SystemHeaderSearchPath []string          `json:"system_search_path,omitempty"`
+	FlagParameters         []string          `json:"flag,omitempty"`
+	SysRoot                string            `json:"system_root,omitempty"`
+	RelativeFilePathFlags  map[string]string `json:"relative_file_path,omitempty"`
+}
+
+type ccMapIdeInfos map[string]ccIdeInfo
+
+type ccDeps struct {
+	C_clang   string        `json:"clang,omitempty"`
+	Cpp_clang string        `json:"clang++,omitempty"`
+	Modules   ccMapIdeInfos `json:"modules,omitempty"`
+}
+
+func (c *ccdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	if !ctx.Config().IsEnvTrue(envVariableCollectCCDeps) {
+		return
+	}
+
+	moduleDeps := ccDeps{}
+	moduleInfos := map[string]ccIdeInfo{}
+
+	// Track which projects have already had CMakeLists.txt generated to keep the first
+	// variant for each project.
+	seenProjects := map[string]bool{}
+
+	pathToCC, _ := evalVariable(ctx, "${config.ClangBin}/")
+	moduleDeps.C_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cClang)
+	moduleDeps.Cpp_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cppClang)
+
+	ctx.VisitAllModules(func(module android.Module) {
+		if ccModule, ok := module.(*Module); ok {
+			if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok {
+				generateCLionProjectData(ctx, compiledModule, ccModule, seenProjects, moduleInfos)
+			}
+		}
+	})
+
+	moduleDeps.Modules = moduleInfos
+
+	ccfpath := android.PathForOutput(ctx, ccdepsJsonFileName).String()
+	err := createJsonFile(moduleDeps, ccfpath)
+	if err != nil {
+		ctx.Errorf(err.Error())
+	}
+}
+
+func parseCompilerCCParameters(ctx android.SingletonContext, params []string) ccParameters {
+	compilerParams := ccParameters{}
+
+	cparams := []string{}
+	for _, param := range params {
+		param, _ = evalVariable(ctx, param)
+		cparams = append(cparams, param)
+	}
+
+	// Soong does not guarantee that each flag will be in an individual string. e.g: The
+	// input received could be:
+	// params = {"-isystem", "path/to/system"}
+	// or it could be
+	// params = {"-isystem path/to/system"}
+	// To normalize the input, we split all strings with the "space" character and consolidate
+	// all tokens into a flattened parameters list
+	cparams = normalizeParameters(cparams)
+
+	for i := 0; i < len(cparams); i++ {
+		param := cparams[i]
+		if param == "" {
+			continue
+		}
+
+		switch categorizeParameter(param) {
+		case headerSearchPath:
+			compilerParams.HeaderSearchPath =
+				append(compilerParams.HeaderSearchPath, strings.TrimPrefix(param, "-I"))
+		case systemHeaderSearchPath:
+			if i < len(params)-1 {
+				compilerParams.SystemHeaderSearchPath = append(compilerParams.SystemHeaderSearchPath, cparams[i+1])
+			}
+			i = i + 1
+		case flag:
+			c := cleanupParameter(param)
+			compilerParams.FlagParameters = append(compilerParams.FlagParameters, c)
+		case systemRoot:
+			if i < len(cparams)-1 {
+				compilerParams.SysRoot = cparams[i+1]
+			}
+			i = i + 1
+		case relativeFilePathFlag:
+			flagComponents := strings.Split(param, "=")
+			if len(flagComponents) == 2 {
+				if compilerParams.RelativeFilePathFlags == nil {
+					compilerParams.RelativeFilePathFlags = map[string]string{}
+				}
+				compilerParams.RelativeFilePathFlags[flagComponents[0]] = flagComponents[1]
+			}
+		}
+	}
+	return compilerParams
+}
+
+func generateCLionProjectData(ctx android.SingletonContext, compiledModule CompiledInterface,
+	ccModule *Module, seenProjects map[string]bool, moduleInfos map[string]ccIdeInfo) {
+	srcs := compiledModule.Srcs()
+	if len(srcs) == 0 {
+		return
+	}
+
+	// Only keep the DeviceArch variant module.
+	if ctx.DeviceConfig().DeviceArch() != ccModule.ModuleBase.Arch().ArchType.Name {
+		return
+	}
+
+	clionProjectLocation := getCMakeListsForModule(ccModule, ctx)
+	if seenProjects[clionProjectLocation] {
+		return
+	}
+
+	seenProjects[clionProjectLocation] = true
+
+	name := ccModule.ModuleBase.Name()
+	dpInfo := moduleInfos[name]
+
+	dpInfo.Path = append(dpInfo.Path, path.Dir(ctx.BlueprintFile(ccModule)))
+	dpInfo.Srcs = append(dpInfo.Srcs, srcs.Strings()...)
+	dpInfo.Path = android.FirstUniqueStrings(dpInfo.Path)
+	dpInfo.Srcs = android.FirstUniqueStrings(dpInfo.Srcs)
+
+	dpInfo.Global_Common_Flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CommonFlags)
+	dpInfo.Local_Common_Flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CommonFlags)
+	dpInfo.Global_C_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CFlags)
+	dpInfo.Local_C_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CFlags)
+	dpInfo.Global_C_only_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.ConlyFlags)
+	dpInfo.Local_C_only_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.ConlyFlags)
+	dpInfo.Global_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Global.CppFlags)
+	dpInfo.Local_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CppFlags)
+	dpInfo.System_include_flags = parseCompilerCCParameters(ctx, ccModule.flags.SystemIncludeFlags)
+
+	dpInfo.Module_name = name
+
+	moduleInfos[name] = dpInfo
+}
+
+type Deal struct {
+	Name    string
+	ideInfo ccIdeInfo
+}
+
+type Deals []Deal
+
+// Ensure it satisfies sort.Interface
+func (d Deals) Len() int           { return len(d) }
+func (d Deals) Less(i, j int) bool { return d[i].Name < d[j].Name }
+func (d Deals) Swap(i, j int)      { d[i], d[j] = d[j], d[i] }
+
+func sortMap(moduleInfos map[string]ccIdeInfo) map[string]ccIdeInfo {
+	var deals Deals
+	for k, v := range moduleInfos {
+		deals = append(deals, Deal{k, v})
+	}
+
+	sort.Sort(deals)
+
+	m := map[string]ccIdeInfo{}
+	for _, d := range deals {
+		m[d.Name] = d.ideInfo
+	}
+	return m
+}
+
+func createJsonFile(moduleDeps ccDeps, ccfpath string) error {
+	file, err := os.Create(ccfpath)
+	if err != nil {
+		return fmt.Errorf("Failed to create file: %s, relative: %v", ccdepsJsonFileName, err)
+	}
+	defer file.Close()
+	moduleDeps.Modules = sortMap(moduleDeps.Modules)
+	buf, err := json.MarshalIndent(moduleDeps, "", "\t")
+	if err != nil {
+		return fmt.Errorf("Write file failed: %s, relative: %v", ccdepsJsonFileName, err)
+	}
+	fmt.Fprintf(file, string(buf))
+	return nil
+}
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 2c18e68..fd5a4da 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -27,6 +27,7 @@
 var sharedLibrarySdkMemberType = &librarySdkMemberType{
 	SdkMemberTypeBase: android.SdkMemberTypeBase{
 		PropertyName: "native_shared_libs",
+		SupportsSdk:  true,
 	},
 	prebuiltModuleType: "cc_prebuilt_library_shared",
 	linkTypes:          []string{"shared"},
@@ -35,6 +36,7 @@
 var staticLibrarySdkMemberType = &librarySdkMemberType{
 	SdkMemberTypeBase: android.SdkMemberTypeBase{
 		PropertyName: "native_static_libs",
+		SupportsSdk:  true,
 	},
 	prebuiltModuleType: "cc_prebuilt_library_static",
 	linkTypes:          []string{"static"},
diff --git a/cc/vndk.go b/cc/vndk.go
index e4453eb..872a473 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -42,7 +42,6 @@
 			vndkCoreLibrariesTxt,
 			vndkSpLibrariesTxt,
 			vndkPrivateLibrariesTxt,
-			vndkUsingCoreVariantLibrariesTxt,
 		}
 	}
 	// Snapshot vndks have their own *.libraries.VER.txt files.
@@ -227,7 +226,7 @@
 	vndkSpLibrariesKey               = android.NewOnceKey("vndkSpLibrarires")
 	llndkLibrariesKey                = android.NewOnceKey("llndkLibrarires")
 	vndkPrivateLibrariesKey          = android.NewOnceKey("vndkPrivateLibrarires")
-	vndkUsingCoreVariantLibrariesKey = android.NewOnceKey("vndkUsingCoreVariantLibrarires")
+	vndkUsingCoreVariantLibrariesKey = android.NewOnceKey("vndkUsingCoreVariantLibraries")
 	vndkMustUseVendorVariantListKey  = android.NewOnceKey("vndkMustUseVendorVariantListKey")
 	vndkLibrariesLock                sync.Mutex
 
@@ -445,7 +444,13 @@
 		return
 	}
 
-	filename := insertVndkVersion(txt.Name(), ctx.DeviceConfig().PlatformVndkVersion())
+	var filename string
+	if txt.Name() != vndkUsingCoreVariantLibrariesTxt {
+		filename = insertVndkVersion(txt.Name(), ctx.DeviceConfig().PlatformVndkVersion())
+	} else {
+		filename = txt.Name()
+	}
+
 	txt.outputFile = android.PathForModuleOut(ctx, filename).OutputPath
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        android.WriteFile,
diff --git a/java/droiddoc.go b/java/droiddoc.go
index ff3f10a..f62f5f9 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -34,6 +34,9 @@
 	android.RegisterSdkMemberType(&droidStubsSdkMemberType{
 		SdkMemberTypeBase: android.SdkMemberTypeBase{
 			PropertyName: "stubs_sources",
+			// stubs_sources can be used with sdk to provide the source stubs for APIs provided by
+			// the APEX.
+			SupportsSdk: true,
 		},
 	})
 }
diff --git a/java/java.go b/java/java.go
index d0bf9d7..a48b5a3 100644
--- a/java/java.go
+++ b/java/java.go
@@ -41,6 +41,7 @@
 		librarySdkMemberType{
 			android.SdkMemberTypeBase{
 				PropertyName: "java_header_libs",
+				SupportsSdk:  true,
 			},
 		},
 	})
@@ -52,6 +53,12 @@
 			},
 		},
 	})
+
+	android.RegisterSdkMemberType(&testSdkMemberType{
+		SdkMemberTypeBase: android.SdkMemberTypeBase{
+			PropertyName: "java_tests",
+		},
+	})
 }
 
 func RegisterJavaBuildComponents(ctx android.RegistrationContext) {
@@ -65,6 +72,7 @@
 	ctx.RegisterModuleType("java_test", TestFactory)
 	ctx.RegisterModuleType("java_test_helper_library", TestHelperLibraryFactory)
 	ctx.RegisterModuleType("java_test_host", TestHostFactory)
+	ctx.RegisterModuleType("java_test_import", JavaTestImportFactory)
 	ctx.RegisterModuleType("java_import", ImportFactory)
 	ctx.RegisterModuleType("java_import_host", ImportFactoryHost)
 	ctx.RegisterModuleType("java_device_for_host", DeviceForHostFactory)
@@ -1764,14 +1772,19 @@
 }
 
 const (
-	aidlIncludeDir = "aidl"
-	javaDir        = "java"
-	jarFileSuffix  = ".jar"
+	aidlIncludeDir   = "aidl"
+	javaDir          = "java"
+	jarFileSuffix    = ".jar"
+	testConfigSuffix = "-AndroidTest.xml"
 )
 
 // path to the jar file of a java library. Relative to <sdk_root>/<api_dir>
-func (j *Library) sdkSnapshotFilePathForJar() string {
-	return filepath.Join(javaDir, j.Name()+jarFileSuffix)
+func sdkSnapshotFilePathForJar(member android.SdkMember) string {
+	return sdkSnapshotFilePathForMember(member, jarFileSuffix)
+}
+
+func sdkSnapshotFilePathForMember(member android.SdkMember, suffix string) string {
+	return filepath.Join(javaDir, member.Name()+suffix)
 }
 
 type librarySdkMemberType struct {
@@ -1804,7 +1817,7 @@
 	j := variant.(*Library)
 
 	exportedJar := jarToExportGetter(j)
-	snapshotRelativeJavaLibPath := j.sdkSnapshotFilePathForJar()
+	snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(member)
 	builder.CopyToSnapshot(exportedJar, snapshotRelativeJavaLibPath)
 
 	for _, dir := range j.AidlIncludeDirs() {
@@ -1931,6 +1944,16 @@
 	Test_suites []string `android:"arch_variant"`
 }
 
+type prebuiltTestProperties struct {
+	// list of compatibility suites (for example "cts", "vts") that the module should be
+	// installed into.
+	Test_suites []string `android:"arch_variant"`
+
+	// the name of the test configuration (for example "AndroidTest.xml") that should be
+	// installed with the module.
+	Test_config *string `android:"path,arch_variant"`
+}
+
 type Test struct {
 	Library
 
@@ -1946,6 +1969,14 @@
 	testHelperLibraryProperties testHelperLibraryProperties
 }
 
+type JavaTestImport struct {
+	Import
+
+	prebuiltTestProperties prebuiltTestProperties
+
+	testConfig android.Path
+}
+
 func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template,
 		j.testProperties.Test_suites, j.testProperties.Auto_gen_config)
@@ -1958,6 +1989,53 @@
 	j.Library.GenerateAndroidBuildActions(ctx)
 }
 
+func (j *JavaTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.prebuiltTestProperties.Test_config, nil,
+		j.prebuiltTestProperties.Test_suites, nil)
+
+	j.Import.GenerateAndroidBuildActions(ctx)
+}
+
+type testSdkMemberType struct {
+	android.SdkMemberTypeBase
+}
+
+func (mt *testSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
+	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+}
+
+func (mt *testSdkMemberType) IsInstance(module android.Module) bool {
+	_, ok := module.(*Test)
+	return ok
+}
+
+func (mt *testSdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) {
+	variants := member.Variants()
+	if len(variants) != 1 {
+		sdkModuleContext.ModuleErrorf("sdk contains %d variants of member %q but only one is allowed", len(variants), member.Name())
+		for _, variant := range variants {
+			sdkModuleContext.ModuleErrorf("    %q", variant)
+		}
+	}
+	variant := variants[0]
+	j := variant.(*Test)
+
+	implementationJars := j.ImplementationJars()
+	if len(implementationJars) != 1 {
+		panic(fmt.Errorf("there must be only one implementation jar from %q", j.Name()))
+	}
+
+	snapshotRelativeJavaLibPath := sdkSnapshotFilePathForJar(member)
+	builder.CopyToSnapshot(implementationJars[0], snapshotRelativeJavaLibPath)
+
+	snapshotRelativeTestConfigPath := sdkSnapshotFilePathForMember(member, testConfigSuffix)
+	builder.CopyToSnapshot(j.testConfig, snapshotRelativeTestConfigPath)
+
+	module := builder.AddPrebuiltModule(member, "java_test_import")
+	module.AddProperty("jars", []string{snapshotRelativeJavaLibPath})
+	module.AddProperty("test_config", snapshotRelativeTestConfigPath)
+}
+
 // java_test builds a and links sources into a `.jar` file for the device, and possibly for the host as well, and
 // creates an `AndroidTest.xml` file to allow running the test with `atest` or a `TEST_MAPPING` file.
 //
@@ -2001,6 +2079,30 @@
 	return module
 }
 
+// java_test_import imports one or more `.jar` files into the build graph as if they were built by a java_test module
+// and makes sure that it is added to the appropriate test suite.
+//
+// By default, a java_test_import has a single variant that expects a `.jar` file containing `.class` files that were
+// compiled against an Android classpath.
+//
+// Specifying `host_supported: true` will produce two variants, one for use as a dependency of device modules and one
+// for host modules.
+func JavaTestImportFactory() android.Module {
+	module := &JavaTestImport{}
+
+	module.AddProperties(
+		&module.Import.properties,
+		&module.prebuiltTestProperties)
+
+	module.Import.properties.Installable = proptools.BoolPtr(true)
+
+	android.InitPrebuiltModule(module, &module.properties.Jars)
+	android.InitApexModule(module)
+	android.InitSdkAwareModule(module)
+	InitJavaModule(module, android.HostAndDeviceSupported)
+	return module
+}
+
 // java_test_host builds a and links sources into a `.jar` file for the host, and creates an `AndroidTest.xml` file to
 // allow running the test with `atest` or a `TEST_MAPPING` file.
 //
diff --git a/java/java_test.go b/java/java_test.go
index 2f67cda..30a8ca6 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -486,6 +486,13 @@
 			name: "stubs-source",
 			srcs: ["stubs/sources"],
 		}
+
+		java_test_import {
+			name: "test",
+			jars: ["a.jar"],
+			test_suites: ["cts"],
+			test_config: "AndroidTest.xml",
+		}
 		`)
 
 	fooModule := ctx.ModuleForTests("foo", "android_common")
diff --git a/java/sdk_library.go b/java/sdk_library.go
index def2753..4492e3f 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -16,7 +16,6 @@
 
 import (
 	"android/soong/android"
-	"android/soong/genrule"
 	"fmt"
 	"io"
 	"path"
@@ -25,7 +24,6 @@
 	"strings"
 	"sync"
 
-	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
@@ -35,32 +33,26 @@
 	sdkTestApiSuffix      = ".test"
 	sdkDocsSuffix         = ".docs"
 	sdkXmlFileSuffix      = ".xml"
-	permissionTemplate    = `<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-	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.
--->
-<permissions>
-	<library name="%s" file="%s"/>
-</permissions>
-`
+	permissionsTemplate   = `<?xml version="1.0" encoding="utf-8"?>\n` +
+		`<!-- Copyright (C) 2018 The Android Open Source Project\n` +
+		`\n` +
+		`    Licensed under the Apache License, Version 2.0 (the "License");\n` +
+		`    you may not use this file except in compliance with the License.\n` +
+		`    You may obtain a copy of the License at\n` +
+		`\n` +
+		`        http://www.apache.org/licenses/LICENSE-2.0\n` +
+		`\n` +
+		`    Unless required by applicable law or agreed to in writing, software\n` +
+		`    distributed under the License is distributed on an "AS IS" BASIS,\n` +
+		`    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n` +
+		`    See the License for the specific language governing permissions and\n` +
+		`    limitations under the License.\n` +
+		`-->\n` +
+		`<permissions>\n` +
+		`    <library name="%s" file="%s"/>\n` +
+		`</permissions>\n`
 )
 
-type stubsLibraryDependencyTag struct {
-	blueprint.BaseDependencyTag
-	name string
-}
-
 var (
 	publicApiStubsTag = dependencyTag{name: "public"}
 	systemApiStubsTag = dependencyTag{name: "system"}
@@ -105,12 +97,22 @@
 	// List of Java libraries that will be in the classpath when building stubs
 	Stub_only_libs []string `android:"arch_variant"`
 
-	// list of package names that will be documented and publicized as API
+	// list of package names that will be documented and publicized as API.
+	// This allows the API to be restricted to a subset of the source files provided.
+	// If this is unspecified then all the source files will be treated as being part
+	// of the API.
 	Api_packages []string
 
 	// list of package names that must be hidden from the API
 	Hidden_api_packages []string
 
+	// the relative path to the directory containing the api specification files.
+	// Defaults to "api".
+	Api_dir *string
+
+	// If set to true there is no runtime library.
+	Api_only *bool
+
 	// local files that are used within user customized droiddoc options.
 	Droiddoc_option_files []string
 
@@ -133,6 +135,9 @@
 	// don't create dist rules.
 	No_dist *bool `blueprint:"mutated"`
 
+	// indicates whether system and test apis should be managed.
+	Has_system_and_test_apis bool `blueprint:"mutated"`
+
 	// TODO: determines whether to create HTML doc or not
 	//Html_doc *bool
 }
@@ -154,7 +159,7 @@
 	systemApiFilePath android.Path
 	testApiFilePath   android.Path
 
-	permissionFile android.Path
+	permissionsFile android.Path
 }
 
 var _ Dependency = (*SdkLibrary)(nil)
@@ -168,8 +173,7 @@
 	}
 	ctx.AddVariationDependencies(nil, publicApiFileTag, module.docsName(apiScopePublic))
 
-	sdkDep := decodeSdkDep(ctx, sdkContext(&module.Library))
-	if sdkDep.hasStandardLibs() {
+	if module.sdkLibraryProperties.Has_system_and_test_apis {
 		if useBuiltStubs {
 			ctx.AddVariationDependencies(nil, systemApiStubsTag, module.stubsName(apiScopeSystem))
 			ctx.AddVariationDependencies(nil, testApiStubsTag, module.stubsName(apiScopeTest))
@@ -182,12 +186,13 @@
 }
 
 func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	module.Library.GenerateAndroidBuildActions(ctx)
-
-	if module.ApexName() != "" {
-		module.buildPermissionFile(ctx)
+	// Don't build an implementation library if this is api only.
+	if !proptools.Bool(module.sdkLibraryProperties.Api_only) {
+		module.Library.GenerateAndroidBuildActions(ctx)
 	}
 
+	module.buildPermissionsFile(ctx)
+
 	// Record the paths to the header jars of the library (stubs and impl).
 	// When this java_sdk_library is dependened from others via "libs" property,
 	// the recorded paths will be returned depending on the link type of the caller.
@@ -223,22 +228,34 @@
 	})
 }
 
-func (module *SdkLibrary) buildPermissionFile(ctx android.ModuleContext) {
-	xmlContent := strings.ReplaceAll(fmt.Sprintf(permissionTemplate, module.BaseModuleName(), module.implPath()), "\n", "\\n")
-	permissionFile := android.PathForModuleOut(ctx, module.xmlFileName())
+func (module *SdkLibrary) buildPermissionsFile(ctx android.ModuleContext) {
+	xmlContent := fmt.Sprintf(permissionsTemplate, module.BaseModuleName(), module.implPath())
+	permissionsFile := android.PathForModuleOut(ctx, module.xmlFileName())
 
-	rule := android.NewRuleBuilder()
-	rule.Command().Text("echo -e ").Text(proptools.ShellEscape(xmlContent)).Text(">").Output(permissionFile)
-	rule.Build(pctx, ctx, "gen_permission_xml", "Generate permission")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.WriteFile,
+		Output:      permissionsFile,
+		Description: "Generating " + module.BaseModuleName() + " permissions",
+		Args: map[string]string{
+			"content": xmlContent,
+		},
+	})
 
-	module.permissionFile = permissionFile
+	module.permissionsFile = permissionsFile
 }
 
-func (module *SdkLibrary) PermissionFile() android.Path {
-	return module.permissionFile
+func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case ".xml":
+		return android.Paths{module.permissionsFile}, nil
+	}
+	return module.Library.OutputFiles(tag)
 }
 
 func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
+	if proptools.Bool(module.sdkLibraryProperties.Api_only) {
+		return nil
+	}
 	entriesList := module.Library.AndroidMkEntries()
 	entries := &entriesList[0]
 	entries.Required = append(entries.Required, module.xmlFileName())
@@ -357,7 +374,7 @@
 // SDK version that the stubs library is built against. Note that this is always
 // *current. Older stubs library built with a numberd SDK version is created from
 // the prebuilt jar.
-func (module *SdkLibrary) sdkVersion(apiScope apiScope) string {
+func (module *SdkLibrary) sdkVersionForScope(apiScope apiScope) string {
 	switch apiScope {
 	case apiScopePublic:
 		return "current"
@@ -370,6 +387,18 @@
 	}
 }
 
+// Get the sdk version for use when compiling the stubs library.
+func (module *SdkLibrary) sdkVersionForStubsLibrary(mctx android.BaseModuleContext, apiScope apiScope) string {
+	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
+	if sdkDep.hasStandardLibs() {
+		// If building against a standard sdk then use the sdk version appropriate for the scope.
+		return module.sdkVersionForScope(apiScope)
+	} else {
+		// Otherwise, use no system module.
+		return "none"
+	}
+}
+
 // $(INTERNAL_PLATFORM_<apiTagName>_API_FILE) points to the generated
 // api file for the current source
 // TODO: remove this when apicheck is done in soong
@@ -417,14 +446,15 @@
 	props := struct {
 		Name                *string
 		Srcs                []string
+		Installable         *bool
 		Sdk_version         *string
+		System_modules      *string
 		Libs                []string
 		Soc_specific        *bool
 		Device_specific     *bool
 		Product_specific    *bool
 		System_ext_specific *bool
 		Compile_dex         *bool
-		System_modules      *string
 		Java_version        *string
 		Product_variables   struct {
 			Unbundled_build struct {
@@ -440,23 +470,19 @@
 		}
 	}{}
 
-	sdkVersion := module.sdkVersion(apiScope)
-	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
-	if !sdkDep.hasStandardLibs() {
-		sdkVersion = "none"
-	}
-
 	props.Name = proptools.StringPtr(module.stubsName(apiScope))
 	// sources are generated from the droiddoc
 	props.Srcs = []string{":" + module.docsName(apiScope)}
+	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
 	props.Sdk_version = proptools.StringPtr(sdkVersion)
+	props.System_modules = module.Library.Module.deviceProperties.System_modules
+	props.Installable = proptools.BoolPtr(false)
 	props.Libs = module.sdkLibraryProperties.Stub_only_libs
 	// Unbundled apps will use the prebult one from /prebuilts/sdk
 	if mctx.Config().UnbundledBuildUsePrebuiltSdks() {
 		props.Product_variables.Unbundled_build.Enabled = proptools.BoolPtr(false)
 	}
 	props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false)
-	props.System_modules = module.Library.Module.deviceProperties.System_modules
 	props.Openjdk9.Srcs = module.Library.Module.properties.Openjdk9.Srcs
 	props.Openjdk9.Javacflags = module.Library.Module.properties.Openjdk9.Javacflags
 	props.Java_version = module.Library.Module.properties.Java_version
@@ -479,12 +505,13 @@
 
 // Creates a droiddoc module that creates stubs source files from the given full source
 // files
-func (module *SdkLibrary) createDocs(mctx android.LoadHookContext, apiScope apiScope) {
+func (module *SdkLibrary) createStubsSources(mctx android.LoadHookContext, apiScope apiScope) {
 	props := struct {
 		Name                             *string
 		Srcs                             []string
 		Installable                      *bool
 		Sdk_version                      *string
+		System_modules                   *string
 		Libs                             []string
 		Arg_files                        []string
 		Args                             *string
@@ -506,6 +533,8 @@
 	}{}
 
 	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
+	// Use the platform API if standard libraries were requested, otherwise use
+	// no default libraries.
 	sdkVersion := ""
 	if !sdkDep.hasStandardLibs() {
 		sdkVersion = "none"
@@ -514,6 +543,7 @@
 	props.Name = proptools.StringPtr(module.docsName(apiScope))
 	props.Srcs = append(props.Srcs, module.Library.Module.properties.Srcs...)
 	props.Sdk_version = proptools.StringPtr(sdkVersion)
+	props.System_modules = module.Library.Module.deviceProperties.System_modules
 	props.Installable = proptools.BoolPtr(false)
 	// A droiddoc module has only one Libs property and doesn't distinguish between
 	// shared libs and static libs. So we need to add both of these libs to Libs property.
@@ -526,21 +556,36 @@
 	props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs
 	props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs
 
-	droiddocArgs := " --stub-packages " + strings.Join(module.sdkLibraryProperties.Api_packages, ":") +
-		" " + android.JoinWithPrefix(module.sdkLibraryProperties.Hidden_api_packages, " --hide-package ") +
-		" " + android.JoinWithPrefix(module.sdkLibraryProperties.Droiddoc_options, " ") +
-		" --hide MissingPermission --hide BroadcastBehavior " +
-		"--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
-		"--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo"
+	droiddocArgs := []string{}
+	if len(module.sdkLibraryProperties.Api_packages) != 0 {
+		droiddocArgs = append(droiddocArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":"))
+	}
+	if len(module.sdkLibraryProperties.Hidden_api_packages) != 0 {
+		droiddocArgs = append(droiddocArgs,
+			android.JoinWithPrefix(module.sdkLibraryProperties.Hidden_api_packages, " --hide-package "))
+	}
+	droiddocArgs = append(droiddocArgs, module.sdkLibraryProperties.Droiddoc_options...)
+	disabledWarnings := []string{
+		"MissingPermission",
+		"BroadcastBehavior",
+		"HiddenSuperclass",
+		"DeprecationMismatch",
+		"UnavailableSymbol",
+		"SdkConstant",
+		"HiddenTypeParameter",
+		"Todo",
+		"Typo",
+	}
+	droiddocArgs = append(droiddocArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
 
 	switch apiScope {
 	case apiScopeSystem:
-		droiddocArgs = droiddocArgs + " -showAnnotation android.annotation.SystemApi"
+		droiddocArgs = append(droiddocArgs, "-showAnnotation android.annotation.SystemApi")
 	case apiScopeTest:
-		droiddocArgs = droiddocArgs + " -showAnnotation android.annotation.TestApi"
+		droiddocArgs = append(droiddocArgs, " -showAnnotation android.annotation.TestApi")
 	}
 	props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files
-	props.Args = proptools.StringPtr(droiddocArgs)
+	props.Args = proptools.StringPtr(strings.Join(droiddocArgs, " "))
 
 	// List of APIs identified from the provided source files are created. They are later
 	// compared against to the not-yet-released (a.k.a current) list of APIs and to the
@@ -555,8 +600,9 @@
 		currentApiFileName = "test-" + currentApiFileName
 		removedApiFileName = "test-" + removedApiFileName
 	}
-	currentApiFileName = path.Join("api", currentApiFileName)
-	removedApiFileName = path.Join("api", removedApiFileName)
+	apiDir := module.getApiDir()
+	currentApiFileName = path.Join(apiDir, currentApiFileName)
+	removedApiFileName = path.Join(apiDir, removedApiFileName)
 	// TODO(jiyong): remove these three props
 	props.Api_tag_name = proptools.StringPtr(module.apiTagName(apiScope))
 	props.Api_filename = proptools.StringPtr(currentApiFileName)
@@ -578,21 +624,6 @@
 
 // Creates the xml file that publicizes the runtime library
 func (module *SdkLibrary) createXmlFile(mctx android.LoadHookContext) {
-
-	// genrule to generate the xml file content from the template above
-	// TODO: preserve newlines in the generate xml file. Newlines are being squashed
-	// in the ninja file. Do we need to have an external tool for this?
-	xmlContent := fmt.Sprintf(permissionTemplate, module.BaseModuleName(), module.implPath())
-	genruleProps := struct {
-		Name *string
-		Cmd  *string
-		Out  []string
-	}{}
-	genruleProps.Name = proptools.StringPtr(module.xmlFileName() + "-gen")
-	genruleProps.Cmd = proptools.StringPtr("echo '" + xmlContent + "' > $(out)")
-	genruleProps.Out = []string{module.xmlFileName()}
-	mctx.CreateModule(genrule.GenRuleFactory, &genruleProps)
-
 	// creates a prebuilt_etc module to actually place the xml file under
 	// <partition>/etc/permissions
 	etcProps := struct {
@@ -605,7 +636,7 @@
 		System_ext_specific *bool
 	}{}
 	etcProps.Name = proptools.StringPtr(module.xmlFileName())
-	etcProps.Src = proptools.StringPtr(":" + module.xmlFileName() + "-gen")
+	etcProps.Src = proptools.StringPtr(":" + module.BaseModuleName() + "{.xml}")
 	etcProps.Sub_dir = proptools.StringPtr("permissions")
 	if module.SocSpecific() {
 		etcProps.Soc_specific = proptools.BoolPtr(true)
@@ -686,6 +717,10 @@
 	}).(*[]string)
 }
 
+func (module *SdkLibrary) getApiDir() string {
+	return proptools.StringDefault(module.sdkLibraryProperties.Api_dir, "api")
+}
+
 // For a java_sdk_library module, create internal modules for stubs, docs,
 // runtime libs and xml file. If requested, the stubs and docs are created twice
 // once for public API level and once for system API level
@@ -695,16 +730,25 @@
 		return
 	}
 
-	if len(module.sdkLibraryProperties.Api_packages) == 0 {
-		mctx.PropertyErrorf("api_packages", "java_sdk_library must specify api_packages")
-		return
+	// If this builds against standard libraries (i.e. is not part of the core libraries)
+	// then assume it provides both system and test apis. Otherwise, assume it does not and
+	// also assume it does not contribute to the dist build.
+	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
+	hasSystemAndTestApis := sdkDep.hasStandardLibs()
+	module.sdkLibraryProperties.Has_system_and_test_apis = hasSystemAndTestApis
+	module.sdkLibraryProperties.No_dist = proptools.BoolPtr(!hasSystemAndTestApis)
+
+	scopes := []string{""}
+	if hasSystemAndTestApis {
+		scopes = append(scopes, "system-", "test-")
 	}
 
 	missing_current_api := false
 
-	for _, scope := range []string{"", "system-", "test-"} {
+	apiDir := module.getApiDir()
+	for _, scope := range scopes {
 		for _, api := range []string{"current.txt", "removed.txt"} {
-			path := path.Join(mctx.ModuleDir(), "api", scope+api)
+			path := path.Join(mctx.ModuleDir(), apiDir, scope+api)
 			p := android.ExistentPathForSource(mctx, path)
 			if !p.Valid() {
 				mctx.ModuleErrorf("Current api file %#v doesn't exist", path)
@@ -723,33 +767,35 @@
 
 		mctx.ModuleErrorf("One or more current api files are missing. "+
 			"You can update them by:\n"+
-			"%s %q && m update-api", script, mctx.ModuleDir())
+			"%s %q %s && m update-api",
+			script, filepath.Join(mctx.ModuleDir(), apiDir), strings.Join(scopes, " "))
 		return
 	}
 
 	// for public API stubs
 	module.createStubsLibrary(mctx, apiScopePublic)
-	module.createDocs(mctx, apiScopePublic)
+	module.createStubsSources(mctx, apiScopePublic)
 
-	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
-	if sdkDep.hasStandardLibs() {
+	if hasSystemAndTestApis {
 		// for system API stubs
 		module.createStubsLibrary(mctx, apiScopeSystem)
-		module.createDocs(mctx, apiScopeSystem)
+		module.createStubsSources(mctx, apiScopeSystem)
 
 		// for test API stubs
 		module.createStubsLibrary(mctx, apiScopeTest)
-		module.createDocs(mctx, apiScopeTest)
-
-		// for runtime
-		module.createXmlFile(mctx)
+		module.createStubsSources(mctx, apiScopeTest)
 	}
 
-	// record java_sdk_library modules so that they are exported to make
-	javaSdkLibraries := javaSdkLibraries(mctx.Config())
-	javaSdkLibrariesLock.Lock()
-	defer javaSdkLibrariesLock.Unlock()
-	*javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName())
+	if !proptools.Bool(module.sdkLibraryProperties.Api_only) {
+		// for runtime
+		module.createXmlFile(mctx)
+
+		// record java_sdk_library modules so that they are exported to make
+		javaSdkLibraries := javaSdkLibraries(mctx.Config())
+		javaSdkLibrariesLock.Lock()
+		defer javaSdkLibrariesLock.Unlock()
+		*javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName())
+	}
 }
 
 func (module *SdkLibrary) InitSdkLibraryProperties() {
diff --git a/java/testing.go b/java/testing.go
index e157dd0..ab3af65 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -34,6 +34,7 @@
 		"GENRULE_NOTICE":         nil,
 		"LIB_NOTICE":             nil,
 		"TOOL_NOTICE":            nil,
+		"AndroidTest.xml":        nil,
 		"java-res/a/a":           nil,
 		"java-res/b/b":           nil,
 		"java-res2/a":            nil,
diff --git a/scripts/gen-java-current-api-files.sh b/scripts/gen-java-current-api-files.sh
index 517d391..547387a 100755
--- a/scripts/gen-java-current-api-files.sh
+++ b/scripts/gen-java-current-api-files.sh
@@ -15,15 +15,16 @@
 # limitations under the License.
 
 if [[ -z "$1" ]]; then
-  echo "usage: $0 <modulePath>" >&2
+  echo "usage: $0 <modulePath> scopes..." >&2
   exit 1
 fi
 
-api_dir=$1/api
+api_dir=$1
+shift
 
 mkdir -p "$api_dir"
 
-scopes=("" system- test-)
+scopes=("" "$@")
 apis=(current removed)
 
 for scope in "${scopes[@]}"; do
@@ -31,3 +32,4 @@
     touch "${api_dir}/${scope}${api}.txt"
   done
 done
+
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index f477445..255ac08 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -500,8 +500,8 @@
 
 func TestSnapshotWithCcStaticLibrary(t *testing.T) {
 	result := testSdkWithCc(t, `
-		sdk {
-			name: "mysdk",
+		module_exports {
+			name: "myexports",
 			native_static_libs: ["mynativelib"],
 		}
 
@@ -520,12 +520,12 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "android_common", "",
+	result.CheckSnapshot("myexports", "android_common", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_static {
-    name: "mysdk_mynativelib@current",
+    name: "myexports_mynativelib@current",
     sdk_member_name: "mynativelib",
     export_include_dirs: ["include/include"],
     arch: {
@@ -560,9 +560,9 @@
     system_shared_libs: [],
 }
 
-sdk_snapshot {
-    name: "mysdk@current",
-    native_static_libs: ["mysdk_mynativelib@current"],
+module_exports_snapshot {
+    name: "myexports@current",
+    native_static_libs: ["myexports_mynativelib@current"],
 }
 `),
 		checkAllCopyRules(`
@@ -584,8 +584,8 @@
 	SkipIfNotLinux(t)
 
 	result := testSdkWithCc(t, `
-		sdk {
-			name: "mysdk",
+		module_exports {
+			name: "myexports",
 			device_supported: false,
 			host_supported: true,
 			native_static_libs: ["mynativelib"],
@@ -608,12 +608,12 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "linux_glibc_common", "",
+	result.CheckSnapshot("myexports", "linux_glibc_common", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 cc_prebuilt_library_static {
-    name: "mysdk_mynativelib@current",
+    name: "myexports_mynativelib@current",
     sdk_member_name: "mynativelib",
     device_supported: false,
     host_supported: true,
@@ -652,11 +652,11 @@
     system_shared_libs: [],
 }
 
-sdk_snapshot {
-    name: "mysdk@current",
+module_exports_snapshot {
+    name: "myexports@current",
     device_supported: false,
     host_supported: true,
-    native_static_libs: ["mysdk_mynativelib@current"],
+    native_static_libs: ["myexports_mynativelib@current"],
 }
 `),
 		checkAllCopyRules(`
diff --git a/sdk/exports.go b/sdk/exports.go
new file mode 100644
index 0000000..d313057
--- /dev/null
+++ b/sdk/exports.go
@@ -0,0 +1,36 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// 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 sdk
+
+import "android/soong/android"
+
+func init() {
+	android.RegisterModuleType("module_exports", ModuleExportsFactory)
+	android.RegisterModuleType("module_exports_snapshot", ModuleExportsSnapshotsFactory)
+}
+
+// module_exports defines the exports of a mainline module. The exports are Soong modules
+// which are required by Soong modules that are not part of the mainline module.
+func ModuleExportsFactory() android.Module {
+	return newSdkModule(true)
+}
+
+// module_exports_snapshot is a versioned snapshot of prebuilt versions of all the exports
+// of a mainline module.
+func ModuleExportsSnapshotsFactory() android.Module {
+	s := newSdkModule(true)
+	s.properties.Snapshot = true
+	return s
+}
diff --git a/sdk/exports_test.go b/sdk/exports_test.go
new file mode 100644
index 0000000..b905d71
--- /dev/null
+++ b/sdk/exports_test.go
@@ -0,0 +1,66 @@
+// Copyright 2019 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 sdk
+
+import (
+	"testing"
+)
+
+// Ensure that module_exports generates a module_exports_snapshot module.
+func TestModuleExportsSnapshot(t *testing.T) {
+	packageBp := `
+		module_exports {
+			name: "myexports",
+			java_libs: [
+				"myjavalib",
+			],
+		}
+
+		java_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+		}
+	`
+
+	result := testSdkWithFs(t, ``,
+		map[string][]byte{
+			"package/Test.java":  nil,
+			"package/Android.bp": []byte(packageBp),
+		})
+
+	result.CheckSnapshot("myexports", "android_common", "package",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "myexports_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    jars: ["java/myjavalib.jar"],
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    java_libs: ["myexports_myjavalib@current"],
+}
+`))
+}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 1aa9184..218a16a 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -214,8 +214,8 @@
 
 func TestSnapshotWithJavaImplLibrary(t *testing.T) {
 	result := testSdkWithJava(t, `
-		sdk {
-			name: "mysdk",
+		module_exports {
+			name: "myexports",
 			java_libs: ["myjavalib"],
 		}
 
@@ -232,12 +232,12 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "android_common", "",
+	result.CheckSnapshot("myexports", "android_common", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "mysdk_myjavalib@current",
+    name: "myexports_myjavalib@current",
     sdk_member_name: "myjavalib",
     jars: ["java/myjavalib.jar"],
 }
@@ -248,9 +248,9 @@
     jars: ["java/myjavalib.jar"],
 }
 
-sdk_snapshot {
-    name: "mysdk@current",
-    java_libs: ["mysdk_myjavalib@current"],
+module_exports_snapshot {
+    name: "myexports@current",
+    java_libs: ["myexports_myjavalib@current"],
 }
 
 `),
@@ -266,8 +266,8 @@
 	SkipIfNotLinux(t)
 
 	result := testSdkWithJava(t, `
-		sdk {
-			name: "mysdk",
+		module_exports {
+			name: "myexports",
 			device_supported: false,
 			host_supported: true,
 			java_libs: ["myjavalib"],
@@ -287,12 +287,12 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "linux_glibc_common", "",
+	result.CheckSnapshot("myexports", "linux_glibc_common", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 java_import {
-    name: "mysdk_myjavalib@current",
+    name: "myexports_myjavalib@current",
     sdk_member_name: "myjavalib",
     device_supported: false,
     host_supported: true,
@@ -307,11 +307,11 @@
     jars: ["java/myjavalib.jar"],
 }
 
-sdk_snapshot {
-    name: "mysdk@current",
+module_exports_snapshot {
+    name: "myexports@current",
     device_supported: false,
     host_supported: true,
-    java_libs: ["mysdk_myjavalib@current"],
+    java_libs: ["myexports_myjavalib@current"],
 }
 `),
 		checkAllCopyRules(`
@@ -321,6 +321,112 @@
 	)
 }
 
+func TestSnapshotWithJavaTest(t *testing.T) {
+	result := testSdkWithJava(t, `
+		module_exports {
+			name: "myexports",
+			java_tests: ["myjavatests"],
+		}
+
+		java_test {
+			name: "myjavatests",
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+			host_supported: true,
+		}
+	`)
+
+	result.CheckSnapshot("myexports", "android_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_test_import {
+    name: "myexports_myjavatests@current",
+    sdk_member_name: "myjavatests",
+    jars: ["java/myjavatests.jar"],
+    test_config: "java/myjavatests-AndroidTest.xml",
+}
+
+java_test_import {
+    name: "myjavatests",
+    prefer: false,
+    jars: ["java/myjavatests.jar"],
+    test_config: "java/myjavatests-AndroidTest.xml",
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    java_tests: ["myexports_myjavatests@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/myjavatests/android_common/javac/myjavatests.jar -> java/myjavatests.jar
+.intermediates/myjavatests/android_common/myjavatests.config -> java/myjavatests-AndroidTest.xml
+`),
+	)
+}
+
+func TestHostSnapshotWithJavaTest(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithJava(t, `
+		module_exports {
+			name: "myexports",
+			device_supported: false,
+			host_supported: true,
+			java_tests: ["myjavatests"],
+		}
+
+		java_test {
+			name: "myjavatests",
+			device_supported: false,
+			host_supported: true,
+			srcs: ["Test.java"],
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+		}
+	`)
+
+	result.CheckSnapshot("myexports", "linux_glibc_common", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_test_import {
+    name: "myexports_myjavatests@current",
+    sdk_member_name: "myjavatests",
+    device_supported: false,
+    host_supported: true,
+    jars: ["java/myjavatests.jar"],
+    test_config: "java/myjavatests-AndroidTest.xml",
+}
+
+java_test_import {
+    name: "myjavatests",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    jars: ["java/myjavatests.jar"],
+    test_config: "java/myjavatests-AndroidTest.xml",
+}
+
+module_exports_snapshot {
+    name: "myexports@current",
+    device_supported: false,
+    host_supported: true,
+    java_tests: ["myexports_myjavatests@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/myjavatests/linux_glibc_common/javac/myjavatests.jar -> java/myjavatests.jar
+.intermediates/myjavatests/linux_glibc_common/myjavatests.config -> java/myjavatests-AndroidTest.xml
+`),
+	)
+}
+
 func testSdkWithDroidstubs(t *testing.T, bp string) *testSdkResult {
 	t.Helper()
 
@@ -366,8 +472,8 @@
 
 func TestSnapshotWithDroidstubs(t *testing.T) {
 	result := testSdkWithDroidstubs(t, `
-		sdk {
-			name: "mysdk",
+		module_exports {
+			name: "myexports",
 			stubs_sources: ["myjavaapistubs"],
 		}
 
@@ -379,12 +485,12 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "android_common", "",
+	result.CheckSnapshot("myexports", "android_common", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 prebuilt_stubs_sources {
-    name: "mysdk_myjavaapistubs@current",
+    name: "myexports_myjavaapistubs@current",
     sdk_member_name: "myjavaapistubs",
     srcs: ["java/myjavaapistubs_stubs_sources"],
 }
@@ -395,14 +501,14 @@
     srcs: ["java/myjavaapistubs_stubs_sources"],
 }
 
-sdk_snapshot {
-    name: "mysdk@current",
-    stubs_sources: ["mysdk_myjavaapistubs@current"],
+module_exports_snapshot {
+    name: "myexports@current",
+    stubs_sources: ["myexports_myjavaapistubs@current"],
 }
 
 `),
 		checkAllCopyRules(""),
-		checkMergeZip(".intermediates/mysdk/android_common/tmp/java/myjavaapistubs_stubs_sources.zip"),
+		checkMergeZip(".intermediates/myexports/android_common/tmp/java/myjavaapistubs_stubs_sources.zip"),
 	)
 }
 
@@ -411,8 +517,8 @@
 	SkipIfNotLinux(t)
 
 	result := testSdkWithDroidstubs(t, `
-		sdk {
-			name: "mysdk",
+		module_exports {
+			name: "myexports",
 			device_supported: false,
 			host_supported: true,
 			stubs_sources: ["myjavaapistubs"],
@@ -428,12 +534,12 @@
 		}
 	`)
 
-	result.CheckSnapshot("mysdk", "linux_glibc_common", "",
+	result.CheckSnapshot("myexports", "linux_glibc_common", "",
 		checkAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
 
 prebuilt_stubs_sources {
-    name: "mysdk_myjavaapistubs@current",
+    name: "myexports_myjavaapistubs@current",
     sdk_member_name: "myjavaapistubs",
     device_supported: false,
     host_supported: true,
@@ -448,14 +554,14 @@
     srcs: ["java/myjavaapistubs_stubs_sources"],
 }
 
-sdk_snapshot {
-    name: "mysdk@current",
+module_exports_snapshot {
+    name: "myexports@current",
     device_supported: false,
     host_supported: true,
-    stubs_sources: ["mysdk_myjavaapistubs@current"],
+    stubs_sources: ["myexports_myjavaapistubs@current"],
 }
 `),
 		checkAllCopyRules(""),
-		checkMergeZip(".intermediates/mysdk/linux_glibc_common/tmp/java/myjavaapistubs_stubs_sources.zip"),
+		checkMergeZip(".intermediates/myexports/linux_glibc_common/tmp/java/myjavaapistubs_stubs_sources.zip"),
 	)
 }
diff --git a/sdk/sdk.go b/sdk/sdk.go
index 62bc06f..44e5cbb 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -33,7 +33,7 @@
 	pctx.Import("android/soong/android")
 	pctx.Import("android/soong/java/config")
 
-	android.RegisterModuleType("sdk", ModuleFactory)
+	android.RegisterModuleType("sdk", SdkModuleFactory)
 	android.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
 	android.PreDepsMutators(RegisterPreDepsMutators)
 	android.PostDepsMutators(RegisterPostDepsMutators)
@@ -60,6 +60,9 @@
 
 type sdkProperties struct {
 	Snapshot bool `blueprint:"mutated"`
+
+	// True if this is a module_exports (or module_exports_snapshot) module type.
+	Module_exports bool `blueprint:"mutated"`
 }
 
 type sdkMemberDependencyTag struct {
@@ -130,6 +133,7 @@
 // * a dependency tag that identifies the member type of a resolved dependency.
 //
 func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynamicSdkMemberTypes {
+
 	var listProperties []*sdkMemberListProperty
 	var fields []reflect.StructField
 
@@ -182,18 +186,25 @@
 
 // sdk defines an SDK which is a logical group of modules (e.g. native libs, headers, java libs, etc.)
 // which Mainline modules like APEX can choose to build with.
-func ModuleFactory() android.Module {
+func SdkModuleFactory() android.Module {
+	return newSdkModule(false)
+}
+
+func newSdkModule(moduleExports bool) *sdk {
 	s := &sdk{}
-
+	s.properties.Module_exports = moduleExports
 	// Get the dynamic sdk member type data for the currently registered sdk member types.
-	s.dynamicSdkMemberTypes = getDynamicSdkMemberTypes(android.SdkMemberTypes)
-
+	var registry *android.SdkMemberTypesRegistry
+	if moduleExports {
+		registry = android.ModuleExportsMemberTypes
+	} else {
+		registry = android.SdkMemberTypes
+	}
+	s.dynamicSdkMemberTypes = getDynamicSdkMemberTypes(registry)
 	// Create an instance of the dynamically created struct that contains all the
 	// properties for the member type specific list properties.
 	s.dynamicMemberTypeListProperties = s.dynamicSdkMemberTypes.createMemberListProperties()
-
 	s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties)
-
 	android.InitAndroidMultiTargetsArchModule(s, android.HostAndDeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(s)
 	android.AddLoadHook(s, func(ctx android.LoadHookContext) {
@@ -208,8 +219,8 @@
 
 // sdk_snapshot is a versioned snapshot of an SDK. This is an auto-generated module.
 func SnapshotModuleFactory() android.Module {
-	s := ModuleFactory()
-	s.(*sdk).properties.Snapshot = true
+	s := newSdkModule(false)
+	s.properties.Snapshot = true
 	return s
 }
 
diff --git a/sdk/testing.go b/sdk/testing.go
index c0d6f51..8097889 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -84,8 +84,10 @@
 	ctx.PostDepsMutators(apex.RegisterPostDepsMutators)
 
 	// from this package
-	ctx.RegisterModuleType("sdk", ModuleFactory)
+	ctx.RegisterModuleType("sdk", SdkModuleFactory)
 	ctx.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
+	ctx.RegisterModuleType("module_exports", ModuleExportsFactory)
+	ctx.RegisterModuleType("module_exports_snapshot", ModuleExportsSnapshotsFactory)
 	ctx.PreDepsMutators(RegisterPreDepsMutators)
 	ctx.PostDepsMutators(RegisterPostDepsMutators)
 
diff --git a/sdk/update.go b/sdk/update.go
index d31fa30..5bc3b83 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -209,7 +209,13 @@
 
 	// Create the snapshot module.
 	snapshotName := ctx.ModuleName() + string(android.SdkVersionSeparator) + builder.version
-	snapshotModule := bpFile.newModule("sdk_snapshot")
+	var snapshotModuleType string
+	if s.properties.Module_exports {
+		snapshotModuleType = "module_exports_snapshot"
+	} else {
+		snapshotModuleType = "sdk_snapshot"
+	}
+	snapshotModule := bpFile.newModule(snapshotModuleType)
 	snapshotModule.AddProperty("name", snapshotName)
 
 	// Make sure that the snapshot has the same visibility as the sdk.
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index f212fb6..2a5a51a 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -59,6 +59,7 @@
         "util.go",
     ],
     testSrcs: [
+        "cleanbuild_test.go",
         "config_test.go",
         "environment_test.go",
         "util_test.go",
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 0b44b4d..1c4f574 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -15,10 +15,12 @@
 package build
 
 import (
+	"bytes"
 	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"sort"
 	"strings"
 
 	"android/soong/ui/metrics"
@@ -177,3 +179,78 @@
 
 	writeConfig()
 }
+
+// cleanOldFiles takes an input file (with all paths relative to basePath), and removes files from
+// the filesystem if they were removed from the input file since the last execution.
+func cleanOldFiles(ctx Context, basePath, file string) {
+	file = filepath.Join(basePath, file)
+	oldFile := file + ".previous"
+
+	if _, err := os.Stat(file); err != nil {
+		ctx.Fatalf("Expected %q to be readable", file)
+	}
+
+	if _, err := os.Stat(oldFile); os.IsNotExist(err) {
+		if err := os.Rename(file, oldFile); err != nil {
+			ctx.Fatalf("Failed to rename file list (%q->%q): %v", file, oldFile, err)
+		}
+		return
+	}
+
+	var newPaths, oldPaths []string
+	if newData, err := ioutil.ReadFile(file); err == nil {
+		if oldData, err := ioutil.ReadFile(oldFile); err == nil {
+			// Common case: nothing has changed
+			if bytes.Equal(newData, oldData) {
+				return
+			}
+			newPaths = strings.Fields(string(newData))
+			oldPaths = strings.Fields(string(oldData))
+		} else {
+			ctx.Fatalf("Failed to read list of installable files (%q): %v", oldFile, err)
+		}
+	} else {
+		ctx.Fatalf("Failed to read list of installable files (%q): %v", file, err)
+	}
+
+	// These should be mostly sorted by make already, but better make sure Go concurs
+	sort.Strings(newPaths)
+	sort.Strings(oldPaths)
+
+	for len(oldPaths) > 0 {
+		if len(newPaths) > 0 {
+			if oldPaths[0] == newPaths[0] {
+				// Same file; continue
+				newPaths = newPaths[1:]
+				oldPaths = oldPaths[1:]
+				continue
+			} else if oldPaths[0] > newPaths[0] {
+				// New file; ignore
+				newPaths = newPaths[1:]
+				continue
+			}
+		}
+		// File only exists in the old list; remove if it exists
+		old := filepath.Join(basePath, oldPaths[0])
+		oldPaths = oldPaths[1:]
+		if fi, err := os.Stat(old); err == nil {
+			if fi.IsDir() {
+				if err := os.Remove(old); err == nil {
+					ctx.Println("Removed directory that is no longer installed: ", old)
+				} else {
+					ctx.Println("Failed to remove directory that is no longer installed (%q): %v", old, err)
+					ctx.Println("It's recommended to run `m installclean`")
+				}
+			} else {
+				if err := os.Remove(old); err == nil {
+					ctx.Println("Removed file that is no longer installed: ", old)
+				} else if !os.IsNotExist(err) {
+					ctx.Fatalf("Failed to remove file that is no longer installed (%q): %v", old, err)
+				}
+			}
+		}
+	}
+
+	// Use the new list as the base for the next build
+	os.Rename(file, oldFile)
+}
diff --git a/ui/build/cleanbuild_test.go b/ui/build/cleanbuild_test.go
new file mode 100644
index 0000000..89f4ad9
--- /dev/null
+++ b/ui/build/cleanbuild_test.go
@@ -0,0 +1,100 @@
+// 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 build
+
+import (
+	"android/soong/ui/logger"
+	"bytes"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"reflect"
+	"sort"
+	"strings"
+	"testing"
+)
+
+func TestCleanOldFiles(t *testing.T) {
+	dir, err := ioutil.TempDir("", "testcleanoldfiles")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(dir)
+
+	ctx := testContext()
+	logBuf := &bytes.Buffer{}
+	ctx.Logger = logger.New(logBuf)
+
+	touch := func(names ...string) {
+		for _, name := range names {
+			if f, err := os.Create(filepath.Join(dir, name)); err != nil {
+				t.Fatal(err)
+			} else {
+				f.Close()
+			}
+		}
+	}
+	runCleanOldFiles := func(names ...string) {
+		data := []byte(strings.Join(names, " "))
+		if err := ioutil.WriteFile(filepath.Join(dir, ".installed"), data, 0666); err != nil {
+			t.Fatal(err)
+		}
+
+		cleanOldFiles(ctx, dir, ".installed")
+	}
+
+	assertFileList := func(names ...string) {
+		t.Helper()
+
+		sort.Strings(names)
+
+		var foundNames []string
+		if foundFiles, err := ioutil.ReadDir(dir); err == nil {
+			for _, fi := range foundFiles {
+				foundNames = append(foundNames, fi.Name())
+			}
+		} else {
+			t.Fatal(err)
+		}
+
+		if !reflect.DeepEqual(names, foundNames) {
+			t.Errorf("Expected a different list of files:\nwant: %v\n got: %v", names, foundNames)
+			t.Error("Log: ", logBuf.String())
+			logBuf.Reset()
+		}
+	}
+
+	// Initial list of potential files
+	runCleanOldFiles("foo", "bar")
+	touch("foo", "bar", "baz")
+	assertFileList("foo", "bar", "baz", ".installed.previous")
+
+	// This should be a no-op, as the list hasn't changed
+	runCleanOldFiles("foo", "bar")
+	assertFileList("foo", "bar", "baz", ".installed", ".installed.previous")
+
+	// This should be a no-op, as only a file was added
+	runCleanOldFiles("foo", "bar", "foo2")
+	assertFileList("foo", "bar", "baz", ".installed.previous")
+
+	// "bar" should be removed, foo2 should be ignored as it was never there
+	runCleanOldFiles("foo")
+	assertFileList("foo", "baz", ".installed.previous")
+
+	// Recreate bar, and create foo2. Ensure that they aren't removed
+	touch("bar", "foo2")
+	runCleanOldFiles("foo", "baz")
+	assertFileList("foo", "bar", "baz", "foo2", ".installed.previous")
+}
diff --git a/ui/build/config.go b/ui/build/config.go
index fae569f..c084171 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -55,8 +55,9 @@
 
 	pdkBuild bool
 
-	brokenDupRules    bool
-	brokenUsesNetwork bool
+	brokenDupRules     bool
+	brokenUsesNetwork  bool
+	brokenNinjaEnvVars []string
 
 	pathReplaced bool
 }
@@ -907,6 +908,14 @@
 	return c.brokenUsesNetwork
 }
 
+func (c *configImpl) SetBuildBrokenNinjaUsesEnvVars(val []string) {
+	c.brokenNinjaEnvVars = val
+}
+
+func (c *configImpl) BuildBrokenNinjaUsesEnvVars() []string {
+	return c.brokenNinjaEnvVars
+}
+
 func (c *configImpl) SetTargetDeviceDir(dir string) {
 	c.targetDeviceDir = dir
 }
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 4270bb1..c3da38b 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -216,6 +216,9 @@
 		// Whether to enable the network during the build
 		"BUILD_BROKEN_USES_NETWORK",
 
+		// Extra environment variables to be exported to ninja
+		"BUILD_BROKEN_NINJA_USES_ENV_VARS",
+
 		// Not used, but useful to be in the soong.log
 		"BOARD_VNDK_VERSION",
 
@@ -284,4 +287,5 @@
 	config.SetPdkBuild(make_vars["TARGET_BUILD_PDK"] == "true")
 	config.SetBuildBrokenDupRules(make_vars["BUILD_BROKEN_DUP_RULES"] == "true")
 	config.SetBuildBrokenUsesNetwork(make_vars["BUILD_BROKEN_USES_NETWORK"] == "true")
+	config.SetBuildBrokenNinjaUsesEnvVars(strings.Fields(make_vars["BUILD_BROKEN_NINJA_USES_ENV_VARS"]))
 }
diff --git a/ui/build/kati.go b/ui/build/kati.go
index ac09ce1..a845c5b 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -153,6 +153,7 @@
 	runKati(ctx, config, katiBuildSuffix, args, func(env *Environment) {})
 
 	cleanCopyHeaders(ctx, config)
+	cleanOldInstalledFiles(ctx, config)
 }
 
 func cleanCopyHeaders(ctx Context, config Config) {
@@ -192,6 +193,23 @@
 		})
 }
 
+func cleanOldInstalledFiles(ctx Context, config Config) {
+	ctx.BeginTrace("clean", "clean old installed files")
+	defer ctx.EndTrace()
+
+	// We shouldn't be removing files from one side of the two-step asan builds
+	var suffix string
+	if v, ok := config.Environment().Get("SANITIZE_TARGET"); ok {
+		if sanitize := strings.Fields(v); inList("address", sanitize) {
+			suffix = "_asan"
+		}
+	}
+
+	cleanOldFiles(ctx, config.ProductOut(), ".installable_files"+suffix)
+
+	cleanOldFiles(ctx, config.HostOut(), ".installable_test_files")
+}
+
 func runKatiPackage(ctx Context, config Config) {
 	ctx.BeginTrace(metrics.RunKati, "kati package")
 	defer ctx.EndTrace()
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index d5baafe..6775ccf 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"os"
 	"path/filepath"
+	"sort"
 	"strconv"
 	"strings"
 	"time"
@@ -65,8 +66,6 @@
 		cmd.Environment.AppendFromKati(config.KatiEnvFile())
 	}
 
-	cmd.Environment.Set("DIST_DIR", config.DistDir())
-
 	// Allow both NINJA_ARGS and NINJA_EXTRA_ARGS, since both have been
 	// used in the past to specify extra ninja arguments.
 	if extra, ok := cmd.Environment.Get("NINJA_ARGS"); ok {
@@ -85,6 +84,72 @@
 			ninjaHeartbeatDuration = overrideDuration
 		}
 	}
+
+	// Filter the environment, as ninja does not rebuild files when environment variables change.
+	//
+	// Anything listed here must not change the output of rules/actions when the value changes,
+	// otherwise incremental builds may be unsafe. Vars explicitly set to stable values
+	// elsewhere in soong_ui are fine.
+	//
+	// For the majority of cases, either Soong or the makefiles should be replicating any
+	// necessary environment variables in the command line of each action that needs it.
+	if cmd.Environment.IsEnvTrue("ALLOW_NINJA_ENV") {
+		ctx.Println("Allowing all environment variables during ninja; incremental builds may be unsafe.")
+	} else {
+		cmd.Environment.Allow(append([]string{
+			"ASAN_SYMBOLIZER_PATH",
+			"HOME",
+			"JAVA_HOME",
+			"LANG",
+			"LC_MESSAGES",
+			"OUT_DIR",
+			"PATH",
+			"PWD",
+			"PYTHONDONTWRITEBYTECODE",
+			"TMPDIR",
+			"USER",
+
+			// TODO: remove these carefully
+			"ASAN_OPTIONS",
+			"TARGET_BUILD_APPS",
+			"TARGET_BUILD_VARIANT",
+			"TARGET_PRODUCT",
+
+			// Goma -- gomacc may not need all of these
+			"GOMA_DIR",
+			"GOMA_DISABLED",
+			"GOMA_FAIL_FAST",
+			"GOMA_FALLBACK",
+			"GOMA_GCE_SERVICE_ACCOUNT",
+			"GOMA_TMP_DIR",
+			"GOMA_USE_LOCAL",
+
+			// RBE client
+			"FLAG_exec_root",
+			"FLAG_exec_strategy",
+			"FLAG_invocation_id",
+			"FLAG_log_dir",
+			"FLAG_platform",
+			"FLAG_server_address",
+
+			// ccache settings
+			"CCACHE_COMPILERCHECK",
+			"CCACHE_SLOPPINESS",
+			"CCACHE_BASEDIR",
+			"CCACHE_CPP2",
+		}, config.BuildBrokenNinjaUsesEnvVars()...)...)
+	}
+
+	cmd.Environment.Set("DIST_DIR", config.DistDir())
+	cmd.Environment.Set("SHELL", "/bin/bash")
+
+	ctx.Verboseln("Ninja environment: ")
+	envVars := cmd.Environment.Environ()
+	sort.Strings(envVars)
+	for _, envVar := range envVars {
+		ctx.Verbosef("  %s", envVar)
+	}
+
 	// Poll the ninja log for updates; if it isn't updated enough, then we want to show some diagnostics
 	done := make(chan struct{})
 	defer close(done)
diff --git a/ui/status/critical_path.go b/ui/status/critical_path.go
index 444327b..8065c60 100644
--- a/ui/status/critical_path.go
+++ b/ui/status/critical_path.go
@@ -112,8 +112,10 @@
 		if !cp.start.IsZero() {
 			elapsedTime := cp.end.Sub(cp.start).Round(time.Second)
 			cp.log.Verbosef("elapsed time %s", elapsedTime.String())
-			cp.log.Verbosef("perfect parallelism ratio %d%%",
-				int(float64(criticalTime)/float64(elapsedTime)*100))
+			if elapsedTime > 0 {
+				cp.log.Verbosef("perfect parallelism ratio %d%%",
+					int(float64(criticalTime)/float64(elapsedTime)*100))
+			}
 		}
 		cp.log.Verbose("critical path:")
 		for i := len(criticalPath) - 1; i >= 0; i-- {