Merge "Allows prebuilts in override_apex."
diff --git a/android/bazel.go b/android/bazel.go
index b01bd70..692fbcd 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -120,6 +120,10 @@
 	// allows modules to opt-out.
 	Bp2BuildDefaultTrueRecursively BazelConversionConfigEntry = iota + 1
 
+	// all modules in this package (not recursively) default to bp2build_available: true.
+	// allows modules to opt-out.
+	Bp2BuildDefaultTrue
+
 	// all modules in this package (not recursively) default to bp2build_available: false.
 	// allows modules to opt-in.
 	Bp2BuildDefaultFalse
@@ -141,6 +145,7 @@
 		"build/bazel/bazel_skylib":/* recursive = */ true,
 		"build/bazel/rules":/* recursive = */ true,
 		"build/bazel/rules_cc":/* recursive = */ true,
+		"build/bazel/scripts":/* recursive = */ true,
 		"build/bazel/tests":/* recursive = */ true,
 		"build/bazel/platforms":/* recursive = */ true,
 		"build/bazel/product_variables":/* recursive = */ true,
@@ -162,8 +167,11 @@
 	bp2buildDefaultConfig = Bp2BuildConfig{
 		"bionic":                            Bp2BuildDefaultTrueRecursively,
 		"build/bazel/examples/apex/minimal": Bp2BuildDefaultTrueRecursively,
+		"development/sdk":                   Bp2BuildDefaultTrueRecursively,
 		"external/gwp_asan":                 Bp2BuildDefaultTrueRecursively,
+		"external/brotli":                   Bp2BuildDefaultTrue,
 		"system/core/libcutils":             Bp2BuildDefaultTrueRecursively,
+		"system/core/libprocessgroup":       Bp2BuildDefaultTrue,
 		"system/core/property_service/libpropertyinfoparser": Bp2BuildDefaultTrueRecursively,
 		"system/libbase":                  Bp2BuildDefaultTrueRecursively,
 		"system/logging/liblog":           Bp2BuildDefaultTrueRecursively,
@@ -172,6 +180,7 @@
 		"external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively,
 		"external/fmtlib":                 Bp2BuildDefaultTrueRecursively,
 		"external/jemalloc_new":           Bp2BuildDefaultTrueRecursively,
+		"external/libcxx":                 Bp2BuildDefaultTrueRecursively,
 		"external/libcxxabi":              Bp2BuildDefaultTrueRecursively,
 		"external/scudo":                  Bp2BuildDefaultTrueRecursively,
 		"prebuilts/clang/host/linux-x86":  Bp2BuildDefaultTrueRecursively,
@@ -216,6 +225,12 @@
 
 		"gwp_asan_crash_handler", // cc_library, ld.lld: error: undefined symbol: memset
 
+		//system/core/libprocessgroup/...
+		"libprocessgroup", // depends on //system/core/libprocessgroup/cgrouprc:libcgrouprc
+
+		//external/brotli/...
+		"brotli-fuzzer-corpus", // "declared output 'external/brotli/c/fuzz/73231c6592f195ffd41100b8706d1138ff6893b9' was not created by genrule"
+
 		// Tests. Handle later.
 		"libbionic_tests_headers_posix", // http://b/186024507, cc_library_static, sched.h, time.h not found
 		"libjemalloc5_integrationtest",
@@ -236,8 +251,12 @@
 	// Per-module denylist to opt modules out of mixed builds. Such modules will
 	// still be generated via bp2build.
 	mixedBuildsDisabledList = []string{
-		"libc++abi",      // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects.
-		"libc++demangle", // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects.
+		"libbrotli",           // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy
+		"libc++fs",            // http://b/198403271, Missing symbols/members in the global namespace when referenced from headers in //external/libcxx/includes
+		"libc++_experimental", // http://b/198403271, Missing symbols/members in the global namespace when referenced from headers in //external/libcxx/includes
+		"libc++_static",       // http://b/198403271, Missing symbols/members in the global namespace when referenced from headers in //external/libcxx/includes
+		"libc++abi",           // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects.
+		"libc++demangle",      // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects.
 	}
 
 	// Used for quicker lookups
@@ -339,11 +358,10 @@
 func bp2buildDefaultTrueRecursively(packagePath string, config Bp2BuildConfig) bool {
 	ret := false
 
-	// Return exact matches in the config.
-	if config[packagePath] == Bp2BuildDefaultTrueRecursively {
+	// Check if the package path has an exact match in the config.
+	if config[packagePath] == Bp2BuildDefaultTrue || config[packagePath] == Bp2BuildDefaultTrueRecursively {
 		return true
-	}
-	if config[packagePath] == Bp2BuildDefaultFalse {
+	} else if config[packagePath] == Bp2BuildDefaultFalse {
 		return false
 	}
 
diff --git a/android/config.go b/android/config.go
index b69963f..a32a1a6 100644
--- a/android/config.go
+++ b/android/config.go
@@ -66,19 +66,35 @@
 	*config
 }
 
-// BuildDir returns the build output directory for the configuration.
+// SoongOutDir returns the build output directory for the configuration.
 func (c Config) SoongOutDir() string {
 	return c.soongOutDir
 }
 
 func (c Config) OutDir() string {
-	return c.soongOutDir
+	return c.outDir
+}
+
+func (c Config) RunGoTests() bool {
+	return c.runGoTests
+}
+
+func (c Config) UseValidationsForGoTests() bool {
+	return c.useValidationsForGoTests
 }
 
 func (c Config) DebugCompilation() bool {
 	return false // Never compile Go code in the main build for debugging
 }
 
+func (c Config) Subninjas() []string {
+	return []string{}
+}
+
+func (c Config) PrimaryBuilderInvocations() []bootstrap.PrimaryBuilderInvocation {
+	return []bootstrap.PrimaryBuilderInvocation{}
+}
+
 // A DeviceConfig object represents the configuration for a particular device
 // being built. For now there will only be one of these, but in the future there
 // may be multiple devices being built.
@@ -122,9 +138,13 @@
 
 	deviceConfig *deviceConfig
 
-	soongOutDir    string // the path of the build output directory
+	outDir         string // The output directory (usually out/)
+	soongOutDir    string
 	moduleListFile string // the path to the file which lists blueprint files to parse.
 
+	runGoTests               bool
+	useValidationsForGoTests bool
+
 	env       map[string]string
 	envLock   sync.Mutex
 	envDeps   map[string]string
@@ -283,9 +303,10 @@
 
 // NullConfig returns a mostly empty Config for use by standalone tools like dexpreopt_gen that
 // use the android package.
-func NullConfig(soongOutDir string) Config {
+func NullConfig(outDir, soongOutDir string) Config {
 	return Config{
 		config: &config{
+			outDir:      outDir,
 			soongOutDir: soongOutDir,
 			fs:          pathtools.OsFs,
 		},
@@ -319,6 +340,9 @@
 			ShippingApiLevel:                  stringPtr("30"),
 		},
 
+		outDir: buildDir,
+		// soongOutDir is inconsistent with production (it should be buildDir + "/soong")
+		// but a lot of tests assume this :(
 		soongOutDir:  buildDir,
 		captureBuild: true,
 		env:          envCopy,
@@ -396,8 +420,8 @@
 // bootstrap run. Only per-run data is reset. Data which needs to persist across
 // multiple runs in the same program execution is carried over (such as Bazel
 // context or environment deps).
-func ConfigForAdditionalRun(c Config) (Config, error) {
-	newConfig, err := NewConfig(c.soongOutDir, c.moduleListFile, c.env)
+func ConfigForAdditionalRun(cmdlineArgs bootstrap.Args, c Config) (Config, error) {
+	newConfig, err := NewConfig(cmdlineArgs, c.soongOutDir, c.env)
 	if err != nil {
 		return Config{}, err
 	}
@@ -408,17 +432,20 @@
 
 // NewConfig creates a new Config object. The srcDir argument specifies the path
 // to the root source directory. It also loads the config file, if found.
-func NewConfig(soongOutDir string, moduleListFile string, availableEnv map[string]string) (Config, error) {
+func NewConfig(cmdlineArgs bootstrap.Args, soongOutDir string, availableEnv map[string]string) (Config, error) {
 	// Make a config with default options.
 	config := &config{
 		ProductVariablesFileName: filepath.Join(soongOutDir, productVariablesFileName),
 
 		env: availableEnv,
 
-		soongOutDir:       soongOutDir,
-		multilibConflicts: make(map[ArchType]bool),
+		outDir:                   cmdlineArgs.OutDir,
+		soongOutDir:              soongOutDir,
+		runGoTests:               cmdlineArgs.RunGoTests,
+		useValidationsForGoTests: cmdlineArgs.UseValidations,
+		multilibConflicts:        make(map[ArchType]bool),
 
-		moduleListFile: moduleListFile,
+		moduleListFile: cmdlineArgs.ModuleListFile,
 		fs:             pathtools.NewOsFs(absSrcDir),
 	}
 
@@ -555,12 +582,10 @@
 
 // BlueprintToolLocation returns the directory containing build system tools
 // from Blueprint, like soong_zip and merge_zips.
-func (c *config) BlueprintToolLocation() string {
+func (c *config) HostToolDir() string {
 	return filepath.Join(c.soongOutDir, "host", c.PrebuiltOS(), "bin")
 }
 
-var _ bootstrap.ConfigBlueprintToolLocation = (*config)(nil)
-
 func (c *config) HostToolPath(ctx PathContext, tool string) Path {
 	return PathForOutput(ctx, "host", c.PrebuiltOS(), "bin", tool)
 }
diff --git a/android/license_sdk_member.go b/android/license_sdk_member.go
index cd36ed6..2ce921b 100644
--- a/android/license_sdk_member.go
+++ b/android/license_sdk_member.go
@@ -31,9 +31,9 @@
 	SdkMemberTypeBase
 }
 
-func (l *licenseSdkMemberType) AddDependencies(mctx BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
+func (l *licenseSdkMemberType) AddDependencies(ctx SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
 	// Add dependencies onto the license module from the sdk module.
-	mctx.AddDependency(mctx.Module(), dependencyTag, names...)
+	ctx.AddDependency(ctx.Module(), dependencyTag, names...)
 }
 
 func (l *licenseSdkMemberType) IsInstance(module Module) bool {
diff --git a/android/paths.go b/android/paths.go
index a733558..763cd7c 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1150,7 +1150,7 @@
 type OutputPath struct {
 	basePath
 
-	// The soong build directory, i.e. Config.BuildDir()
+	// The soong build directory, i.e. Config.SoongOutDir()
 	soongOutDir string
 
 	fullPath string
@@ -1544,7 +1544,7 @@
 type InstallPath struct {
 	basePath
 
-	// The soong build directory, i.e. Config.BuildDir()
+	// The soong build directory, i.e. Config.SoongOutDir()
 	soongOutDir string
 
 	// partitionDir is the part of the InstallPath that is automatically determined according to the context.
diff --git a/android/sdk.go b/android/sdk.go
index da740f3..b8f76c1 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -401,26 +401,26 @@
 	ExportMember() bool
 }
 
-var _ SdkMemberTypeDependencyTag = (*sdkMemberDependencyTag)(nil)
-var _ ReplaceSourceWithPrebuilt = (*sdkMemberDependencyTag)(nil)
+var _ SdkMemberTypeDependencyTag = (*sdkMemberTypeDependencyTag)(nil)
+var _ ReplaceSourceWithPrebuilt = (*sdkMemberTypeDependencyTag)(nil)
 
-type sdkMemberDependencyTag struct {
+type sdkMemberTypeDependencyTag struct {
 	blueprint.BaseDependencyTag
 	memberType SdkMemberType
 	export     bool
 }
 
-func (t *sdkMemberDependencyTag) SdkMemberType(_ Module) SdkMemberType {
+func (t *sdkMemberTypeDependencyTag) SdkMemberType(_ Module) SdkMemberType {
 	return t.memberType
 }
 
-func (t *sdkMemberDependencyTag) ExportMember() bool {
+func (t *sdkMemberTypeDependencyTag) ExportMember() bool {
 	return t.export
 }
 
 // Prevent dependencies from the sdk/module_exports onto their members from being
 // replaced with a preferred prebuilt.
-func (t *sdkMemberDependencyTag) ReplaceSourceWithPrebuilt() bool {
+func (t *sdkMemberTypeDependencyTag) ReplaceSourceWithPrebuilt() bool {
 	return false
 }
 
@@ -428,7 +428,7 @@
 // dependencies added by the tag to be added to the sdk as the specified SdkMemberType and exported
 // (or not) as specified by the export parameter.
 func DependencyTagForSdkMemberType(memberType SdkMemberType, export bool) SdkMemberTypeDependencyTag {
-	return &sdkMemberDependencyTag{memberType: memberType, export: export}
+	return &sdkMemberTypeDependencyTag{memberType: memberType, export: export}
 }
 
 // Interface that must be implemented for every type that can be a member of an
@@ -475,7 +475,7 @@
 	// properties. The dependencies must be added with the supplied tag.
 	//
 	// The BottomUpMutatorContext provided is for the SDK module.
-	AddDependencies(mctx BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string)
+	AddDependencies(ctx SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string)
 
 	// Return true if the supplied module is an instance of this member type.
 	//
@@ -529,6 +529,12 @@
 	CreateVariantPropertiesStruct() SdkMemberProperties
 }
 
+// SdkDependencyContext provides access to information needed by the SdkMemberType.AddDependencies()
+// implementations.
+type SdkDependencyContext interface {
+	BottomUpMutatorContext
+}
+
 // Base type for SdkMemberType implementations.
 type SdkMemberTypeBase struct {
 	PropertyName string
@@ -570,9 +576,6 @@
 type SdkMemberTypesRegistry struct {
 	// The list of types sorted by property name.
 	list []SdkMemberType
-
-	// The key that uniquely identifies this registry instance.
-	key OnceKey
 }
 
 func (r *SdkMemberTypesRegistry) copyAndAppend(memberType SdkMemberType) *SdkMemberTypesRegistry {
@@ -592,18 +595,9 @@
 		return t1.SdkPropertyName() < t2.SdkPropertyName()
 	})
 
-	// Generate a key that identifies the slice of SdkMemberTypes by joining the property names
-	// from all the SdkMemberType .
-	var properties []string
-	for _, t := range list {
-		properties = append(properties, t.SdkPropertyName())
-	}
-	key := NewOnceKey(strings.Join(properties, "|"))
-
 	// Create a new registry so the pointer uniquely identifies the set of registered types.
 	return &SdkMemberTypesRegistry{
 		list: list,
-		key:  key,
 	}
 }
 
@@ -616,8 +610,10 @@
 	return NewCustomOnceKey(r)
 }
 
-// The set of registered SdkMemberTypes, one for sdk module and one for module_exports.
+// The set of registered SdkMemberTypes for module_exports modules.
 var ModuleExportsMemberTypes = &SdkMemberTypesRegistry{}
+
+// The set of registered SdkMemberTypes for sdk modules.
 var SdkMemberTypes = &SdkMemberTypesRegistry{}
 
 // Register an SdkMemberType object to allow them to be used in the sdk and sdk_snapshot module
diff --git a/android/variable.go b/android/variable.go
index 5cf9aa8..a1af527 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -46,6 +46,10 @@
 			Java_resource_dirs []string
 		}
 
+		Platform_sdk_extension_version struct {
+			Cmd *string
+		}
+
 		// unbundled_build is a catch-all property to annotate modules that don't build in one or
 		// more unbundled branches, usually due to dependencies missing from the manifest.
 		Unbundled_build struct {
@@ -108,6 +112,8 @@
 			Static_libs       []string
 			Whole_static_libs []string
 			Shared_libs       []string
+
+			Cmdline []string
 		}
 
 		// eng is true for -eng builds, and can be used to turn on additionaly heavyweight debugging
@@ -170,6 +176,7 @@
 	Platform_sdk_codename                     *string  `json:",omitempty"`
 	Platform_sdk_version_or_codename          *string  `json:",omitempty"`
 	Platform_sdk_final                        *bool    `json:",omitempty"`
+	Platform_sdk_extension_version            *int     `json:",omitempty"`
 	Platform_version_active_codenames         []string `json:",omitempty"`
 	Platform_vndk_version                     *string  `json:",omitempty"`
 	Platform_systemsdk_versions               []string `json:",omitempty"`
diff --git a/apex/apex_test.go b/apex/apex_test.go
index dc66ae6..5c7c90b 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -5129,6 +5129,12 @@
 		// find the dex boot jar in it. We either need to disable the source libfoo
 		// or make the prebuilt libfoo preferred.
 		testDexpreoptWithApexes(t, bp, "module libfoo does not provide a dex boot jar", preparer, fragment)
+		// dexbootjar check is skipped if AllowMissingDependencies is true
+		preparerAllowMissingDeps := android.GroupFixturePreparers(
+			preparer,
+			android.PrepareForTestWithAllowMissingDependencies,
+		)
+		testDexpreoptWithApexes(t, bp, "", preparerAllowMissingDeps, fragment)
 	})
 
 	t.Run("prebuilt library preferred with source", func(t *testing.T) {
diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go
index ebf89ea..71e0cd8 100644
--- a/cc/binary_sdk_member.go
+++ b/cc/binary_sdk_member.go
@@ -38,16 +38,16 @@
 	android.SdkMemberTypeBase
 }
 
-func (mt *binarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	targets := mctx.MultiTargets()
+func (mt *binarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	targets := ctx.MultiTargets()
 	for _, bin := range names {
 		for _, target := range targets {
 			variations := target.Variations()
-			if mctx.Device() {
+			if ctx.Device() {
 				variations = append(variations,
 					blueprint.Variation{Mutator: "image", Variation: android.CoreVariation})
 			}
-			mctx.AddFarVariationDependencies(variations, dependencyTag, bin)
+			ctx.AddFarVariationDependencies(variations, dependencyTag, bin)
 		}
 	}
 }
diff --git a/cc/builder.go b/cc/builder.go
index 842ce85..748377b 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -205,11 +205,13 @@
 			Labels:       map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
 			ExecStrategy: "${config.REClangTidyExecStrategy}",
 			Inputs:       []string{"$in"},
-			// OutputFile here is $in for remote-execution since its possible that
-			// clang-tidy modifies the given input file itself and $out refers to the
-			// ".tidy" file generated for ninja-dependency reasons.
-			OutputFiles: []string{"$in"},
-			Platform:    map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
+			// Although clang-tidy has an option to "fix" source files, that feature is hardly useable
+			// under parallel compilation and RBE. So we assume no OutputFiles here.
+			// The clang-tidy fix option is best run locally in single thread.
+			// Copying source file back to local caused two problems:
+			// (1) New timestamps trigger clang and clang-tidy compilations again.
+			// (2) Changing source files caused concurrent clang or clang-tidy jobs to crash.
+			Platform: map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
 		}, []string{"cFlags", "tidyFlags"}, []string{})
 
 	_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
@@ -384,9 +386,6 @@
 
 	systemIncludeFlags string
 
-	// True if static libraries should be grouped (using `-Wl,--start-group` and `-Wl,--end-group`).
-	groupStaticLibs bool
-
 	proto            android.ProtoFlags
 	protoC           bool // If true, compile protos as `.c` files. Otherwise, output as `.cc`.
 	protoOptionsFile bool // If true, output a proto options file.
@@ -646,7 +645,7 @@
 				OrderOnly: pathDeps,
 				Args: map[string]string{
 					"cFlags":    moduleToolingFlags,
-					"tidyFlags": flags.tidyFlags,
+					"tidyFlags": config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags),
 				},
 			})
 		}
@@ -752,13 +751,7 @@
 		}
 	}
 
-	if flags.groupStaticLibs && !ctx.Darwin() && len(staticLibs) > 0 {
-		libFlagsList = append(libFlagsList, "-Wl,--start-group")
-	}
 	libFlagsList = append(libFlagsList, staticLibs.Strings()...)
-	if flags.groupStaticLibs && !ctx.Darwin() && len(staticLibs) > 0 {
-		libFlagsList = append(libFlagsList, "-Wl,--end-group")
-	}
 
 	if groupLate && !ctx.Darwin() && len(lateStaticLibs) > 0 {
 		libFlagsList = append(libFlagsList, "-Wl,--start-group")
diff --git a/cc/cc.go b/cc/cc.go
index 9c1f559..b0c0299 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -217,8 +217,6 @@
 
 	// True if .s files should be processed with the c preprocessor.
 	AssemblerWithCpp bool
-	// True if static libraries should be grouped (using `-Wl,--start-group` and `-Wl,--end-group`).
-	GroupStaticLibs bool
 
 	proto            android.ProtoFlags
 	protoC           bool // Whether to use C instead of C++
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index c4563e2..cf13503 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -106,6 +106,7 @@
 
 const tidyDefault = "${config.TidyDefaultGlobalChecks}"
 const tidyExternalVendor = "${config.TidyExternalVendorChecks}"
+const tidyDefaultNoAnalyzer = "${config.TidyDefaultGlobalChecks},-clang-analyzer-*"
 
 // This is a map of local path prefixes to the set of default clang-tidy checks
 // to be used.
@@ -139,3 +140,17 @@
 	}
 	return tidyDefault
 }
+
+func TidyFlagsForSrcFile(srcFile android.Path, flags string) string {
+	// Disable clang-analyzer-* checks globally for generated source files
+	// because some of them are too huge. Local .bp files can add wanted
+	// clang-analyzer checks through the tidy_checks property.
+	// Need to do this patch per source file, because some modules
+	// have both generated and organic source files.
+	if _, ok := srcFile.(android.WritablePath); ok {
+		if strings.Contains(flags, tidyDefault) {
+			return strings.ReplaceAll(flags, tidyDefault, tidyDefaultNoAnalyzer)
+		}
+	}
+	return flags
+}
diff --git a/cc/fuzz.go b/cc/fuzz.go
index fbef12b..83f0037 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -123,7 +123,7 @@
 // that should be installed in the fuzz target output directories. This function
 // returns true, unless:
 //  - The module is not an installable shared library, or
-//  - The module is a header, stub, or vendor-linked library, or
+//  - The module is a header or stub, or
 //  - The module is a prebuilt and its source is available, or
 //  - The module is a versioned member of an SDK snapshot.
 func isValidSharedDependency(dependency android.Module) bool {
@@ -141,11 +141,6 @@
 		return false
 	}
 
-	if linkable.UseVndk() {
-		// Discard vendor linked libraries.
-		return false
-	}
-
 	if lib := moduleLibraryInterface(dependency); lib != nil && lib.buildStubs() && linkable.CcLibrary() {
 		// Discard stubs libs (only CCLibrary variants). Prebuilt libraries should not
 		// be excluded on the basis of they're not CCLibrary()'s.
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 9010a1a..1866ff3 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -74,8 +74,8 @@
 	linkTypes []string
 }
 
-func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	targets := mctx.MultiTargets()
+func (mt *librarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	targets := ctx.MultiTargets()
 	for _, lib := range names {
 		for _, target := range targets {
 			name, version := StubsLibNameAndVersion(lib)
@@ -83,21 +83,21 @@
 				version = "latest"
 			}
 			variations := target.Variations()
-			if mctx.Device() {
+			if ctx.Device() {
 				variations = append(variations,
 					blueprint.Variation{Mutator: "image", Variation: android.CoreVariation})
 			}
 			if mt.linkTypes == nil {
-				mctx.AddFarVariationDependencies(variations, dependencyTag, name)
+				ctx.AddFarVariationDependencies(variations, dependencyTag, name)
 			} else {
 				for _, linkType := range mt.linkTypes {
 					libVariations := append(variations,
 						blueprint.Variation{Mutator: "link", Variation: linkType})
-					if mctx.Device() && linkType == "shared" {
+					if ctx.Device() && linkType == "shared" {
 						libVariations = append(libVariations,
 							blueprint.Variation{Mutator: "version", Variation: version})
 					}
-					mctx.AddFarVariationDependencies(libVariations, dependencyTag, name)
+					ctx.AddFarVariationDependencies(libVariations, dependencyTag, name)
 				}
 			}
 		}
diff --git a/cc/linker.go b/cc/linker.go
index 8671dec..0d612b5 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -86,8 +86,7 @@
 	// compiling crt or libc.
 	Nocrt *bool `android:"arch_variant"`
 
-	// group static libraries.  This can resolve missing symbols issues with interdependencies
-	// between static libraries, but it is generally better to order them correctly instead.
+	// deprecated and ignored because lld makes it unnecessary. See b/189475744.
 	Group_static_libs *bool `android:"arch_variant"`
 
 	// list of modules that should be installed with this module.  This is similar to 'required'
@@ -543,10 +542,6 @@
 
 	flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ToolchainLdflags())
 
-	if Bool(linker.Properties.Group_static_libs) {
-		flags.GroupStaticLibs = true
-	}
-
 	// Version_script is not needed when linking stubs lib where the version
 	// script is created from the symbol map file.
 	if !linker.dynamicProperties.BuildStubs {
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 51cdddf..704b03a 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -45,11 +45,17 @@
 	abidw = pctx.AndroidStaticRule("abidw",
 		blueprint.RuleParams{
 			Command: "$abidw --type-id-style hash --no-corpus-path " +
-				"--no-show-locs --no-comp-dir-path -w $symbolList $in | " +
-				"$abitidy --all -o $out",
-			CommandDeps: []string{"$abitidy", "$abidw"},
+				"--no-show-locs --no-comp-dir-path -w $symbolList " +
+				"$in --out-file $out",
+			CommandDeps: []string{"$abidw"},
 		}, "symbolList")
 
+	abitidy = pctx.AndroidStaticRule("abitidy",
+		blueprint.RuleParams{
+			Command:     "$abitidy --all -i $in -o $out",
+			CommandDeps: []string{"$abitidy"},
+		})
+
 	abidiff = pctx.AndroidStaticRule("abidiff",
 		blueprint.RuleParams{
 			// Need to create *some* output for ninja. We don't want to use tee
@@ -313,19 +319,28 @@
 
 func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) {
 	implementationLibrary := this.findImplementationLibrary(ctx)
-	this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
+	abiRawPath := getNdkAbiDumpInstallBase(ctx).Join(ctx,
 		this.apiLevel.String(), ctx.Arch().ArchType.String(),
-		this.libraryName(ctx), "abi.xml")
+		this.libraryName(ctx), "abi.raw.xml")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        abidw,
 		Description: fmt.Sprintf("abidw %s", implementationLibrary),
-		Output:      this.abiDumpPath,
 		Input:       implementationLibrary,
+		Output:      abiRawPath,
 		Implicit:    symbolList,
 		Args: map[string]string{
 			"symbolList": symbolList.String(),
 		},
 	})
+	this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
+		this.apiLevel.String(), ctx.Arch().ArchType.String(),
+		this.libraryName(ctx), "abi.xml")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        abitidy,
+		Description: fmt.Sprintf("abitidy %s", implementationLibrary),
+		Input:       abiRawPath,
+		Output:      this.abiDumpPath,
+	})
 }
 
 func findNextApiLevel(ctx ModuleContext, apiLevel android.ApiLevel) *android.ApiLevel {
diff --git a/cc/util.go b/cc/util.go
index 9bba876..88b0aba 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -91,7 +91,6 @@
 		systemIncludeFlags: strings.Join(in.SystemIncludeFlags, " "),
 
 		assemblerWithCpp: in.AssemblerWithCpp,
-		groupStaticLibs:  in.GroupStaticLibs,
 
 		proto:            in.proto,
 		protoC:           in.protoC,
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 2846387..fa63b46 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -20,6 +20,7 @@
 	"flag"
 	"fmt"
 	"io"
+	"io/ioutil"
 	"log"
 	"os"
 	"os/exec"
@@ -76,6 +77,36 @@
 	return nil
 }
 
+const errorLeadingLines = 20
+const errorTrailingLines = 20
+
+func errMsgFromLog(filename string) string {
+	if filename == "" {
+		return ""
+	}
+
+	data, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return ""
+	}
+
+	lines := strings.Split(strings.TrimSpace(string(data)), "\n")
+	if len(lines) > errorLeadingLines+errorTrailingLines+1 {
+		lines[errorLeadingLines] = fmt.Sprintf("... skipping %d lines ...",
+			len(lines)-errorLeadingLines-errorTrailingLines)
+
+		lines = append(lines[:errorLeadingLines+1],
+			lines[len(lines)-errorTrailingLines:]...)
+	}
+	var buf strings.Builder
+	for _, line := range lines {
+		buf.WriteString("> ")
+		buf.WriteString(line)
+		buf.WriteString("\n")
+	}
+	return buf.String()
+}
+
 // TODO(b/70370883): This tool uses a lot of open files -- over the default
 // soft limit of 1024 on some systems. So bump up to the hard limit until I fix
 // the algorithm.
