Merge "Update allowed deps for sdkext apex"
diff --git a/android/defs.go b/android/defs.go
index 83daa03..2b1bd85 100644
--- a/android/defs.go
+++ b/android/defs.go
@@ -69,8 +69,9 @@
 	// A symlink rule.
 	Symlink = pctx.AndroidStaticRule("Symlink",
 		blueprint.RuleParams{
-			Command:     "rm -f $out && ln -f -s $fromPath $out",
-			Description: "symlink $out",
+			Command:        "rm -f $out && ln -f -s $fromPath $out",
+			Description:    "symlink $out",
+			SymlinkOutputs: []string{"$out"},
 		},
 		"fromPath")
 
diff --git a/android/module.go b/android/module.go
index 822e5bd..70a343e 100644
--- a/android/module.go
+++ b/android/module.go
@@ -43,6 +43,8 @@
 	Description     string
 	Output          WritablePath
 	Outputs         WritablePaths
+	SymlinkOutput   WritablePath
+	SymlinkOutputs  WritablePaths
 	ImplicitOutput  WritablePath
 	ImplicitOutputs WritablePaths
 	Input           Path
@@ -1763,6 +1765,27 @@
 	m.Build(pctx, BuildParams(params))
 }
 
+func validateBuildParams(params blueprint.BuildParams) error {
+	// Validate that the symlink outputs are declared outputs or implicit outputs
+	allOutputs := map[string]bool{}
+	for _, output := range params.Outputs {
+		allOutputs[output] = true
+	}
+	for _, output := range params.ImplicitOutputs {
+		allOutputs[output] = true
+	}
+	for _, symlinkOutput := range params.SymlinkOutputs {
+		if !allOutputs[symlinkOutput] {
+			return fmt.Errorf(
+				"Symlink output %s is not a declared output or implicit output",
+				symlinkOutput)
+		}
+	}
+	return nil
+}
+
+// Convert build parameters from their concrete Android types into their string representations,
+// and combine the singular and plural fields of the same type (e.g. Output and Outputs).
 func convertBuildParams(params BuildParams) blueprint.BuildParams {
 	bparams := blueprint.BuildParams{
 		Rule:            params.Rule,
@@ -1770,6 +1793,7 @@
 		Deps:            params.Deps,
 		Outputs:         params.Outputs.Strings(),
 		ImplicitOutputs: params.ImplicitOutputs.Strings(),
+		SymlinkOutputs:  params.SymlinkOutputs.Strings(),
 		Inputs:          params.Inputs.Strings(),
 		Implicits:       params.Implicits.Strings(),
 		OrderOnly:       params.OrderOnly.Strings(),
@@ -1784,6 +1808,9 @@
 	if params.Output != nil {
 		bparams.Outputs = append(bparams.Outputs, params.Output.String())
 	}
+	if params.SymlinkOutput != nil {
+		bparams.SymlinkOutputs = append(bparams.SymlinkOutputs, params.SymlinkOutput.String())
+	}
 	if params.ImplicitOutput != nil {
 		bparams.ImplicitOutputs = append(bparams.ImplicitOutputs, params.ImplicitOutput.String())
 	}
@@ -1799,6 +1826,7 @@
 
 	bparams.Outputs = proptools.NinjaEscapeList(bparams.Outputs)
 	bparams.ImplicitOutputs = proptools.NinjaEscapeList(bparams.ImplicitOutputs)
+	bparams.SymlinkOutputs = proptools.NinjaEscapeList(bparams.SymlinkOutputs)
 	bparams.Inputs = proptools.NinjaEscapeList(bparams.Inputs)
 	bparams.Implicits = proptools.NinjaEscapeList(bparams.Implicits)
 	bparams.OrderOnly = proptools.NinjaEscapeList(bparams.OrderOnly)
@@ -1855,7 +1883,15 @@
 		m.buildParams = append(m.buildParams, params)
 	}
 
-	m.bp.Build(pctx.PackageContext, convertBuildParams(params))
+	bparams := convertBuildParams(params)
+	err := validateBuildParams(bparams)
+	if err != nil {
+		m.ModuleErrorf(
+			"%s: build parameter validation failed: %s",
+			m.ModuleName(),
+			err.Error())
+	}
+	m.bp.Build(pctx.PackageContext, bparams)
 }
 
 func (m *moduleContext) Phony(name string, deps ...Path) {
diff --git a/android/module_test.go b/android/module_test.go
index 6e648d7..3a039e2 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -187,3 +187,49 @@
 	_, errs = ctx.PrepareBuildActions(config)
 	FailIfNoMatchingErrors(t, `module "foo": depends on disabled module "bar"`, errs)
 }
