1// Copyright 2020 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	"reflect"
19	"strings"
20	"testing"
21
22	"android/soong/android"
23	"android/soong/shared"
24)
25
26func TestRuntimeResourceOverlay(t *testing.T) {
27	fs := android.MockFS{
28		"baz/res/res/values/strings.xml": nil,
29		"bar/res/res/values/strings.xml": nil,
30	}
31	bp := `
32		runtime_resource_overlay {
33			name: "foo",
34			certificate: "platform",
35			lineage: "lineage.bin",
36			rotationMinSdkVersion: "32",
37			product_specific: true,
38			static_libs: ["bar"],
39			resource_libs: ["baz"],
40			aaptflags: ["--keep-raw-values"],
41		}
42
43		runtime_resource_overlay {
44			name: "foo_themed",
45			certificate: "platform",
46			product_specific: true,
47			theme: "faza",
48			overrides: ["foo"],
49		}
50
51		android_library {
52			name: "bar",
53			resource_dirs: ["bar/res"],
54		}
55
56		android_app {
57			name: "baz",
58			sdk_version: "current",
59			resource_dirs: ["baz/res"],
60		}
61	`
62
63	result := android.GroupFixturePreparers(
64		PrepareForTestWithJavaDefaultModules,
65		android.FixtureModifyConfig(android.SetKatiEnabledForTests),
66		fs.AddToFixture(),
67	).RunTestWithBp(t, bp)
68
69	m := result.ModuleForTests("foo", "android_common")
70
71	// Check AAPT2 link flags.
72	aapt2Flags := m.Output("package-res.apk").Args["flags"]
73	expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
74	absentFlags := android.RemoveListFromList(expectedFlags, strings.Split(aapt2Flags, " "))
75	if len(absentFlags) > 0 {
76		t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags)
77	}
78
79	// Check overlay.list output for static_libs dependency.
80	overlayList := android.PathsRelativeToTop(m.Output("aapt2/overlay.list").Inputs)
81	staticLibPackage := "out/soong/.intermediates/bar/android_common/package-res.apk"
82	if !inList(staticLibPackage, overlayList) {
83		t.Errorf("Stactic lib res package %q missing in overlay list: %q", staticLibPackage, overlayList)
84	}
85
86	// Check AAPT2 link flags for resource_libs dependency.
87	resourceLibFlag := "-I " + "out/soong/.intermediates/baz/android_common/package-res.apk"
88	if !strings.Contains(aapt2Flags, resourceLibFlag) {
89		t.Errorf("Resource lib flag %q missing in aapt2 link flags: %q", resourceLibFlag, aapt2Flags)
90	}
91
92	// Check cert signing flags.
93	signedApk := m.Output("signed/foo.apk")
94	actualCertSigningFlags := signedApk.Args["flags"]
95	expectedCertSigningFlags := "--lineage lineage.bin --rotation-min-sdk-version 32"
96	if expectedCertSigningFlags != actualCertSigningFlags {
97		t.Errorf("Incorrect cert signing flags, expected: %q, got: %q", expectedCertSigningFlags, actualCertSigningFlags)
98	}
99
100	signingFlag := signedApk.Args["certificates"]
101	expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
102	if expected != signingFlag {
103		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
104	}
105	androidMkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0]
106	path := androidMkEntries.EntryMap["LOCAL_CERTIFICATE"]
107	expectedPath := []string{"build/make/target/product/security/platform.x509.pem"}
108	if !reflect.DeepEqual(path, expectedPath) {
109		t.Errorf("Unexpected LOCAL_CERTIFICATE value: %v, expected: %v", path, expectedPath)
110	}
111
112	// Check device location.
113	path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
114	expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay")}
115	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
116
117	// A themed module has a different device location
118	m = result.ModuleForTests("foo_themed", "android_common")
119	androidMkEntries = android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0]
120	path = androidMkEntries.EntryMap["LOCAL_MODULE_PATH"]
121	expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay/faza")}
122	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
123
124	overrides := androidMkEntries.EntryMap["LOCAL_OVERRIDES_PACKAGES"]
125	expectedOverrides := []string{"foo"}
126	if !reflect.DeepEqual(overrides, expectedOverrides) {
127		t.Errorf("Unexpected LOCAL_OVERRIDES_PACKAGES value: %v, expected: %v", overrides, expectedOverrides)
128	}
129}
130
131func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) {
132	result := android.GroupFixturePreparers(
133		PrepareForTestWithJavaDefaultModules,
134		android.FixtureModifyConfig(android.SetKatiEnabledForTests),
135	).RunTestWithBp(t, `
136		java_defaults {
137			name: "rro_defaults",
138			theme: "default_theme",
139			product_specific: true,
140			aaptflags: ["--keep-raw-values"],
141		}
142
143		runtime_resource_overlay {
144			name: "foo_with_defaults",
145			defaults: ["rro_defaults"],
146		}
147
148		runtime_resource_overlay {
149			name: "foo_barebones",
150		}
151		`)
152
153	//
154	// RRO module with defaults
155	//
156	m := result.ModuleForTests("foo_with_defaults", "android_common")
157
158	// Check AAPT2 link flags.
159	aapt2Flags := strings.Split(m.Output("package-res.apk").Args["flags"], " ")
160	expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
161	absentFlags := android.RemoveListFromList(expectedFlags, aapt2Flags)
162	if len(absentFlags) > 0 {
163		t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags)
164	}
165
166	// Check device location.
167	path := android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
168	expectedPath := []string{shared.JoinPath("out/target/product/test_device/product/overlay/default_theme")}
169	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
170
171	//
172	// RRO module without defaults
173	//
174	m = result.ModuleForTests("foo_barebones", "android_common")
175
176	// Check AAPT2 link flags.
177	aapt2Flags = strings.Split(m.Output("package-res.apk").Args["flags"], " ")
178	unexpectedFlags := "--keep-raw-values"
179	if inList(unexpectedFlags, aapt2Flags) {
180		t.Errorf("unexpected value, %q is present in aapt2 link flags, %q", unexpectedFlags, aapt2Flags)
181	}
182
183	// Check device location.
184	path = android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
185	expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay")}
186	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
187}
188
189func TestOverrideRuntimeResourceOverlay(t *testing.T) {
190	ctx, _ := testJava(t, `
191		runtime_resource_overlay {
192			name: "foo_overlay",
193			certificate: "platform",
194			product_specific: true,
195			sdk_version: "current",
196		}
197
198		override_runtime_resource_overlay {
199			name: "bar_overlay",
200			base: "foo_overlay",
201			package_name: "com.android.bar.overlay",
202			target_package_name: "com.android.bar",
203			category: "mycategory",
204		}
205		`)
206
207	expectedVariants := []struct {
208		moduleName        string
209		variantName       string
210		apkPath           string
211		overrides         []string
212		targetVariant     string
213		packageFlag       string
214		targetPackageFlag string
215		categoryFlag      string
216	}{
217		{
218			variantName:       "android_common",
219			apkPath:           "out/soong/target/product/test_device/product/overlay/foo_overlay.apk",
220			overrides:         nil,
221			targetVariant:     "android_common",
222			packageFlag:       "",
223			targetPackageFlag: "",
224		},
225		{
226			variantName:       "android_common_bar_overlay",
227			apkPath:           "out/soong/target/product/test_device/product/overlay/bar_overlay.apk",
228			overrides:         []string{"foo_overlay"},
229			targetVariant:     "android_common_bar",
230			packageFlag:       "com.android.bar.overlay",
231			targetPackageFlag: "com.android.bar",
232			categoryFlag:      "mycategory",
233		},
234	}
235	for _, expected := range expectedVariants {
236		variant := ctx.ModuleForTests("foo_overlay", expected.variantName)
237
238		// Check the final apk name
239		variant.Output(expected.apkPath)
240
241		// Check if the overrides field values are correctly aggregated.
242		mod := variant.Module().(*RuntimeResourceOverlay)
243		if !reflect.DeepEqual(expected.overrides, mod.properties.Overrides) {
244			t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
245				expected.overrides, mod.properties.Overrides)
246		}
247
248		// Check aapt2 flags.
249		res := variant.Output("package-res.apk")
250		aapt2Flags := res.Args["flags"]
251		checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag)
252		checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", "")
253		checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-target-package", expected.targetPackageFlag)
254		checkAapt2LinkFlag(t, aapt2Flags, "rename-overlay-category", expected.categoryFlag)
255	}
256}
257
258func TestEnforceRRO_propagatesToDependencies(t *testing.T) {
259	testCases := []struct {
260		name              string
261		enforceRROTargets []string
262		rroDirs           map[string][]string
263	}{
264		{
265			name:              "no RRO",
266			enforceRROTargets: nil,
267			rroDirs: map[string][]string{
268				"foo": nil,
269				"bar": nil,
270			},
271		},
272		{
273			name:              "enforce RRO on all",
274			enforceRROTargets: []string{"*"},
275			rroDirs: map[string][]string{
276				"foo": {"product/vendor/blah/overlay/lib2/res"},
277				"bar": {"product/vendor/blah/overlay/lib2/res"},
278			},
279		},
280		{
281			name:              "enforce RRO on foo",
282			enforceRROTargets: []string{"foo"},
283			rroDirs: map[string][]string{
284				"foo": {"product/vendor/blah/overlay/lib2/res"},
285				"bar": {"product/vendor/blah/overlay/lib2/res"},
286			},
287		},
288	}
289
290	productResourceOverlays := []string{
291		"product/vendor/blah/overlay",
292	}
293
294	fs := android.MockFS{
295		"lib2/res/values/strings.xml":                             nil,
296		"product/vendor/blah/overlay/lib2/res/values/strings.xml": nil,
297	}
298
299	bp := `
300			android_app {
301				name: "foo",
302				sdk_version: "current",
303				resource_dirs: [],
304				static_libs: ["lib"],
305			}
306
307			android_app {
308				name: "bar",
309				sdk_version: "current",
310				resource_dirs: [],
311				static_libs: ["lib"],
312			}
313
314			android_library {
315				name: "lib",
316				sdk_version: "current",
317				resource_dirs: [],
318				static_libs: ["lib2"],
319			}
320
321			android_library {
322				name: "lib2",
323				sdk_version: "current",
324				resource_dirs: ["lib2/res"],
325			}
326		`
327
328	for _, testCase := range testCases {
329		t.Run(testCase.name, func(t *testing.T) {
330			result := android.GroupFixturePreparers(
331				PrepareForTestWithJavaDefaultModules,
332				fs.AddToFixture(),
333				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
334					variables.ProductResourceOverlays = productResourceOverlays
335					if testCase.enforceRROTargets != nil {
336						variables.EnforceRROTargets = testCase.enforceRROTargets
337					}
338				}),
339			).RunTestWithBp(t, bp)
340
341			modules := []string{"foo", "bar"}
342			for _, moduleName := range modules {
343				module := result.ModuleForTests(moduleName, "android_common")
344				mkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, module.Module())[0]
345				actualRRODirs := mkEntries.EntryMap["LOCAL_SOONG_PRODUCT_RRO_DIRS"]
346				if !reflect.DeepEqual(actualRRODirs, testCase.rroDirs[moduleName]) {
347					t.Errorf("exected %s LOCAL_SOONG_PRODUCT_RRO_DIRS entry: %v\ngot:%q",
348						moduleName, testCase.rroDirs[moduleName], actualRRODirs)
349				}
350			}
351		})
352	}
353}
354
355func TestRuntimeResourceOverlayPartition(t *testing.T) {
356	bp := `
357		runtime_resource_overlay {
358			name: "device_specific",
359			device_specific: true,
360		}
361		runtime_resource_overlay {
362			name: "soc_specific",
363			soc_specific: true,
364		}
365		runtime_resource_overlay {
366			name: "system_ext_specific",
367			system_ext_specific: true,
368		}
369		runtime_resource_overlay {
370			name: "product_specific",
371			product_specific: true,
372		}
373		runtime_resource_overlay {
374			name: "default"
375		}
376	`
377	testCases := []struct {
378		name         string
379		expectedPath string
380	}{
381		{
382			name:         "device_specific",
383			expectedPath: "out/soong/target/product/test_device/odm/overlay",
384		},
385		{
386			name:         "soc_specific",
387			expectedPath: "out/soong/target/product/test_device/vendor/overlay",
388		},
389		{
390			name:         "system_ext_specific",
391			expectedPath: "out/soong/target/product/test_device/system_ext/overlay",
392		},
393		{
394			name:         "product_specific",
395			expectedPath: "out/soong/target/product/test_device/product/overlay",
396		},
397		{
398			name:         "default",
399			expectedPath: "out/soong/target/product/test_device/product/overlay",
400		},
401	}
402	for _, testCase := range testCases {
403		ctx, _ := testJava(t, bp)
404		mod := ctx.ModuleForTests(testCase.name, "android_common").Module().(*RuntimeResourceOverlay)
405		android.AssertPathRelativeToTopEquals(t, "Install dir is not correct for "+testCase.name, testCase.expectedPath, mod.installDir)
406	}
407}
408
409func TestRuntimeResourceOverlayFlagsPackages(t *testing.T) {
410	result := android.GroupFixturePreparers(
411		prepareForJavaTest,
412	).RunTestWithBp(t, `
413		runtime_resource_overlay {
414			name: "foo",
415			sdk_version: "current",
416			flags_packages: [
417				"bar",
418				"baz",
419			],
420		}
421		aconfig_declarations {
422			name: "bar",
423			package: "com.example.package.bar",
424			container: "com.android.foo",
425			srcs: [
426				"bar.aconfig",
427			],
428		}
429		aconfig_declarations {
430			name: "baz",
431			package: "com.example.package.baz",
432			container: "com.android.foo",
433			srcs: [
434				"baz.aconfig",
435			],
436		}
437	`)
438
439	foo := result.ModuleForTests("foo", "android_common")
440
441	// runtime_resource_overlay module depends on aconfig_declarations listed in flags_packages
442	android.AssertBoolEquals(t, "foo expected to depend on bar", true,
443		CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "bar"))
444
445	android.AssertBoolEquals(t, "foo expected to depend on baz", true,
446		CheckModuleHasDependency(t, result.TestContext, "foo", "android_common", "baz"))
447
448	aapt2LinkRule := foo.Rule("android/soong/java.aapt2Link")
449	linkInFlags := aapt2LinkRule.Args["inFlags"]
450	android.AssertStringDoesContain(t,
451		"aapt2 link command expected to pass feature flags arguments",
452		linkInFlags,
453		"--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt",
454	)
455}
456