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(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} 252