1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | package org.chromium.content.app; |
6 | |
7 | import android.text.TextUtils; |
8 | import android.util.Log; |
9 | |
10 | import org.chromium.base.JNINamespace; |
11 | import org.chromium.content.common.CommandLine; |
12 | import org.chromium.content.common.ProcessInitException; |
13 | import org.chromium.content.common.ResultCodes; |
14 | import org.chromium.content.common.TraceEvent; |
15 | |
16 | /** |
17 | * This class provides functionality to load and register the native libraries. |
18 | * Callers are allowed to separate loading the libraries from initializing them. |
19 | * This may be an advantage for Android Webview, where the libraries can be loaded |
20 | * by the zygote process, but then needs per process initialization after the |
21 | * application processes are forked from the zygote process. |
22 | * |
23 | * The libraries may be loaded and initialized from any thread. Synchronization |
24 | * primitives are used to ensure that overlapping requests from different |
25 | * threads are handled sequentially. |
26 | * |
27 | * See also content/app/android/library_loader_hooks.cc, which contains |
28 | * the native counterpart to this class. |
29 | */ |
30 | @JNINamespace("content") |
31 | public class LibraryLoader { |
32 | private static final String TAG = "LibraryLoader"; |
33 | |
34 | // Guards all access to the libraries |
35 | private static final Object sLock = new Object(); |
36 | |
37 | // One-way switch becomes true when the libraries are loaded. |
38 | private static boolean sLoaded = false; |
39 | |
40 | // One-way switch becomes true when the libraries are initialized ( |
41 | // by calling nativeLibraryLoaded, which forwards to LibraryLoaded(...) in |
42 | // library_loader_hooks.cc). |
43 | private static boolean sInitialized = false; |
44 | |
45 | // TODO(cjhopman): Remove this once it's unused. |
46 | /** |
47 | * Doesn't do anything. |
48 | */ |
49 | @Deprecated |
50 | public static void setLibraryToLoad(String library) { |
51 | } |
52 | |
53 | /** |
54 | * This method blocks until the library is fully loaded and initialized. |
55 | */ |
56 | public static void ensureInitialized() throws ProcessInitException { |
57 | synchronized (sLock) { |
58 | if (sInitialized) { |
59 | // Already initialized, nothing to do. |
60 | return; |
61 | } |
62 | loadAlreadyLocked(); |
63 | initializeAlreadyLocked(CommandLine.getJavaSwitchesOrNull()); |
64 | } |
65 | } |
66 | |
67 | /** |
68 | * Checks if library is fully loaded and initialized. |
69 | */ |
70 | public static boolean isInitialized() { |
71 | synchronized (sLock) { |
72 | return sInitialized; |
73 | } |
74 | } |
75 | |
76 | /** |
77 | * Loads the library and blocks until the load completes. The caller is responsible |
78 | * for subsequently calling ensureInitialized(). |
79 | * May be called on any thread, but should only be called once. Note the thread |
80 | * this is called on will be the thread that runs the native code's static initializers. |
81 | * See the comment in doInBackground() for more considerations on this. |
82 | * |
83 | * @throws ProcessInitException if the native library failed to load. |
84 | */ |
85 | public static void loadNow() throws ProcessInitException { |
86 | synchronized (sLock) { |
87 | loadAlreadyLocked(); |
88 | } |
89 | } |
90 | |
91 | |
92 | /** |
93 | * initializes the library here and now: must be called on the thread that the |
94 | * native will call its "main" thread. The library must have previously been |
95 | * loaded with loadNow. |
96 | * @param initCommandLine The command line arguments that native command line will |
97 | * be initialized with. |
98 | */ |
99 | static void initialize(String[] initCommandLine) throws ProcessInitException { |
100 | synchronized (sLock) { |
101 | initializeAlreadyLocked(initCommandLine); |
102 | } |
103 | } |
104 | |
105 | |
106 | // Invoke System.loadLibrary(...), triggering JNI_OnLoad in native code |
107 | private static void loadAlreadyLocked() throws ProcessInitException { |
108 | try { |
109 | if (!sLoaded) { |
110 | assert !sInitialized; |
111 | for (String sLibrary : NativeLibraries.libraries) { |
112 | Log.i(TAG, "loading: " + sLibrary); |
113 | System.loadLibrary(sLibrary); |
114 | Log.i(TAG, "loaded: " + sLibrary); |
115 | } |
116 | sLoaded = true; |
117 | } |
118 | } catch (UnsatisfiedLinkError e) { |
119 | throw new ProcessInitException(ResultCodes.RESULT_CODE_NATIVE_LIBRARY_LOAD_FAILED, e); |
120 | } |
121 | } |
122 | |
123 | |
124 | // Invoke content::LibraryLoaded in library_loader_hooks.cc |
125 | private static void initializeAlreadyLocked(String[] initCommandLine) |
126 | throws ProcessInitException { |
127 | if (sInitialized) { |
128 | return; |
129 | } |
130 | int resultCode = nativeLibraryLoaded(initCommandLine); |
131 | if (resultCode != 0) { |
132 | Log.e(TAG, "error calling nativeLibraryLoaded"); |
133 | throw new ProcessInitException(resultCode); |
134 | } |
135 | // From this point on, native code is ready to use and checkIsReady() |
136 | // shouldn't complain from now on (and in fact, it's used by the |
137 | // following calls). |
138 | sInitialized = true; |
139 | CommandLine.enableNativeProxy(); |
140 | TraceEvent.setEnabledToMatchNative(); |
141 | } |
142 | |
143 | // This is the only method that is registered during System.loadLibrary. We then call it |
144 | // to register everything else. This process is called "initialization". |
145 | // This method will be mapped (by generated code) to the LibraryLoaded |
146 | // definition in content/app/android/library_loader_hooks.cc. |
147 | // |
148 | // Return 0 on success, otherwise return the error code from |
149 | // content/public/common/result_codes.h. |
150 | private static native int nativeLibraryLoaded(String[] initCommandLine); |
151 | } |