Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / base / android / java / src / org / chromium / base / library_loader / LibraryLoader.java
index 91e5367..28c688d 100644 (file)
@@ -10,9 +10,10 @@ import android.util.Log;
 
 import org.chromium.base.CommandLine;
 import org.chromium.base.JNINamespace;
-import org.chromium.base.SysUtils;
 import org.chromium.base.TraceEvent;
 
+import javax.annotation.Nullable;
+
 /**
  * This class provides functionality to load and register the native libraries.
  * Callers are allowed to separate loading the libraries from initializing them.
@@ -31,6 +32,9 @@ import org.chromium.base.TraceEvent;
 public class LibraryLoader {
     private static final String TAG = "LibraryLoader";
 
+    // Set to true to enable debug logs.
+    private static final boolean DEBUG = false;
+
     // Guards all access to the libraries
     private static final Object sLock = new Object();
 
@@ -46,6 +50,23 @@ public class LibraryLoader {
     // library_loader_hooks.cc).
     private static boolean sInitialized = false;
 
+    // One-way switches recording attempts to use Relro sharing in the browser.
+    // The flags are used to report UMA stats later.
+    private static boolean sIsUsingBrowserSharedRelros = false;
+    private static boolean sLoadAtFixedAddressFailed = false;
+
+    // One-way switch becomes true if the device supports memory mapping the
+    // APK file with executable permissions.
+    private static boolean sMapApkWithExecPermission = false;
+
+    // One-way switch becomes true if the Chromium library was loaded from the
+    // APK file directly.
+    private static boolean sLibraryWasLoadedFromApk = false;
+
+    // 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 static boolean sLibraryIsMappableInApk = true;
+
     // One-way switch becomes true if the system library loading failed,
     // and the right native library was found and loaded by the hack.
     // The flag is used to report UMA stats later.
@@ -76,7 +97,7 @@ public class LibraryLoader {
      *    http://b/13216167.
      *
      *  @param shouldDeleteOldWorkaroundLibraries The flag tells whether the method
-     *    should delete the old workaround libraries or not.
+     *    should delete the old workaround and fallback libraries or not.
      */
     public static void ensureInitialized(
             Context context, boolean shouldDeleteOldWorkaroundLibraries)
