Index: base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java |
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java |
index a789eff611f68fb972e03c32d871f0534733d622..5dfc2ba2edc3004ccc449a40ade168838648e428 100644 |
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java |
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java |
@@ -11,16 +11,17 @@ import android.content.pm.PackageInfo; |
import android.os.AsyncTask; |
import android.os.Build; |
import android.os.SystemClock; |
-import android.util.Log; |
import org.chromium.base.CalledByNative; |
import org.chromium.base.CommandLine; |
import org.chromium.base.JNINamespace; |
+import org.chromium.base.Log; |
import org.chromium.base.PackageUtils; |
import org.chromium.base.TraceEvent; |
-import org.chromium.base.VisibleForTesting; |
import org.chromium.base.metrics.RecordHistogram; |
+import java.util.concurrent.atomic.AtomicBoolean; |
+ |
import javax.annotation.Nullable; |
/** |
@@ -39,7 +40,7 @@ import javax.annotation.Nullable; |
*/ |
@JNINamespace("base::android") |
public class LibraryLoader { |
- private static final String TAG = "LibraryLoader"; |
+ private static final String TAG = "cr.library_loader"; |
// Set to true to enable debug logs. |
private static final boolean DEBUG = false; |
@@ -73,15 +74,19 @@ public class LibraryLoader { |
// APK file directly. |
private boolean mLibraryWasLoadedFromApk; |
- // One-way switch becomes false if the Chromium library should be loaded |
- // directly from the APK file but it was compressed or not aligned. |
- private boolean mLibraryIsMappableInApk = true; |
- |
// The type of process the shared library is loaded in. |
// This member can be accessed from multiple threads simultaneously, so it have to be |
// final (like now) or be protected in some way (volatile of synchronized). |
private final int mLibraryProcessType; |
+ // One-way switch that becomes true once |
+ // {@link asyncPrefetchLibrariesToMemory} has been called. |
+ private final AtomicBoolean mPrefetchLibraryHasBeenCalled; |
+ |
+ // The number of milliseconds it took to load all the native libraries, which |
+ // will be reported via UMA. Set once when the libraries are done loading. |
+ private long mLibraryLoadTimeMs; |
+ |
/** |
* @param libraryProcessType the process the shared library is loaded in. refer to |
* LibraryProcessType for possible values. |
@@ -101,38 +106,21 @@ public class LibraryLoader { |
private LibraryLoader(int libraryProcessType) { |
mLibraryProcessType = libraryProcessType; |
- } |
- |
- /** |
- * The same as ensureInitialized(null, false), should only be called |
- * by non-browser processes. |
- * |
- * @throws ProcessInitException |
- */ |
- @VisibleForTesting |
- public void ensureInitialized() throws ProcessInitException { |
- ensureInitialized(null, false); |
+ mPrefetchLibraryHasBeenCalled = new AtomicBoolean(); |
} |
/** |
* This method blocks until the library is fully loaded and initialized. |
* |
- * @param context The context in which the method is called, the caller |
- * may pass in a null context if it doesn't know in which context it |
- * is running. |
- * |
- * @param shouldDeleteFallbackLibraries The flag tells whether the method |
- * should delete the fallback libraries or not. |
+ * @param context The context in which the method is called. |
*/ |
- public void ensureInitialized( |
- Context context, boolean shouldDeleteFallbackLibraries) |
- throws ProcessInitException { |
+ public void ensureInitialized(Context context) throws ProcessInitException { |
synchronized (sLock) { |
if (mInitialized) { |
// Already initialized, nothing to do. |
return; |
} |
- loadAlreadyLocked(context, shouldDeleteFallbackLibraries); |
+ loadAlreadyLocked(context); |
initializeAlreadyLocked(); |
} |
} |
@@ -145,32 +133,19 @@ public class LibraryLoader { |
} |
/** |
- * The same as loadNow(null, false), should only be called by |
- * non-browser process. |
- * |
- * @throws ProcessInitException |
- */ |
- public void loadNow() throws ProcessInitException { |
- loadNow(null, false); |
- } |
- |
- /** |
* Loads the library and blocks until the load completes. The caller is responsible |
* for subsequently calling ensureInitialized(). |
* May be called on any thread, but should only be called once. Note the thread |
* this is called on will be the thread that runs the native code's static initializers. |
* See the comment in doInBackground() for more considerations on this. |
* |
- * @param context The context the code is running, or null if it doesn't have one. |
- * @param shouldDeleteFallbackLibraries The flag tells whether the method |
- * should delete the old fallback libraries or not. |
+ * @param context The context the code is running. |
* |
* @throws ProcessInitException if the native library failed to load. |
*/ |
- public void loadNow(Context context, boolean shouldDeleteFallbackLibraries) |
- throws ProcessInitException { |
+ public void loadNow(Context context) throws ProcessInitException { |
synchronized (sLock) { |
- loadAlreadyLocked(context, shouldDeleteFallbackLibraries); |
+ loadAlreadyLocked(context); |
} |
} |
@@ -193,10 +168,9 @@ public class LibraryLoader { |
* |
* This is done this way, as testing shows that fadvise(FADV_WILLNEED) is |
* detrimental to the startup time. |
- * |
- * @param context the application context. |
*/ |
- public void asyncPrefetchLibrariesToMemory(final Context context) { |
+ public void asyncPrefetchLibrariesToMemory() { |
+ if (!mPrefetchLibraryHasBeenCalled.compareAndSet(false, true)) return; |
new AsyncTask<Void, Void, Void>() { |
@Override |
protected Void doInBackground(Void... params) { |
@@ -213,34 +187,26 @@ public class LibraryLoader { |
} |
// Invoke System.loadLibrary(...), triggering JNI_OnLoad in native code |
- private void loadAlreadyLocked( |
- Context context, boolean shouldDeleteFallbackLibraries) |
- throws ProcessInitException { |
+ private void loadAlreadyLocked(Context context) throws ProcessInitException { |
try { |
if (!mLoaded) { |
assert !mInitialized; |
long startTime = SystemClock.uptimeMillis(); |
- boolean useChromiumLinker = Linker.isUsed(); |
- boolean fallbackWasUsed = false; |
+ Linker linker = Linker.getInstance(); |
+ boolean useChromiumLinker = linker.isUsed(); |
if (useChromiumLinker) { |
// Determine the APK file path. |
- String apkFilePath = null; |
- if (context != null) { |
- apkFilePath = getLibraryApkPath(context); |
- } else { |
- Log.w(TAG, "could not check load from APK support due to null context"); |
- } |
- |
+ String apkFilePath = getLibraryApkPath(context); |
// Load libraries using the Chromium linker. |
- Linker.prepareLibraryLoad(); |
+ linker.prepareLibraryLoad(); |
for (String library : NativeLibraries.LIBRARIES) { |
// Don't self-load the linker. This is because the build system is |
// not clever enough to understand that all the libraries packaged |
// in the final .apk don't need to be explicitly loaded. |
- if (Linker.isChromiumLinkerLibrary(library)) { |
+ if (linker.isChromiumLinkerLibrary(library)) { |
if (DEBUG) Log.i(TAG, "ignoring self-linker load"); |
continue; |
} |
@@ -248,26 +214,11 @@ public class LibraryLoader { |
// Determine where the library should be loaded from. |
String zipFilePath = null; |
String libFilePath = System.mapLibraryName(library); |
- if (apkFilePath != null && Linker.isInZipFile()) { |
- // The library is in the APK file. |
- if (!Linker.checkLibraryIsMappableInApk(apkFilePath, libFilePath)) { |
- mLibraryIsMappableInApk = false; |
- } |
- if (mLibraryIsMappableInApk) { |
- // Load directly from the APK. |
- zipFilePath = apkFilePath; |
- Log.i(TAG, "Loading " + library + " directly from within " |
- + apkFilePath); |
- } else { |
- // Unpack library fallback. |
- Log.i(TAG, "Loading " + library |
- + " using unpack library fallback from within " |
- + apkFilePath); |
- libFilePath = LibraryLoaderHelper.buildFallbackLibrary( |
- context, library); |
- fallbackWasUsed = true; |
- Log.i(TAG, "Built fallback library " + libFilePath); |
- } |
+ if (linker.isInZipFile()) { |
+ // Load directly from the APK. |
+ zipFilePath = apkFilePath; |
+ Log.i(TAG, |
+ "Loading " + library + " directly from within " + apkFilePath); |
} else { |
// The library is in its own file. |
Log.i(TAG, "Loading " + library); |
@@ -275,7 +226,7 @@ public class LibraryLoader { |
// Load the library. |
boolean isLoaded = false; |
- if (Linker.isUsingBrowserSharedRelros()) { |
+ if (linker.isUsingBrowserSharedRelros()) { |
mIsUsingBrowserSharedRelros = true; |
try { |
loadLibrary(zipFilePath, libFilePath); |
@@ -283,7 +234,7 @@ public class LibraryLoader { |
} catch (UnsatisfiedLinkError e) { |
Log.w(TAG, "Failed to load native library with shared RELRO, " |
+ "retrying without"); |
- Linker.disableSharedRelros(); |
+ linker.disableSharedRelros(); |
mLoadAtFixedAddressFailed = true; |
} |
} |
@@ -292,7 +243,7 @@ public class LibraryLoader { |
} |
} |
- Linker.finishLibraryLoad(); |
+ linker.finishLibraryLoad(); |
} else { |
// Load libraries using the system linker. |
for (String library : NativeLibraries.LIBRARIES) { |
@@ -300,17 +251,10 @@ public class LibraryLoader { |
} |
} |
- if (!fallbackWasUsed && context != null |
- && shouldDeleteFallbackLibraries) { |
- LibraryLoaderHelper.deleteLibrariesAsynchronously( |
- context, LibraryLoaderHelper.LOAD_FROM_APK_FALLBACK_DIR); |
- } |
- |
long stopTime = SystemClock.uptimeMillis(); |
+ mLibraryLoadTimeMs = stopTime - startTime; |
Log.i(TAG, String.format("Time to load native libraries: %d ms (timestamps %d-%d)", |
- stopTime - startTime, |
- startTime % 10000, |
- stopTime % 10000)); |
+ mLibraryLoadTimeMs, startTime % 10000, stopTime % 10000)); |
mLoaded = true; |
} |
@@ -318,11 +262,9 @@ public class LibraryLoader { |
throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED, e); |
} |
// Check that the version of the library we have loaded matches the version we expect |
- Log.i(TAG, String.format( |
- "Expected native library version number \"%s\"," |
- + "actual native library version number \"%s\"", |
- NativeLibraries.sVersionNumber, |
- nativeGetVersionNumber())); |
+ Log.i(TAG, String.format("Expected native library version number \"%s\", " |
+ + "actual native library version number \"%s\"", |
+ NativeLibraries.sVersionNumber, nativeGetVersionNumber())); |
if (!NativeLibraries.sVersionNumber.equals(nativeGetVersionNumber())) { |
throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION); |
} |
@@ -356,7 +298,7 @@ public class LibraryLoader { |
// Load a native shared library with the Chromium linker. If the zip file |
// path is not null, the library is loaded directly from the zip file. |
private void loadLibrary(@Nullable String zipFilePath, String libFilePath) { |
- Linker.loadLibrary(zipFilePath, libFilePath); |
+ Linker.getInstance().loadLibrary(zipFilePath, libFilePath); |
if (zipFilePath != null) { |
mLibraryWasLoadedFromApk = true; |
} |
@@ -426,26 +368,22 @@ public class LibraryLoader { |
// Record Chromium linker histogram state for the main browser process. Called from |
// onNativeInitializationComplete(). |
private void recordBrowserProcessHistogram(Context context) { |
- if (Linker.isUsed()) { |
+ if (Linker.getInstance().isUsed()) { |
nativeRecordChromiumAndroidLinkerBrowserHistogram(mIsUsingBrowserSharedRelros, |
- mLoadAtFixedAddressFailed, |
- getLibraryLoadFromApkStatus(context)); |
+ mLoadAtFixedAddressFailed, getLibraryLoadFromApkStatus(context), |
+ mLibraryLoadTimeMs); |
} |
} |
// Returns the device's status for loading a library directly from the APK file. |
// This method can only be called when the Chromium linker is used. |
private int getLibraryLoadFromApkStatus(Context context) { |
- assert Linker.isUsed(); |
+ assert Linker.getInstance().isUsed(); |
if (mLibraryWasLoadedFromApk) { |
return LibraryLoadFromApkStatusCodes.SUCCESSFUL; |
} |
- if (!mLibraryIsMappableInApk) { |
- return LibraryLoadFromApkStatusCodes.USED_UNPACK_LIBRARY_FALLBACK; |
- } |
- |
// There were no libraries to be loaded directly from the APK file. |
return LibraryLoadFromApkStatusCodes.UNKNOWN; |
} |
@@ -456,9 +394,9 @@ public class LibraryLoader { |
// RecordChromiumAndroidLinkerRendererHistogram() will record it correctly. |
public void registerRendererProcessHistogram(boolean requestedSharedRelro, |
boolean loadAtFixedAddressFailed) { |
- if (Linker.isUsed()) { |
- nativeRegisterChromiumAndroidLinkerRendererHistogram(requestedSharedRelro, |
- loadAtFixedAddressFailed); |
+ if (Linker.getInstance().isUsed()) { |
+ nativeRegisterChromiumAndroidLinkerRendererHistogram( |
+ requestedSharedRelro, loadAtFixedAddressFailed, mLibraryLoadTimeMs); |
} |
} |
@@ -485,18 +423,18 @@ public class LibraryLoader { |
// Method called to record statistics about the Chromium linker operation for the main |
// browser process. Indicates whether the linker attempted relro sharing for the browser, |
// and if it did, whether the library failed to load at a fixed address. Also records |
- // support for loading a library directly from the APK file. |
+ // support for loading a library directly from the APK file, and the number of milliseconds |
+ // it took to load the libraries. |
private native void nativeRecordChromiumAndroidLinkerBrowserHistogram( |
- boolean isUsingBrowserSharedRelros, |
- boolean loadAtFixedAddressFailed, |
- int libraryLoadFromApkStatus); |
+ boolean isUsingBrowserSharedRelros, boolean loadAtFixedAddressFailed, |
+ int libraryLoadFromApkStatus, long libraryLoadTime); |
// Method called to register (for later recording) statistics about the Chromium linker |
// operation for a renderer process. Indicates whether the linker attempted relro sharing, |
- // and if it did, whether the library failed to load at a fixed address. |
+ // and if it did, whether the library failed to load at a fixed address. Also records the |
+ // number of milliseconds it took to load the libraries. |
private native void nativeRegisterChromiumAndroidLinkerRendererHistogram( |
- boolean requestedSharedRelro, |
- boolean loadAtFixedAddressFailed); |
+ boolean requestedSharedRelro, boolean loadAtFixedAddressFailed, long libraryLoadTime); |
// Get the version of the native library. This is needed so that we can check we |
// have the right version before initializing the (rest of the) JNI. |