+
+func TestValidateCorrectBuildParams(t *testing.T) {
+	config := TestConfig(buildDir, nil, "", nil)
+	pathContext := PathContextForTesting(config)
+	bparams := convertBuildParams(BuildParams{
+		// Test with Output
+		Output:        PathForOutput(pathContext, "undeclared_symlink"),
+		SymlinkOutput: PathForOutput(pathContext, "undeclared_symlink"),
+	})
+
+	err := validateBuildParams(bparams)
+	if err != nil {
+		t.Error(err)
+	}
+
+	bparams = convertBuildParams(BuildParams{
+		// Test with ImplicitOutput
+		ImplicitOutput: PathForOutput(pathContext, "undeclared_symlink"),
+		SymlinkOutput:  PathForOutput(pathContext, "undeclared_symlink"),
+	})
+
+	err = validateBuildParams(bparams)
+	if err != nil {
+		t.Error(err)
+	}
+}
+
+func TestValidateIncorrectBuildParams(t *testing.T) {
+	config := TestConfig(buildDir, nil, "", nil)
+	pathContext := PathContextForTesting(config)
+	params := BuildParams{
+		Output:          PathForOutput(pathContext, "regular_output"),
+		Outputs:         PathsForOutput(pathContext, []string{"out1", "out2"}),
+		ImplicitOutput:  PathForOutput(pathContext, "implicit_output"),
+		ImplicitOutputs: PathsForOutput(pathContext, []string{"i_out1", "_out2"}),
+		SymlinkOutput:   PathForOutput(pathContext, "undeclared_symlink"),
+	}
+
+	bparams := convertBuildParams(params)
+	err := validateBuildParams(bparams)
+	if err != nil {
+		FailIfNoMatchingErrors(t, "undeclared_symlink is not a declared output or implicit output", []error{err})
+	} else {
+		t.Errorf("Expected build params to fail validation: %+v", bparams)
+	}
+}
diff --git a/android/rule_builder.go b/android/rule_builder.go
index afb5f4e..8dc9d6a 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -246,6 +246,41 @@
 	return outputList
 }
 
