| 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.
|
|
|