LibraryLoader.java revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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 5package org.chromium.base.library_loader; 6 7import android.content.Context; 8import android.os.SystemClock; 9import android.util.Log; 10 11import org.chromium.base.CommandLine; 12import org.chromium.base.JNINamespace; 13import org.chromium.base.SysUtils; 14import 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") 31public 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(); 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 */ 134 public static void initialize() throws ProcessInitException { 135 synchronized (sLock) { 136 initializeAlreadyLocked(); 137 } 138 } 139 140 // Invoke System.loadLibrary(...), triggering JNI_OnLoad in native code 141 private static void loadAlreadyLocked( 142 Context context, boolean shouldDeleteOldWorkaroundLibraries) 143 throws ProcessInitException { 144 try { 145 if (!sLoaded) { 146 assert !sInitialized; 147 148 long startTime = SystemClock.uptimeMillis(); 149 boolean useChromiumLinker = Linker.isUsed(); 150 151 if (useChromiumLinker) Linker.prepareLibraryLoad(); 152 153 for (String library : NativeLibraries.LIBRARIES) { 154 if (useChromiumLinker) { 155 if (Linker.isInZipFile()) { 156 String zipfile = context.getApplicationInfo().sourceDir; 157 Log.i(TAG, "Loading " + library + " from within " + zipfile); 158 Linker.loadLibraryInZipFile(zipfile, library); 159 } else { 160 Log.i(TAG, "Loading: " + library); 161 Linker.loadLibrary(library); 162 } 163 } else { 164 try { 165 System.loadLibrary(library); 166 } catch (UnsatisfiedLinkError e) { 167 if (context != null 168 && LibraryLoaderHelper.tryLoadLibraryUsingWorkaround(context, 169 library)) { 170 sNativeLibraryHackWasUsed = true; 171 } else { 172 throw e; 173 } 174 } 175 } 176 } 177 if (useChromiumLinker) Linker.finishLibraryLoad(); 178 179 if (context != null 180 && shouldDeleteOldWorkaroundLibraries 181 && !sNativeLibraryHackWasUsed) { 182 LibraryLoaderHelper.deleteWorkaroundLibrariesAsynchronously( 183 context); 184 } 185 186 long stopTime = SystemClock.uptimeMillis(); 187 Log.i(TAG, String.format("Time to load native libraries: %d ms (timestamps %d-%d)", 188 stopTime - startTime, 189 startTime % 10000, 190 stopTime % 10000)); 191 192 nativeInitCommandLine(CommandLine.getJavaSwitchesOrNull()); 193 CommandLine.enableNativeProxy(); 194 195 sLoaded = true; 196 } 197 } catch (UnsatisfiedLinkError e) { 198 throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED, e); 199 } 200 // Check that the version of the library we have loaded matches the version we expect 201 Log.i(TAG, String.format( 202 "Expected native library version number \"%s\"," + 203 "actual native library version number \"%s\"", 204 NativeLibraries.VERSION_NUMBER, 205 nativeGetVersionNumber())); 206 if (!NativeLibraries.VERSION_NUMBER.equals(nativeGetVersionNumber())) { 207 throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION); 208 } 209 } 210 211 // Invoke base::android::LibraryLoaded in library_loader_hooks.cc 212 private static void initializeAlreadyLocked() throws ProcessInitException { 213 if (sInitialized) { 214 return; 215 } 216 217 if (!nativeLibraryLoaded()) { 218 Log.e(TAG, "error calling nativeLibraryLoaded"); 219 throw new ProcessInitException(LoaderErrors.LOADER_ERROR_FAILED_TO_REGISTER_JNI); 220 } 221 // From this point on, native code is ready to use and checkIsReady() 222 // shouldn't complain from now on (and in fact, it's used by the 223 // following calls). 224 sInitialized = true; 225 226 // From now on, keep tracing in sync with native. 227 TraceEvent.registerNativeEnabledObserver(); 228 } 229 230 // Called after all native initializations are complete. 231 public static void onNativeInitializationComplete() { 232 // Record histogram for the Chromium linker. 233 if (Linker.isUsed()) { 234 nativeRecordChromiumAndroidLinkerHistogram(Linker.loadAtFixedAddressFailed(), 235 SysUtils.isLowEndDevice()); 236 } 237 238 nativeRecordNativeLibraryHack(sNativeLibraryHackWasUsed); 239 } 240 241 private static native void nativeInitCommandLine(String[] initCommandLine); 242 243 // Only methods needed before or during normal JNI registration are during System.OnLoad. 244 // nativeLibraryLoaded is then called to register everything else. This process is called 245 // "initialization". This method will be mapped (by generated code) to the LibraryLoaded 246 // definition in base/android/library_loader/library_loader_hooks.cc. 247 // 248 // Return true on success and false on failure. 249 private static native boolean nativeLibraryLoaded(); 250 251 // Method called to record statistics about the Chromium linker operation, 252 // i.e. whether the library failed to be loaded at a fixed address, and 253 // whether the device is 'low-memory'. 254 private static native void nativeRecordChromiumAndroidLinkerHistogram( 255 boolean loadedAtFixedAddressFailed, 256 boolean isLowMemoryDevice); 257 258 // Get the version of the native library. This is needed so that we can check we 259 // have the right version before initializing the (rest of the) JNI. 260 private static native String nativeGetVersionNumber(); 261 262 private static native void nativeRecordNativeLibraryHack(boolean usedHack); 263} 264