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	"regexp"
20	"strings"
21	"testing"
22
23	"github.com/google/blueprint/proptools"
24
25	"android/soong/android"
26)
27
28func TestAndroidAppImport(t *testing.T) {
29	ctx, _ := testJava(t, `
30		android_app_import {
31			name: "foo",
32			apk: "prebuilts/apk/app.apk",
33			certificate: "platform",
34			dex_preopt: {
35				enabled: true,
36			},
37		}
38		`)
39
40	variant := ctx.ModuleForTests("foo", "android_common")
41
42	// Check dexpreopt outputs.
43	if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
44		variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
45		t.Errorf("can't find dexpreopt outputs")
46	}
47
48	// Check cert signing flag.
49	signedApk := variant.Output("signed/foo.apk")
50	signingFlag := signedApk.Args["certificates"]
51	expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
52	if expected != signingFlag {
53		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
54	}
55}
56
57func TestAndroidAppImport_NoDexPreopt(t *testing.T) {
58	ctx, _ := testJava(t, `
59		android_app_import {
60			name: "foo",
61			apk: "prebuilts/apk/app.apk",
62			certificate: "platform",
63			dex_preopt: {
64				enabled: false,
65			},
66		}
67		`)
68
69	variant := ctx.ModuleForTests("foo", "android_common")
70
71	// Check dexpreopt outputs. They shouldn't exist.
72	if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule != nil ||
73		variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule != nil {
74		t.Errorf("dexpreopt shouldn't have run.")
75	}
76}
77
78func TestAndroidAppImport_Presigned(t *testing.T) {
79	ctx, _ := testJava(t, `
80		android_app_import {
81			name: "foo",
82			apk: "prebuilts/apk/app.apk",
83			presigned: true,
84			dex_preopt: {
85				enabled: true,
86			},
87		}
88		`)
89
90	variant := ctx.ModuleForTests("foo", "android_common")
91
92	// Check dexpreopt outputs.
93	if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
94		variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
95		t.Errorf("can't find dexpreopt outputs")
96	}
97	// Make sure signing was skipped and aligning was done.
98	if variant.MaybeOutput("signed/foo.apk").Rule != nil {
99		t.Errorf("signing rule shouldn't be included.")
100	}
101	if variant.MaybeOutput("zip-aligned/foo.apk").Rule == nil {
102		t.Errorf("can't find aligning rule")
103	}
104}
105
106func TestAndroidAppImport_SigningLineage(t *testing.T) {
107	ctx, _ := testJava(t, `
108	  android_app_import {
109			name: "foo",
110			apk: "prebuilts/apk/app.apk",
111			certificate: "platform",
112			additional_certificates: [":additional_certificate"],
113			lineage: "lineage.bin",
114		}
115
116		android_app_certificate {
117			name: "additional_certificate",
118			certificate: "cert/additional_cert",
119		}
120	`)
121
122	variant := ctx.ModuleForTests("foo", "android_common")
123
124	signedApk := variant.Output("signed/foo.apk")
125	// Check certificates
126	certificatesFlag := signedApk.Args["certificates"]
127	expected := "build/make/target/product/security/platform.x509.pem " +
128		"build/make/target/product/security/platform.pk8 " +
129		"cert/additional_cert.x509.pem cert/additional_cert.pk8"
130	if expected != certificatesFlag {
131		t.Errorf("Incorrect certificates flags, expected: %q, got: %q", expected, certificatesFlag)
132	}
133	// Check cert signing lineage flag.
134	signingFlag := signedApk.Args["flags"]
135	expected = "--lineage lineage.bin"
136	if expected != signingFlag {
137		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
138	}
139}
140
141func TestAndroidAppImport_SigningLineageFilegroup(t *testing.T) {
142	ctx, _ := testJava(t, `
143	  android_app_import {
144			name: "foo",
145			apk: "prebuilts/apk/app.apk",
146			certificate: "platform",
147			lineage: ":lineage_bin",
148		}
149
150		filegroup {
151			name: "lineage_bin",
152			srcs: ["lineage.bin"],
153		}
154	`)
155
156	variant := ctx.ModuleForTests("foo", "android_common")
157
158	signedApk := variant.Output("signed/foo.apk")
159	// Check cert signing lineage flag.
160	signingFlag := signedApk.Args["flags"]
161	expected := "--lineage lineage.bin"
162	if expected != signingFlag {
163		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
164	}
165}
166
167func TestAndroidAppImport_DefaultDevCert(t *testing.T) {
168	ctx, _ := testJava(t, `
169		android_app_import {
170			name: "foo",
171			apk: "prebuilts/apk/app.apk",
172			default_dev_cert: true,
173			dex_preopt: {
174				enabled: true,
175			},
176		}
177		`)
178
179	variant := ctx.ModuleForTests("foo", "android_common")
180
181	// Check dexpreopt outputs.
182	if variant.MaybeOutput("dexpreopt/oat/arm64/package.vdex").Rule == nil ||
183		variant.MaybeOutput("dexpreopt/oat/arm64/package.odex").Rule == nil {
184		t.Errorf("can't find dexpreopt outputs")
185	}
186
187	// Check cert signing flag.
188	signedApk := variant.Output("signed/foo.apk")
189	signingFlag := signedApk.Args["certificates"]
190	expected := "build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8"
191	if expected != signingFlag {
192		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
193	}
194}
195
196func TestAndroidAppImport_DpiVariants(t *testing.T) {
197	bp := `
198		android_app_import {
199			name: "foo",
200			apk: "prebuilts/apk/app.apk",
201			dpi_variants: {
202				xhdpi: {
203					apk: "prebuilts/apk/app_xhdpi.apk",
204				},
205				xxhdpi: {
206					apk: "prebuilts/apk/app_xxhdpi.apk",
207				},
208			},
209			presigned: true,
210			dex_preopt: {
211				enabled: true,
212			},
213		}
214		`
215	testCases := []struct {
216		name                string
217		aaptPreferredConfig *string
218		aaptPrebuiltDPI     []string
219		expected            string
220	}{
221		{
222			name:                "no preferred",
223			aaptPreferredConfig: nil,
224			aaptPrebuiltDPI:     []string{},
225			expected:            "verify_uses_libraries/apk/app.apk",
226		},
227		{
228			name:                "AAPTPreferredConfig matches",
229			aaptPreferredConfig: proptools.StringPtr("xhdpi"),
230			aaptPrebuiltDPI:     []string{"xxhdpi", "ldpi"},
231			expected:            "verify_uses_libraries/apk/app_xhdpi.apk",
232		},
233		{
234			name:                "AAPTPrebuiltDPI matches",
235			aaptPreferredConfig: proptools.StringPtr("mdpi"),
236			aaptPrebuiltDPI:     []string{"xxhdpi", "xhdpi"},
237			expected:            "verify_uses_libraries/apk/app_xxhdpi.apk",
238		},
239		{
240			name:                "non-first AAPTPrebuiltDPI matches",
241			aaptPreferredConfig: proptools.StringPtr("mdpi"),
242			aaptPrebuiltDPI:     []string{"ldpi", "xhdpi"},
243			expected:            "verify_uses_libraries/apk/app_xhdpi.apk",
244		},
245		{
246			name:                "no matches",
247			aaptPreferredConfig: proptools.StringPtr("mdpi"),
248			aaptPrebuiltDPI:     []string{"ldpi", "xxxhdpi"},
249			expected:            "verify_uses_libraries/apk/app.apk",
250		},
251	}
252
253	jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
254	for _, test := range testCases {
255		result := android.GroupFixturePreparers(
256			PrepareForTestWithJavaDefaultModules,
257			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
258				variables.AAPTPreferredConfig = test.aaptPreferredConfig
259				variables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
260			}),
261		).RunTestWithBp(t, bp)
262
263		variant := result.ModuleForTests("foo", "android_common")
264		jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
265		matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
266		if len(matches) != 2 {
267			t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
268		}
269		if strings.HasSuffix(matches[1], test.expected) {
270			t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
271		}
272	}
273}
274
275func TestAndroidAppImport_Filename(t *testing.T) {
276	ctx, _ := testJava(t, `
277		android_app_import {
278			name: "foo",
279			apk: "prebuilts/apk/app.apk",
280			presigned: true,
281		}
282
283		android_app_import {
284			name: "bar",
285			apk: "prebuilts/apk/app.apk",
286			presigned: true,
287			filename: "bar_sample.apk"
288		}
289		`)
290
291	testCases := []struct {
292		name     string
293		expected string
294	}{
295		{
296			name:     "foo",
297			expected: "foo.apk",
298		},
299		{
300			name:     "bar",
301			expected: "bar_sample.apk",
302		},
303	}
304
305	for _, test := range testCases {
306		variant := ctx.ModuleForTests(test.name, "android_common")
307		if variant.MaybeOutput(test.expected).Rule == nil {
308			t.Errorf("can't find output named %q - all outputs: %v", test.expected, variant.AllOutputs())
309		}
310
311		a := variant.Module().(*AndroidAppImport)
312		expectedValues := []string{test.expected}
313		actualValues := android.AndroidMkEntriesForTest(t, ctx, a)[0].EntryMap["LOCAL_INSTALLED_MODULE_STEM"]
314		if !reflect.DeepEqual(actualValues, expectedValues) {
315			t.Errorf("Incorrect LOCAL_INSTALLED_MODULE_STEM value '%s', expected '%s'",
316				actualValues, expectedValues)
317		}
318	}
319}
320
321func TestAndroidAppImport_ArchVariants(t *testing.T) {
322	// The test config's target arch is ARM64.
323	testCases := []struct {
324		name     string
325		bp       string
326		expected string
327	}{
328		{
329			name: "matching arch",
330			bp: `
331				android_app_import {
332					name: "foo",
333					apk: "prebuilts/apk/app.apk",
334					arch: {
335						arm64: {
336							apk: "prebuilts/apk/app_arm64.apk",
337						},
338					},
339					presigned: true,
340					dex_preopt: {
341						enabled: true,
342					},
343				}
344			`,
345			expected: "verify_uses_libraries/apk/app_arm64.apk",
346		},
347		{
348			name: "no matching arch",
349			bp: `
350				android_app_import {
351					name: "foo",
352					apk: "prebuilts/apk/app.apk",
353					arch: {
354						arm: {
355							apk: "prebuilts/apk/app_arm.apk",
356						},
357					},
358					presigned: true,
359					dex_preopt: {
360						enabled: true,
361					},
362				}
363			`,
364			expected: "verify_uses_libraries/apk/app.apk",
365		},
366		{
367			name: "no matching arch without default",
368			bp: `
369				android_app_import {
370					name: "foo",
371					arch: {
372						arm: {
373							apk: "prebuilts/apk/app_arm.apk",
374						},
375					},
376					presigned: true,
377					dex_preopt: {
378						enabled: true,
379					},
380				}
381			`,
382			expected: "",
383		},
384	}
385
386	jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
387	for _, test := range testCases {
388		ctx, _ := testJava(t, test.bp)
389
390		variant := ctx.ModuleForTests("foo", "android_common")
391		if test.expected == "" {
392			if variant.Module().Enabled() {
393				t.Error("module should have been disabled, but wasn't")
394			}
395			continue
396		}
397		jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
398		matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
399		if len(matches) != 2 {
400			t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
401		}
402		if strings.HasSuffix(matches[1], test.expected) {
403			t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
404		}
405	}
406}
407
408func TestAndroidAppImport_overridesDisabledAndroidApp(t *testing.T) {
409	ctx, _ := testJava(t, `
410		android_app {
411			name: "foo",
412			srcs: ["a.java"],
413			enabled: false,
414		}
415
416 		android_app_import {
417			name: "foo",
418			apk: "prebuilts/apk/app.apk",
419			certificate: "platform",
420			prefer: true,
421		}
422		`)
423
424	variant := ctx.ModuleForTests("prebuilt_foo", "android_common")
425	a := variant.Module().(*AndroidAppImport)
426	// The prebuilt module should still be enabled and active even if the source-based counterpart
427	// is disabled.
428	if !a.prebuilt.UsePrebuilt() {
429		t.Errorf("prebuilt foo module is not active")
430	}
431	if !a.Enabled() {
432		t.Errorf("prebuilt foo module is disabled")
433	}
434}
435
436func TestAndroidAppImport_frameworkRes(t *testing.T) {
437	ctx, _ := testJava(t, `
438		android_app_import {
439			name: "framework-res",
440			certificate: "platform",
441			apk: "package-res.apk",
442			prefer: true,
443			export_package_resources: true,
444			// Disable dexpreopt and verify_uses_libraries check as the app
445			// contains no Java code to be dexpreopted.
446			enforce_uses_libs: false,
447			dex_preopt: {
448				enabled: false,
449			},
450		}
451		`)
452
453	mod := ctx.ModuleForTests("prebuilt_framework-res", "android_common").Module()
454	a := mod.(*AndroidAppImport)
455
456	if !a.preprocessed {
457		t.Errorf("prebuilt framework-res is not preprocessed")
458	}
459
460	expectedInstallPath := "out/soong/target/product/test_device/system/framework/framework-res.apk"
461
462	android.AssertPathRelativeToTopEquals(t, "prebuilt framework-res install location", expectedInstallPath, a.dexpreopter.installPath)
463
464	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
465
466	expectedPath := "."
467	// From apk property above, in the root of the source tree.
468	expectedPrebuiltModuleFile := "package-res.apk"
469	// Verify that the apk is preprocessed: The export package is the same
470	// as the prebuilt.
471	expectedSoongResourceExportPackage := expectedPrebuiltModuleFile
472
473	actualPath := entries.EntryMap["LOCAL_PATH"]
474	actualPrebuiltModuleFile := entries.EntryMap["LOCAL_PREBUILT_MODULE_FILE"]
475	actualSoongResourceExportPackage := entries.EntryMap["LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE"]
476
477	if len(actualPath) != 1 {
478		t.Errorf("LOCAL_PATH incorrect len %d", len(actualPath))
479	} else if actualPath[0] != expectedPath {
480		t.Errorf("LOCAL_PATH mismatch, actual: %s, expected: %s", actualPath[0], expectedPath)
481	}
482
483	if len(actualPrebuiltModuleFile) != 1 {
484		t.Errorf("LOCAL_PREBUILT_MODULE_FILE incorrect len %d", len(actualPrebuiltModuleFile))
485	} else if actualPrebuiltModuleFile[0] != expectedPrebuiltModuleFile {
486		t.Errorf("LOCAL_PREBUILT_MODULE_FILE mismatch, actual: %s, expected: %s", actualPrebuiltModuleFile[0], expectedPrebuiltModuleFile)
487	}
488
489	if len(actualSoongResourceExportPackage) != 1 {
490		t.Errorf("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE incorrect len %d", len(actualSoongResourceExportPackage))
491	} else if actualSoongResourceExportPackage[0] != expectedSoongResourceExportPackage {
492		t.Errorf("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE mismatch, actual: %s, expected: %s", actualSoongResourceExportPackage[0], expectedSoongResourceExportPackage)
493	}
494}
495
496func TestAndroidTestImport(t *testing.T) {
497	ctx, _ := testJava(t, `
498		android_test_import {
499			name: "foo",
500			apk: "prebuilts/apk/app.apk",
501			presigned: true,
502			data: [
503				"testdata/data",
504			],
505		}
506		`)
507
508	test := ctx.ModuleForTests("foo", "android_common").Module().(*AndroidTestImport)
509
510	// Check android mks.
511	entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
512	expected := []string{"tests"}
513	actual := entries.EntryMap["LOCAL_MODULE_TAGS"]
514	if !reflect.DeepEqual(expected, actual) {
515		t.Errorf("Unexpected module tags - expected: %q, actual: %q", expected, actual)
516	}
517	expected = []string{"testdata/data:testdata/data"}
518	actual = entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
519	if !reflect.DeepEqual(expected, actual) {
520		t.Errorf("Unexpected test data - expected: %q, actual: %q", expected, actual)
521	}
522}
523
524func TestAndroidTestImport_NoJinUncompressForPresigned(t *testing.T) {
525	ctx, _ := testJava(t, `
526		android_test_import {
527			name: "foo",
528			apk: "prebuilts/apk/app.apk",
529			certificate: "cert/new_cert",
530			data: [
531				"testdata/data",
532			],
533		}
534
535		android_test_import {
536			name: "foo_presigned",
537			apk: "prebuilts/apk/app.apk",
538			presigned: true,
539			data: [
540				"testdata/data",
541			],
542		}
543		`)
544
545	variant := ctx.ModuleForTests("foo", "android_common")
546	jniRule := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
547	if !strings.HasPrefix(jniRule, "if (zipinfo") {
548		t.Errorf("Unexpected JNI uncompress rule command: " + jniRule)
549	}
550
551	variant = ctx.ModuleForTests("foo_presigned", "android_common")
552	jniRule = variant.Output("jnis-uncompressed/foo_presigned.apk").BuildParams.Rule.String()
553	if jniRule != android.Cp.String() {
554		t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
555	}
556	if variant.MaybeOutput("zip-aligned/foo_presigned.apk").Rule == nil {
557		t.Errorf("Presigned test apk should be aligned")
558	}
559}
560
561func TestAndroidTestImport_Preprocessed(t *testing.T) {
562	ctx, _ := testJava(t, `
563		android_test_import {
564			name: "foo",
565			apk: "prebuilts/apk/app.apk",
566			presigned: true,
567			preprocessed: true,
568		}
569
570		android_test_import {
571			name: "foo_cert",
572			apk: "prebuilts/apk/app.apk",
573			certificate: "cert/new_cert",
574			preprocessed: true,
575		}
576		`)
577
578	testModules := []string{"foo", "foo_cert"}
579	for _, m := range testModules {
580		apkName := m + ".apk"
581		variant := ctx.ModuleForTests(m, "android_common")
582		jniRule := variant.Output("jnis-uncompressed/" + apkName).BuildParams.Rule.String()
583		if jniRule != android.Cp.String() {
584			t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
585		}
586
587		// Make sure signing and aligning were skipped.
588		if variant.MaybeOutput("signed/"+apkName).Rule != nil {
589			t.Errorf("signing rule shouldn't be included for preprocessed.")
590		}
591		if variant.MaybeOutput("zip-aligned/"+apkName).Rule != nil {
592			t.Errorf("aligning rule shouldn't be for preprocessed")
593		}
594	}
595}
596