summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libnativeloader/Android.bp2
-rw-r--r--libnativeloader/library_namespaces.cpp21
-rw-r--r--libnativeloader/library_namespaces.h1
-rw-r--r--libnativeloader/library_namespaces_test.cpp56
-rw-r--r--libnativeloader/native_loader.cpp78
-rw-r--r--libnativeloader/test/src/android/test/app/DataAppTest.java35
-rw-r--r--libnativeloader/test/src/android/test/app/ProductAppTest.java26
-rw-r--r--libnativeloader/test/src/android/test/app/VendorAppTest.java35
-rw-r--r--libnativeloader/test/src/android/test/lib/AppTestCommon.java18
-rw-r--r--libnativeloader/test/src/android/test/lib/TestUtils.java6
-rw-r--r--runtime/jni/java_vm_ext.cc12
11 files changed, 222 insertions, 68 deletions
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index 9d8fdb153c..9e298f5400 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -56,6 +56,7 @@ art_cc_library {
"native_loader.cpp",
],
header_libs: [
+ "art_libartbase_headers",
"libnativehelper_header_only",
],
shared_libs: [
@@ -88,6 +89,7 @@ art_cc_library {
],
static_libs: [
"libPlatformProperties",
+ "libmodules-utils-build",
],
},
},
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
index e2b27294f9..7b18b15dd0 100644
--- a/libnativeloader/library_namespaces.cpp
+++ b/libnativeloader/library_namespaces.cpp
@@ -85,6 +85,7 @@ constexpr const char* kProductLibPath = "/product/" LIB ":/system/product/" LIB;
const std::regex kVendorPathRegex("(/system)?/vendor/.*");
const std::regex kProductPathRegex("(/system)?/product/.*");
+const std::regex kSystemPathRegex("/system(_ext)?/.*"); // MUST be tested last.
jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
jclass class_loader_class = env->FindClass("java/lang/ClassLoader");
@@ -103,11 +104,15 @@ ApiDomain GetApiDomainFromPath(const std::string_view path) {
if (is_product_treblelized() && std::regex_match(path.begin(), path.end(), kProductPathRegex)) {
return API_DOMAIN_PRODUCT;
}
+ if (std::regex_match(path.begin(), path.end(), kSystemPathRegex)) {
+ return API_DOMAIN_SYSTEM;
+ }
return API_DOMAIN_DEFAULT;
}
// Returns the API domain for a ':'-separated list of paths, or an error if they
-// match more than one.
+// match more than one. This function does not recognize API_DOMAIN_SYSTEM and
+// will return API_DOMAIN_DEFAULT instead.
Result<ApiDomain> GetApiDomainFromPathList(const std::string& path_list) {
ApiDomain result = API_DOMAIN_DEFAULT;
size_t start_pos = 0;
@@ -115,13 +120,13 @@ Result<ApiDomain> GetApiDomainFromPathList(const std::string& path_list) {
size_t end_pos = path_list.find(':', start_pos);
ApiDomain api_domain =
GetApiDomainFromPath(std::string_view(path_list).substr(start_pos, end_pos));
- // Allow mixing API_DOMAIN_DEFAULT with any other domain. That's a bit lax,
- // since the default e.g. includes /data, which strictly speaking is a
- // separate domain. However, we keep it this way to not risk compat issues
- // until we actually need all domains.
- if (api_domain != API_DOMAIN_DEFAULT) {
- if (result != API_DOMAIN_DEFAULT && result != api_domain) {
- return Error() << "Path list crosses partition boundaries: " << path_list;
+ if (api_domain == API_DOMAIN_VENDOR || api_domain == API_DOMAIN_PRODUCT) {
+ if ((result == API_DOMAIN_VENDOR || result == API_DOMAIN_PRODUCT) && result != api_domain) {
+ // Fail only if the path list has both vendor and product paths. Allow
+ // combinations of either with API_DOMAIN_SYSTEM and API_DOMAIN_DEFAULT,
+ // because the path list we get here may contain shared Java system
+ // libraries and app APKs which may be in /data.
+ return Error() << "Path list crosses vendor/product partition boundaries: " << path_list;
}
result = api_domain;
}
diff --git a/libnativeloader/library_namespaces.h b/libnativeloader/library_namespaces.h
index ae1cd88f20..b932a557e0 100644
--- a/libnativeloader/library_namespaces.h
+++ b/libnativeloader/library_namespaces.h
@@ -53,6 +53,7 @@ using ApiDomain = enum {
API_DOMAIN_DEFAULT = 0, // Locations other than those below, in particular for ordinary apps
API_DOMAIN_VENDOR = 1, // Vendor partition
API_DOMAIN_PRODUCT = 2, // Product partition
+ API_DOMAIN_SYSTEM = 3, // System and system_ext partitions
};
ApiDomain GetApiDomainFromPath(const std::string_view path);
diff --git a/libnativeloader/library_namespaces_test.cpp b/libnativeloader/library_namespaces_test.cpp
index 7780418846..4582223f4a 100644
--- a/libnativeloader/library_namespaces_test.cpp
+++ b/libnativeloader/library_namespaces_test.cpp
@@ -31,40 +31,45 @@ using ::android::base::testing::HasValue;
using ::android::base::testing::WithMessage;
using ::testing::StartsWith;
-TEST(LibraryNamespacesTest, TestGetApiDomainFromPath) {
+static ApiDomain GetProductApiDomain(ApiDomain fallback_domain) {
// GetApiDomainFromPath returns API_DOMAIN_PRODUCT only if the device is
// trebleized and has an unbundled product partition.
- ApiDomain api_domain_product = is_product_treblelized() ? API_DOMAIN_PRODUCT : API_DOMAIN_DEFAULT;
+ return is_product_treblelized() ? API_DOMAIN_PRODUCT : fallback_domain;
+}
+TEST(LibraryNamespacesTest, TestGetApiDomainFromPath) {
EXPECT_EQ(GetApiDomainFromPath("/data/somewhere"), API_DOMAIN_DEFAULT);
- EXPECT_EQ(GetApiDomainFromPath("/system/somewhere"), API_DOMAIN_DEFAULT);
- EXPECT_EQ(GetApiDomainFromPath("/product/somewhere"), api_domain_product);
+ EXPECT_EQ(GetApiDomainFromPath("/system/somewhere"), API_DOMAIN_SYSTEM);
+ EXPECT_EQ(GetApiDomainFromPath("/system_ext/somewhere"), API_DOMAIN_SYSTEM);
+ EXPECT_EQ(GetApiDomainFromPath("/systemext/somewhere"), API_DOMAIN_DEFAULT);
+ EXPECT_EQ(GetApiDomainFromPath("/product/somewhere"), GetProductApiDomain(API_DOMAIN_DEFAULT));
EXPECT_EQ(GetApiDomainFromPath("/vendor/somewhere"), API_DOMAIN_VENDOR);
- EXPECT_EQ(GetApiDomainFromPath("/system/product/somewhere"), api_domain_product);
+ EXPECT_EQ(GetApiDomainFromPath("/system/product/somewhere"),
+ GetProductApiDomain(API_DOMAIN_SYSTEM));
EXPECT_EQ(GetApiDomainFromPath("/system/vendor/somewhere"), API_DOMAIN_VENDOR);
EXPECT_EQ(GetApiDomainFromPath(""), API_DOMAIN_DEFAULT);
EXPECT_EQ(GetApiDomainFromPath("/"), API_DOMAIN_DEFAULT);
EXPECT_EQ(GetApiDomainFromPath("product/somewhere"), API_DOMAIN_DEFAULT);
EXPECT_EQ(GetApiDomainFromPath("/product"), API_DOMAIN_DEFAULT);
- EXPECT_EQ(GetApiDomainFromPath("/product/"), api_domain_product);
+ EXPECT_EQ(GetApiDomainFromPath("/product/"), GetProductApiDomain(API_DOMAIN_DEFAULT));
EXPECT_EQ(GetApiDomainFromPath(":/product/"), API_DOMAIN_DEFAULT);
EXPECT_EQ(GetApiDomainFromPath("/data/somewhere:/product/somewhere"), API_DOMAIN_DEFAULT);
EXPECT_EQ(GetApiDomainFromPath("/vendor/somewhere:/product/somewhere"), API_DOMAIN_VENDOR);
- EXPECT_EQ(GetApiDomainFromPath("/product/somewhere:/vendor/somewhere"), api_domain_product);
+ EXPECT_EQ(GetApiDomainFromPath("/product/somewhere:/vendor/somewhere"),
+ GetProductApiDomain(API_DOMAIN_DEFAULT));
}
TEST(LibraryNamespacesTest, TestGetApiDomainFromPathList) {
- // GetApiDomainFromPath returns API_DOMAIN_PRODUCT only if the device is
- // trebleized and has an unbundled product partition.
- ApiDomain api_domain_product = is_product_treblelized() ? API_DOMAIN_PRODUCT : API_DOMAIN_DEFAULT;
-
EXPECT_THAT(GetApiDomainFromPathList("/data/somewhere"), HasValue(API_DOMAIN_DEFAULT));
EXPECT_THAT(GetApiDomainFromPathList("/system/somewhere"), HasValue(API_DOMAIN_DEFAULT));
- EXPECT_THAT(GetApiDomainFromPathList("/product/somewhere"), HasValue(api_domain_product));
+ EXPECT_THAT(GetApiDomainFromPathList("/system_ext/somewhere"), HasValue(API_DOMAIN_DEFAULT));
+ EXPECT_THAT(GetApiDomainFromPathList("/product/somewhere"),
+ HasValue(GetProductApiDomain(API_DOMAIN_DEFAULT)));
EXPECT_THAT(GetApiDomainFromPathList("/vendor/somewhere"), HasValue(API_DOMAIN_VENDOR));
- EXPECT_THAT(GetApiDomainFromPathList("/system/product/somewhere"), HasValue(api_domain_product));
+ EXPECT_THAT(GetApiDomainFromPathList("/system/product/somewhere"),
+ HasValue(GetProductApiDomain(API_DOMAIN_DEFAULT)));
EXPECT_THAT(GetApiDomainFromPathList("/system/vendor/somewhere"), HasValue(API_DOMAIN_VENDOR));
EXPECT_THAT(GetApiDomainFromPathList(""), HasValue(API_DOMAIN_DEFAULT));
@@ -73,12 +78,25 @@ TEST(LibraryNamespacesTest, TestGetApiDomainFromPathList) {
EXPECT_THAT(GetApiDomainFromPathList("/vendor/somewhere:"), HasValue(API_DOMAIN_VENDOR));
EXPECT_THAT(GetApiDomainFromPathList("/data/somewhere:/product/somewhere"),
- HasValue(api_domain_product));
- if (api_domain_product == API_DOMAIN_PRODUCT) {
- EXPECT_THAT(GetApiDomainFromPathList("/vendor/somewhere:/product/somewhere"),
- HasError(WithMessage(StartsWith("Path list crosses partition boundaries"))));
- EXPECT_THAT(GetApiDomainFromPathList("/product/somewhere:/vendor/somewhere"),
- HasError(WithMessage(StartsWith("Path list crosses partition boundaries"))));
+ HasValue(GetProductApiDomain(API_DOMAIN_DEFAULT)));
+ EXPECT_THAT(GetApiDomainFromPathList("/system/somewhere:/product/somewhere"),
+ HasValue(GetProductApiDomain(API_DOMAIN_DEFAULT)));
+ EXPECT_THAT(GetApiDomainFromPathList("/product/somewhere:/system/somewhere"),
+ HasValue(GetProductApiDomain(API_DOMAIN_DEFAULT)));
+ EXPECT_THAT(GetApiDomainFromPathList("/data/somewhere:/vendor/somewhere"),
+ HasValue(API_DOMAIN_VENDOR));
+ EXPECT_THAT(GetApiDomainFromPathList("/system/somewhere:/vendor/somewhere"),
+ HasValue(API_DOMAIN_VENDOR));
+ EXPECT_THAT(GetApiDomainFromPathList("/vendor/somewhere:/system/somewhere"),
+ HasValue(API_DOMAIN_VENDOR));
+
+ if (GetProductApiDomain(API_DOMAIN_DEFAULT) == API_DOMAIN_PRODUCT) {
+ EXPECT_THAT(
+ GetApiDomainFromPathList("/vendor/somewhere:/product/somewhere"),
+ HasError(WithMessage(StartsWith("Path list crosses vendor/product partition boundaries"))));
+ EXPECT_THAT(
+ GetApiDomainFromPathList("/system/somewhere:/product/somewhere:/vendor/somewhere"),
+ HasError(WithMessage(StartsWith("Path list crosses vendor/product partition boundaries"))));
}
}
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 61925431ef..5b4988ae48 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -25,6 +25,7 @@
#include <memory>
#include <mutex>
#include <optional>
+#include <regex>
#include <string>
#include <vector>
@@ -32,11 +33,13 @@
#include "android-base/macros.h"
#include "android-base/strings.h"
#include "android-base/thread_annotations.h"
+#include "base/macros.h"
#include "nativebridge/native_bridge.h"
#include "nativehelper/scoped_utf_chars.h"
#include "public_libraries.h"
#ifdef ART_TARGET_ANDROID
+#include "android-modules-utils/sdk_level.h"
#include "library_namespaces.h"
#include "log/log.h"
#include "nativeloader/dlext_namespaces.h"
@@ -51,6 +54,9 @@ namespace {
using ::android::base::Result;
using ::android::nativeloader::LibraryNamespaces;
+const std::regex kPartitionNativeLibPathRegex(
+ "/(system(_ext)?|(system/)?(vendor|product))/lib(64)?/.*");
+
// NATIVELOADER_DEFAULT_NAMESPACE_LIBS is an environment variable that can be
// used to list extra libraries (separated by ":") that libnativeloader will
// load from the default namespace. The libraries must be listed without paths,
@@ -87,6 +93,23 @@ std::optional<NativeLoaderNamespace> FindApexNamespace(const char* caller_locati
return std::nullopt;
}
+Result<NativeLoaderNamespace> GetNamespaceForApiDomain(nativeloader::ApiDomain api_domain,
+ bool is_bridged) {
+ switch (api_domain) {
+ case nativeloader::API_DOMAIN_VENDOR:
+ return NativeLoaderNamespace::GetExportedNamespace(nativeloader::kVendorNamespaceName,
+ is_bridged);
+ case nativeloader::API_DOMAIN_PRODUCT:
+ return NativeLoaderNamespace::GetExportedNamespace(nativeloader::kProductNamespaceName,
+ is_bridged);
+ case nativeloader::API_DOMAIN_SYSTEM:
+ return NativeLoaderNamespace::GetSystemNamespace(is_bridged);
+ default:
+ LOG_FATAL("Invalid API domain %d", api_domain);
+ UNREACHABLE();
+ }
+}
+
Result<void> CreateNativeloaderDefaultNamespaceLibsLink(NativeLoaderNamespace& ns)
REQUIRES(g_namespaces_mutex) {
const char* links = getenv("NATIVELOADER_DEFAULT_NAMESPACE_LIBS");
@@ -311,6 +334,61 @@ void* OpenNativeLibrary(JNIEnv* env,
return handle;
}
+ // If the caller is in any of the system image partitions and the library is
+ // in the same partition then load it without regards to public library
+ // restrictions. This is only done if the library is specified by an absolute
+ // path, so we don't affect the lookup process for libraries specified by name
+ // only.
+ if (caller_location != nullptr &&
+ // Check that the library is in the partition-wide native library
+ // location. Apps in the partition may have their own native libraries,
+ // and those should still be loaded with the app's classloader namespace.
+ std::regex_match(path, kPartitionNativeLibPathRegex) &&
+ // Don't do this if the system image is older than V, to avoid any compat
+ // issues with apps and shared libs in them.
+ android::modules::sdklevel::IsAtLeastV()) {
+ nativeloader::ApiDomain caller_api_domain = nativeloader::GetApiDomainFromPath(caller_location);
+ if (caller_api_domain != nativeloader::API_DOMAIN_DEFAULT) {
+ nativeloader::ApiDomain library_api_domain = nativeloader::GetApiDomainFromPath(path);
+
+ if (library_api_domain == caller_api_domain) {
+ bool is_bridged = false;
+ if (library_path_j != nullptr) {
+ ScopedUtfChars library_path_utf_chars(env, library_path_j);
+ if (library_path_utf_chars[0] != '\0') {
+ is_bridged = NativeBridgeIsPathSupported(library_path_utf_chars.c_str());
+ }
+ }
+
+ Result<NativeLoaderNamespace> ns = GetNamespaceForApiDomain(caller_api_domain, is_bridged);
+ if (!ns.ok()) {
+ ALOGD("Failed to find ns for caller %s in API domain %d to load %s (is_bridged=%b): %s",
+ caller_location,
+ caller_api_domain,
+ path,
+ is_bridged,
+ ns.error().message().c_str());
+ *error_msg = strdup(ns.error().message().c_str());
+ return nullptr;
+ }
+
+ *needs_native_bridge = ns.value().IsBridged();
+ Result<void*> handle = ns.value().Load(path);
+ ALOGD("Load %s using ns %s for caller %s in same partition (is_bridged=%b): %s",
+ path,
+ ns.value().name().c_str(),
+ caller_location,
+ is_bridged,
+ handle.ok() ? "ok" : handle.error().message().c_str());
+ if (!handle.ok()) {
+ *error_msg = strdup(handle.error().message().c_str());
+ return nullptr;
+ }
+ return handle.value();
+ }
+ }
+ }
+
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
{
diff --git a/libnativeloader/test/src/android/test/app/DataAppTest.java b/libnativeloader/test/src/android/test/app/DataAppTest.java
index 905b5bb0d1..44aa9111cd 100644
--- a/libnativeloader/test/src/android/test/app/DataAppTest.java
+++ b/libnativeloader/test/src/android/test/app/DataAppTest.java
@@ -67,11 +67,18 @@ public class DataAppTest extends AppTestCommon {
@Test
public void testLoadPrivateLibrariesViaSystemSharedLib() {
- // TODO(b/237577392): Loading a private native system library via a shared system library
- // ought to work.
- TestUtils.assertLibraryInaccessible(() -> SystemSharedLib.loadLibrary("system_private2"));
- TestUtils.assertLibraryInaccessible(
- () -> SystemSharedLib.loadLibrary("systemext_private2"));
+ if (TestUtils.canLoadPrivateLibsFromSamePartition()) {
+ // TODO(b/186729817): These loads work because the findLibrary call in
+ // loadLibrary0 in Runtime.java searches the system libs and converts
+ // them to absolute paths.
+ SystemSharedLib.loadLibrary("system_private2");
+ SystemSharedLib.loadLibrary("systemext_private2");
+ } else {
+ TestUtils.assertLibraryInaccessible(
+ () -> { SystemSharedLib.loadLibrary("system_private2"); });
+ TestUtils.assertLibraryInaccessible(
+ () -> { SystemSharedLib.loadLibrary("systemext_private2"); });
+ }
if (!TestUtils.skipPublicProductLibTests()) {
TestUtils.assertLibraryInaccessible(
@@ -83,12 +90,18 @@ public class DataAppTest extends AppTestCommon {
@Test
public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
- // TODO(b/237577392): Loading a private native system library via a shared system library
- // ought to work.
- TestUtils.assertLibraryInaccessible(
- () -> SystemExtSharedLib.loadLibrary("system_private3"));
- TestUtils.assertLibraryInaccessible(
- () -> SystemExtSharedLib.loadLibrary("systemext_private3"));
+ if (TestUtils.canLoadPrivateLibsFromSamePartition()) {
+ // TODO(b/186729817): These loads work because the findLibrary call in
+ // loadLibrary0 in Runtime.java searches the system libs and converts
+ // them to absolute paths.
+ SystemExtSharedLib.loadLibrary("system_private3");
+ SystemExtSharedLib.loadLibrary("systemext_private3");
+ } else {
+ TestUtils.assertLibraryInaccessible(
+ () -> { SystemExtSharedLib.loadLibrary("system_private3"); });
+ TestUtils.assertLibraryInaccessible(
+ () -> { SystemExtSharedLib.loadLibrary("systemext_private3"); });
+ }
if (!TestUtils.skipPublicProductLibTests()) {
TestUtils.assertLibraryInaccessible(
diff --git a/libnativeloader/test/src/android/test/app/ProductAppTest.java b/libnativeloader/test/src/android/test/app/ProductAppTest.java
index 4cf379c9c6..a42d191f7b 100644
--- a/libnativeloader/test/src/android/test/app/ProductAppTest.java
+++ b/libnativeloader/test/src/android/test/app/ProductAppTest.java
@@ -72,14 +72,21 @@ public class ProductAppTest extends AppTestCommon {
@Test
public void testLoadPrivateLibrariesViaSystemSharedLib() {
- if (!TestUtils.productAppsAreShared()) {
- // TODO(b/237577392): Loading a private native system library via a shared system
- // library ought to work.
+ if (TestUtils.productAppsAreShared() || TestUtils.canLoadPrivateLibsFromSamePartition()) {
+ // TODO(b/186729817): These loads work in the
+ // canLoadPrivateLibsFromSamePartition case because the findLibrary
+ // call in loadLibrary0 in Runtime.java searches the system libs and
+ // converts them to absolute paths.
+ SystemSharedLib.loadLibrary("system_private2");
+ SystemSharedLib.loadLibrary("systemext_private2");
+ } else {
TestUtils.assertLibraryInaccessible(
() -> SystemSharedLib.loadLibrary("system_private2"));
TestUtils.assertLibraryInaccessible(
() -> SystemSharedLib.loadLibrary("systemext_private2"));
+ }
+ if (!TestUtils.productAppsAreShared()) {
TestUtils.assertLibraryInaccessible(
() -> SystemSharedLib.loadLibrary("product_private2"));
}
@@ -89,14 +96,21 @@ public class ProductAppTest extends AppTestCommon {
@Test
public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
- if (!TestUtils.productAppsAreShared()) {
- // TODO(b/237577392): Loading a private native system library via a shared system
- // library ought to work.
+ if (TestUtils.productAppsAreShared() || TestUtils.canLoadPrivateLibsFromSamePartition()) {
+ // TODO(b/186729817): These loads work in the
+ // canLoadPrivateLibsFromSamePartition case because the findLibrary
+ // call in loadLibrary0 in Runtime.java searches the system libs and
+ // converts them to absolute paths.
+ SystemExtSharedLib.loadLibrary("system_private3");
+ SystemExtSharedLib.loadLibrary("systemext_private3");
+ } else {
TestUtils.assertLibraryInaccessible(
() -> SystemExtSharedLib.loadLibrary("system_private3"));
TestUtils.assertLibraryInaccessible(
() -> SystemExtSharedLib.loadLibrary("systemext_private3"));
+ }
+ if (!TestUtils.productAppsAreShared()) {
TestUtils.assertLibraryInaccessible(
() -> SystemExtSharedLib.loadLibrary("product_private3"));
}
diff --git a/libnativeloader/test/src/android/test/app/VendorAppTest.java b/libnativeloader/test/src/android/test/app/VendorAppTest.java
index 377f670c74..b0e141c68b 100644
--- a/libnativeloader/test/src/android/test/app/VendorAppTest.java
+++ b/libnativeloader/test/src/android/test/app/VendorAppTest.java
@@ -71,11 +71,18 @@ public class VendorAppTest extends AppTestCommon {
@Test
public void testLoadPrivateLibrariesViaSystemSharedLib() {
- // TODO(b/237577392): Loading a private native system library via a shared system library
- // ought to work.
- TestUtils.assertLibraryInaccessible(() -> SystemSharedLib.loadLibrary("system_private2"));
- TestUtils.assertLibraryInaccessible(
- () -> SystemSharedLib.loadLibrary("systemext_private2"));
+ if (TestUtils.canLoadPrivateLibsFromSamePartition()) {
+ // TODO(b/186729817): These loads work because the findLibrary call in
+ // loadLibrary0 in Runtime.java searches the system libs and converts
+ // them to absolute paths.
+ SystemSharedLib.loadLibrary("system_private2");
+ SystemSharedLib.loadLibrary("systemext_private2");
+ } else {
+ TestUtils.assertLibraryInaccessible(
+ () -> SystemSharedLib.loadLibrary("system_private2"));
+ TestUtils.assertLibraryInaccessible(
+ () -> SystemSharedLib.loadLibrary("systemext_private2"));
+ }
if (!TestUtils.skipPublicProductLibTests()) {
TestUtils.assertLibraryInaccessible(
@@ -87,12 +94,18 @@ public class VendorAppTest extends AppTestCommon {
@Test
public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
- // TODO(b/237577392): Loading a private native system library via a shared system library
- // ought to work.
- TestUtils.assertLibraryInaccessible(
- () -> SystemExtSharedLib.loadLibrary("system_private3"));
- TestUtils.assertLibraryInaccessible(
- () -> SystemExtSharedLib.loadLibrary("systemext_private3"));
+ if (TestUtils.canLoadPrivateLibsFromSamePartition()) {
+ // TODO(b/186729817): These loads work because the findLibrary call in
+ // loadLibrary0 in Runtime.java searches the system libs and converts
+ // them to absolute paths.
+ SystemExtSharedLib.loadLibrary("system_private3");
+ SystemExtSharedLib.loadLibrary("systemext_private3");
+ } else {
+ TestUtils.assertLibraryInaccessible(
+ () -> SystemExtSharedLib.loadLibrary("system_private3"));
+ TestUtils.assertLibraryInaccessible(
+ () -> SystemExtSharedLib.loadLibrary("systemext_private3"));
+ }
if (!TestUtils.skipPublicProductLibTests()) {
TestUtils.assertLibraryInaccessible(
diff --git a/libnativeloader/test/src/android/test/lib/AppTestCommon.java b/libnativeloader/test/src/android/test/lib/AppTestCommon.java
index 51f4655839..75d45b5ba7 100644
--- a/libnativeloader/test/src/android/test/lib/AppTestCommon.java
+++ b/libnativeloader/test/src/android/test/lib/AppTestCommon.java
@@ -41,7 +41,6 @@ public abstract class AppTestCommon {
private boolean systemPrivateLibsAccessibleFromAppNamespace() {
// Currently it only works from system apps. It also works from product
// apps on old versions where they were treated like system apps.
- // TODO(b/237577392): Fix this to work from system shared libs.
return getAppLocation() == AppLocation.SYSTEM
|| (getAppLocation() == AppLocation.PRODUCT && TestUtils.productAppsAreShared());
}
@@ -52,7 +51,6 @@ public abstract class AppTestCommon {
// In old versions where product apps were treated like system apps, the
// product private libs were included in the system namespace, so
// they're accessible both from system and product apps.
- // TODO(b/237577392): Fix this to work from product shared libs.
return (getAppLocation() == AppLocation.SYSTEM || getAppLocation() == AppLocation.PRODUCT)
&& TestUtils.productAppsAreShared();
}
@@ -60,7 +58,9 @@ public abstract class AppTestCommon {
// Detect exception where we don't switch from a shared system namespace to
// a product or vendor "unbundled" namespace when calling into
// ProductSharedLib and VendorSharedLib. That means they still can load
- // private system libs but not private libs in their own partition.
+ // private system libs but not private libs in their own partition (however
+ // the latter works anyway when canLoadPrivateLibsFromSamePartition() is
+ // true).
// TODO(mast): Stop propagating the shared property (isBundledApp in
// LoadedApk.java) down to public and vendor shared java libs?
private boolean noSwitchToVendorOrProductNamespace() {
@@ -72,7 +72,8 @@ public abstract class AppTestCommon {
@Test
public void testLoadPrivateLibrariesViaSystemSharedLibWithAbsolutePaths() {
- if (systemPrivateLibsAccessibleFromAppNamespace()) {
+ if (TestUtils.canLoadPrivateLibsFromSamePartition()
+ || systemPrivateLibsAccessibleFromAppNamespace()) {
SystemSharedLib.load(TestUtils.libPath("/system", "system_private7"));
SystemSharedLib.load(TestUtils.libPath("/system_ext", "systemext_private7"));
} else {
@@ -98,7 +99,8 @@ public abstract class AppTestCommon {
@Test
public void testLoadPrivateLibrariesViaSystemExtSharedLibWithAbsolutePaths() {
- if (systemPrivateLibsAccessibleFromAppNamespace()) {
+ if (TestUtils.canLoadPrivateLibsFromSamePartition()
+ || systemPrivateLibsAccessibleFromAppNamespace()) {
SystemExtSharedLib.load(TestUtils.libPath("/system", "system_private8"));
SystemExtSharedLib.load(TestUtils.libPath("/system_ext", "systemext_private8"));
} else {
@@ -145,7 +147,8 @@ public abstract class AppTestCommon {
loadPrivateProductLib = getAppLocation() == AppLocation.SYSTEM
|| getAppLocation() == AppLocation.PRODUCT;
} else {
- loadPrivateProductLib = !noSwitchToVendorOrProductNamespace();
+ loadPrivateProductLib = TestUtils.canLoadPrivateLibsFromSamePartition()
+ || !noSwitchToVendorOrProductNamespace();
}
if (loadPrivateProductLib) {
ProductSharedLib.load(TestUtils.libPath("/product", "product_private9"));
@@ -181,7 +184,8 @@ public abstract class AppTestCommon {
});
}
- if (!noSwitchToVendorOrProductNamespace()) {
+ if (TestUtils.canLoadPrivateLibsFromSamePartition()
+ || !noSwitchToVendorOrProductNamespace()) {
VendorSharedLib.load(TestUtils.libPath("/vendor", "vendor_private10"));
} else {
TestUtils.assertLibraryInaccessible(() -> {
diff --git a/libnativeloader/test/src/android/test/lib/TestUtils.java b/libnativeloader/test/src/android/test/lib/TestUtils.java
index 5f5cd911e8..f91eab230a 100644
--- a/libnativeloader/test/src/android/test/lib/TestUtils.java
+++ b/libnativeloader/test/src/android/test/lib/TestUtils.java
@@ -55,6 +55,12 @@ public final class TestUtils {
return !SdkLevel.isAtLeastU() && SystemProperties.get("ro.product.vndk.version").isEmpty();
}
+ // True if apps and shared Java libs in system/product/vendor partitions are
+ // able to load private native libs in the same partition.
+ public static boolean canLoadPrivateLibsFromSamePartition() {
+ return SdkLevel.isAtLeastV();
+ }
+
// Test that private libs are present, as a safeguard so that the dlopen
// failures we expect in other tests aren't due to them not being there.
public static void testPrivateLibsExist(String libDir, String libStem) {
diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc
index 2a364f37b1..bbe3970839 100644
--- a/runtime/jni/java_vm_ext.cc
+++ b/runtime/jni/java_vm_ext.cc
@@ -958,12 +958,12 @@ bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
if (class_linker->IsBootClassLoader(loader)) {
loader = nullptr;
class_loader = nullptr;
- if (caller_class != nullptr) {
- ObjPtr<mirror::Class> caller = soa.Decode<mirror::Class>(caller_class);
- ObjPtr<mirror::DexCache> dex_cache = caller->GetDexCache();
- if (dex_cache != nullptr) {
- caller_location = dex_cache->GetLocation()->ToModifiedUtf8();
- }
+ }
+ if (caller_class != nullptr) {
+ ObjPtr<mirror::Class> caller = soa.Decode<mirror::Class>(caller_class);
+ ObjPtr<mirror::DexCache> dex_cache = caller->GetDexCache();
+ if (dex_cache != nullptr) {
+ caller_location = dex_cache->GetLocation()->ToModifiedUtf8();
}
}