Merge "Add SOONG_SDK_SNAPSHOT_PREFER support"
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 0551a18..364929c 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -302,6 +302,37 @@
     version_script = "v.map",
 )`},
 		},
+		{
+			description:                        "cc_library shared_libs",
+			moduleTypeUnderTest:                "cc_library",
+			moduleTypeUnderTestFactory:         cc.LibraryFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+			dir:                                "foo/bar",
+			filesystem: map[string]string{
+				"foo/bar/Android.bp": `
+cc_library {
+    name: "mylib",
+    bazel_module: { bp2build_available: true },
+}
+
+cc_library {
+    name: "a",
+    shared_libs: ["mylib",],
+    bazel_module: { bp2build_available: true },
+}
+`,
+			},
+			bp: soongCcLibraryPreamble,
+			expectedBazelTargets: []string{`cc_library(
+    name = "a",
+    copts = ["-Ifoo/bar"],
+    dynamic_deps = [":mylib"],
+)`, `cc_library(
+    name = "mylib",
+    copts = ["-Ifoo/bar"],
+)`},
+		},
 	}
 
 	dir := "."
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 1433f6f..7f2554f 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -247,6 +247,7 @@
 // Convenience struct to hold all attributes parsed from linker properties.
 type linkerAttributes struct {
 	deps          bazel.LabelListAttribute
+	dynamicDeps   bazel.LabelListAttribute
 	linkopts      bazel.StringListAttribute
 	versionScript bazel.LabelAttribute
 }
@@ -255,6 +256,7 @@
 // configurable attribute values.
 func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) linkerAttributes {
 	var deps bazel.LabelListAttribute
+	var dynamicDeps bazel.LabelListAttribute
 	var linkopts bazel.StringListAttribute
 	var versionScript bazel.LabelAttribute
 
@@ -266,11 +268,16 @@
 			libs = append(libs, baseLinkerProps.Whole_static_libs...)
 			libs = android.SortedUniqueStrings(libs)
 			deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, libs))
+
 			linkopts.Value = baseLinkerProps.Ldflags
 
 			if baseLinkerProps.Version_script != nil {
 				versionScript.Value = android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script)
 			}
+
+			sharedLibs := baseLinkerProps.Shared_libs
+			dynamicDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, sharedLibs))
+
 			break
 		}
 	}
@@ -283,10 +290,15 @@
 			libs = append(libs, baseLinkerProps.Whole_static_libs...)
 			libs = android.SortedUniqueStrings(libs)
 			deps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, libs))
+
 			linkopts.SetValueForArch(arch.Name, baseLinkerProps.Ldflags)
+
 			if baseLinkerProps.Version_script != nil {
 				versionScript.SetValueForArch(arch.Name,
 					android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
+
+				sharedLibs := baseLinkerProps.Shared_libs
+				dynamicDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs))
 			}
 		}
 	}
@@ -299,12 +311,17 @@
 			libs = append(libs, baseLinkerProps.Whole_static_libs...)
 			libs = android.SortedUniqueStrings(libs)
 			deps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, libs))
+
 			linkopts.SetValueForOS(os.Name, baseLinkerProps.Ldflags)
+
+			sharedLibs := baseLinkerProps.Shared_libs
+			dynamicDeps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs))
 		}
 	}
 
 	return linkerAttributes{
 		deps:          deps,
+		dynamicDeps:   dynamicDeps,
 		linkopts:      linkopts,
 		versionScript: versionScript,
 	}
diff --git a/cc/builder.go b/cc/builder.go
index 0542015..29cde9d 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -143,7 +143,7 @@
 			}(),
 			Pool: darwinStripPool,
 		},
-		"args", "crossCompile")
+		"args")
 
 	// Rule to invoke `strip` (to discard symbols and data from object files) on darwin architecture.
 	darwinStrip = pctx.AndroidStaticRule("darwinStrip",
@@ -993,7 +993,6 @@
 func transformStrip(ctx android.ModuleContext, inputFile android.Path,
 	outputFile android.WritablePath, flags StripFlags) {
 
-	crossCompile := gccCmd(flags.Toolchain, "")
 	args := ""
 	if flags.StripAddGnuDebuglink {
 		args += " --add-gnu-debuglink"
@@ -1010,9 +1009,6 @@
 	if flags.StripKeepSymbolsAndDebugFrame {
 		args += " --keep-symbols-and-debug-frame"
 	}
-	if flags.StripUseGnuStrip {
-		args += " --use-gnu-strip"
-	}
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        strip,
@@ -1020,8 +1016,7 @@
 		Output:      outputFile,
 		Input:       inputFile,
 		Args: map[string]string{
-			"crossCompile": crossCompile,
-			"args":         args,
+			"args": args,
 		},
 	})
 }
diff --git a/cc/library.go b/cc/library.go
index 7b631fa..7e960a7 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -225,6 +225,7 @@
 	Copts                  bazel.StringListAttribute
 	Linkopts               bazel.StringListAttribute
 	Deps                   bazel.LabelListAttribute
+	Dynamic_deps           bazel.LabelListAttribute
 	User_link_flags        bazel.StringListAttribute
 	Includes               bazel.StringListAttribute
 	Static_deps_for_shared bazel.LabelListAttribute
@@ -282,6 +283,7 @@
 		Copts:                  compilerAttrs.copts,
 		Linkopts:               linkerAttrs.linkopts,
 		Deps:                   linkerAttrs.deps,
+		Dynamic_deps:           linkerAttrs.dynamicDeps,
 		Version_script:         linkerAttrs.versionScript,
 		Static_deps_for_shared: sharedAttrs.staticDeps,
 		Includes:               exportedIncludes,
diff --git a/cc/makevars.go b/cc/makevars.go
index 2b7bb9b..da5f1fd 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -285,11 +285,14 @@
 	}
 
 	if target.Os.Class == android.Device {
-		ctx.Strict(makePrefix+"OBJCOPY", gccCmd(toolchain, "objcopy"))
-		ctx.Strict(makePrefix+"LD", gccCmd(toolchain, "ld"))
-		ctx.Strict(makePrefix+"GCC_VERSION", toolchain.GccVersion())
+		ctx.Strict(makePrefix+"OBJCOPY", "${config.ClangBin}/llvm-objcopy")
+		ctx.Strict(makePrefix+"LD", "${config.ClangBin}/lld")
 		ctx.Strict(makePrefix+"NDK_TRIPLE", config.NDKTriple(toolchain))
+		// TODO: work out whether to make this "${config.ClangBin}/llvm-", which
+		// should mostly work, or remove it.
 		ctx.Strict(makePrefix+"TOOLS_PREFIX", gccCmd(toolchain, ""))
+		// TODO: GCC version is obsolete now that GCC has been removed.
+		ctx.Strict(makePrefix+"GCC_VERSION", toolchain.GccVersion())
 	}
 
 	if target.Os.Class == android.Host {
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 24492d4..1844bce 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -130,9 +130,9 @@
 	ProvidesUsesLibrary            string       // library name (usually the same as module name)
 	ClassLoaderContexts            ClassLoaderContextMap
 
-	Archs                   []android.ArchType
-	DexPreoptImagesDeps     []android.OutputPaths
-	DexPreoptImageLocations []string
+	Archs                         []android.ArchType
+	DexPreoptImagesDeps           []android.OutputPaths
+	DexPreoptImageLocationsOnHost []string // boot image location on host (file path without the arch subdirectory)
 
 	PreoptBootClassPathDexFiles     android.Paths // file paths of boot class path files
 	PreoptBootClassPathDexLocations []string      // virtual locations of boot class path files
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index c9a80f8..9d9234f 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -330,7 +330,7 @@
 		Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", module.PreoptBootClassPathDexLocations, ":").
 		Flag("${class_loader_context_arg}").
 		Flag("${stored_class_loader_context_arg}").
-		FlagWithArg("--boot-image=", strings.Join(module.DexPreoptImageLocations, ":")).Implicits(module.DexPreoptImagesDeps[archIdx].Paths()).
+		FlagWithArg("--boot-image=", strings.Join(module.DexPreoptImageLocationsOnHost, ":")).Implicits(module.DexPreoptImagesDeps[archIdx].Paths()).
 		FlagWithInput("--dex-file=", module.DexPath).
 		FlagWithArg("--dex-location=", dexLocationArg).
 		FlagWithOutput("--oat-file=", odexPath).ImplicitOutput(vdexPath).
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index bb4e80e..4ee61b6 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -48,7 +48,7 @@
 		ClassLoaderContexts:             nil,
 		Archs:                           []android.ArchType{android.Arm},
 		DexPreoptImagesDeps:             []android.OutputPaths{android.OutputPaths{}},
-		DexPreoptImageLocations:         []string{},
+		DexPreoptImageLocationsOnHost:   []string{},
 		PreoptBootClassPathDexFiles:     nil,
 		PreoptBootClassPathDexLocations: nil,
 		PreoptExtractedApk:              false,
diff --git a/java/Android.bp b/java/Android.bp
index a17140c..623a6c5 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -65,6 +65,7 @@
         "sdk_library_external.go",
         "support_libraries.go",
         "system_modules.go",
+        "systemserver_classpath_fragment.go",
         "testing.go",
         "tradefed.go",
     ],
@@ -91,6 +92,7 @@
         "rro_test.go",
         "sdk_test.go",
         "system_modules_test.go",
+        "systemserver_classpath_fragment_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index 460cc3e..2e05823 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -51,6 +51,10 @@
 	android.Module
 
 	classpathFragmentBase() *ClasspathFragmentBase
+
+	// ClasspathFragmentToConfiguredJarList returns android.ConfiguredJarList representation of all
+	// the jars in this classpath fragment.
+	ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList
 }
 
 // ClasspathFragmentBase is meant to be embedded in any module types that implement classpathFragment;
@@ -58,6 +62,8 @@
 type ClasspathFragmentBase struct {
 	properties classpathFragmentProperties
 
+	classpathType classpathType
+
 	outputFilepath android.OutputPath
 	installDirPath android.InstallPath
 }
@@ -67,8 +73,9 @@
 }
 
 // Initializes ClasspathFragmentBase struct. Must be called by all modules that include ClasspathFragmentBase.
-func initClasspathFragment(c classpathFragment) {
+func initClasspathFragment(c classpathFragment, classpathType classpathType) {
 	base := c.classpathFragmentBase()
+	base.classpathType = classpathType
 	c.AddProperties(&base.properties)
 }
 
@@ -81,16 +88,26 @@
 	maxSdkVersion int32
 }
 
-func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext) {
+// Converts android.ConfiguredJarList into a list of classpathJars for each given classpathType.
+func configuredJarListToClasspathJars(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, classpaths ...classpathType) []classpathJar {
+	paths := configuredJars.DevicePaths(ctx.Config(), android.Android)
+	jars := make([]classpathJar, 0, len(paths)*len(classpaths))
+	for i := 0; i < len(paths); i++ {
+		for _, classpathType := range classpaths {
+			jars = append(jars, classpathJar{
+				classpath: classpathType,
+				path:      paths[i],
+			})
+		}
+	}
+	return jars
+}
+
+func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, jars []classpathJar) {
 	outputFilename := ctx.ModuleName() + ".pb"
 	c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
 	c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
 
-	var jars []classpathJar
-	jars = appendClasspathJar(jars, BOOTCLASSPATH, defaultBootclasspath(ctx)...)
-	jars = appendClasspathJar(jars, DEX2OATBOOTCLASSPATH, defaultBootImageConfig(ctx).getAnyAndroidVariant().dexLocationsDeps...)
-	jars = appendClasspathJar(jars, SYSTEMSERVERCLASSPATH, systemServerClasspath(ctx)...)
-
 	generatedJson := android.PathForModuleOut(ctx, outputFilename+".json")
 	writeClasspathsJson(ctx, generatedJson, jars)
 
@@ -126,19 +143,8 @@
 	android.WriteFileRule(ctx, output, content.String())
 }
 
-func appendClasspathJar(slice []classpathJar, classpathType classpathType, paths ...string) (result []classpathJar) {
-	result = append(result, slice...)
-	for _, path := range paths {
-		result = append(result, classpathJar{
-			path:      path,
-			classpath: classpathType,
-		})
-	}
-	return
-}
-
 func (c *ClasspathFragmentBase) androidMkEntries() []android.AndroidMkEntries {
-	return []android.AndroidMkEntries{android.AndroidMkEntries{
+	return []android.AndroidMkEntries{{
 		Class:      "ETC",
 		OutputFile: android.OptionalPathForPath(c.outputFilepath),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 3113eb3..8d23ad6 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -180,11 +180,11 @@
 	for _, target := range targets {
 		archs = append(archs, target.Arch.ArchType)
 		variant := bootImage.getVariant(target)
-		images = append(images, variant.images)
+		images = append(images, variant.imagePathOnHost)
 		imagesDeps = append(imagesDeps, variant.imagesDeps)
 	}
 	// The image locations for all Android variants are identical.
-	imageLocations := bootImage.getAnyAndroidVariant().imageLocations()
+	hostImageLocations := bootImage.getAnyAndroidVariant().imageLocations()
 
 	var profileClassListing android.OptionalPath
 	var profileBootListing android.OptionalPath
@@ -224,9 +224,9 @@
 		ProvidesUsesLibrary:            providesUsesLib,
 		ClassLoaderContexts:            d.classLoaderContexts,
 
-		Archs:                   archs,
-		DexPreoptImagesDeps:     imagesDeps,
-		DexPreoptImageLocations: imageLocations,
+		Archs:                         archs,
+		DexPreoptImagesDeps:           imagesDeps,
+		DexPreoptImageLocationsOnHost: hostImageLocations,
 
 		PreoptBootClassPathDexFiles:     dexFiles.Paths(),
 		PreoptBootClassPathDexLocations: dexLocations,
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index ce5155f..ce68c48 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -242,7 +242,7 @@
 	symbolsDir android.OutputPath
 
 	// Subdirectory where the image files are installed.
-	installSubdir string
+	installDirOnHost string
 
 	// A list of (location, jar) pairs for the Java modules in this image.
 	modules android.ConfiguredJarList
@@ -273,8 +273,8 @@
 	dexLocationsDeps []string // for the dependency images and in this image
 
 	// Paths to image files.
-	images     android.OutputPath  // first image file
-	imagesDeps android.OutputPaths // all files
+	imagePathOnHost android.OutputPath  // first image file
+	imagesDeps      android.OutputPaths // all files
 
 	// Only for extensions, paths to the primary boot images.
 	primaryImages android.OutputPath
@@ -365,7 +365,7 @@
 	if image.extends != nil {
 		imageLocations = image.extends.getVariant(image.target).imageLocations()
 	}
-	return append(imageLocations, dexpreopt.PathToLocation(image.images, image.target.Arch.ArchType))
+	return append(imageLocations, dexpreopt.PathToLocation(image.imagePathOnHost, image.target.Arch.ArchType))
 }
 
 func dexpreoptBootJarsFactory() android.SingletonModule {
@@ -604,9 +604,9 @@
 
 	arch := image.target.Arch.ArchType
 	os := image.target.Os.String() // We need to distinguish host-x86 and device-x86.
-	symbolsDir := image.symbolsDir.Join(ctx, os, image.installSubdir, arch.String())
+	symbolsDir := image.symbolsDir.Join(ctx, os, image.installDirOnHost, arch.String())
 	symbolsFile := symbolsDir.Join(ctx, image.stem+".oat")
-	outputDir := image.dir.Join(ctx, os, image.installSubdir, arch.String())
+	outputDir := image.dir.Join(ctx, os, image.installDirOnHost, arch.String())
 	outputPath := outputDir.Join(ctx, image.stem+".oat")
 	oatLocation := dexpreopt.PathToLocation(outputPath, arch)
 	imagePath := outputPath.ReplaceExtension(ctx, "art")
@@ -700,7 +700,7 @@
 
 	cmd.Textf(`|| ( echo %s ; false )`, proptools.ShellEscape(failureMessage))
 
-	installDir := filepath.Join("/", image.installSubdir, arch.String())
+	installDir := filepath.Join("/", image.installDirOnHost, arch.String())
 
 	var vdexInstalls android.RuleBuilderInstalls
 	var unstrippedInstalls android.RuleBuilderInstalls
@@ -790,7 +790,7 @@
 
 	rule.Build("bootJarsProfile", "profile boot jars")
 
-	image.profileInstalls = rule.Installs()
+	image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
 
 	return profile
 }
@@ -943,7 +943,7 @@
 				}
 				sfx := variant.name + suffix + "_" + variant.target.Arch.ArchType.String()
 				ctx.Strict("DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_"+sfx, variant.vdexInstalls.String())
-				ctx.Strict("DEXPREOPT_IMAGE_"+sfx, variant.images.String())
+				ctx.Strict("DEXPREOPT_IMAGE_"+sfx, variant.imagePathOnHost.String())
 				ctx.Strict("DEXPREOPT_IMAGE_DEPS_"+sfx, strings.Join(variant.imagesDeps.Strings(), " "))
 				ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+sfx, variant.installs.String())
 				ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+sfx, variant.unstrippedInstalls.String())
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 72b61e3..7fb0444 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -83,26 +83,26 @@
 		artModules := global.ArtApexJars
 		frameworkModules := global.BootJars.RemoveList(artModules)
 
-		artSubdir := "apex/art_boot_images/javalib"
+		artDirOnHost := "apex/art_boot_images/javalib"
 		frameworkSubdir := "system/framework"
 
 		// ART config for the primary boot image in the ART apex.
 		// It includes the Core Libraries.
 		artCfg := bootImageConfig{
-			name:          artBootImageName,
-			stem:          "boot",
-			installSubdir: artSubdir,
-			modules:       artModules,
+			name:             artBootImageName,
+			stem:             "boot",
+			installDirOnHost: artDirOnHost,
+			modules:          artModules,
 		}
 
 		// Framework config for the boot image extension.
 		// It includes framework libraries and depends on the ART config.
 		frameworkCfg := bootImageConfig{
-			extends:       &artCfg,
-			name:          frameworkBootImageName,
-			stem:          "boot",
-			installSubdir: frameworkSubdir,
-			modules:       frameworkModules,
+			extends:          &artCfg,
+			name:             frameworkBootImageName,
+			stem:             "boot",
+			installDirOnHost: frameworkSubdir,
+			modules:          frameworkModules,
 		}
 
 		configs := map[string]*bootImageConfig{
@@ -129,11 +129,11 @@
 			// Create target-specific variants.
 			for _, target := range targets {
 				arch := target.Arch.ArchType
-				imageDir := c.dir.Join(ctx, target.Os.String(), c.installSubdir, arch.String())
+				imageDir := c.dir.Join(ctx, target.Os.String(), c.installDirOnHost, arch.String())
 				variant := &bootImageVariant{
 					bootImageConfig: c,
 					target:          target,
-					images:          imageDir.Join(ctx, imageName),
+					imagePathOnHost: imageDir.Join(ctx, imageName),
 					imagesDeps:      c.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex"),
 					dexLocations:    c.modules.DevicePaths(ctx.Config(), target.Os),
 				}
@@ -147,7 +147,7 @@
 		// specific to the framework config
 		frameworkCfg.dexPathsDeps = append(artCfg.dexPathsDeps, frameworkCfg.dexPathsDeps...)
 		for i := range targets {
-			frameworkCfg.variants[i].primaryImages = artCfg.variants[i].images
+			frameworkCfg.variants[i].primaryImages = artCfg.variants[i].imagePathOnHost
 			frameworkCfg.variants[i].dexLocationsDeps = append(artCfg.variants[i].dexLocations, frameworkCfg.variants[i].dexLocationsDeps...)
 		}
 
@@ -163,18 +163,6 @@
 	return genBootImageConfigs(ctx)[frameworkBootImageName]
 }
 
-func defaultBootclasspath(ctx android.PathContext) []string {
-	return ctx.Config().OnceStringSlice(defaultBootclasspathKey, func() []string {
-		global := dexpreopt.GetGlobalConfig(ctx)
-		image := defaultBootImageConfig(ctx)
-
-		updatableBootclasspath := global.UpdatableBootJars.DevicePaths(ctx.Config(), android.Android)
-
-		bootclasspath := append(copyOf(image.getAnyAndroidVariant().dexLocationsDeps), updatableBootclasspath...)
-		return bootclasspath
-	})
-}
-
 // Updatable boot config allows to access build/install paths of updatable boot jars without going
 // through the usual trouble of registering dependencies on those modules and extracting build paths
 // from those dependencies.
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 1acb9f4..1caffe4 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -72,8 +72,8 @@
 func platformBootclasspathFactory() android.SingletonModule {
 	m := &platformBootclasspathModule{}
 	m.AddProperties(&m.properties)
-	// TODO(satayev): split systemserver and apex jars into separate configs.
-	initClasspathFragment(m)
+	// TODO(satayev): split apex jars into separate configs.
+	initClasspathFragment(m, BOOTCLASSPATH)
 	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
 	return m
 }
@@ -167,8 +167,6 @@
 }
 
 func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx)
-
 	// Gather all the dependencies from the art, updatable and non-updatable boot jars.
 	artModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathArtBootJarDepTag)
 	nonUpdatableModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathNonUpdatableBootJarDepTag)
@@ -189,6 +187,8 @@
 	b.checkNonUpdatableModules(ctx, nonUpdatableModules)
 	b.checkUpdatableModules(ctx, updatableModules)
 
+	b.generateClasspathProtoBuildActions(ctx)
+
 	b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
 
 	// Nothing to do if skipping the dexpreopt of boot image jars.
@@ -199,6 +199,23 @@
 	b.generateBootImageBuildActions(ctx, updatableModules)
 }
 
+// Generate classpaths.proto config
+func (b *platformBootclasspathModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) {
+	// ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH
+	classpathJars := configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
+	// TODO(satayev): remove updatable boot jars once each apex has its own fragment
+	global := dexpreopt.GetGlobalConfig(ctx)
+	classpathJars = append(classpathJars, configuredJarListToClasspathJars(ctx, global.UpdatableBootJars, BOOTCLASSPATH)...)
+
+	b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
+}
+
+func (b *platformBootclasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
+	global := dexpreopt.GetGlobalConfig(ctx)
+	// TODO(satayev): split ART apex jars into their own classpathFragment
+	return global.BootJars
+}
+
 // checkNonUpdatableModules ensures that the non-updatable modules supplied are not part of an
 // updatable module.
 func (b *platformBootclasspathModule) checkNonUpdatableModules(ctx android.ModuleContext, modules []android.Module) {
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
new file mode 100644
index 0000000..82cdb89
--- /dev/null
+++ b/java/systemserver_classpath_fragment.go
@@ -0,0 +1,67 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"android/soong/android"
+	"android/soong/dexpreopt"
+)
+
+func init() {
+	registerSystemserverClasspathBuildComponents(android.InitRegistrationContext)
+}
+
+func registerSystemserverClasspathBuildComponents(ctx android.RegistrationContext) {
+	// TODO(satayev): add systemserver_classpath_fragment module
+	ctx.RegisterModuleType("platform_systemserverclasspath", platformSystemServerClasspathFactory)
+}
+
+type platformSystemServerClasspathModule struct {
+	android.ModuleBase
+
+	ClasspathFragmentBase
+}
+
+func platformSystemServerClasspathFactory() android.Module {
+	m := &platformSystemServerClasspathModule{}
+	initClasspathFragment(m, SYSTEMSERVERCLASSPATH)
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+	return m
+}
+
+func (b *platformSystemServerClasspathModule) AndroidMkEntries() (entries []android.AndroidMkEntries) {
+	return b.classpathFragmentBase().androidMkEntries()
+}
+
+func (b *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	configuredJars := configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), b.classpathType)
+	b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars)
+}
+
+var platformSystemServerClasspathKey = android.NewOnceKey("platform_systemserverclasspath")
+
+func (b *platformSystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
+	return ctx.Config().Once(platformSystemServerClasspathKey, func() interface{} {
+		global := dexpreopt.GetGlobalConfig(ctx)
+
+		jars := global.SystemServerJars
+
+		// TODO(satayev): split apex jars into separate configs.
+		for i := 0; i < global.UpdatableSystemServerJars.Len(); i++ {
+			jars = jars.Append(global.UpdatableSystemServerJars.Apex(i), global.UpdatableSystemServerJars.Jar(i))
+		}
+		return jars
+	}).(android.ConfiguredJarList)
+}
diff --git a/java/systemserver_classpath_fragment_test.go b/java/systemserver_classpath_fragment_test.go
new file mode 100644
index 0000000..6126d5e
--- /dev/null
+++ b/java/systemserver_classpath_fragment_test.go
@@ -0,0 +1,97 @@
+// 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 java
+
+import (
+	"testing"
+
+	"android/soong/android"
+)
+
+var prepareForTestWithSystemserverClasspath = android.GroupFixturePreparers(
+	PrepareForTestWithJavaDefaultModules,
+)
+
+func TestSystemserverClasspathVariant(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithSystemserverClasspath,
+		android.FixtureWithRootAndroidBp(`
+			platform_systemserverclasspath {
+				name: "platform-systemserverclasspath",
+			}
+		`),
+	).RunTest(t)
+
+	variants := result.ModuleVariantsForTests("platform-systemserverclasspath")
+	android.AssertIntEquals(t, "expect 1 variant", 1, len(variants))
+}
+
+func TestSystemserverClasspath_ClasspathFragmentPaths(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithSystemserverClasspath,
+		android.FixtureWithRootAndroidBp(`
+			platform_systemserverclasspath {
+				name: "platform-systemserverclasspath",
+			}
+		`),
+	).RunTest(t)
+
+	p := result.Module("platform-systemserverclasspath", "android_common").(*platformSystemServerClasspathModule)
+	android.AssertStringEquals(t, "output filepath", p.Name()+".pb", p.ClasspathFragmentBase.outputFilepath.Base())
+	android.AssertPathRelativeToTopEquals(t, "install filepath", "out/soong/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath)
+}
+
+func TestSystemserverClasspathModule_AndroidMkEntries(t *testing.T) {
+	preparer := android.GroupFixturePreparers(
+		prepareForTestWithSystemserverClasspath,
+		android.FixtureWithRootAndroidBp(`
+			platform_systemserverclasspath {
+				name: "platform-systemserverclasspath",
+			}
+		`),
+	)
+
+	t.Run("AndroidMkEntries", func(t *testing.T) {
+		result := preparer.RunTest(t)
+
+		p := result.Module("platform-systemserverclasspath", "android_common").(*platformSystemServerClasspathModule)
+
+		entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)
+		android.AssertIntEquals(t, "AndroidMkEntries count", 1, len(entries))
+	})
+
+	t.Run("classpath-fragment-entry", func(t *testing.T) {
+		result := preparer.RunTest(t)
+
+		want := map[string][]string{
+			"LOCAL_MODULE":                {"platform-systemserverclasspath"},
+			"LOCAL_MODULE_CLASS":          {"ETC"},
+			"LOCAL_INSTALLED_MODULE_STEM": {"platform-systemserverclasspath.pb"},
+			// Output and Install paths are tested separately in TestSystemserverClasspath_ClasspathFragmentPaths
+		}
+
+		p := result.Module("platform-systemserverclasspath", "android_common").(*platformSystemServerClasspathModule)
+
+		entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)
+		got := entries[0]
+		for k, expectedValue := range want {
+			if value, ok := got.EntryMap[k]; ok {
+				android.AssertDeepEquals(t, k, expectedValue, value)
+			} else {
+				t.Errorf("No %s defined, saw %q", k, got.EntryMap)
+			}
+		}
+	})
+}
diff --git a/java/testing.go b/java/testing.go
index 649d27b..37e63f5 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -244,6 +244,7 @@
 	RegisterSdkLibraryBuildComponents(ctx)
 	RegisterStubsBuildComponents(ctx)
 	RegisterSystemModulesBuildComponents(ctx)
+	registerSystemserverClasspathBuildComponents(ctx)
 }
 
 // gatherRequiredDepsForTest gathers the module definitions used by