Update to Chromium //base at Chromium commit e3a753f17bac62738b0dbf0b36510f767b081e4b.

This gets us to sometime during September 9, 2015.

TBR=jamesr@chromium.org

Review URL: https://codereview.chromium.org/2050803003 .
diff --git a/BUILD.gn b/BUILD.gn
index bfc256f..86db541 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -186,6 +186,8 @@
     "deferred_sequenced_task_runner.h",
     "environment.cc",
     "environment.h",
+    "feature_list.cc",
+    "feature_list.h",
     "file_descriptor_posix.h",
     "file_version_info.h",
     "file_version_info_mac.h",
@@ -1588,6 +1590,14 @@
     ]
 
     DEPRECATED_java_in_dir = "android/java/src"
+    if (is_debug) {
+      java_files = [
+        "android/java/debug_src/org/chromium/base/IncrementalInstall.java",
+        "android/java/debug_src/org/chromium/base/Reflect.java",
+      ]
+    } else {
+      java_files = [ "android/java/release_src/org/chromium/base/IncrementalInstall.java" ]
+    }
 
     # A new version of NativeLibraries.java (with the actual correct values)
     # will be created when creating an apk.
diff --git a/OWNERS b/OWNERS
index bcaa81b..932fa89 100644
--- a/OWNERS
+++ b/OWNERS
@@ -26,3 +26,7 @@
 per-file *android*=nyquist@chromium.org
 per-file *android*=rmcilroy@chromium.org
 per-file *android*=yfriedman@chromium.org
+
+# For FeatureList API:
+per-file feature_list*=asvitkine@chromium.org
+per-file feature_list*=isherman@chromium.org
diff --git a/allocator/BUILD.gn b/allocator/BUILD.gn
index 5a1e9df..b6b7fc2 100644
--- a/allocator/BUILD.gn
+++ b/allocator/BUILD.gn
@@ -61,29 +61,31 @@
     libs = [ rebase_path("$target_gen_dir/allocator/libcmt.lib") ]
   }
 
-  action("prep_libc") {
-    script = "prep_libc.py"
-    outputs = [
-      "$target_gen_dir/allocator/libcmt.lib",
-    ]
-    args = [
-      visual_studio_path + "/vc/lib",
-      rebase_path("$target_gen_dir/allocator"),
-      current_cpu,
-    ]
-  }
+  if (!is_component_build) {
+    action("prep_libc") {
+      script = "prep_libc.py"
+      outputs = [
+        "$target_gen_dir/allocator/libcmt.lib",
+      ]
+      args = [
+        visual_studio_path + "/vc/lib",
+        rebase_path("$target_gen_dir/allocator"),
+        current_cpu,
+      ]
+    }
 
-  source_set("allocator_shim") {
-    sources = [
-      "allocator_shim_win.cc",
-    ]
-    configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
+    source_set("allocator_shim") {
+      sources = [
+        "allocator_shim_win.cc",
+      ]
+      configs -= [ "//build/config/compiler:chromium_code" ]
+      configs += [ "//build/config/compiler:no_chromium_code" ]
 
-    public_configs = [ ":nocmt" ]
-    deps = [
-      ":prep_libc",
-    ]
+      public_configs = [ ":nocmt" ]
+      deps = [
+        ":prep_libc",
+      ]
+    }
   }
 }
 
