diff options
author | jgu21 | 2014-09-10 05:58:32 -0500 |
---|---|---|
committer | Andreas Gampe | 2014-09-23 22:08:25 -0500 |
commit | ab0da5a9a6860046619629b8e6b83692d35dff86 (patch) | |
tree | a59fe5a714cf4d1db957a7d26adc49c632e41354 /libnativebridge | |
parent | 8d35442b18e9dddfdde89c81e0cc910c70a12202 (diff) | |
download | platform-system-core-ab0da5a9a6860046619629b8e6b83692d35dff86.tar.gz platform-system-core-ab0da5a9a6860046619629b8e6b83692d35dff86.tar.xz platform-system-core-ab0da5a9a6860046619629b8e6b83692d35dff86.zip |
LibNativeBridge: Add early init & env setup
Add a method to set up /proc/cpuinfo with enough privileges. Set
up the environment for an app in InitializeNativeBridge().
Turn on -Wall for libnativebridge.
Change-Id: I0b93da93251c6b4638de786bf98cf99df07c3fc2
Diffstat (limited to 'libnativebridge')
-rw-r--r-- | libnativebridge/Android.mk | 4 | ||||
-rw-r--r-- | libnativebridge/native_bridge.cc | 224 | ||||
-rw-r--r-- | libnativebridge/tests/UnavailableNativeBridge_test.cpp | 2 |
3 files changed, 221 insertions, 9 deletions
diff --git a/libnativebridge/Android.mk b/libnativebridge/Android.mk index 3009bb959..6c2e43e32 100644 --- a/libnativebridge/Android.mk +++ b/libnativebridge/Android.mk | |||
@@ -13,7 +13,7 @@ LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES) | |||
13 | LOCAL_SHARED_LIBRARIES := liblog | 13 | LOCAL_SHARED_LIBRARIES := liblog |
14 | LOCAL_CLANG := true | 14 | LOCAL_CLANG := true |
15 | LOCAL_CPP_EXTENSION := .cc | 15 | LOCAL_CPP_EXTENSION := .cc |
16 | LOCAL_CFLAGS := -Werror | 16 | LOCAL_CFLAGS := -Werror -Wall |
17 | LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected | 17 | LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected |
18 | LOCAL_LDFLAGS := -ldl | 18 | LOCAL_LDFLAGS := -ldl |
19 | LOCAL_MULTILIB := both | 19 | LOCAL_MULTILIB := both |
@@ -30,7 +30,7 @@ LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES) | |||
30 | LOCAL_SHARED_LIBRARIES := liblog | 30 | LOCAL_SHARED_LIBRARIES := liblog |
31 | LOCAL_CLANG := true | 31 | LOCAL_CLANG := true |
32 | LOCAL_CPP_EXTENSION := .cc | 32 | LOCAL_CPP_EXTENSION := .cc |
33 | LOCAL_CFLAGS := -Werror | 33 | LOCAL_CFLAGS := -Werror -Wall |
34 | LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected | 34 | LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected |
35 | LOCAL_LDFLAGS := -ldl | 35 | LOCAL_LDFLAGS := -ldl |
36 | LOCAL_MULTILIB := both | 36 | LOCAL_MULTILIB := both |
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc index 6602d3fa1..8c0aa84ce 100644 --- a/libnativebridge/native_bridge.cc +++ b/libnativebridge/native_bridge.cc | |||
@@ -16,13 +16,27 @@ | |||
16 | 16 | ||
17 | #include "nativebridge/native_bridge.h" | 17 | #include "nativebridge/native_bridge.h" |
18 | 18 | ||
19 | #include <cstring> | ||
19 | #include <cutils/log.h> | 20 | #include <cutils/log.h> |
20 | #include <dlfcn.h> | 21 | #include <dlfcn.h> |
22 | #include <errno.h> | ||
23 | #include <fcntl.h> | ||
21 | #include <stdio.h> | 24 | #include <stdio.h> |
25 | #include <sys/mount.h> | ||
26 | #include <sys/stat.h> | ||
22 | 27 | ||
23 | 28 | ||
24 | namespace android { | 29 | namespace android { |
25 | 30 | ||
31 | // Environment values required by the apps running with native bridge. | ||
32 | struct NativeBridgeRuntimeValues { | ||
33 | const char* os_arch; | ||
34 | const char* cpu_abi; | ||
35 | const char* cpu_abi2; | ||
36 | const char* *supported_abis; | ||
37 | int32_t abi_count; | ||
38 | }; | ||
39 | |||
26 | // The symbol name exposed by native-bridge with the type of NativeBridgeCallbacks. | 40 | // The symbol name exposed by native-bridge with the type of NativeBridgeCallbacks. |
27 | static constexpr const char* kNativeBridgeInterfaceSymbol = "NativeBridgeItf"; | 41 | static constexpr const char* kNativeBridgeInterfaceSymbol = "NativeBridgeItf"; |
28 | 42 | ||
@@ -68,6 +82,11 @@ static NativeBridgeCallbacks* callbacks = nullptr; | |||
68 | // Callbacks provided by the environment to the bridge. Passed to LoadNativeBridge. | 82 | // Callbacks provided by the environment to the bridge. Passed to LoadNativeBridge. |
69 | static const NativeBridgeRuntimeCallbacks* runtime_callbacks = nullptr; | 83 | static const NativeBridgeRuntimeCallbacks* runtime_callbacks = nullptr; |
70 | 84 | ||
85 | // The app's data directory. | ||
86 | static char* app_data_dir = nullptr; | ||
87 | |||
88 | static constexpr uint32_t kNativeBridgeCallbackVersion = 1; | ||
89 | |||
71 | // Characters allowed in a native bridge filename. The first character must | 90 | // Characters allowed in a native bridge filename. The first character must |
72 | // be in [a-zA-Z] (expected 'l' for "libx"). The rest must be in [a-zA-Z0-9._-]. | 91 | // be in [a-zA-Z] (expected 'l' for "libx"). The rest must be in [a-zA-Z0-9._-]. |
73 | static bool CharacterAllowed(char c, bool first) { | 92 | static bool CharacterAllowed(char c, bool first) { |
@@ -109,6 +128,10 @@ bool NativeBridgeNameAcceptable(const char* nb_library_filename) { | |||
109 | } | 128 | } |
110 | } | 129 | } |
111 | 130 | ||
131 | static bool VersionCheck(NativeBridgeCallbacks* cb) { | ||
132 | return cb != nullptr && cb->version == kNativeBridgeCallbackVersion; | ||
133 | } | ||
134 | |||
112 | bool LoadNativeBridge(const char* nb_library_filename, | 135 | bool LoadNativeBridge(const char* nb_library_filename, |
113 | const NativeBridgeRuntimeCallbacks* runtime_cbs) { | 136 | const NativeBridgeRuntimeCallbacks* runtime_cbs) { |
114 | // We expect only one place that calls LoadNativeBridge: Runtime::Init. At that point we are not | 137 | // We expect only one place that calls LoadNativeBridge: Runtime::Init. At that point we are not |
@@ -116,8 +139,10 @@ bool LoadNativeBridge(const char* nb_library_filename, | |||
116 | 139 | ||
117 | if (state != NativeBridgeState::kNotSetup) { | 140 | if (state != NativeBridgeState::kNotSetup) { |
118 | // Setup has been called before. Ignore this call. | 141 | // Setup has been called before. Ignore this call. |
119 | ALOGW("Called LoadNativeBridge for an already set up native bridge. State is %s.", | 142 | if (nb_library_filename != nullptr) { // Avoids some log-spam for dalvikvm. |
120 | GetNativeBridgeStateString(state)); | 143 | ALOGW("Called LoadNativeBridge for an already set up native bridge. State is %s.", |
144 | GetNativeBridgeStateString(state)); | ||
145 | } | ||
121 | // Note: counts as an error, even though the bridge may be functional. | 146 | // Note: counts as an error, even though the bridge may be functional. |
122 | had_error = true; | 147 | had_error = true; |
123 | return false; | 148 | return false; |
@@ -137,8 +162,14 @@ bool LoadNativeBridge(const char* nb_library_filename, | |||
137 | callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle, | 162 | callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle, |
138 | kNativeBridgeInterfaceSymbol)); | 163 | kNativeBridgeInterfaceSymbol)); |
139 | if (callbacks != nullptr) { | 164 | if (callbacks != nullptr) { |
140 | // Store the handle for later. | 165 | if (VersionCheck(callbacks)) { |
141 | native_bridge_handle = handle; | 166 | // Store the handle for later. |
167 | native_bridge_handle = handle; | ||
168 | } else { | ||
169 | callbacks = nullptr; | ||
170 | dlclose(handle); | ||
171 | ALOGW("Unsupported native bridge interface."); | ||
172 | } | ||
142 | } else { | 173 | } else { |
143 | dlclose(handle); | 174 | dlclose(handle); |
144 | } | 175 | } |
@@ -158,13 +189,194 @@ bool LoadNativeBridge(const char* nb_library_filename, | |||
158 | } | 189 | } |
159 | } | 190 | } |
160 | 191 | ||
161 | bool InitializeNativeBridge() { | 192 | #if defined(__arm__) |
193 | static const char* kRuntimeISA = "arm"; | ||
194 | #elif defined(__aarch64__) | ||
195 | static const char* kRuntimeISA = "arm64"; | ||
196 | #elif defined(__mips__) | ||
197 | static const char* kRuntimeISA = "mips"; | ||
198 | #elif defined(__i386__) | ||
199 | static const char* kRuntimeISA = "x86"; | ||
200 | #elif defined(__x86_64__) | ||
201 | static const char* kRuntimeISA = "x86_64"; | ||
202 | #else | ||
203 | static const char* kRuntimeISA = "unknown"; | ||
204 | #endif | ||
205 | |||
206 | |||
207 | bool NeedsNativeBridge(const char* instruction_set) { | ||
208 | return strncmp(instruction_set, kRuntimeISA, strlen(kRuntimeISA)) != 0; | ||
209 | } | ||
210 | |||
211 | void PreInitializeNativeBridge(const char* app_data_dir_in, const char* instruction_set) { | ||
212 | if (app_data_dir_in == nullptr) { | ||
213 | return; | ||
214 | } | ||
215 | |||
216 | const size_t len = strlen(app_data_dir_in); | ||
217 | // Make a copy for us. | ||
218 | app_data_dir = new char[len]; | ||
219 | strncpy(app_data_dir, app_data_dir_in, len); | ||
220 | |||
221 | if (instruction_set == nullptr) { | ||
222 | return; | ||
223 | } | ||
224 | size_t isa_len = strlen(instruction_set); | ||
225 | if (isa_len > 10) { | ||
226 | // 10 is a loose upper bound on the currently known instruction sets (a tight bound is 7 for | ||
227 | // x86_64 [including the trailing \0]). This is so we don't have to change here if there will | ||
228 | // be another instruction set in the future. | ||
229 | ALOGW("Instruction set %s is malformed, must be less than 10 characters.", instruction_set); | ||
230 | return; | ||
231 | } | ||
232 | |||
233 | // Bind-mount /system/lib{,64}/<isa>/cpuinfo to /proc/cpuinfo. If the file does not exist, the | ||
234 | // mount command will fail, so we safe the extra file existence check... | ||
235 | char cpuinfo_path[1024]; | ||
236 | |||
237 | snprintf(cpuinfo_path, 1024, "/system/lib" | ||
238 | #ifdef __LP64__ | ||
239 | "64" | ||
240 | #endif | ||
241 | "/%s/cpuinfo", instruction_set); | ||
242 | |||
243 | // Bind-mount. | ||
244 | if (TEMP_FAILURE_RETRY(mount("/proc/cpuinfo", cpuinfo_path, nullptr, MS_BIND, nullptr)) == -1) { | ||
245 | ALOGW("Failed to bind-mount %s as /proc/cpuinfo: %d", cpuinfo_path, errno); | ||
246 | } | ||
247 | } | ||
248 | |||
249 | static void SetCpuAbi(JNIEnv* env, jclass build_class, const char* field, const char* value) { | ||
250 | if (value != nullptr) { | ||
251 | jfieldID field_id = env->GetStaticFieldID(build_class, field, "Ljava/lang/String;"); | ||
252 | if (field_id == nullptr) { | ||
253 | env->ExceptionClear(); | ||
254 | ALOGW("Could not find %s field.", field); | ||
255 | return; | ||
256 | } | ||
257 | |||
258 | jstring str = env->NewStringUTF(value); | ||
259 | if (str == nullptr) { | ||
260 | env->ExceptionClear(); | ||
261 | ALOGW("Could not create string %s.", value); | ||
262 | return; | ||
263 | } | ||
264 | |||
265 | env->SetStaticObjectField(build_class, field_id, str); | ||
266 | } | ||
267 | } | ||
268 | |||
269 | static void SetSupportedAbis(JNIEnv* env, jclass build_class, const char* field, | ||
270 | const char* *values, int32_t value_count) { | ||
271 | if (value_count < 0) { | ||
272 | return; | ||
273 | } | ||
274 | if (values == nullptr && value_count > 0) { | ||
275 | ALOGW("More than zero values expected: %d.", value_count); | ||
276 | return; | ||
277 | } | ||
278 | |||
279 | jfieldID field_id = env->GetStaticFieldID(build_class, field, "[Ljava/lang/String;"); | ||
280 | if (field_id != nullptr) { | ||
281 | // Create the array. | ||
282 | jobjectArray array = env->NewObjectArray(value_count, env->FindClass("java/lang/String"), | ||
283 | nullptr); | ||
284 | if (array == nullptr) { | ||
285 | env->ExceptionClear(); | ||
286 | ALOGW("Could not create array."); | ||
287 | return; | ||
288 | } | ||
289 | |||
290 | // Fill the array. | ||
291 | for (int32_t i = 0; i < value_count; i++) { | ||
292 | jstring str = env->NewStringUTF(values[i]); | ||
293 | if (str == nullptr) { | ||
294 | env->ExceptionClear(); | ||
295 | ALOGW("Could not create string %s.", values[i]); | ||
296 | return; | ||
297 | } | ||
298 | |||
299 | env->SetObjectArrayElement(array, i, str); | ||
300 | } | ||
301 | |||
302 | env->SetStaticObjectField(build_class, field_id, array); | ||
303 | } else { | ||
304 | env->ExceptionClear(); | ||
305 | ALOGW("Could not find %s field.", field); | ||
306 | } | ||
307 | } | ||
308 | |||
309 | // Set up the environment for the bridged app. | ||
310 | static void SetupEnvironment(NativeBridgeCallbacks* callbacks, JNIEnv* env, const char* isa) { | ||
311 | // Need a JNIEnv* to do anything. | ||
312 | if (env == nullptr) { | ||
313 | ALOGW("No JNIEnv* to set up app environment."); | ||
314 | return; | ||
315 | } | ||
316 | |||
317 | // Query the bridge for environment values. | ||
318 | const struct NativeBridgeRuntimeValues* env_values = callbacks->getAppEnv(isa); | ||
319 | if (env_values == nullptr) { | ||
320 | return; | ||
321 | } | ||
322 | |||
323 | // Keep the JNIEnv clean. | ||
324 | jint success = env->PushLocalFrame(16); // That should be small and large enough. | ||
325 | if (success < 0) { | ||
326 | // Out of memory, really borked. | ||
327 | ALOGW("Out of memory while setting up app environment."); | ||
328 | env->ExceptionClear(); | ||
329 | return; | ||
330 | } | ||
331 | |||
332 | // Reset CPU_ABI & CPU_ABI2 to values required by the apps running with native bridge. | ||
333 | if (env_values->cpu_abi != nullptr || env_values->cpu_abi2 != nullptr || | ||
334 | env_values->abi_count >= 0) { | ||
335 | jclass bclass_id = env->FindClass("android/os/Build"); | ||
336 | if (bclass_id != nullptr) { | ||
337 | SetCpuAbi(env, bclass_id, "CPU_ABI", env_values->cpu_abi); | ||
338 | SetCpuAbi(env, bclass_id, "CPU_ABI2", env_values->cpu_abi2); | ||
339 | |||
340 | SetSupportedAbis(env, bclass_id, "SUPPORTED_ABIS", env_values->supported_abis, | ||
341 | env_values->abi_count); | ||
342 | } else { | ||
343 | // For example in a host test environment. | ||
344 | env->ExceptionClear(); | ||
345 | ALOGW("Could not find Build class."); | ||
346 | } | ||
347 | } | ||
348 | |||
349 | if (env_values->os_arch != nullptr) { | ||
350 | jclass sclass_id = env->FindClass("java/lang/System"); | ||
351 | if (sclass_id != nullptr) { | ||
352 | jmethodID set_prop_id = env->GetStaticMethodID(sclass_id, "setProperty", | ||
353 | "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); | ||
354 | if (set_prop_id != nullptr) { | ||
355 | // Reset os.arch to the value reqired by the apps running with native bridge. | ||
356 | env->CallStaticObjectMethod(sclass_id, set_prop_id, env->NewStringUTF("os.arch"), | ||
357 | env->NewStringUTF(env_values->os_arch)); | ||
358 | } else { | ||
359 | env->ExceptionClear(); | ||
360 | ALOGW("Could not find setProperty method."); | ||
361 | } | ||
362 | } else { | ||
363 | env->ExceptionClear(); | ||
364 | ALOGW("Could not find System class."); | ||
365 | } | ||
366 | } | ||
367 | |||
368 | // Make it pristine again. | ||
369 | env->PopLocalFrame(nullptr); | ||
370 | } | ||
371 | |||
372 | bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) { | ||
162 | // We expect only one place that calls InitializeNativeBridge: Runtime::DidForkFromZygote. At that | 373 | // We expect only one place that calls InitializeNativeBridge: Runtime::DidForkFromZygote. At that |
163 | // point we are not multi-threaded, so we do not need locking here. | 374 | // point we are not multi-threaded, so we do not need locking here. |
164 | 375 | ||
165 | if (state == NativeBridgeState::kOpened) { | 376 | if (state == NativeBridgeState::kOpened) { |
166 | // Try to initialize. | 377 | // Try to initialize. |
167 | if (callbacks->initialize(runtime_callbacks)) { | 378 | if (callbacks->initialize(runtime_callbacks, app_data_dir, instruction_set)) { |
379 | SetupEnvironment(callbacks, env, instruction_set); | ||
168 | state = NativeBridgeState::kInitialized; | 380 | state = NativeBridgeState::kInitialized; |
169 | } else { | 381 | } else { |
170 | // Unload the library. | 382 | // Unload the library. |
diff --git a/libnativebridge/tests/UnavailableNativeBridge_test.cpp b/libnativebridge/tests/UnavailableNativeBridge_test.cpp index ec96c32fe..ad374a5e9 100644 --- a/libnativebridge/tests/UnavailableNativeBridge_test.cpp +++ b/libnativebridge/tests/UnavailableNativeBridge_test.cpp | |||
@@ -21,7 +21,7 @@ namespace android { | |||
21 | TEST_F(NativeBridgeTest, NoNativeBridge) { | 21 | TEST_F(NativeBridgeTest, NoNativeBridge) { |
22 | EXPECT_EQ(false, NativeBridgeAvailable()); | 22 | EXPECT_EQ(false, NativeBridgeAvailable()); |
23 | // Try to initialize. This should fail as we are not set up. | 23 | // Try to initialize. This should fail as we are not set up. |
24 | EXPECT_EQ(false, InitializeNativeBridge()); | 24 | EXPECT_EQ(false, InitializeNativeBridge(nullptr, nullptr)); |
25 | EXPECT_EQ(true, NativeBridgeError()); | 25 | EXPECT_EQ(true, NativeBridgeError()); |
26 | EXPECT_EQ(false, NativeBridgeAvailable()); | 26 | EXPECT_EQ(false, NativeBridgeAvailable()); |
27 | } | 27 | } |