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

Commit 4f487c5b authored by Jiyong Park's avatar Jiyong Park
Browse files

Install jni symlinks in Soong

The installation of the symlink
(<partition>/app/MyApp/lib/<arch>/libfoo.so) and its target
(/system/lib64/libfoo.so) are now done int Soong.

I gave up the idea of always embedding jni libs to apps, due to a
hard-to-fix regression in storage usage. Specifically, consider this
case.

app --(jni_lib)--> libfoo
    --(jni_lib)--> libbar

libfoo --(shared_lib)--> libbar

Ideally, with the embedding idea, both libfoo and libbar should be
embedded into the app and there should be no libfoo or libbar outside of
the apk, unless there's no dependency to any of them from outside of the
apk.

However, the previous implementation installed libbar to /system/lib64,
leading two copies of the lib; one in /system/lib64, the other in the
apk.

This was happening because libbar was also considered as a transitive
dep of libfoo, and therefore a dependency to its installation path is
added to the apk. The problem here is that the app doesn't know that
libfoo depends on libbar. We in theory can write a very delicate
dependency traverse routine which filters libbar out of the transitive
deps, but that looked too complicated.

Bug: 339923078
Bug: 330276359
Test: Build and watch any bloatbuster warning
Test: m aosp_cf_system_x86_64
The following three files are found
* system/lib64/libnfc_nci_jni.so
* system/etc/libnfc-nci.conf
* system/priv-app/NfcNci/lib/arm64/libnfc_nci_jni.so