+func (r *RuleBuilder) symlinkOutputSet() map[string]WritablePath {
+	symlinkOutputs := make(map[string]WritablePath)
+	for _, c := range r.commands {
+		for _, symlinkOutput := range c.symlinkOutputs {
+			symlinkOutputs[symlinkOutput.String()] = symlinkOutput
+		}
+	}
+	return symlinkOutputs
+}
+
+// SymlinkOutputs returns the list of paths that the executor (Ninja) would
+// verify, after build edge completion, that:
+//
+// 1) Created output symlinks match the list of paths in this list exactly (no more, no fewer)
+// 2) Created output files are *not* declared in this list.
+//
+// These symlink outputs are expected to be a subset of outputs or implicit
+// outputs, or they would fail validation at build param construction time
+// later, to support other non-rule-builder approaches for constructing
+// statements.
+func (r *RuleBuilder) SymlinkOutputs() WritablePaths {
+	symlinkOutputs := r.symlinkOutputSet()
+
+	var symlinkOutputList WritablePaths
+	for _, symlinkOutput := range symlinkOutputs {
+		symlinkOutputList = append(symlinkOutputList, symlinkOutput)
+	}
+
+	sort.Slice(symlinkOutputList, func(i, j int) bool {
+		return symlinkOutputList[i].String() < symlinkOutputList[j].String()
+	})
+
+	return symlinkOutputList
+}
+
 func (r *RuleBuilder) depFileSet() map[string]WritablePath {
 	depFiles := make(map[string]WritablePath)
 	for _, c := range r.commands {
@@ -467,6 +502,7 @@
 		Implicits:       r.Inputs(),
 		Output:          output,
 		ImplicitOutputs: implicitOutputs,
+		SymlinkOutputs:  r.SymlinkOutputs(),
 		Depfile:         depFile,
 		Deps:            depFormat,
 		Description:     desc,
@@ -478,14 +514,15 @@
 // RuleBuilderCommand, so they can be used chained or unchained.  All methods that add text implicitly add a single
 // space as a separator from the previous method.
 type RuleBuilderCommand struct {
-	buf           strings.Builder
-	inputs        Paths
-	implicits     Paths
-	orderOnlys    Paths
-	outputs       WritablePaths
-	depFiles      WritablePaths
-	tools         Paths
-	rspFileInputs Paths
+	buf            strings.Builder
+	inputs         Paths
+	implicits      Paths
+	orderOnlys     Paths
+	outputs        WritablePaths
+	symlinkOutputs WritablePaths
+	depFiles       WritablePaths
+	tools          Paths
+	rspFileInputs  Paths
 
 	// spans [start,end) of the command that should not be ninja escaped
 	unescapedSpans [][2]int
@@ -715,6 +752,40 @@
 	return c
 }
 
+// ImplicitSymlinkOutput declares the specified path as an implicit output that
+// will be a symlink instead of a regular file. Does not modify the command
+// line.
+func (c *RuleBuilderCommand) ImplicitSymlinkOutput(path WritablePath) *RuleBuilderCommand {
+	c.symlinkOutputs = append(c.symlinkOutputs, path)
+	return c.ImplicitOutput(path)
+}
+
+// ImplicitSymlinkOutputs declares the specified paths as implicit outputs that
+// will be a symlinks instead of regular files. Does not modify the command
+// line.
+func (c *RuleBuilderCommand) ImplicitSymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
+	for _, path := range paths {
+		c.ImplicitSymlinkOutput(path)
+	}
+	return c
+}
+
+// SymlinkOutput declares the specified path as an output that will be a symlink
+// instead of a regular file. Modifies the command line.
+func (c *RuleBuilderCommand) SymlinkOutput(path WritablePath) *RuleBuilderCommand {
+	c.symlinkOutputs = append(c.symlinkOutputs, path)
+	return c.Output(path)
+}
+
+// SymlinkOutputsl declares the specified paths as outputs that will be symlinks
+// instead of regular files. Modifies the command line.
+func (c *RuleBuilderCommand) SymlinkOutputs(paths WritablePaths) *RuleBuilderCommand {
+	for _, path := range paths {
+		c.SymlinkOutput(path)
+	}
+	return c
+}
+
 // ImplicitDepFile adds the specified depfile path to the paths returned by RuleBuilder.DepFiles without modifying
 // the command line, and causes RuleBuilder.Build file to set the depfile flag for ninja.  If multiple depfiles
 // are added to commands in a single RuleBuilder then RuleBuilder.Build will add an extra command to merge the
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index c41b067..0d1070d 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -35,6 +35,7 @@
 		"a":       nil,
 		"b":       nil,
 		"ls":      nil,
+		"ln":      nil,
 		"turbine": nil,
 		"java":    nil,
 		"javac":   nil,
@@ -67,6 +68,32 @@
 	// outputs: ["out/linked"]
 }
 
+func ExampleRuleBuilder_SymlinkOutputs() {
+	rule := NewRuleBuilder()
+
+	ctx := pathContext()
+
+	rule.Command().
+		Tool(PathForSource(ctx, "ln")).
+		FlagWithInput("-s ", PathForTesting("a.o")).
+		SymlinkOutput(PathForOutput(ctx, "a"))
+	rule.Command().Text("cp out/a out/b").
+		ImplicitSymlinkOutput(PathForOutput(ctx, "b"))
+
+	fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
+	fmt.Printf("tools: %q\n", rule.Tools())
+	fmt.Printf("inputs: %q\n", rule.Inputs())
+	fmt.Printf("outputs: %q\n", rule.Outputs())
+	fmt.Printf("symlink_outputs: %q\n", rule.SymlinkOutputs())
+
+	// Output:
+	// commands: "ln -s a.o out/a && cp out/a out/b"
+	// tools: ["ln"]
+	// inputs: ["a.o"]
+	// outputs: ["out/a" "out/b"]
+	// symlink_outputs: ["out/a" "out/b"]
+}
+
 func ExampleRuleBuilder_Temporary() {
 	rule := NewRuleBuilder()
 
@@ -293,6 +320,8 @@
 			Input(PathForSource(ctx, "Input")).
 			Output(PathForOutput(ctx, "Output")).
 			OrderOnly(PathForSource(ctx, "OrderOnly")).
+			SymlinkOutput(PathForOutput(ctx, "SymlinkOutput")).
+			ImplicitSymlinkOutput(PathForOutput(ctx, "ImplicitSymlinkOutput")).
 			Text("Text").
 			Tool(PathForSource(ctx, "Tool"))
 
@@ -318,17 +347,18 @@
 	}
 
 	wantInputs := PathsForSource(ctx, []string{"Implicit", "Input", "input", "input2", "input3"})
