Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9477c26f authored by Jiyong Park's avatar Jiyong Park
Browse files

Add platform_apis property to APEX module type

The property is used to allow non-updatable APEXes to use platform APIs
(e.g. symbols marked as "# platform-only").

Bug: 191637950
Test: m com.android.virt com.android.compos
Merged-In: Id2410b4e38a78ec2146a42298840954381a7c472
Change-Id: Id2410b4e38a78ec2146a42298840954381a7c472
(cherry picked from commit fb63625a)
parent 7d55b617
Loading
Loading
Loading
Loading
+14 −1
Original line number Diff line number Diff line
@@ -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
+36 −21
Original line number Diff line number Diff line
@@ -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 {
+12 −0
Original line number Diff line number Diff line
@@ -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
@@ -919,6 +923,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},
@@ -1321,6 +1326,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 {
@@ -2374,6 +2383,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)
	}
+73 −0
Original line number Diff line number Diff line
@@ -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, `
+5 −5
Original line number Diff line number Diff line
@@ -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