@@ -170,6 +201,23 @@
 	}
 }
 
+func outDirBase() string {
+	outDirBase := os.Getenv("OUT_DIR")
+	if outDirBase == "" {
+		return "out"
+	} else {
+		return outDirBase
+	}
+}
+
+func distDir(outDir string) string {
+	if distDir := os.Getenv("DIST_DIR"); distDir != "" {
+		return filepath.Clean(distDir)
+	} else {
+		return filepath.Join(outDir, "dist")
+	}
+}
+
 func main() {
 	stdio := terminal.StdioImpl{}
 
@@ -177,6 +225,12 @@
 	log := logger.New(output)
 	defer log.Cleanup()
 
+	for _, v := range os.Environ() {
+		log.Println("Environment: " + v)
+	}
+
+	log.Printf("Argv: %v\n", os.Args)
+
 	flag.Parse()
 
 	_, cancel := context.WithCancel(context.Background())
@@ -208,13 +262,7 @@
 		if !*incremental {
 			name += "-" + time.Now().Format("20060102150405")
 		}
-
-		outDirBase := os.Getenv("OUT_DIR")
-		if outDirBase == "" {
-			outDirBase = "out"
-		}
-
-		outputDir = filepath.Join(outDirBase, name)
+		outputDir = filepath.Join(outDirBase(), name)
 	}
 
 	log.Println("Output directory:", outputDir)
@@ -231,11 +279,13 @@
 
 	var configLogsDir string
 	if *alternateResultDir {
-		configLogsDir = filepath.Join(outputDir, "dist/logs")
+		configLogsDir = filepath.Join(distDir(outDirBase()), "logs")
 	} else {
 		configLogsDir = outputDir
 	}
 