-	wantOutputs := PathsForOutput(ctx, []string{"ImplicitOutput", "Output", "output", "output2", "output3"})
+	wantOutputs := PathsForOutput(ctx, []string{"ImplicitOutput", "ImplicitSymlinkOutput", "Output", "SymlinkOutput", "output", "output2", "output3"})
 	wantDepFiles := PathsForOutput(ctx, []string{"DepFile", "depfile", "ImplicitDepFile", "depfile2"})
 	wantTools := PathsForSource(ctx, []string{"Tool", "tool2"})
 	wantOrderOnlys := PathsForSource(ctx, []string{"OrderOnly", "OrderOnlys"})
+	wantSymlinkOutputs := PathsForOutput(ctx, []string{"ImplicitSymlinkOutput", "SymlinkOutput"})
 
 	t.Run("normal", func(t *testing.T) {
 		rule := NewRuleBuilder()
 		addCommands(rule)
 
 		wantCommands := []string{
-			"out/DepFile Flag FlagWithArg=arg FlagWithDepFile=out/depfile FlagWithInput=input FlagWithOutput=out/output Input out/Output Text Tool after command2 old cmd",
+			"out/DepFile Flag FlagWithArg=arg FlagWithDepFile=out/depfile FlagWithInput=input FlagWithOutput=out/output Input out/Output out/SymlinkOutput Text Tool after command2 old cmd",
 			"command2 out/depfile2 input2 out/output2 tool2",
 			"command3 input3 out/output2 out/output3",
 		}
@@ -345,6 +375,9 @@
 		if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
 			t.Errorf("\nwant rule.Outputs() = %#v\n                  got %#v", w, g)
 		}
+		if g, w := rule.SymlinkOutputs(), wantSymlinkOutputs; !reflect.DeepEqual(w, g) {
+			t.Errorf("\nwant rule.SymlinkOutputs() = %#v\n                  got %#v", w, g)
+		}
 		if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
 			t.Errorf("\nwant rule.DepFiles() = %#v\n                  got %#v", w, g)
 		}
@@ -365,7 +398,7 @@
 		addCommands(rule)
 
 		wantCommands := []string{
-			"__SBOX_OUT_DIR__/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_OUT_DIR__/depfile FlagWithInput=input FlagWithOutput=__SBOX_OUT_DIR__/output Input __SBOX_OUT_DIR__/Output Text Tool after command2 old cmd",
+			"__SBOX_OUT_DIR__/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_OUT_DIR__/depfile FlagWithInput=input FlagWithOutput=__SBOX_OUT_DIR__/output Input __SBOX_OUT_DIR__/Output __SBOX_OUT_DIR__/SymlinkOutput Text Tool after command2 old cmd",
 			"command2 __SBOX_OUT_DIR__/depfile2 input2 __SBOX_OUT_DIR__/output2 tool2",
 			"command3 input3 __SBOX_OUT_DIR__/output2 __SBOX_OUT_DIR__/output3",
 		}
diff --git a/android/singleton.go b/android/singleton.go
index 9832378..bb6614d 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -168,6 +168,10 @@
 		s.buildParams = append(s.buildParams, params)
 	}
 	bparams := convertBuildParams(params)
+	err := validateBuildParams(bparams)
+	if err != nil {
+		s.Errorf("%s: build parameter validation failed: %s", s.Name(), err.Error())
+	}
 	s.SingletonContext.Build(pctx.PackageContext, bparams)
 
 }
diff --git a/android/variable.go b/android/variable.go
index a9495cc..73062ed 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -90,6 +90,11 @@
 			Required        []string
 			Host_required   []string
 			Target_required []string
+			Strip           struct {
+				All                          *bool
+				Keep_symbols                 *bool
+				Keep_symbols_and_debug_frame *bool
+			}
 		}
 
 		// eng is true for -eng builds, and can be used to turn on additionaly heavyweight debugging
diff --git a/cc/builder.go b/cc/builder.go
index 8c8aa17..81c09b1 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -44,14 +44,14 @@
 		blueprint.RuleParams{
 			Depfile:     "${out}.d",
 			Deps:        blueprint.DepsGCC,
-			Command:     "${config.CcWrapper}$ccCmd -c $cFlags -MD -MF ${out}.d -o $out $in",
+			Command:     "$relPwd ${config.CcWrapper}$ccCmd -c $cFlags -MD -MF ${out}.d -o $out $in",
 			CommandDeps: []string{"$ccCmd"},
 		},
 		"ccCmd", "cFlags")
 
 	ccNoDeps = pctx.AndroidStaticRule("ccNoDeps",
 		blueprint.RuleParams{
-			Command:     "$ccCmd -c $cFlags -o $out $in",
+			Command:     "$relPwd $ccCmd -c $cFlags -o $out $in",
 			CommandDeps: []string{"$ccCmd"},
 		},
 		"ccCmd", "cFlags")
