Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / base / android / java / src / org / chromium / base / library_loader / LibraryLoader.java
1 // Copyright 2014 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.base.library_loader;
6
7 import android.content.Context;
8 import android.os.SystemClock;
9 import android.util.Log;
10
11 import org.chromium.base.CommandLine;
12 import org.chromium.base.JNINamespace;
13 import org.chromium.base.SysUtils;
14 import org.chromium.base.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 base/android/library_loader/library_loader_hooks.cc, which contains
28  * the native counterpart to this class.
29  */
30 @JNINamespace("base::android")
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     // One-way switch becomes true if the system library loading failed,
46     // and the right native library was found and loaded by the hack.
47     // The flag is used to report UMA stats later.
48     private static boolean sNativeLibraryHackWasUsed = false;
49
50     /**
51      * The same as ensureInitialized(null, false), should only be called
52      * by non-browser processes.
53      *
54      * @throws ProcessInitException
55      */
56     public static void ensureInitialized() throws ProcessInitException {
57         ensureInitialized(null, false);
58     }
59
60     /**
61      *  This method blocks until the library is fully loaded and initialized.
62      *
63      *  @param context The context in which the method is called, the caller
64      *    may pass in a null context if it doesn't know in which context it
65      *    is running, or it doesn't need to work around the issue
66      *    http://b/13216167.
67      *
68      *    When the context is not null and native library was not extracted
69      *    by Android package manager, the LibraryLoader class
70      *    will extract the native libraries from APK. This is a hack used to
71      *    work around some Sony devices with the following platform bug:
72      *    http://b/13216167.
73      *
74      *  @param shouldDeleteOldWorkaroundLibraries The flag tells whether the method
75      *    should delete the old workaround libraries or not.
76      */
77     public static void ensureInitialized(
78             Context context, boolean shouldDeleteOldWorkaroundLibraries)
79             throws ProcessInitException {
80         synchronized (sLock) {
81             if (sInitialized) {
82                 // Already initialized, nothing to do.
83                 return;
84             }
85             loadAlreadyLocked(context, shouldDeleteOldWorkaroundLibraries);
86             initializeAlreadyLocked(CommandLine.getJavaSwitchesOrNull());
87         }
88     }
89
90     /**
91      * Checks if library is fully loaded and initialized.
92      */
93     public static boolean isInitialized() {
94         synchronized (sLock) {
95             return sInitialized;
96         }
97     }
98
99     /**
100      * The same as loadNow(null, false), should only be called by
101      * non-browser process.
102      *
103      * @throws ProcessInitException
104      */
105     public static void loadNow() throws ProcessInitException {
106         loadNow(null, false);
107     }
108
109     /**
110      * Loads the library and blocks until the load completes. The caller is responsible
111      * for subsequently calling ensureInitialized().
112      * May be called on any thread, but should only be called once. Note the thread
113      * this is called on will be the thread that runs the native code's static initializers.
114      * See the comment in doInBackground() for more considerations on this.
115      *
116      * @param context The context the code is running, or null if it doesn't have one.
117      * @param shouldDeleteOldWorkaroundLibraries The flag tells whether the method
118      *   should delete the old workaround libraries or not.
119      *
120      * @throws ProcessInitException if the native library failed to load.
121      */
122     public static void loadNow(Context context, boolean shouldDeleteOldWorkaroundLibraries)
123             throws ProcessInitException {
124         synchronized (sLock) {
125             loadAlreadyLocked(context, shouldDeleteOldWorkaroundLibraries);
126         }
127     }
128
129     /**
130      * initializes the library here and now: must be called on the thread that the
131      * native will call its "main" thread. The library must have previously been
132      * loaded with loadNow.
133      * @param initCommandLine The command line arguments that native command line will
134      * be initialized with.
135      */
136     public static void initialize(String[] initCommandLine) throws ProcessInitException {
137         synchronized (sLock) {
138             initializeAlreadyLocked(initCommandLine);
139         }
140     }
141
142     // Invoke System.loadLibrary(...), triggering JNI_OnLoad in native code
143     private static void loadAlreadyLocked(
144             Context context, boolean shouldDeleteOldWorkaroundLibraries)
145             throws ProcessInitException {
146         try {
147             if (!sLoaded) {
148                 assert !sInitialized;
149
150                 long startTime = SystemClock.uptimeMillis();
151                 boolean useChromiumLinker = Linker.isUsed();
152
153                 if (useChromiumLinker) Linker.prepareLibraryLoad();
154
155                 for (String library : NativeLibraries.LIBRARIES) {
156                     Log.i(TAG, "Loading: " + library);
157                     if (useChromiumLinker) {
158                         Linker.loadLibrary(library);
159                     } else {
160                         try {
161                             System.loadLibrary(library);
162                         } catch (UnsatisfiedLinkError e) {
163                             if (context != null
164                                 && LibraryLoaderHelper.tryLoadLibraryUsingWorkaround(context,
165                                                                                      library)) {
166                                 sNativeLibraryHackWasUsed = true;
167                             } else {
168                                 throw e;
169                             }
170                         }
171                     }
172                 }
173                 if (useChromiumLinker) Linker.finishLibraryLoad();
174
175                 if (context != null
176                     && shouldDeleteOldWorkaroundLibraries
177                     && !sNativeLibraryHackWasUsed) {
178                     LibraryLoaderHelper.deleteWorkaroundLibrariesAsynchronously(
179                         context);
180                 }
181
182                 long stopTime = SystemClock.uptimeMillis();
183                 Log.i(TAG, String.format("Time to load native libraries: %d ms (timestamps %d-%d)",
184                         stopTime - startTime,
185                         startTime % 10000,
186                         stopTime % 10000));
187                 sLoaded = true;
188             }
189         } catch (UnsatisfiedLinkError e) {
190             throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED, e);
191         }
192         // Check that the version of the library we have loaded matches the version we expect
193         Log.i(TAG, String.format(
194                 "Expected native library version number \"%s\"," +
195                         "actual native library version number \"%s\"",
196                 NativeLibraries.VERSION_NUMBER,
197                 nativeGetVersionNumber()));
198         if (!NativeLibraries.VERSION_NUMBER.equals(nativeGetVersionNumber())) {
199             throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION);
200         }
201     }
202
203     // Invoke base::android::LibraryLoaded in library_loader_hooks.cc
204     private static void initializeAlreadyLocked(String[] initCommandLine)
205             throws ProcessInitException {
206         if (sInitialized) {
207             return;
208         }
209         if (!nativeLibraryLoaded(initCommandLine)) {
210             Log.e(TAG, "error calling nativeLibraryLoaded");
211             throw new ProcessInitException(LoaderErrors.LOADER_ERROR_FAILED_TO_REGISTER_JNI);
212         }
213         // From this point on, native code is ready to use and checkIsReady()
214         // shouldn't complain from now on (and in fact, it's used by the
215         // following calls).
216         sInitialized = true;
217         CommandLine.enableNativeProxy();
218
219         // From now on, keep tracing in sync with native.
220         TraceEvent.registerNativeEnabledObserver();
221
222         // Record histogram for the Chromium linker.
223         if (Linker.isUsed()) {
224             nativeRecordChromiumAndroidLinkerHistogram(Linker.loadAtFixedAddressFailed(),
225                     SysUtils.isLowEndDevice());
226         }
227
228         nativeRecordNativeLibraryHack(sNativeLibraryHackWasUsed);
229     }
230
231     // Only methods needed before or during normal JNI registration are during System.OnLoad.
232     // nativeLibraryLoaded is then called to register everything else.  This process is called
233     // "initialization".  This method will be mapped (by generated code) to the LibraryLoaded
234     // definition in base/android/library_loader/library_loader_hooks.cc.
235     //
236     // Return true on success and false on failure.
237     private static native boolean nativeLibraryLoaded(String[] initCommandLine);
238
239     // Method called to record statistics about the Chromium linker operation,
240     // i.e. whether the library failed to be loaded at a fixed address, and
241     // whether the device is 'low-memory'.
242     private static native void nativeRecordChromiumAndroidLinkerHistogram(
243             boolean loadedAtFixedAddressFailed,
244             boolean isLowMemoryDevice);
245
246     // Get the version of the native library. This is needed so that we can check we
247     // have the right version before initializing the (rest of the) JNI.
248     private static native String nativeGetVersionNumber();
249
250     private static native void nativeRecordNativeLibraryHack(boolean usedHack);
251 }