@@ -119,7 +140,7 @@ public class LibraryLoader {
      *
      * @param context The context the code is running, or null if it doesn't have one.
      * @param shouldDeleteOldWorkaroundLibraries The flag tells whether the method
-     *   should delete the old workaround libraries or not.
+     *   should delete the old workaround and fallback libraries or not.
      *
      * @throws ProcessInitException if the native library failed to load.
      */
@@ -151,41 +172,88 @@ public class LibraryLoader {
 
                 long startTime = SystemClock.uptimeMillis();
                 boolean useChromiumLinker = Linker.isUsed();
+                boolean fallbackWasUsed = false;
 
                 if (useChromiumLinker) {
+                    String apkFilePath = null;
+                    boolean useMapExecSupportFallback = false;
+
+                    // Check if the device supports memory mapping the APK file
+                    // with executable permissions.
+                    if (context != null) {
+                        apkFilePath = context.getApplicationInfo().sourceDir;
+                        sMapApkWithExecPermission = Linker.checkMapExecSupport(apkFilePath);
+                        if (!sMapApkWithExecPermission && Linker.isInZipFile()) {
+                            Log.w(TAG, "the no map executable support fallback will be used because"
+                                    + " memory mapping the APK file with executable permissions is"
+                                    + " not supported");
+                            Linker.enableNoMapExecSupportFallback();
+                            useMapExecSupportFallback = true;
+                        }
+                    } else {
+                        Log.w(TAG, "could not check load from APK support due to null context");
+                    }
+
                     // Load libraries using the Chromium linker.
                     Linker.prepareLibraryLoad();
 
                     for (String library : NativeLibraries.LIBRARIES) {
-                        String zipfile = null;
-                        if (Linker.isInZipFile()) {
-                            zipfile = context.getApplicationInfo().sourceDir;
-                            Log.i(TAG, "Loading " + library + " from within " + zipfile);
+                        // 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 (DEBUG) Log.i(TAG, "ignoring self-linker load");
+                            continue;
+                        }
+
+                        // 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)) {
+                                sLibraryIsMappableInApk = false;
+                            }
+                            if (sLibraryIsMappableInApk || useMapExecSupportFallback) {
+                                // Load directly from the APK (or use the no map executable
+                                // support fallback, see crazy_linker_elf_loader.cpp).
+                                zipFilePath = apkFilePath;
+                                Log.i(TAG, "Loading " + library + " "
+                                        + (useMapExecSupportFallback
+                                                ? "using no map executable support fallback"
+                                                : "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);
+                            }
                         } else {
-                            Log.i(TAG, "Loading: " + library);
+                            // The library is in its own file.
+                            Log.i(TAG, "Loading " + library);
                         }
 
+                        // Load the library.
                         boolean isLoaded = false;
                         if (Linker.isUsingBrowserSharedRelros()) {
+                            sIsUsingBrowserSharedRelros = true;
                             try {
-                                if (zipfile != null) {
-                                    Linker.loadLibraryInZipFile(zipfile, library);
-                                } else {
-                                    Linker.loadLibrary(library);
-                                }
+                                loadLibrary(zipFilePath, libFilePath);
                                 isLoaded = true;
                             } catch (UnsatisfiedLinkError e) {
-                                Log.w(TAG, "Failed to load native library with shared RELRO, " +
-                                      "retrying without");
+                                Log.w(TAG, "Failed to load native library with shared RELRO, "
+                                        + "retrying without");
                                 Linker.disableSharedRelros();
+                                sLoadAtFixedAddressFailed = true;
                             }
                         }
                         if (!isLoaded) {
-                            if (zipfile != null) {
-                                Linker.loadLibraryInZipFile(zipfile, library);
-                            } else {
-                                Linker.loadLibrary(library);
-                            }
+                            loadLibrary(zipFilePath, libFilePath);
                         }
                     }
 
@@ -197,8 +265,8 @@ public class LibraryLoader {
                             System.loadLibrary(library);
                         } catch (UnsatisfiedLinkError e) {
                             if (context != null
-                                && LibraryLoaderHelper.tryLoadLibraryUsingWorkaround(context,
-                                                                                     library)) {
+                                    && LibraryLoaderHelper.tryLoadLibraryUsingWorkaround(context,
+                                                                                         library)) {
                                 sNativeLibraryHackWasUsed = true;
                             } else {
                                 throw e;
@@ -207,11 +275,15 @@ public class LibraryLoader {
                     }
                 }
 
-                if (context != null
-                    && shouldDeleteOldWorkaroundLibraries
-                    && !sNativeLibraryHackWasUsed) {
-                    LibraryLoaderHelper.deleteWorkaroundLibrariesAsynchronously(
-                        context);
+                if (context != null && shouldDeleteOldWorkaroundLibraries) {
+                    if (!sNativeLibraryHackWasUsed) {
+                        LibraryLoaderHelper.deleteLibrariesAsynchronously(
+                                context, LibraryLoaderHelper.PACKAGE_MANAGER_WORKAROUND_DIR);
+                    }
+                    if (!fallbackWasUsed) {
+                        LibraryLoaderHelper.deleteLibrariesAsynchronously(
+                                context, LibraryLoaderHelper.LOAD_FROM_APK_FALLBACK_DIR);
+                    }
                 }
 
                 long stopTime = SystemClock.uptimeMillis();
@@ -227,15 +299,24 @@ public class LibraryLoader {
         }
         // 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.VERSION_NUMBER,
+                "Expected native library version number \"%s\","
+                        "actual native library version number \"%s\"",
+                NativeLibraries.sVersionNumber,
                 nativeGetVersionNumber()));
-        if (!NativeLibraries.VERSION_NUMBER.equals(nativeGetVersionNumber())) {
+        if (!NativeLibraries.sVersionNumber.equals(nativeGetVersionNumber())) {
             throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION);
         }
     }
 
+    // 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 static void loadLibrary(@Nullable String zipFilePath, String libFilePath) {
+        Linker.loadLibrary(zipFilePath, libFilePath);
+        if (zipFilePath != null) {
+            sLibraryWasLoadedFromApk = true;
+        }
+    }
+
     // The WebView requires the Command Line to be switched over before
     // initialization is done. This is okay in the WebView's case since the
     // JNI is already loaded by this point.
@@ -290,14 +371,56 @@ public class LibraryLoader {
     }
 
     // Called after all native initializations are complete.
-    public static void onNativeInitializationComplete() {
-        // Record histogram for the Chromium linker.
+    public static void onNativeInitializationComplete(Context context) {
+        recordBrowserProcessHistogram(context);
+        nativeRecordNativeLibraryHack(sNativeLibraryHackWasUsed);
+    }
+
+    // Record Chromium linker histogram state for the main browser process. Called from
+    // onNativeInitializationComplete().
+    private static void recordBrowserProcessHistogram(Context context) {
         if (Linker.isUsed()) {
-            nativeRecordChromiumAndroidLinkerHistogram(Linker.loadAtFixedAddressFailed(),
-                    SysUtils.isLowEndDevice());
+            nativeRecordChromiumAndroidLinkerBrowserHistogram(sIsUsingBrowserSharedRelros,
+                                                              sLoadAtFixedAddressFailed,
+                                                              getLibraryLoadFromApkStatus(context));
         }
+    }
 
-        nativeRecordNativeLibraryHack(sNativeLibraryHackWasUsed);
+    // 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 static int getLibraryLoadFromApkStatus(Context context) {
+        assert Linker.isUsed();
+
+        if (sLibraryWasLoadedFromApk) {
+            return sMapApkWithExecPermission
+                    ? LibraryLoadFromApkStatusCodes.SUCCESSFUL
+                    : LibraryLoadFromApkStatusCodes.USED_NO_MAP_EXEC_SUPPORT_FALLBACK;
+        }
+
+        if (!sLibraryIsMappableInApk) {
+            return LibraryLoadFromApkStatusCodes.USED_UNPACK_LIBRARY_FALLBACK;
+        }
+
+        if (context == null) {
+            Log.w(TAG, "Unknown APK filename due to null context");
+            return LibraryLoadFromApkStatusCodes.UNKNOWN;
+        }
+
+        return sMapApkWithExecPermission
+                ? LibraryLoadFromApkStatusCodes.SUPPORTED
+                : LibraryLoadFromApkStatusCodes.NOT_SUPPORTED;
+    }
+
+    // Register pending Chromium linker histogram state for renderer processes. This cannot be
+    // recorded as a histogram immediately because histograms and IPC are not ready at the
+    // time it are captured. This function stores a pending value, so that a later call to
+    // RecordChromiumAndroidLinkerRendererHistogram() will record it correctly.
+    public static void registerRendererProcessHistogram(boolean requestedSharedRelro,
+                                                        boolean loadAtFixedAddressFailed) {
+        if (Linker.isUsed()) {
+            nativeRegisterChromiumAndroidLinkerRendererHistogram(requestedSharedRelro,
+                                                                 loadAtFixedAddressFailed);
+        }
     }
 
     private static native void nativeInitCommandLine(String[] initCommandLine);
@@ -310,12 +433,21 @@ public class LibraryLoader {
     // Return true on success and false on failure.
     private static native boolean nativeLibraryLoaded();
 
-    // Method called to record statistics about the Chromium linker operation,
-    // i.e. whether the library failed to be loaded at a fixed address, and
-    // whether the device is 'low-memory'.
-    private static native void nativeRecordChromiumAndroidLinkerHistogram(
-            boolean loadedAtFixedAddressFailed,
-            boolean isLowMemoryDevice);
+    // 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.
+    private static native void nativeRecordChromiumAndroidLinkerBrowserHistogram(
+            boolean isUsingBrowserSharedRelros,
+            boolean loadAtFixedAddressFailed,
+            int libraryLoadFromApkStatus);
+
+    // 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.
+    private static native void nativeRegisterChromiumAndroidLinkerRendererHistogram(
+            boolean requestedSharedRelro,
+            boolean loadAtFixedAddressFailed);
 
     // 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.