1// Copyright 2018 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package java
16
17import (
18	"path/filepath"
19	"sort"
20	"strings"
21
22	"github.com/google/blueprint/proptools"
23
24	"android/soong/android"
25	"android/soong/dexpreopt"
26)
27
28type DexpreopterInterface interface {
29	// True if the java module is to be dexed and installed on devices.
30	// Structs that embed dexpreopter must implement this.
31	IsInstallable() bool
32
33	// True if dexpreopt is disabled for the java module.
34	dexpreoptDisabled(ctx android.BaseModuleContext, libraryName string) bool
35
36	// If the java module is to be installed into an APEX, this list contains information about the
37	// dexpreopt outputs to be installed on devices. Note that these dexpreopt outputs are installed
38	// outside of the APEX.
39	DexpreoptBuiltInstalledForApex() []dexpreopterInstall
40
41	// The Make entries to install the dexpreopt outputs. Derived from
42	// `DexpreoptBuiltInstalledForApex`.
43	AndroidMkEntriesForApex() []android.AndroidMkEntries
44
45	// See `dexpreopter.outputProfilePathOnHost`.
46	OutputProfilePathOnHost() android.Path
47}
48
49type dexpreopterInstall struct {
50	// A unique name to distinguish an output from others for the same java library module. Usually in
51	// the form of `<arch>-<encoded-path>.odex/vdex/art`.
52	name string
53
54	// The name of the input java module.
55	moduleName string
56
57	// The path to the dexpreopt output on host.
58	outputPathOnHost android.Path
59
60	// The directory on the device for the output to install to.
61	installDirOnDevice android.InstallPath
62
63	// The basename (the last segment of the path) for the output to install as.
64	installFileOnDevice string
65}
66
67// The full module name of the output in the makefile.
68func (install *dexpreopterInstall) FullModuleName() string {
69	return install.moduleName + install.SubModuleName()
70}
71
72// The sub-module name of the output in the makefile (the name excluding the java module name).
73func (install *dexpreopterInstall) SubModuleName() string {
74	return "-dexpreopt-" + install.name
75}
76
77// Returns Make entries for installing the file.
78//
79// This function uses a value receiver rather than a pointer receiver to ensure that the object is
80// safe to use in `android.AndroidMkExtraEntriesFunc`.
81func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries {
82	return android.AndroidMkEntries{
83		Class:      "ETC",
84		OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
85		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
86			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
87				entries.SetString("LOCAL_MODULE", install.FullModuleName())
88				entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String())
89				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
90				entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
91				// Unset LOCAL_SOONG_INSTALLED_MODULE so that this does not default to the primary .apex file
92				// Without this, installation of the dexpreopt artifacts get skipped
93				entries.SetString("LOCAL_SOONG_INSTALLED_MODULE", "")
94			},
95		},
96	}
97}
98
99func (install dexpreopterInstall) PackageFile(ctx android.ModuleContext) android.PackagingSpec {
100	return ctx.PackageFile(install.installDirOnDevice, install.installFileOnDevice, install.outputPathOnHost)
101}
102
103type Dexpreopter struct {
104	dexpreopter
105}
106
107type dexpreopter struct {
108	dexpreoptProperties       DexpreoptProperties
109	importDexpreoptProperties ImportDexpreoptProperties
110
111	// If true, the dexpreopt rules will not be generated
112	// Unlike Dex_preopt.Enabled which is user-facing,
113	// shouldDisableDexpreopt is a mutated propery.
114	shouldDisableDexpreopt bool
115
116	installPath         android.InstallPath
117	uncompressedDex     bool
118	isSDKLibrary        bool
119	isApp               bool
120	isTest              bool
121	isPresignedPrebuilt bool
122	preventInstall      bool
123
124	manifestFile        android.Path
125	statusFile          android.WritablePath
126	enforceUsesLibs     bool
127	classLoaderContexts dexpreopt.ClassLoaderContextMap
128
129	// See the `dexpreopt` function for details.
130	builtInstalled        string
131	builtInstalledForApex []dexpreopterInstall
132
133	// The config is used for two purposes:
134	// - Passing dexpreopt information about libraries from Soong to Make. This is needed when
135	//   a <uses-library> is defined in Android.bp, but used in Android.mk (see dex_preopt_config_merger.py).
136	//   Note that dexpreopt.config might be needed even if dexpreopt is disabled for the library itself.
137	// - Dexpreopt post-processing (using dexpreopt artifacts from a prebuilt system image to incrementally
138	//   dexpreopt another partition).
139	configPath android.WritablePath
140
141	// The path to the profile on host that dexpreopter generates. This is used as the input for
142	// dex2oat.
143	outputProfilePathOnHost android.Path
144
145	// The path to the profile that dexpreopter accepts. It must be in the binary format. If this is
146	// set, it overrides the profile settings in `dexpreoptProperties`.
147	inputProfilePathOnHost android.Path
148
149	// The path to the profile that matches the dex optimized by r8/d8. It is in text format. If this is
150	// set, it will be converted to a binary profile which will be subsequently used for dexpreopt.
151	rewrittenProfile android.Path
152}
153
154type DexpreoptProperties struct {
155	Dex_preopt struct {
156		// If false, prevent dexpreopting.  Defaults to true.
157		Enabled *bool
158
159		// If true, generate an app image (.art file) for this module.
160		App_image *bool
161
162		// If true, use a checked-in profile to guide optimization.  Defaults to false unless
163		// a matching profile is set or a profile is found in PRODUCT_DEX_PREOPT_PROFILE_DIR
164		// that matches the name of this module, in which case it is defaulted to true.
165		Profile_guided *bool
166
167		// If set, provides the path to profile relative to the Android.bp file.  If not set,
168		// defaults to searching for a file that matches the name of this module in the default
169		// profile location set by PRODUCT_DEX_PREOPT_PROFILE_DIR, or empty if not found.
170		Profile *string `android:"path"`
171
172		// If set to true, r8/d8 will use `profile` as input to generate a new profile that matches
173		// the optimized dex.
174		// The new profile will be subsequently used as the profile to dexpreopt the dex file.
175		Enable_profile_rewriting *bool
176	}
177
178	Dex_preopt_result struct {
179		// True if profile-guided optimization is actually enabled.
180		Profile_guided bool
181	} `blueprint:"mutated"`
182}
183
184type ImportDexpreoptProperties struct {
185	Dex_preopt struct {
186		// If true, use the profile in the prebuilt APEX to guide optimization. Defaults to false.
187		Profile_guided *bool
188	}
189}
190
191func init() {
192	dexpreopt.DexpreoptRunningInSoong = true
193}
194
195func isApexVariant(ctx android.BaseModuleContext) bool {
196	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
197	return !apexInfo.IsForPlatform()
198}
199
200func forPrebuiltApex(ctx android.BaseModuleContext) bool {
201	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
202	return apexInfo.ForPrebuiltApex
203}
204
205// For apex variant of modules, this returns true on the source variant if the prebuilt apex
206// has been selected using apex_contributions.
207// The prebuilt apex will be responsible for generating the dexpreopt rules of the deapexed java lib.
208func disableSourceApexVariant(ctx android.BaseModuleContext) bool {
209	if !isApexVariant(ctx) {
210		return false // platform variant
211	}
212	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
213	psi := android.PrebuiltSelectionInfoMap{}
214	ctx.VisitDirectDeps(func(am android.Module) {
215		if prebuiltSelectionInfo, ok := android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider); ok {
216			psi = prebuiltSelectionInfo
217		}
218	})
219	// Find the apex variant for this module
220	_, apexVariantsWithoutTestApexes, _ := android.ListSetDifference(apexInfo.InApexVariants, apexInfo.TestApexes)
221	disableSource := false
222	// find the selected apexes
223	for _, apexVariant := range apexVariantsWithoutTestApexes {
224		for _, selected := range psi.GetSelectedModulesForApiDomain(apexVariant) {
225			// If the apex_contribution for this api domain contains a prebuilt apex, disable the source variant
226			if strings.HasPrefix(selected, "prebuilt_com.google.android") {
227				disableSource = true
228			}
229		}
230	}
231	return disableSource
232}
233
234// Returns whether dexpreopt is applicable to the module.
235// When it returns true, neither profile nor dexpreopt artifacts will be generated.
236func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext, libName string) bool {
237	if !ctx.Device() {
238		return true
239	}
240
241	if d.isTest {
242		return true
243	}
244
245	if !BoolDefault(d.dexpreoptProperties.Dex_preopt.Enabled, true) {
246		return true
247	}
248
249	if d.shouldDisableDexpreopt {
250		return true
251	}
252
253	// If the module is from a prebuilt APEX, it shouldn't be installable, but it can still be
254	// dexpreopted.
255	if !ctx.Module().(DexpreopterInterface).IsInstallable() && !forPrebuiltApex(ctx) {
256		return true
257	}
258
259	if !android.IsModulePreferred(ctx.Module()) {
260		return true
261	}
262
263	if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex {
264		// dexpreopt rules for system server jars can be generated in the ModuleCtx of prebuilt apexes
265		return false
266	}
267
268	global := dexpreopt.GetGlobalConfig(ctx)
269
270	// Use the libName argument to determine if the library being dexpreopt'd is a system server jar
271	// ctx.ModuleName() is not safe. In case of prebuilt apexes, the dexpreopt rules of system server jars
272	// are created in the ctx object of the top-level prebuilt apex.
273	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(libName)
274
275	if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex || isApexVariant(ctx) {
276		// dexpreopt rules for system server jars can be generated in the ModuleCtx of prebuilt apexes
277		if !isApexSystemServerJar {
278			return true
279		}
280		ai, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
281		allApexInfos := []android.ApexInfo{}
282		if allApexInfosProvider, ok := android.ModuleProvider(ctx, android.AllApexInfoProvider); ok {
283			allApexInfos = allApexInfosProvider.ApexInfos
284		}
285		if len(allApexInfos) > 0 && !ai.MinSdkVersion.EqualTo(allApexInfos[0].MinSdkVersion) {
286			// Apex system server jars are dexpreopted and installed on to the system image.
287			// Since we can have BigAndroid and Go variants of system server jar providing apexes,
288			// and these two variants can have different min_sdk_versions, hide one of the apex variants
289			// from make to prevent collisions.
290			//
291			// Unlike cc, min_sdk_version does not have an effect on the build actions of java libraries.
292			ctx.Module().MakeUninstallable()
293		}
294	} else {
295		// Don't preopt the platform variant of an APEX system server jar to avoid conflicts.
296		if isApexSystemServerJar {
297			return true
298		}
299	}
300
301	// TODO: contains no java code
302
303	return false
304}
305
306func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) {
307	if _, isApex := android.ModuleProvider(ctx, android.ApexBundleInfoProvider); isApex && dexpreopt.IsDex2oatNeeded(ctx) {
308		// prebuilt apexes can genererate rules to dexpreopt deapexed jars
309		// Add a dex2oat dep aggressively on _every_ apex module
310		dexpreopt.RegisterToolDeps(ctx)
311		return
312	}
313	if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx, android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())) || !dexpreopt.IsDex2oatNeeded(ctx) {
314		return
315	}
316	dexpreopt.RegisterToolDeps(ctx)
317}
318
319// Returns the install path of the dex jar of a module.
320//
321// Do not rely on `ApexInfo.ApexVariationName` because it can be something like "apex1000", rather
322// than the `name` in the path `/apex/<name>` as suggested in its comment.
323//
324// This function is on a best-effort basis. It cannot handle the case where an APEX jar is not a
325// system server jar, which is fine because we currently only preopt system server jars for APEXes.
326func (d *dexpreopter) getInstallPath(
327	ctx android.ModuleContext, libName string, defaultInstallPath android.InstallPath) android.InstallPath {
328	global := dexpreopt.GetGlobalConfig(ctx)
329	if global.AllApexSystemServerJars(ctx).ContainsJar(libName) {
330		dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, libName)
331		return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/"))
332	}
333	if !d.dexpreoptDisabled(ctx, libName) && isApexVariant(ctx) &&
334		filepath.Base(defaultInstallPath.PartitionDir()) != "apex" {
335		ctx.ModuleErrorf("unable to get the install path of the dex jar for dexpreopt")
336	}
337	return defaultInstallPath
338}
339
340// DexpreoptPrebuiltApexSystemServerJars generates the dexpreopt artifacts from a jar file that has been deapexed from a prebuilt apex
341func (d *Dexpreopter) DexpreoptPrebuiltApexSystemServerJars(ctx android.ModuleContext, libraryName string, di *android.DeapexerInfo) {
342	// A single prebuilt apex can have multiple apex system jars
343	// initialize the output path for this dex jar
344	dc := dexpreopt.GetGlobalConfig(ctx)
345	d.installPath = android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexpreopt.GetSystemServerDexLocation(ctx, dc, libraryName), "/"))
346	// generate the rules for creating the .odex and .vdex files for this system server jar
347	dexJarFile := di.PrebuiltExportPath(ApexRootRelativePathToJavaLib(libraryName))
348
349	d.inputProfilePathOnHost = nil // reset: TODO(spandandas): Make dexpreopter stateless
350	if android.InList(libraryName, di.GetDexpreoptProfileGuidedExportedModuleNames()) {
351		// Set the profile path to guide optimization
352		prof := di.PrebuiltExportPath(ApexRootRelativePathToJavaLib(libraryName) + ".prof")
353		if prof == nil {
354			ctx.ModuleErrorf("Could not find a .prof file in this prebuilt apex")
355		}
356		d.inputProfilePathOnHost = prof
357	}
358
359	d.dexpreopt(ctx, libraryName, dexJarFile)
360}
361
362func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, libName string, dexJarFile android.WritablePath) {
363	global := dexpreopt.GetGlobalConfig(ctx)
364
365	// TODO(b/148690468): The check on d.installPath is to bail out in cases where
366	// the dexpreopter struct hasn't been fully initialized before we're called,
367	// e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively
368	// disabled, even if installable is true.
369	if d.installPath.Base() == "." {
370		return
371	}
372
373	dexLocation := android.InstallPathToOnDevicePath(ctx, d.installPath)
374
375	providesUsesLib := libName
376	if ulib, ok := ctx.Module().(ProvidesUsesLib); ok {
377		name := ulib.ProvidesUsesLib()
378		if name != nil {
379			providesUsesLib = *name
380		}
381	}
382
383	// If it is test, make config files regardless of its dexpreopt setting.
384	// The config files are required for apps defined in make which depend on the lib.
385	if d.isTest && d.dexpreoptDisabled(ctx, libName) {
386		return
387	}
388
389	isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(libName)
390
391	bootImage := defaultBootImageConfig(ctx)
392	// When `global.PreoptWithUpdatableBcp` is true, `bcpForDexpreopt` below includes the mainline
393	// boot jars into bootclasspath, so we should include the mainline boot image as well because it's
394	// generated from those jars.
395	if global.PreoptWithUpdatableBcp {
396		bootImage = mainlineBootImageConfig(ctx)
397	}
398	dexFiles, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
399
400	targets := ctx.MultiTargets()
401	if len(targets) == 0 {
402		// assume this is a java library, dexpreopt for all arches for now
403		for _, target := range ctx.Config().Targets[android.Android] {
404			if target.NativeBridge == android.NativeBridgeDisabled {
405				targets = append(targets, target)
406			}
407		}
408		if isSystemServerJar && libName != "com.android.location.provider" {
409			// If the module is a system server jar, only preopt for the primary arch because the jar can
410			// only be loaded by system server. "com.android.location.provider" is a special case because
411			// it's also used by apps as a shared library.
412			targets = targets[:1]
413		}
414	}
415
416	var archs []android.ArchType
417	var images android.Paths
418	var imagesDeps []android.OutputPaths
419	for _, target := range targets {
420		archs = append(archs, target.Arch.ArchType)
421		variant := bootImage.getVariant(target)
422		images = append(images, variant.imagePathOnHost)
423		imagesDeps = append(imagesDeps, variant.imagesDeps)
424	}
425	// The image locations for all Android variants are identical.
426	hostImageLocations, deviceImageLocations := bootImage.getAnyAndroidVariant().imageLocations()
427
428	var profileClassListing android.OptionalPath
429	var profileBootListing android.OptionalPath
430	profileIsTextListing := false
431
432	if d.inputProfilePathOnHost != nil {
433		profileClassListing = android.OptionalPathForPath(d.inputProfilePathOnHost)
434	} else if BoolDefault(d.dexpreoptProperties.Dex_preopt.Profile_guided, true) && !forPrebuiltApex(ctx) {
435		// If enable_profile_rewriting is set, use the rewritten profile instead of the checked-in profile
436		if d.EnableProfileRewriting() {
437			profileClassListing = android.OptionalPathForPath(d.GetRewrittenProfile())
438			profileIsTextListing = true
439		} else if profile := d.GetProfile(); profile != "" {
440			// If dex_preopt.profile_guided is not set, default it based on the existence of the
441			// dexprepot.profile option or the profile class listing.
442			profileClassListing = android.OptionalPathForPath(
443				android.PathForModuleSrc(ctx, profile))
444			profileBootListing = android.ExistentPathForSource(ctx,
445				ctx.ModuleDir(), profile+"-boot")
446			profileIsTextListing = true
447		} else if global.ProfileDir != "" {
448			profileClassListing = android.ExistentPathForSource(ctx,
449				global.ProfileDir, libName+".prof")
450		}
451	}
452
453	d.dexpreoptProperties.Dex_preopt_result.Profile_guided = profileClassListing.Valid()
454
455	// A single apex can have multiple system server jars
456	// Use the dexJar to create a unique scope for each
457	dexJarStem := strings.TrimSuffix(dexJarFile.Base(), dexJarFile.Ext())
458
459	// Full dexpreopt config, used to create dexpreopt build rules.
460	dexpreoptConfig := &dexpreopt.ModuleConfig{
461		Name:            libName,
462		DexLocation:     dexLocation,
463		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, libName+".jar").OutputPath,
464		DexPath:         dexJarFile,
465		ManifestPath:    android.OptionalPathForPath(d.manifestFile),
466		UncompressedDex: d.uncompressedDex,
467		HasApkLibraries: false,
468		PreoptFlags:     nil,
469
470		ProfileClassListing:  profileClassListing,
471		ProfileIsTextListing: profileIsTextListing,
472		ProfileBootListing:   profileBootListing,
473
474		EnforceUsesLibrariesStatusFile: dexpreopt.UsesLibrariesStatusFile(ctx),
475		EnforceUsesLibraries:           d.enforceUsesLibs,
476		ProvidesUsesLibrary:            providesUsesLib,
477		ClassLoaderContexts:            d.classLoaderContexts,
478
479		Archs:                           archs,
480		DexPreoptImagesDeps:             imagesDeps,
481		DexPreoptImageLocationsOnHost:   hostImageLocations,
482		DexPreoptImageLocationsOnDevice: deviceImageLocations,
483
484		PreoptBootClassPathDexFiles:     dexFiles.Paths(),
485		PreoptBootClassPathDexLocations: dexLocations,
486
487		NoCreateAppImage:    !BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, true),
488		ForceCreateAppImage: BoolDefault(d.dexpreoptProperties.Dex_preopt.App_image, false),
489
490		PresignedPrebuilt: d.isPresignedPrebuilt,
491	}
492
493	d.configPath = android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "dexpreopt.config")
494	dexpreopt.WriteModuleConfig(ctx, dexpreoptConfig, d.configPath)
495
496	if d.dexpreoptDisabled(ctx, libName) {
497		return
498	}
499
500	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
501
502	// The root "product_packages.txt" is generated by `build/make/core/Makefile`. It contains a list
503	// of all packages that are installed on the device. We use `grep` to filter the list by the app's
504	// dependencies to create a per-app list, and use `rsync --checksum` to prevent the file's mtime
505	// from being changed if the contents don't change. This avoids unnecessary dexpreopt reruns.
506	productPackages := android.PathForModuleInPartitionInstall(ctx, "", "product_packages.txt")
507	appProductPackages := android.PathForModuleOut(ctx, "dexpreopt", dexJarStem, "product_packages.txt")
508	appProductPackagesStaging := appProductPackages.ReplaceExtension(ctx, "txt.tmp")
509	clcNames, _ := dexpreopt.ComputeClassLoaderContextDependencies(dexpreoptConfig.ClassLoaderContexts)
510	sort.Strings(clcNames) // The order needs to be deterministic.
511	productPackagesRule := android.NewRuleBuilder(pctx, ctx)
512	if len(clcNames) > 0 {
513		productPackagesRule.Command().
514			Text("grep -F -x").
515			FlagForEachArg("-e ", clcNames).
516			Input(productPackages).
517			FlagWithOutput("> ", appProductPackagesStaging).
518			Text("|| true")
519	} else {
520		productPackagesRule.Command().
521			Text("rm -f").Output(appProductPackagesStaging).
522			Text("&&").
523			Text("touch").Output(appProductPackagesStaging)
524	}
525	productPackagesRule.Command().
526		Text("rsync --checksum").
527		Input(appProductPackagesStaging).
528		Output(appProductPackages)
529	productPackagesRule.Restat().Build("product_packages."+dexJarStem, "dexpreopt product_packages")
530
531	// Prebuilts are active, do not copy the dexpreopt'd source javalib to out/soong/system_server_dexjars
532	// The javalib from the deapexed prebuilt will be copied to this location.
533	// TODO (b/331665856): Implement a principled solution for this.
534	copyApexSystemServerJarDex := !disableSourceApexVariant(ctx) && !ctx.Module().IsHideFromMake()
535	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
536		ctx, globalSoong, global, dexpreoptConfig, appProductPackages, copyApexSystemServerJarDex)
537	if err != nil {
538		ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
539		return
540	}
541
542	dexpreoptRule.Build("dexpreopt"+"."+dexJarStem, "dexpreopt")
543
544	// The current ctx might be of a deapexer module created by a prebuilt apex
545	// Use the path of the dex file to determine the library name
546	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(dexJarStem)
547
548	dexpreoptPartition := d.installPath.Partition()
549	// dexpreoptPartition is set to empty for dexpreopts of system APEX and system_other.
550	// In case of system APEX, however, we can set it to "system" manually.
551	// TODO(b/346662300): Let dexpreopter generate the installPath for dexpreopt files instead of
552	// using the dex location to generate the installPath.
553	if isApexSystemServerJar {
554		dexpreoptPartition = "system"
555	}
556	for _, install := range dexpreoptRule.Installs() {
557		// Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
558		installDir := strings.TrimPrefix(filepath.Dir(install.To), "/")
559		partition := dexpreoptPartition
560		if strings.HasPrefix(installDir, partition+"/") {
561			installDir = strings.TrimPrefix(installDir, partition+"/")
562		} else {
563			// If the partition for the installDir is different from the install partition, set the
564			// partition empty to install the dexpreopt files to the desired partition.
565			// TODO(b/346439786): Define and use the dexpreopt module type to avoid this mismatch.
566			partition = ""
567		}
568		installBase := filepath.Base(install.To)
569		arch := filepath.Base(installDir)
570		installPath := android.PathForModuleInPartitionInstall(ctx, partition, installDir)
571		isProfile := strings.HasSuffix(installBase, ".prof")
572
573		if isProfile {
574			d.outputProfilePathOnHost = install.From
575		}
576
577		if isApexSystemServerJar {
578			// Profiles are handled separately because they are installed into the APEX.
579			if !isProfile {
580				// APEX variants of java libraries are hidden from Make, so their dexpreopt
581				// outputs need special handling. Currently, for APEX variants of java
582				// libraries, only those in the system server classpath are handled here.
583				// Preopting of boot classpath jars in the ART APEX are handled in
584				// java/dexpreopt_bootjars.go, and other APEX jars are not preopted.
585				// The installs will be handled by Make as sub-modules of the java library.
586				d.builtInstalledForApex = append(d.builtInstalledForApex, dexpreopterInstall{
587					name:                arch + "-" + installBase,
588					moduleName:          libName,
589					outputPathOnHost:    install.From,
590					installDirOnDevice:  installPath,
591					installFileOnDevice: installBase,
592				})
593			}
594		} else if !d.preventInstall {
595			ctx.InstallFile(installPath, installBase, install.From)
596		}
597	}
598
599	if !isApexSystemServerJar {
600		d.builtInstalled = dexpreoptRule.Installs().String()
601	}
602}
603
604func getModuleInstallPathInfo(ctx android.ModuleContext, fullInstallPath string) (android.InstallPath, string, string) {
605	installPath := android.PathForModuleInstall(ctx)
606	installDir, installBase := filepath.Split(strings.TrimPrefix(fullInstallPath, "/"))
607
608	if !strings.HasPrefix(installDir, installPath.Partition()+"/") {
609		// Return empty filename if the install partition is not for the target image.
610		return installPath, "", ""
611	}
612	relDir, err := filepath.Rel(installPath.Partition(), installDir)
613	if err != nil {
614		panic(err)
615	}
616	return installPath, relDir, installBase
617}
618
619// RuleBuilder.Install() adds output-to-install copy pairs to a list for Make. To share this
620// information with PackagingSpec in soong, call PackageFile for them.
621// The install path and the target install partition of the module must be the same.
622func packageFile(ctx android.ModuleContext, install android.RuleBuilderInstall) {
623	installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To)
624	// Empty name means the install partition is not for the target image.
625	// For the system image, files for "apex" and "system_other" are skipped here.
626	// The skipped "apex" files are for testing only, for example,
627	// "/apex/art_boot_images/javalib/x86/boot.vdex".
628	// TODO(b/320196894): Files for "system_other" are skipped because soong creates the system
629	// image only for now.
630	if name != "" {
631		ctx.PackageFile(installPath.Join(ctx, relDir), name, install.From)
632	}
633}
634
635func (d *dexpreopter) DexpreoptBuiltInstalledForApex() []dexpreopterInstall {
636	return d.builtInstalledForApex
637}
638
639func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries {
640	var entries []android.AndroidMkEntries
641	for _, install := range d.builtInstalledForApex {
642		entries = append(entries, install.ToMakeEntries())
643	}
644	return entries
645}
646
647func (d *dexpreopter) OutputProfilePathOnHost() android.Path {
648	return d.outputProfilePathOnHost
649}
650
651func (d *dexpreopter) disableDexpreopt() {
652	d.shouldDisableDexpreopt = true
653}
654
655func (d *dexpreopter) EnableProfileRewriting() bool {
656	return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Enable_profile_rewriting)
657}
658
659func (d *dexpreopter) GetProfile() string {
660	return proptools.String(d.dexpreoptProperties.Dex_preopt.Profile)
661}
662
663func (d *dexpreopter) GetProfileGuided() bool {
664	return proptools.Bool(d.dexpreoptProperties.Dex_preopt.Profile_guided)
665}
666
667func (d *dexpreopter) GetRewrittenProfile() android.Path {
668	return d.rewrittenProfile
669}
670
671func (d *dexpreopter) SetRewrittenProfile(p android.Path) {
672	d.rewrittenProfile = p
673}
674