// 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 ( "io/ioutil" "os" "path" "reflect" "regexp" "sort" "strings" "testing" "github.com/google/blueprint/proptools" "android/soong/android" "android/soong/cc" "android/soong/dexpreopt" prebuilt_etc "android/soong/etc" "android/soong/java" "android/soong/sh" ) var buildDir string // 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, handlers ...testCustomizer) { t.Helper() ctx, config := testApexContext(t, bp, handlers...) _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) if len(errs) > 0 { android.FailIfNoMatchingErrors(t, pattern, errs) return } _, errs = ctx.PrepareBuildActions(config) if len(errs) > 0 { android.FailIfNoMatchingErrors(t, pattern, errs) return } t.Fatalf("missing expected error %q (0 errors are returned)", pattern) } func testApex(t *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) { t.Helper() ctx, config := testApexContext(t, bp, handlers...) _, errs := ctx.ParseBlueprintsFiles(".") android.FailIfErrored(t, errs) _, errs = ctx.PrepareBuildActions(config) android.FailIfErrored(t, errs) return ctx, config } type testCustomizer func(fs map[string][]byte, config android.Config) func withFiles(files map[string][]byte) testCustomizer { return func(fs map[string][]byte, config android.Config) { for k, v := range files { fs[k] = v } } } func withTargets(targets map[android.OsType][]android.Target) testCustomizer { return func(fs map[string][]byte, 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) func withNativeBridgeEnabled(_ map[string][]byte, 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) testCustomizer { return func(fs map[string][]byte, config android.Config) { config.TestProductVariables.ManifestPackageNameOverrides = specs } } func withBinder32bit(_ map[string][]byte, config android.Config) { config.TestProductVariables.Binder32bit = proptools.BoolPtr(true) } func withUnbundledBuild(_ map[string][]byte, config android.Config) { config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true) } func testApexContext(_ *testing.T, bp string, handlers ...testCustomizer) (*android.TestContext, android.Config) { android.ClearApexDependency() bp = bp + ` filegroup { name: "myapex-file_contexts", srcs: [ "system/sepolicy/apex/myapex-file_contexts", ], } ` bp = bp + cc.GatherRequiredDepsForTest(android.Android) bp = bp + java.GatherRequiredDepsForTest() fs := map[string][]byte{ "a.java": nil, "PrebuiltAppFoo.apk": nil, "PrebuiltAppFooPriv.apk": nil, "build/make/target/product/security": nil, "apex_manifest.json": nil, "AndroidManifest.xml": nil, "system/sepolicy/apex/myapex-file_contexts": 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/commonapex-file_contexts": nil, "system/sepolicy/apex/com.android.vndk-file_contexts": nil, "mylib.cpp": nil, "mylib_common.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, "build/make/core/proguard.flags": nil, "build/make/core/proguard_basic_keeps.flags": nil, "dummy.txt": nil, "baz": nil, "bar/baz": nil, "testdata/baz": nil, "AppSet.apks": nil, } cc.GatherRequiredFilesForTest(fs) for _, handler := range handlers { // The fs now needs to be populated before creating the config, call handlers twice // for now, once to get any fs changes, and later after the config was created to // set product variables or targets. tempConfig := android.TestArchConfig(buildDir, nil, bp, fs) handler(fs, tempConfig) } config := android.TestArchConfig(buildDir, nil, bp, fs) config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current") config.TestProductVariables.DefaultAppCertificate = proptools.StringPtr("vendor/foo/devkeys/test") config.TestProductVariables.CertificateOverrides = []string{"myapex_keytest:myapex.certificate.override"} config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("Q") config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(false) config.TestProductVariables.Platform_version_active_codenames = []string{"R"} config.TestProductVariables.Platform_vndk_version = proptools.StringPtr("VER") for _, handler := range handlers { // The fs now needs to be populated before creating the config, call handlers twice // for now, earlier to get any fs changes, and now after the config was created to // set product variables or targets. tempFS := map[string][]byte{} handler(tempFS, config) } ctx := android.NewTestArchContext() // from android package android.RegisterPackageBuildComponents(ctx) ctx.PreArchMutators(android.RegisterVisibilityRuleChecker) ctx.RegisterModuleType("apex", BundleFactory) ctx.RegisterModuleType("apex_test", testApexBundleFactory) ctx.RegisterModuleType("apex_vndk", vndkApexBundleFactory) ctx.RegisterModuleType("apex_key", ApexKeyFactory) ctx.RegisterModuleType("apex_defaults", defaultsFactory) ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory) ctx.RegisterModuleType("override_apex", overrideApexFactory) ctx.RegisterModuleType("apex_set", apexSetFactory) ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators) ctx.PreArchMutators(android.RegisterComponentsMutator) ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators) android.RegisterPrebuiltMutators(ctx) // Register these after the prebuilt mutators have been registered to match what // happens at runtime. ctx.PreArchMutators(android.RegisterVisibilityRuleGatherer) ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer) cc.RegisterRequiredBuildComponentsForTest(ctx) ctx.RegisterModuleType("cc_test", cc.TestFactory) ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory) ctx.RegisterModuleType("vndk_libraries_txt", cc.VndkLibrariesTxtFactory) ctx.RegisterModuleType("prebuilt_etc", prebuilt_etc.PrebuiltEtcFactory) ctx.RegisterModuleType("platform_compat_config", java.PlatformCompatConfigFactory) ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory) ctx.RegisterModuleType("filegroup", android.FileGroupFactory) java.RegisterJavaBuildComponents(ctx) java.RegisterSystemModulesBuildComponents(ctx) java.RegisterAppBuildComponents(ctx) java.RegisterSdkLibraryBuildComponents(ctx) ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory) ctx.PreDepsMutators(RegisterPreDepsMutators) ctx.PostDepsMutators(RegisterPostDepsMutators) ctx.Register(config) return ctx, config } func setUp() { var err error buildDir, err = ioutil.TempDir("", "soong_apex_test") if err != nil { panic(err) } } func tearDown() { _ = os.RemoveAll(buildDir) } // 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) } } // Minimal test func TestBasicApex(t *testing.T) { ctx, config := testApex(t, ` apex_defaults { name: "myapex-defaults", manifest: ":myapex.manifest", androidManifest: ":myapex.androidmanifest", key: "myapex.key", native_shared_libs: ["mylib"], multilib: { both: { binaries: ["foo",], } }, java_libs: [ "myjar", "myjar_dex", ], } 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"], 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: [], static_executable: true, stl: "none", apex_available: [ "myapex" ], } cc_library_shared { name: "mylib2", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", notice: "custom_notice", 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", notice: "custom_notice_for_static_lib", // 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_image").Rule("apexRule") // Make sure that Android.mk is created ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, config, "", 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 "+buildDir+"/.intermediates/myapex/android_common_myapex_image/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_myapex") ensureListContains(t, ctx.ModuleVariantsForTests("myjar"), "android_common_myapex") ensureListContains(t, ctx.ModuleVariantsForTests("myjar_dex"), "android_common_myapex") // Ensure that apex variant is created for the indirect dep ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_myapex") ensureListContains(t, ctx.ModuleVariantsForTests("myotherjar"), "android_common_myapex") // 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") // .. 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) } mergeNoticesRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("mergeNoticesRule") noticeInputs := mergeNoticesRule.Inputs.Strings() if len(noticeInputs) != 3 { t.Errorf("number of input notice files: expected = 3, actual = %q", len(noticeInputs)) } ensureListContains(t, noticeInputs, "NOTICE") ensureListContains(t, noticeInputs, "custom_notice") ensureListContains(t, noticeInputs, "custom_notice_for_static_lib") fullDepsInfo := strings.Split(ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("depsinfo/fulllist.txt").Args["content"], "\\n") ensureListContains(t, fullDepsInfo, "myjar(minSdkVersion:(no version)) <- myapex") ensureListContains(t, fullDepsInfo, "mylib(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(ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("depsinfo/flatlist.txt").Args["content"], "\\n") ensureListContains(t, flatDepsInfo, " myjar(minSdkVersion:(no version))") ensureListContains(t, flatDepsInfo, " mylib(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"], } 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", } `) ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ "etc/myetc", "javalib/myjar.jar", "lib64/mylib.so", "app/AppFoo/AppFoo.apk", "overlay/blue/rro.apk", }) } func TestApexManifest(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `) module := ctx.ModuleForTests("myapex", "android_common_myapex_image") 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 TestBasicZipApex(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", payload_type: "zip", native_shared_libs: ["mylib"], } 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"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } `) zipApexRule := ctx.ModuleForTests("myapex", "android_common_myapex_zip").Rule("zipApexRule") copyCmds := zipApexRule.Args["copy_commands"] // Ensure that main rule creates an output ensureContains(t, zipApexRule.Output.String(), "myapex.zipapex.unsigned") // Ensure that APEX variant is created for the direct dep ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_myapex") // Ensure that APEX variant is created for the indirect dep ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_myapex") // Ensure that both direct and indirect deps are copied into apex ensureContains(t, copyCmds, "image.zipapex/lib64/mylib.so") ensureContains(t, copyCmds, "image.zipapex/lib64/mylib2.so") } func TestApexWithStubs(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib", "mylib3"], } 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" ], } `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").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_myapex").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_3/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_myapex/mylib3.so") // .. and not linking to the stubs variant of mylib3 ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_12_myapex/mylib3.so") // 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 is invoked with --apex ensureContains(t, "--apex", ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_3").Rule("genStubSrc").Args["flags"]) ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ "lib64/mylib.so", "lib64/mylib3.so", "lib64/mylib4.so", }) } func TestApexWithExplicitStubsDependency(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex2", key: "myapex2.key", native_shared_libs: ["mylib"], } 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_image").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_myapex2").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(ctx.ModuleForTests("myapex2", "android_common_myapex2_image").Output("depsinfo/fulllist.txt").Args["content"], "\\n") ensureListContains(t, fullDepsInfo, "mylib(minSdkVersion:(no version)) <- myapex2") ensureListContains(t, fullDepsInfo, "libbaz(minSdkVersion:(no version)) <- mylib") ensureListContains(t, fullDepsInfo, "libfoo(minSdkVersion:(no version)) (external) <- mylib") flatDepsInfo := strings.Split(ctx.ModuleForTests("myapex2", "android_common_myapex2_image").Output("depsinfo/flatlist.txt").Args["content"], "\\n") ensureListContains(t, flatDepsInfo, " mylib(minSdkVersion:(no version))") ensureListContains(t, flatDepsInfo, " libbaz(minSdkVersion:(no version))") 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"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], 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" ], } `) apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").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") apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule") ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"])) ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.so") } func TestRuntimeApexShouldInstallHwasanIfLibcDependsOnIt(t *testing.T) { ctx, _ := testApex(t, "", func(fs map[string][]byte, config android.Config) { bp := ` apex { name: "com.android.runtime", key: "com.android.runtime.key", native_shared_libs: ["libc"], } apex_key { name: "com.android.runtime.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libc", no_libcrt: true, nocrt: 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-aarch64-android", no_libcrt: true, nocrt: true, stl: "none", system_shared_libs: [], srcs: [""], stubs: { versions: ["1"] }, sanitize: { never: true, }, } ` // override bp to use hard-coded names: com.android.runtime and libc fs["Android.bp"] = []byte(bp) fs["system/sepolicy/apex/com.android.runtime-file_contexts"] = nil }) ensureExactContents(t, ctx, "com.android.runtime", "android_common_hwasan_com.android.runtime_image", []string{ "lib64/bionic/libc.so", "lib64/bionic/libclang_rt.hwasan-aarch64-android.so", }) hwasan := ctx.ModuleForTests("libclang_rt.hwasan-aarch64-android", "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) { ctx, _ := testApex(t, "", func(fs map[string][]byte, config android.Config) { bp := ` apex { name: "com.android.runtime", key: "com.android.runtime.key", native_shared_libs: ["libc"], } apex_key { name: "com.android.runtime.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libc", no_libcrt: true, nocrt: true, stl: "none", system_shared_libs: [], stubs: { versions: ["1"] }, apex_available: ["com.android.runtime"], } cc_prebuilt_library_shared { name: "libclang_rt.hwasan-aarch64-android", no_libcrt: true, nocrt: true, stl: "none", system_shared_libs: [], srcs: [""], stubs: { versions: ["1"] }, sanitize: { never: true, }, } ` // override bp to use hard-coded names: com.android.runtime and libc fs["Android.bp"] = []byte(bp) fs["system/sepolicy/apex/com.android.runtime-file_contexts"] = nil config.TestProductVariables.SanitizeDevice = []string{"hwaddress"} }) ensureExactContents(t, ctx, "com.android.runtime", "android_common_hwasan_com.android.runtime_image", []string{ "lib64/bionic/libc.so", "lib64/bionic/libclang_rt.hwasan-aarch64-android.so", }) hwasan := ctx.ModuleForTests("libclang_rt.hwasan-aarch64-android", "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 shouldLink string shouldNotLink []string }{ { name: "should link to the latest", minSdkVersion: "", shouldLink: "30", shouldNotLink: []string{"29"}, }, { name: "should link to llndk#29", minSdkVersion: "min_sdk_version: \"29\",", shouldLink: "29", shouldNotLink: []string{"30"}, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", use_vendor: true, native_shared_libs: ["mylib"], `+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_library { name: "libbar", symbol_file: "", } `, func(fs map[string][]byte, config android.Config) { setUseVendorAllowListForTest(config, []string{"myapex"}) }, withUnbundledBuild) // Ensure that LLNDK dep is not included ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ "lib64/mylib.so", }) // Ensure that LLNDK dep is required apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule") ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"])) ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libbar.so") mylibLdFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_shared_myapex").Rule("ld").Args["libFlags"] ensureContains(t, mylibLdFlags, "libbar.llndk/android_vendor.VER_arm64_armv8-a_shared_"+tc.shouldLink+"/libbar.so") for _, ver := range tc.shouldNotLink { ensureNotContains(t, mylibLdFlags, "libbar.llndk/android_vendor.VER_arm64_armv8-a_shared_"+ver+"/libbar.so") } mylibCFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"] ensureContains(t, mylibCFlags, "__LIBBAR_API__="+tc.shouldLink) }) } } func TestApexWithSystemLibsStubs(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib", "mylib_shared", "libdl", "libm"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], 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_image").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_myapex").Rule("ld").Args["libFlags"] mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"] mylibSharedCFlags := ctx.ModuleForTests("mylib_shared", "android_arm64_armv8-a_shared_myapex").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_29/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__=29") ensureContains(t, mylibSharedCFlags, "__LIBC_API__=29") // For dependency to libm // Ensure that mylib is linking with the non-stub (impl) variant ensureContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_shared_myapex/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_myapex/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 #29 link, but fallback to #28 // 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 #28 expectLink("liba", "shared_myapex", "libz", "shared_28") expectNoLink("liba", "shared_myapex", "libz", "shared_30") expectNoLink("liba", "shared_myapex", "libz", "shared") // liba in otherapex is linked to #30 expectLink("liba", "shared_otherapex", "libz", "shared_30") expectNoLink("liba", "shared_otherapex", "libz", "shared_28") expectNoLink("liba", "shared_otherapex", "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"], }, } `, func(fs map[string][]byte, config android.Config) { config.TestProductVariables.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") } // 9000 is quite a magic number. // Finalized SDK codenames are mapped as P(28), Q(29), ... // And, codenames which are not finalized yet(active_codenames + future_codenames) are numbered from 9000, 9001, ... // to distinguish them from finalized and future_api(10000) // In this test, "R" is assumed not finalized yet( listed in Platform_version_active_codenames) and translated into 9000 // (refer android/api_levels.go) expectLink("libx", "shared_myapex", "libz", "shared_9000") expectNoLink("libx", "shared_myapex", "libz", "shared_29") expectNoLink("libx", "shared_myapex", "libz", "shared") } func TestApexMinSdkVersion_DefaultsToLatest(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libx"], } 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_myapex", "libz", "shared_2") expectNoLink("libx", "shared_myapex", "libz", "shared_1") expectNoLink("libx", "shared_myapex", "libz", "shared") } func TestPlatformUsesLatestStubsFromApexes(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libx"], } 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) { 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("libz", "shared", "libx", "shared_2") expectNoLink("libz", "shared", "libz", "shared_1") expectNoLink("libz", "shared", "libz", "shared") } 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"], }, } `, func(fs map[string][]byte, config android.Config) { config.TestProductVariables.SanitizeDevice = []string{"hwaddress"} }) 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_myapex", "libbar", "shared_30") } 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_myapex").Module().(*cc.Module) ensureListContains(t, cm.Properties.AndroidMkStaticLibs, "libgcc_stripped") // note that platform variant is not. cm = ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared").Module().(*cc.Module) ensureListNotContains(t, cm.Properties.AndroidMkStaticLibs, "libgcc_stripped") } func TestApexMinSdkVersion_ErrorIfIncompatibleStubs(t *testing.T) { testApexError(t, `"libz" .*: not found a version\(<=29\)`, ` 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: ["libz"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], min_sdk_version: "29", } cc_library { name: "libz", system_shared_libs: [], stl: "none", stubs: { versions: ["30"], }, } `) } 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", } `) } 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"], 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", } `) } func TestJavaStableSdkVersion(t *testing.T) { testCases := []struct { name string expectedError string bp string }{ { name: "Non-updatable apex with non-stable dep", bp: ` apex { name: "myapex", java_libs: ["myjar"], key: "myapex.key", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } java_library { name: "myjar", srcs: ["foo/bar/MyClass.java"], sdk_version: "core_platform", 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: "core_platform", apex_available: ["myapex"], } `, }, { name: "Updatable apex with non-stable transitive dep", expectedError: "compiles against Android API, but dependency \"transitive-jar\" is compiling against non-public Android 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 { t.Run(test.name, func(t *testing.T) { if test.expectedError == "" { testApex(t, test.bp) } else { testApexError(t, test.expectedError, 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", } 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#29 // 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_myapex", "mylib2", "shared_29") expectLink("mylib", "shared_otherapex", "mylib2", "shared_otherapex") } func TestFilesInSubDir(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], binaries: ["mybin"], prebuilts: ["myetc"], compile_multilib: "both", } 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: [], static_executable: true, stl: "none", apex_available: [ "myapex" ], } `) generateFsRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("generateFsConfig") dirs := strings.Split(generateFsRule.Args["exec_paths"], " ") // Ensure that the subdirectories are all listed ensureListContains(t, dirs, "etc") ensureListContains(t, dirs, "etc/foo") ensureListContains(t, dirs, "etc/foo/bar") ensureListContains(t, dirs, "lib64") ensureListContains(t, dirs, "lib64/foo") ensureListContains(t, dirs, "lib64/foo/bar") ensureListContains(t, dirs, "lib") ensureListContains(t, dirs, "lib/foo") ensureListContains(t, dirs, "lib/foo/bar") ensureListContains(t, dirs, "bin") ensureListContains(t, dirs, "bin/foo") ensureListContains(t, dirs, "bin/foo/bar") } 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, } 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: [], static_executable: true, 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_image", []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 TestUseVendor(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], use_vendor: 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: [], vendor_available: true, stl: "none", apex_available: [ "myapex" ], } cc_library { name: "mylib2", srcs: ["mylib.cpp"], system_shared_libs: [], vendor_available: true, stl: "none", apex_available: [ "myapex" ], } `, func(fs map[string][]byte, config android.Config) { setUseVendorAllowListForTest(config, []string{"myapex"}) }) inputsList := []string{} for _, i := range ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().BuildParamsForTests() { for _, implicit := range i.Implicits { inputsList = append(inputsList, implicit.String()) } } inputsString := strings.Join(inputsList, " ") // ensure that the apex includes vendor variants of the direct and indirect deps ensureContains(t, inputsString, "android_vendor.VER_arm64_armv8-a_shared_myapex/mylib.so") ensureContains(t, inputsString, "android_vendor.VER_arm64_armv8-a_shared_myapex/mylib2.so") // ensure that the apex does not include core variants ensureNotContains(t, inputsString, "android_arm64_armv8-a_shared_myapex/mylib.so") ensureNotContains(t, inputsString, "android_arm64_armv8-a_shared_myapex/mylib2.so") } func TestUseVendorNotAllowedForSystemApexes(t *testing.T) { testApexError(t, `module "myapex" .*: use_vendor: not allowed`, ` apex { name: "myapex", key: "myapex.key", use_vendor: true, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `, func(fs map[string][]byte, config android.Config) { setUseVendorAllowListForTest(config, []string{""}) }) // no error with allow list testApex(t, ` apex { name: "myapex", key: "myapex.key", use_vendor: true, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `, func(fs map[string][]byte, config android.Config) { setUseVendorAllowListForTest(config, []string{"myapex"}) }) } func TestUseVendorFailsIfNotVendorAvailable(t *testing.T) { testApexError(t, `dependency "mylib" of "myapex" missing variant:\n.*image:vendor`, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], use_vendor: true, } 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", } `) } func TestVendorApex(t *testing.T) { ctx, config := testApex(t, ` apex { name: "myapex", key: "myapex.key", binaries: ["mybin"], vendor: true, } 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, ctx, "myapex", "android_common_myapex_image", []string{ "bin/mybin", "lib64/libfoo.so", // TODO(b/159195575): Add an option to use VNDK libs from VNDK APEX "lib64/libc++.so", }) apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, config, "", apexBundle) name := apexBundle.BaseModuleName() prefix := "TARGET_" var builder strings.Builder data.Custom(&builder, name, prefix, "", data) androidMk := builder.String() ensureContains(t, androidMk, `LOCAL_MODULE_PATH := /tmp/target/product/test_device/vendor/apex`) apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule") requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"]) ensureListNotContains(t, requireNativeLibs, ":vndk") } func TestVendorApex_use_vndk_as_stable(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", binaries: ["mybin"], vendor: true, use_vndk_as_stable: true, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_binary { name: "mybin", vendor: true, shared_libs: ["libvndk", "libvendor"], } cc_library { name: "libvndk", vndk: { enabled: true, }, vendor_available: true, } cc_library { name: "libvendor", vendor: true, } `) vendorVariant := "android_vendor.VER_arm64_armv8-a" ldRule := ctx.ModuleForTests("mybin", vendorVariant+"_myapex").Rule("ld") libs := names(ldRule.Args["libFlags"]) // VNDK libs(libvndk/libc++) as they are ensureListContains(t, libs, buildDir+"/.intermediates/libvndk/"+vendorVariant+"_shared/libvndk.so") ensureListContains(t, libs, buildDir+"/.intermediates/libc++/"+vendorVariant+"_shared/libc++.so") // non-stable Vendor libs as APEX variants ensureListContains(t, libs, buildDir+"/.intermediates/libvendor/"+vendorVariant+"_shared_myapex/libvendor.so") // VNDK libs are not included when use_vndk_as_stable: true ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{ "bin/mybin", "lib64/libvendor.so", }) apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule") requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"]) ensureListContains(t, requireNativeLibs, ":vndk") } func TestAndroidMk_UseVendorRequired(t *testing.T) { ctx, config := testApex(t, ` apex { name: "myapex", key: "myapex.key", use_vendor: true, native_shared_libs: ["mylib"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", vendor_available: true, apex_available: ["myapex"], } `, func(fs map[string][]byte, config android.Config) { setUseVendorAllowListForTest(config, []string{"myapex"}) }) apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, config, "", 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 libm libdl\n") } func TestAndroidMk_VendorApexRequired(t *testing.T) { ctx, config := testApex(t, ` apex { name: "myapex", key: "myapex.key", vendor: true, native_shared_libs: ["mylib"], } 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_image").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, config, "", 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 libm.vendor libdl.vendor\n") } func TestAndroidMkWritesCommonProperties(t *testing.T) { ctx, config := testApex(t, ` apex { name: "myapex", key: "myapex.key", vintf_fragments: ["fragment.xml"], init_rc: ["init.rc"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_binary { name: "mybin", } `) apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, config, "", apexBundle) name := apexBundle.BaseModuleName() prefix := "TARGET_" var builder strings.Builder data.Custom(&builder, name, prefix, "", data) androidMk := builder.String() ensureContains(t, androidMk, "LOCAL_VINTF_FRAGMENTS := fragment.xml\n") ensureContains(t, androidMk, "LOCAL_INIT_RC := init.rc\n") } func TestStaticLinking(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], } 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: [ "//apex_available:platform", "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", } 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.public_key_file.String() != "vendor/foo/devkeys/testkey.avbpubkey" { t.Errorf("public key %q is not %q", keys.public_key_file.String(), "vendor/foo/devkeys/testkey.avbpubkey") } if keys.private_key_file.String() != "vendor/foo/devkeys/testkey.pem" { t.Errorf("private key %q is not %q", keys.private_key_file.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_image").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", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", }`) rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").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", } 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_image").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", } 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_image").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", } 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_image").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", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", }`) rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").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", } 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_image").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"], } 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", ], use_apex_name_macro: 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__") ensureNotContains(t, mylibCFlags, "-D__ANDROID_SDK_VERSION__") // APEX variant has __ANDROID_APEX__ and __ANDROID_APEX_SDK__ defined mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"] ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__") ensureContains(t, mylibCFlags, "-D__ANDROID_SDK_VERSION__=10000") ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__") // APEX variant has __ANDROID_APEX__ and __ANDROID_APEX_SDK__ defined mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_otherapex").Rule("cc").Args["cFlags"] ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__") ensureContains(t, mylibCFlags, "-D__ANDROID_SDK_VERSION__=29") ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__") // When cc_library sets use_apex_name_macro: true // apex variants define additional macro to distinguish which apex variant it is built for // 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__") // APEX variant has __ANDROID_APEX__ defined mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"] ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__") ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__") ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__") // APEX variant has __ANDROID_APEX__ defined mylibCFlags = ctx.ModuleForTests("mylib2", "android_arm64_armv8-a_static_otherapex").Rule("cc").Args["cFlags"] ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__") ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__") ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__") // recovery variant does not set __ANDROID_SDK_VERSION__ mylibCFlags = ctx.ModuleForTests("mylib", "android_recovery_arm64_armv8-a_static").Rule("cc").Args["cFlags"] ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__") ensureNotContains(t, mylibCFlags, "-D__ANDROID_SDK_VERSION__") } func TestHeaderLibsDependency(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], } 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 getFiles(t *testing.T, ctx *android.TestContext, moduleName, variant string) []fileInApex { t.Helper() apexRule := ctx.ModuleForTests(moduleName, variant).Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] imageApexDir := "/image.apex/" 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, imageApexDir) if index == -1 { t.Fatal("copyCmds should copy a file to image.apex/", cmd) } dstFile := dst[index+len(imageApexDir):] ret = append(ret, fileInApex{path: dstFile, src: src, isLink: isLink}) } } return ret } func ensureExactContents(t *testing.T, ctx *android.TestContext, moduleName, variant string, files []string) { t.Helper() var failed bool var surplus []string filesMatched := make(map[string]bool) for _, file := range getFiles(t, ctx, moduleName, variant) { mactchFound := false for _, expected := range files { if matched, _ := path.Match(expected, file.path); matched { filesMatched[expected] = true mactchFound = true break } } if !mactchFound { surplus = append(surplus, file.path) } } if len(surplus) > 0 { sort.Strings(surplus) t.Log("surplus files", surplus) failed = true } if len(files) > len(filesMatched) { var missing []string for _, expected := range files { if !filesMatched[expected] { missing = append(missing, expected) } } sort.Strings(missing) t.Log("missing files", missing) failed = true } if failed { t.Fail() } } func TestVndkApexCurrent(t *testing.T) { ctx, _ := testApex(t, ` apex_vndk { name: "myapex", key: "myapex.key", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libvndk", srcs: ["mylib.cpp"], vendor_available: true, vndk: { enabled: true, }, system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } cc_library { name: "libvndksp", srcs: ["mylib.cpp"], vendor_available: true, vndk: { enabled: true, support_system_process: true, }, system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } `+vndkLibrariesTxtFiles("current")) ensureExactContents(t, ctx, "myapex", "android_common_image", []string{ "lib/libvndk.so", "lib/libvndksp.so", "lib/libc++.so", "lib64/libvndk.so", "lib64/libvndksp.so", "lib64/libc++.so", "etc/llndk.libraries.VER.txt", "etc/vndkcore.libraries.VER.txt", "etc/vndksp.libraries.VER.txt", "etc/vndkprivate.libraries.VER.txt", }) } func TestVndkApexWithPrebuilt(t *testing.T) { ctx, _ := testApex(t, ` apex_vndk { name: "myapex", key: "myapex.key", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_prebuilt_library_shared { name: "libvndk", srcs: ["libvndk.so"], vendor_available: true, vndk: { enabled: true, }, system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } cc_prebuilt_library_shared { name: "libvndk.arm", srcs: ["libvndk.arm.so"], vendor_available: true, vndk: { enabled: true, }, enabled: false, arch: { arm: { enabled: true, }, }, system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } `+vndkLibrariesTxtFiles("current"), withFiles(map[string][]byte{ "libvndk.so": nil, "libvndk.arm.so": nil, })) ensureExactContents(t, ctx, "myapex", "android_common_image", []string{ "lib/libvndk.so", "lib/libvndk.arm.so", "lib64/libvndk.so", "lib/libc++.so", "lib64/libc++.so", "etc/*", }) } func vndkLibrariesTxtFiles(vers ...string) (result string) { for _, v := range vers { if v == "current" { for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate"} { result += ` vndk_libraries_txt { name: "` + txt + `.libraries.txt", } ` } } else { for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate"} { result += ` prebuilt_etc { name: "` + txt + `.libraries.` + v + `.txt", src: "dummy.txt", } ` } } } return } func TestVndkApexVersion(t *testing.T) { ctx, _ := testApex(t, ` apex_vndk { name: "myapex_v27", key: "myapex.key", file_contexts: ":myapex-file_contexts", vndk_version: "27", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } vndk_prebuilt_shared { name: "libvndk27", version: "27", vendor_available: true, vndk: { enabled: true, }, target_arch: "arm64", arch: { arm: { srcs: ["libvndk27_arm.so"], }, arm64: { srcs: ["libvndk27_arm64.so"], }, }, apex_available: [ "myapex_v27" ], } vndk_prebuilt_shared { name: "libvndk27", version: "27", vendor_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, "myapex_v27", "android_common_image", []string{ "lib/libvndk27_arm.so", "lib64/libvndk27_arm64.so", "etc/*", }) } func TestVndkApexErrorWithDuplicateVersion(t *testing.T) { testApexError(t, `module "myapex_v27.*" .*: vndk_version: 27 is already defined in "myapex_v27.*"`, ` apex_vndk { name: "myapex_v27", key: "myapex.key", file_contexts: ":myapex-file_contexts", vndk_version: "27", } apex_vndk { name: "myapex_v27_other", key: "myapex.key", file_contexts: ":myapex-file_contexts", vndk_version: "27", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libvndk", srcs: ["mylib.cpp"], vendor_available: true, vndk: { enabled: true, }, system_shared_libs: [], stl: "none", } vndk_prebuilt_shared { name: "libvndk", version: "27", vendor_available: true, vndk: { enabled: true, }, srcs: ["libvndk.so"], } `, withFiles(map[string][]byte{ "libvndk.so": nil, })) } func TestVndkApexNameRule(t *testing.T) { ctx, _ := testApex(t, ` apex_vndk { name: "myapex", key: "myapex.key", file_contexts: ":myapex-file_contexts", } apex_vndk { name: "myapex_v28", key: "myapex.key", file_contexts: ":myapex-file_contexts", vndk_version: "28", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", }`+vndkLibrariesTxtFiles("28", "current")) assertApexName := func(expected, moduleName string) { bundle := ctx.ModuleForTests(moduleName, "android_common_image").Module().(*apexBundle) actual := proptools.String(bundle.properties.Apex_name) if !reflect.DeepEqual(actual, expected) { t.Errorf("Got '%v', expected '%v'", actual, expected) } } assertApexName("com.android.vndk.vVER", "myapex") assertApexName("com.android.vndk.v28", "myapex_v28") } func TestVndkApexSkipsNativeBridgeSupportedModules(t *testing.T) { ctx, _ := testApex(t, ` apex_vndk { name: "myapex", key: "myapex.key", file_contexts: ":myapex-file_contexts", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libvndk", srcs: ["mylib.cpp"], vendor_available: true, native_bridge_supported: true, host_supported: true, vndk: { enabled: true, }, system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } `+vndkLibrariesTxtFiles("current"), withNativeBridgeEnabled) ensureExactContents(t, ctx, "myapex", "android_common_image", []string{ "lib/libvndk.so", "lib64/libvndk.so", "lib/libc++.so", "lib64/libc++.so", "etc/*", }) } func TestVndkApexDoesntSupportNativeBridgeSupported(t *testing.T) { testApexError(t, `module "myapex" .*: native_bridge_supported: .* doesn't support native bridge binary`, ` apex_vndk { name: "myapex", key: "myapex.key", file_contexts: ":myapex-file_contexts", native_bridge_supported: true, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libvndk", srcs: ["mylib.cpp"], vendor_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: "myapex_v27", key: "myapex.key", file_contexts: ":myapex-file_contexts", vndk_version: "27", } 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, vndk: { enabled: true, }, arch: { arm: { srcs: ["libvndk27.so"], } }, } vndk_prebuilt_shared { name: "libvndk27", version: "27", target_arch: "arm", binder32bit: true, vendor_available: true, vndk: { enabled: true, }, arch: { arm: { srcs: ["libvndk27binder32.so"], } }, apex_available: [ "myapex_v27" ], } `+vndkLibrariesTxtFiles("27"), withFiles(map[string][]byte{ "libvndk27.so": nil, "libvndk27binder32.so": nil, }), withBinder32bit, withTargets(map[android.OsType][]android.Target{ android.Android: []android.Target{ {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, "myapex_v27", "android_common_image", []string{ "lib/libvndk27binder32.so", "etc/*", }) } func TestVndkApexShouldNotProvideNativeLibs(t *testing.T) { ctx, _ := testApex(t, ` apex_vndk { name: "myapex", key: "myapex.key", file_contexts: ":myapex-file_contexts", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "libz", vendor_available: true, vndk: { enabled: true, }, stubs: { symbol_file: "libz.map.txt", versions: ["30"], } } `+vndkLibrariesTxtFiles("current"), withFiles(map[string][]byte{ "libz.map.txt": nil, })) apexManifestRule := ctx.ModuleForTests("myapex", "android_common_image").Rule("apexManifestRule") provideNativeLibs := names(apexManifestRule.Args["provideNativeLibs"]) ensureListEmpty(t, provideNativeLibs) } 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", } apex { name: "myapex_dep", key: "myapex.key", native_shared_libs: ["lib_dep"], compile_multilib: "both", file_contexts: ":myapex-file_contexts", } apex { name: "myapex_provider", key: "myapex.key", native_shared_libs: ["libfoo"], compile_multilib: "both", file_contexts: ":myapex-file_contexts", } apex { name: "myapex_selfcontained", key: "myapex.key", native_shared_libs: ["lib_dep", "libfoo"], compile_multilib: "both", file_contexts: ":myapex-file_contexts", } 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: "libfoo", srcs: ["mytest.cpp"], stubs: { versions: ["1"], }, system_shared_libs: [], stl: "none", apex_available: [ "myapex_provider", "myapex_selfcontained", ], } `) var apexManifestRule android.TestingBuildParams var provideNativeLibs, requireNativeLibs []string apexManifestRule = ctx.ModuleForTests("myapex_nodep", "android_common_myapex_nodep_image").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_image").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_image").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_image").Rule("apexManifestRule") provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"]) requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"]) ensureListContains(t, provideNativeLibs, "libfoo.so") ensureListEmpty(t, requireNativeLibs) } func TestApexName(t *testing.T) { ctx, config := testApex(t, ` apex { name: "myapex", key: "myapex.key", apex_name: "com.android.myapex", native_shared_libs: ["mylib"], } 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", ], } `) module := ctx.ModuleForTests("myapex", "android_common_myapex_image") apexManifestRule := module.Rule("apexManifestRule") ensureContains(t, apexManifestRule.Args["opt"], "-v name com.android.myapex") apexRule := module.Rule("apexRule") ensureContains(t, apexRule.Args["opt_flags"], "--do_not_check_keyname") apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, config, "", apexBundle) name := apexBundle.BaseModuleName() prefix := "TARGET_" var builder strings.Builder data.Custom(&builder, name, prefix, "", data) androidMk := builder.String() ensureContains(t, androidMk, "LOCAL_MODULE := mylib.myapex\n") ensureNotContains(t, androidMk, "LOCAL_MODULE := mylib.com.android.myapex\n") } func TestNonTestApex(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib_common"], } 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_image") 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_myapex") // 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 !android.InAnyApex("mylib_common") { t.Log("Found mylib_common not in any apex!") t.Fail() } } func TestTestApex(t *testing.T) { if android.InAnyApex("mylib_common_test") { t.Fatal("mylib_common_test must not be used in any other tests since this checks that global state is not updated in an illegal way!") } ctx, _ := testApex(t, ` apex_test { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib_common_test"], } 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_image") 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_myapex") // 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 TestApexWithTarget(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", 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_image").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_myapex") ensureListContains(t, ctx.ModuleVariantsForTests("mylib_common"), "android_arm64_armv8-a_shared_myapex") ensureListNotContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_myapex") // 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 TestApexWithShBinary(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", binaries: ["myscript"], } 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_image").Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] ensureContains(t, copyCmds, "image.apex/bin/script/myscript.sh") } func TestApexInVariousPartition(t *testing.T) { testcases := []struct { propName, parition, flattenedPartition string }{ {"", "system", "system_ext"}, {"product_specific: true", "product", "product"}, {"soc_specific: true", "vendor", "vendor"}, {"proprietary: true", "vendor", "vendor"}, {"vendor: true", "vendor", "vendor"}, {"system_ext_specific: true", "system_ext", "system_ext"}, } for _, tc := range testcases { t.Run(tc.propName+":"+tc.parition, func(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", `+tc.propName+` } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `) apex := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) expected := buildDir + "/target/product/test_device/" + tc.parition + "/apex" actual := apex.installDir.String() if actual != expected { t.Errorf("wrong install path. expected %q. actual %q", expected, actual) } flattened := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle) expected = buildDir + "/target/product/test_device/" + tc.flattenedPartition + "/apex" actual = flattened.installDir.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", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `) module := ctx.ModuleForTests("myapex", "android_common_myapex_image") 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", } 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", } 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", } 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_image") 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", } 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_image") 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.public_key_file.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.private_key_file.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", }, }, } `) prebuilt := ctx.ModuleForTests("myapex", "android_common").Module().(*Prebuilt) expectedInput := "myapex-arm64.apex" if prebuilt.inputApex.String() != expectedInput { t.Errorf("inputApex invalid. expected: %q, actual: %q", expectedInput, prebuilt.inputApex.String()) } } func TestPrebuiltFilenameOverride(t *testing.T) { ctx, _ := testApex(t, ` prebuilt_apex { name: "myapex", src: "myapex-arm.apex", filename: "notmyapex.apex", } `) p := ctx.ModuleForTests("myapex", "android_common").Module().(*Prebuilt) expected := "notmyapex.apex" if p.installFilename != expected { t.Errorf("installFilename invalid. expected: %q, actual: %q", expected, p.installFilename) } } func TestPrebuiltOverrides(t *testing.T) { ctx, config := testApex(t, ` prebuilt_apex { name: "myapex.prebuilt", src: "myapex-arm.apex", overrides: [ "myapex", ], } `) p := ctx.ModuleForTests("myapex.prebuilt", "android_common").Module().(*Prebuilt) expected := []string{"myapex"} actual := android.AndroidMkEntriesForTest(t, config, "", p)[0].EntryMap["LOCAL_OVERRIDES_MODULES"] if !reflect.DeepEqual(actual, expected) { t.Errorf("Incorrect LOCAL_OVERRIDES_MODULES value '%s', expected '%s'", actual, expected) } } func TestApexWithTests(t *testing.T) { ctx, config := testApex(t, ` apex_test { name: "myapex", key: "myapex.key", 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_image").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_image").Module().(*apexBundle) data := android.AndroidMkDataForTest(t, config, "", 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 := apex_manifest.pb.myapex\n") ensureContains(t, androidMk, "LOCAL_MODULE := apex_pubkey.myapex\n") ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n") flatBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle) data = android.AndroidMkDataForTest(t, config, "", flatBundle) data.Custom(&builder, name, prefix, "", data) flatAndroidMk := builder.String() ensureContainsOnce(t, flatAndroidMk, "LOCAL_TEST_DATA := :baz :bar/baz\n") ensureContainsOnce(t, flatAndroidMk, "LOCAL_TEST_DATA := :testdata/baz\n") } func TestInstallExtraFlattenedApexes(t *testing.T) { ctx, config := testApex(t, ` apex { name: "myapex", key: "myapex.key", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `, func(fs map[string][]byte, config android.Config) { config.TestProductVariables.InstallExtraFlattenedApexes = proptools.BoolPtr(true) }) ab := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle) ensureListContains(t, ab.requiredDeps, "myapex.flattened") mk := android.AndroidMkDataForTest(t, config, "", ab) var builder strings.Builder mk.Custom(&builder, ab.Name(), "TARGET_", "", mk) androidMk := builder.String() ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += myapex.flattened") } func TestApexUsesOtherApex(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], uses: ["commonapex"], } apex { name: "commonapex", key: "myapex.key", native_shared_libs: ["libcommon"], provide_cpp_shared_libs: true, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], shared_libs: ["libcommon"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } cc_library { name: "libcommon", srcs: ["mylib_common.cpp"], system_shared_libs: [], stl: "none", // TODO: remove //apex_available:platform apex_available: [ "//apex_available:platform", "commonapex", "myapex", ], } `) module1 := ctx.ModuleForTests("myapex", "android_common_myapex_image") apexRule1 := module1.Rule("apexRule") copyCmds1 := apexRule1.Args["copy_commands"] module2 := ctx.ModuleForTests("commonapex", "android_common_commonapex_image") apexRule2 := module2.Rule("apexRule") copyCmds2 := apexRule2.Args["copy_commands"] ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_myapex") ensureListContains(t, ctx.ModuleVariantsForTests("libcommon"), "android_arm64_armv8-a_shared_commonapex") ensureContains(t, copyCmds1, "image.apex/lib64/mylib.so") ensureContains(t, copyCmds2, "image.apex/lib64/libcommon.so") ensureNotContains(t, copyCmds1, "image.apex/lib64/libcommon.so") } func TestApexUsesFailsIfNotProvided(t *testing.T) { testApexError(t, `uses: "commonapex" does not provide native_shared_libs`, ` apex { name: "myapex", key: "myapex.key", uses: ["commonapex"], } apex { name: "commonapex", key: "myapex.key", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `) testApexError(t, `uses: "commonapex" is not a provider`, ` apex { name: "myapex", key: "myapex.key", uses: ["commonapex"], } cc_library { name: "commonapex", system_shared_libs: [], stl: "none", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `) } func TestApexUsesFailsIfUseVenderMismatch(t *testing.T) { testApexError(t, `use_vendor: "commonapex" has different value of use_vendor`, ` apex { name: "myapex", key: "myapex.key", use_vendor: true, uses: ["commonapex"], } apex { name: "commonapex", key: "myapex.key", provide_cpp_shared_libs: true, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `, func(fs map[string][]byte, config android.Config) { setUseVendorAllowListForTest(config, []string{"myapex"}) }) } 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 TestApexWithApps(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: [ "AppFoo", "AppFooPriv", ], } 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", 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, 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_image") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] ensureContains(t, copyCmds, "image.apex/app/AppFoo/AppFoo.apk") ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPriv/AppFooPriv.apk") appZipRule := ctx.ModuleForTests("AppFoo", "android_common_myapex").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_myapex").Module().(*cc.Module).OutputFile() // ... 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") } } func TestApexWithAppImports(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: [ "AppFooPrebuilt", "AppFooPrivPrebuilt", ], } 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_image") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] ensureContains(t, copyCmds, "image.apex/app/AppFooPrebuilt/AppFooPrebuilt.apk") ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPrivPrebuilt/AwesomePrebuiltAppFooPriv.apk") } func TestApexWithAppImportsPrefer(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: [ "AppFoo", ], } 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_image", []string{ "app/AppFoo/AppFooPrebuilt.apk", }) } func TestApexWithTestHelperApp(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: [ "TesterHelpAppFoo", ], } 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_image") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] ensureContains(t, copyCmds, "image.apex/app/TesterHelpAppFoo/TesterHelpAppFoo.apk") } func TestApexPropertiesShouldBeDefaultable(t *testing.T) { // libfoo's apex_available comes from cc_defaults testApexError(t, `requires "libfoo" that is not available for the APEX`, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } apex { name: "otherapex", key: "myapex.key", native_shared_libs: ["libfoo"], } 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 is not available for the APEX", ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } apex { name: "otherapex", key: "otherapex.key", native_shared_libs: ["libfoo"], } 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 is not available for the APEX. Dependency path: .*via tag apex\.dependencyTag.*"sharedLib".* .*-> libfoo.*link:shared.* .*via tag cc\.DependencyTag.*"shared".* .*-> libbar.*link:shared.* .*via tag cc\.DependencyTag.*"shared".* .*-> libbaz.*link:shared.*`, ` 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", 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_InvalidApexName(t *testing.T) { testApexError(t, "\"otherapex\" is not a valid module name", ` 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: [], apex_available: ["otherapex"], }`) testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libfoo", "libbar"], } 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_CheckForPlatform(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["libbar", "libbaz"], } 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"], } 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, config := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: ["app"], overrides: ["oldapex"], } override_apex { name: "override_myapex", base: "myapex", apps: ["override_app"], overrides: ["unknownapex"], logging_parent: "com.foo.bar", package_name: "test.overridden.package", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } 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", } `, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"})) originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(android.OverridableModule) overriddenVariant := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex_image").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_myapex_image") apexRule := module.Rule("apexRule") copyCmds := apexRule.Args["copy_commands"] ensureNotContains(t, copyCmds, "image.apex/app/app/app.apk") ensureContains(t, copyCmds, "image.apex/app/override_app/override_app.apk") 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") data := android.AndroidMkDataForTest(t, config, "", 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 := apex_manifest.pb.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 := override_app.myapex") ensureNotContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex") ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex") } 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_image") 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_myapex").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 = map[string][]byte{ "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, // 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"], } 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" ], } `, withFiles(filesForSdkLibrary)) // java_sdk_library installs both impl jar and permission XML ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []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").Rule("java_sdk_xml") ensureContains(t, sdkLibrary.RuleParams.Command, ` 0 { android.FailIfNoMatchingErrors(t, errmsg, errs) return } else { t.Fatalf("missing expected error %q (0 errors are returned)", errmsg) } } func TestUpdatable_should_set_min_sdk_version(t *testing.T) { testApexError(t, `"myapex" .*: updatable: updatable APEXes should set min_sdk_version`, ` apex { name: "myapex", key: "myapex.key", updatable: true, } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } `) } func TestNoUpdatableJarsInBootImage(t *testing.T) { var err string var transform func(*dexpreopt.GlobalConfig) t.Run("updatable jar from ART apex in the ART boot image => ok", func(t *testing.T) { transform = func(config *dexpreopt.GlobalConfig) { config.ArtApexJars = []string{"com.android.art.something:some-art-lib"} } testNoUpdatableJarsInBootImage(t, "", transform) }) t.Run("updatable jar from ART apex in the framework boot image => error", func(t *testing.T) { err = "module 'some-art-lib' from updatable apex 'com.android.art.something' is not allowed in the framework boot image" transform = func(config *dexpreopt.GlobalConfig) { config.BootJars = []string{"com.android.art.something:some-art-lib"} } testNoUpdatableJarsInBootImage(t, err, transform) }) t.Run("updatable jar from some other apex in the ART boot image => error", func(t *testing.T) { err = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the ART boot image" transform = func(config *dexpreopt.GlobalConfig) { config.ArtApexJars = []string{"some-updatable-apex:some-updatable-apex-lib"} } testNoUpdatableJarsInBootImage(t, err, transform) }) t.Run("non-updatable jar from some other apex in the ART boot image => error", func(t *testing.T) { err = "module 'some-non-updatable-apex-lib' is not allowed in the ART boot image" transform = func(config *dexpreopt.GlobalConfig) { config.ArtApexJars = []string{"some-non-updatable-apex:some-non-updatable-apex-lib"} } testNoUpdatableJarsInBootImage(t, err, transform) }) t.Run("updatable jar from some other apex in the framework boot image => error", func(t *testing.T) { err = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the framework boot image" transform = func(config *dexpreopt.GlobalConfig) { config.BootJars = []string{"some-updatable-apex:some-updatable-apex-lib"} } testNoUpdatableJarsInBootImage(t, err, transform) }) t.Run("non-updatable jar from some other apex in the framework boot image => ok", func(t *testing.T) { transform = func(config *dexpreopt.GlobalConfig) { config.BootJars = []string{"some-non-updatable-apex:some-non-updatable-apex-lib"} } testNoUpdatableJarsInBootImage(t, "", transform) }) t.Run("nonexistent jar in the ART boot image => error", func(t *testing.T) { err = "failed to find a dex jar path for module 'nonexistent'" transform = func(config *dexpreopt.GlobalConfig) { config.ArtApexJars = []string{"platform:nonexistent"} } testNoUpdatableJarsInBootImage(t, err, transform) }) t.Run("nonexistent jar in the framework boot image => error", func(t *testing.T) { err = "failed to find a dex jar path for module 'nonexistent'" transform = func(config *dexpreopt.GlobalConfig) { config.BootJars = []string{"platform:nonexistent"} } testNoUpdatableJarsInBootImage(t, err, transform) }) t.Run("platform jar in the ART boot image => error", func(t *testing.T) { err = "module 'some-platform-lib' is not allowed in the ART boot image" transform = func(config *dexpreopt.GlobalConfig) { config.ArtApexJars = []string{"platform:some-platform-lib"} } testNoUpdatableJarsInBootImage(t, err, transform) }) t.Run("platform jar in the framework boot image => ok", func(t *testing.T) { transform = func(config *dexpreopt.GlobalConfig) { config.BootJars = []string{"platform:some-platform-lib"} } testNoUpdatableJarsInBootImage(t, "", transform) }) } func TestTestFor(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib", "myprivlib"], } 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"], }, apex_available: ["myapex"], } cc_library { name: "myprivlib", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", apex_available: ["myapex"], } cc_test { name: "mytest", gtest: false, srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", shared_libs: ["mylib", "myprivlib"], test_for: ["myapex"] } `) // the test 'mytest' is a test for the apex, therefore is linked to the // actual implementation of mylib instead of its stub. ldFlags := ctx.ModuleForTests("mytest", "android_arm64_armv8-a").Rule("ld").Args["libFlags"] ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so") ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so") } // TODO(jungjw): Move this to proptools func intPtr(i int) *int { return &i } func TestApexSet(t *testing.T) { ctx, config := testApex(t, ` apex_set { name: "myapex", set: "myapex.apks", filename: "foo_v2.apex", overrides: ["foo"], } `, func(fs map[string][]byte, config android.Config) { config.TestProductVariables.Platform_sdk_version = intPtr(30) config.TestProductVariables.DeviceArch = proptools.StringPtr("arm") config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm64") }) m := ctx.ModuleForTests("myapex", "android_common") // Check extract_apks tool parameters. extractedApex := m.Output(buildDir + "/.intermediates/myapex/android_common/foo_v2.apex") actual := extractedApex.Args["abis"] expected := "ARMEABI_V7A,ARM64_V8A" if actual != expected { t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual) } actual = extractedApex.Args["sdk-version"] expected = "30" if actual != expected { t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual) } a := m.Module().(*ApexSet) expectedOverrides := []string{"foo"} actualOverrides := android.AndroidMkEntriesForTest(t, config, "", a)[0].EntryMap["LOCAL_OVERRIDES_MODULES"] if !reflect.DeepEqual(actualOverrides, expectedOverrides) { t.Errorf("Incorrect LOCAL_OVERRIDES_MODULES - expected %q vs actual %q", expectedOverrides, actualOverrides) } } func TestNoStaticLinkingToStubsLib(t *testing.T) { testApexError(t, `.*required by "mylib" is a native library providing stub.*`, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], static_libs: ["otherlib"], system_shared_libs: [], stl: "none", apex_available: [ "myapex" ], } cc_library { name: "otherlib", srcs: ["mylib.cpp"], system_shared_libs: [], stl: "none", stubs: { versions: ["1", "2", "3"], }, apex_available: [ "myapex" ], } `) } func TestApexKeysTxt(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } prebuilt_apex { name: "myapex", prefer: true, arch: { arm64: { src: "myapex-arm64.apex", }, arm: { src: "myapex-arm.apex", }, }, } apex_set { name: "myapex_set", set: "myapex.apks", filename: "myapex_set.apex", overrides: ["myapex"], } `) apexKeysText := ctx.SingletonForTests("apex_keys_text") content := apexKeysText.MaybeDescription("apexkeys.txt").BuildParams.Args["content"] ensureContains(t, content, `name="myapex_set.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED" partition="system"`) ensureContains(t, content, `name="myapex.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED" partition="system"`) } func TestAllowedFiles(t *testing.T) { ctx, _ := testApex(t, ` apex { name: "myapex", key: "myapex.key", apps: ["app"], allowed_files: "allowed.txt", } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } android_app { name: "app", srcs: ["foo/bar/MyClass.java"], package_name: "foo", sdk_version: "none", system_modules: "none", apex_available: [ "myapex" ], } `, withFiles(map[string][]byte{ "sub/Android.bp": []byte(` override_apex { name: "override_myapex", base: "myapex", apps: ["override_app"], allowed_files: ":allowed", } // Overridable "path" property should be referenced indirectly filegroup { name: "allowed", srcs: ["allowed.txt"], } override_android_app { name: "override_app", base: "app", package_name: "bar", } `), })) rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("diffApexContentRule") if expected, actual := "allowed.txt", rule.Args["allowed_files_file"]; expected != actual { t.Errorf("allowed_files_file: expected %q but got %q", expected, actual) } rule2 := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex_image").Rule("diffApexContentRule") if expected, actual := "sub/allowed.txt", rule2.Args["allowed_files_file"]; expected != actual { t.Errorf("allowed_files_file: expected %q but got %q", expected, actual) } } func TestNonPreferredPrebuiltDependency(t *testing.T) { _, _ = testApex(t, ` apex { name: "myapex", key: "myapex.key", native_shared_libs: ["mylib"], } apex_key { name: "myapex.key", public_key: "testkey.avbpubkey", private_key: "testkey.pem", } cc_library { name: "mylib", srcs: ["mylib.cpp"], stubs: { versions: ["10000"], }, apex_available: ["myapex"], } cc_prebuilt_library_shared { name: "mylib", prefer: false, srcs: ["prebuilt.so"], stubs: { versions: ["10000"], }, apex_available: ["myapex"], } `) } func TestMain(m *testing.M) { run := func() int { setUp() defer tearDown() return m.Run() } os.Exit(run()) }