@@ -220,7 +220,7 @@
 			Labels:       map[string]string{"type": "abi-dump", "tool": "header-abi-dumper"},
 			ExecStrategy: "${config.REAbiDumperExecStrategy}",
 			Platform: map[string]string{
-				remoteexec.PoolKey: "${config.RECXXPool}",
+				remoteexec.PoolKey:      "${config.RECXXPool}",
 			},
 		}, []string{"cFlags", "exportDirs"}, nil)
 
diff --git a/cc/config/global.go b/cc/config/global.go
index 1aa2621..a170652 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -53,13 +53,6 @@
 		"-Werror=pragma-pack",
 		"-Werror=pragma-pack-suspicious-include",
 		"-Werror=unreachable-code-loop-increment",
-
-		// -fdebug-compilation-dir=. is used to make both the action command line and the output
-		// independent of the working directory of the action.
-		// Using cc1 flags since RBE's input processor does not yet have the updated version
-		// of LLVM that promotes the cc1 flag to driver level flag.
-		// See: https://reviews.llvm.org/D63387
-		"-Xclang,-fdebug-compilation-dir,.",
 	}
 
 	commonGlobalConlyflags = []string{}
@@ -157,6 +150,10 @@
 var pctx = android.NewPackageContext("android/soong/cc/config")
 
 func init() {
+	if android.BuildOs == android.Linux {
+		commonGlobalCflags = append(commonGlobalCflags, "-fdebug-prefix-map=/proc/self/cwd=")
+	}
+
 	pctx.StaticVariable("CommonGlobalConlyflags", strings.Join(commonGlobalConlyflags, " "))
 	pctx.StaticVariable("DeviceGlobalCppflags", strings.Join(deviceGlobalCppflags, " "))
 	pctx.StaticVariable("DeviceGlobalLdflags", strings.Join(deviceGlobalLdflags, " "))
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index 4a8d3cd..47110c9 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -87,7 +87,7 @@
 	ruleFile := "boot-foo.art"
 
 	expectedInputs := []string{
-		"dex_artjars/android/apex/com.android.art/javalib/arm64/boot.art",
+		"dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
 		"dex_bootjars_input/foo.jar",
 		"dex_bootjars_input/bar.jar",
 		"dex_bootjars_input/baz.jar",
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index f0d82ff..0f8888a 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -89,7 +89,7 @@
 		frameworkModules := global.BootJars.CopyOf()
 		frameworkModules.RemoveList(artModules)
 
-		artSubdir := "apex/com.android.art/javalib"
+		artSubdir := "apex/art_boot_images/javalib"
 		frameworkSubdir := "system/framework"
 
 		// ART config for the primary boot image in the ART apex.
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 480f9b7..edb8b30 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -404,13 +404,21 @@
 	// ctx's Platform or Specific functions represent where this sysprop_library installed.
 	installedInSystem := ctx.Platform() || ctx.SystemExtSpecific()
 	installedInVendorOrOdm := ctx.SocSpecific() || ctx.DeviceSpecific()
+	installedInProduct := ctx.ProductSpecific()
 	isOwnerPlatform := false
-	stub := "sysprop-library-stub-"
+	var stub string
+
+	if installedInVendorOrOdm {
+		stub = "sysprop-library-stub-vendor"
+	} else if installedInProduct {
+		stub = "sysprop-library-stub-product"
+	} else {
+		stub = "sysprop-library-stub-platform"
+	}
 
 	switch m.Owner() {
 	case "Platform":
 		// Every partition can access platform-defined properties
-		stub += "platform"
 		isOwnerPlatform = true
 	case "Vendor":
 		// System can't access vendor's properties
@@ -418,14 +426,12 @@
 			ctx.ModuleErrorf("None of soc_specific, device_specific, product_specific is true. " +
 				"System can't access sysprop_library owned by Vendor")
 		}
-		stub += "vendor"
 	case "Odm":
 		// Only vendor can access Odm-defined properties
 		if !installedInVendorOrOdm {
 			ctx.ModuleErrorf("Neither soc_speicifc nor device_specific is true. " +
 				"Odm-defined properties should be accessed only in Vendor or Odm")
 		}
-		stub += "vendor"
 	default:
 		ctx.PropertyErrorf("property_owner",
 			"Unknown value %s: must be one of Platform, Vendor or Odm", m.Owner())
diff --git a/sysprop/sysprop_test.go b/sysprop/sysprop_test.go
index 711129c..3be1945 100644
--- a/sysprop/sysprop_test.go
+++ b/sysprop/sysprop_test.go
@@ -103,6 +103,8 @@
 		"api/sysprop-platform-on-product-latest.txt":  nil,
 		"api/sysprop-vendor-current.txt":              nil,
 		"api/sysprop-vendor-latest.txt":               nil,
+		"api/sysprop-vendor-on-product-current.txt":   nil,
+		"api/sysprop-vendor-on-product-latest.txt":    nil,
 		"api/sysprop-odm-current.txt":                 nil,
 		"api/sysprop-odm-latest.txt":                  nil,
 		"framework/aidl/a.aidl":                       nil,
@@ -182,8 +184,15 @@
 			srcs: ["com/android/VendorProperties.sysprop"],
 			api_packages: ["com.android"],
 			property_owner: "Vendor",
+			vendor: true,
+		}
+
+		sysprop_library {
+			name: "sysprop-vendor-on-product",
+			srcs: ["com/android/VendorProperties.sysprop"],
+			api_packages: ["com.android"],
+			property_owner: "Vendor",
 			product_specific: true,
-			vendor_available: true,
 		}
 
 		sysprop_library {
@@ -213,7 +222,7 @@
 			srcs: ["c.java"],
 			sdk_version: "system_current",
 			product_specific: true,
-			libs: ["sysprop-platform", "sysprop-vendor"],
+			libs: ["sysprop-platform", "sysprop-vendor-on-product"],
 		}
 
 		java_library {
@@ -240,7 +249,7 @@
 			name: "cc-client-product",
 			srcs: ["d.cpp"],
 			product_specific: true,
-			static_libs: ["sysprop-platform-on-product", "sysprop-vendor"],
+			static_libs: ["sysprop-platform-on-product", "sysprop-vendor-on-product"],
 		}
 
 		cc_library {
@@ -290,6 +299,12 @@
 			soc_specific: true,
 			sdk_version: "core_current",
 		}
+
+		java_library {
+			name: "sysprop-library-stub-product",
+			product_specific: true,
+			sdk_version: "core_current",
+		}
 		`)
 
 	// Check for generated cc_library
@@ -317,13 +332,14 @@
 				expectedApexAvailableOnLibrary, library.ApexProperties.Apex_available)
 		}
 
-		// core variant of vendor-owned sysprop_library is for product
-		ctx.ModuleForTests("libsysprop-vendor", variant)
+		// product variant of vendor-owned sysprop_library
+		ctx.ModuleForTests("libsysprop-vendor-on-product", variant)
 	}
 
 	ctx.ModuleForTests("sysprop-platform", "android_common")
 	ctx.ModuleForTests("sysprop-platform_public", "android_common")
 	ctx.ModuleForTests("sysprop-vendor", "android_common")
+	ctx.ModuleForTests("sysprop-vendor-on-product", "android_common")
 
 	// Check for exported includes
 	coreVariant := "android_arm64_armv8-a_static"
@@ -336,7 +352,7 @@
 	platformOnProductPath := "libsysprop-platform-on-product/android_arm64_armv8-a_static/gen/sysprop/public/include"
 
 	vendorInternalPath := "libsysprop-vendor/android_vendor.VER_arm64_armv8-a_static/gen/sysprop/include"
-	vendorPublicPath := "libsysprop-vendor/android_arm64_armv8-a_static/gen/sysprop/public/include"
+	vendorPublicPath := "libsysprop-vendor-on-product/android_arm64_armv8-a_static/gen/sysprop/public/include"
 
 	platformClient := ctx.ModuleForTests("cc-client-platform", coreVariant)
 	platformFlags := platformClient.Rule("cc").Args["cFlags"]
diff --git a/ui/build/kati.go b/ui/build/kati.go
index f6d3a57..f6c0f52 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -68,6 +68,7 @@
 		"--ninja_suffix=" + config.KatiSuffix() + extraSuffix,
 		"--no_ninja_prelude",
 		"--use_ninja_phony_output",
+		"--use_ninja_symlink_outputs",
 		"--regen",
 		"--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"),
 		"--detect_android_echo",