diff --git a/android/animation_frame_time_histogram.cc b/android/animation_frame_time_histogram.cc
index 0d79619..2cf7516 100644
--- a/android/animation_frame_time_histogram.cc
+++ b/android/animation_frame_time_histogram.cc
@@ -10,9 +10,9 @@
 
 // static
 void SaveHistogram(JNIEnv* env,
-                   jobject jcaller,
-                   jstring j_histogram_name,
-                   jlongArray j_frame_times_ms,
+                   const JavaParamRef<jobject>& jcaller,
+                   const JavaParamRef<jstring>& j_histogram_name,
+                   const JavaParamRef<jlongArray>& j_frame_times_ms,
                    jint j_count) {
   jlong *frame_times_ms = env->GetLongArrayElements(j_frame_times_ms, NULL);
   std::string histogram_name = base::android::ConvertJavaStringToUTF8(
diff --git a/android/application_status_listener.cc b/android/application_status_listener.cc
index 3e6fbf8..5aee781 100644
--- a/android/application_status_listener.cc
+++ b/android/application_status_listener.cc
@@ -66,7 +66,7 @@
 }
 
 static void OnApplicationStateChange(JNIEnv* env,
-                                     jclass clazz,
+                                     const JavaParamRef<jclass>& clazz,
                                      jint new_state) {
   ApplicationState application_state = static_cast<ApplicationState>(new_state);
   ApplicationStatusListener::NotifyApplicationStateChange(application_state);
diff --git a/android/command_line_android.cc b/android/command_line_android.cc
index 3a2b1f2..e196aed 100644
--- a/android/command_line_android.cc
+++ b/android/command_line_android.cc
@@ -31,18 +31,21 @@
 
 }  // namespace
 
-static void Reset(JNIEnv* env, jclass clazz) {
+static void Reset(JNIEnv* env, const JavaParamRef<jclass>& clazz) {
   CommandLine::Reset();
 }
 
-static jboolean HasSwitch(JNIEnv* env, jclass clazz, jstring jswitch) {
+static jboolean HasSwitch(JNIEnv* env,
+                          const JavaParamRef<jclass>& clazz,
+                          const JavaParamRef<jstring>& jswitch) {
   std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
   return CommandLine::ForCurrentProcess()->HasSwitch(switch_string);
 }
 
-static ScopedJavaLocalRef<jstring> GetSwitchValue(JNIEnv* env,
-                                                  jclass clazz,
-                                                  jstring jswitch) {
+static ScopedJavaLocalRef<jstring> GetSwitchValue(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& jswitch) {
   std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
   std::string value(CommandLine::ForCurrentProcess()->GetSwitchValueNative(
       switch_string));
@@ -51,21 +54,27 @@
   return ConvertUTF8ToJavaString(env, value);
 }
 
-static void AppendSwitch(JNIEnv* env, jclass clazz, jstring jswitch) {
+static void AppendSwitch(JNIEnv* env,
+                         const JavaParamRef<jclass>& clazz,
+                         const JavaParamRef<jstring>& jswitch) {
   std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
   CommandLine::ForCurrentProcess()->AppendSwitch(switch_string);
 }
 
-static void AppendSwitchWithValue(JNIEnv* env, jclass clazz,
-                                  jstring jswitch, jstring jvalue) {
+static void AppendSwitchWithValue(JNIEnv* env,
+                                  const JavaParamRef<jclass>& clazz,
+                                  const JavaParamRef<jstring>& jswitch,
+                                  const JavaParamRef<jstring>& jvalue) {
   std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
   std::string value_string (ConvertJavaStringToUTF8(env, jvalue));
   CommandLine::ForCurrentProcess()->AppendSwitchASCII(switch_string,
                                                       value_string);
 }
 
-static void AppendSwitchesAndArguments(JNIEnv* env, jclass clazz,
-                                       jobjectArray array) {
+static void AppendSwitchesAndArguments(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jobjectArray>& array) {
   AppendJavaStringArrayToCommandLine(env, array, false);
 }
 
diff --git a/android/cpu_features.cc b/android/cpu_features.cc
index 6a18695..c139b78 100644
--- a/android/cpu_features.cc
+++ b/android/cpu_features.cc
@@ -10,11 +10,11 @@
 namespace base {
 namespace android {
 
-jint GetCoreCount(JNIEnv*, jclass) {
+jint GetCoreCount(JNIEnv*, const JavaParamRef<jclass>&) {
   return android_getCpuCount();
 }
 
-jlong GetCpuFeatures(JNIEnv*, jclass) {
+jlong GetCpuFeatures(JNIEnv*, const JavaParamRef<jclass>&) {
   return android_getCpuFeatures();
 }
 
diff --git a/android/field_trial_list.cc b/android/field_trial_list.cc
index 3ff3811..9731a48 100644
--- a/android/field_trial_list.cc
+++ b/android/field_trial_list.cc
@@ -13,15 +13,18 @@
 using base::android::ConvertJavaStringToUTF8;
 using base::android::ConvertUTF8ToJavaString;
 
-static ScopedJavaLocalRef<jstring> FindFullName(JNIEnv* env,
-                                                jclass clazz,
-                                                jstring jtrial_name) {
+static ScopedJavaLocalRef<jstring> FindFullName(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& jtrial_name) {
   std::string trial_name(ConvertJavaStringToUTF8(env, jtrial_name));
   return ConvertUTF8ToJavaString(
       env, base::FieldTrialList::FindFullName(trial_name));
 }
 
-static jboolean TrialExists(JNIEnv* env, jclass clazz, jstring jtrial_name) {
+static jboolean TrialExists(JNIEnv* env,
+                            const JavaParamRef<jclass>& clazz,
+                            const JavaParamRef<jstring>& jtrial_name) {
   std::string trial_name(ConvertJavaStringToUTF8(env, jtrial_name));
   return base::FieldTrialList::TrialExists(trial_name);
 }
diff --git a/android/important_file_writer_android.cc b/android/important_file_writer_android.cc
index bcbd785..f324738 100644
--- a/android/important_file_writer_android.cc
+++ b/android/important_file_writer_android.cc
@@ -15,9 +15,9 @@
 namespace android {
 
 static jboolean WriteFileAtomically(JNIEnv* env,
-                                    jclass /* clazz */,
-                                    jstring file_name,
-                                    jbyteArray data) {
+                                    const JavaParamRef<jclass>& /* clazz */,
+                                    const JavaParamRef<jstring>& file_name,
+                                    const JavaParamRef<jbyteArray>& data) {
   // This is called on the UI thread during shutdown to save tab data, so
   // needs to enable IO.
   base::ThreadRestrictions::ScopedAllowIO allow_io;
diff --git a/android/java/debug_src/org/chromium/base/IncrementalInstall.java b/android/java/debug_src/org/chromium/base/IncrementalInstall.java
new file mode 100644
index 0000000..71b2b87
--- /dev/null
+++ b/android/java/debug_src/org/chromium/base/IncrementalInstall.java
@@ -0,0 +1,44 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.content.Context;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Contains hooks for making Incremental Install work. Incremental Install is
+ * where native libraries and (TODO) .dex files are pushed out-of-band to
+ * external storage rather than packaged with the app.
+ */
+public final class IncrementalInstall {
+    private static final String MANAGED_DIR_PREFIX = "/data/local/tmp/incremental-app-";
+
+    public static void initialize(Context context) {
+        File incrementalAppDir = new File(MANAGED_DIR_PREFIX + context.getPackageName());
+        File incrementalLibDir = new File(incrementalAppDir, "lib");
+        injectNativeLibDir(context.getClassLoader(), incrementalLibDir);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static void injectNativeLibDir(ClassLoader loader, File nativeLibDir) {
+        try {
+            Object dexPathList = Reflect.getField(loader, "pathList");
+            Object currentDirs = Reflect.getField(dexPathList, "nativeLibraryDirectories");
+            if (currentDirs instanceof List) {
+                List<File> dirsAsList = (List<File>) currentDirs;
+                dirsAsList.add(nativeLibDir);
+            } else {
+                File[] dirsAsArray = (File[]) currentDirs;
+                File[] newDirs = new File[] {nativeLibDir};
+                Reflect.setField(dexPathList, "nativeLibraryDirectories",
+                        Reflect.concatArrays(dirsAsArray, newDirs));
+            }
+        } catch (NoSuchFieldException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/android/java/debug_src/org/chromium/base/Reflect.java b/android/java/debug_src/org/chromium/base/Reflect.java
new file mode 100644
index 0000000..b705771
--- /dev/null
+++ b/android/java/debug_src/org/chromium/base/Reflect.java
@@ -0,0 +1,73 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+
+/**
+ * Reflection helper methods.
+ */
+final class Reflect {
+    /**
+     * Sets the value of an object's field (even if it's not visible).
+     *
+     * @param instance The object containing the field to set.
+     * @param name The name of the field to set.
+     * @param value The new value for the field.
+     */
+    static void setField(Object instance, String name, Object value) throws NoSuchFieldException {
+        Field field = findField(instance, name);
+        try {
+            field.setAccessible(true);
+            field.set(instance, value);
+        } catch (IllegalAccessException e) {
+            // This shouldn't happen.
+        }
+    }
+
+    /**
+     * Retrieves the value of an object's field (even if it's not visible).
+     *
+     * @param instance The object containing the field to set.
+     * @param name The name of the field to set.
+     * @return The field's value. Primitive values are returned as their boxed type.
+     */
+    static Object getField(Object instance, String name) throws NoSuchFieldException {
+        Field field = findField(instance, name);
+        try {
+            field.setAccessible(true);
+            return field.get(instance);
+        } catch (IllegalAccessException e) {
+            // This shouldn't happen.
+        }
+        return null;
+    }
+
+    /**
+     * Concatenates two arrays into a new array. The arrays must be of the same type.
+     */
+    static Object[] concatArrays(Object[] left, Object[] right) {
+        Object[] result = (Object[]) (Array.newInstance(
+                left.getClass().getComponentType(), left.length + right.length));
+        System.arraycopy(left, 0, result, 0, left.length);
+        System.arraycopy(right, 0, result, left.length, right.length);
+        return result;
+    }
+
+    /**
+     * Finds the Field with the given name for the given object, traversing superclasses if
+     * necessary.
+     */
+    private static Field findField(Object instance, String name) throws NoSuchFieldException {
+        for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
+            try {
+                return clazz.getDeclaredField(name);
+            } catch (NoSuchFieldException e) {
+            }
+        }
+        throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass());
+    }
+}
diff --git a/android/java/release_src/org/chromium/base/IncrementalInstall.java b/android/java/release_src/org/chromium/base/IncrementalInstall.java
new file mode 100644
index 0000000..09dfd41
--- /dev/null
+++ b/android/java/release_src/org/chromium/base/IncrementalInstall.java
@@ -0,0 +1,14 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base;
+
+import android.content.Context;
+
+/**
+ * Contains empty stubs for IncrementalInstall.
+ */
+public final class IncrementalInstall {
+    public static void initialize(Context context) {}
+}
diff --git a/android/java/src/org/chromium/base/ApiCompatibilityUtils.java b/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
index c2d596e..a119acb 100644
--- a/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
+++ b/android/java/src/org/chromium/base/ApiCompatibilityUtils.java
@@ -12,6 +12,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
@@ -429,4 +430,28 @@
         }
         return drawable;
     }
+
+    /**
+     * @see android.content.res.Resources#getColor(int id).
+     */
+    @SuppressWarnings("deprecation")
+    public static int getColor(Resources res, int id) throws NotFoundException {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            return res.getColor(id, null);
+        } else {
+            return res.getColor(id);
+        }
+    }
+
+    /**
+     * @see android.content.res.Resources#getColorStateList(int id).
+     */
+    @SuppressWarnings("deprecation")
+    public static ColorStateList getColorStateList(Resources res, int id) throws NotFoundException {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            return res.getColorStateList(id, null);
+        } else {
+            return res.getColorStateList(id);
+        }
+    }
 }
diff --git a/android/java/src/org/chromium/base/BaseChromiumApplication.java b/android/java/src/org/chromium/base/BaseChromiumApplication.java
index b71b646..ffa3c22 100644
--- a/android/java/src/org/chromium/base/BaseChromiumApplication.java
+++ b/android/java/src/org/chromium/base/BaseChromiumApplication.java
@@ -19,6 +19,7 @@
     @Override
     protected void attachBaseContext(Context base) {
         super.attachBaseContext(base);
+        IncrementalInstall.initialize(this);
     }
 
     /**
diff --git a/android/java/src/org/chromium/base/Log.java b/android/java/src/org/chromium/base/Log.java
index 3b46cdc..2a77211 100644
--- a/android/java/src/org/chromium/base/Log.java
+++ b/android/java/src/org/chromium/base/Log.java
@@ -17,7 +17,7 @@
  * the origin of logs, and enable or disable logging in different parts of the code.
  * </p>
  * <p>
- * @see usage documentation: <a href="README_logging.md">README_logging.md</a>.
+ * Usage documentation: {@code //docs/logging.md}.
  * </p>
  */
 public class Log {
@@ -60,7 +60,12 @@
         return "[" + getCallOrigin() + "] " + formatLog(messageTemplate, params);
     }
 
-    /** Convenience function, forwards to {@link android.util.Log#isLoggable(String, int)}. */
+    /**
+     * Convenience function, forwards to {@link android.util.Log#isLoggable(String, int)}.
+     *
+     * Note: Has no effect on whether logs are sent or not. Use a method with
+     * {@link RemovableInRelease} to log something in Debug builds only.
+     */
     public static boolean isLoggable(String tag, int level) {
         return android.util.Log.isLoggable(tag, level);
     }
@@ -79,14 +84,12 @@
      *             one is a {@link Throwable}, its trace will be printed.
      */
     private static void verbose(String tag, String messageTemplate, Object... args) {
-        if (Log.isLoggable(tag, Log.VERBOSE)) {
-            String message = formatLogWithStack(messageTemplate, args);
-            Throwable tr = getThrowableToLog(args);
-            if (tr != null) {
-                android.util.Log.v(tag, message, tr);
-            } else {
-                android.util.Log.v(tag, message);
-            }
+        String message = formatLogWithStack(messageTemplate, args);
+        Throwable tr = getThrowableToLog(args);
+        if (tr != null) {
+            android.util.Log.v(tag, message, tr);
+        } else {
+            android.util.Log.v(tag, message);
         }
     }
 
@@ -165,14 +168,12 @@
      *             one is a {@link Throwable}, its trace will be printed.
      */
     private static void debug(String tag, String messageTemplate, Object... args) {
-        if (isLoggable(tag, Log.DEBUG)) {
-            String message = formatLogWithStack(messageTemplate, args);
-            Throwable tr = getThrowableToLog(args);
-            if (tr != null) {
-                android.util.Log.d(tag, message, tr);
-            } else {
-                android.util.Log.d(tag, message);
-            }
+        String message = formatLogWithStack(messageTemplate, args);
+        Throwable tr = getThrowableToLog(args);
+        if (tr != null) {
+            android.util.Log.d(tag, message, tr);
+        } else {
+            android.util.Log.d(tag, message);
         }
     }
 
@@ -246,14 +247,12 @@
      */
     @VisibleForTesting
     public static void i(String tag, String messageTemplate, Object... args) {
-        if (Log.isLoggable(tag, Log.INFO)) {
-            String message = formatLog(messageTemplate, args);
-            Throwable tr = getThrowableToLog(args);
-            if (tr != null) {
-                android.util.Log.i(tag, message, tr);
-            } else {
-                android.util.Log.i(tag, message);
-            }
+        String message = formatLog(messageTemplate, args);
+        Throwable tr = getThrowableToLog(args);
+        if (tr != null) {
+            android.util.Log.i(tag, message, tr);
+        } else {
+            android.util.Log.i(tag, message);
         }
     }
 
@@ -268,14 +267,12 @@
      */
     @VisibleForTesting
     public static void w(String tag, String messageTemplate, Object... args) {
-        if (Log.isLoggable(tag, Log.WARN)) {
-            String message = formatLog(messageTemplate, args);
-            Throwable tr = getThrowableToLog(args);
-            if (tr != null) {
-                android.util.Log.w(tag, message, tr);
-            } else {
-                android.util.Log.w(tag, message);
-            }
+        String message = formatLog(messageTemplate, args);
+        Throwable tr = getThrowableToLog(args);
+        if (tr != null) {
+            android.util.Log.w(tag, message, tr);
+        } else {
+            android.util.Log.w(tag, message);
         }
     }
 
@@ -290,14 +287,12 @@
      */
     @VisibleForTesting
     public static void e(String tag, String messageTemplate, Object... args) {
-        if (Log.isLoggable(tag, Log.ERROR)) {
-            String message = formatLog(messageTemplate, args);
-            Throwable tr = getThrowableToLog(args);
-            if (tr != null) {
-                android.util.Log.e(tag, message, tr);
-            } else {
-                android.util.Log.e(tag, message);
-            }
+        String message = formatLog(messageTemplate, args);
+        Throwable tr = getThrowableToLog(args);
+        if (tr != null) {
+            android.util.Log.e(tag, message, tr);
+        } else {
+            android.util.Log.e(tag, message);
         }
     }
 
@@ -316,14 +311,12 @@
      */
     @VisibleForTesting
     public static void wtf(String tag, String messageTemplate, Object... args) {
-        if (Log.isLoggable(tag, Log.ASSERT)) {
-            String message = formatLog(messageTemplate, args);
-            Throwable tr = getThrowableToLog(args);
-            if (tr != null) {
-                android.util.Log.wtf(tag, message, tr);
-            } else {
-                android.util.Log.wtf(tag, message);
-            }
+        String message = formatLog(messageTemplate, args);
+        Throwable tr = getThrowableToLog(args);
+        if (tr != null) {
+            android.util.Log.wtf(tag, message, tr);
+        } else {
+            android.util.Log.wtf(tag, message);
         }
     }
 
diff --git a/android/java/src/org/chromium/base/PathUtils.java b/android/java/src/org/chromium/base/PathUtils.java
index 58b54ff..6f29732 100644
--- a/android/java/src/org/chromium/base/PathUtils.java
+++ b/android/java/src/org/chromium/base/PathUtils.java
@@ -23,8 +23,7 @@
     private static final int DATA_DIRECTORY = 0;
     private static final int DATABASE_DIRECTORY = 1;
     private static final int CACHE_DIRECTORY = 2;
-    private static final int DOWNLOADS_DIRECTORY = 3;
-    private static final int NUM_DIRECTORIES = 4;
+    private static final int NUM_DIRECTORIES = 3;
     private static AsyncTask<String, Void, String[]> sDirPathFetchTask;
 
     private static File sThumbnailDirectory;
@@ -50,12 +49,7 @@
                 paths[DATABASE_DIRECTORY] = appContext.getDatabasePath("foo").getParent();
                 // TODO(wnwen): Find a way to avoid calling this function in renderer process.
                 if (appContext.getCacheDir() != null) {
-                    // These paths are only available in the browser process.
                     paths[CACHE_DIRECTORY] = appContext.getCacheDir().getPath();
-                    paths[DOWNLOADS_DIRECTORY] = Environment
-                                                         .getExternalStoragePublicDirectory(
-                                                                 Environment.DIRECTORY_DOWNLOADS)
-                                                         .getPath();
                 }
                 return paths;
             }
@@ -121,8 +115,8 @@
     @SuppressWarnings("unused")
     @CalledByNative
     private static String getDownloadsDirectory(Context appContext) {
-        assert sDirPathFetchTask != null : "setDataDirectorySuffix must be called first.";
-        return getDirectoryPath(DOWNLOADS_DIRECTORY);
+        return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+                .getPath();
     }
 
     /**
diff --git a/android/java/src/org/chromium/base/README_logging.md b/android/java/src/org/chromium/base/README_logging.md
deleted file mode 100644
index 6104bf0..0000000
--- a/android/java/src/org/chromium/base/README_logging.md
+++ /dev/null
@@ -1,181 +0,0 @@
-## Logging ##
-
-Logging used to be done using Android's [android.util.Log]
-(http://developer.android.com/reference/android/util/Log.html).
-
-A wrapper on that is now available: org.chromium.base.Log. It is designed to write logs as
-belonging to logical groups going beyond single classes, and to make it easy to switch logging on
-or off for individual groups.
-
-Usage:
-
-    private static final String TAG = "cr.YourModuleTag";
-    ...
-    Log.i(TAG, "Logged INFO message.");
-    Log.d(TAG, "Some DEBUG info: %s", data);
-
-Output:
-
-    I/cr.YourModuleTag: ( 999): Logged INFO message
-    D/cr.YourModuleTag: ( 999): [MyClass.java:42] Some DEBUG info: data's toString output
-
-Here, **TAG** will be a feature or package name, "MediaRemote" or "NFC" for example. In most
-cases, the class name is not needed.
-
-**Caveat:** Property keys are limited to 23 characters. If the tag is too long, `Log#isLoggable`
-throws a RuntimeException.
-
-### Verbose and Debug logs have special handling ###
-
-*   `Log.v` and `Log.d` Calls made using `org.chromium.base.Log` are stripped
-    out of production binaries using Proguard. There is no way to get those logs
-	in release builds.
-
-*   The file name and line number will be prepended to the log message.
-    For higher priority logs, those are not added for performance concerns.
-
-*   By default, Verbose and Debug logs are not enabled, see guarding:
-
-### Log calls are guarded: Tag groups can be enabled or disabled using ADB ###
-
-    adb shell setprop log.tag.cr.YourModuleTag <LEVEL>
-
-Level here is either `VERBOSE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `ASSERT`, or `SUPPRESS`
-By default, the level for all tags is `INFO`.
-
-### An exception trace is printed when the exception is the last parameter ###
-
-As with `java.util.Log`, putting a throwable as last parameter will dump the corresponding stack
-trace:
-
-    Log.i(TAG, "An error happened: %s", e)
-
-    I/cr.YourModuleTag: ( 999): An error happened: This is the exception's message
-    I/cr.YourModuleTag: ( 999): java.lang.Exception: This is the exception's message
-    I/cr.YourModuleTag: ( 999):     at foo.bar.MyClass.test(MyClass.java:42)
-    I/cr.YourModuleTag: ( 999):     ...
-
-Having the exception as last parameter doesn't prevent it from being used for string formatting.
-
-### Logging Best Practices
-
-#### Rule #1: Never log PII (Personal Identification Information):
-
-This is a huge concern, because other applications can access the log and extract a lot of data
-from your own by doing so. Even if JellyBean restricted this, people are going to run your
-application on rooted devices and allow some apps to access it. Also anyone with USB access to the
-device can use ADB to get the full logcat and get the same data right now.
-
-If you really need to print something , print a series of Xs instead (e.g. "XXXXXX"), or print a
-truncated hash of the PII instead. Truncation is required to make it harder for an attacker to
-recover the full data through rainbow tables and similar methods.
-
-Similarly, avoid dumping API keys, cookies, etc...
-
-#### Rule #2: Do not write debug logs in production code:
-
-The kernel log buffer is global and of limited size. Any extra debug log you add to your activity
-or service makes it more difficult to diagnose problems on other parts of the system, because they
-tend to push the interesting bit out of the buffer too soon. This is a recurring problem on
-Android, so avoid participating into it.
-
-Logs can be disabled using system properties. Because log messages might not be
-written, the cost of creating them should also be avoided. This can be done using three
-complementary ways:
-
--   Use string formatting instead of concatenations
-
-        // BAD
-        Log.d(TAG, "I " + preference + " writing logs.");
-
-        // BETTER
-        Log.d(TAG, "I %s writing logs.", preference);
-
-    If logging is disabled, the function's arguments will still have to be computed and provided
-    as input. The first call above will always lead to the creation of a `StringBuilder` and a few
-    concatenations, while the second just passes the arguments and won't need that.
-
--   Guard expensive calls
-
-    Sometimes the values to log aren't readily available and need to be computed specially. This
-    should be avoided when logging is disabled.
-
-    Using `Log#isLoggable` will return whether logging for a specific tag is allowed or not. It is
-    the call used inside the log functions and using allows to know when running the expensive
-    functions is needed.
-
-        if (Log.isLoggable(TAG, Log.DEBUG) {
-          Log.d(TAG, "Something happened: %s", dumpDom(tab));
-        }
-
-    For more info, See the [android framework documentation]
-    (http://developer.android.com/tools/debugging/debugging-log.html).
-
-    Using a debug constant is a less flexible, but more perfomance oriented alternative.
-
-        static private final boolean DEBUG = false;  // set to 'true' to enable debug
-        ...
-        if (DEBUG) {
-          Log.i(TAG, createThatExpensiveLogMessage(activity))
-        }
-
-    Because the variable is a `static final` that can be evaluated at compile time, the Java
-    compiler will optimize out all guarded calls from the generated `.class` file. Changing it
-    however requires editing each of the files for which debug should be enabled and recompiling,
-    while the previous method can enable or disable debugging for a whole feature without changing
-    any source file.
-
--   Annotate debug functions with the `@RemovableInRelease` annotation.
-
-    That annotation tells Proguard to assume that a given function has no side effects, and is
-    called only for its returned value. If this value is unused, the call will be removed. If the
-    function is not called at all, it will also be removed. Since Proguard is already used to
-    strip debug and verbose calls out of release builds, this annotation allows it to have a
-    deeper action by removing also function calls used to generate the log call's arguments.
-  
-        /* If that function is only used in Log.d calls, proguard should completely remove it from
-         * the release builds. */
-        @RemovableInRelease
-        private static String getSomeDebugLogString(Thing[] things) {
-          /* Still needs to be guarded to avoid impacting debug builds, or in case it's used for
-           * some other log levels. But at least it is done only once, inside the function. */
-          if (!Log.isLoggable(TAG, Log.DEBUG)) return null;
-
-          StringBuilder sb = new StringBuilder("Reporting " + thing.length + " things:");
-          for (Thing thing : things) {
-            sb.append('\n').append(thing.id).append(' ').append(report.foo);
-          }
-          return sb.toString();
-        }
-
-        public void bar() {
-          ...
-          Log.d(TAG, getSomeDebugLogString(things)); /* In debug builds, the function does nothing
-                                                      * is debug is disabled, and the entire line 
-                                                      * is removed in release builds. */
-        }
-
-    Again, this is useful only if the input to that function are variables already available in
-    the scope. The idea is to move computations, concatenations, etc. to a place where that can be
-    removed when not needed, without invading the main function's logic.
-
-#### Rule #3: Favor small log messages
-
-This is still related to the global fixed-sized kernel buffer used to keep all logs. Try to make
-your log information as terse as possible. This reduces the risk of pushing interesting log data
-out of the buffer when something really nasty happens. It's really better to have a single-line
-log message, than several ones. I.e. don't use:
-
-    Log.GROUP.d(TAG, "field1 = %s", value1);
-    Log.GROUP.d(TAG, "field2 = %s", value2);
-    Log.GROUP.d(TAG, "field3 = %s", value3);
-
-Instead, write this as:
-
-    Log.d(TAG, "field1 = %s, field2 = %s, field3 = %s", value1, value2, value3);
-
-That doesn't seem to be much different if you count overall character counts, but each independent
-log entry also implies a small, but non-trivial header, in the kernel log buffer.
-And since every byte count, you can also try something even shorter, as in:
-
-    Log.d(TAG, "fields [%s,%s,%s]", value1, value2, value3);
diff --git a/android/java/src/org/chromium/base/library_loader/LegacyLinker.java b/android/java/src/org/chromium/base/library_loader/LegacyLinker.java
index 8e6776a..05c861d 100644
--- a/android/java/src/org/chromium/base/library_loader/LegacyLinker.java
+++ b/android/java/src/org/chromium/base/library_loader/LegacyLinker.java
@@ -71,76 +71,54 @@
         return new LegacyLinker();
     }
 
-    // Used internally to initialize the linker's data. Assume lock is held.
+    // Used internally to initialize the linker's data. Assumes lock is held.
+    // Loads JNI, and sets mMemoryDeviceConfig and mBrowserUsesSharedRelro.
     private void ensureInitializedLocked() {
         assert Thread.holdsLock(mLock);
 
-        if (mInitialized) {
+        if (mInitialized || !NativeLibraries.sUseLinker) {
             return;
         }
 
-        if (NativeLibraries.sUseLinker) {
-            // Load libchromium_android_linker.so.
-            loadLinkerJNILibrary();
+        // On first call, load libchromium_android_linker.so. Cannot be done in the
+        // constructor because instantiation occurs on the UI thread.
+        loadLinkerJniLibrary();
 
-            if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT) {
-                if (SysUtils.isLowEndDevice()) {
-                    mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_LOW;
-                } else {
-                    mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_NORMAL;
-                }
+        if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT) {
+            if (SysUtils.isLowEndDevice()) {
+                mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_LOW;
+            } else {
+                mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_NORMAL;
             }
+        }
 
-            switch (BROWSER_SHARED_RELRO_CONFIG) {
-                case BROWSER_SHARED_RELRO_CONFIG_NEVER:
-                    mBrowserUsesSharedRelro = false;
-                    break;
-                case BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY:
-                    if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW) {
-                        mBrowserUsesSharedRelro = true;
-                        Log.w(TAG, "Low-memory device: shared RELROs used in all processes");
-                    } else {
-                        mBrowserUsesSharedRelro = false;
-                    }
-                    break;
-                case BROWSER_SHARED_RELRO_CONFIG_ALWAYS:
-                    Log.w(TAG, "Beware: shared RELROs used in all processes!");
+        // Cannot run in the constructor because SysUtils.isLowEndDevice() relies
+        // on CommandLine, which may not be available at instantiation.
+        switch (BROWSER_SHARED_RELRO_CONFIG) {
+            case BROWSER_SHARED_RELRO_CONFIG_NEVER:
+                mBrowserUsesSharedRelro = false;
+                break;
+            case BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY:
+                if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW) {
                     mBrowserUsesSharedRelro = true;
-                    break;
-                default:
-                    assert false : "Unreached";
-                    break;
-            }
-        } else {
-            if (DEBUG) {
-                Log.i(TAG, "Linker disabled");
-            }
+                    Log.w(TAG, "Low-memory device: shared RELROs used in all processes");
+                } else {
+                    mBrowserUsesSharedRelro = false;
+                }
+                break;
+            case BROWSER_SHARED_RELRO_CONFIG_ALWAYS:
+                Log.w(TAG, "Beware: shared RELROs used in all processes!");
+                mBrowserUsesSharedRelro = true;
+                break;
+            default:
+                assert false : "Unreached";
+                break;
         }
 
         mInitialized = true;
     }
 
     /**
-     * Call this method to determine if this chromium project must
-     * use this linker. If not, System.loadLibrary() should be used to load
-     * libraries instead.
-     */
-    @Override
-    public boolean isUsed() {
-        // Only GYP targets that are APKs and have the 'use_chromium_linker' variable
-        // defined as 1 will use this linker. For all others (the default), the
-        // auto-generated NativeLibraries.sUseLinker variable will be false.
-        if (!NativeLibraries.sUseLinker) {
-            return false;
-        }
-
-        synchronized (mLock) {
-            ensureInitializedLocked();
-            return true;
-        }
-    }
-
-    /**
      * Call this method to determine if the linker will try to use shared RELROs
      * for the browser process.
      */
@@ -187,10 +165,8 @@
             if (DEBUG) {
                 Log.i(TAG,
                         String.format(Locale.US,
-                                "mInBrowserProcess=%s mBrowserUsesSharedRelro=%s mWaitForSharedRelros=%s",
-                                mInBrowserProcess ? "true" : "false",
-                                mBrowserUsesSharedRelro ? "true" : "false",
-                                mWaitForSharedRelros ? "true" : "false"));
+                                "mInBrowserProcess=%b mBrowserUsesSharedRelro=%b mWaitForSharedRelros=%b",
+                                mInBrowserProcess, mBrowserUsesSharedRelro, mWaitForSharedRelros));
             }
 
             if (mLoadedLibraries == null) {
@@ -315,6 +291,7 @@
             Log.i(TAG, "disableSharedRelros() called");
         }
         synchronized (mLock) {
+            ensureInitializedLocked();
             mInBrowserProcess = false;
             mWaitForSharedRelros = false;
             mBrowserUsesSharedRelro = false;
@@ -441,7 +418,9 @@
         }
 
         // In service processes, close all file descriptors from the map now.
-        if (!mInBrowserProcess) closeLibInfoMap(relroMap);
+        if (!mInBrowserProcess) {
+            closeLibInfoMap(relroMap);
+        }
 
         if (DEBUG) {
             Log.i(TAG, "Linker.useSharedRelrosLocked() exiting");
diff --git a/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index e581300..4fa8020 100644
--- a/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -219,13 +219,10 @@
                 assert !mInitialized;
 
                 long startTime = SystemClock.uptimeMillis();
-                Linker linker = Linker.getInstance();
-                boolean useChromiumLinker = linker.isUsed();
 
-                if (useChromiumLinker) {
-                    // Determine the APK file path.
-                    String apkFilePath = getLibraryApkPath(context);
+                if (Linker.isUsed()) {
                     // Load libraries using the Chromium linker.
+                    Linker linker = Linker.getInstance();
                     linker.prepareLibraryLoad();
 
                     for (String library : NativeLibraries.LIBRARIES) {
@@ -242,8 +239,8 @@
                         String libFilePath = System.mapLibraryName(library);
                         if (linker.isInZipFile()) {
                             // Load directly from the APK.
-                            zipFilePath = apkFilePath;
-                            Log.i(TAG, "Loading " + library + " from within " + apkFilePath);
+                            zipFilePath = getLibraryApkPath(context);
+                            Log.i(TAG, "Loading " + library + " from within " + zipFilePath);
                         } else {
                             // The library is in its own file.
                             Log.i(TAG, "Loading " + library);
diff --git a/android/java/src/org/chromium/base/library_loader/Linker.java b/android/java/src/org/chromium/base/library_loader/Linker.java
index c696288..9c280e8 100644
--- a/android/java/src/org/chromium/base/library_loader/Linker.java
+++ b/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -148,6 +148,9 @@
     // Log tag for this class.
     private static final String TAG = "cr.library_loader";
 
+    // Name of the library that contains our JNI code.
+    private static final String LINKER_JNI_LIBRARY = "chromium_android_linker";
+
     // Constants used to control the behaviour of the browser process with
     // regards to the shared RELRO section. Not applicable to ModernLinker.
     //   NEVER        -> The browser never uses it itself.
@@ -180,9 +183,6 @@
     // Not used by ModernLinker.
     protected int mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_INIT;
 
-    // Name of the library that contains our JNI code.
-    protected static final String LINKER_JNI_LIBRARY = "chromium_android_linker";
-
     // Set to true to enable debug logs.
     protected static final boolean DEBUG = false;
 
@@ -257,7 +257,7 @@
      *
      * @return true if native library linker tests are enabled.
      */
-    public static boolean areLinkerTestsEnabled() {
+    public static boolean areTestsEnabled() {
         return NativeLibraries.sEnableLinkerTests;
     }
 
@@ -292,7 +292,7 @@
      *
      * @param type LINKER_IMPLEMENTATION_LEGACY or LINKER_IMPLEMENTATION_MODERN
      */
-    public static final void setLinkerImplementationForTesting(int type) {
+    public static final void setImplementationForTesting(int type) {
         // Sanity check. This method may only be called during tests.
         assertLinkerTestsAreEnabled();
         assertForTesting(
@@ -318,18 +318,21 @@
      *
      * @return LINKER_IMPLEMENTATION_LEGACY or LINKER_IMPLEMENTATION_MODERN
      */
-    public int getLinkerImplementationForTesting() {
+    public int getImplementationForTesting() {
         // Sanity check. This method may only be called during tests.
         assertLinkerTestsAreEnabled();
-        assertForTesting(sSingleton != null);
 
-        if (sSingleton instanceof ModernLinker) {
-            return LINKER_IMPLEMENTATION_MODERN;
-        } else if (sSingleton instanceof LegacyLinker) {
-            return LINKER_IMPLEMENTATION_LEGACY;
+        synchronized (sSingletonLock) {
+            assertForTesting(sSingleton != null);
+
+            if (sSingleton instanceof ModernLinker) {
+                return LINKER_IMPLEMENTATION_MODERN;
+            } else if (sSingleton instanceof LegacyLinker) {
+                return LINKER_IMPLEMENTATION_LEGACY;
+            }
+            Log.e(TAG, "Invalid linker: " + sSingleton.getClass().getName());
+            return 0;
         }
-        Log.e(TAG, "Invalid linker: " + sSingleton.getClass().getName());
-        return 0;
     }
 
     /**
@@ -358,7 +361,7 @@
      */
     public void setTestRunnerClassNameForTesting(String testRunnerClassName) {
         if (DEBUG) {
-            Log.i(TAG, "setTestRunnerByClassNameForTesting(" + testRunnerClassName + ") called");
+            Log.i(TAG, "setTestRunnerClassNameForTesting(" + testRunnerClassName + ") called");
         }
         // Sanity check. This method may only be called during tests.
         assertLinkerTestsAreEnabled();
@@ -387,6 +390,42 @@
     }
 
     /**
+     * Set up the Linker for a test.
+     * Convenience function that calls setImplementationForTesting() to force an
+     * implementation, and then setTestRunnerClassNameForTesting() to set the test
+     * class name.
+     *
+     * On first call, instantiates a Linker of the requested type and sets its test
+     * runner class name. On subsequent calls, checks that the singleton produced by
+     * the first call matches the requested type and test runner class name.
+     */
+    public static void setupForTesting(int type, String testRunnerClassName) {
+        if (DEBUG) {
+            Log.i(TAG, "setupForTesting(" + type + ", " + testRunnerClassName + ") called");
+        }
+        // Sanity check. This method may only be called during tests.
+        assertLinkerTestsAreEnabled();
+
+        synchronized (sSingletonLock) {
+            // If this is the first call, configure the Linker to the given type and test class.
+            if (sSingleton == null) {
+                setImplementationForTesting(type);
+                sSingleton.setTestRunnerClassNameForTesting(testRunnerClassName);
+                return;
+            }
+
+            // If not the first call, check that the Linker configuration matches this request.
+            assertForTesting(sSingleton.getImplementationForTesting() == type);
+            String ourTestRunnerClassName = sSingleton.getTestRunnerClassNameForTesting();
+            if (testRunnerClassName == null) {
+                assertForTesting(ourTestRunnerClassName == null);
+            } else {
+                assertForTesting(ourTestRunnerClassName.equals(testRunnerClassName));
+            }
+        }
+    }
+
+    /**
      * Instantiate and run the current TestRunner, if any. The TestRunner implementation
      * must be instantiated _after_ all libraries are loaded to ensure that its
      * native methods are properly registered.
@@ -471,7 +510,7 @@
      * In a component build, the suffix ".cr" is added to each library name, so
      * if the initial load fails we retry with a suffix.
      */
-    protected void loadLinkerJNILibrary() {
+    protected static void loadLinkerJniLibrary() {
         String libName = "lib" + LINKER_JNI_LIBRARY + ".so";
         if (DEBUG) {
             Log.i(TAG, "Loading " + libName);
@@ -479,7 +518,7 @@
         try {
             System.loadLibrary(LINKER_JNI_LIBRARY);
         } catch (UnsatisfiedLinkError e) {
-            Log.w(TAG, "Couldn't load " + libName + ", trying " + libName + ".so");
+            Log.w(TAG, "Couldn't load " + libName + ", trying " + libName + ".cr");
             System.loadLibrary(LINKER_JNI_LIBRARY + ".cr");
         }
     }
@@ -553,7 +592,7 @@
      * Call this method to determine if the chromium project must load the library
      * directly from a zip file.
      */
-    public boolean isInZipFile() {
+    public static boolean isInZipFile() {
         // The auto-generated NativeLibraries.sUseLibraryInZipFile variable will be true
         // if the library remains embedded in the APK zip file on the target.
         return NativeLibraries.sUseLibraryInZipFile;
@@ -564,7 +603,11 @@
      * use this linker. If not, System.loadLibrary() should be used to load
      * libraries instead.
      */
-    public abstract boolean isUsed();
+    public static boolean isUsed() {
+        // The auto-generated NativeLibraries.sUseLinker variable will be true if the
+        // build has not explicitly disabled Linker features.
+        return NativeLibraries.sUseLinker;
+    }
 
     /**
      * Call this method to determine if the linker will try to use shared RELROs
diff --git a/android/java/src/org/chromium/base/library_loader/ModernLinker.java b/android/java/src/org/chromium/base/library_loader/ModernLinker.java
index a437779..4b01186 100644
--- a/android/java/src/org/chromium/base/library_loader/ModernLinker.java
+++ b/android/java/src/org/chromium/base/library_loader/ModernLinker.java
@@ -70,32 +70,20 @@
         return new ModernLinker();
     }
 
-    // Used internally to initialize the linker's data. Assume lock is held.
+    // Used internally to initialize the linker's data. Assumes lock is held.
     private void ensureInitializedLocked() {
         assert Thread.holdsLock(mLock);
         assert NativeLibraries.sUseLinker;
 
-        // On first call, load libchromium_android_linker.so.
+        // On first call, load libchromium_android_linker.so. Cannot be done in the
+        // constructor because the instance is constructed on the UI thread.
         if (!mInitialized) {
-            loadLinkerJNILibrary();
+            loadLinkerJniLibrary();
             mInitialized = true;
         }
     }
 
     /**
-     * Call this method to determine if this chromium project must
-     * use this linker. If not, System.loadLibrary() should be used to load
-     * libraries instead.
-     */
-    @Override
-    public boolean isUsed() {
-        // Only GYP targets that are APKs and have the 'use_chromium_linker' variable
-        // defined as 1 will use this linker. For all others (the default), the
-        // auto-generated NativeLibraries.sUseLinker variable will be false.
-        return NativeLibraries.sUseLinker;
-    }
-
-    /**
      * Call this method to determine if the linker will try to use shared RELROs
      * for the browser process.
      */
diff --git a/android/jni_generator/golden_sample_for_tests_jni.h b/android/jni_generator/golden_sample_for_tests_jni.h
index 8979697..fd1a632 100644
--- a/android/jni_generator/golden_sample_for_tests_jni.h
+++ b/android/jni_generator/golden_sample_for_tests_jni.h
@@ -41,14 +41,16 @@
 
 // Step 2: method stubs.
 
-static jlong Init(JNIEnv* env, jobject jcaller,
-    jstring param);
+static jlong Init(JNIEnv* env,
+                  const JavaParamRef<jobject>& jcaller,
+                  const JavaParamRef<jstring>& param);
 
 static jlong Java_org_chromium_example_jni_1generator_SampleForTests_nativeInit(
     JNIEnv* env,
     jobject jcaller,
     jstring param) {
-  return Init(env, jcaller, param);
+  return Init(env, JavaParamRef<jobject>(env, jcaller),
+              JavaParamRef<jstring>(env, param));
 }
 
 static void
@@ -58,46 +60,51 @@
     jlong nativeCPPClass) {
   CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
   CHECK_NATIVE_PTR(env, jcaller, native, "Destroy");
-  return native->Destroy(env, jcaller);
+  return native->Destroy(env, JavaParamRef<jobject>(env, jcaller));
 }
 
-static jdouble GetDoubleFunction(JNIEnv* env, jobject jcaller);
+static jdouble GetDoubleFunction(JNIEnv* env,
+                                 const JavaParamRef<jobject>& jcaller);
 
 static jdouble
 Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetDoubleFunction(
     JNIEnv* env,
     jobject jcaller) {
-  return GetDoubleFunction(env, jcaller);
+  return GetDoubleFunction(env, JavaParamRef<jobject>(env, jcaller));
 }
 
-static jfloat GetFloatFunction(JNIEnv* env, jclass jcaller);
+static jfloat GetFloatFunction(JNIEnv* env,
+                               const JavaParamRef<jclass>& jcaller);
 
 static jfloat
 Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetFloatFunction(
     JNIEnv* env,
     jclass jcaller) {
-  return GetFloatFunction(env, jcaller);
+  return GetFloatFunction(env, JavaParamRef<jclass>(env, jcaller));
 }
 
-static void SetNonPODDatatype(JNIEnv* env, jobject jcaller,
-    jobject rect);
+static void SetNonPODDatatype(JNIEnv* env,
+                              const JavaParamRef<jobject>& jcaller,
+                              const JavaParamRef<jobject>& rect);
 
 static void
 Java_org_chromium_example_jni_1generator_SampleForTests_nativeSetNonPODDatatype(
     JNIEnv* env,
     jobject jcaller,
     jobject rect) {
-  return SetNonPODDatatype(env, jcaller, rect);
+  return SetNonPODDatatype(env, JavaParamRef<jobject>(env, jcaller),
+                           JavaParamRef<jobject>(env, rect));
 }
 
-static ScopedJavaLocalRef<jobject> GetNonPODDatatype(JNIEnv* env,
-                                                     jobject jcaller);
+static ScopedJavaLocalRef<jobject> GetNonPODDatatype(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller);
 
 static jobject
 Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetNonPODDatatype(
     JNIEnv* env,
     jobject jcaller) {
-  return GetNonPODDatatype(env, jcaller).Release();
+  return GetNonPODDatatype(env, JavaParamRef<jobject>(env, jcaller)).Release();
 }
 
 static jint
@@ -107,7 +114,7 @@
     jlong nativeCPPClass) {
   CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
   CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0);
-  return native->Method(env, jcaller);
+  return native->Method(env, JavaParamRef<jobject>(env, jcaller));
 }
 
 static jdouble
@@ -118,7 +125,7 @@
   CPPClass::InnerClass* native =
       reinterpret_cast<CPPClass::InnerClass*>(nativePtr);
   CHECK_NATIVE_PTR(env, jcaller, native, "MethodOtherP0", 0);
-  return native->MethodOtherP0(env, jcaller);
+  return native->MethodOtherP0(env, JavaParamRef<jobject>(env, jcaller));
 }
 
 static void
@@ -129,7 +136,8 @@
     jobject b) {
   CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
   CHECK_NATIVE_PTR(env, jcaller, native, "AddStructB");
-  return native->AddStructB(env, jcaller, b);
+  return native->AddStructB(env, JavaParamRef<jobject>(env, jcaller),
+                            JavaParamRef<jobject>(env, b));
 }
 
 static void
@@ -139,7 +147,8 @@
     jlong nativeCPPClass) {
   CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
   CHECK_NATIVE_PTR(env, jcaller, native, "IterateAndDoSomethingWithStructB");
-  return native->IterateAndDoSomethingWithStructB(env, jcaller);
+  return native->IterateAndDoSomethingWithStructB(
+      env, JavaParamRef<jobject>(env, jcaller));
 }
 
 static jstring
@@ -149,7 +158,8 @@
     jlong nativeCPPClass) {
   CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass);
   CHECK_NATIVE_PTR(env, jcaller, native, "ReturnAString", NULL);
-  return native->ReturnAString(env, jcaller).Release();
+  return native->ReturnAString(env, JavaParamRef<jobject>(env, jcaller))
+      .Release();
 }
 
 static base::subtle::AtomicWord g_SampleForTests_javaMethod = 0;
diff --git a/android/jni_generator/jni_generator.py b/android/jni_generator/jni_generator.py
index 33f83ec..5d262c8 100755
--- a/android/jni_generator/jni_generator.py
+++ b/android/jni_generator/jni_generator.py
@@ -135,6 +135,19 @@
     return 'jobject'
 
 
+def WrapCTypeForDeclaration(c_type):
+  """Wrap the C datatype in a JavaRef if required."""
+  if re.match(RE_SCOPED_JNI_TYPES, c_type):
+    return 'const JavaParamRef<' + c_type + '>&'
+  else:
+    return c_type
+
+
+def JavaDataTypeToCForDeclaration(java_type):
+  """Returns a JavaRef-wrapped C datatype for the given java type."""
+  return WrapCTypeForDeclaration(JavaDataTypeToC(java_type))
+
+
 def JavaDataTypeToCForCalledByNativeParam(java_type):
   """Returns a C datatype to be when calling from native."""
   if java_type == 'int':
@@ -527,10 +540,9 @@
   return called_by_natives
 
 
-# Regex to match the JNI return types that should be included in a
-# ScopedJavaLocalRef.
-RE_SCOPED_JNI_RETURN_TYPES = re.compile(
-    'jobject|jclass|jstring|jthrowable|.*Array')
+# Regex to match the JNI types that should be wrapped in a JavaRef.
+RE_SCOPED_JNI_TYPES = re.compile('jobject|jclass|jstring|jthrowable|.*Array')
+
 
 # Regex to match a string like "@CalledByNative public void foo(int bar)".
 RE_CALLED_BY_NATIVE = re.compile(
@@ -1005,18 +1017,36 @@
       return '\n'.join(all_namespaces) + '\n'
     return ''
 
-  def GetJNIFirstParam(self, native):
-    ret = []
+  def GetJNIFirstParamType(self, native):
     if native.type == 'method':
-      ret = ['jobject jcaller']
+      return 'jobject'
     elif native.type == 'function':
       if native.static:
-        ret = ['jclass jcaller']
+        return 'jclass'
       else:
-        ret = ['jobject jcaller']
-    return ret
+        return 'jobject'
+
+  def GetJNIFirstParam(self, native, for_declaration):
+    c_type = self.GetJNIFirstParamType(native)
+    if for_declaration:
+      c_type = WrapCTypeForDeclaration(c_type)
+    return [c_type + ' jcaller']
 
   def GetParamsInDeclaration(self, native):
+    """Returns the params for the forward declaration.
+
+    Args:
+      native: the native dictionary describing the method.
+
+    Returns:
+      A string containing the params.
+    """
+    return ',\n    '.join(self.GetJNIFirstParam(native, True) +
+                          [JavaDataTypeToCForDeclaration(param.datatype) + ' ' +
+                           param.name
+                           for param in native.params])
+
+  def GetParamsInStub(self, native):
     """Returns the params for the stub declaration.
 
     Args:
@@ -1025,7 +1055,7 @@
     Returns:
       A string containing the params.
     """
-    return ',\n    '.join(self.GetJNIFirstParam(native) +
+    return ',\n    '.join(self.GetJNIFirstParam(native, False) +
                           [JavaDataTypeToC(param.datatype) + ' ' +
                            param.name
                            for param in native.params])
@@ -1056,6 +1086,16 @@
               'JAVA_NAME': java_name}
     return template.substitute(values)
 
+  def GetJavaParamRefForCall(self, c_type, name):
+    return Template('JavaParamRef<${TYPE}>(env, ${NAME})').substitute({
+        'TYPE': c_type,
+        'NAME': name,
+    })
+
+  def GetJNIFirstParamForCall(self, native):
+    c_type = self.GetJNIFirstParamType(native)
+    return [self.GetJavaParamRefForCall(c_type, 'jcaller')]
+
   def GetNativeStub(self, native):
     is_method = native.type == 'method'
 
@@ -1065,8 +1105,14 @@
       params = native.params
     params_in_call = []
     if not self.options.pure_native_methods:
-      params_in_call = ['env', 'jcaller']
-    params_in_call = ', '.join(params_in_call + [p.name for p in params])
+      params_in_call = ['env'] + self.GetJNIFirstParamForCall(native)
+    for p in params:
+      c_type = JavaDataTypeToC(p.datatype)
+      if re.match(RE_SCOPED_JNI_TYPES, c_type):
+        params_in_call.append(self.GetJavaParamRefForCall(c_type, p.name))
+      else:
+        params_in_call.append(p.name)
+    params_in_call = ', '.join(params_in_call)
 
     if self.options.native_exports:
       stub_visibility = 'extern "C" __attribute__((visibility("default")))\n'
@@ -1074,7 +1120,7 @@
       stub_visibility = 'static '
     return_type = return_declaration = JavaDataTypeToC(native.return_type)
     post_call = ''
-    if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
+    if re.match(RE_SCOPED_JNI_TYPES, return_type):
       post_call = '.Release()'
       return_declaration = 'ScopedJavaLocalRef<' + return_type + '>'
     values = {
@@ -1082,6 +1128,7 @@
         'RETURN_DECLARATION': return_declaration,
         'NAME': native.name,
         'PARAMS': self.GetParamsInDeclaration(native),
+        'PARAMS_IN_STUB': self.GetParamsInStub(native),
         'PARAMS_IN_CALL': params_in_call,
         'POST_CALL': post_call,
         'STUB_NAME': self.GetStubName(native),
@@ -1099,7 +1146,7 @@
       })
       template = Template("""\
 ${STUB_VISIBILITY}${RETURN} ${STUB_NAME}(JNIEnv* env,
-    ${PARAMS}) {
+    ${PARAMS_IN_STUB}) {
   ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME});
   CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN});
   return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL};
@@ -1109,7 +1156,7 @@
       template = Template("""
 static ${RETURN_DECLARATION} ${NAME}(JNIEnv* env, ${PARAMS});
 
-${STUB_VISIBILITY}${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS}) {
+${STUB_VISIBILITY}${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_STUB}) {
   return ${NAME}(${PARAMS_IN_CALL})${POST_CALL};
 }
 """)
@@ -1157,7 +1204,7 @@
     if return_type != 'void':
       pre_call = ' ' + pre_call
       return_declaration = return_type + ' ret ='
-      if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type):
+      if re.match(RE_SCOPED_JNI_TYPES, return_type):
         return_type = 'ScopedJavaLocalRef<' + return_type + '>'
         return_clause = 'return ' + return_type + '(env, ret);'
       else:
diff --git a/android/jni_generator/jni_generator_helper.h b/android/jni_generator/jni_generator_helper.h
index b9736e1..1fdc4ee 100644
--- a/android/jni_generator/jni_generator_helper.h
+++ b/android/jni_generator/jni_generator_helper.h
@@ -36,5 +36,6 @@
 }  // namespace jni_generator
 
 using base::android::ScopedJavaLocalRef;
+using base::android::JavaParamRef;
 
 #endif  // BASE_ANDROID_JNI_GENERATOR_JNI_GENERATOR_HELPER_H_
diff --git a/android/jni_generator/sample_for_tests.cc b/android/jni_generator/sample_for_tests.cc
index c5820ab..ee8d6db 100644
--- a/android/jni_generator/sample_for_tests.cc
+++ b/android/jni_generator/sample_for_tests.cc
@@ -67,22 +67,27 @@
 }
 
 // Static free functions declared and called directly from java.
-static jlong Init(JNIEnv* env, jobject caller, jstring param) {
+static jlong Init(JNIEnv* env,
+                  const JavaParamRef<jobject>& caller,
+                  const JavaParamRef<jstring>& param) {
   return 0;
 }
 
-static jdouble GetDoubleFunction(JNIEnv*, jobject) {
+static jdouble GetDoubleFunction(JNIEnv*, const JavaParamRef<jobject>&) {
   return 0;
 }
 
-static jfloat GetFloatFunction(JNIEnv*, jclass) {
+static jfloat GetFloatFunction(JNIEnv*, const JavaParamRef<jclass>&) {
   return 0;
 }
 
-static void SetNonPODDatatype(JNIEnv*, jobject, jobject) {
-}
+static void SetNonPODDatatype(JNIEnv*,
+                              const JavaParamRef<jobject>&,
+                              const JavaParamRef<jobject>&) {}
 
-static ScopedJavaLocalRef<jobject> GetNonPODDatatype(JNIEnv*, jobject) {
+static ScopedJavaLocalRef<jobject> GetNonPODDatatype(
+    JNIEnv*,
+    const JavaParamRef<jobject>&) {
   return ScopedJavaLocalRef<jobject>();
 }
 
diff --git a/android/jni_generator/testEagerCalledByNativesOption.golden b/android/jni_generator/testEagerCalledByNativesOption.golden
index cdd6151..b01cd39 100644
--- a/android/jni_generator/testEagerCalledByNativesOption.golden
+++ b/android/jni_generator/testEagerCalledByNativesOption.golden
@@ -36,7 +36,7 @@
     jint arg1) {
   Test* native = reinterpret_cast<Test*>(nativeTest);
   CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0);
-  return native->Method(env, jcaller, arg1);
+  return native->Method(env, JavaParamRef<jobject>(env, jcaller), arg1);
 }
 
 namespace {
diff --git a/android/jni_generator/testInnerClassNatives.golden b/android/jni_generator/testInnerClassNatives.golden
index d6676c4..ad140e2 100644
--- a/android/jni_generator/testInnerClassNatives.golden
+++ b/android/jni_generator/testInnerClassNatives.golden
@@ -28,11 +28,11 @@
 
 // Step 2: method stubs.
 
-static jint Init(JNIEnv* env, jobject jcaller);
+static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller);
 
 static jint Java_org_chromium_TestJni_00024MyInnerClass_nativeInit(JNIEnv* env,
     jobject jcaller) {
-  return Init(env, jcaller);
+  return Init(env, JavaParamRef<jobject>(env, jcaller));
 }
 
 // Step 3: RegisterNatives.
diff --git a/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden b/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden
index a9e645c..0a890e7 100644
--- a/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden
+++ b/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden
@@ -29,17 +29,17 @@
 
 // Step 2: method stubs.
 
-static jint Init(JNIEnv* env, jobject jcaller);
+static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller);
 
 static jint Java_org_chromium_TestJni_nativeInit(JNIEnv* env, jobject jcaller) {
-  return Init(env, jcaller);
+  return Init(env, JavaParamRef<jobject>(env, jcaller));
 }
 
-static jint Init(JNIEnv* env, jobject jcaller);
+static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller);
 
 static jint Java_org_chromium_TestJni_00024MyOtherInnerClass_nativeInit(JNIEnv*
     env, jobject jcaller) {
-  return Init(env, jcaller);
+  return Init(env, JavaParamRef<jobject>(env, jcaller));
 }
 
 // Step 3: RegisterNatives.
diff --git a/android/jni_generator/testInnerClassNativesMultiple.golden b/android/jni_generator/testInnerClassNativesMultiple.golden
index 5f54364..268f794 100644
--- a/android/jni_generator/testInnerClassNativesMultiple.golden
+++ b/android/jni_generator/testInnerClassNativesMultiple.golden
@@ -30,18 +30,18 @@
 
 // Step 2: method stubs.
 
-static jint Init(JNIEnv* env, jobject jcaller);
+static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller);
 
 static jint Java_org_chromium_TestJni_00024MyInnerClass_nativeInit(JNIEnv* env,
     jobject jcaller) {
-  return Init(env, jcaller);
+  return Init(env, JavaParamRef<jobject>(env, jcaller));
 }
 
-static jint Init(JNIEnv* env, jobject jcaller);
+static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller);
 
 static jint Java_org_chromium_TestJni_00024MyOtherInnerClass_nativeInit(JNIEnv*
     env, jobject jcaller) {
-  return Init(env, jcaller);
+  return Init(env, JavaParamRef<jobject>(env, jcaller));
 }
 
 // Step 3: RegisterNatives.
diff --git a/android/jni_generator/testJNIInitNativeNameOption.golden b/android/jni_generator/testJNIInitNativeNameOption.golden
index 084f08d..46a57ac 100644
--- a/android/jni_generator/testJNIInitNativeNameOption.golden
+++ b/android/jni_generator/testJNIInitNativeNameOption.golden
@@ -33,7 +33,7 @@
     jint arg1) {
   Test* native = reinterpret_cast<Test*>(nativeTest);
   CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0);
-  return native->Method(env, jcaller, arg1);
+  return native->Method(env, JavaParamRef<jobject>(env, jcaller), arg1);
 }
 
 // Step 3: RegisterNatives.
diff --git a/android/jni_generator/testJarJarRemapping.golden b/android/jni_generator/testJarJarRemapping.golden
index b626a8a..fe13952 100644
--- a/android/jni_generator/testJarJarRemapping.golden
+++ b/android/jni_generator/testJarJarRemapping.golden
@@ -27,40 +27,44 @@
 
 // Step 2: method stubs.
 
-static void Test(JNIEnv* env, jclass jcaller,
-    jobject t);
+static void Test(JNIEnv* env, const JavaParamRef<jclass>& jcaller,
+    const JavaParamRef<jobject>& t);
 
 static void Java_com_test_jni_1generator_Example_nativeTest(JNIEnv* env, jclass
     jcaller,
     jobject t) {
-  return Test(env, jcaller, t);
+  return Test(env, JavaParamRef<jclass>(env, jcaller),
+      JavaParamRef<jobject>(env, t));
 }
 
-static void Test2(JNIEnv* env, jclass jcaller,
-    jobject t);
+static void Test2(JNIEnv* env, const JavaParamRef<jclass>& jcaller,
+    const JavaParamRef<jobject>& t);
 
 static void Java_com_test_jni_1generator_Example_nativeTest2(JNIEnv* env, jclass
     jcaller,
     jobject t) {
-  return Test2(env, jcaller, t);
+  return Test2(env, JavaParamRef<jclass>(env, jcaller),
+      JavaParamRef<jobject>(env, t));
 }
 
-static void Test3(JNIEnv* env, jclass jcaller,
-    jobject t);
+static void Test3(JNIEnv* env, const JavaParamRef<jclass>& jcaller,
+    const JavaParamRef<jobject>& t);
 
 static void Java_com_test_jni_1generator_Example_nativeTest3(JNIEnv* env, jclass
     jcaller,
     jobject t) {
-  return Test3(env, jcaller, t);
+  return Test3(env, JavaParamRef<jclass>(env, jcaller),
+      JavaParamRef<jobject>(env, t));
 }
 
-static void Test4(JNIEnv* env, jclass jcaller,
-    jobject t);
+static void Test4(JNIEnv* env, const JavaParamRef<jclass>& jcaller,
+    const JavaParamRef<jobject>& t);
 
 static void Java_com_test_jni_1generator_Example_nativeTest4(JNIEnv* env, jclass
     jcaller,
     jobject t) {
-  return Test4(env, jcaller, t);
+  return Test4(env, JavaParamRef<jclass>(env, jcaller),
+      JavaParamRef<jobject>(env, t));
 }
 
 // Step 3: RegisterNatives.
diff --git a/android/jni_generator/testMultipleJNIAdditionalImport.golden b/android/jni_generator/testMultipleJNIAdditionalImport.golden
index eb15190..6de8c21 100644
--- a/android/jni_generator/testMultipleJNIAdditionalImport.golden
+++ b/android/jni_generator/testMultipleJNIAdditionalImport.golden
@@ -27,15 +27,17 @@
 
 // Step 2: method stubs.
 
-static void DoSomething(JNIEnv* env, jclass jcaller,
-    jobject callback1,
-    jobject callback2);
+static void DoSomething(JNIEnv* env, const JavaParamRef<jclass>& jcaller,
+    const JavaParamRef<jobject>& callback1,
+    const JavaParamRef<jobject>& callback2);
 
 static void Java_org_chromium_foo_Foo_nativeDoSomething(JNIEnv* env, jclass
     jcaller,
     jobject callback1,
     jobject callback2) {
-  return DoSomething(env, jcaller, callback1, callback2);
+  return DoSomething(env, JavaParamRef<jclass>(env, jcaller),
+      JavaParamRef<jobject>(env, callback1), JavaParamRef<jobject>(env,
+      callback2));
 }
 
 static base::subtle::AtomicWord g_Foo_calledByNative = 0;
diff --git a/android/jni_generator/testNativeExportsOption.golden b/android/jni_generator/testNativeExportsOption.golden
index 68736ad..13cd2b9 100644
--- a/android/jni_generator/testNativeExportsOption.golden
+++ b/android/jni_generator/testNativeExportsOption.golden
@@ -36,7 +36,7 @@
     jint arg1) {
   Test* native = reinterpret_cast<Test*>(nativeTest);
   CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0);
-  return native->StaticMethod(env, jcaller, arg1);
+  return native->StaticMethod(env, JavaParamRef<jobject>(env, jcaller), arg1);
 }
 
 extern "C" __attribute__((visibility("default")))
@@ -48,25 +48,25 @@
     jint arg1) {
   Test* native = reinterpret_cast<Test*>(nativeTest);
   CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0);
-  return native->Method(env, jcaller, arg1);
+  return native->Method(env, JavaParamRef<jobject>(env, jcaller), arg1);
 }
 
-static jint Init(JNIEnv* env, jobject jcaller);
+static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller);
 
 extern "C" __attribute__((visibility("default")))
 jint
     Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit(JNIEnv*
     env, jobject jcaller) {
-  return Init(env, jcaller);
+  return Init(env, JavaParamRef<jobject>(env, jcaller));
 }
 
-static jint Init(JNIEnv* env, jobject jcaller);
+static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller);
 
 extern "C" __attribute__((visibility("default")))
 jint
     Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit(JNIEnv*
     env, jobject jcaller) {
-  return Init(env, jcaller);
+  return Init(env, JavaParamRef<jobject>(env, jcaller));
 }
 
 static base::subtle::AtomicWord g_SampleForTests_testMethodWithParam = 0;
diff --git a/android/jni_generator/testNativeExportsOptionalOption.golden b/android/jni_generator/testNativeExportsOptionalOption.golden
index 6e2b655..e2cf051 100644
--- a/android/jni_generator/testNativeExportsOptionalOption.golden
+++ b/android/jni_generator/testNativeExportsOptionalOption.golden
@@ -36,7 +36,7 @@
     jint arg1) {
   Test* native = reinterpret_cast<Test*>(nativeTest);
   CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0);
-  return native->StaticMethod(env, jcaller, arg1);
+  return native->StaticMethod(env, JavaParamRef<jobject>(env, jcaller), arg1);
 }
 
 extern "C" __attribute__((visibility("default")))
@@ -48,25 +48,25 @@
     jint arg1) {
   Test* native = reinterpret_cast<Test*>(nativeTest);
   CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0);
-  return native->Method(env, jcaller, arg1);
+  return native->Method(env, JavaParamRef<jobject>(env, jcaller), arg1);
 }
 
-static jint Init(JNIEnv* env, jobject jcaller);
+static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller);
 
 extern "C" __attribute__((visibility("default")))
 jint
     Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit(JNIEnv*
     env, jobject jcaller) {
-  return Init(env, jcaller);
+  return Init(env, JavaParamRef<jobject>(env, jcaller));
 }
 
-static jint Init(JNIEnv* env, jobject jcaller);
+static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller);
 
 extern "C" __attribute__((visibility("default")))
 jint
     Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit(JNIEnv*
     env, jobject jcaller) {
-  return Init(env, jcaller);
+  return Init(env, JavaParamRef<jobject>(env, jcaller));
 }
 
 static base::subtle::AtomicWord g_SampleForTests_testMethodWithParam = 0;
diff --git a/android/jni_generator/testNatives.golden b/android/jni_generator/testNatives.golden
index b19c337..f9538a3 100644
--- a/android/jni_generator/testNatives.golden
+++ b/android/jni_generator/testNatives.golden
@@ -27,10 +27,10 @@
 
 // Step 2: method stubs.
 
-static jint Init(JNIEnv* env, jobject jcaller);
+static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller);
 
 static jint Java_org_chromium_TestJni_nativeInit(JNIEnv* env, jobject jcaller) {
-  return Init(env, jcaller);
+  return Init(env, JavaParamRef<jobject>(env, jcaller));
 }
 
 static void Java_org_chromium_TestJni_nativeDestroy(JNIEnv* env,
@@ -39,7 +39,7 @@
   ChromeBrowserProvider* native =
       reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider);
   CHECK_NATIVE_PTR(env, jcaller, native, "Destroy");
-  return native->Destroy(env, jcaller);
+  return native->Destroy(env, JavaParamRef<jobject>(env, jcaller));
 }
 
 static jlong Java_org_chromium_TestJni_nativeAddBookmark(JNIEnv* env,
@@ -52,58 +52,67 @@
   ChromeBrowserProvider* native =
       reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider);
   CHECK_NATIVE_PTR(env, jcaller, native, "AddBookmark", 0);
-  return native->AddBookmark(env, jcaller, url, title, isFolder, parentId);
+  return native->AddBookmark(env, JavaParamRef<jobject>(env, jcaller),
+      JavaParamRef<jstring>(env, url), JavaParamRef<jstring>(env, title),
+      isFolder, parentId);
 }
 
-static ScopedJavaLocalRef<jstring> GetDomainAndRegistry(JNIEnv* env, jclass
-    jcaller,
-    jstring url);
+static ScopedJavaLocalRef<jstring> GetDomainAndRegistry(JNIEnv* env, const
+    JavaParamRef<jclass>& jcaller,
+    const JavaParamRef<jstring>& url);
 
 static jstring Java_org_chromium_TestJni_nativeGetDomainAndRegistry(JNIEnv* env,
     jclass jcaller,
     jstring url) {
-  return GetDomainAndRegistry(env, jcaller, url).Release();
+  return GetDomainAndRegistry(env, JavaParamRef<jclass>(env, jcaller),
+      JavaParamRef<jstring>(env, url)).Release();
 }
 
-static void CreateHistoricalTabFromState(JNIEnv* env, jclass jcaller,
-    jbyteArray state,
+static void CreateHistoricalTabFromState(JNIEnv* env, const
+    JavaParamRef<jclass>& jcaller,
+    const JavaParamRef<jbyteArray>& state,
     jint tab_index);
 
 static void Java_org_chromium_TestJni_nativeCreateHistoricalTabFromState(JNIEnv*
     env, jclass jcaller,
     jbyteArray state,
     jint tab_index) {
-  return CreateHistoricalTabFromState(env, jcaller, state, tab_index);
+  return CreateHistoricalTabFromState(env, JavaParamRef<jclass>(env, jcaller),
+      JavaParamRef<jbyteArray>(env, state), tab_index);
 }
 
-static ScopedJavaLocalRef<jbyteArray> GetStateAsByteArray(JNIEnv* env, jobject
-    jcaller,
-    jobject view);
+static ScopedJavaLocalRef<jbyteArray> GetStateAsByteArray(JNIEnv* env, const
+    JavaParamRef<jobject>& jcaller,
+    const JavaParamRef<jobject>& view);
 
 static jbyteArray Java_org_chromium_TestJni_nativeGetStateAsByteArray(JNIEnv*
     env, jobject jcaller,
     jobject view) {
-  return GetStateAsByteArray(env, jcaller, view).Release();
+  return GetStateAsByteArray(env, JavaParamRef<jobject>(env, jcaller),
+      JavaParamRef<jobject>(env, view)).Release();
 }
 
 static ScopedJavaLocalRef<jobjectArray> GetAutofillProfileGUIDs(JNIEnv* env,
-    jclass jcaller);
+    const JavaParamRef<jclass>& jcaller);
 
 static jobjectArray
     Java_org_chromium_TestJni_nativeGetAutofillProfileGUIDs(JNIEnv* env, jclass
     jcaller) {
-  return GetAutofillProfileGUIDs(env, jcaller).Release();
+  return GetAutofillProfileGUIDs(env, JavaParamRef<jclass>(env,
+      jcaller)).Release();
 }
 
-static void SetRecognitionResults(JNIEnv* env, jobject jcaller,
+static void SetRecognitionResults(JNIEnv* env, const JavaParamRef<jobject>&
+    jcaller,
     jint sessionId,
-    jobjectArray results);
+    const JavaParamRef<jobjectArray>& results);
 
 static void Java_org_chromium_TestJni_nativeSetRecognitionResults(JNIEnv* env,
     jobject jcaller,
     jint sessionId,
     jobjectArray results) {
-  return SetRecognitionResults(env, jcaller, sessionId, results);
+  return SetRecognitionResults(env, JavaParamRef<jobject>(env, jcaller),
+      sessionId, JavaParamRef<jobjectArray>(env, results));
 }
 
 static jlong Java_org_chromium_TestJni_nativeAddBookmarkFromAPI(JNIEnv* env,
@@ -119,24 +128,29 @@
   ChromeBrowserProvider* native =
       reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider);
   CHECK_NATIVE_PTR(env, jcaller, native, "AddBookmarkFromAPI", 0);
-  return native->AddBookmarkFromAPI(env, jcaller, url, created, isBookmark,
-      date, favicon, title, visits);
+  return native->AddBookmarkFromAPI(env, JavaParamRef<jobject>(env, jcaller),
+      JavaParamRef<jstring>(env, url), JavaParamRef<jobject>(env, created),
+      JavaParamRef<jobject>(env, isBookmark), JavaParamRef<jobject>(env, date),
+      JavaParamRef<jbyteArray>(env, favicon), JavaParamRef<jstring>(env, title),
+      JavaParamRef<jobject>(env, visits));
 }
 
-static jint FindAll(JNIEnv* env, jobject jcaller,
-    jstring find);
+static jint FindAll(JNIEnv* env, const JavaParamRef<jobject>& jcaller,
+    const JavaParamRef<jstring>& find);
 
 static jint Java_org_chromium_TestJni_nativeFindAll(JNIEnv* env, jobject
     jcaller,
     jstring find) {
-  return FindAll(env, jcaller, find);
+  return FindAll(env, JavaParamRef<jobject>(env, jcaller),
+      JavaParamRef<jstring>(env, find));
 }
 
-static ScopedJavaLocalRef<jobject> GetInnerClass(JNIEnv* env, jclass jcaller);
+static ScopedJavaLocalRef<jobject> GetInnerClass(JNIEnv* env, const
+    JavaParamRef<jclass>& jcaller);
 
 static jobject Java_org_chromium_TestJni_nativeGetInnerClass(JNIEnv* env, jclass
     jcaller) {
-  return GetInnerClass(env, jcaller).Release();
+  return GetInnerClass(env, JavaParamRef<jclass>(env, jcaller)).Release();
 }
 
 static jobject Java_org_chromium_TestJni_nativeQueryBitmap(JNIEnv* env,
@@ -149,8 +163,10 @@
   ChromeBrowserProvider* native =
       reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider);
   CHECK_NATIVE_PTR(env, jcaller, native, "QueryBitmap", NULL);
-  return native->QueryBitmap(env, jcaller, projection, selection, selectionArgs,
-      sortOrder).Release();
+  return native->QueryBitmap(env, JavaParamRef<jobject>(env, jcaller),
+      JavaParamRef<jobjectArray>(env, projection), JavaParamRef<jstring>(env,
+      selection), JavaParamRef<jobjectArray>(env, selectionArgs),
+      JavaParamRef<jstring>(env, sortOrder)).Release();
 }
 
 static void Java_org_chromium_TestJni_nativeGotOrientation(JNIEnv* env,
@@ -162,17 +178,19 @@
   DataFetcherImplAndroid* native =
       reinterpret_cast<DataFetcherImplAndroid*>(nativeDataFetcherImplAndroid);
   CHECK_NATIVE_PTR(env, jcaller, native, "GotOrientation");
-  return native->GotOrientation(env, jcaller, alpha, beta, gamma);
+  return native->GotOrientation(env, JavaParamRef<jobject>(env, jcaller), alpha,
+      beta, gamma);
 }
 
-static ScopedJavaLocalRef<jthrowable> MessWithJavaException(JNIEnv* env, jclass
-    jcaller,
-    jthrowable e);
+static ScopedJavaLocalRef<jthrowable> MessWithJavaException(JNIEnv* env, const
+    JavaParamRef<jclass>& jcaller,
+    const JavaParamRef<jthrowable>& e);
 
 static jthrowable Java_org_chromium_TestJni_nativeMessWithJavaException(JNIEnv*
     env, jclass jcaller,
     jthrowable e) {
-  return MessWithJavaException(env, jcaller, e).Release();
+  return MessWithJavaException(env, JavaParamRef<jclass>(env, jcaller),
+      JavaParamRef<jthrowable>(env, e)).Release();
 }
 
 // Step 3: RegisterNatives.
diff --git a/android/jni_generator/testNativesLong.golden b/android/jni_generator/testNativesLong.golden
index 1235616..d5b67ba 100644
--- a/android/jni_generator/testNativesLong.golden
+++ b/android/jni_generator/testNativesLong.golden
@@ -32,7 +32,7 @@
   ChromeBrowserProvider* native =
       reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider);
   CHECK_NATIVE_PTR(env, jcaller, native, "Destroy");
-  return native->Destroy(env, jcaller);
+  return native->Destroy(env, JavaParamRef<jobject>(env, jcaller));
 }
 
 // Step 3: RegisterNatives.
diff --git a/android/jni_generator/testSingleJNIAdditionalImport.golden b/android/jni_generator/testSingleJNIAdditionalImport.golden
index 3536139..1b2895e 100644
--- a/android/jni_generator/testSingleJNIAdditionalImport.golden
+++ b/android/jni_generator/testSingleJNIAdditionalImport.golden
@@ -27,13 +27,14 @@
 
 // Step 2: method stubs.
 
-static void DoSomething(JNIEnv* env, jclass jcaller,
-    jobject callback);
+static void DoSomething(JNIEnv* env, const JavaParamRef<jclass>& jcaller,
+    const JavaParamRef<jobject>& callback);
 
 static void Java_org_chromium_foo_Foo_nativeDoSomething(JNIEnv* env, jclass
     jcaller,
     jobject callback) {
-  return DoSomething(env, jcaller, callback);
+  return DoSomething(env, JavaParamRef<jclass>(env, jcaller),
+      JavaParamRef<jobject>(env, callback));
 }
 
 static base::subtle::AtomicWord g_Foo_calledByNative = 0;
diff --git a/android/junit/src/org/chromium/base/LogTest.java b/android/junit/src/org/chromium/base/LogTest.java
index e5ce239..6f4e406 100644
--- a/android/junit/src/org/chromium/base/LogTest.java
+++ b/android/junit/src/org/chromium/base/LogTest.java
@@ -9,19 +9,16 @@
 import static org.junit.Assert.assertTrue;
 
 import org.chromium.testing.local.LocalRobolectricTestRunner;
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
 import org.robolectric.shadows.ShadowLog;
 
 import java.util.List;
 
 /** Unit tests for {@link Log}. */
 @RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = {LogTest.PermissiveShadowLog.class})
+@Config(manifest = Config.NONE)
 public class LogTest {
     /** Tests that the computed call origin is the correct one. */
     @Test
@@ -74,139 +71,4 @@
         assertEquals(t2, logs.get(logs.size() - 1).throwable);
         assertEquals("Bar MyThrowable MyOtherThrowable", logs.get(logs.size() - 1).msg);
     }
-
-    public void verboseLoggingTest() {
-        PermissiveShadowLog.setLevel(Log.VERBOSE);
-        List<ShadowLog.LogItem> logs = ShadowLog.getLogs();
-
-        Log.wtf("Foo", "Bar");
-        Log.e("Foo", "Bar");
-        Log.w("Foo", "Bar");
-        Log.i("Foo", "Bar");
-        Log.d("Foo", "Bar");
-        Log.v("Foo", "Bar");
-
-        assertEquals(Log.ASSERT, logs.get(0).type);
-        assertEquals(Log.ERROR, logs.get(1).type);
-        assertEquals(Log.WARN, logs.get(2).type);
-        assertEquals(Log.INFO, logs.get(3).type);
-        assertEquals(Log.DEBUG, logs.get(4).type);
-        assertEquals(Log.VERBOSE, logs.get(5).type);
-        assertEquals(6, logs.size());
-    }
-
-    @Test
-    public void debugLoggingTest() {
-        PermissiveShadowLog.setLevel(Log.DEBUG);
-        List<ShadowLog.LogItem> logs = ShadowLog.getLogs();
-
-        Log.wtf("Foo", "Bar");
-        Log.e("Foo", "Bar");
-        Log.w("Foo", "Bar");
-        Log.i("Foo", "Bar");
-        Log.d("Foo", "Bar");
-        Log.v("Foo", "Bar");
-
-        assertEquals(Log.ASSERT, logs.get(0).type);
-        assertEquals(Log.ERROR, logs.get(1).type);
-        assertEquals(Log.WARN, logs.get(2).type);
-        assertEquals(Log.INFO, logs.get(3).type);
-        assertEquals(Log.DEBUG, logs.get(4).type);
-        assertEquals(5, logs.size());
-    }
-
-    @Test
-    public void infoLoggingTest() {
-        PermissiveShadowLog.setLevel(Log.INFO);
-        List<ShadowLog.LogItem> logs = ShadowLog.getLogs();
-
-        Log.wtf("Foo", "Bar");
-        Log.e("Foo", "Bar");
-        Log.w("Foo", "Bar");
-        Log.i("Foo", "Bar");
-        Log.d("Foo", "Bar");
-        Log.v("Foo", "Bar");
-
-        assertEquals(Log.ASSERT, logs.get(0).type);
-        assertEquals(Log.ERROR, logs.get(1).type);
-        assertEquals(Log.WARN, logs.get(2).type);
-        assertEquals(Log.INFO, logs.get(3).type);
-        assertEquals(4, logs.size());
-    }
-
-    @Test
-    public void warnLoggingTest() {
-        PermissiveShadowLog.setLevel(Log.WARN);
-        List<ShadowLog.LogItem> logs = ShadowLog.getLogs();
-
-        Log.wtf("Foo", "Bar");
-        Log.e("Foo", "Bar");
-        Log.w("Foo", "Bar");
-        Log.i("Foo", "Bar");
-        Log.d("Foo", "Bar");
-        Log.v("Foo", "Bar");
-
-        assertEquals(Log.ASSERT, logs.get(0).type);
-        assertEquals(Log.ERROR, logs.get(1).type);
-        assertEquals(Log.WARN, logs.get(2).type);
-        assertEquals(3, logs.size());
-    }
-
-    @Test
-    public void errorLoggingTest() {
-        PermissiveShadowLog.setLevel(Log.ERROR);
-        List<ShadowLog.LogItem> logs = ShadowLog.getLogs();
-
-        Log.wtf("Foo", "Bar");
-        Log.e("Foo", "Bar");
-        Log.w("Foo", "Bar");
-        Log.i("Foo", "Bar");
-        Log.d("Foo", "Bar");
-        Log.v("Foo", "Bar");
-
-        assertEquals(Log.ASSERT, logs.get(0).type);
-        assertEquals(Log.ERROR, logs.get(1).type);
-        assertEquals(2, logs.size());
-    }
-
-    @Test
-    public void assertLoggingTest() {
-        PermissiveShadowLog.setLevel(Log.ASSERT);
-        List<ShadowLog.LogItem> logs = ShadowLog.getLogs();
-
-        Log.wtf("Foo", "Bar");
-        Log.e("Foo", "Bar");
-        Log.w("Foo", "Bar");
-        Log.i("Foo", "Bar");
-        Log.d("Foo", "Bar");
-        Log.v("Foo", "Bar");
-
-        assertEquals(Log.ASSERT, logs.get(0).type);
-        assertEquals(1, logs.size());
-    }
-
-    @Before
-    public void beforeTest() {
-        PermissiveShadowLog.reset();
-    }
-
-    /** Needed to allow debug/verbose logging that is disabled by default. */
-    @Implements(android.util.Log.class)
-    public static class PermissiveShadowLog extends ShadowLog {
-        private static int sLevel = Log.VERBOSE;
-
-        /** Sets the log level for all tags. */
-        public static void setLevel(int level) {
-            sLevel = level;
-        }
-
-        @Implementation
-        public static boolean isLoggable(String tag, int level) {
-            return level >= sLevel;
-        }
-
-        public static void reset() {
-            sLevel = Log.VERBOSE;
-        }
-    }
 }
diff --git a/android/library_loader/library_loader_hooks.cc b/android/library_loader/library_loader_hooks.cc
index 1f745ec..4a61012 100644
--- a/android/library_loader/library_loader_hooks.cc
+++ b/android/library_loader/library_loader_hooks.cc
@@ -57,7 +57,7 @@
 
 static void RegisterChromiumAndroidLinkerRendererHistogram(
     JNIEnv* env,
-    jobject jcaller,
+    const JavaParamRef<jobject>& jcaller,
     jboolean requested_shared_relro,
     jboolean load_at_fixed_address_failed,
     jlong library_load_time_ms) {
@@ -89,7 +89,7 @@
 
 static void RecordChromiumAndroidLinkerBrowserHistogram(
     JNIEnv* env,
-    jobject jcaller,
+    const JavaParamRef<jobject>& jcaller,
     jboolean is_using_browser_shared_relros,
     jboolean load_at_fixed_address_failed,
     jint library_load_from_apk_status,
@@ -121,13 +121,15 @@
   g_registration_callback = func;
 }
 
-static void InitCommandLine(JNIEnv* env,
-                            jobject jcaller,
-                            jobjectArray init_command_line) {
+static void InitCommandLine(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller,
+    const JavaParamRef<jobjectArray>& init_command_line) {
   InitNativeCommandLineFromJavaArray(env, init_command_line);
 }
 
-static jboolean LibraryLoaded(JNIEnv* env, jobject jcaller) {
+static jboolean LibraryLoaded(JNIEnv* env,
+                              const JavaParamRef<jobject>& jcaller) {
   if (g_registration_callback == NULL) {
     return true;
   }
@@ -141,7 +143,9 @@
   }
 }
 
-static jboolean ForkAndPrefetchNativeLibrary(JNIEnv* env, jclass clazz) {
+static jboolean ForkAndPrefetchNativeLibrary(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz) {
   return NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary();
 }
 
@@ -153,7 +157,9 @@
   g_library_version_number = strdup(version_number);
 }
 
-ScopedJavaLocalRef<jstring> GetVersionNumber(JNIEnv* env, jobject jcaller) {
+ScopedJavaLocalRef<jstring> GetVersionNumber(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& jcaller) {
   return ConvertUTF8ToJavaString(env, g_library_version_number);
 }
 
diff --git a/android/library_loader/library_prefetcher.cc b/android/library_loader/library_prefetcher.cc
index 9b54843..4c47bed 100644
--- a/android/library_loader/library_prefetcher.cc
+++ b/android/library_loader/library_prefetcher.cc
@@ -118,6 +118,12 @@
 
 // static
 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() {
+// Avoid forking with cygprofile instrumentation because the latter performs
+// memory allocations.
+#if defined(CYGPROFILE_INSTRUMENTATION)
+  return false;
+#endif
+
   // Looking for ranges is done before the fork, to avoid syscalls and/or memory
   // allocations in the forked process. The child process inherits the lock
   // state of its parent thread. It cannot rely on being able to acquire any
@@ -126,6 +132,7 @@
   std::vector<AddressRange> ranges;
   if (!FindRanges(&ranges))
     return false;
+
   pid_t pid = fork();
   if (pid == 0) {
     setpriority(PRIO_PROCESS, 0, kBackgroundPriority);
diff --git a/android/memory_pressure_listener_android.cc b/android/memory_pressure_listener_android.cc
index 80c07bc..9d3dd46 100644
--- a/android/memory_pressure_listener_android.cc
+++ b/android/memory_pressure_listener_android.cc
@@ -8,8 +8,9 @@
 #include "jni/MemoryPressureListener_jni.h"
 
 // Defined and called by JNI.
-static void OnMemoryPressure(
-    JNIEnv* env, jclass clazz, jint memory_pressure_level) {
+static void OnMemoryPressure(JNIEnv* env,
+                             const JavaParamRef<jclass>& clazz,
+                             jint memory_pressure_level) {
   base::MemoryPressureListener::NotifyMemoryPressure(
       static_cast<base::MemoryPressureListener::MemoryPressureLevel>(
           memory_pressure_level));
diff --git a/android/path_service_android.cc b/android/path_service_android.cc
index 18ca70c..9972bbb 100644
--- a/android/path_service_android.cc
+++ b/android/path_service_android.cc
@@ -13,7 +13,10 @@
 namespace base {
 namespace android {
 
-void Override(JNIEnv* env, jclass clazz, jint what, jstring path) {
+void Override(JNIEnv* env,
+              const JavaParamRef<jclass>& clazz,
+              jint what,
+              const JavaParamRef<jstring>& path) {
   FilePath file_path(ConvertJavaStringToUTF8(env, path));
   PathService::Override(what, file_path);
 }
diff --git a/android/record_histogram.cc b/android/record_histogram.cc
index 0688e4f..61bbf88 100644
--- a/android/record_histogram.cc
+++ b/android/record_histogram.cc
@@ -174,8 +174,8 @@
 }  // namespace
 
 void RecordBooleanHistogram(JNIEnv* env,
-                            jclass clazz,
-                            jstring j_histogram_name,
+                            const JavaParamRef<jclass>& clazz,
+                            const JavaParamRef<jstring>& j_histogram_name,
                             jint j_histogram_key,
                             jboolean j_sample) {
   bool sample = static_cast<bool>(j_sample);
@@ -185,8 +185,8 @@
 }
 
 void RecordEnumeratedHistogram(JNIEnv* env,
-                               jclass clazz,
-                               jstring j_histogram_name,
+                               const JavaParamRef<jclass>& clazz,
+                               const JavaParamRef<jstring>& j_histogram_name,
                                jint j_histogram_key,
                                jint j_sample,
                                jint j_boundary) {
@@ -198,8 +198,8 @@
 }
 
 void RecordCustomCountHistogram(JNIEnv* env,
-                                jclass clazz,
-                                jstring j_histogram_name,
+                                const JavaParamRef<jclass>& clazz,
+                                const JavaParamRef<jstring>& j_histogram_name,
                                 jint j_histogram_key,
                                 jint j_sample,
                                 jint j_min,
@@ -214,8 +214,8 @@
 }
 
 void RecordLinearCountHistogram(JNIEnv* env,
-                                jclass clazz,
-                                jstring j_histogram_name,
+                                const JavaParamRef<jclass>& clazz,
+                                const JavaParamRef<jstring>& j_histogram_name,
                                 jint j_histogram_key,
                                 jint j_sample,
                                 jint j_min,
@@ -230,31 +230,32 @@
 }
 
 void RecordSparseHistogram(JNIEnv* env,
-                                 jclass clazz,
-                                 jstring j_histogram_name,
-                                 jint j_histogram_key,
-                                 jint j_sample) {
-    int sample = static_cast<int>(j_sample);
-    g_histograms.Get()
-        .SparseHistogram(env, j_histogram_name, j_histogram_key)
-        ->Add(sample);
+                           const JavaParamRef<jclass>& clazz,
+                           const JavaParamRef<jstring>& j_histogram_name,
+                           jint j_histogram_key,
+                           jint j_sample) {
+  int sample = static_cast<int>(j_sample);
+  g_histograms.Get()
+      .SparseHistogram(env, j_histogram_name, j_histogram_key)
+      ->Add(sample);
 }
 
-void RecordCustomTimesHistogramMilliseconds(JNIEnv* env,
-                                            jclass clazz,
-                                            jstring j_histogram_name,
-                                            jint j_histogram_key,
-                                            jlong j_duration,
-                                            jlong j_min,
-                                            jlong j_max,
-                                            jint j_num_buckets) {
+void RecordCustomTimesHistogramMilliseconds(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& j_histogram_name,
+    jint j_histogram_key,
+    jlong j_duration,
+    jlong j_min,
+    jlong j_max,
+    jint j_num_buckets) {
   g_histograms.Get()
       .CustomTimesHistogram(env, j_histogram_name, j_histogram_key, j_min,
                             j_max, j_num_buckets)
       ->AddTime(TimeDelta::FromMilliseconds(static_cast<int64>(j_duration)));
 }
 
-void Initialize(JNIEnv* env, jclass) {
+void Initialize(JNIEnv* env, const JavaParamRef<jclass>&) {
   StatisticsRecorder::Initialize();
 }
 
@@ -262,10 +263,11 @@
 // MetricsUtils.HistogramDelta. It should live in a test-specific file, but we
 // currently can't have test-specific native code packaged in test-specific Java
 // targets - see http://crbug.com/415945.
-jint GetHistogramValueCountForTesting(JNIEnv* env,
-                                      jclass clazz,
-                                      jstring histogram_name,
-                                      jint sample) {
+jint GetHistogramValueCountForTesting(
+    JNIEnv* env,
+    const JavaParamRef<jclass>& clazz,
+    const JavaParamRef<jstring>& histogram_name,
+    jint sample) {
   HistogramBase* histogram = StatisticsRecorder::FindHistogram(
       android::ConvertJavaStringToUTF8(env, histogram_name));
   if (histogram == nullptr) {
diff --git a/android/record_user_action.cc b/android/record_user_action.cc
index 6172f2e..1452341 100644
--- a/android/record_user_action.cc
+++ b/android/record_user_action.cc
@@ -11,7 +11,9 @@
 namespace base {
 namespace android {
 
-static void RecordUserAction(JNIEnv* env, jclass clazz, jstring j_action) {
+static void RecordUserAction(JNIEnv* env,
+                             const JavaParamRef<jclass>& clazz,
+                             const JavaParamRef<jstring>& j_action) {
   RecordComputedAction(ConvertJavaStringToUTF8(env, j_action));
 }
 
diff --git a/android/scoped_java_ref.h b/android/scoped_java_ref.h
index 37e7fd3..6274969 100644
--- a/android/scoped_java_ref.h
+++ b/android/scoped_java_ref.h
@@ -94,6 +94,27 @@
   DISALLOW_COPY_AND_ASSIGN(JavaRef);
 };
 
+// Holds a local reference to a JNI method parameter.
+// Method parameters should not be deleted, and so this class exists purely to
+// wrap them as a JavaRef<T> in the JNI binding generator. Do not create
+// instances manually.
+template <typename T>
+class JavaParamRef : public JavaRef<T> {
+ public:
+  // Assumes that |obj| is a parameter passed to a JNI method from Java.
+  // Does not assume ownership as parameters should not be deleted.
+  JavaParamRef(JNIEnv* env, T obj) : JavaRef<T>(env, obj) {}
+
+  ~JavaParamRef() {}
+
+  // TODO(torne): remove this cast once we're using JavaRef consistently.
+  // http://crbug.com/506850
+  operator T() const { return JavaRef<T>::obj(); }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(JavaParamRef);
+};
+
 // Holds a local reference to a Java object. The local reference is scoped
 // to the lifetime of this object.
 // Instances of this class may hold onto any JNIEnv passed into it until
@@ -207,7 +228,12 @@
     this->Reset(NULL, other.obj());
   }
 
-  template<typename U>
+  template <typename U>
+  void Reset(JNIEnv* env, const JavaParamRef<U>& other) {
+    this->Reset(env, other.obj());
+  }
+
+  template <typename U>
   void Reset(JNIEnv* env, U obj) {
     implicit_cast<T>(obj);  // Ensure U is assignable to T
     this->SetNewGlobalRef(env, obj);
diff --git a/android/trace_event_binding.cc b/android/trace_event_binding.cc
index 3c5ee17..f761a64 100644
--- a/android/trace_event_binding.cc
+++ b/android/trace_event_binding.cc
@@ -69,23 +69,26 @@
 
 }  // namespace
 
-static void RegisterEnabledObserver(JNIEnv* env, jclass clazz) {
+static void RegisterEnabledObserver(JNIEnv* env,
+                                    const JavaParamRef<jclass>& clazz) {
   bool enabled = trace_event::TraceLog::GetInstance()->IsEnabled();
   base::android::Java_TraceEvent_setEnabled(env, enabled);
   trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(
       g_trace_enabled_state_observer_.Pointer());
 }
 
-static void StartATrace(JNIEnv* env, jclass clazz) {
+static void StartATrace(JNIEnv* env, const JavaParamRef<jclass>& clazz) {
   base::trace_event::TraceLog::GetInstance()->StartATrace();
 }
 
-static void StopATrace(JNIEnv* env, jclass clazz) {
+static void StopATrace(JNIEnv* env, const JavaParamRef<jclass>& clazz) {
   base::trace_event::TraceLog::GetInstance()->StopATrace();
 }
 
-static void Instant(JNIEnv* env, jclass clazz,
-                    jstring jname, jstring jarg) {
+static void Instant(JNIEnv* env,
+                    const JavaParamRef<jclass>& clazz,
+                    const JavaParamRef<jstring>& jname,
+                    const JavaParamRef<jstring>& jarg) {
   TraceEventDataConverter converter(env, jname, jarg);
   if (converter.arg()) {
     TRACE_EVENT_COPY_INSTANT1(kJavaCategory, converter.name(),
@@ -97,8 +100,10 @@
   }
 }
 
-static void Begin(JNIEnv* env, jclass clazz,
-                  jstring jname, jstring jarg) {
+static void Begin(JNIEnv* env,
+                  const JavaParamRef<jclass>& clazz,
+                  const JavaParamRef<jstring>& jname,
+                  const JavaParamRef<jstring>& jarg) {
   TraceEventDataConverter converter(env, jname, jarg);
   if (converter.arg()) {
     TRACE_EVENT_COPY_BEGIN1(kJavaCategory, converter.name(),
@@ -108,8 +113,10 @@
   }
 }
 
-static void End(JNIEnv* env, jclass clazz,
-                jstring jname, jstring jarg) {
+static void End(JNIEnv* env,
+                const JavaParamRef<jclass>& clazz,
+                const JavaParamRef<jstring>& jname,
+                const JavaParamRef<jstring>& jarg) {
   TraceEventDataConverter converter(env, jname, jarg);
   if (converter.arg()) {
     TRACE_EVENT_COPY_END1(kJavaCategory, converter.name(),
@@ -119,20 +126,26 @@
   }
 }
 
-static void BeginToplevel(JNIEnv* env, jclass clazz) {
+static void BeginToplevel(JNIEnv* env, const JavaParamRef<jclass>& clazz) {
   TRACE_EVENT_BEGIN0(kToplevelCategory, kLooperDispatchMessage);
 }
 
-static void EndToplevel(JNIEnv* env, jclass clazz) {
+static void EndToplevel(JNIEnv* env, const JavaParamRef<jclass>& clazz) {
   TRACE_EVENT_END0(kToplevelCategory, kLooperDispatchMessage);
 }
 
-static void StartAsync(JNIEnv* env, jclass clazz, jstring jname, jlong jid) {
+static void StartAsync(JNIEnv* env,
+                       const JavaParamRef<jclass>& clazz,
+                       const JavaParamRef<jstring>& jname,
+                       jlong jid) {
   TraceEventDataConverter converter(env, jname, nullptr);
   TRACE_EVENT_COPY_ASYNC_BEGIN0(kJavaCategory, converter.name(), jid);
 }
 
-static void FinishAsync(JNIEnv* env, jclass clazz, jstring jname, jlong jid) {
+static void FinishAsync(JNIEnv* env,
+                        const JavaParamRef<jclass>& clazz,
+                        const JavaParamRef<jstring>& jname,
+                        jlong jid) {
   TraceEventDataConverter converter(env, jname, nullptr);
   TRACE_EVENT_COPY_ASYNC_END0(kJavaCategory, converter.name(), jid);
 }
diff --git a/base_unittests.isolate b/base_unittests.isolate
index c4c0fb2..208501f 100644
--- a/base_unittests.isolate
+++ b/base_unittests.isolate
@@ -25,7 +25,6 @@
       'variables': {
         'files': [
           '../testing/test_env.py',
-          '<(PRODUCT_DIR)/base_unittests<(EXECUTABLE_SUFFIX)',
         ],
       },
     }],
diff --git a/feature_list.cc b/feature_list.cc
new file mode 100644
index 0000000..16eba67
--- /dev/null
+++ b/feature_list.cc
@@ -0,0 +1,113 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/feature_list.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/string_split.h"
+
+namespace base {
+
+namespace {
+
+// Pointer to the FeatureList instance singleton that was set via
+// FeatureList::SetInstance(). Does not use base/memory/singleton.h in order to
+// have more control over initialization timing. Leaky.
+FeatureList* g_instance = nullptr;
+
+// Splits a comma-separated string containing feature names into a vector.
+std::vector<std::string> SplitFeatureListString(const std::string& input) {
+  return SplitString(input, ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+}
+
+}  // namespace
+
+FeatureList::FeatureList() : initialized_(false) {}
+
+FeatureList::~FeatureList() {}
+
+void FeatureList::InitializeFromCommandLine(
+    const std::string& enable_features,
+    const std::string& disable_features) {
+  DCHECK(!initialized_);
+
+  // Process disabled features first, so that disabled ones take precedence over
+  // enabled ones (since RegisterOverride() uses insert()).
+  for (const auto& feature_name : SplitFeatureListString(disable_features)) {
+    RegisterOverride(feature_name, OVERRIDE_DISABLE_FEATURE);
+  }
+  for (const auto& feature_name : SplitFeatureListString(enable_features)) {
+    RegisterOverride(feature_name, OVERRIDE_ENABLE_FEATURE);
+  }
+}
+
+// static
+bool FeatureList::IsEnabled(const Feature& feature) {
+  return GetInstance()->IsFeatureEnabled(feature);
+}
+
+// static
+FeatureList* FeatureList::GetInstance() {
+  return g_instance;
+}
+
+// static
+void FeatureList::SetInstance(scoped_ptr<FeatureList> instance) {
+  DCHECK(!g_instance);
+  instance->FinalizeInitialization();
+
+  // Note: Intentional leak of global singleton.
+  g_instance = instance.release();
+}
+
+// static
+void FeatureList::ClearInstanceForTesting() {
+  delete g_instance;
+  g_instance = nullptr;
+}
+
+void FeatureList::FinalizeInitialization() {
+  DCHECK(!initialized_);
+  initialized_ = true;
+}
+
+bool FeatureList::IsFeatureEnabled(const Feature& feature) {
+  DCHECK(initialized_);
+  DCHECK(CheckFeatureIdentity(feature)) << feature.name;
+
+  auto it = overrides_.find(feature.name);
+  if (it != overrides_.end()) {
+    const OverrideEntry& entry = it->second;
+    // TODO(asvitkine) Expand this section as more support is added.
+    return entry.overridden_state == OVERRIDE_ENABLE_FEATURE;
+  }
+  // Otherwise, return the default state.
+  return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
+}
+
+void FeatureList::RegisterOverride(const std::string& feature_name,
+                                   OverrideState overridden_state) {
+  DCHECK(!initialized_);
+  overrides_.insert(make_pair(feature_name, OverrideEntry(overridden_state)));
+}
+
+bool FeatureList::CheckFeatureIdentity(const Feature& feature) {
+  AutoLock auto_lock(feature_identity_tracker_lock_);
+
+  auto it = feature_identity_tracker_.find(feature.name);
+  if (it == feature_identity_tracker_.end()) {
+    // If it's not tracked yet, register it.
+    feature_identity_tracker_[feature.name] = &feature;
+    return true;
+  }
+  // Compare address of |feature| to the existing tracked entry.
+  return it->second == &feature;
+}
+
+FeatureList::OverrideEntry::OverrideEntry(OverrideState overridden_state)
+    : overridden_state(overridden_state) {}
+
+}  // namespace base
diff --git a/feature_list.h b/feature_list.h
new file mode 100644
index 0000000..6e4ad58
--- /dev/null
+++ b/feature_list.h
@@ -0,0 +1,162 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FEATURE_LIST_H_
+#define BASE_FEATURE_LIST_H_
+
+#include <map>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+
+namespace base {
+
+// Specifies whether a given feature is enabled or disabled by default.
+enum FeatureState {
+  FEATURE_DISABLED_BY_DEFAULT,
+  FEATURE_ENABLED_BY_DEFAULT,
+};
+
+// The Feature struct is used to define the default state for a feature. See
+// comment below for more details. There must only ever be one struct instance
+// for a given feature name - generally defined as a constant global variable or
+// file static.
+struct BASE_EXPORT Feature {
+  // The name of the feature. This should be unique to each feature and is used
+  // for enabling/disabling features via command line flags and experiments.
+  const char* const name;
+
+  // The default state (i.e. enabled or disabled) for this feature.
+  const FeatureState default_state;
+};
+
+// The FeatureList class is used to determine whether a given feature is on or
+// off. It provides an authoritative answer, taking into account command-line
+// overrides and experimental control.
+//
+// The basic use case is for any feature that can be toggled (e.g. through
+// command-line or an experiment) to have a defined Feature struct, e.g.:
+//
+//   struct base::Feature kMyGreatFeature {
+//     "MyGreatFeature", base::FEATURE_ENABLED_BY_DEFAULT
+//   };
+//
+// Then, client code that wishes to query the state of the feature would check:
+//
+//   if (base::FeatureList::IsEnabled(kMyGreatFeature)) {
+//     // Feature code goes here.
+//   }
+//
+// Behind the scenes, the above call would take into account any command-line
+// flags to enable or disable the feature, any experiments that may control it
+// and finally its default state (in that order of priority), to determine
+// whether the feature is on.
+//
+// Features can be explicitly forced on or off by specifying a list of comma-
+// separated feature names via the following command-line flags:
+//
+//   --enable-features=Feature5,Feature7
+//   --disable-features=Feature1,Feature2,Feature3
+//
+// After initialization (which should be done single-threaded), the FeatureList
+// API is thread safe.
+//
+// Note: This class is a singleton, but does not use base/memory/singleton.h in
+// order to have control over its initialization sequence. Specifically, the
+// intended use is to create an instance of this class and fully initialize it,
+// before setting it as the singleton for a process, via SetInstance().
+class BASE_EXPORT FeatureList {
+ public:
+  FeatureList();
+  ~FeatureList();
+
+  // Initializes feature overrides via command-line flags |enable_features| and
+  // |disable_features|, each of which is a comma-separated list of features to
+  // enable or disable, respectively. If a feature appears on both lists, then
+  // it will be disabled. Must only be invoked during the initialization phase
+  // (before FinalizeInitialization() has been called).
+  void InitializeFromCommandLine(const std::string& enable_features,
+                                 const std::string& disable_features);
+
+  // Returns whether the given |feature| is enabled. Must only be called after
+  // the singleton instance has been registered via SetInstance(). Additionally,
+  // a feature with a given name must only have a single corresponding Feature
+  // struct, which is checked in builds with DCHECKs enabled.
+  static bool IsEnabled(const Feature& feature);
+
+  // Returns the singleton instance of FeatureList. Will return null until an
+  // instance is registered via SetInstance().
+  static FeatureList* GetInstance();
+
+  // Registers the given |instance| to be the singleton feature list for this
+  // process. This should only be called once and |instance| must not be null.
+  static void SetInstance(scoped_ptr<FeatureList> instance);
+
+  // Clears the previously-registered singleton instance for tests.
+  static void ClearInstanceForTesting();
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(FeatureListTest, CheckFeatureIdentity);
+
+  // Specifies whether a feature override enables or disables the feature.
+  enum OverrideState {
+    OVERRIDE_DISABLE_FEATURE,
+    OVERRIDE_ENABLE_FEATURE,
+  };
+
+  // Finalizes the initialization state of the FeatureList, so that no further
+  // overrides can be registered. This is called by SetInstance() on the
+  // singleton feature list that is being registered.
+  void FinalizeInitialization();
+
+  // Returns whether the given |feature| is enabled. This is invoked by the
+  // public FeatureList::IsEnabled() static function on the global singleton.
+  // Requires the FeatureList to have already been fully initialized.
+  bool IsFeatureEnabled(const Feature& feature);
+
+  // Registers an override for feature |feature_name|. The override specifies
+  // whether the feature should be on or off (via |overridden_state|), which
+  // will take precedence over the feature's default state.
+  void RegisterOverride(const std::string& feature_name,
+                        OverrideState overridden_state);
+
+  // Verifies that there's only a single definition of a Feature struct for a
+  // given feature name. Keeps track of the first seen Feature struct for each
+  // feature. Returns false when called on a Feature struct with a different
+  // address than the first one it saw for that feature name. Used only from
+  // DCHECKs and tests.
+  bool CheckFeatureIdentity(const Feature& feature);
+
+  struct OverrideEntry {
+    // The overridden enable (on/off) state of the feature.
+    const OverrideState overridden_state;
+
+    // TODO(asvitkine): Expand this as more support is added.
+
+    explicit OverrideEntry(OverrideState overridden_state);
+  };
+  // Map from feature name to an OverrideEntry struct for the feature, if it
+  // exists.
+  std::map<std::string, OverrideEntry> overrides_;
+
+  // Locked map that keeps track of seen features, to ensure a single feature is
+  // only defined once. This verification is only done in builds with DCHECKs
+  // enabled.
+  Lock feature_identity_tracker_lock_;
+  std::map<std::string, const Feature*> feature_identity_tracker_;
+
+  // Whether this object has been fully initialized. This gets set to true as a
+  // result of FinalizeInitialization().
+  bool initialized_;
+
+  DISALLOW_COPY_AND_ASSIGN(FeatureList);
+};
+
+}  // namespace base
+
+#endif  // BASE_FEATURE_LIST_H_
diff --git a/feature_list_unittest.cc b/feature_list_unittest.cc
new file mode 100644
index 0000000..c9423ce
--- /dev/null
+++ b/feature_list_unittest.cc
@@ -0,0 +1,108 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/feature_list.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+const char kFeatureOnByDefaultName[] = "OnByDefault";
+struct Feature kFeatureOnByDefault {
+  kFeatureOnByDefaultName, FEATURE_ENABLED_BY_DEFAULT
+};
+
+const char kFeatureOffByDefaultName[] = "OffByDefault";
+struct Feature kFeatureOffByDefault {
+  kFeatureOffByDefaultName, FEATURE_DISABLED_BY_DEFAULT
+};
+
+}  // namespace
+
+class FeatureListTest : public testing::Test {
+ public:
+  FeatureListTest() : feature_list_(nullptr) {
+    RegisterFeatureListInstance(make_scoped_ptr(new FeatureList));
+  }
+  ~FeatureListTest() override { ClearFeatureListInstance(); }
+
+  void RegisterFeatureListInstance(scoped_ptr<FeatureList> feature_list) {
+    feature_list_ = feature_list.get();
+    FeatureList::SetInstance(feature_list.Pass());
+  }
+  void ClearFeatureListInstance() {
+    FeatureList::ClearInstanceForTesting();
+    feature_list_ = nullptr;
+  }
+
+  FeatureList* feature_list() { return feature_list_; }
+
+ private:
+  // Weak. Owned by the FeatureList::SetInstance().
+  FeatureList* feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(FeatureListTest);
+};
+
+TEST_F(FeatureListTest, DefaultStates) {
+  EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
+  EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
+}
+
+TEST_F(FeatureListTest, InitializeFromCommandLine) {
+  struct {
+    const char* enable_features;
+    const char* disable_features;
+    bool expected_feature_on_state;
+    bool expected_feature_off_state;
+  } test_cases[] = {
+      {"", "", true, false},
+      {"OffByDefault", "", true, true},
+      {"OffByDefault", "OnByDefault", false, true},
+      {"OnByDefault,OffByDefault", "", true, true},
+      {"", "OnByDefault,OffByDefault", false, false},
+      // In the case an entry is both, disable takes precedence.
+      {"OnByDefault", "OnByDefault,OffByDefault", false, false},
+  };
+
+  for (size_t i = 0; i < arraysize(test_cases); ++i) {
+    const auto& test_case = test_cases[i];
+
+    ClearFeatureListInstance();
+    scoped_ptr<FeatureList> feature_list(new FeatureList);
+    feature_list->InitializeFromCommandLine(test_case.enable_features,
+                                            test_case.disable_features);
+    RegisterFeatureListInstance(feature_list.Pass());
+
+    EXPECT_EQ(test_case.expected_feature_on_state,
+              FeatureList::IsEnabled(kFeatureOnByDefault))
+        << i;
+    EXPECT_EQ(test_case.expected_feature_off_state,
+              FeatureList::IsEnabled(kFeatureOffByDefault))
+        << i;
+  }
+}
+
+TEST_F(FeatureListTest, CheckFeatureIdentity) {
+  // Tests that CheckFeatureIdentity() correctly detects when two different
+  // structs with the same feature name are passed to it.
+
+  // Call it twice for each feature at the top of the file, since the first call
+  // makes it remember the entry and the second call will verify it.
+  EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault));
+  EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault));
+  EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOffByDefault));
+  EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOffByDefault));
+
+  // Now, call it with a distinct struct for |kFeatureOnByDefaultName|, which
+  // should return false.
+  struct Feature kFeatureOnByDefault2 {
+    kFeatureOnByDefaultName, FEATURE_ENABLED_BY_DEFAULT
+  };
+  EXPECT_FALSE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault2));
+}
+
+}  // namespace base
diff --git a/json/string_escape.cc b/json/string_escape.cc
index 469f9f9..f5d6a76 100644
--- a/json/string_escape.cc
+++ b/json/string_escape.cc
@@ -59,6 +59,14 @@
     case '<':
       dest->append("\\u003C");
       break;
+    // Escape the "Line Separator" and "Paragraph Separator" characters, since
+    // they should be treated like a new line \r or \n.
+    case 0x2028:
+      dest->append("\\u2028");
+      break;
+    case 0x2029:
+      dest->append("\\u2029");
+      break;
     default:
       return false;
   }
diff --git a/json/string_escape_unittest.cc b/json/string_escape_unittest.cc
index 100373f..738a657 100644
--- a/json/string_escape_unittest.cc
+++ b/json/string_escape_unittest.cc
@@ -15,12 +15,13 @@
     const char* to_escape;
     const char* escaped;
   } cases[] = {
-    {"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
-    {"a\b\f\n\r\t\v\1\\.\"z",
-        "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
-    {"b\x0f\x7f\xf0\xff!",  // \xf0\xff is not a valid UTF-8 unit.
-        "b\\u000F\x7F\xEF\xBF\xBD\xEF\xBF\xBD!"},
-    {"c<>d", "c\\u003C>d"},
+      {"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
+      {"a\b\f\n\r\t\v\1\\.\"z", "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
+      {"b\x0f\x7f\xf0\xff!",  // \xf0\xff is not a valid UTF-8 unit.
+       "b\\u000F\x7F\xEF\xBF\xBD\xEF\xBF\xBD!"},
+      {"c<>d", "c\\u003C>d"},
+      {"Hello\xe2\x80\xa8world", "Hello\\u2028world"},
+      {"\xe2\x80\xa9purple", "\\u2029purple"},
   };
 
   for (size_t i = 0; i < arraysize(cases); ++i) {
@@ -73,12 +74,13 @@
     const wchar_t* to_escape;
     const char* escaped;
   } cases[] = {
-    {L"b\uffb1\u00ff", "b\xEF\xBE\xB1\xC3\xBF"},
-    {L"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
-    {L"a\b\f\n\r\t\v\1\\.\"z",
-        "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
-    {L"b\x0f\x7f\xf0\xff!", "b\\u000F\x7F\xC3\xB0\xC3\xBF!"},
-    {L"c<>d", "c\\u003C>d"},
+      {L"b\uffb1\u00ff", "b\xEF\xBE\xB1\xC3\xBF"},
+      {L"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
+      {L"a\b\f\n\r\t\v\1\\.\"z", "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
+      {L"b\x0f\x7f\xf0\xff!", "b\\u000F\x7F\xC3\xB0\xC3\xBF!"},
+      {L"c<>d", "c\\u003C>d"},
+      {L"Hello\u2028world", "Hello\\u2028world"},
+      {L"\u2029purple", "\\u2029purple"},
   };
 
   for (size_t i = 0; i < arraysize(cases); ++i) {
diff --git a/logging.cc b/logging.cc
index 97ee134..c6992b9 100644
--- a/logging.cc
+++ b/logging.cc
@@ -545,8 +545,8 @@
 LogMessage::~LogMessage() {
 #if !defined(OFFICIAL_BUILD) && !defined(OS_NACL) && !defined(__UCLIBC__) && \
     !defined(FNL_MUSL)
-  if (severity_ == LOG_FATAL) {
-    // Include a stack trace on a fatal.
+  if (severity_ == LOG_FATAL && !base::debug::BeingDebugged()) {
+    // Include a stack trace on a fatal, unless a debugger is attached.
     base::debug::StackTrace trace;
     stream_ << std::endl;  // Newline to separate from log message.
     trace.OutputToStream(&stream_);
@@ -640,7 +640,11 @@
       // information, and displaying message boxes when the application is
       // hosed can cause additional problems.
 #ifndef NDEBUG
-      DisplayDebugMessageInDialog(stream_.str());
+      if (!base::debug::BeingDebugged()) {
+        // Displaying a dialog is unnecessary when debugging and can complicate
+        // debugging.
+        DisplayDebugMessageInDialog(stream_.str());
+      }
 #endif
       // Crash the process to generate a dump.
       base::debug::BreakDebugger();
diff --git a/mac/foundation_util.h b/mac/foundation_util.h
index 353ed7c..6e8505d 100644
--- a/mac/foundation_util.h
+++ b/mac/foundation_util.h
@@ -373,6 +373,14 @@
 // Converts |str| to a FilePath. Returns an empty path if |str| is nil.
 BASE_EXPORT FilePath NSStringToFilePath(NSString* str);
 
+#if defined(__OBJC__)
+// Converts |range| to an NSRange, returning the new range in |range_out|.
+// Returns true if conversion was successful, false if the values of |range|
+// could not be converted to NSUIntegers.
+BASE_EXPORT bool CFRangeToNSRange(CFRange range,
+                                  NSRange* range_out) WARN_UNUSED_RESULT;
+#endif  // defined(__OBJC__)
+
 }  // namespace mac
 }  // namespace base
 
diff --git a/mac/foundation_util.mm b/mac/foundation_util.mm
index 27d6e7c..bd5d514 100644
--- a/mac/foundation_util.mm
+++ b/mac/foundation_util.mm
@@ -11,6 +11,7 @@
 #include "base/logging.h"
 #include "base/mac/bundle_locations.h"
 #include "base/mac/mac_logging.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/strings/sys_string_conversions.h"
 
 #if !defined(OS_IOS)
@@ -430,6 +431,19 @@
   return FilePath([str fileSystemRepresentation]);
 }
 
+bool CFRangeToNSRange(CFRange range, NSRange* range_out) {
+  if (base::IsValueInRangeForNumericType<decltype(range_out->location)>(
+          range.location) &&
+      base::IsValueInRangeForNumericType<decltype(range_out->length)>(
+          range.length) &&
+      base::IsValueInRangeForNumericType<decltype(range_out->location)>(
+          range.location + range.length)) {
+    *range_out = NSMakeRange(range.location, range.length);
+    return true;
+  }
+  return false;
+}
+
 }  // namespace mac
 }  // namespace base
 
diff --git a/mac/foundation_util_unittest.mm b/mac/foundation_util_unittest.mm
index e60a0f6..c688442 100644
--- a/mac/foundation_util_unittest.mm
+++ b/mac/foundation_util_unittest.mm
@@ -317,6 +317,18 @@
   EXPECT_EQ(FilePath("/a/b"), NSStringToFilePath(@"/a/b"));
 }
 
+TEST(FoundationUtilTest, CFRangeToNSRange) {
+  NSRange range_out;
+  EXPECT_TRUE(CFRangeToNSRange(CFRangeMake(10, 5), &range_out));
+  EXPECT_EQ(10UL, range_out.location);
+  EXPECT_EQ(5UL, range_out.length);
+  EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(-1, 5), &range_out));
+  EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(5, -1), &range_out));
+  EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(-1, -1), &range_out));
+  EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(LONG_MAX, LONG_MAX), &range_out));
+  EXPECT_FALSE(CFRangeToNSRange(CFRangeMake(LONG_MIN, LONG_MAX), &range_out));
+}
+
 TEST(StringNumberConversionsTest, FormatNSInteger) {
   // The PRI[dxu]NS macro assumes that NSInteger is a typedef to "int" on
   // 32-bit architecture and a typedef to "long" on 64-bit architecture
diff --git a/memory/BUILD.gn b/memory/BUILD.gn
index 4250631..bd94bb2 100644
--- a/memory/BUILD.gn
+++ b/memory/BUILD.gn
@@ -69,6 +69,15 @@
     sources -= [ "shared_memory_posix.cc" ]
   }
 
+  if (is_ios) {
+    set_sources_assignment_filter([])
+    sources += [
+      "shared_memory_handle_mac.cc",
+      "shared_memory_mac.cc",
+    ]
+    set_sources_assignment_filter(sources_assignment_filter)
+  }
+
   if (is_android) {
     deps = [
       "//third_party/ashmem",
diff --git a/memory/memory_pressure_listener.cc b/memory/memory_pressure_listener.cc
index 2a1be74..8071d37 100644
--- a/memory/memory_pressure_listener.cc
+++ b/memory/memory_pressure_listener.cc
@@ -32,6 +32,10 @@
     ObserverListThreadSafe<MemoryPressureListener>,
     LeakyLazyObserverListTraits> g_observers = LAZY_INSTANCE_INITIALIZER;
 
+// All memory pressure notifications within this process will be suppressed if
+// this variable is set to 1.
+subtle::Atomic32 g_notifications_suppressed = 0;
+
 }  // namespace
 
 MemoryPressureListener::MemoryPressureListener(
@@ -54,8 +58,20 @@
   DCHECK_NE(memory_pressure_level, MEMORY_PRESSURE_LEVEL_NONE);
   TRACE_EVENT1("memory", "MemoryPressureListener::NotifyMemoryPressure",
       "level", memory_pressure_level);
+  if (AreNotificationsSuppressed())
+    return;
   g_observers.Get().Notify(FROM_HERE, &MemoryPressureListener::Notify,
                            memory_pressure_level);
 }
 
+// static
+bool MemoryPressureListener::AreNotificationsSuppressed() {
+  return subtle::Acquire_Load(&g_notifications_suppressed) == 1;
+}
+
+// static
+void MemoryPressureListener::SetNotificationsSuppressed(bool suppress) {
+  subtle::Release_Store(&g_notifications_suppressed, suppress ? 1 : 0);
+}
+
 }  // namespace base
diff --git a/memory/memory_pressure_listener.h b/memory/memory_pressure_listener.h
index 6adaeee..83d47d7 100644
--- a/memory/memory_pressure_listener.h
+++ b/memory/memory_pressure_listener.h
@@ -72,6 +72,12 @@
   // Intended for use by the platform specific implementation.
   static void NotifyMemoryPressure(MemoryPressureLevel memory_pressure_level);
 
+  // These methods should not be used anywhere else but in memory measurement
+  // code, where they are intended to maintain stable conditions across
+  // measurements.
+  static bool AreNotificationsSuppressed();
+  static void SetNotificationsSuppressed(bool suppressed);
+
  private:
   void Notify(MemoryPressureLevel memory_pressure_level);
 
diff --git a/memory/memory_pressure_listener_unittest.cc b/memory/memory_pressure_listener_unittest.cc
new file mode 100644
index 0000000..1bcab2f
--- /dev/null
+++ b/memory/memory_pressure_listener_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/memory_pressure_listener.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+
+using MockCallback =
+    testing::MockFunction<void(MemoryPressureListener::MemoryPressureLevel)>;
+
+TEST(MemoryPressureListenerTest, NotifyMemoryPressure) {
+  MessageLoopForUI message_loop;
+  MockCallback callback;
+  scoped_ptr<MemoryPressureListener> listener(new MemoryPressureListener(
+      Bind(&MockCallback::Call, Unretained(&callback))));
+
+  // Memory pressure notifications are not suppressed by default.
+  EXPECT_FALSE(MemoryPressureListener::AreNotificationsSuppressed());
+  EXPECT_CALL(callback,
+              Call(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE))
+      .Times(1);
+  MemoryPressureListener::NotifyMemoryPressure(
+      MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
+  message_loop.RunUntilIdle();
+
+  // Enable suppressing memory pressure notifications.
+  MemoryPressureListener::SetNotificationsSuppressed(true);
+  EXPECT_TRUE(MemoryPressureListener::AreNotificationsSuppressed());
+  EXPECT_CALL(callback, Call(testing::_)).Times(0);
+  MemoryPressureListener::NotifyMemoryPressure(
+      MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE);
+  message_loop.RunUntilIdle();
+
+  // Disable suppressing memory pressure notifications.
+  MemoryPressureListener::SetNotificationsSuppressed(false);
+  EXPECT_FALSE(MemoryPressureListener::AreNotificationsSuppressed());
+  EXPECT_CALL(callback,
+              Call(MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL))
+      .Times(1);
+  MemoryPressureListener::NotifyMemoryPressure(
+      MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
+  message_loop.RunUntilIdle();
+
+  listener.reset();
+}
+
+}  // namespace base
diff --git a/memory/shared_memory_handle.h b/memory/shared_memory_handle.h
index 7af8729..01a7f75 100644
--- a/memory/shared_memory_handle.h
+++ b/memory/shared_memory_handle.h
@@ -9,7 +9,7 @@
 
 #if defined(OS_WIN)
 #include <windows.h>
-#elif defined(OS_MACOSX) && !defined(OS_IOS)
+#elif defined(OS_MACOSX)
 #include <sys/types.h>
 #include "base/base_export.h"
 #include "base/file_descriptor_posix.h"
@@ -27,7 +27,7 @@
 // the underlying OS handle to a shared memory segment.
 #if defined(OS_WIN)
 typedef HANDLE SharedMemoryHandle;
-#elif defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS))
+#elif defined(OS_POSIX) && !defined(OS_MACOSX)
 typedef FileDescriptor SharedMemoryHandle;
 #else
 class BASE_EXPORT SharedMemoryHandle {
diff --git a/memory/shared_memory_handle_mac.cc b/memory/shared_memory_handle_mac.cc
index 86f5c54..05e04d7 100644
--- a/memory/shared_memory_handle_mac.cc
+++ b/memory/shared_memory_handle_mac.cc
@@ -8,7 +8,6 @@
 
 #include "base/posix/eintr_wrapper.h"
 
-#if defined(OS_MACOSX) && !defined(OS_IOS)
 namespace base {
 
 static_assert(sizeof(SharedMemoryHandle::Type) <=
@@ -83,4 +82,3 @@
 }
 
 }  // namespace base
-#endif  // defined(OS_MACOSX) && !defined(OS_IOS)
diff --git a/message_loop/message_pump_android.cc b/message_loop/message_pump_android.cc
index babd17b..a0eee12 100644
--- a/message_loop/message_pump_android.cc
+++ b/message_loop/message_pump_android.cc
@@ -21,8 +21,10 @@
 // ----------------------------------------------------------------------------
 // This method can not move to anonymous namespace as it has been declared as
 // 'static' in system_message_handler_jni.h.
-static void DoRunLoopOnce(JNIEnv* env, jobject obj, jlong native_delegate,
-    jlong delayed_scheduled_time_ticks) {
+static void DoRunLoopOnce(JNIEnv* env,
+                          const JavaParamRef<jobject>& obj,
+                          jlong native_delegate,
+                          jlong delayed_scheduled_time_ticks) {
   base::MessagePump::Delegate* delegate =
       reinterpret_cast<base::MessagePump::Delegate*>(native_delegate);
   DCHECK(delegate);
diff --git a/pickle.cc b/pickle.cc
index d4487d4..7c8ffed 100644
--- a/pickle.cc
+++ b/pickle.cc
@@ -254,9 +254,8 @@
       header_size_(other.header_size_),
       capacity_after_header_(0),
       write_offset_(other.write_offset_) {
-  size_t payload_size = header_size_ + other.header_->payload_size;
-  Resize(payload_size);
-  memcpy(header_, other.header_, payload_size);
+  Resize(other.header_->payload_size);
+  memcpy(header_, other.header_, header_size_ + other.header_->payload_size);
 }
 
 Pickle::~Pickle() {
diff --git a/pickle.h b/pickle.h
index 40a983b..b6ec116 100644
--- a/pickle.h
+++ b/pickle.h
@@ -296,6 +296,7 @@
   }
   inline void WriteBytesCommon(const void* data, size_t length);
 
+  FRIEND_TEST_ALL_PREFIXES(PickleTest, DeepCopyResize);
   FRIEND_TEST_ALL_PREFIXES(PickleTest, Resize);
   FRIEND_TEST_ALL_PREFIXES(PickleTest, FindNext);
   FRIEND_TEST_ALL_PREFIXES(PickleTest, FindNextWithIncompleteHeader);
diff --git a/pickle_unittest.cc b/pickle_unittest.cc
index b0a8f21..6f9fcc7 100644
--- a/pickle_unittest.cc
+++ b/pickle_unittest.cc
@@ -428,4 +428,18 @@
   EXPECT_EQ(data, outdata);
 }
 
+// Checks that when a pickle is deep-copied, the result is not larger than
+// needed.
+TEST(PickleTest, DeepCopyResize) {
+  Pickle pickle;
+  while (pickle.capacity_after_header() != pickle.payload_size())
+    pickle.WriteBool(true);
+
+  // Make a deep copy.
+  Pickle pickle2(pickle);
+
+  // Check that there isn't any extraneous capacity.
+  EXPECT_EQ(pickle.capacity_after_header(), pickle2.capacity_after_header());
+}
+
 }  // namespace base
diff --git a/power_monitor/power_monitor_device_source_android.cc b/power_monitor/power_monitor_device_source_android.cc
index 4d9eb52..9671c30 100644
--- a/power_monitor/power_monitor_device_source_android.cc
+++ b/power_monitor/power_monitor_device_source_android.cc
@@ -19,15 +19,15 @@
 namespace android {
 
 // Native implementation of PowerMonitor.java.
-void OnBatteryChargingChanged(JNIEnv* env, jclass clazz) {
+void OnBatteryChargingChanged(JNIEnv* env, const JavaParamRef<jclass>& clazz) {
   ProcessPowerEventHelper(PowerMonitorSource::POWER_STATE_EVENT);
 }
 
-void OnMainActivityResumed(JNIEnv* env, jclass clazz) {
+void OnMainActivityResumed(JNIEnv* env, const JavaParamRef<jclass>& clazz) {
   ProcessPowerEventHelper(PowerMonitorSource::RESUME_EVENT);
 }
 
-void OnMainActivitySuspended(JNIEnv* env, jclass clazz) {
+void OnMainActivitySuspended(JNIEnv* env, const JavaParamRef<jclass>& clazz) {
   ProcessPowerEventHelper(PowerMonitorSource::SUSPEND_EVENT);
 }
 
diff --git a/process/kill.h b/process/kill.h
index dbd32e1..b6873e6 100644
--- a/process/kill.h
+++ b/process/kill.h
@@ -21,11 +21,11 @@
 // exit code arguments to KillProcess*(), use platform/application
 // specific values instead.
 enum TerminationStatus {
-  TERMINATION_STATUS_NORMAL_TERMINATION,   // zero exit status
-  TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status
-  TERMINATION_STATUS_PROCESS_WAS_KILLED,   // e.g. SIGKILL or task manager kill
-  TERMINATION_STATUS_PROCESS_CRASHED,      // e.g. Segmentation fault
-  TERMINATION_STATUS_STILL_RUNNING,        // child hasn't exited yet
+  TERMINATION_STATUS_NORMAL_TERMINATION,    // zero exit status
+  TERMINATION_STATUS_ABNORMAL_TERMINATION,  // non-zero exit status
+  TERMINATION_STATUS_PROCESS_WAS_KILLED,    // e.g. SIGKILL or task manager kill
+  TERMINATION_STATUS_PROCESS_CRASHED,       // e.g. Segmentation fault
+  TERMINATION_STATUS_STILL_RUNNING,         // child hasn't exited yet
 #if defined(OS_CHROMEOS)
   // Used for the case when oom-killer kills a process on ChromeOS.
   TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM,
@@ -35,8 +35,9 @@
   // the termination status.  We can't know if the termination was a crash or an
   // oom kill for sure, but we can use status of the strong process bindings as
   // a hint.
-  TERMINATION_STATUS_OOM_PROTECTED,        // child was protected from oom kill
+  TERMINATION_STATUS_OOM_PROTECTED,  // child was protected from oom kill
 #endif
+  TERMINATION_STATUS_LAUNCH_FAILED,  // child process never launched
   TERMINATION_STATUS_MAX_ENUM
 };
 
diff --git a/synchronization/condition_variable_posix.cc b/synchronization/condition_variable_posix.cc
index 013284c..c9a2ec4 100644
--- a/synchronization/condition_variable_posix.cc
+++ b/synchronization/condition_variable_posix.cc
@@ -42,6 +42,20 @@
 }
 
 ConditionVariable::~ConditionVariable() {
+#if defined(OS_MACOSX)
+  // This hack is necessary to avoid a fatal pthreads subsystem bug in the
+  // Darwin kernel. http://crbug.com/517681.
+  {
+    base::Lock lock;
+    base::AutoLock l(lock);
+    struct timespec ts;
+    ts.tv_sec = 0;
+    ts.tv_nsec = 1;
+    pthread_cond_timedwait_relative_np(&condition_, lock.lock_.native_handle(),
+                                       &ts);
+  }
+#endif
+
   int rv = pthread_cond_destroy(&condition_);
   DCHECK_EQ(0, rv);
 }
diff --git a/trace_event/memory_dump_manager.cc b/trace_event/memory_dump_manager.cc
index f3739cb..71a9dae 100644
--- a/trace_event/memory_dump_manager.cc
+++ b/trace_event/memory_dump_manager.cc
@@ -38,23 +38,27 @@
 
 namespace {
 
-// Throttle mmaps at a rate of once every kHeavyMmapsDumpsRate standard dumps.
-const int kHeavyDumpsRate = 8;  // 250 ms * 8 = 2000 ms.
-const int kDumpIntervalMs = 250;
 const int kTraceEventNumArgs = 1;
 const char* kTraceEventArgNames[] = {"dumps"};
 const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE};
 
 StaticAtomicSequenceNumber g_next_guid;
 uint32 g_periodic_dumps_count = 0;
+uint32 g_heavy_dumps_rate = 0;
 MemoryDumpManager* g_instance_for_testing = nullptr;
 
 void RequestPeriodicGlobalDump() {
-  MemoryDumpArgs::LevelOfDetail dump_level_of_detail =
-      g_periodic_dumps_count == 0 ? MemoryDumpArgs::LevelOfDetail::HIGH
-                                  : MemoryDumpArgs::LevelOfDetail::LOW;
-  if (++g_periodic_dumps_count == kHeavyDumpsRate)
-    g_periodic_dumps_count = 0;
+  MemoryDumpArgs::LevelOfDetail dump_level_of_detail;
+  if (g_heavy_dumps_rate == 0) {
+    dump_level_of_detail = MemoryDumpArgs::LevelOfDetail::LOW;
+  } else {
+    dump_level_of_detail = g_periodic_dumps_count == 0
+                               ? MemoryDumpArgs::LevelOfDetail::HIGH
+                               : MemoryDumpArgs::LevelOfDetail::LOW;
+
+    if (++g_periodic_dumps_count == g_heavy_dumps_rate)
+      g_periodic_dumps_count = 0;
+  }
 
   MemoryDumpArgs dump_args = {dump_level_of_detail};
   MemoryDumpManager::GetInstance()->RequestGlobalDump(
@@ -116,14 +120,7 @@
 #endif
 
 #if (defined(OS_LINUX) && !defined(FNL_MUSL)) || defined(OS_ANDROID)
-  // The memory maps dump provider is currently disabled for security reasons
-  // and will be enabled once tracing is more secure (crbug.com/517906).
-  // It is still enabled for running benchmarks.
-  if (CommandLine::ForCurrentProcess()->HasSwitch(
-          "enable-memory-benchmarking")) {
-    RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance());
-  }
-
+  RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance());
   RegisterDumpProvider(MallocDumpProvider::GetInstance());
   system_allocator_pool_name_ = MallocDumpProvider::kAllocatedObjects;
 #endif
@@ -419,18 +416,44 @@
   subtle::NoBarrier_Store(&memory_tracing_enabled_, 1);
 
   // TODO(primiano): This is a temporary hack to disable periodic memory dumps
-  // when running memory benchmarks until they can be enabled/disabled in
-  // base::trace_event::TraceConfig. See https://goo.gl/5Hj3o0.
+  // when running memory benchmarks until telemetry uses TraceConfig to
+  // enable/disable periodic dumps.
   // The same mechanism should be used to disable periodic dumps in tests.
-  if (delegate_->IsCoordinatorProcess() &&
-      !CommandLine::ForCurrentProcess()->HasSwitch(
-          "enable-memory-benchmarking") &&
-      !disable_periodic_dumps_for_testing_) {
-    g_periodic_dumps_count = 0;
-    periodic_dump_timer_.Start(FROM_HERE,
-                               TimeDelta::FromMilliseconds(kDumpIntervalMs),
-                               base::Bind(&RequestPeriodicGlobalDump));
+  if (!delegate_->IsCoordinatorProcess() ||
+      CommandLine::ForCurrentProcess()->HasSwitch(
+          "enable-memory-benchmarking") ||
+      disable_periodic_dumps_for_testing_) {
+    return;
   }
+
+  // Enable periodic dumps. At the moment the periodic support is limited to at
+  // most one low-detail periodic dump and at most one high-detail periodic
+  // dump. If both are specified the high-detail period must be an integer
+  // multiple of the low-level one.
+  g_periodic_dumps_count = 0;
+  const TraceConfig trace_config =
+      TraceLog::GetInstance()->GetCurrentTraceConfig();
+  const TraceConfig::MemoryDumpConfig& config_list =
+      trace_config.memory_dump_config();
+  if (config_list.empty())
+    return;
+
+  uint32 min_timer_period_ms = std::numeric_limits<uint32>::max();
+  uint32 heavy_dump_period_ms = 0;
+  DCHECK_LE(config_list.size(), 2u);
+  for (const TraceConfig::MemoryDumpTriggerConfig& config : config_list) {
+    DCHECK(config.periodic_interval_ms);
+    if (config.level_of_detail == MemoryDumpArgs::LevelOfDetail::HIGH)
+      heavy_dump_period_ms = config.periodic_interval_ms;
+    min_timer_period_ms =
+        std::min(min_timer_period_ms, config.periodic_interval_ms);
+  }
+  DCHECK_EQ(0u, heavy_dump_period_ms % min_timer_period_ms);
+  g_heavy_dumps_rate = heavy_dump_period_ms / min_timer_period_ms;
+
+  periodic_dump_timer_.Start(FROM_HERE,
+                             TimeDelta::FromMilliseconds(min_timer_period_ms),
+                             base::Bind(&RequestPeriodicGlobalDump));
 }
 
 void MemoryDumpManager::OnTraceLogDisabled() {
diff --git a/trace_event/memory_dump_manager_unittest.cc b/trace_event/memory_dump_manager_unittest.cc
index 4d0372f..6200c2d 100644
--- a/trace_event/memory_dump_manager_unittest.cc
+++ b/trace_event/memory_dump_manager_unittest.cc
@@ -25,8 +25,8 @@
 namespace base {
 namespace trace_event {
 namespace {
-MemoryDumpArgs high_detail_args = {MemoryDumpArgs::LevelOfDetail::HIGH};
-MemoryDumpArgs low_detail_args = {MemoryDumpArgs::LevelOfDetail::LOW};
+MemoryDumpArgs g_high_detail_args = {MemoryDumpArgs::LevelOfDetail::HIGH};
+MemoryDumpArgs g_low_detail_args = {MemoryDumpArgs::LevelOfDetail::LOW};
 }
 
 // Testing MemoryDumpManagerDelegate which short-circuits dump requests locally
@@ -44,6 +44,16 @@
   }
 };
 
+class MemoryDumpManagerDelegateForPeriodicDumpTest
+    : public MemoryDumpManagerDelegateForTesting {
+ public:
+  MOCK_METHOD2(RequestGlobalMemoryDump,
+               void(const MemoryDumpRequestArgs& args,
+                    const MemoryDumpCallback& callback));
+
+  virtual bool IsCoordinatorProcess() const { return true; }
+};
+
 class MemoryDumpManagerTest : public testing::Test {
  public:
   void SetUp() override {
@@ -53,7 +63,6 @@
     MemoryDumpManager::SetInstanceForTesting(mdm_.get());
     ASSERT_EQ(mdm_, MemoryDumpManager::GetInstance());
     MemoryDumpManager::GetInstance()->Initialize();
-    MemoryDumpManager::GetInstance()->SetDelegate(&delegate_);
   }
 
   void TearDown() override {
@@ -72,19 +81,35 @@
   }
 
  protected:
+  void SetDelegate(scoped_ptr<MemoryDumpManagerDelegateForTesting> delegate) {
+    delegate_ = delegate.Pass();
+    MemoryDumpManager::GetInstance()->SetDelegate(delegate_.get());
+  }
+
+  // This enalbes tracing using the legacy category filter string.
   void EnableTracing(const char* category) {
+    if (!delegate_) {
+      delegate_.reset(new MemoryDumpManagerDelegateForTesting());
+      MemoryDumpManager::GetInstance()->SetDelegate(delegate_.get());
+    }
     TraceLog::GetInstance()->SetEnabled(
         TraceConfig(category, ""), TraceLog::RECORDING_MODE);
   }
 
+  void EnableTracingWithTraceConfig(const char* trace_config) {
+    DCHECK(delegate_);
+    TraceConfig tc(trace_config);
+    TraceLog::GetInstance()->SetEnabled(tc, TraceLog::RECORDING_MODE);
+  }
+
   void DisableTracing() { TraceLog::GetInstance()->SetDisabled(); }
 
   scoped_ptr<MemoryDumpManager> mdm_;
   bool last_callback_success_;
+  scoped_ptr<MemoryDumpManagerDelegateForTesting> delegate_;
 
  private:
   scoped_ptr<MessageLoop> message_loop_;
-  MemoryDumpManagerDelegateForTesting delegate_;
 
   // We want our singleton torn down after each test.
   ShadowingAtExitManager at_exit_manager_;
@@ -170,7 +195,7 @@
   EnableTracing("foo-and-bar-but-not-memory");
   EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
   mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                          high_detail_args);
+                          g_high_detail_args);
   DisableTracing();
 
   // Now repeat enabling the memory category and check that the dumper is
@@ -179,7 +204,7 @@
   EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(3).WillRepeatedly(Return(true));
   for (int i = 0; i < 3; ++i)
     mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                            high_detail_args);
+                            g_high_detail_args);
   DisableTracing();
 
   mdm_->UnregisterDumpProvider(&mdp);
@@ -188,7 +213,7 @@
   EnableTracing(MemoryDumpManager::kTraceCategory);
   EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
   mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                          high_detail_args);
+                          g_high_detail_args);
   TraceLog::GetInstance()->SetDisabled();
 }
 
@@ -205,7 +230,7 @@
           Invoke(&mdp_high_detail,
                  &MockDumpProvider::OnMemoryDump_CheckMemoryDumpArgs));
   mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                          high_detail_args);
+                          g_high_detail_args);
   DisableTracing();
   mdm_->UnregisterDumpProvider(&mdp_high_detail);
 
@@ -221,7 +246,7 @@
           Invoke(&mdp_low_detail,
                  &MockDumpProvider::OnMemoryDump_CheckMemoryDumpArgs));
   mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                          low_detail_args);
+                          g_low_detail_args);
   DisableTracing();
   mdm_->UnregisterDumpProvider(&mdp_low_detail);
 }
@@ -244,7 +269,7 @@
 
   for (int i = 0; i < 2; ++i)
     mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                            high_detail_args);
+                            g_high_detail_args);
 
   DisableTracing();
 }
@@ -259,7 +284,7 @@
   EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(1).WillRepeatedly(Return(true));
   EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(0);
   mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                          high_detail_args);
+                          g_high_detail_args);
   DisableTracing();
 
   // Invert: enable mdp1 and disable mdp2.
@@ -269,7 +294,7 @@
   EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(0);
   EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(1).WillRepeatedly(Return(true));
   mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                          high_detail_args);
+                          g_high_detail_args);
   DisableTracing();
 
   // Enable both mdp1 and mdp2.
@@ -278,7 +303,7 @@
   EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(1).WillRepeatedly(Return(true));
   EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(1).WillRepeatedly(Return(true));
   mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                          high_detail_args);
+                          g_high_detail_args);
   DisableTracing();
 }
 
@@ -293,7 +318,7 @@
     EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(1);
     EnableTracing(MemoryDumpManager::kTraceCategory);
     mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                            high_detail_args);
+                            g_high_detail_args);
     DisableTracing();
   }
 
@@ -303,7 +328,7 @@
     EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
     EnableTracing(MemoryDumpManager::kTraceCategory);
     mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                            high_detail_args);
+                            g_high_detail_args);
     DisableTracing();
   }
 
@@ -314,7 +339,7 @@
     EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
     EnableTracing(MemoryDumpManager::kTraceCategory);
     mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                            high_detail_args);
+                            g_high_detail_args);
     DisableTracing();
   }
 
@@ -326,7 +351,7 @@
     EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(1);
     EnableTracing(MemoryDumpManager::kTraceCategory);
     mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                            high_detail_args);
+                            g_high_detail_args);
     DisableTracing();
   }
 }
@@ -366,7 +391,7 @@
           Bind(&MemoryDumpManagerTest::DumpCallbackAdapter, Unretained(this),
                MessageLoop::current()->task_runner(), run_loop.QuitClosure());
       mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                              high_detail_args, callback);
+                              g_high_detail_args, callback);
       // This nested message loop (|run_loop|) will be quit if and only if
       // the RequestGlobalDump callback is invoked.
       run_loop.Run();
@@ -414,7 +439,7 @@
   for (int i = 0; i < 1 + MemoryDumpManager::kMaxConsecutiveFailuresCount;
        i++) {
     mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                            high_detail_args);
+                            g_high_detail_args);
   }
 
   DisableTracing();
@@ -445,7 +470,7 @@
 
   for (int i = 0; i < 4; i++) {
     mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                            high_detail_args);
+                            g_high_detail_args);
   }
 
   DisableTracing();
@@ -476,7 +501,7 @@
 
   for (int i = 0; i < 4; i++) {
     mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                            high_detail_args);
+                            g_high_detail_args);
   }
 
   DisableTracing();
@@ -526,7 +551,7 @@
 
   EnableTracing(MemoryDumpManager::kTraceCategory);
   MemoryDumpRequestArgs request_args = {0, MemoryDumpType::EXPLICITLY_TRIGGERED,
-                                        high_detail_args};
+                                        g_high_detail_args};
   mdm_->CreateProcessDump(request_args, callback);
 
   run_loop.Run();
@@ -552,11 +577,65 @@
         Bind(&MemoryDumpManagerTest::DumpCallbackAdapter, Unretained(this),
              MessageLoop::current()->task_runner(), run_loop.QuitClosure());
     mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
-                            high_detail_args, callback);
+                            g_high_detail_args, callback);
     run_loop.Run();
   }
   EXPECT_FALSE(last_callback_success_);
 }
 
+MATCHER(IsHighDetail, "") {
+  return arg.dump_args.level_of_detail == MemoryDumpArgs::LevelOfDetail::HIGH;
+}
+
+MATCHER(IsLowDetail, "") {
+  return arg.dump_args.level_of_detail == MemoryDumpArgs::LevelOfDetail::LOW;
+}
+
+TEST_F(MemoryDumpManagerTest, SchedulePeriodicDumpsFromTraceConfig) {
+  const char kMemoryDumpTraceConfigString[] =
+      "{"
+      "\"included_categories\":["
+      "\"disabled-by-default-memory-infra\""
+      "],"
+      "\"memory_dump_config\":{"
+      "\"triggers\":["
+      "{"
+      "\"mode\":\"light\","
+      "\"periodic_interval_ms\":1"
+      "},"
+      "{"
+      "\"mode\":\"detailed\","
+      "\"periodic_interval_ms\":3"
+      "}"
+      "]"
+      "}"
+      "}";
+
+  RunLoop run_loop;
+  scoped_ptr<MemoryDumpManagerDelegateForPeriodicDumpTest> delegate(
+      new MemoryDumpManagerDelegateForPeriodicDumpTest());
+
+  auto quit_closure = run_loop.QuitClosure();
+  testing::InSequence sequence;
+  EXPECT_CALL(*delegate.get(), RequestGlobalMemoryDump(IsHighDetail(), _))
+      .Times(1);
+  EXPECT_CALL(*delegate.get(), RequestGlobalMemoryDump(IsLowDetail(), _))
+      .Times(2);
+  EXPECT_CALL(*delegate.get(), RequestGlobalMemoryDump(IsHighDetail(), _))
+      .Times(1);
+  EXPECT_CALL(*delegate.get(), RequestGlobalMemoryDump(IsLowDetail(), _))
+      .Times(1)
+      .WillOnce(Invoke([quit_closure](const MemoryDumpRequestArgs& args,
+                                      const MemoryDumpCallback& callback) {
+        TraceLog::GetInstance()->SetDisabled();
+        ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, quit_closure);
+      }));
+
+  SetDelegate(delegate.Pass());
+  EnableTracingWithTraceConfig(kMemoryDumpTraceConfigString);
+
+  run_loop.Run();
+}
+
 }  // namespace trace_event
 }  // namespace base