// Copyright 2018 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 apex import ( "fmt" "path" "path/filepath" "reflect" "regexp" "sort" "strconv" "strings" "testing" "android/soong/aconfig/codegen" "github.com/google/blueprint" "github.com/google/blueprint/proptools" "android/soong/android" "android/soong/bpf" "android/soong/cc" "android/soong/dexpreopt" prebuilt_etc "android/soong/etc" "android/soong/filesystem" "android/soong/java" "android/soong/rust" "android/soong/sh" ) // names returns name list from white space separated string func names(s string) (ns []string) { for _, n := range strings.Split(s, " ") { if len(n) > 0 { ns = append(ns, n) } } return } func testApexError(t *testing.T, pattern, bp string, preparers ...android.FixturePreparer) { t.Helper() android.GroupFixturePreparers( prepareForApexTest, android.GroupFixturePreparers(preparers...), ). ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)). RunTestWithBp(t, bp) } func testApex(t *testing.T, bp string, preparers ...android.FixturePreparer) *android.TestContext { t.Helper() optionalBpPreparer := android.NullFixturePreparer if bp != "" { optionalBpPreparer = android.FixtureWithRootAndroidBp(bp) } result := android.GroupFixturePreparers( prepareForApexTest, android.GroupFixturePreparers(preparers...), optionalBpPreparer, ).RunTest(t) return result.TestContext } func withFiles(files android.MockFS) android.FixturePreparer { return files.AddToFixture() } func withTargets(targets map[android.OsType][]android.Target) android.FixturePreparer { return android.FixtureModifyConfig(func(config android.Config) { for k, v := range targets { config.Targets[k] = v } }) } // withNativeBridgeTargets sets configuration with targets including: // - X86_64 (primary) // - X86 (secondary) // - Arm64 on X86_64 (native bridge) // - Arm on X86 (native bridge) var withNativeBridgeEnabled = android.FixtureModifyConfig( func(config android.Config) { config.Targets[android.Android] = []android.Target{ {Os: android.Android, Arch: android.Arch{ArchType: android.X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""}, {Os: android.Android, Arch: android.Arch{ArchType: android.X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""}, {Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "x86_64", NativeBridgeRelativePath: "arm64"}, {Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "x86", NativeBridgeRelativePath: "arm"}, } }, ) func withManifestPackageNameOverrides(specs []string) android.FixturePreparer { return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.ManifestPackageNameOverrides = specs }) } func withApexGlobalMinSdkVersionOverride(minSdkOverride *string) android.FixturePreparer { return android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.ApexGlobalMinSdkVersionOverride = minSdkOverride }) } var withBinder32bit = android.FixtureModifyProductVariables( func(variables android.FixtureProductVariables) { variables.Binder32bit = proptools.BoolPtr(true) }, ) var withUnbundledBuild = android.FixtureModifyProductVariables( func(variables android.FixtureProductVariables) { variables.Unbundled_build = proptools.BoolPtr(true) }, ) // Legacy preparer used for running tests within the apex package. // // This includes everything that was needed to run any test in the apex package prior to the // introduction of the test fixtures. Tests that are being converted to use fixtures directly // rather than through the testApex...() methods should avoid using this and instead use the // various preparers directly, using android.GroupFixturePreparers(...) to group them when // necessary. // // deprecated var prepareForApexTest = android.GroupFixturePreparers( // General preparers in alphabetical order as test infrastructure will enforce correct // registration order. android.PrepareForTestWithAndroidBuildComponents, bpf.PrepareForTestWithBpf, cc.PrepareForTestWithCcBuildComponents, java.PrepareForTestWithDexpreopt, prebuilt_etc.PrepareForTestWithPrebuiltEtc, rust.PrepareForTestWithRustDefaultModules, sh.PrepareForTestWithShBuildComponents, codegen.PrepareForTestWithAconfigBuildComponents, PrepareForTestWithApexBuildComponents, // Additional apex test specific preparers. android.FixtureAddTextFile("system/sepolicy/Android.bp", ` filegroup { name: "myapex-file_contexts", srcs: [ "apex/myapex-file_contexts", ], } `), prepareForTestWithMyapex, android.FixtureMergeMockFs(android.MockFS{ "a.java": nil, "PrebuiltAppFoo.apk": nil, "PrebuiltAppFooPriv.apk": nil, "apex_manifest.json": nil, "AndroidManifest.xml": nil, "system/sepolicy/apex/myapex.updatable-file_contexts": nil, "system/sepolicy/apex/myapex2-file_contexts": nil, "system/sepolicy/apex/otherapex-file_contexts": nil, "system/sepolicy/apex/com.android.vndk-file_contexts": nil, "system/sepolicy/apex/com.android.vndk.current-file_contexts": nil, "mylib.cpp": nil, "mytest.cpp": nil, "mytest1.cpp": nil, "mytest2.cpp": nil, "mytest3.cpp": nil, "myprebuilt": nil, "my_include": nil, "foo/bar/MyClass.java": nil, "prebuilt.jar": nil, "prebuilt.so": nil, "vendor/foo/devkeys/test.x509.pem": nil, "vendor/foo/devkeys/test.pk8": nil, "testkey.x509.pem": nil, "testkey.pk8": nil, "testkey.override.x509.pem": nil, "testkey.override.pk8": nil, "vendor/foo/devkeys/testkey.avbpubkey": nil, "vendor/foo/devkeys/testkey.pem": nil, "NOTICE": nil, "custom_notice": nil, "custom_notice_for_static_lib": nil, "testkey2.avbpubkey": nil, "testkey2.pem": nil, "myapex-arm64.apex": nil, "myapex-arm.apex": nil, "myapex.apks": nil, "frameworks/base/api/current.txt": nil, "framework/aidl/a.aidl": nil, "dummy.txt": nil, "baz": nil, "bar/baz": nil, "testdata/baz": nil, "AppSet.apks": nil, "foo.rs": nil, "libfoo.jar": nil, "libbar.jar": nil, }, ), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.DefaultAppCertificate = proptools.StringPtr("vendor/foo/devkeys/test") variables.CertificateOverrides = []string{"myapex_keytest:myapex.certificate.override"} variables.Platform_sdk_codename = proptools.StringPtr("Q") variables.Platform_sdk_final = proptools.BoolPtr(false) // "Tiramisu" needs to be in the next line for compatibility with soong code, // not because of these tests specifically (it's not used by the tests) variables.Platform_version_active_codenames = []string{"Q", "Tiramisu"} variables.BuildId = proptools.StringPtr("TEST.BUILD_ID") }), ) var prepareForTestWithMyapex = android.FixtureMergeMockFs(android.MockFS{ "system/sepolicy/apex/myapex-file_contexts": nil, }) // ensure that 'result' equals 'expected' func ensureEquals(t *testing.T, result string, expected string) { t.Helper() if result != expected { t.Errorf("%q != %q", expected, result) } } // ensure that 'result' contains 'expected' func ensureContains(t *testing.T, result string, expected string) { t.Helper() if !strings.Contains(result, expected) { t.Errorf("%q is not found in %q", expected, result) } } // ensure that 'result' contains 'expected' exactly one time func ensureContainsOnce(t *testing.T, result string, expected string) { t.Helper() count := strings.Count(result, expected) if count != 1 { t.Errorf("%q is found %d times (expected 1 time) in %q", expected, count, result) } } // ensures that 'result' does not contain 'notExpected' func ensureNotContains(t *testing.T, result string, notExpected string) { t.Helper() if strings.Contains(result, notExpected) { t.Errorf("%q is found in %q", notExpected, result) } } func ensureMatches(t *testing.T, result string, expectedRex string) { ok, err := regexp.MatchString(expectedRex, result) if err != nil { t.Fatalf("regexp failure trying to match %s against `%s` expression: %s", result, expectedRex, err) return } if !ok { t.Errorf("%s does not match regular expession %s", result, expectedRex) } } func ensureListContains(t *testing.T, result []string, expected string) { t.Helper() if !android.InList(expected, result) { t.Errorf("%q is not found in %v", expected, result) } } func ensureListNotContains(t *testing.T, result []string, notExpected string) { t.Helper() if android.InList(notExpected, result) { t.Errorf("%q is found in %v", notExpected, result) } } func ensureListEmpty(t *testing.T, result []string) { t.Helper() if len(result) > 0 { t.Errorf("%q is expected to be empty", result) } } func ensureListNotEmpty(t *testing.T, result []string) { t.Helper() if len(result) == 0 { t.Errorf("%q is expected to be not empty", result) } } // Minimal test func TestBasicApex(t *testing.T) { ctx := testApex(t, ` apex_defaults { name: "myapex-defaults", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", binaries: ["foo.rust"], native_shared_libs: [ "mylib", "libfoo.ffi", ], rust_dyn_libs: ["libfoo.dylib.rust"], multilib: { both: { binaries: ["foo"], } }, java_libs: [ "myjar", "myjar_dex", ], updatable: false, } apex { name: "myapex", defaults: ["myapex-defaults"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } filegroup { name: "myapex.manifest", srcs: ["apex_manifest.json"], } filegroup { name: "myapex.androidmanifest", srcs: ["AndroidManifest.xml"], } cc_library { name: "mylib", srcs: ["mylib.cpp"], shared_libs: [ "mylib2", "libbar.ffi", ], system_shared_libs: [], stl: "none", // TODO: remove //apex_available:platform apex_available: [ "//apex_available:platform", "myapex", ], } cc_binary { name: "foo", srcs: ["mylib.cpp"], compile_multilib: "both", multilib: { lib32: { suffix: "32", }, lib64: { suffix: "64", }, }, symlinks: ["foo_link_"], symlink_preferred_arch: true, system_shared_libs: [], stl: "none", apex_available: [ "myapex", "com.android.gki.*" ], } rust_binary { name: "foo.rust", srcs: ["foo.rs"], rlibs: ["libfoo.rlib.rust"], rustlibs: ["libfoo.dylib.rust"], apex_available: ["myapex"], } rust_library_rlib { name: "libfoo.rlib.rust", srcs: ["foo.rs"], crate_name: "foo", apex_available: ["myapex"], shared_libs: ["libfoo.shared_from_rust"], } cc_library_shared { name: "libfoo.shared_from_rust", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: ["myapex"], } rust_library_dylib { name: "libfoo.dylib.rust", srcs: ["foo.rs"], crate_name: "foo", apex_available: ["myapex"], } rust_ffi_shared { name: "libfoo.ffi", srcs: ["foo.rs"], crate_name: "foo", apex_available: ["myapex"], } rust_ffi_shared { name: "libbar.ffi", srcs: ["foo.rs"], crate_name: "bar", apex_available: ["myapex"], } apex { name: "com.android.gki.fake", binaries: ["foo"], key: "myapex.key", file_contexts: ":myapex-file_contexts", updatable: false, } cc_library_shared { name: "mylib2", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", static_libs: ["libstatic"], // TODO: remove //apex_available:platform apex_available: [ "//apex_available:platform", "myapex", ], } cc_prebuilt_library_shared { name: "mylib2", srcs: ["prebuilt.so"], // TODO: remove //apex_available:platform apex_available: [ "//apex_available:platform", "myapex", ], } cc_library_static { name: "libstatic", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", // TODO: remove //apex_available:platform apex_available: [ "//apex_available:platform", "myapex", ], } java_library { name: "myjar", srcs: ["foo/bar/MyClass.java"], stem: "myjar_stem", sdk_version: "none", system_modules: "none", static_libs: ["myotherjar"], libs: ["mysharedjar"], // TODO: remove //apex_available:platform apex_available: [ "//apex_available:platform", "myapex", ], } dex_import { name: "myjar_dex", jars: ["prebuilt.jar"], apex_available: [ "//apex_available:platform", "myapex", ], } java_library { name: "myotherjar", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", // TODO: remove //apex_available:platform apex_available: [ "//apex_available:platform", "myapex", ], } java_library { name: "mysharedjar", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", } `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") // Make sure that Android.mk is created ab := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, ab) var builder strings.Builder data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data) androidMk := builder.String() ensureContains(t, androidMk, "LOCAL_MODULE := mylib.myapex\n") ensureNotContains(t, androidMk, "LOCAL_MODULE := mylib.com.android.myapex\n") optFlags := apexRule.Args["opt_flags"] ensureContains(t, optFlags, "--pubkey vendor/foo/devkeys/testkey.avbpubkey") // Ensure that the NOTICE output is being packaged as an asset. ensureContains(t, optFlags, "--assets_dir out/soong/.intermediates/myapex/android_common_myapex/NOTICE") copyCmds := apexRule.Args["copy_commands"] // Ensure that main rule creates an output ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned") // Ensure that apex variant is created for the direct dep ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_apex10000") ensureListContains(t, ctx.ModuleVariantsForTests("myjar"), "android_common_apex10000") ensureListContains(t, ctx.ModuleVariantsForTests("myjar_dex"), "android_common_apex10000") ensureListContains(t, ctx.ModuleVariantsForTests("foo.rust"), "android_arm64_armv8-a_apex10000") ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.ffi"), "android_arm64_armv8-a_shared_apex10000") // Ensure that apex variant is created for the indirect dep ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_apex10000") ensureListContains(t, ctx.ModuleVariantsForTests("myotherjar"), "android_common_apex10000") ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.rlib.rust"), "android_arm64_armv8-a_rlib_dylib-std_apex10000") ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.dylib.rust"), "android_arm64_armv8-a_dylib_apex10000") ensureListContains(t, ctx.ModuleVariantsForTests("libbar.ffi"), "android_arm64_armv8-a_shared_apex10000") ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.shared_from_rust"), "android_arm64_armv8-a_shared_apex10000") // Ensure that both direct and indirect deps are copied into apex ensureContains(t, copyCmds, "image.apex/lib64/mylib.so") ensureContains(t, copyCmds, "image.apex/lib64/mylib2.so") ensureContains(t, copyCmds, "image.apex/javalib/myjar_stem.jar") ensureContains(t, copyCmds, "image.apex/javalib/myjar_dex.jar") ensureContains(t, copyCmds, "image.apex/lib64/libfoo.dylib.rust.dylib.so") ensureContains(t, copyCmds, "image.apex/lib64/libfoo.ffi.so") ensureContains(t, copyCmds, "image.apex/lib64/libbar.ffi.so") ensureContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so") // .. but not for java libs ensureNotContains(t, copyCmds, "image.apex/javalib/myotherjar.jar") ensureNotContains(t, copyCmds, "image.apex/javalib/msharedjar.jar") // Ensure that the platform variant ends with _shared or _common ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared") ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared") ensureListContains(t, ctx.ModuleVariantsForTests("myjar"), "android_common") ensureListContains(t, ctx.ModuleVariantsForTests("myotherjar"), "android_common") ensureListContains(t, ctx.ModuleVariantsForTests("mysharedjar"), "android_common") // Ensure that dynamic dependency to java libs are not included ensureListNotContains(t, ctx.ModuleVariantsForTests("mysharedjar"), "android_common_myapex") // Ensure that all symlinks are present. found_foo_link_64 := false found_foo := false for _, cmd := range strings.Split(copyCmds, " && ") { if strings.HasPrefix(cmd, "ln -sfn foo64") { if strings.HasSuffix(cmd, "bin/foo") { found_foo = true } else if strings.HasSuffix(cmd, "bin/foo_link_64") { found_foo_link_64 = true } } } good := found_foo && found_foo_link_64 if !good { t.Errorf("Could not find all expected symlinks! foo: %t, foo_link_64: %t. Command was %s", found_foo, found_foo_link_64, copyCmds) } fullDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx, ctx.ModuleForTests("myapex", "android_common_myapex").Output("depsinfo/fulllist.txt")), "\n") ensureListContains(t, fullDepsInfo, " myjar(minSdkVersion:(no version)) <- myapex") ensureListContains(t, fullDepsInfo, " mylib2(minSdkVersion:(no version)) <- mylib") ensureListContains(t, fullDepsInfo, " myotherjar(minSdkVersion:(no version)) <- myjar") ensureListContains(t, fullDepsInfo, " mysharedjar(minSdkVersion:(no version)) (external) <- myjar") flatDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx, ctx.ModuleForTests("myapex", "android_common_myapex").Output("depsinfo/flatlist.txt")), "\n") ensureListContains(t, flatDepsInfo, "myjar(minSdkVersion:(no version))") ensureListContains(t, flatDepsInfo, "mylib2(minSdkVersion:(no version))") ensureListContains(t, flatDepsInfo, "myotherjar(minSdkVersion:(no version))") ensureListContains(t, flatDepsInfo, "mysharedjar(minSdkVersion:(no version)) (external)") } func TestDefaults(t *testing.T) { ctx := testApex(t, ` apex_defaults { name: "myapex-defaults", key: "myapex.key", prebuilts: ["myetc"], native_shared_libs: ["mylib"], java_libs: ["myjar"], apps: ["AppFoo"], rros: ["rro"], bpfs: ["bpf", "netdTest"], updatable: false, } prebuilt_etc { name: "myetc", src: "myprebuilt", } apex { name: "myapex", defaults: ["myapex-defaults"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } java_library { name: "myjar", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", apex_available: [ "myapex" ], } android_app { name: "AppFoo", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", apex_available: [ "myapex" ], } runtime_resource_overlay { name: "rro", theme: "blue", } bpf { name: "bpf", srcs: ["bpf.c", "bpf2.c"], } bpf { name: "netdTest", srcs: ["netdTest.c"], sub_dir: "netd", } `) ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "etc/myetc", "javalib/myjar.jar", "lib64/mylib.so", "app/AppFoo@TEST.BUILD_ID/AppFoo.apk", "overlay/blue/rro.apk", "etc/bpf/bpf.o", "etc/bpf/bpf2.o", "etc/bpf/netd/netdTest.o", }) } func TestApexManifest(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `) module := ctx.ModuleForTests("myapex", "android_common_myapex") args := module.Rule("apexRule").Args if manifest := args["manifest"]; manifest != module.Output("apex_manifest.pb").Output.String() { t.Error("manifest should be apex_manifest.pb, but " + manifest) } } func TestApexManifestMinSdkVersion(t *testing.T) { ctx := testApex(t, ` apex_defaults { name: "my_defaults", key: "myapex.key", product_specific: true, file_contexts: ":my-file-contexts", updatable: false, } apex { name: "myapex_30", min_sdk_version: "30", defaults: ["my_defaults"], } apex { name: "myapex_current", min_sdk_version: "current", defaults: ["my_defaults"], } apex { name: "myapex_none", defaults: ["my_defaults"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } filegroup { name: "my-file-contexts", srcs: ["product_specific_file_contexts"], } `, withFiles(map[string][]byte{ "product_specific_file_contexts": nil, }), android.FixtureModifyProductVariables( func(variables android.FixtureProductVariables) { variables.Unbundled_build = proptools.BoolPtr(true) variables.Always_use_prebuilt_sdks = proptools.BoolPtr(false) }), android.FixtureMergeEnv(map[string]string{ "UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT": "true", })) testCases := []struct { module string minSdkVersion string }{ { module: "myapex_30", minSdkVersion: "30", }, { module: "myapex_current", minSdkVersion: "Q.$$(cat out/soong/api_fingerprint.txt)", }, { module: "myapex_none", minSdkVersion: "Q.$$(cat out/soong/api_fingerprint.txt)", }, } for _, tc := range testCases { module := ctx.ModuleForTests(tc.module, "android_common_"+tc.module) args := module.Rule("apexRule").Args optFlags := args["opt_flags"] if !strings.Contains(optFlags, "--min_sdk_version "+tc.minSdkVersion) { t.Errorf("%s: Expected min_sdk_version=%s, got: %s", tc.module, tc.minSdkVersion, optFlags) } } } func TestApexWithDessertSha(t *testing.T) { ctx := testApex(t, ` apex_defaults { name: "my_defaults", key: "myapex.key", product_specific: true, file_contexts: ":my-file-contexts", updatable: false, } apex { name: "myapex_30", min_sdk_version: "30", defaults: ["my_defaults"], } apex { name: "myapex_current", min_sdk_version: "current", defaults: ["my_defaults"], } apex { name: "myapex_none", defaults: ["my_defaults"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } filegroup { name: "my-file-contexts", srcs: ["product_specific_file_contexts"], } `, withFiles(map[string][]byte{ "product_specific_file_contexts": nil, }), android.FixtureModifyProductVariables( func(variables android.FixtureProductVariables) { variables.Unbundled_build = proptools.BoolPtr(true) variables.Always_use_prebuilt_sdks = proptools.BoolPtr(false) }), android.FixtureMergeEnv(map[string]string{ "UNBUNDLED_BUILD_TARGET_SDK_WITH_DESSERT_SHA": "UpsideDownCake.abcdefghijklmnopqrstuvwxyz123456", })) testCases := []struct { module string minSdkVersion string }{ { module: "myapex_30", minSdkVersion: "30", }, { module: "myapex_current", minSdkVersion: "UpsideDownCake.abcdefghijklmnopqrstuvwxyz123456", }, { module: "myapex_none", minSdkVersion: "UpsideDownCake.abcdefghijklmnopqrstuvwxyz123456", }, } for _, tc := range testCases { module := ctx.ModuleForTests(tc.module, "android_common_"+tc.module) args := module.Rule("apexRule").Args optFlags := args["opt_flags"] if !strings.Contains(optFlags, "--min_sdk_version "+tc.minSdkVersion) { t.Errorf("%s: Expected min_sdk_version=%s, got: %s", tc.module, tc.minSdkVersion, optFlags) } } } func TestFileContexts(t *testing.T) { for _, vendor := range []bool{true, false} { prop := "" if vendor { prop = "vendor: true,\n" } ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", updatable: false, `+prop+` } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `) rule := ctx.ModuleForTests("myapex", "android_common_myapex").Output("file_contexts") if vendor { android.AssertStringDoesContain(t, "should force-label as vendor_apex_metadata_file", rule.RuleParams.Command, "apex_manifest\\\\.pb u:object_r:vendor_apex_metadata_file:s0") } else { android.AssertStringDoesContain(t, "should force-label as system_file", rule.RuleParams.Command, "apex_manifest\\\\.pb u:object_r:system_file:s0") } } } func TestApexWithStubs(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib", "mylib3"], binaries: ["foo.rust"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], shared_libs: ["mylib2", "mylib3"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } cc_library { name: "mylib2", srcs: ["mylib.cpp"], cflags: ["-include mylib.h"], system_shared_libs: [], stl: "none", stubs: { versions: ["1", "2", "3"], }, } cc_library { name: "mylib3", srcs: ["mylib.cpp"], shared_libs: ["mylib4"], system_shared_libs: [], stl: "none", stubs: { versions: ["10", "11", "12"], }, apex_available: [ "myapex" ], } cc_library { name: "mylib4", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } rust_binary { name: "foo.rust", srcs: ["foo.rs"], shared_libs: ["libfoo.shared_from_rust"], prefer_rlib: true, apex_available: ["myapex"], } cc_library_shared { name: "libfoo.shared_from_rust", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", stubs: { versions: ["10", "11", "12"], }, } `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that direct non-stubs dep is always included ensureContains(t, copyCmds, "image.apex/lib64/mylib.so") // Ensure that indirect stubs dep is not included ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so") // Ensure that direct stubs dep is included ensureContains(t, copyCmds, "image.apex/lib64/mylib3.so") mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"] // Ensure that mylib is linking with the latest version of stubs for mylib2 ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so") // ... and not linking to the non-stub (impl) variant of mylib2 ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so") // Ensure that mylib is linking with the non-stub (impl) of mylib3 (because mylib3 is in the same apex) ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_apex10000/mylib3.so") // .. and not linking to the stubs variant of mylib3 ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_12/mylib3.so") // Comment out this test. Now it fails after the optimization of sharing "cflags" in cc/cc.go // is replaced by sharing of "cFlags" in cc/builder.go. // The "cflags" contains "-include mylib.h", but cFlags contained only a reference to the // module variable representing "cflags". So it was not detected by ensureNotContains. // Now "cFlags" is a reference to a module variable like $flags1, which includes all previous // content of "cflags". ModuleForTests...Args["cFlags"] returns the full string of $flags1, // including the original cflags's "-include mylib.h". // // Ensure that stubs libs are built without -include flags // mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"] // ensureNotContains(t, mylib2Cflags, "-include ") // Ensure that genstub for platform-provided lib is invoked with --systemapi ensureContains(t, ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_3").Rule("genStubSrc").Args["flags"], "--systemapi") // Ensure that genstub for apex-provided lib is invoked with --apex ensureContains(t, ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_shared_12").Rule("genStubSrc").Args["flags"], "--apex") ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "lib64/mylib.so", "lib64/mylib3.so", "lib64/mylib4.so", "bin/foo.rust", "lib64/libc++.so", // by the implicit dependency from foo.rust "lib64/liblog.so", // by the implicit dependency from foo.rust }) // Ensure that stub dependency from a rust module is not included ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so") // The rust module is linked to the stub cc library rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"] ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so") ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so") apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule") ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.shared_from_rust.so") } func TestApexShouldNotEmbedStubVariant(t *testing.T) { testApexError(t, `module "myapex" .*: native_shared_libs: "libbar" is a stub`, ` apex { name: "myapex", key: "myapex.key", vendor: true, updatable: false, native_shared_libs: ["libbar"], // should not add an LLNDK stub in a vendor apex } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libbar", srcs: ["mylib.cpp"], llndk: { symbol_file: "libbar.map.txt", } } `) } func TestApexCanUsePrivateApis(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], binaries: ["foo.rust"], updatable: false, platform_apis: true, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], shared_libs: ["mylib2"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } cc_library { name: "mylib2", srcs: ["mylib.cpp"], cflags: ["-include mylib.h"], system_shared_libs: [], stl: "none", stubs: { versions: ["1", "2", "3"], }, } rust_binary { name: "foo.rust", srcs: ["foo.rs"], shared_libs: ["libfoo.shared_from_rust"], prefer_rlib: true, apex_available: ["myapex"], } cc_library_shared { name: "libfoo.shared_from_rust", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", stubs: { versions: ["10", "11", "12"], }, } `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that indirect stubs dep is not included ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so") ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so") // Ensure that we are using non-stub variants of mylib2 and libfoo.shared_from_rust (because // of the platform_apis: true) mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"] ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so") ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so") rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"] ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so") ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so") } func TestApexWithStubsWithMinSdkVersion(t *testing.T) { t.Parallel() ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib", "mylib3"], min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], shared_libs: ["mylib2", "mylib3"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], min_sdk_version: "28", } cc_library { name: "mylib2", srcs: ["mylib.cpp"], cflags: ["-include mylib.h"], system_shared_libs: [], stl: "none", stubs: { versions: ["28", "29", "30", "current"], }, min_sdk_version: "28", } cc_library { name: "mylib3", srcs: ["mylib.cpp"], shared_libs: ["mylib4"], system_shared_libs: [], stl: "none", stubs: { versions: ["28", "29", "30", "current"], }, apex_available: [ "myapex" ], min_sdk_version: "28", } cc_library { name: "mylib4", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], min_sdk_version: "28", } `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that direct non-stubs dep is always included ensureContains(t, copyCmds, "image.apex/lib64/mylib.so") // Ensure that indirect stubs dep is not included ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so") // Ensure that direct stubs dep is included ensureContains(t, copyCmds, "image.apex/lib64/mylib3.so") mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex29").Rule("ld").Args["libFlags"] // Ensure that mylib is linking with the latest version of stub for mylib2 ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so") // ... and not linking to the non-stub (impl) variant of mylib2 ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so") // Ensure that mylib is linking with the non-stub (impl) of mylib3 (because mylib3 is in the same apex) ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_apex29/mylib3.so") // .. and not linking to the stubs variant of mylib3 ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_29/mylib3.so") // Ensure that stubs libs are built without -include flags mylib2Cflags := ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("cc").Args["cFlags"] ensureNotContains(t, mylib2Cflags, "-include ") // Ensure that genstub is invoked with --systemapi ensureContains(t, ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_shared_29").Rule("genStubSrc").Args["flags"], "--systemapi") ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "lib64/mylib.so", "lib64/mylib3.so", "lib64/mylib4.so", }) } func TestApex_PlatformUsesLatestStubFromApex(t *testing.T) { t.Parallel() // myapex (Z) // mylib -----------------. // | // otherapex (29) | // libstub's versions: 29 Z current // | // | // libplatform ----------------' ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], min_sdk_version: "Z", // non-final } cc_library { name: "mylib", srcs: ["mylib.cpp"], shared_libs: ["libstub"], apex_available: ["myapex"], min_sdk_version: "Z", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } apex { name: "otherapex", key: "myapex.key", native_shared_libs: ["libstub"], min_sdk_version: "29", } cc_library { name: "libstub", srcs: ["mylib.cpp"], stubs: { versions: ["29", "Z", "current"], }, apex_available: ["otherapex"], min_sdk_version: "29", } // platform module depending on libstub from otherapex should use the latest stub("current") cc_library { name: "libplatform", srcs: ["mylib.cpp"], shared_libs: ["libstub"], } `, android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.Platform_sdk_codename = proptools.StringPtr("Z") variables.Platform_sdk_final = proptools.BoolPtr(false) variables.Platform_version_active_codenames = []string{"Z"} }), ) // Ensure that mylib from myapex is built against the latest stub (current) mylibCflags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"] ensureContains(t, mylibCflags, "-D__LIBSTUB_API__=10000 ") mylibLdflags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"] ensureContains(t, mylibLdflags, "libstub/android_arm64_armv8-a_shared_current/libstub.so ") // Ensure that libplatform is built against latest stub ("current") of mylib3 from the apex libplatformCflags := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"] ensureContains(t, libplatformCflags, "-D__LIBSTUB_API__=10000 ") // "current" maps to 10000 libplatformLdflags := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"] ensureContains(t, libplatformLdflags, "libstub/android_arm64_armv8-a_shared_current/libstub.so ") } func TestApexWithExplicitStubsDependency(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex2", key: "myapex2.key", native_shared_libs: ["mylib"], updatable: false, } apex_key { name: "myapex2.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], shared_libs: ["libfoo#10"], static_libs: ["libbaz"], system_shared_libs: [], stl: "none", apex_available: [ "myapex2" ], } cc_library { name: "libfoo", srcs: ["mylib.cpp"], shared_libs: ["libbar"], system_shared_libs: [], stl: "none", stubs: { versions: ["10", "20", "30"], }, } cc_library { name: "libbar", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", } cc_library_static { name: "libbaz", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex2" ], } `) apexRule := ctx.ModuleForTests("myapex2", "android_common_myapex2").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that direct non-stubs dep is always included ensureContains(t, copyCmds, "image.apex/lib64/mylib.so") // Ensure that indirect stubs dep is not included ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.so") // Ensure that dependency of stubs is not included ensureNotContains(t, copyCmds, "image.apex/lib64/libbar.so") mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"] // Ensure that mylib is linking with version 10 of libfoo ensureContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_shared_10/libfoo.so") // ... and not linking to the non-stub (impl) variant of libfoo ensureNotContains(t, mylibLdFlags, "libfoo/android_arm64_armv8-a_shared/libfoo.so") libFooStubsLdFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared_10").Rule("ld").Args["libFlags"] // Ensure that libfoo stubs is not linking to libbar (since it is a stubs) ensureNotContains(t, libFooStubsLdFlags, "libbar.so") fullDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx, ctx.ModuleForTests("myapex2", "android_common_myapex2").Output("depsinfo/fulllist.txt")), "\n") ensureListContains(t, fullDepsInfo, " libfoo(minSdkVersion:(no version)) (external) <- mylib") flatDepsInfo := strings.Split(android.ContentFromFileRuleForTests(t, ctx, ctx.ModuleForTests("myapex2", "android_common_myapex2").Output("depsinfo/flatlist.txt")), "\n") ensureListContains(t, flatDepsInfo, "libfoo(minSdkVersion:(no version)) (external)") } func TestApexWithRuntimeLibsDependency(t *testing.T) { /* myapex | v (runtime_libs) mylib ------+------> libfoo [provides stub] | `------> libbar */ ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], static_libs: ["libstatic"], shared_libs: ["libshared"], runtime_libs: ["libfoo", "libbar"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } cc_library { name: "libfoo", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", stubs: { versions: ["10", "20", "30"], }, } cc_library { name: "libbar", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } cc_library { name: "libstatic", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], runtime_libs: ["libstatic_to_runtime"], } cc_library { name: "libshared", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], runtime_libs: ["libshared_to_runtime"], } cc_library { name: "libstatic_to_runtime", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } cc_library { name: "libshared_to_runtime", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that direct non-stubs dep is always included ensureContains(t, copyCmds, "image.apex/lib64/mylib.so") // Ensure that indirect stubs dep is not included ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.so") // Ensure that runtime_libs dep in included ensureContains(t, copyCmds, "image.apex/lib64/libbar.so") ensureContains(t, copyCmds, "image.apex/lib64/libshared.so") ensureContains(t, copyCmds, "image.apex/lib64/libshared_to_runtime.so") ensureNotContains(t, copyCmds, "image.apex/lib64/libstatic_to_runtime.so") apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule") ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"])) ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.so") } var prepareForTestOfRuntimeApexWithHwasan = android.GroupFixturePreparers( cc.PrepareForTestWithCcBuildComponents, PrepareForTestWithApexBuildComponents, android.FixtureAddTextFile("bionic/apex/Android.bp", ` apex { name: "com.android.runtime", key: "com.android.runtime.key", native_shared_libs: ["libc"], updatable: false, } apex_key { name: "com.android.runtime.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `), android.FixtureAddFile("system/sepolicy/apex/com.android.runtime-file_contexts", nil), ) func TestRuntimeApexShouldInstallHwasanIfLibcDependsOnIt(t *testing.T) { result := android.GroupFixturePreparers(prepareForTestOfRuntimeApexWithHwasan).RunTestWithBp(t, ` cc_library { name: "libc", no_libcrt: true, nocrt: true, no_crt_pad_segment: true, stl: "none", system_shared_libs: [], stubs: { versions: ["1"] }, apex_available: ["com.android.runtime"], sanitize: { hwaddress: true, } } cc_prebuilt_library_shared { name: "libclang_rt.hwasan", no_libcrt: true, nocrt: true, no_crt_pad_segment: true, stl: "none", system_shared_libs: [], srcs: [""], stubs: { versions: ["1"] }, stem: "libclang_rt.hwasan-aarch64-android", sanitize: { never: true, }, apex_available: [ "//apex_available:anyapex", "//apex_available:platform", ], } `) ctx := result.TestContext ensureExactContents(t, ctx, "com.android.runtime", "android_common_hwasan_com.android.runtime", []string{ "lib64/bionic/libc.so", "lib64/bionic/libclang_rt.hwasan-aarch64-android.so", }) hwasan := ctx.ModuleForTests("libclang_rt.hwasan", "android_arm64_armv8-a_shared") installed := hwasan.Description("install libclang_rt.hwasan") ensureContains(t, installed.Output.String(), "/system/lib64/bootstrap/libclang_rt.hwasan-aarch64-android.so") symlink := hwasan.Description("install symlink libclang_rt.hwasan") ensureEquals(t, symlink.Args["fromPath"], "/apex/com.android.runtime/lib64/bionic/libclang_rt.hwasan-aarch64-android.so") ensureContains(t, symlink.Output.String(), "/system/lib64/libclang_rt.hwasan-aarch64-android.so") } func TestRuntimeApexShouldInstallHwasanIfHwaddressSanitized(t *testing.T) { result := android.GroupFixturePreparers( prepareForTestOfRuntimeApexWithHwasan, android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.SanitizeDevice = []string{"hwaddress"} }), ).RunTestWithBp(t, ` cc_library { name: "libc", no_libcrt: true, nocrt: true, no_crt_pad_segment: true, stl: "none", system_shared_libs: [], stubs: { versions: ["1"] }, apex_available: ["com.android.runtime"], } cc_prebuilt_library_shared { name: "libclang_rt.hwasan", no_libcrt: true, nocrt: true, no_crt_pad_segment: true, stl: "none", system_shared_libs: [], srcs: [""], stubs: { versions: ["1"] }, stem: "libclang_rt.hwasan-aarch64-android", sanitize: { never: true, }, apex_available: [ "//apex_available:anyapex", "//apex_available:platform", ], } `) ctx := result.TestContext ensureExactContents(t, ctx, "com.android.runtime", "android_common_hwasan_com.android.runtime", []string{ "lib64/bionic/libc.so", "lib64/bionic/libclang_rt.hwasan-aarch64-android.so", }) hwasan := ctx.ModuleForTests("libclang_rt.hwasan", "android_arm64_armv8-a_shared") installed := hwasan.Description("install libclang_rt.hwasan") ensureContains(t, installed.Output.String(), "/system/lib64/bootstrap/libclang_rt.hwasan-aarch64-android.so") symlink := hwasan.Description("install symlink libclang_rt.hwasan") ensureEquals(t, symlink.Args["fromPath"], "/apex/com.android.runtime/lib64/bionic/libclang_rt.hwasan-aarch64-android.so") ensureContains(t, symlink.Output.String(), "/system/lib64/libclang_rt.hwasan-aarch64-android.so") } func TestApexDependsOnLLNDKTransitively(t *testing.T) { testcases := []struct { name string minSdkVersion string apexVariant string shouldLink string shouldNotLink []string }{ { name: "unspecified version links to the latest", minSdkVersion: "", apexVariant: "apex10000", shouldLink: "current", shouldNotLink: []string{"29", "30"}, }, { name: "always use the latest", minSdkVersion: "min_sdk_version: \"29\",", apexVariant: "apex29", shouldLink: "current", shouldNotLink: []string{"29", "30"}, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], updatable: false, `+tc.minSdkVersion+` } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], vendor_available: true, shared_libs: ["libbar"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], min_sdk_version: "29", } cc_library { name: "libbar", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", stubs: { versions: ["29","30"] }, llndk: { symbol_file: "libbar.map.txt", } } `, withUnbundledBuild, ) // Ensure that LLNDK dep is not included ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "lib64/mylib.so", }) // Ensure that LLNDK dep is required apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule") ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"])) ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libbar.so") mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_"+tc.apexVariant).Rule("ld").Args["libFlags"] ensureContains(t, mylibLdFlags, "libbar/android_arm64_armv8-a_shared_"+tc.shouldLink+"/libbar.so") for _, ver := range tc.shouldNotLink { ensureNotContains(t, mylibLdFlags, "libbar/android_arm64_armv8-a_shared_"+ver+"/libbar.so") } mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_"+tc.apexVariant).Rule("cc").Args["cFlags"] ver := tc.shouldLink if tc.shouldLink == "current" { ver = strconv.Itoa(android.FutureApiLevelInt) } ensureContains(t, mylibCFlags, "__LIBBAR_API__="+ver) }) } } func TestApexWithSystemLibsStubs(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib", "mylib_shared", "libdl", "libm"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], system_shared_libs: ["libc", "libm"], shared_libs: ["libdl#27"], stl: "none", apex_available: [ "myapex" ], } cc_library_shared { name: "mylib_shared", srcs: ["mylib.cpp"], shared_libs: ["libdl#27"], stl: "none", apex_available: [ "myapex" ], } cc_library { name: "libBootstrap", srcs: ["mylib.cpp"], stl: "none", bootstrap: true, } `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that mylib, libm, libdl are included. ensureContains(t, copyCmds, "image.apex/lib64/mylib.so") ensureContains(t, copyCmds, "image.apex/lib64/bionic/libm.so") ensureContains(t, copyCmds, "image.apex/lib64/bionic/libdl.so") // Ensure that libc is not included (since it has stubs and not listed in native_shared_libs) ensureNotContains(t, copyCmds, "image.apex/lib64/bionic/libc.so") mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"] mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"] mylibSharedCFlags := ctx.ModuleForTests("mylib_shared", "android_arm64_armv8-a_shared_apex10000").Rule("cc").Args["cFlags"] // For dependency to libc // Ensure that mylib is linking with the latest version of stubs ensureContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared_current/libc.so") // ... and not linking to the non-stub (impl) variant ensureNotContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared/libc.so") // ... Cflags from stub is correctly exported to mylib ensureContains(t, mylibCFlags, "__LIBC_API__=10000") ensureContains(t, mylibSharedCFlags, "__LIBC_API__=10000") // For dependency to libm // Ensure that mylib is linking with the non-stub (impl) variant ensureContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_shared_apex10000/libm.so") // ... and not linking to the stub variant ensureNotContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_shared_29/libm.so") // ... and is not compiling with the stub ensureNotContains(t, mylibCFlags, "__LIBM_API__=29") ensureNotContains(t, mylibSharedCFlags, "__LIBM_API__=29") // For dependency to libdl // Ensure that mylib is linking with the specified version of stubs ensureContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_27/libdl.so") // ... and not linking to the other versions of stubs ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_28/libdl.so") ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_29/libdl.so") // ... and not linking to the non-stub (impl) variant ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_apex10000/libdl.so") // ... Cflags from stub is correctly exported to mylib ensureContains(t, mylibCFlags, "__LIBDL_API__=27") ensureContains(t, mylibSharedCFlags, "__LIBDL_API__=27") // Ensure that libBootstrap is depending on the platform variant of bionic libs libFlags := ctx.ModuleForTests("libBootstrap", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"] ensureContains(t, libFlags, "libc/android_arm64_armv8-a_shared/libc.so") ensureContains(t, libFlags, "libm/android_arm64_armv8-a_shared/libm.so") ensureContains(t, libFlags, "libdl/android_arm64_armv8-a_shared/libdl.so") } func TestApexMinSdkVersion_NativeModulesShouldBeBuiltAgainstStubs(t *testing.T) { // there are three links between liba --> libz. // 1) myapex -> libx -> liba -> libz : this should be #30 link // 2) otherapex -> liby -> liba -> libz : this should be #30 link // 3) (platform) -> liba -> libz : this should be non-stub link ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libx"], min_sdk_version: "29", } apex { name: "otherapex", key: "myapex.key", native_shared_libs: ["liby"], min_sdk_version: "30", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libx", shared_libs: ["liba"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], min_sdk_version: "29", } cc_library { name: "liby", shared_libs: ["liba"], system_shared_libs: [], stl: "none", apex_available: [ "otherapex" ], min_sdk_version: "29", } cc_library { name: "liba", shared_libs: ["libz"], system_shared_libs: [], stl: "none", apex_available: [ "//apex_available:anyapex", "//apex_available:platform", ], min_sdk_version: "29", } cc_library { name: "libz", system_shared_libs: [], stl: "none", stubs: { versions: ["28", "30"], }, } `) expectLink := func(from, from_variant, to, to_variant string) { ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"] ensureContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so") } expectNoLink := func(from, from_variant, to, to_variant string) { ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"] ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so") } // platform liba is linked to non-stub version expectLink("liba", "shared", "libz", "shared") // liba in myapex is linked to current expectLink("liba", "shared_apex29", "libz", "shared_current") expectNoLink("liba", "shared_apex29", "libz", "shared_30") expectNoLink("liba", "shared_apex29", "libz", "shared_28") expectNoLink("liba", "shared_apex29", "libz", "shared") // liba in otherapex is linked to current expectLink("liba", "shared_apex30", "libz", "shared_current") expectNoLink("liba", "shared_apex30", "libz", "shared_30") expectNoLink("liba", "shared_apex30", "libz", "shared_28") expectNoLink("liba", "shared_apex30", "libz", "shared") } func TestApexMinSdkVersion_SupportsCodeNames(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libx"], min_sdk_version: "R", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libx", shared_libs: ["libz"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], min_sdk_version: "R", } cc_library { name: "libz", system_shared_libs: [], stl: "none", stubs: { versions: ["29", "R"], }, } `, android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.Platform_version_active_codenames = []string{"R"} }), ) expectLink := func(from, from_variant, to, to_variant string) { ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"] ensureContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so") } expectNoLink := func(from, from_variant, to, to_variant string) { ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"] ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so") } expectLink("libx", "shared_apex10000", "libz", "shared_current") expectNoLink("libx", "shared_apex10000", "libz", "shared_R") expectNoLink("libx", "shared_apex10000", "libz", "shared_29") expectNoLink("libx", "shared_apex10000", "libz", "shared") } func TestApexMinSdkVersion_SupportsCodeNames_JavaLibs(t *testing.T) { testApex(t, ` apex { name: "myapex", key: "myapex.key", java_libs: ["libx"], min_sdk_version: "S", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } java_library { name: "libx", srcs: ["a.java"], apex_available: [ "myapex" ], sdk_version: "current", min_sdk_version: "S", // should be okay } `, android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.Platform_version_active_codenames = []string{"S"} variables.Platform_sdk_codename = proptools.StringPtr("S") }), ) } func TestApexMinSdkVersion_DefaultsToLatest(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libx"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libx", shared_libs: ["libz"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } cc_library { name: "libz", system_shared_libs: [], stl: "none", stubs: { versions: ["1", "2"], }, } `) expectLink := func(from, from_variant, to, to_variant string) { ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"] ensureContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so") } expectNoLink := func(from, from_variant, to, to_variant string) { ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"] ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so") } expectLink("libx", "shared_apex10000", "libz", "shared_current") expectNoLink("libx", "shared_apex10000", "libz", "shared_1") expectNoLink("libx", "shared_apex10000", "libz", "shared_2") expectNoLink("libx", "shared_apex10000", "libz", "shared") } func TestApexMinSdkVersion_InVendorApex(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], updatable: true, vendor: true, min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], vendor_available: true, min_sdk_version: "29", shared_libs: ["libbar"], } cc_library { name: "libbar", stubs: { versions: ["29", "30"] }, llndk: { symbol_file: "libbar.map.txt" }, } `) vendorVariant := "android_vendor_arm64_armv8-a" mylib := ctx.ModuleForTests("mylib", vendorVariant+"_shared_apex29") // Ensure that mylib links with "current" LLNDK libFlags := names(mylib.Rule("ld").Args["libFlags"]) ensureListContains(t, libFlags, "out/soong/.intermediates/libbar/"+vendorVariant+"_shared/libbar.so") // Ensure that mylib is targeting 29 ccRule := ctx.ModuleForTests("mylib", vendorVariant+"_static_apex29").Output("obj/mylib.o") ensureContains(t, ccRule.Args["cFlags"], "-target aarch64-linux-android29") // Ensure that the correct variant of crtbegin_so is used. crtBegin := mylib.Rule("ld").Args["crtBegin"] ensureContains(t, crtBegin, "out/soong/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"crtbegin_so/"+vendorVariant+"_apex29/crtbegin_so.o") // Ensure that the crtbegin_so used by the APEX is targeting 29 cflags := ctx.ModuleForTests("crtbegin_so", vendorVariant+"_apex29").Rule("cc").Args["cFlags"] android.AssertStringDoesContain(t, "cflags", cflags, "-target aarch64-linux-android29") } func TestTrackAllowedDeps(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", updatable: true, native_shared_libs: [ "mylib", "yourlib", ], min_sdk_version: "29", } apex { name: "myapex2", key: "myapex.key", updatable: false, native_shared_libs: ["yourlib"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], shared_libs: ["libbar"], min_sdk_version: "29", apex_available: ["myapex"], } cc_library { name: "libbar", stubs: { versions: ["29", "30"] }, } cc_library { name: "yourlib", srcs: ["mylib.cpp"], min_sdk_version: "29", apex_available: ["myapex", "myapex2", "//apex_available:platform"], } `, withFiles(android.MockFS{ "packages/modules/common/build/allowed_deps.txt": nil, })) depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton") inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings() android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs, "out/soong/.intermediates/myapex/android_common_myapex/depsinfo/flatlist.txt") android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs, "out/soong/.intermediates/myapex2/android_common_myapex2/depsinfo/flatlist.txt") myapex := ctx.ModuleForTests("myapex", "android_common_myapex") flatlist := strings.Split(android.ContentFromFileRuleForTests(t, ctx, myapex.Output("depsinfo/flatlist.txt")), "\n") android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep", flatlist, "libbar(minSdkVersion:(no version)) (external)") android.AssertStringListDoesNotContain(t, "do not track if not available for platform", flatlist, "mylib:(minSdkVersion:29)") android.AssertStringListContains(t, "track platform-available lib", flatlist, "yourlib(minSdkVersion:29)") } func TestTrackAllowedDeps_SkipWithoutAllowedDepsTxt(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", updatable: true, min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `) depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton") if nil != depsinfo.MaybeRule("generateApexDepsInfoFilesRule").Output { t.Error("apex_depsinfo_singleton shouldn't run when allowed_deps.txt doesn't exist") } } func TestPlatformUsesLatestStubsFromApexes(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libx"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libx", system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], stubs: { versions: ["1", "2"], }, } cc_library { name: "libz", shared_libs: ["libx"], system_shared_libs: [], stl: "none", } `) expectLink := func(from, from_variant, to, to_variant string) { t.Helper() ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"] ensureContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so") } expectNoLink := func(from, from_variant, to, to_variant string) { t.Helper() ldArgs := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld").Args["libFlags"] ensureNotContains(t, ldArgs, "android_arm64_armv8-a_"+to_variant+"/"+to+".so") } expectLink("libz", "shared", "libx", "shared_current") expectNoLink("libz", "shared", "libx", "shared_2") expectNoLink("libz", "shared", "libz", "shared_1") expectNoLink("libz", "shared", "libz", "shared") } var prepareForTestWithSantitizeHwaddress = android.FixtureModifyProductVariables( func(variables android.FixtureProductVariables) { variables.SanitizeDevice = []string{"hwaddress"} }, ) func TestQApexesUseLatestStubsInBundledBuildsAndHWASAN(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libx"], min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libx", shared_libs: ["libbar"], apex_available: [ "myapex" ], min_sdk_version: "29", } cc_library { name: "libbar", stubs: { versions: ["29", "30"], }, } `, prepareForTestWithSantitizeHwaddress, ) expectLink := func(from, from_variant, to, to_variant string) { ld := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld") libFlags := ld.Args["libFlags"] ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so") } expectLink("libx", "shared_hwasan_apex29", "libbar", "shared_current") } func TestQTargetApexUsesStaticUnwinder(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libx"], min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libx", apex_available: [ "myapex" ], min_sdk_version: "29", } `) // ensure apex variant of c++ is linked with static unwinder cm := ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared_apex29").Module().(*cc.Module) ensureListContains(t, cm.Properties.AndroidMkStaticLibs, "libunwind") // note that platform variant is not. cm = ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared").Module().(*cc.Module) ensureListNotContains(t, cm.Properties.AndroidMkStaticLibs, "libunwind") } func TestApexMinSdkVersion_ErrorIfIncompatibleVersion(t *testing.T) { testApexError(t, `module "mylib".*: should support min_sdk_version\(29\)`, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex", ], min_sdk_version: "30", } `) testApexError(t, `module "libfoo.ffi".*: should support min_sdk_version\(29\)`, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo.ffi"], min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } rust_ffi_shared { name: "libfoo.ffi", srcs: ["foo.rs"], crate_name: "foo", apex_available: [ "myapex", ], min_sdk_version: "30", } `) testApexError(t, `module "libfoo".*: should support min_sdk_version\(29\)`, ` apex { name: "myapex", key: "myapex.key", java_libs: ["libfoo"], min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } java_import { name: "libfoo", jars: ["libfoo.jar"], apex_available: [ "myapex", ], min_sdk_version: "30", } `) // Skip check for modules compiling against core API surface testApex(t, ` apex { name: "myapex", key: "myapex.key", java_libs: ["libfoo"], min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } java_library { name: "libfoo", srcs: ["Foo.java"], apex_available: [ "myapex", ], // Compile against core API surface sdk_version: "core_current", min_sdk_version: "30", } `) } func TestApexMinSdkVersion_Okay(t *testing.T) { testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], java_libs: ["libbar"], min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libfoo", srcs: ["mylib.cpp"], shared_libs: ["libfoo_dep"], apex_available: ["myapex"], min_sdk_version: "29", } cc_library { name: "libfoo_dep", srcs: ["mylib.cpp"], apex_available: ["myapex"], min_sdk_version: "29", } java_library { name: "libbar", sdk_version: "current", srcs: ["a.java"], static_libs: [ "libbar_dep", "libbar_import_dep", ], apex_available: ["myapex"], min_sdk_version: "29", } java_library { name: "libbar_dep", sdk_version: "current", srcs: ["a.java"], apex_available: ["myapex"], min_sdk_version: "29", } java_import { name: "libbar_import_dep", jars: ["libbar.jar"], apex_available: ["myapex"], min_sdk_version: "29", } `) } func TestApexMinSdkVersion_MinApiForArch(t *testing.T) { // Tests that an apex dependency with min_sdk_version higher than the // min_sdk_version of the apex is allowed as long as the dependency's // min_sdk_version is less than or equal to the api level that the // architecture was introduced in. In this case, arm64 didn't exist // until api level 21, so the arm64 code will never need to run on // an api level 20 device, even if other architectures of the apex // will. testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], min_sdk_version: "20", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libfoo", srcs: ["mylib.cpp"], apex_available: ["myapex"], min_sdk_version: "21", stl: "none", } `) } func TestJavaStableSdkVersion(t *testing.T) { testCases := []struct { name string expectedError string bp string preparer android.FixturePreparer }{ { name: "Non-updatable apex with non-stable dep", bp: ` apex { name: "myapex", java_libs: ["myjar"], key: "myapex.key", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } java_library { name: "myjar", srcs: ["foo/bar/MyClass.java"], sdk_version: "test_current", apex_available: ["myapex"], } `, }, { name: "Updatable apex with stable dep", bp: ` apex { name: "myapex", java_libs: ["myjar"], key: "myapex.key", updatable: true, min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } java_library { name: "myjar", srcs: ["foo/bar/MyClass.java"], sdk_version: "current", apex_available: ["myapex"], min_sdk_version: "29", } `, }, { name: "Updatable apex with non-stable dep", expectedError: "cannot depend on \"myjar\"", bp: ` apex { name: "myapex", java_libs: ["myjar"], key: "myapex.key", updatable: true, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } java_library { name: "myjar", srcs: ["foo/bar/MyClass.java"], sdk_version: "test_current", apex_available: ["myapex"], } `, }, { name: "Updatable apex with non-stable legacy core platform dep", expectedError: `\Qcannot depend on "myjar-uses-legacy": non stable SDK core_platform_current - uses legacy core platform\E`, bp: ` apex { name: "myapex", java_libs: ["myjar-uses-legacy"], key: "myapex.key", updatable: true, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } java_library { name: "myjar-uses-legacy", srcs: ["foo/bar/MyClass.java"], sdk_version: "core_platform", apex_available: ["myapex"], } `, preparer: java.FixtureUseLegacyCorePlatformApi("myjar-uses-legacy"), }, { name: "Updatable apex with non-stable transitive dep", // This is not actually detecting that the transitive dependency is unstable, rather it is // detecting that the transitive dependency is building against a wider API surface than the // module that depends on it is using. expectedError: "compiles against Android API, but dependency \"transitive-jar\" is compiling against private API.", bp: ` apex { name: "myapex", java_libs: ["myjar"], key: "myapex.key", updatable: true, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } java_library { name: "myjar", srcs: ["foo/bar/MyClass.java"], sdk_version: "current", apex_available: ["myapex"], static_libs: ["transitive-jar"], } java_library { name: "transitive-jar", srcs: ["foo/bar/MyClass.java"], sdk_version: "core_platform", apex_available: ["myapex"], } `, }, } for _, test := range testCases { if test.name != "Updatable apex with non-stable legacy core platform dep" { continue } t.Run(test.name, func(t *testing.T) { errorHandler := android.FixtureExpectsNoErrors if test.expectedError != "" { errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError) } android.GroupFixturePreparers( java.PrepareForTestWithJavaDefaultModules, PrepareForTestWithApexBuildComponents, prepareForTestWithMyapex, android.OptionalFixturePreparer(test.preparer), ). ExtendWithErrorHandler(errorHandler). RunTestWithBp(t, test.bp) }) } } func TestApexMinSdkVersion_ErrorIfDepIsNewer(t *testing.T) { testApexError(t, `module "mylib2".*: should support min_sdk_version\(29\) for "myapex"`, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], shared_libs: ["mylib2"], system_shared_libs: [], stl: "none", apex_available: [ "myapex", ], min_sdk_version: "29", } // indirect part of the apex cc_library { name: "mylib2", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex", ], min_sdk_version: "30", } `) } func TestApexMinSdkVersion_ErrorIfDepIsNewer_Java(t *testing.T) { testApexError(t, `module "bar".*: should support min_sdk_version\(29\) for "myapex"`, ` apex { name: "myapex", key: "myapex.key", apps: ["AppFoo"], min_sdk_version: "29", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_app { name: "AppFoo", srcs: ["foo/bar/MyClass.java"], sdk_version: "current", min_sdk_version: "29", system_modules: "none", stl: "none", static_libs: ["bar"], apex_available: [ "myapex" ], } java_library { name: "bar", sdk_version: "current", srcs: ["a.java"], apex_available: [ "myapex" ], } `) } func TestApexMinSdkVersion_OkayEvenWhenDepIsNewer_IfItSatisfiesApexMinSdkVersion(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } // mylib in myapex will link to mylib2#current // mylib in otherapex will link to mylib2(non-stub) in otherapex as well cc_library { name: "mylib", srcs: ["mylib.cpp"], shared_libs: ["mylib2"], system_shared_libs: [], stl: "none", apex_available: ["myapex", "otherapex"], min_sdk_version: "29", } cc_library { name: "mylib2", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: ["otherapex"], stubs: { versions: ["29", "30"] }, min_sdk_version: "30", } apex { name: "otherapex", key: "myapex.key", native_shared_libs: ["mylib", "mylib2"], min_sdk_version: "30", } `) expectLink := func(from, from_variant, to, to_variant string) { ld := ctx.ModuleForTests(from, "android_arm64_armv8-a_"+from_variant).Rule("ld") libFlags := ld.Args["libFlags"] ensureContains(t, libFlags, "android_arm64_armv8-a_"+to_variant+"/"+to+".so") } expectLink("mylib", "shared_apex29", "mylib2", "shared_current") expectLink("mylib", "shared_apex30", "mylib2", "shared_apex30") } func TestApexMinSdkVersion_WorksWithSdkCodename(t *testing.T) { withSAsActiveCodeNames := android.FixtureModifyProductVariables( func(variables android.FixtureProductVariables) { variables.Platform_sdk_codename = proptools.StringPtr("S") variables.Platform_version_active_codenames = []string{"S"} }, ) testApexError(t, `libbar.*: should support min_sdk_version\(S\)`, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], min_sdk_version: "S", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libfoo", shared_libs: ["libbar"], apex_available: ["myapex"], min_sdk_version: "29", } cc_library { name: "libbar", apex_available: ["myapex"], } `, withSAsActiveCodeNames) } func TestApexMinSdkVersion_WorksWithActiveCodenames(t *testing.T) { withSAsActiveCodeNames := android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.Platform_sdk_codename = proptools.StringPtr("S") variables.Platform_version_active_codenames = []string{"S", "T"} }) ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], min_sdk_version: "S", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libfoo", shared_libs: ["libbar"], apex_available: ["myapex"], min_sdk_version: "S", } cc_library { name: "libbar", stubs: { symbol_file: "libbar.map.txt", versions: ["30", "S", "T"], }, } `, withSAsActiveCodeNames) // ensure libfoo is linked with current version of libbar stub libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared_apex10000") libFlags := libfoo.Rule("ld").Args["libFlags"] ensureContains(t, libFlags, "android_arm64_armv8-a_shared_current/libbar.so") } func TestFilesInSubDir(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], binaries: ["mybin", "mybin.rust"], prebuilts: ["myetc"], compile_multilib: "both", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } prebuilt_etc { name: "myetc", src: "myprebuilt", sub_dir: "foo/bar", } cc_library { name: "mylib", srcs: ["mylib.cpp"], relative_install_path: "foo/bar", system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } cc_binary { name: "mybin", srcs: ["mylib.cpp"], relative_install_path: "foo/bar", system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } rust_binary { name: "mybin.rust", srcs: ["foo.rs"], relative_install_path: "rust_subdir", apex_available: [ "myapex" ], } `) generateFsRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("generateFsConfig") cmd := generateFsRule.RuleParams.Command // Ensure that the subdirectories are all listed ensureContains(t, cmd, "/etc ") ensureContains(t, cmd, "/etc/foo ") ensureContains(t, cmd, "/etc/foo/bar ") ensureContains(t, cmd, "/lib64 ") ensureContains(t, cmd, "/lib64/foo ") ensureContains(t, cmd, "/lib64/foo/bar ") ensureContains(t, cmd, "/lib ") ensureContains(t, cmd, "/lib/foo ") ensureContains(t, cmd, "/lib/foo/bar ") ensureContains(t, cmd, "/bin ") ensureContains(t, cmd, "/bin/foo ") ensureContains(t, cmd, "/bin/foo/bar ") ensureContains(t, cmd, "/bin/rust_subdir ") } func TestFilesInSubDirWhenNativeBridgeEnabled(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", multilib: { both: { native_shared_libs: ["mylib"], binaries: ["mybin"], }, }, compile_multilib: "both", native_bridge_supported: true, updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", relative_install_path: "foo/bar", system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], native_bridge_supported: true, } cc_binary { name: "mybin", relative_install_path: "foo/bar", system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], native_bridge_supported: true, compile_multilib: "both", // default is "first" for binary multilib: { lib64: { suffix: "64", }, }, } `, withNativeBridgeEnabled) ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "bin/foo/bar/mybin", "bin/foo/bar/mybin64", "bin/arm/foo/bar/mybin", "bin/arm64/foo/bar/mybin64", "lib/foo/bar/mylib.so", "lib/arm/foo/bar/mylib.so", "lib64/foo/bar/mylib.so", "lib64/arm64/foo/bar/mylib.so", }) } func TestVendorApex(t *testing.T) { result := android.GroupFixturePreparers( prepareForApexTest, android.FixtureModifyConfig(android.SetKatiEnabledForTests), ).RunTestWithBp(t, ` apex { name: "myapex", key: "myapex.key", binaries: ["mybin"], vendor: true, updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_binary { name: "mybin", vendor: true, shared_libs: ["libfoo"], } cc_library { name: "libfoo", proprietary: true, } `) ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex", []string{ "bin/mybin", "lib64/libfoo.so", // TODO(b/159195575): Add an option to use VNDK libs from VNDK APEX "lib64/libc++.so", }) apexBundle := result.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, result.TestContext, apexBundle) name := apexBundle.BaseModuleName() prefix := "TARGET_" var builder strings.Builder data.Custom(&builder, name, prefix, "", data) androidMk := android.StringRelativeToTop(result.Config, builder.String()) installPath := "out/target/product/test_device/vendor/apex" ensureContains(t, androidMk, "LOCAL_MODULE_PATH := "+installPath) apexManifestRule := result.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule") requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"]) ensureListNotContains(t, requireNativeLibs, ":vndk") } func TestProductVariant(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", updatable: false, product_specific: true, binaries: ["foo"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_binary { name: "foo", product_available: true, apex_available: ["myapex"], srcs: ["foo.cpp"], } `) cflags := strings.Fields( ctx.ModuleForTests("foo", "android_product_arm64_armv8-a_apex10000").Rule("cc").Args["cFlags"]) ensureListContains(t, cflags, "-D__ANDROID_VNDK__") ensureListContains(t, cflags, "-D__ANDROID_APEX__") ensureListContains(t, cflags, "-D__ANDROID_PRODUCT__") ensureListNotContains(t, cflags, "-D__ANDROID_VENDOR__") } func TestApex_withPrebuiltFirmware(t *testing.T) { testCases := []struct { name string additionalProp string }{ {"system apex with prebuilt_firmware", ""}, {"vendor apex with prebuilt_firmware", "vendor: true,"}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", prebuilts: ["myfirmware"], updatable: false, `+tc.additionalProp+` } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } prebuilt_firmware { name: "myfirmware", src: "myfirmware.bin", filename_from_src: true, `+tc.additionalProp+` } `) ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "etc/firmware/myfirmware.bin", }) }) } } func TestAndroidMk_VendorApexRequired(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", vendor: true, native_shared_libs: ["mylib"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", vendor_available: true, } `) apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, apexBundle) name := apexBundle.BaseModuleName() prefix := "TARGET_" var builder strings.Builder data.Custom(&builder, name, prefix, "", data) androidMk := builder.String() ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES := libc++.vendor.myapex:64 mylib.vendor.myapex:64 libc.vendor libm.vendor libdl.vendor\n") } func TestAndroidMkWritesCommonProperties(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", vintf_fragments: ["fragment.xml"], init_rc: ["init.rc"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_binary { name: "mybin", } `) apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, apexBundle) name := apexBundle.BaseModuleName() prefix := "TARGET_" var builder strings.Builder data.Custom(&builder, name, prefix, "", data) androidMk := builder.String() ensureContains(t, androidMk, "LOCAL_FULL_VINTF_FRAGMENTS := fragment.xml\n") ensureContains(t, androidMk, "LOCAL_FULL_INIT_RC := init.rc\n") } func TestStaticLinking(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", stubs: { versions: ["1", "2", "3"], }, apex_available: ["myapex"], } cc_binary { name: "not_in_apex", srcs: ["mylib.cpp"], static_libs: ["mylib"], static_executable: true, system_shared_libs: [], stl: "none", } `) ldFlags := ctx.ModuleForTests("not_in_apex", "android_arm64_armv8-a").Rule("ld").Args["libFlags"] // Ensure that not_in_apex is linking with the static variant of mylib ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_static/mylib.a") } func TestKeys(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex_keytest", key: "myapex.key", certificate: ":myapex.certificate", native_shared_libs: ["mylib"], file_contexts: ":myapex-file_contexts", updatable: false, } cc_library { name: "mylib", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex_keytest" ], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_app_certificate { name: "myapex.certificate", certificate: "testkey", } android_app_certificate { name: "myapex.certificate.override", certificate: "testkey.override", } `) // check the APEX keys keys := ctx.ModuleForTests("myapex.key", "android_common").Module().(*apexKey) if keys.publicKeyFile.String() != "vendor/foo/devkeys/testkey.avbpubkey" { t.Errorf("public key %q is not %q", keys.publicKeyFile.String(), "vendor/foo/devkeys/testkey.avbpubkey") } if keys.privateKeyFile.String() != "vendor/foo/devkeys/testkey.pem" { t.Errorf("private key %q is not %q", keys.privateKeyFile.String(), "vendor/foo/devkeys/testkey.pem") } // check the APK certs. It should be overridden to myapex.certificate.override certs := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest").Rule("signapk").Args["certificates"] if certs != "testkey.override.x509.pem testkey.override.pk8" { t.Errorf("cert and private key %q are not %q", certs, "testkey.override.509.pem testkey.override.pk8") } } func TestCertificate(t *testing.T) { t.Run("if unspecified, it defaults to DefaultAppCertificate", func(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", }`) rule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk") expected := "vendor/foo/devkeys/test.x509.pem vendor/foo/devkeys/test.pk8" if actual := rule.Args["certificates"]; actual != expected { t.Errorf("certificates should be %q, not %q", expected, actual) } }) t.Run("override when unspecified", func(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex_keytest", key: "myapex.key", file_contexts: ":myapex-file_contexts", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_app_certificate { name: "myapex.certificate.override", certificate: "testkey.override", }`) rule := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest").Rule("signapk") expected := "testkey.override.x509.pem testkey.override.pk8" if actual := rule.Args["certificates"]; actual != expected { t.Errorf("certificates should be %q, not %q", expected, actual) } }) t.Run("if specified as :module, it respects the prop", func(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", certificate: ":myapex.certificate", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_app_certificate { name: "myapex.certificate", certificate: "testkey", }`) rule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk") expected := "testkey.x509.pem testkey.pk8" if actual := rule.Args["certificates"]; actual != expected { t.Errorf("certificates should be %q, not %q", expected, actual) } }) t.Run("override when specifiec as <:module>", func(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex_keytest", key: "myapex.key", file_contexts: ":myapex-file_contexts", certificate: ":myapex.certificate", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_app_certificate { name: "myapex.certificate.override", certificate: "testkey.override", }`) rule := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest").Rule("signapk") expected := "testkey.override.x509.pem testkey.override.pk8" if actual := rule.Args["certificates"]; actual != expected { t.Errorf("certificates should be %q, not %q", expected, actual) } }) t.Run("if specified as name, finds it from DefaultDevKeyDir", func(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", certificate: "testkey", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", }`) rule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk") expected := "vendor/foo/devkeys/testkey.x509.pem vendor/foo/devkeys/testkey.pk8" if actual := rule.Args["certificates"]; actual != expected { t.Errorf("certificates should be %q, not %q", expected, actual) } }) t.Run("override when specified as ", func(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex_keytest", key: "myapex.key", file_contexts: ":myapex-file_contexts", certificate: "testkey", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_app_certificate { name: "myapex.certificate.override", certificate: "testkey.override", }`) rule := ctx.ModuleForTests("myapex_keytest", "android_common_myapex_keytest").Rule("signapk") expected := "testkey.override.x509.pem testkey.override.pk8" if actual := rule.Args["certificates"]; actual != expected { t.Errorf("certificates should be %q, not %q", expected, actual) } }) } func TestMacro(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib", "mylib2"], updatable: false, } apex { name: "otherapex", key: "myapex.key", native_shared_libs: ["mylib", "mylib2"], min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex", "otherapex", ], recovery_available: true, min_sdk_version: "29", } cc_library { name: "mylib2", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex", "otherapex", ], static_libs: ["mylib3"], recovery_available: true, min_sdk_version: "29", } cc_library { name: "mylib3", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex", "otherapex", ], recovery_available: true, min_sdk_version: "29", } `) // non-APEX variant does not have __ANDROID_APEX__ defined mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"] ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__") // APEX variant has __ANDROID_APEX__ and __ANDROID_APEX__ defined mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex10000").Rule("cc").Args["cFlags"] ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__") // APEX variant has __ANDROID_APEX__ and __ANDROID_APEX__ defined mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_apex29").Rule("cc").Args["cFlags"] ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__") // When a cc_library sets use_apex_name_macro: true each apex gets a unique variant and // each variant defines additional macros to distinguish which apex variant it is built for // non-APEX variant does not have __ANDROID_APEX__ defined mylibCFlags = ctx.ModuleForTests("mylib3", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"] ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__") // recovery variant does not set __ANDROID_APEX__ mylibCFlags = ctx.ModuleForTests("mylib3", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"] ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__") // non-APEX variant does not have __ANDROID_APEX__ defined mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"] ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__") // recovery variant does not set __ANDROID_APEX__ mylibCFlags = ctx.ModuleForTests("mylib2", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"] ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__") } func TestHeaderLibsDependency(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library_headers { name: "mylib_headers", export_include_dirs: ["my_include"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } cc_library { name: "mylib", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", header_libs: ["mylib_headers"], export_header_lib_headers: ["mylib_headers"], stubs: { versions: ["1", "2", "3"], }, apex_available: [ "myapex" ], } cc_library { name: "otherlib", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", shared_libs: ["mylib"], } `) cFlags := ctx.ModuleForTests("otherlib", "android_arm64_armv8-a_static").Rule("cc").Args["cFlags"] // Ensure that the include path of the header lib is exported to 'otherlib' ensureContains(t, cFlags, "-Imy_include") } type fileInApex struct { path string // path in apex src string // src path isLink bool } func (f fileInApex) String() string { return f.src + ":" + f.path } func (f fileInApex) match(expectation string) bool { parts := strings.Split(expectation, ":") if len(parts) == 1 { match, _ := path.Match(parts[0], f.path) return match } if len(parts) == 2 { matchSrc, _ := path.Match(parts[0], f.src) matchDst, _ := path.Match(parts[1], f.path) return matchSrc && matchDst } panic("invalid expected file specification: " + expectation) } func getFiles(t *testing.T, ctx *android.TestContext, moduleName, variant string) []fileInApex { t.Helper() module := ctx.ModuleForTests(moduleName, variant) apexRule := module.MaybeRule("apexRule") apexDir := "/image.apex/" copyCmds := apexRule.Args["copy_commands"] var ret []fileInApex for _, cmd := range strings.Split(copyCmds, "&&") { cmd = strings.TrimSpace(cmd) if cmd == "" { continue } terms := strings.Split(cmd, " ") var dst, src string var isLink bool switch terms[0] { case "mkdir": case "cp": if len(terms) != 3 && len(terms) != 4 { t.Fatal("copyCmds contains invalid cp command", cmd) } dst = terms[len(terms)-1] src = terms[len(terms)-2] isLink = false case "ln": if len(terms) != 3 && len(terms) != 4 { // ln LINK TARGET or ln -s LINK TARGET t.Fatal("copyCmds contains invalid ln command", cmd) } dst = terms[len(terms)-1] src = terms[len(terms)-2] isLink = true default: t.Fatalf("copyCmds should contain mkdir/cp commands only: %q", cmd) } if dst != "" { index := strings.Index(dst, apexDir) if index == -1 { t.Fatal("copyCmds should copy a file to "+apexDir, cmd) } dstFile := dst[index+len(apexDir):] ret = append(ret, fileInApex{path: dstFile, src: src, isLink: isLink}) } } return ret } func assertFileListEquals(t *testing.T, expectedFiles []string, actualFiles []fileInApex) { t.Helper() var failed bool var surplus []string filesMatched := make(map[string]bool) for _, file := range actualFiles { matchFound := false for _, expected := range expectedFiles { if file.match(expected) { matchFound = true filesMatched[expected] = true break } } if !matchFound { surplus = append(surplus, file.String()) } } if len(surplus) > 0 { sort.Strings(surplus) t.Log("surplus files", surplus) failed = true } if len(expectedFiles) > len(filesMatched) { var missing []string for _, expected := range expectedFiles { if !filesMatched[expected] { missing = append(missing, expected) } } sort.Strings(missing) t.Log("missing files", missing) failed = true } if failed { t.Fail() } } func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName, variant string, files []string) { assertFileListEquals(t, files, getFiles(t, ctx, moduleName, variant)) } func ensureExactDeapexedContents(t *testing.T, ctx *android.TestContext, moduleName string, variant string, files []string) { deapexer := ctx.ModuleForTests(moduleName+".deapexer", variant).Description("deapex") outputs := make([]string, 0, len(deapexer.ImplicitOutputs)+1) if deapexer.Output != nil { outputs = append(outputs, deapexer.Output.String()) } for _, output := range deapexer.ImplicitOutputs { outputs = append(outputs, output.String()) } actualFiles := make([]fileInApex, 0, len(outputs)) for _, output := range outputs { dir := "/deapexer/" pos := strings.LastIndex(output, dir) if pos == -1 { t.Fatal("Unknown deapexer output ", output) } path := output[pos+len(dir):] actualFiles = append(actualFiles, fileInApex{path: path, src: "", isLink: false}) } assertFileListEquals(t, files, actualFiles) } func vndkLibrariesTxtFiles(vers ...string) (result string) { for _, v := range vers { for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate", "vndkproduct"} { result += ` prebuilt_etc { name: "` + txt + `.libraries.` + v + `.txt", src: "dummy.txt", } ` } } return } func TestVndkApexVersion(t *testing.T) { ctx := testApex(t, ` apex_vndk { name: "com.android.vndk.v27", key: "myapex.key", file_contexts: ":myapex-file_contexts", vndk_version: "27", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } vndk_prebuilt_shared { name: "libvndk27", version: "27", vendor_available: true, product_available: true, vndk: { enabled: true, }, target_arch: "arm64", arch: { arm: { srcs: ["libvndk27_arm.so"], }, arm64: { srcs: ["libvndk27_arm64.so"], }, }, apex_available: [ "com.android.vndk.v27" ], } vndk_prebuilt_shared { name: "libvndk27", version: "27", vendor_available: true, product_available: true, vndk: { enabled: true, }, target_arch: "x86_64", arch: { x86: { srcs: ["libvndk27_x86.so"], }, x86_64: { srcs: ["libvndk27_x86_64.so"], }, }, } `+vndkLibrariesTxtFiles("27"), withFiles(map[string][]byte{ "libvndk27_arm.so": nil, "libvndk27_arm64.so": nil, "libvndk27_x86.so": nil, "libvndk27_x86_64.so": nil, })) ensureExactContents(t, ctx, "com.android.vndk.v27", "android_common", []string{ "lib/libvndk27_arm.so", "lib64/libvndk27_arm64.so", "etc/*", }) } func TestVndkApexNameRule(t *testing.T) { ctx := testApex(t, ` apex_vndk { name: "com.android.vndk.v29", key: "myapex.key", file_contexts: ":myapex-file_contexts", vndk_version: "29", updatable: false, } apex_vndk { name: "com.android.vndk.v28", key: "myapex.key", file_contexts: ":myapex-file_contexts", vndk_version: "28", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", }`+vndkLibrariesTxtFiles("28", "29")) assertApexName := func(expected, moduleName string) { module := ctx.ModuleForTests(moduleName, "android_common") apexManifestRule := module.Rule("apexManifestRule") ensureContains(t, apexManifestRule.Args["opt"], "-v name "+expected) } assertApexName("com.android.vndk.v29", "com.android.vndk.v29") assertApexName("com.android.vndk.v28", "com.android.vndk.v28") } func TestVndkApexDoesntSupportNativeBridgeSupported(t *testing.T) { testApexError(t, `module "com.android.vndk.current" .*: native_bridge_supported: .* doesn't support native bridge binary`, ` apex_vndk { name: "com.android.vndk.current", key: "com.android.vndk.current.key", file_contexts: ":myapex-file_contexts", native_bridge_supported: true, } apex_key { name: "com.android.vndk.current.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libvndk", srcs: ["mylib.cpp"], vendor_available: true, product_available: true, native_bridge_supported: true, host_supported: true, vndk: { enabled: true, }, system_shared_libs: [], stl: "none", } `) } func TestVndkApexWithBinder32(t *testing.T) { ctx := testApex(t, ` apex_vndk { name: "com.android.vndk.v27", key: "myapex.key", file_contexts: ":myapex-file_contexts", vndk_version: "27", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } vndk_prebuilt_shared { name: "libvndk27", version: "27", target_arch: "arm", vendor_available: true, product_available: true, vndk: { enabled: true, }, arch: { arm: { srcs: ["libvndk27.so"], } }, } vndk_prebuilt_shared { name: "libvndk27", version: "27", target_arch: "arm", binder32bit: true, vendor_available: true, product_available: true, vndk: { enabled: true, }, arch: { arm: { srcs: ["libvndk27binder32.so"], } }, apex_available: [ "com.android.vndk.v27" ], } `+vndkLibrariesTxtFiles("27"), withFiles(map[string][]byte{ "libvndk27.so": nil, "libvndk27binder32.so": nil, }), withBinder32bit, withTargets(map[android.OsType][]android.Target{ android.Android: { {Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""}, }, }), ) ensureExactContents(t, ctx, "com.android.vndk.v27", "android_common", []string{ "lib/libvndk27binder32.so", "etc/*", }) } func TestDependenciesInApexManifest(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex_nodep", key: "myapex.key", native_shared_libs: ["lib_nodep"], compile_multilib: "both", file_contexts: ":myapex-file_contexts", updatable: false, } apex { name: "myapex_dep", key: "myapex.key", native_shared_libs: ["lib_dep"], compile_multilib: "both", file_contexts: ":myapex-file_contexts", updatable: false, } apex { name: "myapex_provider", key: "myapex.key", native_shared_libs: ["libfoo"], compile_multilib: "both", file_contexts: ":myapex-file_contexts", updatable: false, } apex { name: "myapex_selfcontained", key: "myapex.key", native_shared_libs: ["lib_dep_on_bar", "libbar"], compile_multilib: "both", file_contexts: ":myapex-file_contexts", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "lib_nodep", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex_nodep" ], } cc_library { name: "lib_dep", srcs: ["mylib.cpp"], shared_libs: ["libfoo"], system_shared_libs: [], stl: "none", apex_available: [ "myapex_dep", "myapex_provider", "myapex_selfcontained", ], } cc_library { name: "lib_dep_on_bar", srcs: ["mylib.cpp"], shared_libs: ["libbar"], system_shared_libs: [], stl: "none", apex_available: [ "myapex_selfcontained", ], } cc_library { name: "libfoo", srcs: ["mytest.cpp"], stubs: { versions: ["1"], }, system_shared_libs: [], stl: "none", apex_available: [ "myapex_provider", ], } cc_library { name: "libbar", srcs: ["mytest.cpp"], stubs: { versions: ["1"], }, system_shared_libs: [], stl: "none", apex_available: [ "myapex_selfcontained", ], } `) var apexManifestRule android.TestingBuildParams var provideNativeLibs, requireNativeLibs []string apexManifestRule = ctx.ModuleForTests("myapex_nodep", "android_common_myapex_nodep").Rule("apexManifestRule") provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"]) requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"]) ensureListEmpty(t, provideNativeLibs) ensureListEmpty(t, requireNativeLibs) apexManifestRule = ctx.ModuleForTests("myapex_dep", "android_common_myapex_dep").Rule("apexManifestRule") provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"]) requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"]) ensureListEmpty(t, provideNativeLibs) ensureListContains(t, requireNativeLibs, "libfoo.so") apexManifestRule = ctx.ModuleForTests("myapex_provider", "android_common_myapex_provider").Rule("apexManifestRule") provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"]) requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"]) ensureListContains(t, provideNativeLibs, "libfoo.so") ensureListEmpty(t, requireNativeLibs) apexManifestRule = ctx.ModuleForTests("myapex_selfcontained", "android_common_myapex_selfcontained").Rule("apexManifestRule") provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"]) requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"]) ensureListContains(t, provideNativeLibs, "libbar.so") ensureListEmpty(t, requireNativeLibs) } func TestOverrideApexManifestDefaultVersion(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "//apex_available:platform", "myapex", ], } `, android.FixtureMergeEnv(map[string]string{ "OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION": "1234", })) module := ctx.ModuleForTests("myapex", "android_common_myapex") apexManifestRule := module.Rule("apexManifestRule") ensureContains(t, apexManifestRule.Args["default_version"], "1234") } func TestCompileMultilibProp(t *testing.T) { testCases := []struct { compileMultiLibProp string containedLibs []string notContainedLibs []string }{ { containedLibs: []string{ "image.apex/lib64/mylib.so", "image.apex/lib/mylib.so", }, compileMultiLibProp: `compile_multilib: "both",`, }, { containedLibs: []string{"image.apex/lib64/mylib.so"}, notContainedLibs: []string{"image.apex/lib/mylib.so"}, compileMultiLibProp: `compile_multilib: "first",`, }, { containedLibs: []string{"image.apex/lib64/mylib.so"}, notContainedLibs: []string{"image.apex/lib/mylib.so"}, // compile_multilib, when unset, should result to the same output as when compile_multilib is "first" }, { containedLibs: []string{"image.apex/lib64/mylib.so"}, notContainedLibs: []string{"image.apex/lib/mylib.so"}, compileMultiLibProp: `compile_multilib: "64",`, }, { containedLibs: []string{"image.apex/lib/mylib.so"}, notContainedLibs: []string{"image.apex/lib64/mylib.so"}, compileMultiLibProp: `compile_multilib: "32",`, }, } for _, testCase := range testCases { ctx := testApex(t, fmt.Sprintf(` apex { name: "myapex", key: "myapex.key", %s native_shared_libs: ["mylib"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], apex_available: [ "//apex_available:platform", "myapex", ], } `, testCase.compileMultiLibProp), ) module := ctx.ModuleForTests("myapex", "android_common_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] for _, containedLib := range testCase.containedLibs { ensureContains(t, copyCmds, containedLib) } for _, notContainedLib := range testCase.notContainedLibs { ensureNotContains(t, copyCmds, notContainedLib) } } } func TestNonTestApex(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib_common"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib_common", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "//apex_available:platform", "myapex", ], } `) module := ctx.ModuleForTests("myapex", "android_common_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] if apex, ok := module.Module().(*apexBundle); !ok || apex.testApex { t.Log("Apex was a test apex!") t.Fail() } // Ensure that main rule creates an output ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned") // Ensure that apex variant is created for the direct dep ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_shared_apex10000") // Ensure that both direct and indirect deps are copied into apex ensureContains(t, copyCmds, "image.apex/lib64/mylib_common.so") // Ensure that the platform variant ends with _shared ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_shared") if !ctx.ModuleForTests("mylib_common", "android_arm64_armv8-a_shared_apex10000").Module().(*cc.Module).InAnyApex() { t.Log("Found mylib_common not in any apex!") t.Fail() } } func TestTestApex(t *testing.T) { ctx := testApex(t, ` apex_test { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib_common_test"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib_common_test", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", // TODO: remove //apex_available:platform apex_available: [ "//apex_available:platform", "myapex", ], } `) module := ctx.ModuleForTests("myapex", "android_common_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] if apex, ok := module.Module().(*apexBundle); !ok || !apex.testApex { t.Log("Apex was not a test apex!") t.Fail() } // Ensure that main rule creates an output ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned") // Ensure that apex variant is created for the direct dep ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common_test"), "android_arm64_armv8-a_shared_apex10000") // Ensure that both direct and indirect deps are copied into apex ensureContains(t, copyCmds, "image.apex/lib64/mylib_common_test.so") // Ensure that the platform variant ends with _shared ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common_test"), "android_arm64_armv8-a_shared") } func TestLibzVendorIsntStable(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", updatable: false, binaries: ["mybin"], } apex { name: "myvendorapex", key: "myapex.key", file_contexts: "myvendorapex_file_contexts", vendor: true, updatable: false, binaries: ["mybin"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_binary { name: "mybin", vendor_available: true, system_shared_libs: [], stl: "none", shared_libs: ["libz"], apex_available: ["//apex_available:anyapex"], } cc_library { name: "libz", vendor_available: true, system_shared_libs: [], stl: "none", stubs: { versions: ["28", "30"], }, target: { vendor: { no_stubs: true, }, }, } `, withFiles(map[string][]byte{ "myvendorapex_file_contexts": nil, })) // libz provides stubs for core variant. { ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "bin/mybin", }) apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexManifestRule") android.AssertStringEquals(t, "should require libz", apexManifestRule.Args["requireNativeLibs"], "libz.so") } // libz doesn't provide stubs for vendor variant. { ensureExactContents(t, ctx, "myvendorapex", "android_common_myvendorapex", []string{ "bin/mybin", "lib64/libz.so", }) apexManifestRule := ctx.ModuleForTests("myvendorapex", "android_common_myvendorapex").Rule("apexManifestRule") android.AssertStringEquals(t, "should not require libz", apexManifestRule.Args["requireNativeLibs"], "") } } func TestApexWithTarget(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", updatable: false, multilib: { first: { native_shared_libs: ["mylib_common"], } }, target: { android: { multilib: { first: { native_shared_libs: ["mylib"], } } }, host: { multilib: { first: { native_shared_libs: ["mylib2"], } } } } } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", // TODO: remove //apex_available:platform apex_available: [ "//apex_available:platform", "myapex", ], } cc_library { name: "mylib_common", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", compile_multilib: "first", // TODO: remove //apex_available:platform apex_available: [ "//apex_available:platform", "myapex", ], } cc_library { name: "mylib2", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", compile_multilib: "first", } `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that main rule creates an output ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned") // Ensure that apex variant is created for the direct dep ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_apex10000") ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_shared_apex10000") ensureListNotContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_apex10000") // Ensure that both direct and indirect deps are copied into apex ensureContains(t, copyCmds, "image.apex/lib64/mylib.so") ensureContains(t, copyCmds, "image.apex/lib64/mylib_common.so") ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so") // Ensure that the platform variant ends with _shared ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared") ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_shared") ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared") } func TestApexWithArch(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", updatable: false, native_shared_libs: ["mylib.generic"], arch: { arm64: { native_shared_libs: ["mylib.arm64"], exclude_native_shared_libs: ["mylib.generic"], }, x86_64: { native_shared_libs: ["mylib.x64"], exclude_native_shared_libs: ["mylib.generic"], }, } } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib.generic", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", // TODO: remove //apex_available:platform apex_available: [ "//apex_available:platform", "myapex", ], } cc_library { name: "mylib.arm64", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", // TODO: remove //apex_available:platform apex_available: [ "//apex_available:platform", "myapex", ], } cc_library { name: "mylib.x64", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", // TODO: remove //apex_available:platform apex_available: [ "//apex_available:platform", "myapex", ], } `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that apex variant is created for the direct dep ensureListContains(t, ctx.ModuleVariantsForTests("mylib.arm64"), "android_arm64_armv8-a_shared_apex10000") ensureListNotContains(t, ctx.ModuleVariantsForTests("mylib.generic"), "android_arm64_armv8-a_shared_apex10000") ensureListNotContains(t, ctx.ModuleVariantsForTests("mylib.x64"), "android_arm64_armv8-a_shared_apex10000") // Ensure that both direct and indirect deps are copied into apex ensureContains(t, copyCmds, "image.apex/lib64/mylib.arm64.so") ensureNotContains(t, copyCmds, "image.apex/lib64/mylib.x64.so") } func TestApexWithShBinary(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", sh_binaries: ["myscript"], updatable: false, compile_multilib: "both", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } sh_binary { name: "myscript", src: "mylib.cpp", filename: "myscript.sh", sub_dir: "script", } `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] ensureContains(t, copyCmds, "image.apex/bin/script/myscript.sh") } func TestApexInVariousPartition(t *testing.T) { testcases := []struct { propName, partition string }{ {"", "system"}, {"product_specific: true", "product"}, {"soc_specific: true", "vendor"}, {"proprietary: true", "vendor"}, {"vendor: true", "vendor"}, {"system_ext_specific: true", "system_ext"}, } for _, tc := range testcases { t.Run(tc.propName+":"+tc.partition, func(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", updatable: false, `+tc.propName+` } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `) apex := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) expected := "out/soong/target/product/test_device/" + tc.partition + "/apex" actual := apex.installDir.RelativeToTop().String() if actual != expected { t.Errorf("wrong install path. expected %q. actual %q", expected, actual) } }) } } func TestFileContexts_FindInDefaultLocationIfNotSet(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `) module := ctx.ModuleForTests("myapex", "android_common_myapex") rule := module.Output("file_contexts") ensureContains(t, rule.RuleParams.Command, "cat system/sepolicy/apex/myapex-file_contexts") } func TestFileContexts_ShouldBeUnderSystemSepolicyForSystemApexes(t *testing.T) { testApexError(t, `"myapex" .*: file_contexts: should be under system/sepolicy`, ` apex { name: "myapex", key: "myapex.key", file_contexts: "my_own_file_contexts", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `, withFiles(map[string][]byte{ "my_own_file_contexts": nil, })) } func TestFileContexts_ProductSpecificApexes(t *testing.T) { testApexError(t, `"myapex" .*: file_contexts: cannot find`, ` apex { name: "myapex", key: "myapex.key", product_specific: true, file_contexts: "product_specific_file_contexts", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `) ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", product_specific: true, file_contexts: "product_specific_file_contexts", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `, withFiles(map[string][]byte{ "product_specific_file_contexts": nil, })) module := ctx.ModuleForTests("myapex", "android_common_myapex") rule := module.Output("file_contexts") ensureContains(t, rule.RuleParams.Command, "cat product_specific_file_contexts") } func TestFileContexts_SetViaFileGroup(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", product_specific: true, file_contexts: ":my-file-contexts", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } filegroup { name: "my-file-contexts", srcs: ["product_specific_file_contexts"], } `, withFiles(map[string][]byte{ "product_specific_file_contexts": nil, })) module := ctx.ModuleForTests("myapex", "android_common_myapex") rule := module.Output("file_contexts") ensureContains(t, rule.RuleParams.Command, "cat product_specific_file_contexts") } func TestApexKeyFromOtherModule(t *testing.T) { ctx := testApex(t, ` apex_key { name: "myapex.key", public_key: ":my.avbpubkey", private_key: ":my.pem", product_specific: true, } filegroup { name: "my.avbpubkey", srcs: ["testkey2.avbpubkey"], } filegroup { name: "my.pem", srcs: ["testkey2.pem"], } `) apex_key := ctx.ModuleForTests("myapex.key", "android_common").Module().(*apexKey) expected_pubkey := "testkey2.avbpubkey" actual_pubkey := apex_key.publicKeyFile.String() if actual_pubkey != expected_pubkey { t.Errorf("wrong public key path. expected %q. actual %q", expected_pubkey, actual_pubkey) } expected_privkey := "testkey2.pem" actual_privkey := apex_key.privateKeyFile.String() if actual_privkey != expected_privkey { t.Errorf("wrong private key path. expected %q. actual %q", expected_privkey, actual_privkey) } } func TestPrebuilt(t *testing.T) { ctx := testApex(t, ` prebuilt_apex { name: "myapex", arch: { arm64: { src: "myapex-arm64.apex", }, arm: { src: "myapex-arm.apex", }, }, } `) testingModule := ctx.ModuleForTests("myapex", "android_common_myapex") prebuilt := testingModule.Module().(*Prebuilt) expectedInput := "myapex-arm64.apex" if prebuilt.inputApex.String() != expectedInput { t.Errorf("inputApex invalid. expected: %q, actual: %q", expectedInput, prebuilt.inputApex.String()) } android.AssertStringDoesContain(t, "Invalid provenance metadata file", prebuilt.ProvenanceMetaDataFile().String(), "soong/.intermediates/provenance_metadata/myapex/provenance_metadata.textproto") rule := testingModule.Rule("genProvenanceMetaData") android.AssertStringEquals(t, "Invalid input", "myapex-arm64.apex", rule.Inputs[0].String()) android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/myapex/provenance_metadata.textproto", rule.Output.String()) android.AssertStringEquals(t, "Invalid args", "myapex", rule.Args["module_name"]) android.AssertStringEquals(t, "Invalid args", "/system/apex/myapex.apex", rule.Args["install_path"]) entries := android.AndroidMkEntriesForTest(t, ctx, testingModule.Module())[0] android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "prebuilt_apex", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0]) } func TestPrebuiltMissingSrc(t *testing.T) { testApexError(t, `module "myapex" variant "android_common_myapex".*: prebuilt_apex does not support "arm64_armv8-a"`, ` prebuilt_apex { name: "myapex", } `) } func TestPrebuiltFilenameOverride(t *testing.T) { ctx := testApex(t, ` prebuilt_apex { name: "myapex", src: "myapex-arm.apex", filename: "notmyapex.apex", } `) testingModule := ctx.ModuleForTests("myapex", "android_common_myapex") p := testingModule.Module().(*Prebuilt) expected := "notmyapex.apex" if p.installFilename != expected { t.Errorf("installFilename invalid. expected: %q, actual: %q", expected, p.installFilename) } rule := testingModule.Rule("genProvenanceMetaData") android.AssertStringEquals(t, "Invalid input", "myapex-arm.apex", rule.Inputs[0].String()) android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/myapex/provenance_metadata.textproto", rule.Output.String()) android.AssertStringEquals(t, "Invalid args", "myapex", rule.Args["module_name"]) android.AssertStringEquals(t, "Invalid args", "/system/apex/notmyapex.apex", rule.Args["install_path"]) } func TestApexSetFilenameOverride(t *testing.T) { testApex(t, ` apex_set { name: "com.company.android.myapex", apex_name: "com.android.myapex", set: "company-myapex.apks", filename: "com.company.android.myapex.apex" } `).ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex") testApex(t, ` apex_set { name: "com.company.android.myapex", apex_name: "com.android.myapex", set: "company-myapex.apks", filename: "com.company.android.myapex.capex" } `).ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex") testApexError(t, `filename should end in .apex or .capex for apex_set`, ` apex_set { name: "com.company.android.myapex", apex_name: "com.android.myapex", set: "company-myapex.apks", filename: "some-random-suffix" } `) } func TestPrebuiltOverrides(t *testing.T) { ctx := testApex(t, ` prebuilt_apex { name: "myapex.prebuilt", src: "myapex-arm.apex", overrides: [ "myapex", ], } `) testingModule := ctx.ModuleForTests("myapex.prebuilt", "android_common_myapex.prebuilt") p := testingModule.Module().(*Prebuilt) expected := []string{"myapex"} actual := android.AndroidMkEntriesForTest(t, ctx, p)[0].EntryMap["LOCAL_OVERRIDES_MODULES"] if !reflect.DeepEqual(actual, expected) { t.Errorf("Incorrect LOCAL_OVERRIDES_MODULES value '%s', expected '%s'", actual, expected) } rule := testingModule.Rule("genProvenanceMetaData") android.AssertStringEquals(t, "Invalid input", "myapex-arm.apex", rule.Inputs[0].String()) android.AssertStringEquals(t, "Invalid output", "out/soong/.intermediates/provenance_metadata/myapex.prebuilt/provenance_metadata.textproto", rule.Output.String()) android.AssertStringEquals(t, "Invalid args", "myapex.prebuilt", rule.Args["module_name"]) android.AssertStringEquals(t, "Invalid args", "/system/apex/myapex.prebuilt.apex", rule.Args["install_path"]) } func TestPrebuiltApexName(t *testing.T) { testApex(t, ` prebuilt_apex { name: "com.company.android.myapex", apex_name: "com.android.myapex", src: "company-myapex-arm.apex", } `).ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex") testApex(t, ` apex_set { name: "com.company.android.myapex", apex_name: "com.android.myapex", set: "company-myapex.apks", } `).ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex") } func TestPrebuiltApexNameWithPlatformBootclasspath(t *testing.T) { _ = android.GroupFixturePreparers( java.PrepareForTestWithJavaDefaultModules, PrepareForTestWithApexBuildComponents, android.FixtureWithRootAndroidBp(` platform_bootclasspath { name: "platform-bootclasspath", fragments: [ { apex: "com.android.art", module: "art-bootclasspath-fragment", }, ], } prebuilt_apex { name: "com.company.android.art", apex_name: "com.android.art", src: "com.company.android.art-arm.apex", exported_bootclasspath_fragments: ["art-bootclasspath-fragment"], } prebuilt_bootclasspath_fragment { name: "art-bootclasspath-fragment", image_name: "art", contents: ["core-oj"], hidden_api: { annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv", metadata: "my-bootclasspath-fragment/metadata.csv", index: "my-bootclasspath-fragment/index.csv", stub_flags: "my-bootclasspath-fragment/stub-flags.csv", all_flags: "my-bootclasspath-fragment/all-flags.csv", }, } java_import { name: "core-oj", jars: ["prebuilt.jar"], } `), ).RunTest(t) } // A minimal context object for use with DexJarBuildPath type moduleErrorfTestCtx struct { } func (ctx moduleErrorfTestCtx) ModuleErrorf(format string, args ...interface{}) { } // These tests verify that the prebuilt_apex/deapexer to java_import wiring allows for the // propagation of paths to dex implementation jars from the former to the latter. func TestPrebuiltExportDexImplementationJars(t *testing.T) { transform := android.NullFixturePreparer checkDexJarBuildPath := func(t *testing.T, ctx *android.TestContext, name string) { t.Helper() // Make sure the import has been given the correct path to the dex jar. p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency) dexJarBuildPath := p.DexJarBuildPath(moduleErrorfTestCtx{}).PathOrNil() stem := android.RemoveOptionalPrebuiltPrefix(name) android.AssertStringEquals(t, "DexJarBuildPath should be apex-related path.", ".intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar", android.NormalizePathForTesting(dexJarBuildPath)) } checkDexJarInstallPath := func(t *testing.T, ctx *android.TestContext, name string) { t.Helper() // Make sure the import has been given the correct path to the dex jar. p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency) dexJarBuildPath := p.DexJarInstallPath() stem := android.RemoveOptionalPrebuiltPrefix(name) android.AssertStringEquals(t, "DexJarInstallPath should be apex-related path.", "target/product/test_device/apex/myapex/javalib/"+stem+".jar", android.NormalizePathForTesting(dexJarBuildPath)) } ensureNoSourceVariant := func(t *testing.T, ctx *android.TestContext, name string) { t.Helper() // Make sure that an apex variant is not created for the source module. android.AssertArrayString(t, "Check if there is no source variant", []string{"android_common"}, ctx.ModuleVariantsForTests(name)) } t.Run("prebuilt only", func(t *testing.T) { bp := ` prebuilt_apex { name: "myapex", arch: { arm64: { src: "myapex-arm64.apex", }, arm: { src: "myapex-arm.apex", }, }, exported_java_libs: ["libfoo", "libbar"], } java_import { name: "libfoo", jars: ["libfoo.jar"], } java_sdk_library_import { name: "libbar", public: { jars: ["libbar.jar"], }, } ` // Make sure that dexpreopt can access dex implementation files from the prebuilt. ctx := testDexpreoptWithApexes(t, bp, "", transform) deapexerName := deapexerModuleName("prebuilt_myapex") android.AssertStringEquals(t, "APEX module name from deapexer name", "prebuilt_myapex", apexModuleName(deapexerName)) // Make sure that the deapexer has the correct input APEX. deapexer := ctx.ModuleForTests(deapexerName, "android_common") rule := deapexer.Rule("deapexer") if expected, actual := []string{"myapex-arm64.apex"}, android.NormalizePathsForTesting(rule.Implicits); !reflect.DeepEqual(expected, actual) { t.Errorf("expected: %q, found: %q", expected, actual) } // Make sure that the prebuilt_apex has the correct input APEX. prebuiltApex := ctx.ModuleForTests("myapex", "android_common_myapex") rule = prebuiltApex.Rule("android/soong/android.Cp") if expected, actual := "myapex-arm64.apex", android.NormalizePathForTesting(rule.Input); !reflect.DeepEqual(expected, actual) { t.Errorf("expected: %q, found: %q", expected, actual) } checkDexJarBuildPath(t, ctx, "libfoo") checkDexJarInstallPath(t, ctx, "libfoo") checkDexJarBuildPath(t, ctx, "libbar") checkDexJarInstallPath(t, ctx, "libbar") }) t.Run("prebuilt with source preferred", func(t *testing.T) { bp := ` prebuilt_apex { name: "myapex", arch: { arm64: { src: "myapex-arm64.apex", }, arm: { src: "myapex-arm.apex", }, }, exported_java_libs: ["libfoo", "libbar"], } java_import { name: "libfoo", jars: ["libfoo.jar"], } java_library { name: "libfoo", } java_sdk_library_import { name: "libbar", public: { jars: ["libbar.jar"], }, } java_sdk_library { name: "libbar", srcs: ["foo/bar/MyClass.java"], unsafe_ignore_missing_latest_api: true, } ` // Make sure that dexpreopt can access dex implementation files from the prebuilt. ctx := testDexpreoptWithApexes(t, bp, "", transform) checkDexJarBuildPath(t, ctx, "prebuilt_libfoo") checkDexJarInstallPath(t, ctx, "prebuilt_libfoo") ensureNoSourceVariant(t, ctx, "libfoo") checkDexJarBuildPath(t, ctx, "prebuilt_libbar") checkDexJarInstallPath(t, ctx, "prebuilt_libbar") ensureNoSourceVariant(t, ctx, "libbar") }) t.Run("prebuilt preferred with source", func(t *testing.T) { bp := ` prebuilt_apex { name: "myapex", arch: { arm64: { src: "myapex-arm64.apex", }, arm: { src: "myapex-arm.apex", }, }, exported_java_libs: ["libfoo", "libbar"], } java_import { name: "libfoo", prefer: true, jars: ["libfoo.jar"], } java_library { name: "libfoo", } java_sdk_library_import { name: "libbar", prefer: true, public: { jars: ["libbar.jar"], }, } java_sdk_library { name: "libbar", srcs: ["foo/bar/MyClass.java"], unsafe_ignore_missing_latest_api: true, } ` // Make sure that dexpreopt can access dex implementation files from the prebuilt. ctx := testDexpreoptWithApexes(t, bp, "", transform) checkDexJarBuildPath(t, ctx, "prebuilt_libfoo") checkDexJarInstallPath(t, ctx, "prebuilt_libfoo") ensureNoSourceVariant(t, ctx, "libfoo") checkDexJarBuildPath(t, ctx, "prebuilt_libbar") checkDexJarInstallPath(t, ctx, "prebuilt_libbar") ensureNoSourceVariant(t, ctx, "libbar") }) } func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { preparer := android.GroupFixturePreparers( java.FixtureConfigureApexBootJars("myapex:libfoo", "myapex:libbar"), // Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding // is disabled. android.FixtureAddTextFile("frameworks/base/Android.bp", ""), // Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi // file creation. java.FixtureConfigureBootJars("platform:foo"), android.FixtureModifyMockFS(func(fs android.MockFS) { fs["platform/Android.bp"] = []byte(` java_library { name: "foo", srcs: ["Test.java"], compile_dex: true, } `) fs["platform/Test.java"] = nil }), ) checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) { t.Helper() s := ctx.ModuleForTests("dex_bootjars", "android_common") foundLibfooJar := false base := stem + ".jar" for _, output := range s.AllOutputs() { if filepath.Base(output) == base { foundLibfooJar = true buildRule := s.Output(output) android.AssertStringEquals(t, "boot dex jar path", bootDexJarPath, buildRule.Input.String()) } } if !foundLibfooJar { t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs %q", android.StringPathsRelativeToTop(ctx.Config().SoongOutDir(), s.AllOutputs())) } } checkHiddenAPIIndexFromClassesInputs := func(t *testing.T, ctx *android.TestContext, expectedIntermediateInputs string) { t.Helper() platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common") var rule android.TestingBuildParams rule = platformBootclasspath.Output("hiddenapi-monolithic/index-from-classes.csv") java.CheckHiddenAPIRuleInputs(t, "intermediate index", expectedIntermediateInputs, rule) } checkHiddenAPIIndexFromFlagsInputs := func(t *testing.T, ctx *android.TestContext, expectedIntermediateInputs string) { t.Helper() platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common") var rule android.TestingBuildParams rule = platformBootclasspath.Output("hiddenapi-index.csv") java.CheckHiddenAPIRuleInputs(t, "monolithic index", expectedIntermediateInputs, rule) } fragment := java.ApexVariantReference{ Apex: proptools.StringPtr("myapex"), Module: proptools.StringPtr("my-bootclasspath-fragment"), } t.Run("prebuilt only", func(t *testing.T) { bp := ` prebuilt_apex { name: "myapex", arch: { arm64: { src: "myapex-arm64.apex", }, arm: { src: "myapex-arm.apex", }, }, exported_bootclasspath_fragments: ["my-bootclasspath-fragment"], } prebuilt_bootclasspath_fragment { name: "my-bootclasspath-fragment", contents: ["libfoo", "libbar"], apex_available: ["myapex"], hidden_api: { annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv", metadata: "my-bootclasspath-fragment/metadata.csv", index: "my-bootclasspath-fragment/index.csv", signature_patterns: "my-bootclasspath-fragment/signature-patterns.csv", filtered_stub_flags: "my-bootclasspath-fragment/filtered-stub-flags.csv", filtered_flags: "my-bootclasspath-fragment/filtered-flags.csv", }, } java_import { name: "libfoo", jars: ["libfoo.jar"], apex_available: ["myapex"], permitted_packages: ["foo"], } java_sdk_library_import { name: "libbar", public: { jars: ["libbar.jar"], }, apex_available: ["myapex"], shared_library: false, permitted_packages: ["bar"], } ` ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment) checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`) checkHiddenAPIIndexFromFlagsInputs(t, ctx, ` my-bootclasspath-fragment/index.csv out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv `) }) t.Run("apex_set only", func(t *testing.T) { bp := ` apex_set { name: "myapex", set: "myapex.apks", exported_java_libs: ["myjavalib"], exported_bootclasspath_fragments: ["my-bootclasspath-fragment"], exported_systemserverclasspath_fragments: ["my-systemserverclasspath-fragment"], } java_import { name: "myjavalib", jars: ["myjavalib.jar"], apex_available: ["myapex"], permitted_packages: ["javalib"], } prebuilt_bootclasspath_fragment { name: "my-bootclasspath-fragment", contents: ["libfoo", "libbar"], apex_available: ["myapex"], hidden_api: { annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv", metadata: "my-bootclasspath-fragment/metadata.csv", index: "my-bootclasspath-fragment/index.csv", signature_patterns: "my-bootclasspath-fragment/signature-patterns.csv", filtered_stub_flags: "my-bootclasspath-fragment/filtered-stub-flags.csv", filtered_flags: "my-bootclasspath-fragment/filtered-flags.csv", }, } prebuilt_systemserverclasspath_fragment { name: "my-systemserverclasspath-fragment", contents: ["libbaz"], apex_available: ["myapex"], } java_import { name: "libfoo", jars: ["libfoo.jar"], apex_available: ["myapex"], permitted_packages: ["foo"], } java_sdk_library_import { name: "libbar", public: { jars: ["libbar.jar"], }, apex_available: ["myapex"], shared_library: false, permitted_packages: ["bar"], } java_sdk_library_import { name: "libbaz", public: { jars: ["libbaz.jar"], }, apex_available: ["myapex"], shared_library: false, permitted_packages: ["baz"], } ` ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment) checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`) checkHiddenAPIIndexFromFlagsInputs(t, ctx, ` my-bootclasspath-fragment/index.csv out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv `) myApex := ctx.ModuleForTests("myapex", "android_common_myapex").Module() overrideNames := []string{ "myapex", "myjavalib.myapex", "libfoo.myapex", "libbar.myapex", "libbaz.myapex", } mkEntries := android.AndroidMkEntriesForTest(t, ctx, myApex) for i, e := range mkEntries { g := e.OverrideName if w := overrideNames[i]; w != g { t.Errorf("Expected override name %q, got %q", w, g) } } }) t.Run("prebuilt with source library preferred", func(t *testing.T) { bp := ` prebuilt_apex { name: "myapex", arch: { arm64: { src: "myapex-arm64.apex", }, arm: { src: "myapex-arm.apex", }, }, exported_bootclasspath_fragments: ["my-bootclasspath-fragment"], } prebuilt_bootclasspath_fragment { name: "my-bootclasspath-fragment", contents: ["libfoo", "libbar"], apex_available: ["myapex"], hidden_api: { annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv", metadata: "my-bootclasspath-fragment/metadata.csv", index: "my-bootclasspath-fragment/index.csv", stub_flags: "my-bootclasspath-fragment/stub-flags.csv", all_flags: "my-bootclasspath-fragment/all-flags.csv", }, } java_import { name: "libfoo", jars: ["libfoo.jar"], apex_available: ["myapex"], } java_library { name: "libfoo", srcs: ["foo/bar/MyClass.java"], apex_available: ["myapex"], } java_sdk_library_import { name: "libbar", public: { jars: ["libbar.jar"], }, apex_available: ["myapex"], shared_library: false, } java_sdk_library { name: "libbar", srcs: ["foo/bar/MyClass.java"], unsafe_ignore_missing_latest_api: true, apex_available: ["myapex"], } ` // In this test the source (java_library) libfoo is active since the // prebuilt (java_import) defaults to prefer:false. However the // prebuilt_apex module always depends on the prebuilt, and so it doesn't // find the dex boot jar in it. We either need to disable the source libfoo // or make the prebuilt libfoo preferred. testDexpreoptWithApexes(t, bp, "module libfoo does not provide a dex boot jar", preparer, fragment) // dexbootjar check is skipped if AllowMissingDependencies is true preparerAllowMissingDeps := android.GroupFixturePreparers( preparer, android.PrepareForTestWithAllowMissingDependencies, ) testDexpreoptWithApexes(t, bp, "", preparerAllowMissingDeps, fragment) }) t.Run("prebuilt library preferred with source", func(t *testing.T) { bp := ` apex { name: "myapex", key: "myapex.key", updatable: false, bootclasspath_fragments: ["my-bootclasspath-fragment"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } bootclasspath_fragment { name: "my-bootclasspath-fragment", contents: ["libfoo", "libbar"], apex_available: ["myapex"], hidden_api: { split_packages: ["*"], }, } prebuilt_apex { name: "myapex", arch: { arm64: { src: "myapex-arm64.apex", }, arm: { src: "myapex-arm.apex", }, }, exported_bootclasspath_fragments: ["my-bootclasspath-fragment"], } prebuilt_bootclasspath_fragment { name: "my-bootclasspath-fragment", prefer: true, contents: ["libfoo", "libbar"], apex_available: ["myapex"], hidden_api: { annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv", metadata: "my-bootclasspath-fragment/metadata.csv", index: "my-bootclasspath-fragment/index.csv", signature_patterns: "my-bootclasspath-fragment/signature-patterns.csv", filtered_stub_flags: "my-bootclasspath-fragment/filtered-stub-flags.csv", filtered_flags: "my-bootclasspath-fragment/filtered-flags.csv", }, } java_import { name: "libfoo", prefer: true, jars: ["libfoo.jar"], apex_available: ["myapex"], permitted_packages: ["foo"], } java_library { name: "libfoo", srcs: ["foo/bar/MyClass.java"], apex_available: ["myapex"], installable: true, } java_sdk_library_import { name: "libbar", prefer: true, public: { jars: ["libbar.jar"], }, apex_available: ["myapex"], shared_library: false, permitted_packages: ["bar"], } java_sdk_library { name: "libbar", srcs: ["foo/bar/MyClass.java"], unsafe_ignore_missing_latest_api: true, apex_available: ["myapex"], compile_dex: true, } ` ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment) checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`) checkHiddenAPIIndexFromFlagsInputs(t, ctx, ` my-bootclasspath-fragment/index.csv out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv `) }) t.Run("prebuilt with source apex preferred", func(t *testing.T) { bp := ` apex { name: "myapex", key: "myapex.key", updatable: false, bootclasspath_fragments: ["my-bootclasspath-fragment"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } bootclasspath_fragment { name: "my-bootclasspath-fragment", contents: ["libfoo", "libbar"], apex_available: ["myapex"], hidden_api: { split_packages: ["*"], }, } prebuilt_apex { name: "myapex", arch: { arm64: { src: "myapex-arm64.apex", }, arm: { src: "myapex-arm.apex", }, }, exported_bootclasspath_fragments: ["my-bootclasspath-fragment"], } prebuilt_bootclasspath_fragment { name: "my-bootclasspath-fragment", contents: ["libfoo", "libbar"], apex_available: ["myapex"], hidden_api: { annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv", metadata: "my-bootclasspath-fragment/metadata.csv", index: "my-bootclasspath-fragment/index.csv", signature_patterns: "my-bootclasspath-fragment/signature-patterns.csv", filtered_stub_flags: "my-bootclasspath-fragment/filtered-stub-flags.csv", filtered_flags: "my-bootclasspath-fragment/filtered-flags.csv", }, } java_import { name: "libfoo", jars: ["libfoo.jar"], apex_available: ["myapex"], } java_library { name: "libfoo", srcs: ["foo/bar/MyClass.java"], apex_available: ["myapex"], permitted_packages: ["foo"], installable: true, } java_sdk_library_import { name: "libbar", public: { jars: ["libbar.jar"], }, apex_available: ["myapex"], shared_library: false, } java_sdk_library { name: "libbar", srcs: ["foo/bar/MyClass.java"], unsafe_ignore_missing_latest_api: true, apex_available: ["myapex"], permitted_packages: ["bar"], compile_dex: true, } ` ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment) checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/hiddenapi-modular/encoded/libfoo.jar") checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/hiddenapi-modular/encoded/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`) checkHiddenAPIIndexFromFlagsInputs(t, ctx, ` out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv out/soong/.intermediates/my-bootclasspath-fragment/android_common_myapex/modular-hiddenapi/index.csv out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv `) }) t.Run("prebuilt preferred with source apex disabled", func(t *testing.T) { bp := ` apex { name: "myapex", enabled: false, key: "myapex.key", bootclasspath_fragments: ["my-bootclasspath-fragment"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } bootclasspath_fragment { name: "my-bootclasspath-fragment", enabled: false, contents: ["libfoo", "libbar"], apex_available: ["myapex"], hidden_api: { split_packages: ["*"], }, } prebuilt_apex { name: "myapex", arch: { arm64: { src: "myapex-arm64.apex", }, arm: { src: "myapex-arm.apex", }, }, exported_bootclasspath_fragments: ["my-bootclasspath-fragment"], } prebuilt_bootclasspath_fragment { name: "my-bootclasspath-fragment", contents: ["libfoo", "libbar"], apex_available: ["myapex"], hidden_api: { annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv", metadata: "my-bootclasspath-fragment/metadata.csv", index: "my-bootclasspath-fragment/index.csv", signature_patterns: "my-bootclasspath-fragment/signature-patterns.csv", filtered_stub_flags: "my-bootclasspath-fragment/filtered-stub-flags.csv", filtered_flags: "my-bootclasspath-fragment/filtered-flags.csv", }, } java_import { name: "libfoo", jars: ["libfoo.jar"], apex_available: ["myapex"], permitted_packages: ["foo"], } java_library { name: "libfoo", enabled: false, srcs: ["foo/bar/MyClass.java"], apex_available: ["myapex"], installable: true, } java_sdk_library_import { name: "libbar", public: { jars: ["libbar.jar"], }, apex_available: ["myapex"], shared_library: false, permitted_packages: ["bar"], } java_sdk_library { name: "libbar", enabled: false, srcs: ["foo/bar/MyClass.java"], unsafe_ignore_missing_latest_api: true, apex_available: ["myapex"], compile_dex: true, } ` // This test disables libbar, which causes the ComponentDepsMutator to add // deps on libbar.stubs and other sub-modules that don't exist. We can // enable AllowMissingDependencies to work around that, but enabling that // causes extra checks for missing source files to dex_bootjars, so add those // to the mock fs as well. preparer2 := android.GroupFixturePreparers( preparer, android.PrepareForTestWithAllowMissingDependencies, android.FixtureMergeMockFs(map[string][]byte{ "build/soong/scripts/check_boot_jars/package_allowed_list.txt": nil, "frameworks/base/config/boot-profile.txt": nil, }), ) ctx := testDexpreoptWithApexes(t, bp, "", preparer2, fragment) checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/prebuilt_myapex.deapexer/android_common/deapexer/javalib/libbar.jar") // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexFromClassesInputs(t, ctx, `out/soong/.intermediates/platform/foo/android_common/javac/foo.jar`) checkHiddenAPIIndexFromFlagsInputs(t, ctx, ` my-bootclasspath-fragment/index.csv out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv out/soong/.intermediates/packages/modules/com.android.art/art-bootclasspath-fragment/android_common_apex10000/modular-hiddenapi/index.csv `) }) t.Run("Co-existing unflagged apexes should create a duplicate module error", func(t *testing.T) { bp := ` // Source apex { name: "myapex", enabled: false, key: "myapex.key", bootclasspath_fragments: ["my-bootclasspath-fragment"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } // Prebuilt prebuilt_apex { name: "myapex.v1", source_apex_name: "myapex", arch: { arm64: { src: "myapex-arm64.apex", }, arm: { src: "myapex-arm.apex", }, }, exported_bootclasspath_fragments: ["my-bootclasspath-fragment"], prefer: true, } prebuilt_apex { name: "myapex.v2", source_apex_name: "myapex", arch: { arm64: { src: "myapex-arm64.apex", }, arm: { src: "myapex-arm.apex", }, }, exported_bootclasspath_fragments: ["my-bootclasspath-fragment"], prefer: true, } prebuilt_bootclasspath_fragment { name: "my-bootclasspath-fragment", contents: ["libfoo", "libbar"], apex_available: ["myapex"], hidden_api: { annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv", metadata: "my-bootclasspath-fragment/metadata.csv", index: "my-bootclasspath-fragment/index.csv", stub_flags: "my-bootclasspath-fragment/stub-flags.csv", all_flags: "my-bootclasspath-fragment/all-flags.csv", }, prefer: true, } java_import { name: "libfoo", jars: ["libfoo.jar"], apex_available: ["myapex"], prefer: true, } java_import { name: "libbar", jars: ["libbar.jar"], apex_available: ["myapex"], prefer: true, } ` testDexpreoptWithApexes(t, bp, "Multiple prebuilt modules prebuilt_myapex.v1 and prebuilt_myapex.v2 have been marked as preferred for this source module", preparer, fragment) }) } func TestApexWithTests(t *testing.T) { ctx := testApex(t, ` apex_test { name: "myapex", key: "myapex.key", updatable: false, tests: [ "mytest", "mytests", ], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } filegroup { name: "fg", srcs: [ "baz", "bar/baz" ], } cc_test { name: "mytest", gtest: false, srcs: ["mytest.cpp"], relative_install_path: "test", shared_libs: ["mylib"], system_shared_libs: [], static_executable: true, stl: "none", data: [":fg"], } cc_library { name: "mylib", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", } filegroup { name: "fg2", srcs: [ "testdata/baz" ], } cc_test { name: "mytests", gtest: false, srcs: [ "mytest1.cpp", "mytest2.cpp", "mytest3.cpp", ], test_per_src: true, relative_install_path: "test", system_shared_libs: [], static_executable: true, stl: "none", data: [ ":fg", ":fg2", ], } `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that test dep (and their transitive dependencies) are copied into apex. ensureContains(t, copyCmds, "image.apex/bin/test/mytest") ensureContains(t, copyCmds, "image.apex/lib64/mylib.so") //Ensure that test data are copied into apex. ensureContains(t, copyCmds, "image.apex/bin/test/baz") ensureContains(t, copyCmds, "image.apex/bin/test/bar/baz") // Ensure that test deps built with `test_per_src` are copied into apex. ensureContains(t, copyCmds, "image.apex/bin/test/mytest1") ensureContains(t, copyCmds, "image.apex/bin/test/mytest2") ensureContains(t, copyCmds, "image.apex/bin/test/mytest3") // Ensure the module is correctly translated. bundle := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, bundle) name := bundle.BaseModuleName() prefix := "TARGET_" var builder strings.Builder data.Custom(&builder, name, prefix, "", data) androidMk := builder.String() ensureContains(t, androidMk, "LOCAL_MODULE := mytest.myapex\n") ensureContains(t, androidMk, "LOCAL_MODULE := mytest1.myapex\n") ensureContains(t, androidMk, "LOCAL_MODULE := mytest2.myapex\n") ensureContains(t, androidMk, "LOCAL_MODULE := mytest3.myapex\n") ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n") } func TestErrorsIfDepsAreNotEnabled(t *testing.T) { testApexError(t, `module "myapex" .* depends on disabled module "libfoo"`, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libfoo", stl: "none", system_shared_libs: [], enabled: false, apex_available: ["myapex"], } `) testApexError(t, `module "myapex" .* depends on disabled module "myjar"`, ` apex { name: "myapex", key: "myapex.key", java_libs: ["myjar"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } java_library { name: "myjar", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", enabled: false, apex_available: ["myapex"], } `) } func TestApexWithJavaImport(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", java_libs: ["myjavaimport"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } java_import { name: "myjavaimport", apex_available: ["myapex"], jars: ["my.jar"], compile_dex: true, } `) module := ctx.ModuleForTests("myapex", "android_common_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] ensureContains(t, copyCmds, "image.apex/javalib/myjavaimport.jar") } func TestApexWithApps(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: [ "AppFoo", "AppFooPriv", ], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_app { name: "AppFoo", srcs: ["foo/bar/MyClass.java"], sdk_version: "current", system_modules: "none", use_embedded_native_libs: true, jni_libs: ["libjni"], stl: "none", apex_available: [ "myapex" ], } android_app { name: "AppFooPriv", srcs: ["foo/bar/MyClass.java"], sdk_version: "current", system_modules: "none", privileged: true, privapp_allowlist: "privapp_allowlist_com.android.AppFooPriv.xml", stl: "none", apex_available: [ "myapex" ], } cc_library_shared { name: "libjni", srcs: ["mylib.cpp"], shared_libs: ["libfoo"], stl: "none", system_shared_libs: [], apex_available: [ "myapex" ], sdk_version: "current", } cc_library_shared { name: "libfoo", stl: "none", system_shared_libs: [], apex_available: [ "myapex" ], sdk_version: "current", } `) module := ctx.ModuleForTests("myapex", "android_common_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] ensureContains(t, copyCmds, "image.apex/app/AppFoo@TEST.BUILD_ID/AppFoo.apk") ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPriv@TEST.BUILD_ID/AppFooPriv.apk") ensureContains(t, copyCmds, "image.apex/etc/permissions/privapp_allowlist_com.android.AppFooPriv.xml") appZipRule := ctx.ModuleForTests("AppFoo", "android_common_apex10000").Description("zip jni libs") // JNI libraries are uncompressed if args := appZipRule.Args["jarArgs"]; !strings.Contains(args, "-L 0") { t.Errorf("jni libs are not uncompressed for AppFoo") } // JNI libraries including transitive deps are for _, jni := range []string{"libjni", "libfoo"} { jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_sdk_shared_apex10000").Module().(*cc.Module).OutputFile().RelativeToTop() // ... embedded inside APK (jnilibs.zip) ensureListContains(t, appZipRule.Implicits.Strings(), jniOutput.String()) // ... and not directly inside the APEX ensureNotContains(t, copyCmds, "image.apex/lib64/"+jni+".so") } apexBundle := module.Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, apexBundle) var builder strings.Builder data.Custom(&builder, apexBundle.Name(), "TARGET_", "", data) androidMk := builder.String() ensureContains(t, androidMk, "LOCAL_MODULE := AppFooPriv.myapex") ensureContains(t, androidMk, "LOCAL_MODULE := AppFoo.myapex") ensureMatches(t, androidMk, "LOCAL_SOONG_INSTALLED_MODULE := \\S+AppFooPriv.apk") ensureMatches(t, androidMk, "LOCAL_SOONG_INSTALLED_MODULE := \\S+AppFoo.apk") ensureMatches(t, androidMk, "LOCAL_SOONG_INSTALL_PAIRS := \\S+AppFooPriv.apk") ensureContains(t, androidMk, "LOCAL_SOONG_INSTALL_PAIRS := privapp_allowlist_com.android.AppFooPriv.xml:$(PRODUCT_OUT)/apex/myapex/etc/permissions/privapp_allowlist_com.android.AppFooPriv.xml") } func TestApexWithAppImportBuildId(t *testing.T) { invalidBuildIds := []string{"../", "a b", "a/b", "a/b/../c", "/a"} for _, id := range invalidBuildIds { message := fmt.Sprintf("Unable to use build id %s as filename suffix", id) fixture := android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.BuildId = proptools.StringPtr(id) }) testApexError(t, message, `apex { name: "myapex", key: "myapex.key", apps: ["AppFooPrebuilt"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_app_import { name: "AppFooPrebuilt", apk: "PrebuiltAppFoo.apk", presigned: true, apex_available: ["myapex"], } `, fixture) } } func TestApexWithAppImports(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: [ "AppFooPrebuilt", "AppFooPrivPrebuilt", ], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_app_import { name: "AppFooPrebuilt", apk: "PrebuiltAppFoo.apk", presigned: true, dex_preopt: { enabled: false, }, apex_available: ["myapex"], } android_app_import { name: "AppFooPrivPrebuilt", apk: "PrebuiltAppFooPriv.apk", privileged: true, presigned: true, dex_preopt: { enabled: false, }, filename: "AwesomePrebuiltAppFooPriv.apk", apex_available: ["myapex"], } `) module := ctx.ModuleForTests("myapex", "android_common_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] ensureContains(t, copyCmds, "image.apex/app/AppFooPrebuilt@TEST.BUILD_ID/AppFooPrebuilt.apk") ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPrivPrebuilt@TEST.BUILD_ID/AwesomePrebuiltAppFooPriv.apk") } func TestApexWithAppImportsPrefer(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: [ "AppFoo", ], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_app { name: "AppFoo", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", apex_available: [ "myapex" ], } android_app_import { name: "AppFoo", apk: "AppFooPrebuilt.apk", filename: "AppFooPrebuilt.apk", presigned: true, prefer: true, apex_available: ["myapex"], } `, withFiles(map[string][]byte{ "AppFooPrebuilt.apk": nil, })) ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "app/AppFoo@TEST.BUILD_ID/AppFooPrebuilt.apk", }) } func TestApexWithTestHelperApp(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: [ "TesterHelpAppFoo", ], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_test_helper_app { name: "TesterHelpAppFoo", srcs: ["foo/bar/MyClass.java"], apex_available: [ "myapex" ], } `) module := ctx.ModuleForTests("myapex", "android_common_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] ensureContains(t, copyCmds, "image.apex/app/TesterHelpAppFoo@TEST.BUILD_ID/TesterHelpAppFoo.apk") } func TestApexPropertiesShouldBeDefaultable(t *testing.T) { // libfoo's apex_available comes from cc_defaults testApexError(t, `requires "libfoo" that doesn't list the APEX under 'apex_available'.`, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } apex { name: "otherapex", key: "myapex.key", native_shared_libs: ["libfoo"], updatable: false, } cc_defaults { name: "libfoo-defaults", apex_available: ["otherapex"], } cc_library { name: "libfoo", defaults: ["libfoo-defaults"], stl: "none", system_shared_libs: [], }`) } func TestApexAvailable_DirectDep(t *testing.T) { // libfoo is not available to myapex, but only to otherapex testApexError(t, "requires \"libfoo\" that doesn't list the APEX under 'apex_available'.", ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } apex { name: "otherapex", key: "otherapex.key", native_shared_libs: ["libfoo"], updatable: false, } apex_key { name: "otherapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libfoo", stl: "none", system_shared_libs: [], apex_available: ["otherapex"], }`) } func TestApexAvailable_IndirectDep(t *testing.T) { // libbbaz is an indirect dep testApexError(t, `requires "libbaz" that doesn't list the APEX under 'apex_available'.\n\nDependency path: .*via tag apex\.dependencyTag\{"sharedLib"\} .*-> libfoo.*link:shared.* .*via tag cc\.libraryDependencyTag.*Kind:sharedLibraryDependency.* .*-> libbar.*link:shared.* .*via tag cc\.libraryDependencyTag.*Kind:sharedLibraryDependency.* .*-> libbaz.*link:shared.*`, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libfoo", stl: "none", shared_libs: ["libbar"], system_shared_libs: [], apex_available: ["myapex"], } cc_library { name: "libbar", stl: "none", shared_libs: ["libbaz"], system_shared_libs: [], apex_available: ["myapex"], } cc_library { name: "libbaz", stl: "none", system_shared_libs: [], }`) } func TestApexAvailable_IndirectStaticDep(t *testing.T) { testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libfoo", stl: "none", static_libs: ["libbar"], system_shared_libs: [], apex_available: ["myapex"], } cc_library { name: "libbar", stl: "none", shared_libs: ["libbaz"], system_shared_libs: [], apex_available: ["myapex"], } cc_library { name: "libbaz", stl: "none", system_shared_libs: [], }`) testApexError(t, `requires "libbar" that doesn't list the APEX under 'apex_available'.`, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libfoo", stl: "none", static_libs: ["libbar"], system_shared_libs: [], apex_available: ["myapex"], } cc_library { name: "libbar", stl: "none", system_shared_libs: [], }`) } func TestApexAvailable_InvalidApexName(t *testing.T) { testApexError(t, "\"otherapex\" is not a valid module name", ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libfoo", stl: "none", system_shared_libs: [], apex_available: ["otherapex"], }`) testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo", "libbar"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libfoo", stl: "none", system_shared_libs: [], runtime_libs: ["libbaz"], apex_available: ["myapex"], } cc_library { name: "libbar", stl: "none", system_shared_libs: [], apex_available: ["//apex_available:anyapex"], } cc_library { name: "libbaz", stl: "none", system_shared_libs: [], stubs: { versions: ["10", "20", "30"], }, }`) } func TestApexAvailable_ApexAvailableNameWithVersionCodeError(t *testing.T) { t.Run("negative variant_version produces error", func(t *testing.T) { testApexError(t, "expected an integer between 0-9; got -1", ` apex { name: "myapex", key: "myapex.key", apex_available_name: "com.android.foo", variant_version: "-1", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `) }) t.Run("variant_version greater than 9 produces error", func(t *testing.T) { testApexError(t, "expected an integer between 0-9; got 10", ` apex { name: "myapex", key: "myapex.key", apex_available_name: "com.android.foo", variant_version: "10", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `) }) } func TestApexAvailable_ApexAvailableNameWithVersionCode(t *testing.T) { context := android.GroupFixturePreparers( android.PrepareForIntegrationTestWithAndroid, PrepareForTestWithApexBuildComponents, android.FixtureMergeMockFs(android.MockFS{ "system/sepolicy/apex/foo-file_contexts": nil, "system/sepolicy/apex/bar-file_contexts": nil, }), ) result := context.RunTestWithBp(t, ` apex { name: "foo", key: "myapex.key", apex_available_name: "com.android.foo", variant_version: "0", updatable: false, } apex { name: "bar", key: "myapex.key", apex_available_name: "com.android.foo", variant_version: "3", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } override_apex { name: "myoverrideapex", base: "bar", } `) fooManifestRule := result.ModuleForTests("foo", "android_common_foo").Rule("apexManifestRule") fooExpectedDefaultVersion := android.DefaultUpdatableModuleVersion fooActualDefaultVersion := fooManifestRule.Args["default_version"] if fooActualDefaultVersion != fooExpectedDefaultVersion { t.Errorf("expected to find defaultVersion %q; got %q", fooExpectedDefaultVersion, fooActualDefaultVersion) } barManifestRule := result.ModuleForTests("bar", "android_common_bar").Rule("apexManifestRule") defaultVersionInt, _ := strconv.Atoi(android.DefaultUpdatableModuleVersion) barExpectedDefaultVersion := fmt.Sprint(defaultVersionInt + 3) barActualDefaultVersion := barManifestRule.Args["default_version"] if barActualDefaultVersion != barExpectedDefaultVersion { t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion) } overrideBarManifestRule := result.ModuleForTests("bar", "android_common_myoverrideapex_myoverrideapex").Rule("apexManifestRule") overrideBarActualDefaultVersion := overrideBarManifestRule.Args["default_version"] if overrideBarActualDefaultVersion != barExpectedDefaultVersion { t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion) } } func TestApexAvailable_ApexAvailableName(t *testing.T) { t.Run("using name of apex that sets apex_available_name is not allowed", func(t *testing.T) { testApexError(t, "Consider adding \"myapex\" to 'apex_available' property of \"AppFoo\"", ` apex { name: "myapex_sminus", key: "myapex.key", apps: ["AppFoo"], apex_available_name: "myapex", updatable: false, } apex { name: "myapex", key: "myapex.key", apps: ["AppFoo"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_app { name: "AppFoo", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", apex_available: [ "myapex_sminus" ], }`, android.FixtureMergeMockFs(android.MockFS{ "system/sepolicy/apex/myapex_sminus-file_contexts": nil, }), ) }) t.Run("apex_available_name allows module to be used in two different apexes", func(t *testing.T) { testApex(t, ` apex { name: "myapex_sminus", key: "myapex.key", apps: ["AppFoo"], apex_available_name: "myapex", updatable: false, } apex { name: "myapex", key: "myapex.key", apps: ["AppFoo"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_app { name: "AppFoo", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", apex_available: [ "myapex" ], }`, android.FixtureMergeMockFs(android.MockFS{ "system/sepolicy/apex/myapex_sminus-file_contexts": nil, }), ) }) t.Run("override_apexes work with apex_available_name", func(t *testing.T) { testApex(t, ` override_apex { name: "myoverrideapex_sminus", base: "myapex_sminus", key: "myapex.key", apps: ["AppFooOverride"], } override_apex { name: "myoverrideapex", base: "myapex", key: "myapex.key", apps: ["AppFooOverride"], } apex { name: "myapex_sminus", key: "myapex.key", apps: ["AppFoo"], apex_available_name: "myapex", updatable: false, } apex { name: "myapex", key: "myapex.key", apps: ["AppFoo"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_app { name: "AppFooOverride", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", apex_available: [ "myapex" ], } android_app { name: "AppFoo", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", apex_available: [ "myapex" ], }`, android.FixtureMergeMockFs(android.MockFS{ "system/sepolicy/apex/myapex_sminus-file_contexts": nil, }), ) }) } func TestApexAvailable_ApexAvailableNameWithOverrides(t *testing.T) { context := android.GroupFixturePreparers( android.PrepareForIntegrationTestWithAndroid, PrepareForTestWithApexBuildComponents, java.PrepareForTestWithDexpreopt, android.FixtureMergeMockFs(android.MockFS{ "system/sepolicy/apex/myapex-file_contexts": nil, "system/sepolicy/apex/myapex_sminus-file_contexts": nil, }), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.BuildId = proptools.StringPtr("buildid") }), ) context.RunTestWithBp(t, ` override_apex { name: "myoverrideapex_sminus", base: "myapex_sminus", } override_apex { name: "myoverrideapex", base: "myapex", } apex { name: "myapex", key: "myapex.key", apps: ["AppFoo"], updatable: false, } apex { name: "myapex_sminus", apex_available_name: "myapex", key: "myapex.key", apps: ["AppFoo_sminus"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_app { name: "AppFoo", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", apex_available: [ "myapex" ], } android_app { name: "AppFoo_sminus", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", min_sdk_version: "29", system_modules: "none", apex_available: [ "myapex" ], }`) } func TestApexAvailable_CheckForPlatform(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libbar", "libbaz"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libfoo", stl: "none", system_shared_libs: [], shared_libs: ["libbar"], apex_available: ["//apex_available:platform"], } cc_library { name: "libfoo2", stl: "none", system_shared_libs: [], shared_libs: ["libbaz"], apex_available: ["//apex_available:platform"], } cc_library { name: "libbar", stl: "none", system_shared_libs: [], apex_available: ["myapex"], } cc_library { name: "libbaz", stl: "none", system_shared_libs: [], apex_available: ["myapex"], stubs: { versions: ["1"], }, }`) // libfoo shouldn't be available to platform even though it has "//apex_available:platform", // because it depends on libbar which isn't available to platform libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module().(*cc.Module) if libfoo.NotAvailableForPlatform() != true { t.Errorf("%q shouldn't be available to platform", libfoo.String()) } // libfoo2 however can be available to platform because it depends on libbaz which provides // stubs libfoo2 := ctx.ModuleForTests("libfoo2", "android_arm64_armv8-a_shared").Module().(*cc.Module) if libfoo2.NotAvailableForPlatform() == true { t.Errorf("%q should be available to platform", libfoo2.String()) } } func TestApexAvailable_CreatedForApex(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libfoo", stl: "none", system_shared_libs: [], apex_available: ["myapex"], static: { apex_available: ["//apex_available:platform"], }, }`) libfooShared := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module().(*cc.Module) if libfooShared.NotAvailableForPlatform() != true { t.Errorf("%q shouldn't be available to platform", libfooShared.String()) } libfooStatic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Module().(*cc.Module) if libfooStatic.NotAvailableForPlatform() != false { t.Errorf("%q should be available to platform", libfooStatic.String()) } } func TestOverrideApex(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: ["app"], bpfs: ["bpf"], prebuilts: ["myetc"], overrides: ["oldapex"], updatable: false, } override_apex { name: "override_myapex", base: "myapex", apps: ["override_app"], bpfs: ["overrideBpf"], prebuilts: ["override_myetc"], overrides: ["unknownapex"], compile_multilib: "first", multilib: { lib32: { native_shared_libs: ["mylib32"], }, lib64: { native_shared_libs: ["mylib64"], }, }, logging_parent: "com.foo.bar", package_name: "test.overridden.package", key: "mynewapex.key", certificate: ":myapex.certificate", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } apex_key { name: "mynewapex.key", public_key: "testkey2.avbpubkey", private_key: "testkey2.pem", } android_app_certificate { name: "myapex.certificate", certificate: "testkey", } android_app { name: "app", srcs: ["foo/bar/MyClass.java"], package_name: "foo", sdk_version: "none", system_modules: "none", apex_available: [ "myapex" ], } override_android_app { name: "override_app", base: "app", package_name: "bar", } bpf { name: "bpf", srcs: ["bpf.c"], } bpf { name: "overrideBpf", srcs: ["overrideBpf.c"], } prebuilt_etc { name: "myetc", src: "myprebuilt", } prebuilt_etc { name: "override_myetc", src: "override_myprebuilt", } cc_library { name: "mylib32", apex_available: [ "myapex" ], } cc_library { name: "mylib64", apex_available: [ "myapex" ], } `, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"})) originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(android.OverridableModule) overriddenVariant := ctx.ModuleForTests("myapex", "android_common_override_myapex_override_myapex").Module().(android.OverridableModule) if originalVariant.GetOverriddenBy() != "" { t.Errorf("GetOverriddenBy should be empty, but was %q", originalVariant.GetOverriddenBy()) } if overriddenVariant.GetOverriddenBy() != "override_myapex" { t.Errorf("GetOverriddenBy should be \"override_myapex\", but was %q", overriddenVariant.GetOverriddenBy()) } module := ctx.ModuleForTests("myapex", "android_common_override_myapex_override_myapex") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] ensureNotContains(t, copyCmds, "image.apex/app/app@TEST.BUILD_ID/app.apk") ensureContains(t, copyCmds, "image.apex/app/override_app@TEST.BUILD_ID/override_app.apk") ensureNotContains(t, copyCmds, "image.apex/etc/bpf/bpf.o") ensureContains(t, copyCmds, "image.apex/etc/bpf/overrideBpf.o") ensureNotContains(t, copyCmds, "image.apex/etc/myetc") ensureContains(t, copyCmds, "image.apex/etc/override_myetc") apexBundle := module.Module().(*apexBundle) name := apexBundle.Name() if name != "override_myapex" { t.Errorf("name should be \"override_myapex\", but was %q", name) } if apexBundle.overridableProperties.Logging_parent != "com.foo.bar" { t.Errorf("override_myapex should have logging parent (com.foo.bar), but was %q.", apexBundle.overridableProperties.Logging_parent) } optFlags := apexRule.Args["opt_flags"] ensureContains(t, optFlags, "--override_apk_package_name test.overridden.package") ensureContains(t, optFlags, "--pubkey testkey2.avbpubkey") signApkRule := module.Rule("signapk") ensureEquals(t, signApkRule.Args["certificates"], "testkey.x509.pem testkey.pk8") data := android.AndroidMkDataForTest(t, ctx, apexBundle) var builder strings.Builder data.Custom(&builder, name, "TARGET_", "", data) androidMk := builder.String() ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex") ensureContains(t, androidMk, "LOCAL_MODULE := overrideBpf.o.override_myapex") ensureContains(t, androidMk, "LOCAL_MODULE_STEM := override_myapex.apex") ensureContains(t, androidMk, "LOCAL_OVERRIDES_MODULES := unknownapex myapex") ensureNotContains(t, androidMk, "LOCAL_MODULE := app.myapex") ensureNotContains(t, androidMk, "LOCAL_MODULE := bpf.myapex") ensureNotContains(t, androidMk, "LOCAL_MODULE := override_app.myapex") ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex") } func TestMinSdkVersionOverride(t *testing.T) { // Override from 29 to 31 minSdkOverride31 := "31" ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], updatable: true, min_sdk_version: "29" } override_apex { name: "override_myapex", base: "myapex", logging_parent: "com.foo.bar", package_name: "test.overridden.package" } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], runtime_libs: ["libbar"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], min_sdk_version: "apex_inherit" } cc_library { name: "libbar", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], min_sdk_version: "apex_inherit" } `, withApexGlobalMinSdkVersionOverride(&minSdkOverride31)) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that direct non-stubs dep is always included ensureContains(t, copyCmds, "image.apex/lib64/mylib.so") // Ensure that runtime_libs dep in included ensureContains(t, copyCmds, "image.apex/lib64/libbar.so") // Ensure libraries target overridden min_sdk_version value ensureListContains(t, ctx.ModuleVariantsForTests("libbar"), "android_arm64_armv8-a_shared_apex31") } func TestMinSdkVersionOverrideToLowerVersionNoOp(t *testing.T) { // Attempt to override from 31 to 29, should be a NOOP minSdkOverride29 := "29" ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], updatable: true, min_sdk_version: "31" } override_apex { name: "override_myapex", base: "myapex", logging_parent: "com.foo.bar", package_name: "test.overridden.package" } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], runtime_libs: ["libbar"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], min_sdk_version: "apex_inherit" } cc_library { name: "libbar", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], min_sdk_version: "apex_inherit" } `, withApexGlobalMinSdkVersionOverride(&minSdkOverride29)) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] // Ensure that direct non-stubs dep is always included ensureContains(t, copyCmds, "image.apex/lib64/mylib.so") // Ensure that runtime_libs dep in included ensureContains(t, copyCmds, "image.apex/lib64/libbar.so") // Ensure libraries target the original min_sdk_version value rather than the overridden ensureListContains(t, ctx.ModuleVariantsForTests("libbar"), "android_arm64_armv8-a_shared_apex31") } func TestLegacyAndroid10Support(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], stl: "libc++", system_shared_libs: [], apex_available: [ "myapex" ], min_sdk_version: "29", } `, withUnbundledBuild) module := ctx.ModuleForTests("myapex", "android_common_myapex") args := module.Rule("apexRule").Args ensureContains(t, args["opt_flags"], "--manifest_json "+module.Output("apex_manifest.json").Output.String()) ensureNotContains(t, args["opt_flags"], "--no_hashtree") // The copies of the libraries in the apex should have one more dependency than // the ones outside the apex, namely the unwinder. Ideally we should check // the dependency names directly here but for some reason the names are blank in // this test. for _, lib := range []string{"libc++", "mylib"} { apexImplicits := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared_apex29").Rule("ld").Implicits nonApexImplicits := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld").Implicits if len(apexImplicits) != len(nonApexImplicits)+1 { t.Errorf("%q missing unwinder dep", lib) } } } var filesForSdkLibrary = android.MockFS{ "api/current.txt": nil, "api/removed.txt": nil, "api/system-current.txt": nil, "api/system-removed.txt": nil, "api/test-current.txt": nil, "api/test-removed.txt": nil, "100/public/api/foo.txt": nil, "100/public/api/foo-removed.txt": nil, "100/system/api/foo.txt": nil, "100/system/api/foo-removed.txt": nil, // For java_sdk_library_import "a.jar": nil, } func TestJavaSDKLibrary(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", java_libs: ["foo"], updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } java_sdk_library { name: "foo", srcs: ["a.java"], api_packages: ["foo"], apex_available: [ "myapex" ], } prebuilt_apis { name: "sdk", api_dirs: ["100"], } `, withFiles(filesForSdkLibrary)) // java_sdk_library installs both impl jar and permission XML ensureExactContents(t, ctx, "myapex", "android_common_myapex", []string{ "javalib/foo.jar", "etc/permissions/foo.xml", }) // Permission XML should point to the activated path of impl jar of java_sdk_library sdkLibrary := ctx.ModuleForTests("foo.xml", "android_common_myapex").Output("foo.xml") contents := android.ContentFromFileRuleForTests(t, ctx, sdkLibrary) ensureMatches(t, contents, "`) } func TestCannedFsConfig_HasCustomConfig(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", canned_fs_config: "my_config", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", }`) mod := ctx.ModuleForTests("myapex", "android_common_myapex") generateFsRule := mod.Rule("generateFsConfig") cmd := generateFsRule.RuleParams.Command // Ensure that canned_fs_config has "cat my_config" at the end ensureContains(t, cmd, `( echo '/ 1000 1000 0755'; echo '/apex_manifest.json 1000 1000 0644'; echo '/apex_manifest.pb 1000 1000 0644'; cat my_config ) >`) } func TestStubLibrariesMultipleApexViolation(t *testing.T) { testCases := []struct { desc string hasStubs bool apexAvailable string expectedError string }{ { desc: "non-stub library can have multiple apex_available", hasStubs: false, apexAvailable: `["myapex", "otherapex"]`, }, { desc: "stub library should not be available to anyapex", hasStubs: true, apexAvailable: `["//apex_available:anyapex"]`, expectedError: "Stub libraries should have a single apex_available.*anyapex", }, { desc: "stub library should not be available to multiple apexes", hasStubs: true, apexAvailable: `["myapex", "otherapex"]`, expectedError: "Stub libraries should have a single apex_available.*myapex.*otherapex", }, { desc: "stub library can be available to a core apex and a test apex", hasStubs: true, apexAvailable: `["myapex", "test_myapex"]`, }, } bpTemplate := ` cc_library { name: "libfoo", %v apex_available: %v, } apex { name: "myapex", key: "apex.key", updatable: false, native_shared_libs: ["libfoo"], } apex { name: "otherapex", key: "apex.key", updatable: false, } apex_test { name: "test_myapex", key: "apex.key", updatable: false, native_shared_libs: ["libfoo"], } apex_key { name: "apex.key", } ` for _, tc := range testCases { stubs := "" if tc.hasStubs { stubs = `stubs: {symbol_file: "libfoo.map.txt"},` } bp := fmt.Sprintf(bpTemplate, stubs, tc.apexAvailable) mockFsFixturePreparer := android.FixtureModifyMockFS(func(fs android.MockFS) { fs["system/sepolicy/apex/test_myapex-file_contexts"] = nil }) if tc.expectedError == "" { testApex(t, bp, mockFsFixturePreparer) } else { testApexError(t, tc.expectedError, bp, mockFsFixturePreparer) } } } func TestFileSystemShouldSkipApexLibraries(t *testing.T) { context := android.GroupFixturePreparers( android.PrepareForIntegrationTestWithAndroid, cc.PrepareForIntegrationTestWithCc, PrepareForTestWithApexBuildComponents, prepareForTestWithMyapex, filesystem.PrepareForTestWithFilesystemBuildComponents, ) result := context.RunTestWithBp(t, ` android_system_image { name: "myfilesystem", deps: [ "libfoo", ], linker_config_src: "linker.config.json", } cc_library { name: "libfoo", shared_libs: [ "libbar", ], stl: "none", } cc_library { name: "libbar", stl: "none", apex_available: ["myapex"], } apex { name: "myapex", native_shared_libs: ["libbar"], key: "myapex.key", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `) inputs := result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img").Implicits android.AssertStringListDoesNotContain(t, "filesystem should not have libbar", inputs.Strings(), "out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so") } var apex_default_bp = ` apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } filegroup { name: "myapex.manifest", srcs: ["apex_manifest.json"], } filegroup { name: "myapex.androidmanifest", srcs: ["AndroidManifest.xml"], } ` func TestAconfigFilesJavaDeps(t *testing.T) { ctx := testApex(t, apex_default_bp+` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", java_libs: [ "my_java_library_foo", "my_java_library_bar", ], updatable: false, } java_library { name: "my_java_library_foo", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", static_libs: ["my_java_aconfig_library_foo"], apex_available: [ "myapex", ], } java_library { name: "my_java_library_bar", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", static_libs: ["my_java_aconfig_library_bar"], apex_available: [ "myapex", ], } aconfig_declarations { name: "my_aconfig_declarations_foo", package: "com.example.package", container: "myapex", srcs: ["foo.aconfig"], } java_aconfig_library { name: "my_java_aconfig_library_foo", aconfig_declarations: "my_aconfig_declarations_foo", apex_available: [ "myapex", ], } aconfig_declarations { name: "my_aconfig_declarations_bar", package: "com.example.package", container: "myapex", srcs: ["bar.aconfig"], } java_aconfig_library { name: "my_java_aconfig_library_bar", aconfig_declarations: "my_aconfig_declarations_bar", apex_available: [ "myapex", ], } `) mod := ctx.ModuleForTests("myapex", "android_common_myapex") s := mod.Rule("apexRule").Args["copy_commands"] copyCmds := regexp.MustCompile(" *&& *").Split(s, -1) if len(copyCmds) != 8 { t.Fatalf("Expected 5 commands, got %d in:\n%s", len(copyCmds), s) } ensureMatches(t, copyCmds[4], "^cp -f .*/aconfig_flags.pb .*/image.apex/etc$") ensureMatches(t, copyCmds[5], "^cp -f .*/package.map .*/image.apex/etc$") ensureMatches(t, copyCmds[6], "^cp -f .*/flag.map .*/image.apex/etc$") ensureMatches(t, copyCmds[7], "^cp -f .*/flag.val .*/image.apex/etc$") inputs := []string{ "my_aconfig_declarations_foo/intermediate.pb", "my_aconfig_declarations_bar/intermediate.pb", } VerifyAconfigRule(t, &mod, "combine_aconfig_declarations", inputs, "android_common_myapex/aconfig_flags.pb", "", "") VerifyAconfigRule(t, &mod, "create_aconfig_package_map_file", inputs, "android_common_myapex/package.map", "myapex", "package_map") VerifyAconfigRule(t, &mod, "create_aconfig_flag_map_file", inputs, "android_common_myapex/flag.map", "myapex", "flag_map") VerifyAconfigRule(t, &mod, "create_aconfig_flag_val_file", inputs, "android_common_myapex/flag.val", "myapex", "flag_val") } func TestAconfigFilesJavaAndCcDeps(t *testing.T) { ctx := testApex(t, apex_default_bp+` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", java_libs: [ "my_java_library_foo", ], native_shared_libs: [ "my_cc_library_bar", ], binaries: [ "my_cc_binary_baz", ], updatable: false, } java_library { name: "my_java_library_foo", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", static_libs: ["my_java_aconfig_library_foo"], apex_available: [ "myapex", ], } cc_library { name: "my_cc_library_bar", srcs: ["foo/bar/MyClass.cc"], static_libs: [ "my_cc_aconfig_library_bar", "my_cc_aconfig_library_baz", ], apex_available: [ "myapex", ], } cc_binary { name: "my_cc_binary_baz", srcs: ["foo/bar/MyClass.cc"], static_libs: ["my_cc_aconfig_library_baz"], apex_available: [ "myapex", ], } aconfig_declarations { name: "my_aconfig_declarations_foo", package: "com.example.package", container: "myapex", srcs: ["foo.aconfig"], } java_aconfig_library { name: "my_java_aconfig_library_foo", aconfig_declarations: "my_aconfig_declarations_foo", apex_available: [ "myapex", ], } aconfig_declarations { name: "my_aconfig_declarations_bar", package: "com.example.package", container: "myapex", srcs: ["bar.aconfig"], } cc_aconfig_library { name: "my_cc_aconfig_library_bar", aconfig_declarations: "my_aconfig_declarations_bar", apex_available: [ "myapex", ], } aconfig_declarations { name: "my_aconfig_declarations_baz", package: "com.example.package", container: "myapex", srcs: ["baz.aconfig"], } cc_aconfig_library { name: "my_cc_aconfig_library_baz", aconfig_declarations: "my_aconfig_declarations_baz", apex_available: [ "myapex", ], } cc_library { name: "server_configurable_flags", srcs: ["server_configurable_flags.cc"], } cc_library { name: "libbase", srcs: ["libbase.cc"], apex_available: [ "myapex", ], } cc_library { name: "libaconfig_storage_read_api_cc", srcs: ["libaconfig_storage_read_api_cc.cc"], } `) mod := ctx.ModuleForTests("myapex", "android_common_myapex") s := mod.Rule("apexRule").Args["copy_commands"] copyCmds := regexp.MustCompile(" *&& *").Split(s, -1) if len(copyCmds) != 12 { t.Fatalf("Expected 12 commands, got %d in:\n%s", len(copyCmds), s) } ensureMatches(t, copyCmds[8], "^cp -f .*/aconfig_flags.pb .*/image.apex/etc$") ensureMatches(t, copyCmds[9], "^cp -f .*/package.map .*/image.apex/etc$") ensureMatches(t, copyCmds[10], "^cp -f .*/flag.map .*/image.apex/etc$") ensureMatches(t, copyCmds[11], "^cp -f .*/flag.val .*/image.apex/etc$") inputs := []string{ "my_aconfig_declarations_foo/intermediate.pb", "my_cc_library_bar/android_arm64_armv8-a_shared_apex10000/myapex/aconfig_merged.pb", "my_aconfig_declarations_baz/intermediate.pb", } VerifyAconfigRule(t, &mod, "combine_aconfig_declarations", inputs, "android_common_myapex/aconfig_flags.pb", "", "") VerifyAconfigRule(t, &mod, "create_aconfig_package_map_file", inputs, "android_common_myapex/package.map", "myapex", "package_map") VerifyAconfigRule(t, &mod, "create_aconfig_flag_map_file", inputs, "android_common_myapex/flag.map", "myapex", "flag_map") VerifyAconfigRule(t, &mod, "create_aconfig_flag_val_file", inputs, "android_common_myapex/flag.val", "myapex", "flag_val") } func TestAconfigFilesRustDeps(t *testing.T) { ctx := testApex(t, apex_default_bp+` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", native_shared_libs: [ "libmy_rust_library", ], binaries: [ "my_rust_binary", ], rust_dyn_libs: [ "libmy_rust_dylib", ], updatable: false, } rust_library { name: "libflags_rust", // test mock crate_name: "flags_rust", srcs: ["lib.rs"], apex_available: [ "myapex", ], } rust_library { name: "liblazy_static", // test mock crate_name: "lazy_static", srcs: ["src/lib.rs"], apex_available: [ "myapex", ], } rust_library { name: "libaconfig_storage_read_api", // test mock crate_name: "aconfig_storage_read_api", srcs: ["src/lib.rs"], apex_available: [ "myapex", ], } rust_library { name: "liblogger", // test mock crate_name: "logger", srcs: ["src/lib.rs"], apex_available: [ "myapex", ], } rust_library { name: "liblog_rust", // test mock crate_name: "log_rust", srcs: ["src/lib.rs"], apex_available: [ "myapex", ], } rust_ffi_shared { name: "libmy_rust_library", srcs: ["src/lib.rs"], rustlibs: ["libmy_rust_aconfig_library_foo"], crate_name: "my_rust_library", apex_available: [ "myapex", ], } rust_library_dylib { name: "libmy_rust_dylib", srcs: ["foo/bar/MyClass.rs"], rustlibs: ["libmy_rust_aconfig_library_bar"], crate_name: "my_rust_dylib", apex_available: [ "myapex", ], } rust_binary { name: "my_rust_binary", srcs: ["foo/bar/MyClass.rs"], rustlibs: [ "libmy_rust_aconfig_library_baz", "libmy_rust_dylib", ], apex_available: [ "myapex", ], } aconfig_declarations { name: "my_aconfig_declarations_foo", package: "com.example.package", container: "myapex", srcs: ["foo.aconfig"], } aconfig_declarations { name: "my_aconfig_declarations_bar", package: "com.example.package", container: "myapex", srcs: ["bar.aconfig"], } aconfig_declarations { name: "my_aconfig_declarations_baz", package: "com.example.package", container: "myapex", srcs: ["baz.aconfig"], } rust_aconfig_library { name: "libmy_rust_aconfig_library_foo", aconfig_declarations: "my_aconfig_declarations_foo", crate_name: "my_rust_aconfig_library_foo", apex_available: [ "myapex", ], } rust_aconfig_library { name: "libmy_rust_aconfig_library_bar", aconfig_declarations: "my_aconfig_declarations_bar", crate_name: "my_rust_aconfig_library_bar", apex_available: [ "myapex", ], } rust_aconfig_library { name: "libmy_rust_aconfig_library_baz", aconfig_declarations: "my_aconfig_declarations_baz", crate_name: "my_rust_aconfig_library_baz", apex_available: [ "myapex", ], } `) mod := ctx.ModuleForTests("myapex", "android_common_myapex") s := mod.Rule("apexRule").Args["copy_commands"] copyCmds := regexp.MustCompile(" *&& *").Split(s, -1) if len(copyCmds) != 32 { t.Fatalf("Expected 28 commands, got %d in:\n%s", len(copyCmds), s) } ensureMatches(t, copyCmds[28], "^cp -f .*/aconfig_flags.pb .*/image.apex/etc$") ensureMatches(t, copyCmds[29], "^cp -f .*/package.map .*/image.apex/etc$") ensureMatches(t, copyCmds[30], "^cp -f .*/flag.map .*/image.apex/etc$") ensureMatches(t, copyCmds[31], "^cp -f .*/flag.val .*/image.apex/etc$") inputs := []string{ "my_aconfig_declarations_foo/intermediate.pb", "my_aconfig_declarations_bar/intermediate.pb", "my_aconfig_declarations_baz/intermediate.pb", "my_rust_binary/android_arm64_armv8-a_apex10000/myapex/aconfig_merged.pb", } VerifyAconfigRule(t, &mod, "combine_aconfig_declarations", inputs, "android_common_myapex/aconfig_flags.pb", "", "") VerifyAconfigRule(t, &mod, "create_aconfig_package_map_file", inputs, "android_common_myapex/package.map", "myapex", "package_map") VerifyAconfigRule(t, &mod, "create_aconfig_flag_map_file", inputs, "android_common_myapex/flag.map", "myapex", "flag_map") VerifyAconfigRule(t, &mod, "create_aconfig_flag_val_file", inputs, "android_common_myapex/flag.val", "myapex", "flag_val") } func VerifyAconfigRule(t *testing.T, mod *android.TestingModule, desc string, inputs []string, output string, container string, file_type string) { aconfigRule := mod.Description(desc) s := " " + aconfigRule.Args["cache_files"] aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:] if len(aconfigArgs) != len(inputs) { t.Fatalf("Expected %d commands, got %d in:\n%s", len(inputs), len(aconfigArgs), s) } ensureEquals(t, container, aconfigRule.Args["container"]) ensureEquals(t, file_type, aconfigRule.Args["file_type"]) buildParams := aconfigRule.BuildParams for _, input := range inputs { android.EnsureListContainsSuffix(t, aconfigArgs, input) android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), input) } ensureContains(t, buildParams.Output.String(), output) } func TestAconfigFilesOnlyMatchCurrentApex(t *testing.T) { ctx := testApex(t, apex_default_bp+` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", java_libs: [ "my_java_library_foo", "other_java_library_bar", ], updatable: false, } java_library { name: "my_java_library_foo", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", static_libs: ["my_java_aconfig_library_foo"], apex_available: [ "myapex", ], } java_library { name: "other_java_library_bar", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", static_libs: ["other_java_aconfig_library_bar"], apex_available: [ "myapex", ], } aconfig_declarations { name: "my_aconfig_declarations_foo", package: "com.example.package", container: "myapex", srcs: ["foo.aconfig"], } java_aconfig_library { name: "my_java_aconfig_library_foo", aconfig_declarations: "my_aconfig_declarations_foo", apex_available: [ "myapex", ], } aconfig_declarations { name: "other_aconfig_declarations_bar", package: "com.example.package", container: "otherapex", srcs: ["bar.aconfig"], } java_aconfig_library { name: "other_java_aconfig_library_bar", aconfig_declarations: "other_aconfig_declarations_bar", apex_available: [ "myapex", ], } `) mod := ctx.ModuleForTests("myapex", "android_common_myapex") combineAconfigRule := mod.Rule("All_aconfig_declarations_dump") s := " " + combineAconfigRule.Args["cache_files"] aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:] if len(aconfigArgs) != 1 { t.Fatalf("Expected 1 commands, got %d in:\n%s", len(aconfigArgs), s) } android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_foo/intermediate.pb") buildParams := combineAconfigRule.BuildParams if len(buildParams.Inputs) != 1 { t.Fatalf("Expected 1 input, got %d", len(buildParams.Inputs)) } android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_foo/intermediate.pb") ensureContains(t, buildParams.Output.String(), "android_common_myapex/aconfig_flags.pb") } func TestAconfigFilesRemoveDuplicates(t *testing.T) { ctx := testApex(t, apex_default_bp+` apex { name: "myapex", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", java_libs: [ "my_java_library_foo", "my_java_library_bar", ], updatable: false, } java_library { name: "my_java_library_foo", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", static_libs: ["my_java_aconfig_library_foo"], apex_available: [ "myapex", ], } java_library { name: "my_java_library_bar", srcs: ["foo/bar/MyClass.java"], sdk_version: "none", system_modules: "none", static_libs: ["my_java_aconfig_library_bar"], apex_available: [ "myapex", ], } aconfig_declarations { name: "my_aconfig_declarations_foo", package: "com.example.package", container: "myapex", srcs: ["foo.aconfig"], } java_aconfig_library { name: "my_java_aconfig_library_foo", aconfig_declarations: "my_aconfig_declarations_foo", apex_available: [ "myapex", ], } java_aconfig_library { name: "my_java_aconfig_library_bar", aconfig_declarations: "my_aconfig_declarations_foo", apex_available: [ "myapex", ], } `) mod := ctx.ModuleForTests("myapex", "android_common_myapex") combineAconfigRule := mod.Rule("All_aconfig_declarations_dump") s := " " + combineAconfigRule.Args["cache_files"] aconfigArgs := regexp.MustCompile(" --cache ").Split(s, -1)[1:] if len(aconfigArgs) != 1 { t.Fatalf("Expected 1 commands, got %d in:\n%s", len(aconfigArgs), s) } android.EnsureListContainsSuffix(t, aconfigArgs, "my_aconfig_declarations_foo/intermediate.pb") buildParams := combineAconfigRule.BuildParams if len(buildParams.Inputs) != 1 { t.Fatalf("Expected 1 input, got %d", len(buildParams.Inputs)) } android.EnsureListContainsSuffix(t, buildParams.Inputs.Strings(), "my_aconfig_declarations_foo/intermediate.pb") ensureContains(t, buildParams.Output.String(), "android_common_myapex/aconfig_flags.pb") } // Test that the boot jars come from the _selected_ apex prebuilt // RELEASE_APEX_CONTIRBUTIONS_* build flags will be used to select the correct prebuilt for a specific release config func TestBootDexJarsMultipleApexPrebuilts(t *testing.T) { checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) { t.Helper() s := ctx.ModuleForTests("dex_bootjars", "android_common") foundLibfooJar := false base := stem + ".jar" for _, output := range s.AllOutputs() { if filepath.Base(output) == base { foundLibfooJar = true buildRule := s.Output(output) android.AssertStringEquals(t, "boot dex jar path", bootDexJarPath, buildRule.Input.String()) } } if !foundLibfooJar { t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs %q", android.StringPathsRelativeToTop(ctx.Config().SoongOutDir(), s.AllOutputs())) } } // Check that the boot jars of the selected apex are run through boot_jars_package_check // This validates that the jars on the bootclasspath do not contain packages outside an allowlist checkBootJarsPackageCheck := func(t *testing.T, ctx *android.TestContext, expectedBootJar string) { platformBcp := ctx.ModuleForTests("platform-bootclasspath", "android_common") bootJarsCheckRule := platformBcp.Rule("boot_jars_package_check") android.AssertStringMatches(t, "Could not find the correct boot dex jar in package check rule", bootJarsCheckRule.RuleParams.Command, "build/soong/scripts/check_boot_jars/package_allowed_list.txt.*"+expectedBootJar) } // Check that the boot jars used to generate the monolithic hiddenapi flags come from the selected apex checkBootJarsForMonolithicHiddenapi := func(t *testing.T, ctx *android.TestContext, expectedBootJar string) { monolithicHiddenapiFlagsCmd := ctx.ModuleForTests("platform-bootclasspath", "android_common").Output("out/soong/hiddenapi/hiddenapi-stub-flags.txt").RuleParams.Command android.AssertStringMatches(t, "Could not find the correct boot dex jar in monolithic hiddenapi flags generation command", monolithicHiddenapiFlagsCmd, "--boot-dex="+expectedBootJar) } bp := ` // Source APEX. java_library { name: "framework-foo", srcs: ["foo.java"], installable: true, apex_available: [ "com.android.foo", ], } bootclasspath_fragment { name: "foo-bootclasspath-fragment", contents: ["framework-foo"], apex_available: [ "com.android.foo", ], hidden_api: { split_packages: ["*"], }, } apex_key { name: "com.android.foo.key", public_key: "com.android.foo.avbpubkey", private_key: "com.android.foo.pem", } apex { name: "com.android.foo", key: "com.android.foo.key", bootclasspath_fragments: ["foo-bootclasspath-fragment"], updatable: false, } // Prebuilt APEX. java_sdk_library_import { name: "framework-foo", public: { jars: ["foo.jar"], }, apex_available: ["com.android.foo"], shared_library: false, } prebuilt_bootclasspath_fragment { name: "foo-bootclasspath-fragment", contents: ["framework-foo"], hidden_api: { annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv", metadata: "my-bootclasspath-fragment/metadata.csv", index: "my-bootclasspath-fragment/index.csv", stub_flags: "my-bootclasspath-fragment/stub-flags.csv", all_flags: "my-bootclasspath-fragment/all-flags.csv", }, apex_available: [ "com.android.foo", ], } prebuilt_apex { name: "com.android.foo", apex_name: "com.android.foo", src: "com.android.foo-arm.apex", exported_bootclasspath_fragments: ["foo-bootclasspath-fragment"], } // Another Prebuilt ART APEX prebuilt_apex { name: "com.android.foo.v2", apex_name: "com.android.foo", // Used to determine the API domain src: "com.android.foo-arm.apex", exported_bootclasspath_fragments: ["foo-bootclasspath-fragment"], } // APEX contribution modules apex_contributions { name: "foo.source.contributions", api_domain: "com.android.foo", contents: ["com.android.foo"], } apex_contributions { name: "foo.prebuilt.contributions", api_domain: "com.android.foo", contents: ["prebuilt_com.android.foo"], } apex_contributions { name: "foo.prebuilt.v2.contributions", api_domain: "com.android.foo", contents: ["com.android.foo.v2"], // prebuilt_ prefix is missing because of prebuilt_rename mutator } ` testCases := []struct { desc string selectedApexContributions string expectedBootJar string }{ { desc: "Source apex com.android.foo is selected, bootjar should come from source java library", selectedApexContributions: "foo.source.contributions", expectedBootJar: "out/soong/.intermediates/foo-bootclasspath-fragment/android_common_apex10000/hiddenapi-modular/encoded/framework-foo.jar", }, { desc: "Prebuilt apex prebuilt_com.android.foo is selected, profile should come from .prof deapexed from the prebuilt", selectedApexContributions: "foo.prebuilt.contributions", expectedBootJar: "out/soong/.intermediates/prebuilt_com.android.foo.deapexer/android_common/deapexer/javalib/framework-foo.jar", }, { desc: "Prebuilt apex prebuilt_com.android.foo.v2 is selected, profile should come from .prof deapexed from the prebuilt", selectedApexContributions: "foo.prebuilt.v2.contributions", expectedBootJar: "out/soong/.intermediates/prebuilt_com.android.foo.v2.deapexer/android_common/deapexer/javalib/framework-foo.jar", }, } fragment := java.ApexVariantReference{ Apex: proptools.StringPtr("com.android.foo"), Module: proptools.StringPtr("foo-bootclasspath-fragment"), } for _, tc := range testCases { preparer := android.GroupFixturePreparers( java.FixtureConfigureApexBootJars("com.android.foo:framework-foo"), android.FixtureMergeMockFs(map[string][]byte{ "system/sepolicy/apex/com.android.foo-file_contexts": nil, }), // Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi // file creation. java.FixtureConfigureBootJars("platform:foo"), android.FixtureModifyMockFS(func(fs android.MockFS) { fs["platform/Android.bp"] = []byte(` java_library { name: "foo", srcs: ["Test.java"], compile_dex: true, } `) fs["platform/Test.java"] = nil }), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.BuildFlags = map[string]string{ "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": tc.selectedApexContributions, } }), ) ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment) checkBootDexJarPath(t, ctx, "framework-foo", tc.expectedBootJar) checkBootJarsPackageCheck(t, ctx, tc.expectedBootJar) checkBootJarsForMonolithicHiddenapi(t, ctx, tc.expectedBootJar) } } // Test that product packaging installs the selected mainline module (either source or a specific prebuilt) // RELEASE_APEX_CONTIRBUTIONS_* build flags will be used to select the correct prebuilt for a specific release config func TestInstallationRulesForMultipleApexPrebuilts(t *testing.T) { // check that the LOCAL_MODULE in the generated mk file matches the name used in PRODUCT_PACKAGES // Since the name used in PRODUCT_PACKAGES does not contain prebuilt_ prefix, LOCAL_MODULE should not contain any prefix either checkLocalModuleName := func(t *testing.T, ctx *android.TestContext, soongApexModuleName string, expectedLocalModuleName string) { // Variations are created based on apex_name entries := android.AndroidMkEntriesForTest(t, ctx, ctx.ModuleForTests(soongApexModuleName, "android_common_com.android.foo").Module()) android.AssertStringEquals(t, "LOCAL_MODULE of the prebuilt apex must match the name listed in PRODUCT_PACKAGES", expectedLocalModuleName, entries[0].EntryMap["LOCAL_MODULE"][0]) } // for a mainline module family, check that only the flagged soong module is visible to make checkHideFromMake := func(t *testing.T, ctx *android.TestContext, visibleModuleName string, hiddenModuleNames []string) { variation := func(moduleName string) string { ret := "android_common_com.android.foo" if moduleName == "com.google.android.foo" { ret = "android_common_com.google.android.foo_com.google.android.foo" } return ret } visibleModule := ctx.ModuleForTests(visibleModuleName, variation(visibleModuleName)).Module() android.AssertBoolEquals(t, "Apex "+visibleModuleName+" selected using apex_contributions should be visible to make", false, visibleModule.IsHideFromMake()) for _, hiddenModuleName := range hiddenModuleNames { hiddenModule := ctx.ModuleForTests(hiddenModuleName, variation(hiddenModuleName)).Module() android.AssertBoolEquals(t, "Apex "+hiddenModuleName+" not selected using apex_contributions should be hidden from make", true, hiddenModule.IsHideFromMake()) } } bp := ` apex_key { name: "com.android.foo.key", public_key: "com.android.foo.avbpubkey", private_key: "com.android.foo.pem", } // AOSP source apex apex { name: "com.android.foo", key: "com.android.foo.key", updatable: false, } // Google source apex override_apex { name: "com.google.android.foo", base: "com.android.foo", key: "com.android.foo.key", } // Prebuilt Google APEX. prebuilt_apex { name: "com.google.android.foo", apex_name: "com.android.foo", src: "com.android.foo-arm.apex", prefer: true, // prefer is set to true on both the prebuilts to induce an error if flagging is not present } // Another Prebuilt Google APEX prebuilt_apex { name: "com.google.android.foo.v2", apex_name: "com.android.foo", source_apex_name: "com.google.android.foo", // source_apex_name becomes LOCAL_MODULE in the generated mk file src: "com.android.foo-arm.apex", prefer: true, // prefer is set to true on both the prebuilts to induce an error if flagging is not present } // APEX contribution modules apex_contributions { name: "foo.source.contributions", api_domain: "com.android.foo", contents: ["com.google.android.foo"], } apex_contributions { name: "foo.prebuilt.contributions", api_domain: "com.android.foo", contents: ["prebuilt_com.google.android.foo"], } apex_contributions { name: "foo.prebuilt.v2.contributions", api_domain: "com.android.foo", contents: ["prebuilt_com.google.android.foo.v2"], } // This is an incompatible module because it selects multiple versions of the same mainline module apex_contributions { name: "foo.prebuilt.duplicate.contributions", api_domain: "com.android.foo", contents: [ "prebuilt_com.google.android.foo", "prebuilt_com.google.android.foo.v2", ], } ` testCases := []struct { desc string selectedApexContributions string expectedVisibleModuleName string expectedHiddenModuleNames []string expectedError string }{ { desc: "Source apex is selected, prebuilts should be hidden from make", selectedApexContributions: "foo.source.contributions", expectedVisibleModuleName: "com.google.android.foo", expectedHiddenModuleNames: []string{"prebuilt_com.google.android.foo", "prebuilt_com.google.android.foo.v2"}, }, { desc: "Prebuilt apex prebuilt_com.android.foo is selected, source and the other prebuilt should be hidden from make", selectedApexContributions: "foo.prebuilt.contributions", expectedVisibleModuleName: "prebuilt_com.google.android.foo", expectedHiddenModuleNames: []string{"com.google.android.foo", "prebuilt_com.google.android.foo.v2"}, }, { desc: "Prebuilt apex prebuilt_com.android.fooi.v2 is selected, source and the other prebuilt should be hidden from make", selectedApexContributions: "foo.prebuilt.v2.contributions", expectedVisibleModuleName: "prebuilt_com.google.android.foo.v2", expectedHiddenModuleNames: []string{"com.google.android.foo", "prebuilt_com.google.android.foo"}, }, { desc: "Multiple versions of a prebuilt apex is selected in the same release config", selectedApexContributions: "foo.prebuilt.duplicate.contributions", expectedError: "Found duplicate variations of the same module in apex_contributions: prebuilt_com.google.android.foo and prebuilt_com.google.android.foo.v2", }, } for _, tc := range testCases { preparer := android.GroupFixturePreparers( android.FixtureMergeMockFs(map[string][]byte{ "system/sepolicy/apex/com.android.foo-file_contexts": nil, }), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.BuildFlags = map[string]string{ "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": tc.selectedApexContributions, } }), ) if tc.expectedError != "" { preparer = preparer.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(tc.expectedError)) testApex(t, bp, preparer) return } ctx := testApex(t, bp, preparer) // Check that the LOCAL_MODULE of the two prebuilts is com.android.foo // This ensures that product packaging can pick them for installation if it has been flagged by apex_contributions checkLocalModuleName(t, ctx, "prebuilt_com.google.android.foo", "com.google.android.foo") checkLocalModuleName(t, ctx, "prebuilt_com.google.android.foo.v2", "com.google.android.foo") // Check that // 1. The contents of the selected apex_contributions are visible to make // 2. The rest of the apexes in the mainline module family (source or other prebuilt) is hidden from make checkHideFromMake(t, ctx, tc.expectedVisibleModuleName, tc.expectedHiddenModuleNames) } } func TestAconfifDeclarationsValidation(t *testing.T) { aconfigDeclarationLibraryString := func(moduleNames []string) (ret string) { for _, moduleName := range moduleNames { ret += fmt.Sprintf(` aconfig_declarations { name: "%[1]s", package: "com.example.package", container: "system", srcs: [ "%[1]s.aconfig", ], } java_aconfig_library { name: "%[1]s-lib", aconfig_declarations: "%[1]s", } `, moduleName) } return ret } result := android.GroupFixturePreparers( prepareForApexTest, java.PrepareForTestWithJavaSdkLibraryFiles, java.FixtureWithLastReleaseApis("foo"), android.FixtureModifyConfig(func(config android.Config) { config.SetApiLibraries([]string{"foo"}) }), ).RunTestWithBp(t, ` java_library { name: "baz-java-lib", static_libs: [ "baz-lib", ], } filegroup { name: "qux-filegroup", srcs: [ ":qux-lib{.generated_srcjars}", ], } filegroup { name: "qux-another-filegroup", srcs: [ ":qux-filegroup", ], } java_library { name: "quux-java-lib", srcs: [ "a.java", ], libs: [ "quux-lib", ], } java_sdk_library { name: "foo", srcs: [ ":qux-another-filegroup", ], api_packages: ["foo"], system: { enabled: true, }, module_lib: { enabled: true, }, test: { enabled: true, }, static_libs: [ "bar-lib", ], libs: [ "baz-java-lib", "quux-java-lib", ], aconfig_declarations: [ "bar", ], } `+aconfigDeclarationLibraryString([]string{"bar", "baz", "qux", "quux"})) m := result.ModuleForTests("foo.stubs.source", "android_common") outDir := "out/soong/.intermediates" // Arguments passed to aconfig to retrieve the state of the flags defined in the // textproto files aconfigFlagArgs := m.Output("released-flagged-apis-exportable.txt").Args["flags_path"] // "bar-lib" is a static_lib of "foo" and is passed to metalava as classpath. Thus the // cache file provided by the associated aconfig_declarations module "bar" should be passed // to aconfig. android.AssertStringDoesContain(t, "cache file of a java_aconfig_library static_lib "+ "passed as an input", aconfigFlagArgs, fmt.Sprintf("%s/%s/intermediate.pb", outDir, "bar")) // "baz-java-lib", which statically depends on "baz-lib", is a lib of "foo" and is passed // to metalava as classpath. Thus the cache file provided by the associated // aconfig_declarations module "baz" should be passed to aconfig. android.AssertStringDoesContain(t, "cache file of a lib that statically depends on "+ "java_aconfig_library passed as an input", aconfigFlagArgs, fmt.Sprintf("%s/%s/intermediate.pb", outDir, "baz")) // "qux-lib" is passed to metalava as src via the filegroup, thus the cache file provided by // the associated aconfig_declarations module "qux" should be passed to aconfig. android.AssertStringDoesContain(t, "cache file of srcs java_aconfig_library passed as an "+ "input", aconfigFlagArgs, fmt.Sprintf("%s/%s/intermediate.pb", outDir, "qux")) // "quux-java-lib" is a lib of "foo" and is passed to metalava as classpath, but does not // statically depend on "quux-lib". Therefore, the cache file provided by the associated // aconfig_declarations module "quux" should not be passed to aconfig. android.AssertStringDoesNotContain(t, "cache file of a lib that does not statically "+ "depend on java_aconfig_library not passed as an input", aconfigFlagArgs, fmt.Sprintf("%s/%s/intermediate.pb", outDir, "quux")) } func TestMultiplePrebuiltsWithSameBase(t *testing.T) { ctx := testApex(t, ` apex { name: "myapex", key: "myapex.key", prebuilts: ["myetc", "myetc2"], min_sdk_version: "29", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } prebuilt_etc { name: "myetc", src: "myprebuilt", filename: "myfilename", } prebuilt_etc { name: "myetc2", sub_dir: "mysubdir", src: "myprebuilt", filename: "myfilename", } `, withFiles(android.MockFS{ "packages/modules/common/build/allowed_deps.txt": nil, })) ab := ctx.ModuleForTests("myapex", "android_common_myapex").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, ctx, ab) var builder strings.Builder data.Custom(&builder, ab.BaseModuleName(), "TARGET_", "", data) androidMk := builder.String() android.AssertStringDoesContain(t, "not found", androidMk, "LOCAL_MODULE := etc_myfilename.myapex") android.AssertStringDoesContain(t, "not found", androidMk, "LOCAL_MODULE := etc_mysubdir_myfilename.myapex") } func TestApexMinSdkVersionOverride(t *testing.T) { checkMinSdkVersion := func(t *testing.T, module android.TestingModule, expectedMinSdkVersion string) { args := module.Rule("apexRule").Args optFlags := args["opt_flags"] if !strings.Contains(optFlags, "--min_sdk_version "+expectedMinSdkVersion) { t.Errorf("%s: Expected min_sdk_version=%s, got: %s", module.Module(), expectedMinSdkVersion, optFlags) } } checkHasDep := func(t *testing.T, ctx *android.TestContext, m android.Module, wantDep android.Module) { t.Helper() found := false ctx.VisitDirectDeps(m, func(dep blueprint.Module) { if dep == wantDep { found = true } }) if !found { t.Errorf("Could not find a dependency from %v to %v\n", m, wantDep) } } ctx := testApex(t, ` apex { name: "com.android.apex30", min_sdk_version: "30", key: "apex30.key", java_libs: ["javalib"], } java_library { name: "javalib", srcs: ["A.java"], apex_available: ["com.android.apex30"], min_sdk_version: "30", sdk_version: "current", } override_apex { name: "com.mycompany.android.apex30", base: "com.android.apex30", } override_apex { name: "com.mycompany.android.apex31", base: "com.android.apex30", min_sdk_version: "31", } apex_key { name: "apex30.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `, android.FixtureMergeMockFs(android.MockFS{ "system/sepolicy/apex/com.android.apex30-file_contexts": nil, }), ) baseModule := ctx.ModuleForTests("com.android.apex30", "android_common_com.android.apex30") checkMinSdkVersion(t, baseModule, "30") // Override module, but uses same min_sdk_version overridingModuleSameMinSdkVersion := ctx.ModuleForTests("com.android.apex30", "android_common_com.mycompany.android.apex30_com.mycompany.android.apex30") javalibApex30Variant := ctx.ModuleForTests("javalib", "android_common_apex30") checkMinSdkVersion(t, overridingModuleSameMinSdkVersion, "30") checkHasDep(t, ctx, overridingModuleSameMinSdkVersion.Module(), javalibApex30Variant.Module()) // Override module, uses different min_sdk_version overridingModuleDifferentMinSdkVersion := ctx.ModuleForTests("com.android.apex30", "android_common_com.mycompany.android.apex31_com.mycompany.android.apex31") javalibApex31Variant := ctx.ModuleForTests("javalib", "android_common_apex31") checkMinSdkVersion(t, overridingModuleDifferentMinSdkVersion, "31") checkHasDep(t, ctx, overridingModuleDifferentMinSdkVersion.Module(), javalibApex31Variant.Module()) } func TestOverrideApexWithPrebuiltApexPreferred(t *testing.T) { context := android.GroupFixturePreparers( android.PrepareForIntegrationTestWithAndroid, PrepareForTestWithApexBuildComponents, android.FixtureMergeMockFs(android.MockFS{ "system/sepolicy/apex/foo-file_contexts": nil, }), ) res := context.RunTestWithBp(t, ` apex { name: "foo", key: "myapex.key", apex_available_name: "com.android.foo", variant_version: "0", updatable: false, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } prebuilt_apex { name: "foo", src: "foo.apex", prefer: true, } override_apex { name: "myoverrideapex", base: "foo", } `) java.CheckModuleHasDependency(t, res.TestContext, "myoverrideapex", "android_common_myoverrideapex_myoverrideapex", "foo") }