blob: 3ce3ca95736236291c19b5d83730aa51d9533b35 [file] [log] [blame]
Orion Hodson9b16e342019-10-09 13:29:16 +01001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <dlfcn.h>
18#include <memory>
19#include <unordered_map>
20
21#include <android-base/strings.h>
22#include <gmock/gmock.h>
23#include <gtest/gtest.h>
24#include <jni.h>
25
26#include "native_loader_namespace.h"
27#include "nativeloader/dlext_namespaces.h"
28#include "nativeloader/native_loader.h"
29#include "public_libraries.h"
30
Orion Hodson9b16e342019-10-09 13:29:16 +010031namespace android {
32namespace nativeloader {
33
Martin Stjernholm48297332019-11-12 21:21:32 +000034using ::testing::Eq;
35using ::testing::Return;
36using ::testing::StrEq;
37using ::testing::_;
38using internal::ConfigEntry;
39using internal::ParseConfig;
40
Orion Hodson9b16e342019-10-09 13:29:16 +010041// gmock interface that represents interested platform APIs on libdl and libnativebridge
42class Platform {
43 public:
44 virtual ~Platform() {}
45
46 // libdl APIs
47 virtual void* dlopen(const char* filename, int flags) = 0;
48 virtual int dlclose(void* handle) = 0;
49 virtual char* dlerror(void) = 0;
50
51 // These mock_* are the APIs semantically the same across libdl and libnativebridge.
52 // Instead of having two set of mock APIs for the two, define only one set with an additional
53 // argument 'bool bridged' to identify the context (i.e., called for libdl or libnativebridge).
54 typedef char* mock_namespace_handle;
55 virtual bool mock_init_anonymous_namespace(bool bridged, const char* sonames,
56 const char* search_paths) = 0;
57 virtual mock_namespace_handle mock_create_namespace(
58 bool bridged, const char* name, const char* ld_library_path, const char* default_library_path,
59 uint64_t type, const char* permitted_when_isolated_path, mock_namespace_handle parent) = 0;
60 virtual bool mock_link_namespaces(bool bridged, mock_namespace_handle from,
61 mock_namespace_handle to, const char* sonames) = 0;
62 virtual mock_namespace_handle mock_get_exported_namespace(bool bridged, const char* name) = 0;
63 virtual void* mock_dlopen_ext(bool bridged, const char* filename, int flags,
64 mock_namespace_handle ns) = 0;
65
66 // libnativebridge APIs for which libdl has no corresponding APIs
67 virtual bool NativeBridgeInitialized() = 0;
68 virtual const char* NativeBridgeGetError() = 0;
69 virtual bool NativeBridgeIsPathSupported(const char*) = 0;
70 virtual bool NativeBridgeIsSupported(const char*) = 0;
71
72 // To mock "ClassLoader Object.getParent()"
73 virtual const char* JniObject_getParent(const char*) = 0;
74};
75
76// The mock does not actually create a namespace object. But simply casts the pointer to the
77// string for the namespace name as the handle to the namespace object.
78#define TO_ANDROID_NAMESPACE(str) \
79 reinterpret_cast<struct android_namespace_t*>(const_cast<char*>(str))
80
81#define TO_BRIDGED_NAMESPACE(str) \
82 reinterpret_cast<struct native_bridge_namespace_t*>(const_cast<char*>(str))
83
84#define TO_MOCK_NAMESPACE(ns) reinterpret_cast<Platform::mock_namespace_handle>(ns)
85
86// These represents built-in namespaces created by the linker according to ld.config.txt
87static std::unordered_map<std::string, Platform::mock_namespace_handle> namespaces = {
88 {"platform", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("platform"))},
89 {"default", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("default"))},
90 {"art", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("art"))},
91 {"sphal", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("sphal"))},
92 {"vndk", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("vndk"))},
93 {"neuralnetworks", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("neuralnetworks"))},
94};
95
96// The actual gmock object
97class MockPlatform : public Platform {
98 public:
99 explicit MockPlatform(bool is_bridged) : is_bridged_(is_bridged) {
100 ON_CALL(*this, NativeBridgeIsSupported(_)).WillByDefault(Return(is_bridged_));
101 ON_CALL(*this, NativeBridgeIsPathSupported(_)).WillByDefault(Return(is_bridged_));
102 ON_CALL(*this, mock_get_exported_namespace(_, _))
Martin Stjernholm48297332019-11-12 21:21:32 +0000103 .WillByDefault(testing::Invoke([](bool, const char* name) -> mock_namespace_handle {
Orion Hodson9b16e342019-10-09 13:29:16 +0100104 if (namespaces.find(name) != namespaces.end()) {
105 return namespaces[name];
106 }
107 return TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("(namespace not found"));
108 }));
109 }
110
111 // Mocking libdl APIs
112 MOCK_METHOD2(dlopen, void*(const char*, int));
113 MOCK_METHOD1(dlclose, int(void*));
114 MOCK_METHOD0(dlerror, char*());
115
116 // Mocking the common APIs
117 MOCK_METHOD3(mock_init_anonymous_namespace, bool(bool, const char*, const char*));
118 MOCK_METHOD7(mock_create_namespace,
119 mock_namespace_handle(bool, const char*, const char*, const char*, uint64_t,
120 const char*, mock_namespace_handle));
121 MOCK_METHOD4(mock_link_namespaces,
122 bool(bool, mock_namespace_handle, mock_namespace_handle, const char*));
123 MOCK_METHOD2(mock_get_exported_namespace, mock_namespace_handle(bool, const char*));
124 MOCK_METHOD4(mock_dlopen_ext, void*(bool, const char*, int, mock_namespace_handle));
125
126 // Mocking libnativebridge APIs
127 MOCK_METHOD0(NativeBridgeInitialized, bool());
128 MOCK_METHOD0(NativeBridgeGetError, const char*());
129 MOCK_METHOD1(NativeBridgeIsPathSupported, bool(const char*));
130 MOCK_METHOD1(NativeBridgeIsSupported, bool(const char*));
131
132 // Mocking "ClassLoader Object.getParent()"
133 MOCK_METHOD1(JniObject_getParent, const char*(const char*));
134
135 private:
136 bool is_bridged_;
137};
138
139static std::unique_ptr<MockPlatform> mock;
140
141// Provide C wrappers for the mock object.
142extern "C" {
143void* dlopen(const char* file, int flag) {
144 return mock->dlopen(file, flag);
145}
146
147int dlclose(void* handle) {
148 return mock->dlclose(handle);
149}
150
151char* dlerror(void) {
152 return mock->dlerror();
153}
154
155bool android_init_anonymous_namespace(const char* sonames, const char* search_path) {
156 return mock->mock_init_anonymous_namespace(false, sonames, search_path);
157}
158
159struct android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path,
160 const char* default_library_path,
161 uint64_t type,
162 const char* permitted_when_isolated_path,
163 struct android_namespace_t* parent) {
164 return TO_ANDROID_NAMESPACE(
165 mock->mock_create_namespace(false, name, ld_library_path, default_library_path, type,
166 permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent)));
167}
168
169bool android_link_namespaces(struct android_namespace_t* from, struct android_namespace_t* to,
170 const char* sonames) {
171 return mock->mock_link_namespaces(false, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames);
172}
173
174struct android_namespace_t* android_get_exported_namespace(const char* name) {
175 return TO_ANDROID_NAMESPACE(mock->mock_get_exported_namespace(false, name));
176}
177
178void* android_dlopen_ext(const char* filename, int flags, const android_dlextinfo* info) {
179 return mock->mock_dlopen_ext(false, filename, flags, TO_MOCK_NAMESPACE(info->library_namespace));
180}
181
182// libnativebridge APIs
183bool NativeBridgeIsSupported(const char* libpath) {
184 return mock->NativeBridgeIsSupported(libpath);
185}
186
187struct native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name) {
188 return TO_BRIDGED_NAMESPACE(mock->mock_get_exported_namespace(true, name));
189}
190
191struct native_bridge_namespace_t* NativeBridgeCreateNamespace(
192 const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type,
193 const char* permitted_when_isolated_path, struct native_bridge_namespace_t* parent) {
194 return TO_BRIDGED_NAMESPACE(
195 mock->mock_create_namespace(true, name, ld_library_path, default_library_path, type,
196 permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent)));
197}
198
199bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from,
200 struct native_bridge_namespace_t* to, const char* sonames) {
201 return mock->mock_link_namespaces(true, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames);
202}
203
204void* NativeBridgeLoadLibraryExt(const char* libpath, int flag,
205 struct native_bridge_namespace_t* ns) {
206 return mock->mock_dlopen_ext(true, libpath, flag, TO_MOCK_NAMESPACE(ns));
207}
208
209bool NativeBridgeInitialized() {
210 return mock->NativeBridgeInitialized();
211}
212
213bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames,
214 const char* anon_ns_library_path) {
215 return mock->mock_init_anonymous_namespace(true, public_ns_sonames, anon_ns_library_path);
216}
217
218const char* NativeBridgeGetError() {
219 return mock->NativeBridgeGetError();
220}
221
222bool NativeBridgeIsPathSupported(const char* path) {
223 return mock->NativeBridgeIsPathSupported(path);
224}
225
226} // extern "C"
227
228// A very simple JNI mock.
229// jstring is a pointer to utf8 char array. We don't need utf16 char here.
230// jobject, jclass, and jmethodID are also a pointer to utf8 char array
231// Only a few JNI methods that are actually used in libnativeloader are mocked.
232JNINativeInterface* CreateJNINativeInterface() {
233 JNINativeInterface* inf = new JNINativeInterface();
234 memset(inf, 0, sizeof(JNINativeInterface));
235
236 inf->GetStringUTFChars = [](JNIEnv*, jstring s, jboolean*) -> const char* {
237 return reinterpret_cast<const char*>(s);
238 };
239
240 inf->ReleaseStringUTFChars = [](JNIEnv*, jstring, const char*) -> void { return; };
241
242 inf->NewStringUTF = [](JNIEnv*, const char* bytes) -> jstring {
243 return reinterpret_cast<jstring>(const_cast<char*>(bytes));
244 };
245
246 inf->FindClass = [](JNIEnv*, const char* name) -> jclass {
247 return reinterpret_cast<jclass>(const_cast<char*>(name));
248 };
249
250 inf->CallObjectMethodV = [](JNIEnv*, jobject obj, jmethodID mid, va_list) -> jobject {
251 if (strcmp("getParent", reinterpret_cast<const char*>(mid)) == 0) {
252 // JniObject_getParent can be a valid jobject or nullptr if there is
253 // no parent classloader.
254 const char* ret = mock->JniObject_getParent(reinterpret_cast<const char*>(obj));
255 return reinterpret_cast<jobject>(const_cast<char*>(ret));
256 }
257 return nullptr;
258 };
259
260 inf->GetMethodID = [](JNIEnv*, jclass, const char* name, const char*) -> jmethodID {
261 return reinterpret_cast<jmethodID>(const_cast<char*>(name));
262 };
263
264 inf->NewWeakGlobalRef = [](JNIEnv*, jobject obj) -> jobject { return obj; };
265
266 inf->IsSameObject = [](JNIEnv*, jobject a, jobject b) -> jboolean {
267 return strcmp(reinterpret_cast<const char*>(a), reinterpret_cast<const char*>(b)) == 0;
268 };
269
270 return inf;
271}
272
273static void* const any_nonnull = reinterpret_cast<void*>(0x12345678);
274
275// Custom matcher for comparing namespace handles
276MATCHER_P(NsEq, other, "") {
277 *result_listener << "comparing " << reinterpret_cast<const char*>(arg) << " and " << other;
278 return strcmp(reinterpret_cast<const char*>(arg), reinterpret_cast<const char*>(other)) == 0;
279}
280
281/////////////////////////////////////////////////////////////////
282
283// Test fixture
284class NativeLoaderTest : public ::testing::TestWithParam<bool> {
285 protected:
286 bool IsBridged() { return GetParam(); }
287
288 void SetUp() override {
Martin Stjernholm48297332019-11-12 21:21:32 +0000289 mock = std::make_unique<testing::NiceMock<MockPlatform>>(IsBridged());
Orion Hodson9b16e342019-10-09 13:29:16 +0100290
291 env = std::make_unique<JNIEnv>();
292 env->functions = CreateJNINativeInterface();
293 }
294
295 void SetExpectations() {
296 std::vector<std::string> default_public_libs =
297 android::base::Split(preloadable_public_libraries(), ":");
298 for (auto l : default_public_libs) {
299 EXPECT_CALL(*mock, dlopen(StrEq(l.c_str()), RTLD_NOW | RTLD_NODELETE))
300 .WillOnce(Return(any_nonnull));
301 }
302 }
303
304 void RunTest() { InitializeNativeLoader(); }
305
306 void TearDown() override {
307 ResetNativeLoader();
308 delete env->functions;
309 mock.reset();
310 }
311
312 std::unique_ptr<JNIEnv> env;
313};
314
315/////////////////////////////////////////////////////////////////
316
317TEST_P(NativeLoaderTest, InitializeLoadsDefaultPublicLibraries) {
318 SetExpectations();
319 RunTest();
320}
321
322INSTANTIATE_TEST_SUITE_P(NativeLoaderTests, NativeLoaderTest, testing::Bool());
323
324/////////////////////////////////////////////////////////////////
325
326class NativeLoaderTest_Create : public NativeLoaderTest {
327 protected:
328 // Test inputs (initialized to the default values). Overriding these
329 // must be done before calling SetExpectations() and RunTest().
330 uint32_t target_sdk_version = 29;
331 std::string class_loader = "my_classloader";
332 bool is_shared = false;
333 std::string dex_path = "/data/app/foo/classes.dex";
334 std::string library_path = "/data/app/foo/lib/arm";
335 std::string permitted_path = "/data/app/foo/lib";
336
337 // expected output (.. for the default test inputs)
338 std::string expected_namespace_name = "classloader-namespace";
339 uint64_t expected_namespace_flags =
340 ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS;
341 std::string expected_library_path = library_path;
342 std::string expected_permitted_path = std::string("/data:/mnt/expand:") + permitted_path;
343 std::string expected_parent_namespace = "platform";
344 bool expected_link_with_platform_ns = true;
345 bool expected_link_with_art_ns = true;
346 bool expected_link_with_sphal_ns = !vendor_public_libraries().empty();
347 bool expected_link_with_vndk_ns = false;
348 bool expected_link_with_default_ns = false;
349 bool expected_link_with_neuralnetworks_ns = true;
350 std::string expected_shared_libs_to_platform_ns = default_public_libraries();
351 std::string expected_shared_libs_to_art_ns = art_public_libraries();
352 std::string expected_shared_libs_to_sphal_ns = vendor_public_libraries();
353 std::string expected_shared_libs_to_vndk_ns = vndksp_libraries();
354 std::string expected_shared_libs_to_default_ns = default_public_libraries();
355 std::string expected_shared_libs_to_neuralnetworks_ns = neuralnetworks_public_libraries();
356
357 void SetExpectations() {
358 NativeLoaderTest::SetExpectations();
359
360 ON_CALL(*mock, JniObject_getParent(StrEq(class_loader))).WillByDefault(Return(nullptr));
361
Martin Stjernholm48297332019-11-12 21:21:32 +0000362 EXPECT_CALL(*mock, NativeBridgeIsPathSupported(_)).Times(testing::AnyNumber());
363 EXPECT_CALL(*mock, NativeBridgeInitialized()).Times(testing::AnyNumber());
Orion Hodson9b16e342019-10-09 13:29:16 +0100364
365 EXPECT_CALL(*mock, mock_create_namespace(
366 Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
367 StrEq(expected_library_path), expected_namespace_flags,
368 StrEq(expected_permitted_path), NsEq(expected_parent_namespace.c_str())))
369 .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(dex_path.c_str()))));
370 if (expected_link_with_platform_ns) {
371 EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("platform"),
372 StrEq(expected_shared_libs_to_platform_ns)))
373 .WillOnce(Return(true));
374 }
375 if (expected_link_with_art_ns) {
376 EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("art"),
377 StrEq(expected_shared_libs_to_art_ns)))
378 .WillOnce(Return(true));
379 }
380 if (expected_link_with_sphal_ns) {
381 EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("sphal"),
382 StrEq(expected_shared_libs_to_sphal_ns)))
383 .WillOnce(Return(true));
384 }
385 if (expected_link_with_vndk_ns) {
386 EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("vndk"),
387 StrEq(expected_shared_libs_to_vndk_ns)))
388 .WillOnce(Return(true));
389 }
390 if (expected_link_with_default_ns) {
391 EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("default"),
392 StrEq(expected_shared_libs_to_default_ns)))
393 .WillOnce(Return(true));
394 }
395 if (expected_link_with_neuralnetworks_ns) {
396 EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("neuralnetworks"),
397 StrEq(expected_shared_libs_to_neuralnetworks_ns)))
398 .WillOnce(Return(true));
399 }
400 }
401
402 void RunTest() {
403 NativeLoaderTest::RunTest();
404
405 jstring err = CreateClassLoaderNamespace(
406 env(), target_sdk_version, env()->NewStringUTF(class_loader.c_str()), is_shared,
407 env()->NewStringUTF(dex_path.c_str()), env()->NewStringUTF(library_path.c_str()),
408 env()->NewStringUTF(permitted_path.c_str()));
409
410 // no error
411 EXPECT_EQ(err, nullptr);
412
413 if (!IsBridged()) {
414 struct android_namespace_t* ns =
415 FindNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str()));
416
417 // The created namespace is for this apk
418 EXPECT_EQ(dex_path.c_str(), reinterpret_cast<const char*>(ns));
419 } else {
420 struct NativeLoaderNamespace* ns =
421 FindNativeLoaderNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str()));
422
423 // The created namespace is for the this apk
424 EXPECT_STREQ(dex_path.c_str(),
425 reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace()));
426 }
427 }
428
429 JNIEnv* env() { return NativeLoaderTest::env.get(); }
430};
431
432TEST_P(NativeLoaderTest_Create, DownloadedApp) {
433 SetExpectations();
434 RunTest();
435}
436
437TEST_P(NativeLoaderTest_Create, BundledSystemApp) {
438 dex_path = "/system/app/foo/foo.apk";
439 is_shared = true;
440
441 expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
442 SetExpectations();
443 RunTest();
444}
445
446TEST_P(NativeLoaderTest_Create, BundledVendorApp) {
447 dex_path = "/vendor/app/foo/foo.apk";
448 is_shared = true;
449
450 expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
451 SetExpectations();
452 RunTest();
453}
454
455TEST_P(NativeLoaderTest_Create, UnbundledVendorApp) {
456 dex_path = "/vendor/app/foo/foo.apk";
457 is_shared = false;
458
459 expected_namespace_name = "vendor-classloader-namespace";
460 expected_library_path = expected_library_path + ":/vendor/lib";
461 expected_permitted_path = expected_permitted_path + ":/vendor/lib";
462 expected_shared_libs_to_platform_ns =
463 expected_shared_libs_to_platform_ns + ":" + llndk_libraries();
464 expected_link_with_vndk_ns = true;
465 SetExpectations();
466 RunTest();
467}
468
469TEST_P(NativeLoaderTest_Create, BundledProductApp_pre30) {
470 dex_path = "/product/app/foo/foo.apk";
471 is_shared = true;
472
473 expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
474 SetExpectations();
475 RunTest();
476}
477
478TEST_P(NativeLoaderTest_Create, BundledProductApp_post30) {
479 dex_path = "/product/app/foo/foo.apk";
480 is_shared = true;
481 target_sdk_version = 30;
482
483 expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
484 SetExpectations();
485 RunTest();
486}
487
488TEST_P(NativeLoaderTest_Create, UnbundledProductApp_pre30) {
489 dex_path = "/product/app/foo/foo.apk";
490 is_shared = false;
491 SetExpectations();
492 RunTest();
493}
494
495TEST_P(NativeLoaderTest_Create, UnbundledProductApp_post30) {
496 dex_path = "/product/app/foo/foo.apk";
497 is_shared = false;
498 target_sdk_version = 30;
499
500 expected_namespace_name = "vendor-classloader-namespace";
501 expected_library_path = expected_library_path + ":/product/lib:/system/product/lib";
502 expected_permitted_path = expected_permitted_path + ":/product/lib:/system/product/lib";
503 expected_shared_libs_to_platform_ns =
504 expected_shared_libs_to_platform_ns + ":" + llndk_libraries();
505 expected_link_with_vndk_ns = true;
506 SetExpectations();
507 RunTest();
508}
509
510TEST_P(NativeLoaderTest_Create, NamespaceForSharedLibIsNotUsedAsAnonymousNamespace) {
511 if (IsBridged()) {
512 // There is no shared lib in translated arch
513 // TODO(jiyong): revisit this
514 return;
515 }
516 // compared to apks, for java shared libs, library_path is empty; java shared
517 // libs don't have their own native libs. They use platform's.
518 library_path = "";
519 expected_library_path = library_path;
520 // no ALSO_USED_AS_ANONYMOUS
521 expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
522 SetExpectations();
523 RunTest();
524}
525
526TEST_P(NativeLoaderTest_Create, TwoApks) {
527 SetExpectations();
528 const uint32_t second_app_target_sdk_version = 29;
529 const std::string second_app_class_loader = "second_app_classloader";
530 const bool second_app_is_shared = false;
531 const std::string second_app_dex_path = "/data/app/bar/classes.dex";
532 const std::string second_app_library_path = "/data/app/bar/lib/arm";
533 const std::string second_app_permitted_path = "/data/app/bar/lib";
534 const std::string expected_second_app_permitted_path =
535 std::string("/data:/mnt/expand:") + second_app_permitted_path;
536 const std::string expected_second_app_parent_namespace = "classloader-namespace";
537 // no ALSO_USED_AS_ANONYMOUS
538 const uint64_t expected_second_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
539
540 // The scenario is that second app is loaded by the first app.
541 // So the first app's classloader (`classloader`) is parent of the second
542 // app's classloader.
543 ON_CALL(*mock, JniObject_getParent(StrEq(second_app_class_loader)))
544 .WillByDefault(Return(class_loader.c_str()));
545
546 // namespace for the second app is created. Its parent is set to the namespace
547 // of the first app.
548 EXPECT_CALL(*mock, mock_create_namespace(
549 Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
550 StrEq(second_app_library_path), expected_second_namespace_flags,
551 StrEq(expected_second_app_permitted_path), NsEq(dex_path.c_str())))
552 .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(second_app_dex_path.c_str()))));
553 EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), NsEq(second_app_dex_path.c_str()), _, _))
554 .WillRepeatedly(Return(true));
555
556 RunTest();
557 jstring err = CreateClassLoaderNamespace(
558 env(), second_app_target_sdk_version, env()->NewStringUTF(second_app_class_loader.c_str()),
559 second_app_is_shared, env()->NewStringUTF(second_app_dex_path.c_str()),
560 env()->NewStringUTF(second_app_library_path.c_str()),
561 env()->NewStringUTF(second_app_permitted_path.c_str()));
562
563 // success
564 EXPECT_EQ(err, nullptr);
565
566 if (!IsBridged()) {
567 struct android_namespace_t* ns =
568 FindNamespaceByClassLoader(env(), env()->NewStringUTF(second_app_class_loader.c_str()));
569
570 // The created namespace is for the second apk
571 EXPECT_EQ(second_app_dex_path.c_str(), reinterpret_cast<const char*>(ns));
572 } else {
573 struct NativeLoaderNamespace* ns = FindNativeLoaderNamespaceByClassLoader(
574 env(), env()->NewStringUTF(second_app_class_loader.c_str()));
575
576 // The created namespace is for the second apk
577 EXPECT_STREQ(second_app_dex_path.c_str(),
578 reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace()));
579 }
580}
581
582INSTANTIATE_TEST_SUITE_P(NativeLoaderTests_Create, NativeLoaderTest_Create, testing::Bool());
583
584const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
585 [](const struct ConfigEntry&) -> Result<bool> { return true; };
586
587TEST(NativeLoaderConfigParser, NamesAndComments) {
588 const char file_content[] = R"(
589######
590
591libA.so
592#libB.so
593
594
595 libC.so
596libD.so
597 #### libE.so
598)";
599 const std::vector<std::string> expected_result = {"libA.so", "libC.so", "libD.so"};
600 Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
601 ASSERT_TRUE(result) << result.error().message();
602 ASSERT_EQ(expected_result, *result);
603}
604
605TEST(NativeLoaderConfigParser, WithBitness) {
606 const char file_content[] = R"(
607libA.so 32
608libB.so 64
609libC.so
610)";
611#if defined(__LP64__)
612 const std::vector<std::string> expected_result = {"libB.so", "libC.so"};
613#else
614 const std::vector<std::string> expected_result = {"libA.so", "libC.so"};
615#endif
616 Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
617 ASSERT_TRUE(result) << result.error().message();
618 ASSERT_EQ(expected_result, *result);
619}
620
621TEST(NativeLoaderConfigParser, WithNoPreload) {
622 const char file_content[] = R"(
623libA.so nopreload
624libB.so nopreload
625libC.so
626)";
627
628 const std::vector<std::string> expected_result = {"libC.so"};
629 Result<std::vector<std::string>> result =
630 ParseConfig(file_content,
631 [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
632 ASSERT_TRUE(result) << result.error().message();
633 ASSERT_EQ(expected_result, *result);
634}
635
636TEST(NativeLoaderConfigParser, WithNoPreloadAndBitness) {
637 const char file_content[] = R"(
638libA.so nopreload 32
639libB.so 64 nopreload
640libC.so 32
641libD.so 64
642libE.so nopreload
643)";
644
645#if defined(__LP64__)
646 const std::vector<std::string> expected_result = {"libD.so"};
647#else
648 const std::vector<std::string> expected_result = {"libC.so"};
649#endif
650 Result<std::vector<std::string>> result =
651 ParseConfig(file_content,
652 [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
653 ASSERT_TRUE(result) << result.error().message();
654 ASSERT_EQ(expected_result, *result);
655}
656
657TEST(NativeLoaderConfigParser, RejectMalformed) {
658 ASSERT_FALSE(ParseConfig("libA.so 32 64", always_true));
659 ASSERT_FALSE(ParseConfig("libA.so 32 32", always_true));
660 ASSERT_FALSE(ParseConfig("libA.so 32 nopreload 64", always_true));
661 ASSERT_FALSE(ParseConfig("32 libA.so nopreload", always_true));
662 ASSERT_FALSE(ParseConfig("nopreload libA.so 32", always_true));
663 ASSERT_FALSE(ParseConfig("libA.so nopreload # comment", always_true));
664}
665
666} // namespace nativeloader
667} // namespace android