enforce sdk_version for JNI libs for updatable APKs

JNI libs for "updatable" APKs or APKs in "updatable" APEXes should set
sdk_version which is equal to or less than APK's min_sdk_version.

In fact, we'd better check if min_sdk_version of JNI libs matches(or is
earlier than) min_sdk_version of the APK. But for now the build system
can't handle sdk_version/min_sdk_version correctly for JNI libs. That's
why sdk_version of JNI libs is enforced to match with min_sdk_version
of APK in this change.

Bug: 145796956
Test: m
Change-Id: Iee7e09dd25bd139d84d50f6a77836b4068c78ea9
diff --git a/java/app.go b/java/app.go
index d25575c..0237bbf 100755
--- a/java/app.go
+++ b/java/app.go
@@ -270,19 +270,47 @@
 }
 
 func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) {
-	if Bool(a.appProperties.Updatable) {
+	if Bool(a.appProperties.Updatable) || a.ApexModuleBase.Updatable() {
 		if !a.sdkVersion().stable() {
 			ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.sdkVersion())
 		}
 		if String(a.deviceProperties.Min_sdk_version) == "" {
 			ctx.PropertyErrorf("updatable", "updatable apps must set min_sdk_version.")
 		}
+		if minSdkVersion, err := a.minSdkVersion().effectiveVersion(ctx); err == nil {
+			a.checkJniLibsSdkVersion(ctx, minSdkVersion)
+		} else {
+			ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
+		}
 	}
 
 	a.checkPlatformAPI(ctx)
 	a.checkSdkVersions(ctx)
 }
 
+// If an updatable APK sets min_sdk_version, min_sdk_vesion of JNI libs should match with it.
+// This check is enforced for "updatable" APKs (including APK-in-APEX).
+// b/155209650: until min_sdk_version is properly supported, use sdk_version instead.
+// because, sdk_version is overridden by min_sdk_version (if set as smaller)
+// and linkType is checked with dependencies so we can be sure that the whole dependency tree
+// will meet the requirements.
+func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion sdkVersion) {
+	// It's enough to check direct JNI deps' sdk_version because all transitive deps from JNI deps are checked in cc.checkLinkType()
+	ctx.VisitDirectDeps(func(m android.Module) {
+		if !IsJniDepTag(ctx.OtherModuleDependencyTag(m)) {
+			return
+		}
+		dep, _ := m.(*cc.Module)
+		jniSdkVersion, err := android.ApiStrToNum(ctx, dep.SdkVersion())
+		if err != nil || int(minSdkVersion) < jniSdkVersion {
+			ctx.OtherModuleErrorf(dep, "sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)",
+				dep.SdkVersion(), a.MinSdkVersion(), ctx.ModuleName())
+			return
+		}
+
+	})
+}
+
 // Returns true if the native libraries should be stored in the APK uncompressed and the
 // extractNativeLibs application flag should be set to false in the manifest.
 func (a *AndroidApp) useEmbeddedNativeLibs(ctx android.ModuleContext) bool {
diff --git a/java/app_test.go b/java/app_test.go
index 998c76a..c4f1d3f 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -385,6 +385,127 @@
 	}
 }
 
+func TestUpdatableApps_JniLibsShouldShouldSupportMinSdkVersion(t *testing.T) {
+	testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			updatable: true,
+			sdk_version: "current",
+			min_sdk_version: "current",
+			jni_libs: ["libjni"],
+		}
+
+		cc_library {
+			name: "libjni",
+			stl: "none",
+			system_shared_libs: [],
+			sdk_version: "current",
+		}
+	`)
+}
+
+func TestUpdatableApps_JniLibShouldBeBuiltAgainstMinSdkVersion(t *testing.T) {
+	bp := cc.GatherRequiredDepsForTest(android.Android) + `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			updatable: true,
+			sdk_version: "current",
+			min_sdk_version: "29",
+			jni_libs: ["libjni"],
+		}
+
+		cc_library {
+			name: "libjni",
+			stl: "none",
+			system_shared_libs: [],
+			sdk_version: "29",
+		}
+
+		ndk_prebuilt_object {
+			name: "ndk_crtbegin_so.29",
+			sdk_version: "29",
+		}
+
+		ndk_prebuilt_object {
+			name: "ndk_crtend_so.29",
+			sdk_version: "29",
+		}
+	`
+	fs := map[string][]byte{
+		"prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtbegin_so.o": nil,
+		"prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtend_so.o":   nil,
+		"prebuilts/ndk/current/platforms/android-29/arch-arm/usr/lib/crtbegin_so.o":   nil,
+		"prebuilts/ndk/current/platforms/android-29/arch-arm/usr/lib/crtend_so.o":     nil,
+	}
+
+	ctx, _ := testJavaWithConfig(t, testConfig(nil, bp, fs))
+
+	inputs := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits
+	var crtbeginFound, crtendFound bool
+	for _, input := range inputs {
+		switch input.String() {
+		case "prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtbegin_so.o":
+			crtbeginFound = true
+		case "prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtend_so.o":
+			crtendFound = true
+		}
+	}
+	if !crtbeginFound || !crtendFound {
+		t.Error("should link with ndk_crtbegin_so.29 and ndk_crtend_so.29")
+	}
+}
+
+func TestUpdatableApps_ErrorIfJniLibDoesntSupportMinSdkVersion(t *testing.T) {
+	bp := cc.GatherRequiredDepsForTest(android.Android) + `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			updatable: true,
+			sdk_version: "current",
+			min_sdk_version: "29",  // this APK should support 29
+			jni_libs: ["libjni"],
+		}
+
+		cc_library {
+			name: "libjni",
+			stl: "none",
+			sdk_version: "current",
+		}
+	`
+	testJavaError(t, `"libjni" .*: sdk_version\(current\) is higher than min_sdk_version\(29\)`, bp)
+}
+
+func TestUpdatableApps_ErrorIfDepSdkVersionIsHigher(t *testing.T) {
+	bp := cc.GatherRequiredDepsForTest(android.Android) + `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			updatable: true,
+			sdk_version: "current",
+			min_sdk_version: "29",  // this APK should support 29
+			jni_libs: ["libjni"],
+		}
+
+		cc_library {
+			name: "libjni",
+			stl: "none",
+			shared_libs: ["libbar"],
+			system_shared_libs: [],
+			sdk_version: "27",
+		}
+
+		cc_library {
+			name: "libbar",
+			stl: "none",
+			system_shared_libs: [],
+			sdk_version: "current",
+		}
+	`
+	testJavaError(t, `"libjni" .*: links "libbar" built against newer API version "current"`, bp)
+}
+
 func TestResourceDirs(t *testing.T) {
 	testCases := []struct {
 		name      string