+	log.Println("Logs dir: " + configLogsDir)
+
 	os.MkdirAll(configLogsDir, 0777)
 	log.SetOutput(filepath.Join(configLogsDir, "soong.log"))
 	trace.SetOutput(filepath.Join(configLogsDir, "build.trace"))
@@ -348,10 +398,11 @@
 			FileArgs: []zip.FileArg{
 				{GlobDir: logsDir, SourcePrefixToStrip: logsDir},
 			},
-			OutputFilePath:   filepath.Join(outputDir, "dist/logs.zip"),
+			OutputFilePath:   filepath.Join(distDir(outDirBase()), "logs.zip"),
 			NumParallelJobs:  runtime.NumCPU(),
 			CompressionLevel: 5,
 		}
+		log.Printf("Logs zip: %v\n", args.OutputFilePath)
 		if err := zip.Zip(args); err != nil {
 			log.Fatalf("Error zipping logs: %v", err)
 		}
@@ -424,10 +475,6 @@
 		args = append(args, "--soong-only")
 	}
 
-	if *alternateResultDir {
-		args = append(args, "dist")
-	}
-
 	cmd := exec.Command(mpctx.SoongUi, args...)
 	cmd.Stdout = consoleLogWriter
 	cmd.Stderr = consoleLogWriter
@@ -439,6 +486,11 @@
 		"TARGET_BUILD_APPS=",
 		"TARGET_BUILD_UNBUNDLED=")
 
+	if *alternateResultDir {
+		cmd.Env = append(cmd.Env,
+			"DIST_DIR="+filepath.Join(distDir(outDirBase()), "products/"+product))
+	}
+
 	action := &status.Action{
 		Description: product,
 		Outputs:     []string{product},
@@ -459,9 +511,17 @@
 			}
 		}
 	}
