diff --git a/android/apex.go b/android/apex.go index 4618fe97e..be9adc494 100644 --- a/android/apex.go +++ b/android/apex.go @@ -54,6 +54,10 @@ type ApexInfo struct { // True if this module comes from an updatable apexBundle. Updatable bool + // True if this module can use private platform APIs. Only non-updatable APEX can set this + // to true. + UsePlatformApis bool + // The list of SDK modules that the containing apexBundle depends on. RequiredSdks SdkRefs @@ -91,12 +95,17 @@ var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex") // of a module can be deduped into one variation. For example, if libfoo is included in both apex.a // and apex.b, and if the two APEXes have the same min_sdk_version (say 29), then libfoo doesn't // have to be built twice, but only once. In that case, the two apex variations apex.a and apex.b -// are configured to have the same alias variation named apex29. +// are configured to have the same alias variation named apex29. Whether platform APIs is allowed +// or not also matters; if two APEXes don't have the same allowance, they get different names and +// thus wouldn't be merged. func (i ApexInfo) mergedName(ctx PathContext) string { name := "apex" + strconv.Itoa(i.MinSdkVersion.FinalOrFutureInt()) for _, sdk := range i.RequiredSdks { name += "_" + sdk.Name + "_" + sdk.Version } + if i.UsePlatformApis { + name += "_private" + } return name } @@ -527,6 +536,10 @@ func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexIn merged[index].InApexModules = append(merged[index].InApexModules, apexInfo.InApexModules...) merged[index].ApexContents = append(merged[index].ApexContents, apexInfo.ApexContents...) merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable + if merged[index].UsePlatformApis != apexInfo.UsePlatformApis { + panic(fmt.Errorf("variants having different UsePlatformApis can't be merged")) + } + merged[index].UsePlatformApis = apexInfo.UsePlatformApis } else { seen[mergedName] = len(merged) apexInfo.ApexVariationName = mergedName diff --git a/android/apex_test.go b/android/apex_test.go index e1123692d..60a639b4b 100644 --- a/android/apex_test.go +++ b/android/apex_test.go @@ -33,10 +33,10 @@ func Test_mergeApexVariations(t *testing.T) { { name: "single", in: []ApexInfo{ - {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, }, wantAliases: [][2]string{ {"foo", "apex10000"}, @@ -45,11 +45,11 @@ func Test_mergeApexVariations(t *testing.T) { { name: "merge", in: []ApexInfo{ - {"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false}}, + {"apex10000_baz_1", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false}}, wantAliases: [][2]string{ {"bar", "apex10000_baz_1"}, {"foo", "apex10000_baz_1"}, @@ -58,12 +58,12 @@ func Test_mergeApexVariations(t *testing.T) { { name: "don't merge version", in: []ApexInfo{ - {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", uncheckedFinalApiLevel(30), false, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex30", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, - {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"apex30", uncheckedFinalApiLevel(30), false, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, }, wantAliases: [][2]string{ {"bar", "apex30"}, @@ -73,11 +73,11 @@ func Test_mergeApexVariations(t *testing.T) { { name: "merge updatable", in: []ApexInfo{ - {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, true, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, true, false, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, }, wantAliases: [][2]string{ {"bar", "apex10000"}, @@ -87,12 +87,12 @@ func Test_mergeApexVariations(t *testing.T) { { name: "don't merge sdks", in: []ApexInfo{ - {"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, false, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000_baz_2", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, - {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"apex10000_baz_2", FutureApiLevel, false, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"apex10000_baz_1", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, }, wantAliases: [][2]string{ {"bar", "apex10000_baz_2"}, @@ -102,21 +102,36 @@ func Test_mergeApexVariations(t *testing.T) { { name: "don't merge when for prebuilt_apex", in: []ApexInfo{ - {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, true, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, // This one should not be merged in with the others because it is for // a prebuilt_apex. - {"baz", FutureApiLevel, true, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex}, + {"baz", FutureApiLevel, true, false, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, - {"baz", FutureApiLevel, true, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex}, + {"apex10000", FutureApiLevel, true, false, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, + {"baz", FutureApiLevel, true, false, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex}, }, wantAliases: [][2]string{ {"bar", "apex10000"}, {"foo", "apex10000"}, }, }, + { + name: "don't merge different UsePlatformApis", + in: []ApexInfo{ + {"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, false, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + }, + wantMerged: []ApexInfo{ + {"apex10000_private", FutureApiLevel, false, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + }, + wantAliases: [][2]string{ + {"bar", "apex10000_private"}, + {"foo", "apex10000"}, + }, + }, } for _, tt := range tests { diff --git a/apex/apex.go b/apex/apex.go index 96ff96ad7..c27b01f8d 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -130,6 +130,10 @@ type apexBundleProperties struct { // symlinking to the system libs. Default is true. Updatable *bool + // Whether this APEX can use platform APIs or not. Can be set to true only when `updatable: + // false`. Default is false. + Platform_apis *bool + // Whether this APEX is installable to one of the partitions like system, vendor, etc. // Default: true. Installable *bool @@ -908,6 +912,7 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { MinSdkVersion: minSdkVersion, RequiredSdks: a.RequiredSdks(), Updatable: a.Updatable(), + UsePlatformApis: a.UsePlatformApis(), InApexVariants: []string{mctx.ModuleName()}, // could be com.android.foo InApexModules: []string{a.Name()}, // could be com.mycompany.android.foo ApexContents: []*android.ApexContents{apexContents}, @@ -1274,6 +1279,10 @@ func (a *apexBundle) Updatable() bool { return proptools.BoolDefault(a.properties.Updatable, true) } +func (a *apexBundle) UsePlatformApis() bool { + return proptools.BoolDefault(a.properties.Platform_apis, false) +} + // getCertString returns the name of the cert that should be used to sign this APEX. This is // basically from the "certificate" property, but could be overridden by the device config. func (a *apexBundle) getCertString(ctx android.BaseModuleContext) string { @@ -2317,6 +2326,9 @@ func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) { if String(a.properties.Min_sdk_version) == "" { ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well") } + if a.UsePlatformApis() { + ctx.PropertyErrorf("updatable", "updatable APEXes can't use platform APIs") + } a.checkJavaStableSdkVersion(ctx) a.checkClasspathFragments(ctx) } diff --git a/apex/apex_test.go b/apex/apex_test.go index 6280d118c..628d9b19e 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -957,6 +957,79 @@ func TestApexWithStubs(t *testing.T) { ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so") } +func TestApexCanUsePrivateApis(t *testing.T) { + ctx := testApex(t, ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["mylib"], + binaries: ["foo.rust"], + updatable: false, + platform_apis: true, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + cc_library { + name: "mylib", + srcs: ["mylib.cpp"], + shared_libs: ["mylib2"], + system_shared_libs: [], + stl: "none", + apex_available: [ "myapex" ], + } + + cc_library { + name: "mylib2", + srcs: ["mylib.cpp"], + cflags: ["-include mylib.h"], + system_shared_libs: [], + stl: "none", + stubs: { + versions: ["1", "2", "3"], + }, + } + + rust_binary { + name: "foo.rust", + srcs: ["foo.rs"], + shared_libs: ["libfoo.shared_from_rust"], + prefer_rlib: true, + apex_available: ["myapex"], + } + + cc_library_shared { + name: "libfoo.shared_from_rust", + srcs: ["mylib.cpp"], + system_shared_libs: [], + stl: "none", + stubs: { + versions: ["10", "11", "12"], + }, + } + `) + + apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule") + copyCmds := apexRule.Args["copy_commands"] + + // Ensure that indirect stubs dep is not included + ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so") + ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so") + + // Ensure that we are using non-stub variants of mylib2 and libfoo.shared_from_rust (because + // of the platform_apis: true) + mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000_private").Rule("ld").Args["libFlags"] + ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so") + ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so") + rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000_private").Rule("rustc").Args["linkFlags"] + ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so") + ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so") +} + func TestApexWithStubsWithMinSdkVersion(t *testing.T) { t.Parallel() ctx := testApex(t, ` diff --git a/cc/cc.go b/cc/cc.go index 818d5ff40..4a4a10e4a 100644 --- a/cc/cc.go +++ b/cc/cc.go @@ -2904,11 +2904,11 @@ func ChooseStubOrImpl(ctx android.ModuleContext, dep android.Module) (SharedLibr // of apex sdk enforcement below to choose right version. useStubs = true } - } else if apexInfo.IsForPlatform() { - // If not building for APEX, use stubs only when it is from - // an APEX (and not from platform) - // However, for host, ramdisk, vendor_ramdisk, recovery or bootstrap modules, - // always link to non-stub variant + } else if apexInfo.IsForPlatform() || apexInfo.UsePlatformApis { + // If not building for APEX or the containing APEX allows the use of + // platform APIs, use stubs only when it is from an APEX (and not from + // platform) However, for host, ramdisk, vendor_ramdisk, recovery or + // bootstrap modules, always link to non-stub variant useStubs = dep.(android.ApexModule).NotInPlatform() && !bootstrap if useStubs { // Another exception: if this module is a test for an APEX, then