Change-Id: I0930cb1ebb8ca8a6efd64b1ce2cdfd1c47fe19ef
parent 16209835
Loading
Loading
Loading
Loading
+0 −18
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@ package java
import (
	"fmt"
	"io"
	"strings"

	"android/soong/android"

@@ -413,23 +412,6 @@ func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries {
				if app.embeddedJniLibs {
					jniSymbols := app.JNISymbolsInstalls(app.installPathForJNISymbols.String())
					entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", jniSymbols.String())
				} else {
					for _, jniLib := range app.jniLibs {
						entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
						var partitionTag string

						// Mimic the creation of partition_tag in build/make,
						// which defaults to an empty string when the partition is system.
						// Otherwise, capitalize with a leading _
						if jniLib.partition == "system" {
							partitionTag = ""
						} else {
							split := strings.Split(jniLib.partition, "/")
							partitionTag = "_" + strings.ToUpper(split[len(split)-1])
						}
						entries.AddStrings("LOCAL_SOONG_JNI_LIBS_PARTITION_"+jniLib.target.Arch.ArchType.String(),
							jniLib.name+":"+partitionTag)
					}
				}

				if len(app.jniCoverageOutputs) > 0 {
+0 −149
Original line number Diff line number Diff line
@@ -19,9 +19,6 @@ import (
	"testing"

	"android/soong/android"
	"android/soong/cc"

	"github.com/google/blueprint/proptools"
)

func TestRequired(t *testing.T) {
@@ -255,149 +252,3 @@ func TestGetOverriddenPackages(t *testing.T) {
		android.AssertDeepEquals(t, "overrides property", expected.overrides, actual)
	}
}

func TestJniPartition(t *testing.T) {
	bp := `
		cc_library {
			name: "libjni_system",
			system_shared_libs: [],
			sdk_version: "current",
			stl: "none",
		}

		cc_library {
			name: "libjni_system_ext",
			system_shared_libs: [],
			sdk_version: "current",
			stl: "none",
			system_ext_specific: true,
		}

		cc_library {
			name: "libjni_odm",
			system_shared_libs: [],
			sdk_version: "current",
			stl: "none",
			device_specific: true,
		}

		cc_library {
			name: "libjni_product",
			system_shared_libs: [],
			sdk_version: "current",
			stl: "none",
			product_specific: true,
		}

		cc_library {
			name: "libjni_vendor",
			system_shared_libs: [],
			sdk_version: "current",
			stl: "none",
			soc_specific: true,
		}

		android_app {
			name: "test_app_system_jni_system",
			privileged: true,
			platform_apis: true,
			certificate: "platform",
			jni_libs: ["libjni_system"],
		}

		android_app {
			name: "test_app_system_jni_system_ext",
			privileged: true,
			platform_apis: true,
			certificate: "platform",
			jni_libs: ["libjni_system_ext"],
		}

		android_app {
			name: "test_app_system_ext_jni_system",
			privileged: true,
			platform_apis: true,
			certificate: "platform",
			jni_libs: ["libjni_system"],
			system_ext_specific: true
		}

		android_app {
			name: "test_app_system_ext_jni_system_ext",
			sdk_version: "core_platform",
			jni_libs: ["libjni_system_ext"],
			system_ext_specific: true
		}

		android_app {
			name: "test_app_product_jni_product",
			sdk_version: "core_platform",
			jni_libs: ["libjni_product"],
			product_specific: true
		}

		android_app {
			name: "test_app_vendor_jni_odm",
			sdk_version: "core_platform",
			jni_libs: ["libjni_odm"],
			soc_specific: true
		}

		android_app {
			name: "test_app_odm_jni_vendor",
			sdk_version: "core_platform",
			jni_libs: ["libjni_vendor"],
			device_specific: true
		}
		android_app {
			name: "test_app_system_jni_multiple",
			privileged: true,
			platform_apis: true,
			certificate: "platform",
			jni_libs: ["libjni_system", "libjni_system_ext"],
		}
		android_app {
			name: "test_app_vendor_jni_multiple",
			sdk_version: "core_platform",
			jni_libs: ["libjni_odm", "libjni_vendor"],
			soc_specific: true
		}
		`
	arch := "arm64"
	ctx := android.GroupFixturePreparers(
		PrepareForTestWithJavaDefaultModules,
		cc.PrepareForTestWithCcDefaultModules,
		android.PrepareForTestWithAndroidMk,
		android.FixtureModifyConfig(func(config android.Config) {
			config.TestProductVariables.DeviceArch = proptools.StringPtr(arch)
		}),
	).
		RunTestWithBp(t, bp)
	testCases := []struct {
		name           string
		partitionNames []string
		partitionTags  []string
	}{
		{"test_app_system_jni_system", []string{"libjni_system"}, []string{""}},
		{"test_app_system_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
		{"test_app_system_ext_jni_system", []string{"libjni_system"}, []string{""}},
		{"test_app_system_ext_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
		{"test_app_product_jni_product", []string{"libjni_product"}, []string{"_PRODUCT"}},
		{"test_app_vendor_jni_odm", []string{"libjni_odm"}, []string{"_ODM"}},
		{"test_app_odm_jni_vendor", []string{"libjni_vendor"}, []string{"_VENDOR"}},
		{"test_app_system_jni_multiple", []string{"libjni_system", "libjni_system_ext"}, []string{"", "_SYSTEM_EXT"}},
		{"test_app_vendor_jni_multiple", []string{"libjni_odm", "libjni_vendor"}, []string{"_ODM", "_VENDOR"}},
	}

	for _, test := range testCases {
		t.Run(test.name, func(t *testing.T) {
			mod := ctx.ModuleForTests(test.name, "android_common").Module()
			entry := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0]
			for i := range test.partitionNames {
				actual := entry.EntryMap["LOCAL_SOONG_JNI_LIBS_PARTITION_"+arch][i]
				expected := test.partitionNames[i] + ":" + test.partitionTags[i]
				android.AssertStringEquals(t, "Expected and actual differ", expected, actual)
			}
		})
	}
}
+21 −0
Original line number Diff line number Diff line
@@ -911,6 +911,26 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
			installed := ctx.InstallFile(a.installDir, extra.Base(), extra)
			extraInstalledPaths = append(extraInstalledPaths, installed)
		}
		// If we don't embed jni libs, make sure that those are installed along with the
		// app, and also place symlinks to the installed paths under the lib/<arch>
		// directory of the app installation directory. ex:
		// /system/app/MyApp/lib/arm64/libfoo.so -> /system/lib64/libfoo.so
		if !a.embeddedJniLibs {
			for _, jniLib := range jniLibs {
				archStr := jniLib.target.Arch.ArchType.String()
				symlinkDir := a.installDir.Join(ctx, "lib", archStr)
				for _, installedLib := range jniLib.installPaths {
					// install the symlink target along with the app
					extraInstalledPaths = append(extraInstalledPaths, installedLib)
					ctx.PackageFile(installedLib, "", jniLib.path)

					// install the symlink itself
					symlinkName := installedLib.Base()
					symlinkTarget := android.InstallPathToOnDevicePath(ctx, installedLib)
					ctx.InstallAbsoluteSymlink(symlinkDir, symlinkName, symlinkTarget)
				}
			}
		}
		ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile, extraInstalledPaths...)
	}

@@ -998,6 +1018,7 @@ func collectJniDeps(ctx android.ModuleContext,
						coverageFile:   dep.CoverageOutputFile(),
						unstrippedFile: dep.UnstrippedOutputFile(),
						partition:      dep.Partition(),
						installPaths:   dep.FilesToInstall(),
					})
				} else if ctx.Config().AllowMissingDependencies() {
					ctx.AddMissingDependencies([]string{otherName})
+1 −0
Original line number Diff line number Diff line
@@ -491,6 +491,7 @@ type jniLib struct {
	coverageFile   android.OptionalPath
	unstrippedFile android.Path
	partition      string
	installPaths   android.InstallPaths
}

func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext android.SdkContext, d dexer) {