+	var errOutput string
+	if err == nil {
+		errOutput = ""
+	} else {
+		errOutput = errMsgFromLog(consoleLogPath)
+	}
+
 	mpctx.Status.FinishAction(status.ActionResult{
 		Action: action,
 		Error:  err,
+		Output: errOutput,
 	})
 }
 
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index 9f09224..703a875 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -16,7 +16,7 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-bootstrap_go_binary {
+blueprint_go_binary {
     name: "soong_build",
     deps: [
         "blueprint",
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index beda184..3e724ee 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -35,7 +35,7 @@
 
 var (
 	topDir           string
-	outDir           string
+	soongOutDir      string
 	availableEnvFile string
 	usedEnvFile      string
 
@@ -55,33 +55,34 @@
 func init() {
 	// Flags that make sense in every mode
 	flag.StringVar(&topDir, "top", "", "Top directory of the Android source tree")
-	flag.StringVar(&outDir, "out", "", "Soong output directory (usually $TOP/out/soong)")
+	flag.StringVar(&soongOutDir, "soong_out", "", "Soong output directory (usually $TOP/out/soong)")
 	flag.StringVar(&availableEnvFile, "available_env", "", "File containing available environment variables")
 	flag.StringVar(&usedEnvFile, "used_env", "", "File containing used environment variables")
+	flag.StringVar(&globFile, "globFile", "build-globs.ninja", "the Ninja file of globs to output")
+	flag.StringVar(&globListDir, "globListDir", "", "the directory containing the glob list files")
+	flag.StringVar(&cmdlineArgs.OutDir, "out", "", "the ninja builddir directory")
+	flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse")
 
 	// Debug flags
 	flag.StringVar(&delveListen, "delve_listen", "", "Delve port to listen on for debugging")
 	flag.StringVar(&delvePath, "delve_path", "", "Path to Delve. Only used if --delve_listen is set")
+	flag.StringVar(&cmdlineArgs.Cpuprofile, "cpuprofile", "", "write cpu profile to file")
+	flag.StringVar(&cmdlineArgs.TraceFile, "trace", "", "write trace to file")
+	flag.StringVar(&cmdlineArgs.Memprofile, "memprofile", "", "write memory profile to file")
+	flag.BoolVar(&cmdlineArgs.NoGC, "nogc", false, "turn off GC for debugging")
 
 	// Flags representing various modes soong_build can run in
 	flag.StringVar(&moduleGraphFile, "module_graph_file", "", "JSON module graph file to output")
 	flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
 	flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
 	flag.StringVar(&bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit")
-
 	flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
-	flag.StringVar(&globFile, "globFile", "build-globs.ninja", "the Ninja file of globs to output")
-	flag.StringVar(&globListDir, "globListDir", "", "the directory containing the glob list files")
-	flag.StringVar(&cmdlineArgs.BuildDir, "b", ".", "the build output directory")
-	flag.StringVar(&cmdlineArgs.NinjaBuildDir, "n", "", "the ninja builddir directory")
-	flag.StringVar(&cmdlineArgs.Cpuprofile, "cpuprofile", "", "write cpu profile to file")
-	flag.StringVar(&cmdlineArgs.TraceFile, "trace", "", "write trace to file")
-	flag.StringVar(&cmdlineArgs.Memprofile, "memprofile", "", "write memory profile to file")
-	flag.BoolVar(&cmdlineArgs.NoGC, "nogc", false, "turn off GC for debugging")
+	flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
+
+	// Flags that probably shouldn't be flags of soong_build but we haven't found
+	// the time to remove them yet
 	flag.BoolVar(&cmdlineArgs.RunGoTests, "t", false, "build and run go tests during bootstrap")
 	flag.BoolVar(&cmdlineArgs.UseValidations, "use-validations", false, "use validations to depend on go tests")
-	flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse")
-	flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
 }
 
 func newNameResolver(config android.Config) *android.NameResolver {
@@ -111,8 +112,8 @@
 	return ctx
 }
 
-func newConfig(outDir string, availableEnv map[string]string) android.Config {
-	configuration, err := android.NewConfig(outDir, cmdlineArgs.ModuleListFile, availableEnv)
+func newConfig(cmdlineArgs bootstrap.Args, outDir string, availableEnv map[string]string) android.Config {
+	configuration, err := android.NewConfig(cmdlineArgs, outDir, availableEnv)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
@@ -138,13 +139,13 @@
 		os.Exit(1)
 	}
 	// Second pass: Full analysis, using the bazel command results. Output ninja file.
-	secondConfig, err := android.ConfigForAdditionalRun(configuration)
+	secondArgs = cmdlineArgs
+	secondConfig, err := android.ConfigForAdditionalRun(secondArgs, configuration)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
 	}
 	secondCtx := newContext(secondConfig, true)
-	secondArgs = cmdlineArgs
 	ninjaDeps := bootstrap.RunBlueprint(secondArgs, secondCtx.Context, secondConfig)
 	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
@@ -296,7 +297,7 @@
 
 	availableEnv := parseAvailableEnv()
 
-	configuration := newConfig(outDir, availableEnv)
+	configuration := newConfig(cmdlineArgs, soongOutDir, availableEnv)
 	extraNinjaDeps := []string{
 		configuration.ProductVariablesFileName,
 		usedEnvFile,
@@ -503,8 +504,8 @@
 		"bazel-" + filepath.Base(topDir),
 	}
 
-	if cmdlineArgs.NinjaBuildDir[0] != '/' {
-		excludes = append(excludes, cmdlineArgs.NinjaBuildDir)
+	if cmdlineArgs.OutDir[0] != '/' {
+		excludes = append(excludes, cmdlineArgs.OutDir)
 	}
 
 	existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index 7dbe74c..ba05d94 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -87,7 +87,9 @@
 		usage("--module configuration file is required")
 	}
 
-	ctx := &builderContext{android.NullConfig(*outDir)}
+	// NOTE: duplicating --out_dir here is incorrect (one should be the another
+	// plus "/soong" but doing so apparently breaks dexpreopt
+	ctx := &builderContext{android.NullConfig(*outDir, *outDir)}
 
 	globalSoongConfigData, err := ioutil.ReadFile(*globalSoongConfigPath)
 	if err != nil {
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 29a8a39..73d807d 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -60,8 +60,8 @@
 	// https://source.android.com/devices/bootloader/partitions/vendor-boot-partitions
 	Vendor_boot *bool
 
-	// Optional kernel commandline
-	Cmdline *string `android:"arch_variant"`
+	// Optional kernel commandline arguments
+	Cmdline []string `android:"arch_variant"`
 
 	// File that contains bootconfig parameters. This can be set only when `vendor_boot` is true
 	// and `header_version` is greater than or equal to 4.
@@ -152,7 +152,7 @@
 	dtb := android.PathForModuleSrc(ctx, dtbName)
 	cmd.FlagWithInput("--dtb ", dtb)
 
-	cmdline := proptools.String(b.properties.Cmdline)
+	cmdline := strings.Join(b.properties.Cmdline, " ")
 	if cmdline != "" {
 		flag := "--cmdline "
 		if vendor {
diff --git a/java/app_import.go b/java/app_import.go
index b5a6084..3e5f972 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -204,9 +204,9 @@
 		return false
 	}
 
-	// Uncompress dex in APKs of privileged apps
-	if ctx.Config().UncompressPrivAppDex() && a.Privileged() {
-		return true
+	// Uncompress dex in APKs of priv-apps if and only if DONT_UNCOMPRESS_PRIV_APPS_DEXS is false.
+	if a.Privileged() {
+		return ctx.Config().UncompressPrivAppDex()
 	}
 
 	return shouldUncompressDex(ctx, &a.dexpreopter)
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 024a3df..efa52c1 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -15,6 +15,7 @@
 package java
 
 import (
+	"fmt"
 	"reflect"
 	"regexp"
 	"strings"
@@ -656,3 +657,74 @@
 		}
 	}
 }
+
+func TestAndroidTestImport_UncompressDex(t *testing.T) {
+	testCases := []struct {
+		name string
+		bp   string
+	}{
+		{
+			name: "normal",
+			bp: `
+				android_app_import {
+					name: "foo",
+					presigned: true,
+					apk: "prebuilts/apk/app.apk",
+				}
+			`,
+		},
+		{
+			name: "privileged",
+			bp: `
+				android_app_import {
+					name: "foo",
+					presigned: true,
+					privileged: true,
+					apk: "prebuilts/apk/app.apk",
+				}
+			`,
+		},
+	}
+
+	test := func(t *testing.T, bp string, unbundled bool, dontUncompressPrivAppDexs bool) {
+		t.Helper()
+
+		result := android.GroupFixturePreparers(
+			prepareForJavaTest,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				if unbundled {
+					variables.Unbundled_build = proptools.BoolPtr(true)
+				}
+				variables.UncompressPrivAppDex = proptools.BoolPtr(!dontUncompressPrivAppDexs)
+			}),
+		).RunTestWithBp(t, bp)
+
+		foo := result.ModuleForTests("foo", "android_common")
+		actual := foo.MaybeRule("uncompress-dex").Rule != nil
+
+		expect := !unbundled
+		if strings.Contains(bp, "privileged: true") {
+			if dontUncompressPrivAppDexs {
+				expect = false
+			} else {
+				// TODO(b/194504107): shouldn't priv-apps be always uncompressed unless
+				// DONT_UNCOMPRESS_PRIV_APPS_DEXS is true (regardless of unbundling)?
+				// expect = true
+			}
+		}
+
+		android.AssertBoolEquals(t, "uncompress dex", expect, actual)
+	}
+
+	for _, unbundled := range []bool{false, true} {
+		for _, dontUncompressPrivAppDexs := range []bool{false, true} {
+			for _, tt := range testCases {
+				name := fmt.Sprintf("%s,unbundled:%t,dontUncompressPrivAppDexs:%t",
+					tt.name, unbundled, dontUncompressPrivAppDexs)
+				t.Run(name, func(t *testing.T) {
+					test(t, tt.bp, unbundled, dontUncompressPrivAppDexs)
+				})
+			}
+		}
+	}
+}
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 7577316..f7561b4 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -718,8 +718,8 @@
 	android.SdkMemberTypeBase
 }
 
-func (b *bootclasspathFragmentMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (b *bootclasspathFragmentMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (b *bootclasspathFragmentMemberType) IsInstance(module android.Module) bool {
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 1019b4c..946092c 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -500,7 +500,11 @@
 		dst := dstBootJarsByModule[name]
 
 		if src == nil {
-			ctx.ModuleErrorf("module %s does not provide a dex boot jar", name)
+			if !ctx.Config().AllowMissingDependencies() {
+				ctx.ModuleErrorf("module %s does not provide a dex boot jar", name)
+			} else {
+				ctx.AddMissingDependencies([]string{name})
+			}
 		} else if dst == nil {
 			ctx.ModuleErrorf("module %s is not part of the boot configuration", name)
 		} else {
diff --git a/java/java.go b/java/java.go
index 4c2ca9b..1a052b4 100644
--- a/java/java.go
+++ b/java/java.go
@@ -574,8 +574,8 @@
 	copyEverythingToSnapshot = false
 )
 
-func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (mt *librarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (mt *librarySdkMemberType) IsInstance(module android.Module) bool {
@@ -875,8 +875,8 @@
 	android.SdkMemberTypeBase
 }
 
-func (mt *testSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (mt *testSdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (mt *testSdkMemberType) IsInstance(module android.Module) bool {
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 712c2a2..0d8ebac 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -134,8 +134,8 @@
 	android.SdkMemberTypeBase
 }
 
-func (b *compatConfigMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (b *compatConfigMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (b *compatConfigMemberType) IsInstance(module android.Module) bool {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index c50e077..ce8f179 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -2471,8 +2471,8 @@
 	android.SdkMemberTypeBase
 }
 
-func (s *sdkLibrarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (s *sdkLibrarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (s *sdkLibrarySdkMemberType) IsInstance(module android.Module) bool {
diff --git a/java/system_modules.go b/java/system_modules.go
index d0dc74a..fec8eba 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -245,8 +245,8 @@
 	android.SdkMemberTypeBase
 }
 
-func (mt *systemModulesSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (mt *systemModulesSdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (mt *systemModulesSdkMemberType) IsInstance(module android.Module) bool {
diff --git a/scripts/hiddenapi/generate_hiddenapi_lists.py b/scripts/hiddenapi/generate_hiddenapi_lists.py
index 5ab93d1..35e0948 100755
--- a/scripts/hiddenapi/generate_hiddenapi_lists.py
+++ b/scripts/hiddenapi/generate_hiddenapi_lists.py
@@ -16,8 +16,6 @@
 """Generate API lists for non-SDK API enforcement."""
 import argparse
 from collections import defaultdict, namedtuple
-import functools
-import os
 import re
 import sys
 
@@ -60,15 +58,15 @@
 # For example, the max-target-P list is checked in as it was in P,
 # but signatures have changes since then. The flag instructs this
 # script to skip any entries which do not exist any more.
-FLAG_IGNORE_CONFLICTS = "ignore-conflicts"
+FLAG_IGNORE_CONFLICTS = 'ignore-conflicts'
 
 # Option specified after one of FLAGS_API_LIST to express that all
 # apis within a given set of packages should be assign the given flag.
-FLAG_PACKAGES = "packages"
+FLAG_PACKAGES = 'packages'
 
 # Option specified after one of FLAGS_API_LIST to indicate an extra
 # tag that should be added to the matching APIs.
-FLAG_TAG = "tag"
+FLAG_TAG = 'tag'
 
 # Regex patterns of fields/methods used in serialization. These are
 # considered public API despite being hidden.
@@ -84,24 +82,30 @@
 
 # Single regex used to match serialization API. It combines all the
 # SERIALIZATION_PATTERNS into a single regular expression.
-SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$')
+SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) +
+                                 r')$')
 
 # Predicates to be used with filter_apis.
-HAS_NO_API_LIST_ASSIGNED = lambda api, flags: not FLAGS_API_LIST_SET.intersection(flags)
+HAS_NO_API_LIST_ASSIGNED = \
+    lambda api,flags: not FLAGS_API_LIST_SET.intersection(flags)
+
 IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
 
 
 class StoreOrderedOptions(argparse.Action):
-    """An argparse action that stores a number of option arguments in the order that
-    they were specified.
+    """An argparse action that stores a number of option arguments in the order
+
+    that they were specified.
     """
-    def __call__(self, parser, args, values, option_string = None):
+
+    def __call__(self, parser, args, values, option_string=None):
         items = getattr(args, self.dest, None)
         if items is None:
             items = []
         items.append([option_string.lstrip('-'), values])
         setattr(args, self.dest, items)
 
+
 def get_args():
     """Parses command line arguments.
 
@@ -110,22 +114,43 @@
     """
     parser = argparse.ArgumentParser()
     parser.add_argument('--output', required=True)
-    parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE',
+    parser.add_argument(
+        '--csv',
+        nargs='*',
+        default=[],
+        metavar='CSV_FILE',
         help='CSV files to be merged into output')
 
     for flag in ALL_FLAGS:
-        parser.add_argument('--' + flag, dest='ordered_flags', metavar='TXT_FILE',
-            action=StoreOrderedOptions, help='lists of entries with flag "' + flag + '"')
-    parser.add_argument('--' + FLAG_IGNORE_CONFLICTS, dest='ordered_flags', nargs=0,
-        action=StoreOrderedOptions, help='Indicates that only known and otherwise unassigned '
-        'entries should be assign the given flag. Must follow a list of entries and applies '
-        'to the preceding such list.')
-    parser.add_argument('--' + FLAG_PACKAGES, dest='ordered_flags', nargs=0,
-        action=StoreOrderedOptions, help='Indicates that the previous list of entries '
-        'is a list of packages. All members in those packages will be given the flag. '
-        'Must follow a list of entries and applies to the preceding such list.')
-    parser.add_argument('--' + FLAG_TAG, dest='ordered_flags', nargs=1,
-        action=StoreOrderedOptions, help='Adds an extra tag to the previous list of entries. '
+        parser.add_argument(
+            '--' + flag,
+            dest='ordered_flags',
+            metavar='TXT_FILE',
+            action=StoreOrderedOptions,
+            help='lists of entries with flag "' + flag + '"')
+    parser.add_argument(
+        '--' + FLAG_IGNORE_CONFLICTS,
+        dest='ordered_flags',
+        nargs=0,
+        action=StoreOrderedOptions,
+        help='Indicates that only known and otherwise unassigned '
+        'entries should be assign the given flag. Must follow a list of '
+        'entries and applies to the preceding such list.')
+    parser.add_argument(
+        '--' + FLAG_PACKAGES,
+        dest='ordered_flags',
+        nargs=0,
+        action=StoreOrderedOptions,
+        help='Indicates that the previous list of entries '
+        'is a list of packages. All members in those packages will be given '
+        'the flag. Must follow a list of entries and applies to the preceding '
+        'such list.')
+    parser.add_argument(
+        '--' + FLAG_TAG,
+        dest='ordered_flags',
+        nargs=1,
+        action=StoreOrderedOptions,
+        help='Adds an extra tag to the previous list of entries. '
         'Must follow a list of entries and applies to the preceding such list.')
 
     return parser.parse_args()
@@ -143,9 +168,9 @@
         Lines of the file as a list of string.
     """
     with open(filename, 'r') as f:
-        lines = f.readlines();
-    lines = filter(lambda line: not line.startswith('#'), lines)
-    lines = map(lambda line: line.strip(), lines)
+        lines = f.readlines()
+    lines = [line for line in lines if not line.startswith('#')]
+    lines = [line.strip() for line in lines]
     return set(lines)
 
 
@@ -156,7 +181,7 @@
         filename (string): Path to the file to be writing into.
         lines (list): List of strings to write into the file.
     """
-    lines = map(lambda line: line + '\n', lines)
+    lines = [line + '\n' for line in lines]
     with open(filename, 'w') as f:
         f.writelines(lines)
 
@@ -170,17 +195,19 @@
     Returns:
         The package name of the class containing the field/method.
     """
-    full_class_name = signature.split(";->")[0]
+    full_class_name = signature.split(';->')[0]
     # Example: Landroid/hardware/radio/V1_2/IRadio$Proxy
-    if (full_class_name[0] != "L"):
-        raise ValueError("Expected to start with 'L': %s" % full_class_name)
+    if full_class_name[0] != 'L':
+        raise ValueError("Expected to start with 'L': %s"
+                         % full_class_name)
     full_class_name = full_class_name[1:]
     # If full_class_name doesn't contain '/', then package_name will be ''.
-    package_name = full_class_name.rpartition("/")[0]
+    package_name = full_class_name.rpartition('/')[0]
     return package_name.replace('/', '.')
 
 
 class FlagsDict:
+
     def __init__(self):
         self._dict_keyset = set()
         self._dict = defaultdict(set)
@@ -188,37 +215,43 @@
     def _check_entries_set(self, keys_subset, source):
         assert isinstance(keys_subset, set)
         assert keys_subset.issubset(self._dict_keyset), (
-            "Error: {} specifies signatures not present in code:\n"
-            "{}"
-            "Please visit go/hiddenapi for more information.").format(
-                source, "".join(map(lambda x: "  " + str(x) + "\n", keys_subset - self._dict_keyset)))
+            'Error: {} specifies signatures not present in code:\n'
+            '{}'
+            'Please visit go/hiddenapi for more information.').format(
+                source, ''.join(
+                    ['  ' + str(x) + '\n' for x in
+                     keys_subset - self._dict_keyset]))
 
     def _check_flags_set(self, flags_subset, source):
         assert isinstance(flags_subset, set)
         assert flags_subset.issubset(ALL_FLAGS_SET), (
-            "Error processing: {}\n"
-            "The following flags were not recognized: \n"
-            "{}\n"
-            "Please visit go/hiddenapi for more information.").format(
-                source, "\n".join(flags_subset - ALL_FLAGS_SET))
+            'Error processing: {}\n'
+            'The following flags were not recognized: \n'
+            '{}\n'
+            'Please visit go/hiddenapi for more information.').format(
+                source, '\n'.join(flags_subset - ALL_FLAGS_SET))
 
     def filter_apis(self, filter_fn):
         """Returns APIs which match a given predicate.
 
-        This is a helper function which allows to filter on both signatures (keys) and
-        flags (values). The built-in filter() invokes the lambda only with dict's keys.
+        This is a helper function which allows to filter on both signatures
+        (keys) and
+        flags (values). The built-in filter() invokes the lambda only with
+        dict's keys.
 
         Args:
-            filter_fn : Function which takes two arguments (signature/flags) and returns a boolean.
+            filter_fn : Function which takes two arguments (signature/flags) and
+              returns a boolean.
 
         Returns:
             A set of APIs which match the predicate.
         """
-        return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset))
+        return {x for x in self._dict_keyset if filter_fn(x, self._dict[x])}
 
     def get_valid_subset_of_unassigned_apis(self, api_subset):
-        """Sanitizes a key set input to only include keys which exist in the dictionary
-        and have not been assigned any API list flags.
+        """Sanitizes a key set input to only include keys which exist in the
+
+        dictionary and have not been assigned any API list flags.
 
         Args:
             entries_subset (set/list): Key set to be sanitized.
@@ -227,7 +260,8 @@
             Sanitized key set.
         """
         assert isinstance(api_subset, set)
-        return api_subset.intersection(self.filter_apis(HAS_NO_API_LIST_ASSIGNED))
+        return api_subset.intersection(
+            self.filter_apis(HAS_NO_API_LIST_ASSIGNED))
 
     def generate_csv(self):
         """Constructs CSV entries from a dictionary.
@@ -235,15 +269,16 @@
         Old versions of flags are used to generate the file.
 
         Returns:
-            List of lines comprising a CSV file. See "parse_and_merge_csv" for format description.
+            List of lines comprising a CSV file. See "parse_and_merge_csv" for
+            format description.
         """
         lines = []
         for api in self._dict:
-          flags = sorted(self._dict[api])
-          lines.append(",".join([api] + flags))
+            flags = sorted(self._dict[api])
+            lines.append(','.join([api] + flags))
         return sorted(lines)
 
-    def parse_and_merge_csv(self, csv_lines, source = "<unknown>"):
+    def parse_and_merge_csv(self, csv_lines, source='<unknown>'):
         """Parses CSV entries and merges them into a given dictionary.
 
         The expected CSV format is:
@@ -251,21 +286,20 @@
 
         Args:
             csv_lines (list of strings): Lines read from a CSV file.
-            source (string): Origin of `csv_lines`. Will be printed in error messages.
-
-        Throws:
-            AssertionError if parsed flags are invalid.
+            source (string): Origin of `csv_lines`. Will be printed in error
+              messages.
+        Throws: AssertionError if parsed flags are invalid.
         """
         # Split CSV lines into arrays of values.
-        csv_values = [ line.split(',') for line in csv_lines ]
+        csv_values = [line.split(',') for line in csv_lines]
 
         # Update the full set of API signatures.
-        self._dict_keyset.update([ csv[0] for csv in csv_values ])
+        self._dict_keyset.update([csv[0] for csv in csv_values])
 
         # Check that all flags are known.
         csv_flags = set()
         for csv in csv_values:
-          csv_flags.update(csv[1:])
+            csv_flags.update(csv[1:])
         self._check_flags_set(csv_flags, source)
 
         # Iterate over all CSV lines, find entry in dict and append flags to it.
@@ -275,47 +309,53 @@
                 flags.append(FLAG_SDK)
             self._dict[csv[0]].update(flags)
 
-    def assign_flag(self, flag, apis, source="<unknown>", tag = None):
+    def assign_flag(self, flag, apis, source='<unknown>', tag=None):
         """Assigns a flag to given subset of entries.
 
         Args:
             flag (string): One of ALL_FLAGS.
             apis (set): Subset of APIs to receive the flag.
-            source (string): Origin of `entries_subset`. Will be printed in error messages.
-
-        Throws:
-            AssertionError if parsed API signatures of flags are invalid.
+            source (string): Origin of `entries_subset`. Will be printed in
+              error messages.
+        Throws: AssertionError if parsed API signatures of flags are invalid.
         """
         # Check that all APIs exist in the dict.
         self._check_entries_set(apis, source)
 
         # Check that the flag is known.
-        self._check_flags_set(set([ flag ]), source)
+        self._check_flags_set(set([flag]), source)
 
-        # Iterate over the API subset, find each entry in dict and assign the flag to it.
+        # Iterate over the API subset, find each entry in dict and assign the
+        # flag to it.
         for api in apis:
             self._dict[api].add(flag)
             if tag:
                 self._dict[api].add(tag)
 
 
-FlagFile = namedtuple('FlagFile', ('flag', 'file', 'ignore_conflicts', 'packages', 'tag'))
+FlagFile = namedtuple('FlagFile',
+                      ('flag', 'file', 'ignore_conflicts', 'packages', 'tag'))
+
 
 def parse_ordered_flags(ordered_flags):
     r = []
-    currentflag, file, ignore_conflicts, packages, tag = None, None, False, False, None
+    currentflag, file, ignore_conflicts, packages, tag = None, None, False, \
+        False, None
     for flag_value in ordered_flags:
         flag, value = flag_value[0], flag_value[1]
         if flag in ALL_FLAGS_SET:
             if currentflag:
-                r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
+                r.append(
+                    FlagFile(currentflag, file, ignore_conflicts, packages,
+                             tag))
                 ignore_conflicts, packages, tag = False, False, None
             currentflag = flag
             file = value
         else:
             if currentflag is None:
-                raise argparse.ArgumentError('--%s is only allowed after one of %s' % (
-                    flag, ' '.join(['--%s' % f for f in ALL_FLAGS_SET])))
+                raise argparse.ArgumentError( #pylint: disable=no-value-for-parameter
+                    '--%s is only allowed after one of %s' %
+                    (flag, ' '.join(['--%s' % f for f in ALL_FLAGS_SET])))
             if flag == FLAG_IGNORE_CONFLICTS:
                 ignore_conflicts = True
             elif flag == FLAG_PACKAGES:
@@ -323,13 +363,12 @@
             elif flag == FLAG_TAG:
                 tag = value[0]
 
-
     if currentflag:
         r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
     return r
 
 
-def main(argv):
+def main(argv): #pylint: disable=unused-argument
     # Parse arguments.
     args = vars(get_args())
     flagfiles = parse_ordered_flags(args['ordered_flags'] or [])
@@ -342,7 +381,7 @@
     # contain the full set of APIs. Subsequent additions from text files
     # will be able to detect invalid entries, and/or filter all as-yet
     # unassigned entries.
-    for filename in args["csv"]:
+    for filename in args['csv']:
         flags.parse_and_merge_csv(read_lines(filename), filename)
 
     # Combine inputs which do not require any particular order.
@@ -352,24 +391,28 @@
     # (2) Merge text files with a known flag into the dictionary.
     for info in flagfiles:
         if (not info.ignore_conflicts) and (not info.packages):
-            flags.assign_flag(info.flag, read_lines(info.file), info.file, info.tag)
+            flags.assign_flag(info.flag, read_lines(info.file), info.file,
+                              info.tag)
 
     # Merge text files where conflicts should be ignored.
     # This will only assign the given flag if:
     # (a) the entry exists, and
     # (b) it has not been assigned any other flag.
-    # Because of (b), this must run after all strict assignments have been performed.
+    # Because of (b), this must run after all strict assignments have been
+    # performed.
     for info in flagfiles:
         if info.ignore_conflicts:
-            valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(info.file))
-            flags.assign_flag(info.flag, valid_entries, filename, info.tag)
+            valid_entries = flags.get_valid_subset_of_unassigned_apis(
+                read_lines(info.file))
+            flags.assign_flag(info.flag, valid_entries, filename, info.tag) #pylint: disable=undefined-loop-variable
 
-    # All members in the specified packages will be assigned the appropriate flag.
+    # All members in the specified packages will be assigned the appropriate
+    # flag.
     for info in flagfiles:
         if info.packages:
             packages_needing_list = set(read_lines(info.file))
-            should_add_signature_to_list = lambda sig,lists: extract_package(
-                sig) in packages_needing_list and not lists
+            should_add_signature_to_list = lambda sig, lists: extract_package(
+                sig) in packages_needing_list and not lists #pylint: disable=cell-var-from-loop
             valid_entries = flags.filter_apis(should_add_signature_to_list)
             flags.assign_flag(info.flag, valid_entries, info.file, info.tag)
 
@@ -377,7 +420,8 @@
     flags.assign_flag(FLAG_BLOCKED, flags.filter_apis(HAS_NO_API_LIST_ASSIGNED))
 
     # Write output.
-    write_lines(args["output"], flags.generate_csv())
+    write_lines(args['output'], flags.generate_csv())
 
-if __name__ == "__main__":
+
+if __name__ == '__main__':
     main(sys.argv)
diff --git a/scripts/hiddenapi/generate_hiddenapi_lists_test.py b/scripts/hiddenapi/generate_hiddenapi_lists_test.py
index b81424b..204de97 100755
--- a/scripts/hiddenapi/generate_hiddenapi_lists_test.py
+++ b/scripts/hiddenapi/generate_hiddenapi_lists_test.py
@@ -15,34 +15,39 @@
 # limitations under the License.
 """Unit tests for Hidden API list generation."""
 import unittest
-from generate_hiddenapi_lists import *
+from generate_hiddenapi_lists import *  # pylint: disable=wildcard-import,unused-wildcard-import
+
 
 class TestHiddenapiListGeneration(unittest.TestCase):
-
     def test_filter_apis(self):
         # Initialize flags so that A and B are put on the allow list and
         # C, D, E are left unassigned. Try filtering for the unassigned ones.
         flags = FlagsDict()
-        flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B,' + FLAG_SDK,
-                        'C', 'D', 'E'])
+        flags.parse_and_merge_csv(
+            ['A,' + FLAG_SDK, 'B,' + FLAG_SDK, 'C', 'D', 'E']
+        )
         filter_set = flags.filter_apis(lambda api, flags: not flags)
         self.assertTrue(isinstance(filter_set, set))
-        self.assertEqual(filter_set, set([ 'C', 'D', 'E' ]))
+        self.assertEqual(filter_set, set(['C', 'D', 'E']))
 
     def test_get_valid_subset_of_unassigned_keys(self):
         # Create flags where only A is unassigned.
         flags = FlagsDict()
         flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B', 'C'])
         flags.assign_flag(FLAG_UNSUPPORTED, set(['C']))
-        self.assertEqual(flags.generate_csv(),
-            [ 'A,' + FLAG_SDK, 'B', 'C,' + FLAG_UNSUPPORTED ])
+        self.assertEqual(
+            flags.generate_csv(),
+            ['A,' + FLAG_SDK, 'B', 'C,' + FLAG_UNSUPPORTED],
+        )
 
         # Check three things:
         # (1) B is selected as valid unassigned
         # (2) A is not selected because it is assigned to the allow list
         # (3) D is not selected because it is not a valid key
         self.assertEqual(
-            flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ]))
+            flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])),
+            set(['B']),
+        )
 
     def test_parse_and_merge_csv(self):
         flags = FlagsDict()
@@ -51,41 +56,48 @@
         self.assertEqual(flags.generate_csv(), [])
 
         # Test new additions.
-        flags.parse_and_merge_csv([
-            'A,' + FLAG_UNSUPPORTED,
-            'B,' + FLAG_BLOCKED + ',' + FLAG_MAX_TARGET_O,
-            'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
-            'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
-            'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
-        ])
-        self.assertEqual(flags.generate_csv(), [
-            'A,' + FLAG_UNSUPPORTED,
-            'B,' + FLAG_BLOCKED + "," + FLAG_MAX_TARGET_O,
-            'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
-            'D,' + FLAG_TEST_API + ',' + FLAG_UNSUPPORTED,
-            'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
-        ])
+        flags.parse_and_merge_csv(
+            [
+                'A,' + FLAG_UNSUPPORTED,
+                'B,' + FLAG_BLOCKED + ',' + FLAG_MAX_TARGET_O,
+                'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
+                'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
+                'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
+            ]
+        )
+        self.assertEqual(
+            flags.generate_csv(),
+            [
+                'A,' + FLAG_UNSUPPORTED,
+                'B,' + FLAG_BLOCKED + "," + FLAG_MAX_TARGET_O,
+                'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
+                'D,' + FLAG_TEST_API + ',' + FLAG_UNSUPPORTED,
+                'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
+            ],
+        )
 
         # Test unknown flag.
         with self.assertRaises(AssertionError):
-            flags.parse_and_merge_csv([ 'Z,foo' ])
+            flags.parse_and_merge_csv(['Z,foo'])
 
     def test_assign_flag(self):
         flags = FlagsDict()
         flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B'])
 
         # Test new additions.
-        flags.assign_flag(FLAG_UNSUPPORTED, set([ 'A', 'B' ]))
-        self.assertEqual(flags.generate_csv(),
-            [ 'A,' + FLAG_SDK + "," + FLAG_UNSUPPORTED, 'B,' + FLAG_UNSUPPORTED ])
+        flags.assign_flag(FLAG_UNSUPPORTED, set(['A', 'B']))
+        self.assertEqual(
+            flags.generate_csv(),
+            ['A,' + FLAG_SDK + "," + FLAG_UNSUPPORTED, 'B,' + FLAG_UNSUPPORTED],
+        )
 
         # Test invalid API signature.
         with self.assertRaises(AssertionError):
-            flags.assign_flag(FLAG_SDK, set([ 'C' ]))
+            flags.assign_flag(FLAG_SDK, set(['C']))
 
         # Test invalid flag.
         with self.assertRaises(AssertionError):
-            flags.assign_flag('foo', set([ 'A' ]))
+            flags.assign_flag('foo', set(['A']))
 
     def test_extract_package(self):
         signature = 'Lcom/foo/bar/Baz;->method1()Lcom/bar/Baz;'
@@ -100,5 +112,6 @@
         expected_package = 'com.foo_bar.baz'
         self.assertEqual(extract_package(signature), expected_package)
 
+
 if __name__ == '__main__':
     unittest.main(verbosity=2)
diff --git a/scripts/hiddenapi/merge_csv.py b/scripts/hiddenapi/merge_csv.py
index a65326c..c17ec25 100755
--- a/scripts/hiddenapi/merge_csv.py
+++ b/scripts/hiddenapi/merge_csv.py
@@ -13,8 +13,7 @@
 # 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.
-"""
-Merge multiple CSV files, possibly with different columns.
+"""Merge multiple CSV files, possibly with different columns.
 """
 
 import argparse
@@ -26,34 +25,52 @@
 
 from zipfile import ZipFile
 
-args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.')
-args_parser.add_argument('--header', help='Comma separated field names; '
-                                          'if missing determines the header from input files.')
-args_parser.add_argument('--zip_input', help='Treat files as ZIP archives containing CSV files to merge.',
-                         action="store_true")
-args_parser.add_argument('--key_field', help='The name of the field by which the rows should be sorted. '
-                                             'Must be in the field names. '
-                                             'Will be the first field in the output. '
-                                             'All input files must be sorted by that field.')
-args_parser.add_argument('--output', help='Output file for merged CSV.',
-                         default='-', type=argparse.FileType('w'))
+args_parser = argparse.ArgumentParser(
+    description='Merge given CSV files into a single one.'
+)
+args_parser.add_argument(
+    '--header',
+    help='Comma separated field names; '
+    'if missing determines the header from input files.',
+)
+args_parser.add_argument(
+    '--zip_input',
+    help='Treat files as ZIP archives containing CSV files to merge.',
+    action="store_true",
+)
+args_parser.add_argument(
+    '--key_field',
+    help='The name of the field by which the rows should be sorted. '
+    'Must be in the field names. '
+    'Will be the first field in the output. '
+    'All input files must be sorted by that field.',
+)
+args_parser.add_argument(
+    '--output',
+    help='Output file for merged CSV.',
+    default='-',
+    type=argparse.FileType('w'),
+)
 args_parser.add_argument('files', nargs=argparse.REMAINDER)
 args = args_parser.parse_args()
 
 
-def dict_reader(input):
-    return csv.DictReader(input, delimiter=',', quotechar='|')
+def dict_reader(csvfile):
+    return csv.DictReader(csvfile, delimiter=',', quotechar='|')
+
 
 csv_readers = []
-if not(args.zip_input):
+if not args.zip_input:
     for file in args.files:
         csv_readers.append(dict_reader(open(file, 'r')))
 else:
     for file in args.files:
-        with ZipFile(file) as zip:
-            for entry in zip.namelist():
+        with ZipFile(file) as zipfile:
+            for entry in zipfile.namelist():
                 if entry.endswith('.uau'):
-                    csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
+                    csv_readers.append(
+                        dict_reader(io.TextIOWrapper(zipfile.open(entry, 'r')))
+                    )
 
 if args.header:
     fieldnames = args.header.split(',')
@@ -73,8 +90,8 @@
     keyField = args.key_field
     if keyField:
         assert keyField in fieldnames, (
-            "--key_field {} not found, must be one of {}\n").format(
-            keyField, ",".join(fieldnames))
+            "--key_field {} not found, must be one of {}\n"
+        ).format(keyField, ",".join(fieldnames))
         # Make the key field the first field in the output
         keyFieldIndex = fieldnames.index(args.key_field)
         fieldnames.insert(0, fieldnames.pop(keyFieldIndex))
@@ -83,11 +100,17 @@
         all_rows = heapq.merge(*csv_readers, key=operator.itemgetter(keyField))
 
 # Write all rows from the input files to the output:
-writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
-                        dialect='unix', fieldnames=fieldnames)
+writer = csv.DictWriter(
+    args.output,
+    delimiter=',',
+    quotechar='|',
+    quoting=csv.QUOTE_MINIMAL,
+    dialect='unix',
+    fieldnames=fieldnames,
+)
 writer.writeheader()
 
 # Read all the rows from the input and write them to the output in the correct
 # order:
 for row in all_rows:
-  writer.writerow(row)
+    writer.writerow(row)
diff --git a/scripts/hiddenapi/signature_patterns.py b/scripts/hiddenapi/signature_patterns.py
index a7c5bb4..0acb2a0 100755
--- a/scripts/hiddenapi/signature_patterns.py
+++ b/scripts/hiddenapi/signature_patterns.py
@@ -13,22 +13,26 @@
 # 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.
-"""
-Generate a set of signature patterns from the modular flags generated by a
+"""Generate a set of signature patterns from the modular flags generated by a
 bootclasspath_fragment that can be used to select a subset of monolithic flags
 against which the modular flags can be compared.
 """
 
 import argparse
 import csv
+import sys
 
-def dict_reader(input):
-    return csv.DictReader(input, delimiter=',', quotechar='|', fieldnames=['signature'])
+def dict_reader(csvfile):
+    return csv.DictReader(
+        csvfile, delimiter=',', quotechar='|', fieldnames=['signature']
+    )
+
 
 def produce_patterns_from_file(file):
     with open(file, 'r') as f:
         return produce_patterns_from_stream(f)
 
+
 def produce_patterns_from_stream(stream):
     # Read in all the signatures into a list and remove member names.
     patterns = set()
@@ -38,18 +42,26 @@
         # Remove the class specific member signature
         pieces = text.split(";->")
         qualifiedClassName = pieces[0]
-            # Remove inner class names as they cannot be separated from the containing outer class.
+        # Remove inner class names as they cannot be separated
+        # from the containing outer class.
         pieces = qualifiedClassName.split("$", maxsplit=1)
         pattern = pieces[0]
         patterns.add(pattern)
 
-    patterns = list(patterns)
+    patterns = list(patterns) #pylint: disable=redefined-variable-type
     patterns.sort()
     return patterns
 
+
 def main(args):
-    args_parser = argparse.ArgumentParser(description='Generate a set of signature patterns that select a subset of monolithic hidden API files.')
-    args_parser.add_argument('--flags', help='The stub flags file which contains an entry for every dex member')
+    args_parser = argparse.ArgumentParser(
+        description='Generate a set of signature patterns '
+        'that select a subset of monolithic hidden API files.'
+    )
+    args_parser.add_argument(
+        '--flags',
+        help='The stub flags file which contains an entry for every dex member',
+    )
     args_parser.add_argument('--output', help='Generated signature prefixes')
     args = args_parser.parse_args(args)
 
@@ -62,5 +74,6 @@
             outputFile.write(pattern)
             outputFile.write("\n")
 
+
 if __name__ == "__main__":
     main(sys.argv[1:])
diff --git a/scripts/hiddenapi/signature_patterns_test.py b/scripts/hiddenapi/signature_patterns_test.py
index 0431f45..3babe54 100755
--- a/scripts/hiddenapi/signature_patterns_test.py
+++ b/scripts/hiddenapi/signature_patterns_test.py
@@ -18,21 +18,25 @@
 import io
 import unittest
 
-from signature_patterns import *
+from signature_patterns import * #pylint: disable=unused-wildcard-import,wildcard-import
+
 
 class TestGeneratedPatterns(unittest.TestCase):
-
-    def produce_patterns_from_string(self, csv):
-        with io.StringIO(csv) as f:
+    def produce_patterns_from_string(self, csvdata):
+        with io.StringIO(csvdata) as f:
             return produce_patterns_from_stream(f)
 
     def test_generate(self):
-        patterns = self.produce_patterns_from_string('''
+        #pylint: disable=line-too-long
+        patterns = self.produce_patterns_from_string(
+            '''
 Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V,blocked
 Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;,public-api
 Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
 Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
-''')
+'''
+        )
+        #pylint: enable=line-too-long
         expected = [
             "java/lang/Character",
             "java/lang/Object",
@@ -40,5 +44,6 @@
         ]
         self.assertEqual(expected, patterns)
 
+
 if __name__ == '__main__':
     unittest.main(verbosity=2)
diff --git a/scripts/hiddenapi/verify_overlaps.py b/scripts/hiddenapi/verify_overlaps.py
index 6432bf1..4cd7e63 100755
--- a/scripts/hiddenapi/verify_overlaps.py
+++ b/scripts/hiddenapi/verify_overlaps.py
@@ -13,8 +13,7 @@
 # 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.
-"""
-Verify that one set of hidden API flags is a subset of another.
+"""Verify that one set of hidden API flags is a subset of another.
 """
 
 import argparse
@@ -22,9 +21,9 @@
 import sys
 from itertools import chain
 
+#pylint: disable=line-too-long
 class InteriorNode:
-    """
-    An interior node in a trie.
+    """An interior node in a trie.
 
     Each interior node has a dict that maps from an element of a signature to
     either another interior node or a leaf. Each interior node represents either
@@ -52,19 +51,21 @@
 
     Attributes:
         nodes: a dict from an element of the signature to the Node/Leaf
-        containing the next element/value.
+          containing the next element/value.
     """
+    #pylint: enable=line-too-long
+
     def __init__(self):
         self.nodes = {}
 
+    #pylint: disable=line-too-long
     def signatureToElements(self, signature):
-        """
-        Split a signature or a prefix into a number of elements:
+        """Split a signature or a prefix into a number of elements:
         1. The packages (excluding the leading L preceding the first package).
         2. The class names, from outermost to innermost.
         3. The member signature.
-
-        e.g. Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
+        e.g.
+        Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
         will be broken down into these elements:
         1. package:java
         2. package:lang
@@ -88,19 +89,21 @@
         elements = parts[0].split("/")
         packages = elements[0:-1]
         className = elements[-1]
-        if className == "*" or className == "**":
+        if className in ("*" , "**"): #pylint: disable=no-else-return
             # Cannot specify a wildcard and target a specific member
             if len(member) != 0:
-                raise Exception("Invalid signature %s: contains wildcard %s and member signature %s"
-                                % (signature, className, member[0]))
+                raise Exception(
+                    "Invalid signature %s: contains wildcard %s and member " \
+                    "signature %s"
+                    % (signature, className, member[0]))
             wildcard = [className]
             # Assemble the parts into a single list, adding prefixes to identify
             # the different parts.
             #  0 - package:java
             #  1 - package:lang
             #  2 - *
-            return list(chain(map(lambda x : "package:" + x, packages),
-                              wildcard))
+            return list(
+                chain(["package:" + x for x in packages], wildcard))
         else:
             # Split the class name into outer / inner classes
             #  0 - Character
@@ -113,13 +116,16 @@
             #  2 - class:Character
             #  3 - class:UnicodeScript
             #  4 - member:of(I)Ljava/lang/Character$UnicodeScript;
-            return list(chain(map(lambda x : "package:" + x, packages),
-                              map(lambda x : "class:" + x, classes),
-                              map(lambda x : "member:" + x, member)))
+            return list(
+                chain(
+                    ["package:" + x for x in packages],
+                    ["class:" + x for x in classes],
+                    ["member:" + x for x in member]))
+    #pylint: enable=line-too-long
 
     def add(self, signature, value):
-        """
-        Associate the value with the specific signature.
+        """Associate the value with the specific signature.
+
         :param signature: the member signature
         :param value: the value to associated with the signature
         :return: n/a
@@ -132,21 +138,22 @@
             if element in node.nodes:
                 node = node.nodes[element]
             else:
-                next = InteriorNode()
-                node.nodes[element] = next
-                node = next
+                next_node = InteriorNode()
+                node.nodes[element] = next_node
+                node = next_node
         # Add a Leaf containing the value and associate it with the member
         # signature within the class.
         lastElement = elements[-1]
         if not lastElement.startswith("member:"):
-            raise Exception("Invalid signature: %s, does not identify a specific member" % signature)
+            raise Exception(
+                "Invalid signature: %s, does not identify a specific member" %
+                signature)
         if lastElement in node.nodes:
             raise Exception("Duplicate signature: %s" % signature)
         node.nodes[lastElement] = Leaf(value)
 
     def getMatchingRows(self, pattern):
-        """
-        Get the values (plural) associated with the pattern.
+        """Get the values (plural) associated with the pattern.
 
         e.g. If the pattern is a full signature then this will return a list
         containing the value associated with that signature.
@@ -175,13 +182,13 @@
         elements = self.signatureToElements(pattern)
         node = self
         # Include all values from this node and all its children.
-        selector = lambda x : True
+        selector = lambda x: True
         lastElement = elements[-1]
-        if lastElement == "*" or lastElement == "**":
+        if lastElement in ("*", "**"):
             elements = elements[:-1]
             if lastElement == "*":
                 # Do not include values from sub-packages.
-                selector = lambda x : not x.startswith("package:")
+                selector = lambda x: not x.startswith("package:")
         for element in elements:
             if element in node.nodes:
                 node = node.nodes[element]
@@ -190,19 +197,18 @@
         return chain.from_iterable(node.values(selector))
 
     def values(self, selector):
-        """
-        :param selector: a function that can be applied to a key in the nodes
+        """:param selector: a function that can be applied to a key in the nodes
         attribute to determine whether to return its values.
-        :return: A list of iterables of all the values associated with this
-        node and its children.
+
+        :return: A list of iterables of all the values associated with
+        this node and its children.
         """
         values = []
         self.appendValues(values, selector)
         return values
 
     def appendValues(self, values, selector):
-        """
-        Append the values associated with this node and its children to the
+        """Append the values associated with this node and its children to the
         list.
 
         For each item (key, child) in nodes the child node's values are returned
@@ -216,105 +222,116 @@
         """
         for key, node in self.nodes.items():
             if selector(key):
-                node.appendValues(values, lambda x : True)
+                node.appendValues(values, lambda x: True)
+
 
 class Leaf:
-    """
-    A leaf of the trie
+    """A leaf of the trie
 
     Attributes:
         value: the value associated with this leaf.
     """
+
     def __init__(self, value):
         self.value = value
 
-    def values(self, selector):
-        """
-        :return: A list of a list of the value associated with this node.
+    def values(self, selector): #pylint: disable=unused-argument
+        """:return: A list of a list of the value associated with this node.
         """
         return [[self.value]]
 
-    def appendValues(self, values, selector):
-        """
-        Appends a list of the value associated with this node to the list.
+    def appendValues(self, values, selector): #pylint: disable=unused-argument
+        """Appends a list of the value associated with this node to the list.
+
         :param values: a list of a iterables of values.
         """
         values.append([self.value])
 
-def dict_reader(input):
-    return csv.DictReader(input, delimiter=',', quotechar='|', fieldnames=['signature'])
+
+def dict_reader(csvfile):
+    return csv.DictReader(
+        csvfile, delimiter=",", quotechar="|", fieldnames=["signature"])
+
 
 def read_flag_trie_from_file(file):
-    with open(file, 'r') as stream:
+    with open(file, "r") as stream:
         return read_flag_trie_from_stream(stream)
 
+
 def read_flag_trie_from_stream(stream):
     trie = InteriorNode()
     reader = dict_reader(stream)
     for row in reader:
-        signature = row['signature']
+        signature = row["signature"]
         trie.add(signature, row)
     return trie
 
-def extract_subset_from_monolithic_flags_as_dict_from_file(monolithicTrie, patternsFile):
-    """
-    Extract a subset of flags from the dict containing all the monolithic flags.
+
+def extract_subset_from_monolithic_flags_as_dict_from_file(
+        monolithicTrie, patternsFile):
+    """Extract a subset of flags from the dict containing all the monolithic
+    flags.
 
     :param monolithicFlagsDict: the dict containing all the monolithic flags.
     :param patternsFile: a file containing a list of signature patterns that
     define the subset.
     :return: the dict from signature to row.
     """
-    with open(patternsFile, 'r') as stream:
-        return extract_subset_from_monolithic_flags_as_dict_from_stream(monolithicTrie, stream)
+    with open(patternsFile, "r") as stream:
+        return extract_subset_from_monolithic_flags_as_dict_from_stream(
+            monolithicTrie, stream)
 
-def extract_subset_from_monolithic_flags_as_dict_from_stream(monolithicTrie, stream):
-    """
-    Extract a subset of flags from the trie containing all the monolithic flags.
+
+def extract_subset_from_monolithic_flags_as_dict_from_stream(
+        monolithicTrie, stream):
+    """Extract a subset of flags from the trie containing all the monolithic
+    flags.
 
     :param monolithicTrie: the trie containing all the monolithic flags.
     :param stream: a stream containing a list of signature patterns that define
     the subset.
     :return: the dict from signature to row.
     """
-    dict = {}
+    dict_signature_to_row = {}
     for pattern in stream:
         pattern = pattern.rstrip()
         rows = monolithicTrie.getMatchingRows(pattern)
         for row in rows:
-            signature = row['signature']
-            dict[signature] = row
-    return dict
+            signature = row["signature"]
+            dict_signature_to_row[signature] = row
+    return dict_signature_to_row
+
 
 def read_signature_csv_from_stream_as_dict(stream):
-    """
-    Read the csv contents from the stream into a dict. The first column is assumed to be the
-    signature and used as the key. The whole row is stored as the value.
+    """Read the csv contents from the stream into a dict. The first column is
+    assumed to be the signature and used as the key.
 
+    The whole row is stored as the value.
     :param stream: the csv contents to read
     :return: the dict from signature to row.
     """
-    dict = {}
+    dict_signature_to_row = {}
     reader = dict_reader(stream)
     for row in reader:
-        signature = row['signature']
-        dict[signature] = row
-    return dict
+        signature = row["signature"]
+        dict_signature_to_row[signature] = row
+    return dict_signature_to_row
+
 
 def read_signature_csv_from_file_as_dict(csvFile):
-    """
-    Read the csvFile into a dict. The first column is assumed to be the
-    signature and used as the key. The whole row is stored as the value.
+    """Read the csvFile into a dict. The first column is assumed to be the
+    signature and used as the key.
 
+    The whole row is stored as the value.
     :param csvFile: the csv file to read
     :return: the dict from signature to row.
     """
-    with open(csvFile, 'r') as f:
+    with open(csvFile, "r") as f:
         return read_signature_csv_from_stream_as_dict(f)
 
+
 def compare_signature_flags(monolithicFlagsDict, modularFlagsDict):
-    """
-    Compare the signature flags between the two dicts.
+    """Compare the signature flags between the two dicts.
 
     :param monolithicFlagsDict: the dict containing the subset of the monolithic
     flags that should be equal to the modular flags.
@@ -327,7 +344,8 @@
     mismatchingSignatures = []
     # Create a sorted set of all the signatures from both the monolithic and
     # modular dicts.
-    allSignatures = sorted(set(chain(monolithicFlagsDict.keys(), modularFlagsDict.keys())))
+    allSignatures = sorted(
+        set(chain(monolithicFlagsDict.keys(), modularFlagsDict.keys())))
     for signature in allSignatures:
         monolithicRow = monolithicFlagsDict.get(signature, {})
         monolithicFlags = monolithicRow.get(None, [])
@@ -337,13 +355,21 @@
         else:
             modularFlags = ["blocked"]
         if monolithicFlags != modularFlags:
-            mismatchingSignatures.append((signature, modularFlags, monolithicFlags))
+            mismatchingSignatures.append(
+                (signature, modularFlags, monolithicFlags))
     return mismatchingSignatures
 
+
 def main(argv):
-    args_parser = argparse.ArgumentParser(description='Verify that sets of hidden API flags are each a subset of the monolithic flag file.')
-    args_parser.add_argument('monolithicFlags', help='The monolithic flag file')
-    args_parser.add_argument('modularFlags', nargs=argparse.REMAINDER, help='Flags produced by individual bootclasspath_fragment modules')
+    args_parser = argparse.ArgumentParser(
+        description="Verify that sets of hidden API flags are each a subset of "
+        "the monolithic flag file."
+    )
+    args_parser.add_argument("monolithicFlags", help="The monolithic flag file")
+    args_parser.add_argument(
+        "modularFlags",
+        nargs=argparse.REMAINDER,
+        help="Flags produced by individual bootclasspath_fragment modules")
     args = args_parser.parse_args(argv[1:])
 
     # Read in all the flags into the trie
@@ -358,9 +384,13 @@
         parts = modularPair.split(":")
         modularFlagsPath = parts[0]
         modularPatternsPath = parts[1]
-        modularFlagsDict = read_signature_csv_from_file_as_dict(modularFlagsPath)
-        monolithicFlagsSubsetDict = extract_subset_from_monolithic_flags_as_dict_from_file(monolithicTrie, modularPatternsPath)
-        mismatchingSignatures = compare_signature_flags(monolithicFlagsSubsetDict, modularFlagsDict)
+        modularFlagsDict = read_signature_csv_from_file_as_dict(
+            modularFlagsPath)
+        monolithicFlagsSubsetDict = \
+            extract_subset_from_monolithic_flags_as_dict_from_file(
+            monolithicTrie, modularPatternsPath)
+        mismatchingSignatures = compare_signature_flags(
+            monolithicFlagsSubsetDict, modularFlagsDict)
         if mismatchingSignatures:
             failed = True
             print("ERROR: Hidden API flags are inconsistent:")
@@ -369,11 +399,12 @@
             for mismatch in mismatchingSignatures:
                 signature = mismatch[0]
                 print()
-                print("< " + ",".join([signature]+ mismatch[1]))
-                print("> " + ",".join([signature]+ mismatch[2]))
+                print("< " + ",".join([signature] + mismatch[1]))
+                print("> " + ",".join([signature] + mismatch[2]))
 
     if failed:
         sys.exit(1)
 
+
 if __name__ == "__main__":
     main(sys.argv)
diff --git a/scripts/hiddenapi/verify_overlaps_test.py b/scripts/hiddenapi/verify_overlaps_test.py
index 00c0611..22a1cdf 100755
--- a/scripts/hiddenapi/verify_overlaps_test.py
+++ b/scripts/hiddenapi/verify_overlaps_test.py
@@ -13,12 +13,12 @@
 # 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.
-
 """Unit tests for verify_overlaps_test.py."""
 import io
 import unittest
 
-from verify_overlaps import *
+from verify_overlaps import * #pylint: disable=unused-wildcard-import,wildcard-import
+
 
 class TestSignatureToElements(unittest.TestCase):
 
@@ -34,8 +34,10 @@
             'class:1',
             'member:<init>()V',
         ]
-        self.assertEqual(expected, self.signatureToElements(
-            "Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V"))
+        self.assertEqual(
+            expected,
+            self.signatureToElements(
+                'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V'))
 
     def test_signatureToElements_2(self):
         expected = [
@@ -44,8 +46,9 @@
             'class:Object',
             'member:hashCode()I',
         ]
-        self.assertEqual(expected, self.signatureToElements(
-            "Ljava/lang/Object;->hashCode()I"))
+        self.assertEqual(
+            expected,
+            self.signatureToElements('Ljava/lang/Object;->hashCode()I'))
 
     def test_signatureToElements_3(self):
         expected = [
@@ -56,39 +59,46 @@
             'class:ExternalSyntheticLambda0',
             'member:<init>(Ljava/lang/CharSequence;)V',
         ]
-        self.assertEqual(expected, self.signatureToElements(
-            "Ljava/lang/CharSequence$$ExternalSyntheticLambda0;"
-            "-><init>(Ljava/lang/CharSequence;)V"))
+        self.assertEqual(
+            expected,
+            self.signatureToElements(
+                'Ljava/lang/CharSequence$$ExternalSyntheticLambda0;'
+                '-><init>(Ljava/lang/CharSequence;)V'))
 
+#pylint: disable=line-too-long
 class TestDetectOverlaps(unittest.TestCase):
 
-    def read_flag_trie_from_string(self, csv):
-        with io.StringIO(csv) as f:
+    def read_flag_trie_from_string(self, csvdata):
+        with io.StringIO(csvdata) as f:
             return read_flag_trie_from_stream(f)
 
-    def read_signature_csv_from_string_as_dict(self, csv):
-        with io.StringIO(csv) as f:
+    def read_signature_csv_from_string_as_dict(self, csvdata):
+        with io.StringIO(csvdata) as f:
             return read_signature_csv_from_stream_as_dict(f)
 
-    def extract_subset_from_monolithic_flags_as_dict_from_string(self, monolithic, patterns):
+    def extract_subset_from_monolithic_flags_as_dict_from_string(
+            self, monolithic, patterns):
         with io.StringIO(patterns) as f:
-            return extract_subset_from_monolithic_flags_as_dict_from_stream(monolithic, f)
+            return extract_subset_from_monolithic_flags_as_dict_from_stream(
+                monolithic, f)
 
-    extractInput = '''
+    extractInput = """
 Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
 Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
 Ljava/util/zip/ZipFile;-><clinit>()V,blocked
 Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;,blocked
 Ljava/lang/Character;->serialVersionUID:J,sdk
 Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V,blocked
-'''
+"""
 
     def test_extract_subset_signature(self):
-        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
 
         patterns = 'Ljava/lang/Object;->hashCode()I'
 
-        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
         expected = {
             'Ljava/lang/Object;->hashCode()I': {
                 None: ['public-api', 'system-api', 'test-api'],
@@ -98,11 +108,13 @@
         self.assertEqual(expected, subset)
 
     def test_extract_subset_class(self):
-        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
 
         patterns = 'java/lang/Object'
 
-        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
         expected = {
             'Ljava/lang/Object;->hashCode()I': {
                 None: ['public-api', 'system-api', 'test-api'],
@@ -116,16 +128,20 @@
         self.assertEqual(expected, subset)
 
     def test_extract_subset_outer_class(self):
-        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
 
         patterns = 'java/lang/Character'
 
-        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
         expected = {
-            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
-                None: ['blocked'],
-                'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
-            },
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
+                {
+                    None: ['blocked'],
+                    'signature':
+                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+                },
             'Ljava/lang/Character;->serialVersionUID:J': {
                 None: ['sdk'],
                 'signature': 'Ljava/lang/Character;->serialVersionUID:J',
@@ -134,30 +150,38 @@
         self.assertEqual(expected, subset)
 
     def test_extract_subset_nested_class(self):
-        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
 
         patterns = 'java/lang/Character$UnicodeScript'
 
-        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
         expected = {
-            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
-                None: ['blocked'],
-                'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
-            },
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
+                {
+                    None: ['blocked'],
+                    'signature':
+                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+                },
         }
         self.assertEqual(expected, subset)
 
     def test_extract_subset_package(self):
-        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
 
         patterns = 'java/lang/*'
 
-        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
         expected = {
-            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
-                None: ['blocked'],
-                'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
-            },
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
+                {
+                    None: ['blocked'],
+                    'signature':
+                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+                },
             'Ljava/lang/Character;->serialVersionUID:J': {
                 None: ['sdk'],
                 'signature': 'Ljava/lang/Character;->serialVersionUID:J',
@@ -178,16 +202,20 @@
         self.assertEqual(expected, subset)
 
     def test_extract_subset_recursive_package(self):
-        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
 
         patterns = 'java/**'
 
-        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
         expected = {
-            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;': {
-                None: ['blocked'],
-                'signature': 'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
-            },
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
+                {
+                    None: ['blocked'],
+                    'signature':
+                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+                },
             'Ljava/lang/Character;->serialVersionUID:J': {
                 None: ['sdk'],
                 'signature': 'Ljava/lang/Character;->serialVersionUID:J',
@@ -212,47 +240,53 @@
         self.assertEqual(expected, subset)
 
     def test_extract_subset_invalid_pattern_wildcard_and_member(self):
-        monolithic = self.read_flag_trie_from_string(TestDetectOverlaps.extractInput)
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
 
         patterns = 'Ljava/lang/*;->hashCode()I'
 
         with self.assertRaises(Exception) as context:
-            self.extract_subset_from_monolithic_flags_as_dict_from_string(monolithic, patterns)
-        self.assertTrue("contains wildcard * and member signature hashCode()I" in str(context.exception))
+            self.extract_subset_from_monolithic_flags_as_dict_from_string(
+                monolithic, patterns)
+        self.assertTrue('contains wildcard * and member signature hashCode()I'
+                        in str(context.exception))
 
     def test_read_trie_duplicate(self):
         with self.assertRaises(Exception) as context:
-            self.read_flag_trie_from_string('''
+            self.read_flag_trie_from_string("""
 Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
 Ljava/lang/Object;->hashCode()I,blocked
-''')
-        self.assertTrue("Duplicate signature: Ljava/lang/Object;->hashCode()I" in str(context.exception))
+""")
+        self.assertTrue('Duplicate signature: Ljava/lang/Object;->hashCode()I'
+                        in str(context.exception))
 
     def test_read_trie_missing_member(self):
         with self.assertRaises(Exception) as context:
-            self.read_flag_trie_from_string('''
+            self.read_flag_trie_from_string("""
 Ljava/lang/Object,public-api,system-api,test-api
-''')
-        self.assertTrue("Invalid signature: Ljava/lang/Object, does not identify a specific member" in str(context.exception))
+""")
+        self.assertTrue(
+            'Invalid signature: Ljava/lang/Object, does not identify a specific member'
+            in str(context.exception))
 
     def test_match(self):
-        monolithic = self.read_signature_csv_from_string_as_dict('''
+        monolithic = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
-''')
-        modular = self.read_signature_csv_from_string_as_dict('''
+""")
+        modular = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
-''')
+""")
         mismatches = compare_signature_flags(monolithic, modular)
         expected = []
         self.assertEqual(expected, mismatches)
 
     def test_mismatch_overlapping_flags(self):
-        monolithic = self.read_signature_csv_from_string_as_dict('''
+        monolithic = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,public-api
-''')
-        modular = self.read_signature_csv_from_string_as_dict('''
+""")
+        modular = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
-''')
+""")
         mismatches = compare_signature_flags(monolithic, modular)
         expected = [
             (
@@ -263,14 +297,13 @@
         ]
         self.assertEqual(expected, mismatches)
 
-
     def test_mismatch_monolithic_blocked(self):
-        monolithic = self.read_signature_csv_from_string_as_dict('''
+        monolithic = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
-''')
-        modular = self.read_signature_csv_from_string_as_dict('''
+""")
+        modular = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
-''')
+""")
         mismatches = compare_signature_flags(monolithic, modular)
         expected = [
             (
@@ -282,12 +315,12 @@
         self.assertEqual(expected, mismatches)
 
     def test_mismatch_modular_blocked(self):
-        monolithic = self.read_signature_csv_from_string_as_dict('''
+        monolithic = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
-''')
-        modular = self.read_signature_csv_from_string_as_dict('''
+""")
+        modular = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
-''')
+""")
         mismatches = compare_signature_flags(monolithic, modular)
         expected = [
             (
@@ -300,9 +333,9 @@
 
     def test_match_treat_missing_from_modular_as_blocked(self):
         monolithic = self.read_signature_csv_from_string_as_dict('')
-        modular = self.read_signature_csv_from_string_as_dict('''
+        modular = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
-''')
+""")
         mismatches = compare_signature_flags(monolithic, modular)
         expected = [
             (
@@ -314,9 +347,9 @@
         self.assertEqual(expected, mismatches)
 
     def test_mismatch_treat_missing_from_modular_as_blocked(self):
-        monolithic = self.read_signature_csv_from_string_as_dict('''
+        monolithic = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
-''')
+""")
         modular = {}
         mismatches = compare_signature_flags(monolithic, modular)
         expected = [
@@ -329,13 +362,14 @@
         self.assertEqual(expected, mismatches)
 
     def test_blocked_missing_from_modular(self):
-        monolithic = self.read_signature_csv_from_string_as_dict('''
+        monolithic = self.read_signature_csv_from_string_as_dict("""
 Ljava/lang/Object;->hashCode()I,blocked
-''')
+""")
         modular = {}
         mismatches = compare_signature_flags(monolithic, modular)
         expected = []
         self.assertEqual(expected, mismatches)
+#pylint: enable=line-too-long
 
 if __name__ == '__main__':
     unittest.main(verbosity=2)
diff --git a/sdk/Android.bp b/sdk/Android.bp
index 368c03a..0c9bf27 100644
--- a/sdk/Android.bp
+++ b/sdk/Android.bp
@@ -16,6 +16,7 @@
     srcs: [
         "bp.go",
         "exports.go",
+        "member_type.go",
         "sdk.go",
         "update.go",
     ],
diff --git a/sdk/member_type.go b/sdk/member_type.go
new file mode 100644
index 0000000..ee27c86
--- /dev/null
+++ b/sdk/member_type.go
@@ -0,0 +1,164 @@
+// Copyright (C) 2021 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 (
+	"reflect"
+
+	"android/soong/android"
+	"github.com/google/blueprint/proptools"
+)
+
+// Contains information about the sdk properties that list sdk members by type, e.g.
+// Java_header_libs.
+type sdkMemberTypeListProperty struct {
+	// getter for the list of member names
+	getter func(properties interface{}) []string
+
+	// setter for the list of member names
+	setter func(properties interface{}, list []string)
+
+	// the type of member referenced in the list
+	memberType android.SdkMemberType
+
+	// the dependency tag used for items in this list that can be used to determine the memberType
+	// for a resolved dependency.
+	dependencyTag android.SdkMemberTypeDependencyTag
+}
+
+func (p *sdkMemberTypeListProperty) propertyName() string {
+	return p.memberType.SdkPropertyName()
+}
+
+// Cache of dynamically generated dynamicSdkMemberTypes objects. The key is the pointer
+// to a slice of SdkMemberType instances held in android.SdkMemberTypes.
+var dynamicSdkMemberTypesMap android.OncePer
+
+// A dynamically generated set of member list properties and associated structure type.
+type dynamicSdkMemberTypes struct {
+	// The dynamically generated structure type.
+	//
+	// Contains one []string exported field for each android.SdkMemberTypes. The name of the field
+	// is the exported form of the value returned by SdkMemberType.SdkPropertyName().
+	propertiesStructType reflect.Type
+
+	// Information about each of the member type specific list properties.
+	memberTypeListProperties []*sdkMemberTypeListProperty
+
+	memberTypeToProperty map[android.SdkMemberType]*sdkMemberTypeListProperty
+}
+
+func (d *dynamicSdkMemberTypes) createMemberTypeListProperties() interface{} {
+	return reflect.New(d.propertiesStructType).Interface()
+}
+
+func getDynamicSdkMemberTypes(registry *android.SdkMemberTypesRegistry) *dynamicSdkMemberTypes {
+
+	// Get a key that uniquely identifies the registry contents.
+	key := registry.UniqueOnceKey()
+
+	// Get the registered types.
+	registeredTypes := registry.RegisteredTypes()
+
+	// Get the cached value, creating new instance if necessary.
+	return dynamicSdkMemberTypesMap.Once(key, func() interface{} {
+		return createDynamicSdkMemberTypes(registeredTypes)
+	}).(*dynamicSdkMemberTypes)
+}
+
+// Create the dynamicSdkMemberTypes from the list of registered member types.
+//
+// A struct is created which contains one exported field per member type corresponding to
+// the SdkMemberType.SdkPropertyName() value.
+//
+// A list of sdkMemberTypeListProperty instances is created, one per member type that provides:
+// * a reference to the member type.
+// * a getter for the corresponding field in the properties struct.
+// * a dependency tag that identifies the member type of a resolved dependency.
+//
+func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynamicSdkMemberTypes {
+
+	var listProperties []*sdkMemberTypeListProperty
+	memberTypeToProperty := map[android.SdkMemberType]*sdkMemberTypeListProperty{}
+	var fields []reflect.StructField
+
+	// Iterate over the member types creating StructField and sdkMemberTypeListProperty objects.
+	nextFieldIndex := 0
+	for _, memberType := range sdkMemberTypes {
+
+		p := memberType.SdkPropertyName()
+
+		var getter func(properties interface{}) []string
+		var setter func(properties interface{}, list []string)
+		if memberType.RequiresBpProperty() {
+			// Create a dynamic exported field for the member type's property.
+			fields = append(fields, reflect.StructField{
+				Name: proptools.FieldNameForProperty(p),
+				Type: reflect.TypeOf([]string{}),
+				Tag:  `android:"arch_variant"`,
+			})
+
+			// Copy the field index for use in the getter func as using the loop variable directly will
+			// cause all funcs to use the last value.
+			fieldIndex := nextFieldIndex
+			nextFieldIndex += 1
+
+			getter = func(properties interface{}) []string {
+				// The properties is expected to be of the following form (where
+				// <Module_types> is the name of an SdkMemberType.SdkPropertyName().
+				//     properties *struct {<Module_types> []string, ....}
+				//
+				// Although it accesses the field by index the following reflection code is equivalent to:
+				//    *properties.<Module_types>
+				//
+				list := reflect.ValueOf(properties).Elem().Field(fieldIndex).Interface().([]string)
+				return list
+			}
+
+			setter = func(properties interface{}, list []string) {
+				// The properties is expected to be of the following form (where
+				// <Module_types> is the name of an SdkMemberType.SdkPropertyName().
+				//     properties *struct {<Module_types> []string, ....}
+				//
+				// Although it accesses the field by index the following reflection code is equivalent to:
+				//    *properties.<Module_types> = list
+				//
+				reflect.ValueOf(properties).Elem().Field(fieldIndex).Set(reflect.ValueOf(list))
+			}
+		}
+
+		// Create an sdkMemberTypeListProperty for the member type.
+		memberListProperty := &sdkMemberTypeListProperty{
+			getter:     getter,
+			setter:     setter,
+			memberType: memberType,
+
+			// Dependencies added directly from member properties are always exported.
+			dependencyTag: android.DependencyTagForSdkMemberType(memberType, true),
+		}
+
+		memberTypeToProperty[memberType] = memberListProperty
+		listProperties = append(listProperties, memberListProperty)
+	}
+
+	// Create a dynamic struct from the collated fields.
+	propertiesStructType := reflect.StructOf(fields)
+
+	return &dynamicSdkMemberTypes{
+		memberTypeListProperties: listProperties,
+		memberTypeToProperty:     memberTypeToProperty,
+		propertiesStructType:     propertiesStructType,
+	}
+}
diff --git a/sdk/sdk.go b/sdk/sdk.go
index b1c8aeb..6dea752 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"io"
-	"reflect"
 	"strconv"
 
 	"github.com/google/blueprint"
@@ -50,7 +49,7 @@
 	// The dynamically generated information about the registered SdkMemberType
 	dynamicSdkMemberTypes *dynamicSdkMemberTypes
 
-	// The dynamically created instance of the properties struct containing the sdk member
+	// The dynamically created instance of the properties struct containing the sdk member type
 	// list properties, e.g. java_libs.
 	dynamicMemberTypeListProperties interface{}
 
@@ -95,148 +94,6 @@
 	Prebuilt_visibility []string
 }
 
-// Contains information about the sdk properties that list sdk members, e.g.
-// Java_header_libs.
-type sdkMemberListProperty struct {
-	// getter for the list of member names
-	getter func(properties interface{}) []string
-
-	// setter for the list of member names
-	setter func(properties interface{}, list []string)
-
-	// the type of member referenced in the list
-	memberType android.SdkMemberType
-
-	// the dependency tag used for items in this list that can be used to determine the memberType
-	// for a resolved dependency.
-	dependencyTag android.SdkMemberTypeDependencyTag
-}
-
-func (p *sdkMemberListProperty) propertyName() string {
-	return p.memberType.SdkPropertyName()
-}
-
-// Cache of dynamically generated dynamicSdkMemberTypes objects. The key is the pointer
-// to a slice of SdkMemberType instances held in android.SdkMemberTypes.
-var dynamicSdkMemberTypesMap android.OncePer
-
-// A dynamically generated set of member list properties and associated structure type.
-type dynamicSdkMemberTypes struct {
-	// The dynamically generated structure type.
-	//
-	// Contains one []string exported field for each android.SdkMemberTypes. The name of the field
-	// is the exported form of the value returned by SdkMemberType.SdkPropertyName().
-	propertiesStructType reflect.Type
-
-	// Information about each of the member type specific list properties.
-	memberListProperties []*sdkMemberListProperty
-
-	memberTypeToProperty map[android.SdkMemberType]*sdkMemberListProperty
-}
-
-func (d *dynamicSdkMemberTypes) createMemberListProperties() interface{} {
-	return reflect.New(d.propertiesStructType).Interface()
-}
-
-func getDynamicSdkMemberTypes(registry *android.SdkMemberTypesRegistry) *dynamicSdkMemberTypes {
-
-	// Get a key that uniquely identifies the registry contents.
-	key := registry.UniqueOnceKey()
-
-	// Get the registered types.
-	registeredTypes := registry.RegisteredTypes()
-
-	// Get the cached value, creating new instance if necessary.
-	return dynamicSdkMemberTypesMap.Once(key, func() interface{} {
-		return createDynamicSdkMemberTypes(registeredTypes)
-	}).(*dynamicSdkMemberTypes)
-}
-
-// Create the dynamicSdkMemberTypes from the list of registered member types.
-//
-// A struct is created which contains one exported field per member type corresponding to
-// the SdkMemberType.SdkPropertyName() value.
-//
-// A list of sdkMemberListProperty instances is created, one per member type that provides:
-// * a reference to the member type.
-// * a getter for the corresponding field in the properties struct.
-// * a dependency tag that identifies the member type of a resolved dependency.
-//
-func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynamicSdkMemberTypes {
-
-	var listProperties []*sdkMemberListProperty
-	memberTypeToProperty := map[android.SdkMemberType]*sdkMemberListProperty{}
-	var fields []reflect.StructField
-
-	// Iterate over the member types creating StructField and sdkMemberListProperty objects.
-	nextFieldIndex := 0
-	for _, memberType := range sdkMemberTypes {
-
-		p := memberType.SdkPropertyName()
-
-		var getter func(properties interface{}) []string
-		var setter func(properties interface{}, list []string)
-		if memberType.RequiresBpProperty() {
-			// Create a dynamic exported field for the member type's property.
-			fields = append(fields, reflect.StructField{
-				Name: proptools.FieldNameForProperty(p),
-				Type: reflect.TypeOf([]string{}),
-				Tag:  `android:"arch_variant"`,
-			})
-
-			// Copy the field index for use in the getter func as using the loop variable directly will
-			// cause all funcs to use the last value.
-			fieldIndex := nextFieldIndex
-			nextFieldIndex += 1
-
-			getter = func(properties interface{}) []string {
-				// The properties is expected to be of the following form (where
-				// <Module_types> is the name of an SdkMemberType.SdkPropertyName().
-				//     properties *struct {<Module_types> []string, ....}
-				//
-				// Although it accesses the field by index the following reflection code is equivalent to:
-				//    *properties.<Module_types>
-				//
-				list := reflect.ValueOf(properties).Elem().Field(fieldIndex).Interface().([]string)
-				return list
-			}
-
-			setter = func(properties interface{}, list []string) {
-				// The properties is expected to be of the following form (where
-				// <Module_types> is the name of an SdkMemberType.SdkPropertyName().
-				//     properties *struct {<Module_types> []string, ....}
-				//
-				// Although it accesses the field by index the following reflection code is equivalent to:
-				//    *properties.<Module_types> = list
-				//
-				reflect.ValueOf(properties).Elem().Field(fieldIndex).Set(reflect.ValueOf(list))
-			}
-		}
-
-		// Create an sdkMemberListProperty for the member type.
-		memberListProperty := &sdkMemberListProperty{
-			getter:     getter,
-			setter:     setter,
-			memberType: memberType,
-
-			// Dependencies added directly from member properties are always exported.
-			dependencyTag: android.DependencyTagForSdkMemberType(memberType, true),
-		}
-
-		memberTypeToProperty[memberType] = memberListProperty
-		listProperties = append(listProperties, memberListProperty)
-	}
-
-	// Create a dynamic struct from the collated fields.
-	propertiesStructType := reflect.StructOf(fields)
-
-	return &dynamicSdkMemberTypes{
-		memberListProperties: listProperties,
-		memberTypeToProperty: memberTypeToProperty,
-		propertiesStructType: propertiesStructType,
-	}
-}
-
 // 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 SdkModuleFactory() android.Module {
@@ -247,16 +104,16 @@
 	s := &sdk{}
 	s.properties.Module_exports = moduleExports
 	// Get the dynamic sdk member type data for the currently registered sdk member types.
-	var registry *android.SdkMemberTypesRegistry
+	var typeRegistry *android.SdkMemberTypesRegistry
 	if moduleExports {
-		registry = android.ModuleExportsMemberTypes
+		typeRegistry = android.ModuleExportsMemberTypes
 	} else {
-		registry = android.SdkMemberTypes
+		typeRegistry = android.SdkMemberTypes
 	}
-	s.dynamicSdkMemberTypes = getDynamicSdkMemberTypes(registry)
+	s.dynamicSdkMemberTypes = getDynamicSdkMemberTypes(typeRegistry)
 	// 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.dynamicMemberTypeListProperties = s.dynamicSdkMemberTypes.createMemberTypeListProperties()
 	s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties)
 
 	// Make sure that the prebuilt visibility property is verified for errors.
@@ -280,11 +137,11 @@
 	return s
 }
 
-func (s *sdk) memberListProperties() []*sdkMemberListProperty {
-	return s.dynamicSdkMemberTypes.memberListProperties
+func (s *sdk) memberTypeListProperties() []*sdkMemberTypeListProperty {
+	return s.dynamicSdkMemberTypes.memberTypeListProperties
 }
 
-func (s *sdk) memberListProperty(memberType android.SdkMemberType) *sdkMemberListProperty {
+func (s *sdk) memberTypeListProperty(memberType android.SdkMemberType) *sdkMemberTypeListProperty {
 	return s.dynamicSdkMemberTypes.memberTypeToProperty[memberType]
 }
 
@@ -341,6 +198,19 @@
 	}}
 }
 
+// newDependencyContext creates a new SdkDependencyContext for this sdk.
+func (s *sdk) newDependencyContext(mctx android.BottomUpMutatorContext) android.SdkDependencyContext {
+	return &dependencyContext{
+		BottomUpMutatorContext: mctx,
+	}
+}
+
+type dependencyContext struct {
+	android.BottomUpMutatorContext
+}
+
+var _ android.SdkDependencyContext = (*dependencyContext)(nil)
+
 // RegisterPreDepsMutators registers pre-deps mutators to support modules implementing SdkAware
 // interface and the sdk module type. This function has been made public to be called by tests
 // outside of the sdk package
@@ -410,14 +280,15 @@
 	if s, ok := mctx.Module().(*sdk); ok {
 		// Add dependencies from enabled and non CommonOS variants to the sdk member variants.
 		if s.Enabled() && !s.IsCommonOSVariant() {
-			for _, memberListProperty := range s.memberListProperties() {
+			ctx := s.newDependencyContext(mctx)
+			for _, memberListProperty := range s.memberTypeListProperties() {
 				if memberListProperty.getter == nil {
 					continue
 				}
 				names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
 				if len(names) > 0 {
 					tag := memberListProperty.dependencyTag
-					memberListProperty.memberType.AddDependencies(mctx, tag, names)
+					memberListProperty.memberType.AddDependencies(ctx, tag, names)
 				}
 			}
 		}
diff --git a/sdk/update.go b/sdk/update.go
index 1cd8f13..96a6e69 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -251,7 +251,7 @@
 	}
 
 	var members []*sdkMember
-	for _, memberListProperty := range s.memberListProperties() {
+	for _, memberListProperty := range s.memberTypeListProperties() {
 		membersOfType := byType[memberListProperty.memberType]
 		members = append(members, membersOfType...)
 	}
@@ -667,7 +667,7 @@
 		staticProperties := &snapshotModuleStaticProperties{
 			Compile_multilib: sdkVariant.multilibUsages.String(),
 		}
-		dynamicProperties := s.dynamicSdkMemberTypes.createMemberListProperties()
+		dynamicProperties := s.dynamicSdkMemberTypes.createMemberTypeListProperties()
 
 		combinedProperties := &combinedSnapshotModuleProperties{
 			sdkVariant:        sdkVariant,
@@ -687,7 +687,7 @@
 		}
 
 		combined := sdkVariantToCombinedProperties[memberVariantDep.sdkVariant]
-		memberListProperty := s.memberListProperty(memberVariantDep.memberType)
+		memberListProperty := s.memberTypeListProperty(memberVariantDep.memberType)
 		memberName := ctx.OtherModuleName(memberVariantDep.variant)
 
 		if memberListProperty.getter == nil {
@@ -717,7 +717,7 @@
 	}
 
 	// Extract the common members, removing them from the original properties.
-	commonDynamicProperties := s.dynamicSdkMemberTypes.createMemberListProperties()
+	commonDynamicProperties := s.dynamicSdkMemberTypes.createMemberTypeListProperties()
 	extractor := newCommonValueExtractor(commonDynamicProperties)
 	extractCommonProperties(ctx, extractor, commonDynamicProperties, propertyContainers)
 
@@ -750,7 +750,7 @@
 	}
 
 	dynamicMemberTypeListProperties := combined.dynamicProperties
-	for _, memberListProperty := range s.memberListProperties() {
+	for _, memberListProperty := range s.memberTypeListProperties() {
 		if memberListProperty.getter == nil {
 			continue
 		}
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 610e427..b37a7f8 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -144,7 +144,7 @@
   run_soong
   local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
 
-  local glob_deps_file=out/soong/.bootstrap/globs/0.d
+  local glob_deps_file=out/soong/globs/build/0.d
 
   if [ -e "$glob_deps_file" ]; then
     fail "Glob deps file unexpectedly written on first build"
diff --git a/ui/build/config.go b/ui/build/config.go
index 6a05f4f..6de7a05 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -739,6 +739,20 @@
 	return filepath.Join(c.OutDir(), "soong")
 }
 
+func (c *configImpl) PrebuiltOS() string {
+	switch runtime.GOOS {
+	case "linux":
+		return "linux-x86"
+	case "darwin":
+		return "darwin-x86"
+	default:
+		panic("Unknown GOOS")
+	}
+}
+func (c *configImpl) HostToolDir() string {
+	return filepath.Join(c.SoongOutDir(), "host", c.PrebuiltOS(), "bin")
+}
+
 func (c *configImpl) MainNinjaFile() string {
 	return shared.JoinPath(c.SoongOutDir(), "build.ninja")
 }
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 058f819..d4f6f2f 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -71,9 +71,18 @@
 // A tiny struct used to tell Blueprint that it's in bootstrap mode. It would
 // probably be nicer to use a flag in bootstrap.Args instead.
 type BlueprintConfig struct {
-	soongOutDir      string
-	outDir           string
-	debugCompilation bool
+	toolDir                   string
+	soongOutDir               string
+	outDir                    string
+	runGoTests                bool
+	useValidations            bool
+	debugCompilation          bool
+	subninjas                 []string
+	primaryBuilderInvocations []bootstrap.PrimaryBuilderInvocation
+}
+
+func (c BlueprintConfig) HostToolDir() string {
+	return c.toolDir
 }
 
 func (c BlueprintConfig) SoongOutDir() string {
@@ -84,10 +93,26 @@
 	return c.outDir
 }
 
+func (c BlueprintConfig) RunGoTests() bool {
+	return c.runGoTests
+}
+
+func (c BlueprintConfig) UseValidationsForGoTests() bool {
+	return c.useValidations
+}
+
 func (c BlueprintConfig) DebugCompilation() bool {
 	return c.debugCompilation
 }
 
+func (c BlueprintConfig) Subninjas() []string {
+	return c.subninjas
+}
+
+func (c BlueprintConfig) PrimaryBuilderInvocations() []bootstrap.PrimaryBuilderInvocation {
+	return c.primaryBuilderInvocations
+}
+
 func environmentArgs(config Config, suffix string) []string {
 	return []string{
 		"--available_env", shared.JoinPath(config.SoongOutDir(), availableEnvFile),
@@ -128,16 +153,14 @@
 
 	args.RunGoTests = !config.skipSoongTests
 	args.UseValidations = true // Use validations to depend on tests
-	args.BuildDir = config.SoongOutDir()
-	args.NinjaBuildDir = config.OutDir()
-	args.TopFile = "Android.bp"
+	args.SoongOutDir = config.SoongOutDir()
+	args.OutDir = config.OutDir()
 	args.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list")
 	args.OutFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja")
 	// The primary builder (aka soong_build) will use bootstrapGlobFile as the globFile to generate build.ninja(.d)
 	// Building soong_build does not require a glob file
 	// Using "" instead of "<soong_build_glob>.ninja" will ensure that an unused glob file is not written to out/soong/.bootstrap during StagePrimary
 	args.Subninjas = []string{bootstrapGlobFile, bp2buildGlobFile}
-	args.GeneratingPrimaryBuilder = true
 	args.EmptyNinjaFile = config.EmptyNinjaFile()
 
 	args.DelveListen = os.Getenv("SOONG_DELVE")
@@ -153,7 +176,7 @@
 	}
 
 	soongBuildArgs := []string{
-		"--globListDir", "globs",
+		"--globListDir", "build",
 		"--globFile", bootstrapGlobFile,
 	}
 
@@ -169,7 +192,7 @@
 
 	bp2buildArgs := []string{
 		"--bp2build_marker", config.Bp2BuildMarkerFile(),
-		"--globListDir", "globs.bp2build",
+		"--globListDir", "bp2build",
 		"--globFile", bp2buildGlobFile,
 	}
 
@@ -185,7 +208,7 @@
 
 	moduleGraphArgs := []string{
 		"--module_graph_file", config.ModuleGraphFile(),
-		"--globListDir", "globs.modulegraph",
+		"--globListDir", "modulegraph",
 		"--globFile", moduleGraphGlobFile,
 	}
 
@@ -208,11 +231,17 @@
 	blueprintCtx := blueprint.NewContext()
 	blueprintCtx.SetIgnoreUnknownModuleTypes(true)
 	blueprintConfig := BlueprintConfig{
-		soongOutDir:      config.SoongOutDir(),
-		outDir:           config.OutDir(),
-		debugCompilation: os.Getenv("SOONG_DELVE") != "",
+		soongOutDir:               config.SoongOutDir(),
+		toolDir:                   config.HostToolDir(),
+		outDir:                    config.OutDir(),
+		runGoTests:                !config.skipSoongTests,
+		useValidations:            true,
+		debugCompilation:          os.Getenv("SOONG_DELVE") != "",
+		subninjas:                 args.Subninjas,
+		primaryBuilderInvocations: args.PrimaryBuilderInvocations,
 	}
 
+	args.EmptyNinjaFile = false
 	bootstrapDeps := bootstrap.RunBlueprint(args, blueprintCtx, blueprintConfig)
 	err := deptools.WriteDepFile(bootstrapDepFile, args.OutFile, bootstrapDeps)
 	if err != nil {
@@ -283,7 +312,7 @@
 		}
 	}()
 
-	runMicrofactory(ctx, config, ".bootstrap/bpglob", "github.com/google/blueprint/bootstrap/bpglob",
+	runMicrofactory(ctx, config, filepath.Join(config.HostToolDir(), "bpglob"), "github.com/google/blueprint/bootstrap/bpglob",
 		map[string]string{"github.com/google/blueprint": "build/blueprint"})
 
 	ninja := func(name, ninjaFile string, targets ...string) {
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index 57ceaba..f9a60b6 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -51,7 +51,6 @@
 	executable := config.PrebuiltBuildTool("ninja")
 
 	commonArgs := []string{}
-	commonArgs = append(commonArgs, config.NinjaArgs()...)
 	commonArgs = append(commonArgs, "-f", config.CombinedNinjaFile())
 	args := append(commonArgs, "-t", "targets", "rule")