[go: nahoru, domu]

Reland #3: [Android JNI] Generate calls to RegisterNatives()

Reverted in: I31b4c809584f8dbb8e5626a28562d94a422611e7

Reason for reland: CL 556581 has fixed the bug.

TBR=sky@chromium.org,yfriedman@chromium.org,phajdan.jr@chromium.org,mef@chromium.org,torne@chromium.org,sergeyu@chromium.org,agrieve@chromium.org,halliwell@chromium.org,joedow@chromium.org,estevenson@chromium.org,jbudorick@chromium.org,yipengw@chromium.org

Bug: 683256, 738067
Cq-Include-Trybots: master.tryserver.chromium.android:android_cronet_tester
Change-Id: Ie1afd9b6e606ebecfdf5ffbd0897fabbdfd3e157
Reviewed-on: https://chromium-review.googlesource.com/558004
Commit-Queue: Yipeng Wang <yipengw@chromium.org>
Reviewed-by: Andrew Grieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/master@{#483760}
diff --git a/android_webview/test/embedded_test_server/BUILD.gn b/android_webview/test/embedded_test_server/BUILD.gn
index e9b2f3a..f9d6dbf9 100644
--- a/android_webview/test/embedded_test_server/BUILD.gn
+++ b/android_webview/test/embedded_test_server/BUILD.gn
@@ -57,6 +57,8 @@
     ":aw_java_test_native_support",
     "//net:test_support",
   ]
+  configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
+  configs += [ "//build/config/android:hide_all_but_jni" ]
 }
 
 android_apk("aw_net_test_support_apk") {
diff --git a/base/android/jni_generator/SampleForTests_jni.golden b/base/android/jni_generator/SampleForTests_jni.golden
index 7b384252..e50583ab 100644
--- a/base/android/jni_generator/SampleForTests_jni.golden
+++ b/base/android/jni_generator/SampleForTests_jni.golden
@@ -482,24 +482,35 @@
     },
 };
 
+// TODO(agrieve): Remove these empty registration functions and functions
+// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  if (jni_generator::ShouldSkipJniRegistration(false))
-    return true;
+  return true;
+}
 
-  const int kMethodsInnerClassSize = arraysize(kMethodsInnerClass);
+}  // namespace android
+}  // namespace base
+
+JNI_REGISTRATION_EXPORT bool
+    RegisterNative_org_chromium_example_jni_1generator_SampleForTests(JNIEnv*
+    env) {
+
+  const int kMethodsInnerClassSize =
+      arraysize(base::android::kMethodsInnerClass);
 
   if (env->RegisterNatives(InnerClass_clazz(env),
-                           kMethodsInnerClass,
+                           base::android::kMethodsInnerClass,
                            kMethodsInnerClassSize) < 0) {
     jni_generator::HandleRegistrationError(
         env, InnerClass_clazz(env), __FILE__);
     return false;
   }
 
-  const int kMethodsSampleForTestsSize = arraysize(kMethodsSampleForTests);
+  const int kMethodsSampleForTestsSize =
+      arraysize(base::android::kMethodsSampleForTests);
 
   if (env->RegisterNatives(SampleForTests_clazz(env),
-                           kMethodsSampleForTests,
+                           base::android::kMethodsSampleForTests,
                            kMethodsSampleForTestsSize) < 0) {
     jni_generator::HandleRegistrationError(
         env, SampleForTests_clazz(env), __FILE__);
@@ -509,7 +520,4 @@
   return true;
 }
 
-}  // namespace android
-}  // namespace base
-
 #endif  // org_chromium_example_jni_generator_SampleForTests_JNI
diff --git a/base/android/jni_generator/jni_exception_list.gni b/base/android/jni_generator/jni_exception_list.gni
new file mode 100644
index 0000000..a990cc2
--- /dev/null
+++ b/base/android/jni_generator/jni_exception_list.gni
@@ -0,0 +1,16 @@
+# Copyright 2017 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.
+
+import("//device/vr/features/features.gni")
+
+jni_exception_files = [
+  "//base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
+  "//base/android/java/src/org/chromium/base/library_loader/Linker.java",
+  "//base/android/java/src/org/chromium/base/library_loader/ModernLinker.java",
+]
+
+# Exclude it from JNI registration if VR is not enabled.
+if (!enable_vr) {
+  jni_exception_files += [ "//chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java" ]
+}
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py
index 3818009..8249fc9 100755
--- a/base/android/jni_generator/jni_generator.py
+++ b/base/android/jni_generator/jni_generator.py
@@ -401,7 +401,7 @@
 
 
 def IsMainDexJavaClass(contents):
-  """Returns "true" if the class is annotated with "@MainDex", "false" if not.
+  """Returns True if the class is annotated with "@MainDex", False if not.
 
   JNI registration doesn't always need to be completed for non-browser processes
   since most Java code is only used by the browser process. Classes that are
@@ -409,8 +409,17 @@
   to force JNI registration.
   """
   re_maindex = re.compile(r'@MainDex[\s\S]*class({|[\s\S]*{)')
-  found = re.search(re_maindex, contents)
-  return 'true' if found else 'false'
+  return bool(re.search(re_maindex, contents))
+
+
+def GetBinaryClassName(fully_qualified_class):
+  """Returns a string concatenating the Java package and class."""
+  return fully_qualified_class.replace('_', '_1').replace('/', '_')
+
+
+def GetRegistrationFunctionName(fully_qualified_class):
+  """Returns the register name with a given class."""
+  return 'RegisterNative_' + GetBinaryClassName(fully_qualified_class)
 
 
 def GetStaticCastForReturnType(return_type):
@@ -620,7 +629,6 @@
           is_constructor=True)]
     self.called_by_natives = MangleCalledByNatives(self.jni_params,
                                                    self.called_by_natives)
-
     self.constant_fields = []
     re_constant_field = re.compile('.*?public static final int (?P<name>.*?);')
     re_constant_field_value = re.compile(
@@ -673,13 +681,12 @@
     jni_namespace = ExtractJNINamespace(contents) or options.namespace
     natives = ExtractNatives(contents, options.ptr_type)
     called_by_natives = ExtractCalledByNatives(self.jni_params, contents)
-    maindex = IsMainDexJavaClass(contents)
     if len(natives) == 0 and len(called_by_natives) == 0:
       raise SyntaxError('Unable to find any JNI methods for %s.' %
                         fully_qualified_class)
     inl_header_file_generator = InlHeaderFileGenerator(
         jni_namespace, fully_qualified_class, natives, called_by_natives, [],
-        self.jni_params, options, maindex)
+        self.jni_params, options)
     self.content = inl_header_file_generator.GetContent()
 
   @classmethod
@@ -715,8 +722,7 @@
   """Generates an inline header file for JNI integration."""
 
   def __init__(self, namespace, fully_qualified_class, natives,
-               called_by_natives, constant_fields, jni_params, options,
-               maindex='false'):
+               called_by_natives, constant_fields, jni_params, options):
     self.namespace = namespace
     self.fully_qualified_class = fully_qualified_class
     self.class_name = self.fully_qualified_class.split('/')[-1]
@@ -724,7 +730,6 @@
     self.called_by_natives = called_by_natives
     self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI'
     self.constant_fields = constant_fields
-    self.maindex = maindex
     self.jni_params = jni_params
     self.options = options
 
@@ -766,8 +771,9 @@
 
 // Step 3: RegisterNatives.
 $JNI_NATIVE_METHODS
-$REGISTER_NATIVES
+$REGISTER_NATIVES_EMPTY
 $CLOSE_NAMESPACE
+$REGISTER_NATIVES
 
 #endif  // ${HEADER_GUARD}
 """)
@@ -779,8 +785,9 @@
         'METHOD_STUBS': self.GetMethodStubsString(),
         'OPEN_NAMESPACE': self.GetOpenNamespaceString(),
         'JNI_NATIVE_METHODS': self.GetJNINativeMethodsString(),
-        'REGISTER_NATIVES': self.GetRegisterNativesString(),
+        'REGISTER_NATIVES_EMPTY': self.GetOriginalRegisterNativesString(),
         'CLOSE_NAMESPACE': self.GetCloseNamespaceString(),
+        'REGISTER_NATIVES': self.GetRegisterNativesString(),
         'HEADER_GUARD': self.header_guard,
         'INCLUDES': self.GetIncludesString(),
     }
@@ -829,14 +836,19 @@
     return '\n'.join(ret)
 
   def SubstituteNativeMethods(self, template):
-    """Substitutes JAVA_CLASS and KMETHODS in the provided template."""
+    """Substitutes NAMESPACE, JAVA_CLASS and KMETHODS in the provided
+    template."""
     ret = []
     all_classes = self.GetUniqueClasses(self.natives)
     all_classes[self.class_name] = self.fully_qualified_class
     for clazz in all_classes:
       kmethods = self.GetKMethodsString(clazz)
+      namespace_str = ''
+      if self.namespace:
+        namespace_str = self.namespace + '::'
       if kmethods:
-        values = {'JAVA_CLASS': clazz,
+        values = {'NAMESPACE': namespace_str,
+                  'JAVA_CLASS': clazz,
                   'KMETHODS': kmethods}
         ret += [template.substitute(values)]
     if not ret: return ''
@@ -853,32 +865,38 @@
 """)
     return self.SubstituteNativeMethods(template)
 
+  # TODO(agrieve): Remove this function when deleting original registers.
+  # https://crbug.com/683256.
+  def GetOriginalRegisterNativesString(self):
+    """Return the code for original RegisterNatives"""
+    natives = self.GetRegisterNativesImplString()
+    if not natives:
+      return ''
+
+    return """
+// TODO(agrieve): Remove these empty registration functions and functions
+// calling them. https://crbug.com/683256.
+static bool RegisterNativesImpl(JNIEnv* env) {
+  return true;
+}
+"""
+
   def GetRegisterNativesString(self):
     """Returns the code for RegisterNatives."""
     natives = self.GetRegisterNativesImplString()
     if not natives:
       return ''
-
     template = Template("""\
-${REGISTER_NATIVES_SIGNATURE} {
-${EARLY_EXIT}
+JNI_REGISTRATION_EXPORT bool ${REGISTER_NAME}(JNIEnv* env) {
 ${NATIVES}
   return true;
 }
 """)
-    signature = 'static bool RegisterNativesImpl(JNIEnv* env)'
-    early_exit = ''
-    if self.options.native_exports_optional:
-      early_exit = """\
-  if (jni_generator::ShouldSkipJniRegistration(%s))
-    return true;
-""" % self.maindex
-
-    values = {'REGISTER_NATIVES_SIGNATURE': signature,
-              'EARLY_EXIT': early_exit,
-              'NATIVES': natives,
-             }
-
+    values = {
+        'REGISTER_NAME':
+            GetRegistrationFunctionName(self.fully_qualified_class),
+        'NATIVES': natives
+    }
     return template.substitute(values)
 
   def GetRegisterNativesImplString(self):
@@ -887,10 +905,11 @@
       return ''
 
     template = Template("""\
-  const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS});
+  const int kMethods${JAVA_CLASS}Size =
+      arraysize(${NAMESPACE}kMethods${JAVA_CLASS});
 
   if (env->RegisterNatives(${JAVA_CLASS}_clazz(env),
-                           kMethods${JAVA_CLASS},
+                           ${NAMESPACE}kMethods${JAVA_CLASS},
                            kMethods${JAVA_CLASS}Size) < 0) {
     jni_generator::HandleRegistrationError(
         env, ${JAVA_CLASS}_clazz(env), __FILE__);
@@ -974,7 +993,7 @@
     """
     template = Template("Java_${JAVA_NAME}_native${NAME}")
 
-    java_name = self.fully_qualified_class.replace('_', '_1').replace('/', '_')
+    java_name = GetBinaryClassName(self.fully_qualified_class)
     if native.java_class_name:
       java_name += '_00024' + native.java_class_name
 
@@ -1314,19 +1333,21 @@
     print e
     sys.exit(1)
   if output_file:
-    if not os.path.exists(os.path.dirname(os.path.abspath(output_file))):
-      os.makedirs(os.path.dirname(os.path.abspath(output_file)))
-    if options.optimize_generation and os.path.exists(output_file):
-      with file(output_file, 'r') as f:
-        existing_content = f.read()
-        if existing_content == content:
-          return
-    with file(output_file, 'w') as f:
-      f.write(content)
+    WriteOutput(output_file, content)
   else:
     print content
 
 
+def WriteOutput(output_file, content):
+  if os.path.exists(output_file):
+    with open(output_file) as f:
+      existing_content = f.read()
+      if existing_content == content:
+        return
+  with open(output_file, 'w') as f:
+    f.write(content)
+
+
 def GetScriptName():
   script_components = os.path.abspath(sys.argv[0]).split(os.path.sep)
   base_index = 0
@@ -1363,10 +1384,6 @@
   option_parser.add_option('--output_dir',
                            help='The output directory. Must be used with '
                            '--input')
-  option_parser.add_option('--optimize_generation', type="int",
-                           default=0, help='Whether we should optimize JNI '
-                           'generation by not regenerating files if they have '
-                           'not changed.')
   option_parser.add_option('--script_name', default=GetScriptName(),
                            help='The name of this script in the generated '
                            'header.')
diff --git a/base/android/jni_generator/jni_generator_helper.h b/base/android/jni_generator/jni_generator_helper.h
index 3062806..53629a657 100644
--- a/base/android/jni_generator/jni_generator_helper.h
+++ b/base/android/jni_generator/jni_generator_helper.h
@@ -30,6 +30,13 @@
 #define JNI_GENERATOR_EXPORT extern "C" __attribute__((visibility("default")))
 #endif
 
+// Used to export JNI registration functions.
+#if defined(COMPONENT_BUILD)
+#define JNI_REGISTRATION_EXPORT __attribute__((visibility("default")))
+#else
+#define JNI_REGISTRATION_EXPORT
+#endif
+
 namespace jni_generator {
 
 inline void HandleRegistrationError(JNIEnv* env,
@@ -42,12 +49,14 @@
   base::android::CheckException(env);
 }
 
+// TODO(estevenson): Remove this function since all natives are registered
+// together. Currently gvr-android-sdk stil calls it.
+// https://crbug.com/664306.
 inline bool ShouldSkipJniRegistration(bool is_maindex_class) {
   switch (base::android::GetJniRegistrationType()) {
     case base::android::ALL_JNI_REGISTRATION:
       return false;
     case base::android::NO_JNI_REGISTRATION:
-      // TODO(estevenson): Change this to a DCHECK.
       return true;
     case base::android::SELECTIVE_JNI_REGISTRATION:
       return !is_maindex_class;
diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py
index d667a47..41e617f 100755
--- a/base/android/jni_generator/jni_generator_tests.py
+++ b/base/android/jni_generator/jni_generator_tests.py
@@ -967,33 +967,6 @@
                                              test_options)
     self.assertGoldenTextEquals(h.GetContent())
 
-  def testMainDexFile(self):
-    test_data = """
-    package org.chromium.example.jni_generator;
-
-    @MainDex
-    class Test {
-        private static native int nativeStaticMethod(long nativeTest, int arg1);
-    }
-    """
-    options = TestOptions()
-    jni_from_java = jni_generator.JNIFromJavaSource(
-      test_data, 'org/chromium/foo/Bar', options)
-    self.assertGoldenTextEquals(jni_from_java.GetContent())
-
-  def testNonMainDexFile(self):
-    test_data = """
-    package org.chromium.example.jni_generator;
-
-    class Test {
-        private static native int nativeStaticMethod(long nativeTest, int arg1);
-    }
-    """
-    options = TestOptions()
-    jni_from_java = jni_generator.JNIFromJavaSource(
-      test_data, 'org/chromium/foo/Bar', options)
-    self.assertGoldenTextEquals(jni_from_java.GetContent())
-
   def testMainDexAnnotation(self):
     mainDexEntries = [
       '@MainDex public class Test {',
@@ -1021,7 +994,7 @@
       '@MainDex public class Test extends Testable<java.io.Serializable> {',
     ]
     for entry in mainDexEntries:
-      self.assertEquals("true", IsMainDexJavaClass(entry))
+      self.assertEquals(True, IsMainDexJavaClass(entry))
 
   def testNoMainDexAnnotation(self):
     noMainDexEntries = [
@@ -1031,7 +1004,7 @@
       'public class Test extends BaseTest {'
     ]
     for entry in noMainDexEntries:
-      self.assertEquals("false", IsMainDexJavaClass(entry))
+      self.assertEquals(False, IsMainDexJavaClass(entry))
 
   def testNativeExportsOnlyOption(self):
     test_data = """
diff --git a/base/android/jni_generator/jni_registration_generator.py b/base/android/jni_generator/jni_registration_generator.py
new file mode 100755
index 0000000..a7ede2db
--- /dev/null
+++ b/base/android/jni_generator/jni_registration_generator.py
@@ -0,0 +1,180 @@
+#!/usr/bin/env python
+# Copyright 2017 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.
+
+"""Generate JNI registration entry points
+
+Creates a header file with two static functions: RegisterMainDexNatives() and
+RegisterNonMainDexNatives(). Together, these will use manual JNI registration
+to register all native methods that exist within an application."""
+
+import argparse
+import jni_generator
+import os
+import string
+import sys
+from util import build_utils
+
+
+def GenerateJNIHeader(java_file_paths, output_file, args):
+  """Generate a header file including two registration functions.
+
+  Forward declares all JNI registration functions created by jni_generator.py.
+  Calls the functions in RegisterMainDexNatives() if they are main dex. And
+  calls them in RegisterNonMainDexNatives() if they are non-main dex.
+
+  Args:
+      java_file_paths: A list of java file paths.
+      output_file: A relative path to output file.
+      args: All input arguments.
+  """
+  registration_dict = {
+      'FORWARD_DECLARATIONS': '',
+      'REGISTER_MAIN_DEX_NATIVES': '',
+      'REGISTER_NON_MAIN_DEX_NATIVES': ''
+  }
+  # Sort the file list to make sure the order is deterministic.
+  java_file_paths.sort()
+  for path in java_file_paths:
+    if path in args.no_register_java:
+      continue
+    with open(path) as f:
+      contents = f.read()
+    natives = jni_generator.ExtractNatives(contents, 'long')
+    if len(natives) == 0:
+      continue
+    fully_qualified_class = jni_generator.ExtractFullyQualifiedJavaClassName(
+        path, contents)
+    main_dex = jni_generator.IsMainDexJavaClass(contents)
+    header_generator = HeaderGenerator(
+        fully_qualified_class, registration_dict, main_dex)
+    registration_dict = header_generator.GetContent()
+
+  header_content = CreateFromDict(registration_dict)
+  if output_file:
+    jni_generator.WriteOutput(output_file, header_content)
+  else:
+    print header_content
+
+
+def CreateFromDict(registration_dict):
+  """Returns the content of the header file."""
+
+  template = string.Template("""\
+// Copyright 2017 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.
+
+
+// This file is autogenerated by
+//     base/android/jni_generator/jni_registration_generator.py
+// Please do not change its content.
+
+#ifndef HEADER_GUARD
+#define HEADER_GUARD
+
+#include <jni.h>
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+#include "base/android/jni_int_wrapper.h"
+
+// Step 1: Forward declaration.
+${FORWARD_DECLARATIONS}
+
+// Step 2: Main dex and non-main dex registration functions.
+
+bool RegisterMainDexNatives(JNIEnv* env) {
+${REGISTER_MAIN_DEX_NATIVES}
+
+  return true;
+}
+
+bool RegisterNonMainDexNatives(JNIEnv* env) {
+${REGISTER_NON_MAIN_DEX_NATIVES}
+
+  return true;
+}
+
+#endif  // HEADER_GUARD
+""")
+  if len(registration_dict['FORWARD_DECLARATIONS']) == 0:
+    return ''
+  return jni_generator.WrapOutput(template.substitute(registration_dict))
+
+
+class HeaderGenerator(object):
+  """Generates an inline header file for JNI registration."""
+
+  def __init__(self, fully_qualified_class, registration_dict, main_dex):
+    self.fully_qualified_class = fully_qualified_class
+    self.class_name = self.fully_qualified_class.split('/')[-1]
+    self.registration_dict = registration_dict
+    self.main_dex = main_dex
+
+  def GetContent(self):
+    self._AddForwardDeclaration()
+    self._AddRegisterNatives()
+    return self.registration_dict
+
+  def _AddForwardDeclaration(self):
+    """Add the content of the forward declaration to the dictionary."""
+    template = string.Template('JNI_REGISTRATION_EXPORT bool ${METHOD_NAME}('
+                               'JNIEnv* env);\n')
+    value = {
+        'METHOD_NAME':
+            jni_generator.GetRegistrationFunctionName(
+                self.fully_qualified_class)
+    }
+    self.registration_dict['FORWARD_DECLARATIONS'] += template.substitute(value)
+
+  def _AddRegisterNatives(self):
+    """Add the body of the RegisterNativesImpl method to the dictionary."""
+    template = string.Template("""
+if (!${REGISTER_NAME}(env))
+  return false;
+""")
+    value = {
+        'REGISTER_NAME':
+            jni_generator.GetRegistrationFunctionName(
+                self.fully_qualified_class)
+    }
+    register_body = template.substitute(value)
+    if self.main_dex:
+      self.registration_dict['REGISTER_MAIN_DEX_NATIVES'] += register_body
+    else:
+      self.registration_dict['REGISTER_NON_MAIN_DEX_NATIVES'] += register_body
+
+
+def main(argv):
+  arg_parser = argparse.ArgumentParser()
+  build_utils.AddDepfileOption(arg_parser)
+
+  arg_parser.add_argument('--sources_files',
+                          help='A list of .sources files which contain Java '
+                          'file paths. Must be used with --output.')
+  arg_parser.add_argument('--output',
+                          help='The output file path.')
+  arg_parser.add_argument('--no_register_java',
+                          help='A list of Java files which should be ignored '
+                          'by the parser.')
+  args = arg_parser.parse_args(build_utils.ExpandFileArgs(argv[1:]))
+  args.sources_files = build_utils.ParseGnList(args.sources_files)
+
+  if args.sources_files:
+    java_file_paths = []
+    for f in args.sources_files:
+      # java_file_paths stores each Java file path as a string.
+      java_file_paths += build_utils.ReadSourcesList(f)
+  else:
+    print '\nError: Must specify --sources_files.'
+    return 1
+  output_file = args.output
+  GenerateJNIHeader(java_file_paths, output_file, args)
+
+  if args.depfile:
+    build_utils.WriteDepfile(args.depfile, output_file)
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv))
diff --git a/base/android/jni_generator/testInnerClassNatives.golden b/base/android/jni_generator/testInnerClassNatives.golden
index 20b8830..363f916 100644
--- a/base/android/jni_generator/testInnerClassNatives.golden
+++ b/base/android/jni_generator/testInnerClassNatives.golden
@@ -51,11 +51,16 @@
     },
 };
 
+// TODO(agrieve): Remove these empty registration functions and functions
+// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  if (jni_generator::ShouldSkipJniRegistration(false))
-    return true;
+  return true;
+}
 
-  const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass);
+JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_TestJni(JNIEnv* env) {
+
+  const int kMethodsMyInnerClassSize =
+      arraysize(kMethodsMyInnerClass);
 
   if (env->RegisterNatives(MyInnerClass_clazz(env),
                            kMethodsMyInnerClass,
diff --git a/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden b/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden
index 67352e7..6c07b58 100644
--- a/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden
+++ b/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden
@@ -67,9 +67,13 @@
 "I", reinterpret_cast<void*>(Java_org_chromium_TestJni_nativeInit) },
 };
 
+// TODO(agrieve): Remove these empty registration functions and functions
+// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  if (jni_generator::ShouldSkipJniRegistration(false))
-    return true;
+  return true;
+}
+
+JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_TestJni(JNIEnv* env) {
 
   const int kMethodsMyOtherInnerClassSize =
       arraysize(kMethodsMyOtherInnerClass);
@@ -82,7 +86,8 @@
     return false;
   }
 
-  const int kMethodsTestJniSize = arraysize(kMethodsTestJni);
+  const int kMethodsTestJniSize =
+      arraysize(kMethodsTestJni);
 
   if (env->RegisterNatives(TestJni_clazz(env),
                            kMethodsTestJni,
diff --git a/base/android/jni_generator/testInnerClassNativesMultiple.golden b/base/android/jni_generator/testInnerClassNativesMultiple.golden
index 7807efa..add5ac9 100644
--- a/base/android/jni_generator/testInnerClassNativesMultiple.golden
+++ b/base/android/jni_generator/testInnerClassNativesMultiple.golden
@@ -74,9 +74,13 @@
     },
 };
 
+// TODO(agrieve): Remove these empty registration functions and functions
+// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  if (jni_generator::ShouldSkipJniRegistration(false))
-    return true;
+  return true;
+}
+
+JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_TestJni(JNIEnv* env) {
 
   const int kMethodsMyOtherInnerClassSize =
       arraysize(kMethodsMyOtherInnerClass);
@@ -89,7 +93,8 @@
     return false;
   }
 
-  const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass);
+  const int kMethodsMyInnerClassSize =
+      arraysize(kMethodsMyInnerClass);
 
   if (env->RegisterNatives(MyInnerClass_clazz(env),
                            kMethodsMyInnerClass,
diff --git a/base/android/jni_generator/testMainDexFile.golden b/base/android/jni_generator/testMainDexFile.golden
deleted file mode 100644
index cbb2a7d..0000000
--- a/base/android/jni_generator/testMainDexFile.golden
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2014 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.
-
-// This file is autogenerated by
-//     base/android/jni_generator/jni_generator.py
-// For
-//     org/chromium/foo/Bar
-
-#ifndef org_chromium_foo_Bar_JNI
-#define org_chromium_foo_Bar_JNI
-
-#include <jni.h>
-
-#include "base/android/jni_generator/jni_generator_helper.h"
-
-#include "base/android/jni_int_wrapper.h"
-
-// Step 1: forward declarations.
-namespace {
-const char kBarClassPath[] = "org/chromium/foo/Bar";
-// Leaking this jclass as we cannot use LazyInstance from some threads.
-base::subtle::AtomicWord g_Bar_clazz __attribute__((unused)) = 0;
-#define Bar_clazz(env) base::android::LazyGetClass(env, kBarClassPath, &g_Bar_clazz)
-
-}  // namespace
-
-// Step 2: method stubs.
-JNI_GENERATOR_EXPORT jint Java_org_chromium_foo_Bar_nativeStaticMethod(JNIEnv*
-    env, jobject jcaller,
-    jlong nativeTest,
-    jint arg1) {
-  Test* native = reinterpret_cast<Test*>(nativeTest);
-  CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0);
-  return native->StaticMethod(env, base::android::JavaParamRef<jobject>(env,
-      jcaller), arg1);
-}
-
-// Step 3: RegisterNatives.
-
-static const JNINativeMethod kMethodsBar[] = {
-    { "nativeStaticMethod",
-"("
-"J"
-"I"
-")"
-"I", reinterpret_cast<void*>(Java_org_chromium_foo_Bar_nativeStaticMethod) },
-};
-
-static bool RegisterNativesImpl(JNIEnv* env) {
-  if (jni_generator::ShouldSkipJniRegistration(true))
-    return true;
-
-  const int kMethodsBarSize = arraysize(kMethodsBar);
-
-  if (env->RegisterNatives(Bar_clazz(env),
-                           kMethodsBar,
-                           kMethodsBarSize) < 0) {
-    jni_generator::HandleRegistrationError(
-        env, Bar_clazz(env), __FILE__);
-    return false;
-  }
-
-  return true;
-}
-
-#endif  // org_chromium_foo_Bar_JNI
diff --git a/base/android/jni_generator/testMultipleJNIAdditionalImport.golden b/base/android/jni_generator/testMultipleJNIAdditionalImport.golden
index 0eecb5a..03aac48e 100644
--- a/base/android/jni_generator/testMultipleJNIAdditionalImport.golden
+++ b/base/android/jni_generator/testMultipleJNIAdditionalImport.golden
@@ -75,11 +75,16 @@
 "V", reinterpret_cast<void*>(Java_org_chromium_foo_Foo_nativeDoSomething) },
 };
 
+// TODO(agrieve): Remove these empty registration functions and functions
+// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  if (jni_generator::ShouldSkipJniRegistration(false))
-    return true;
+  return true;
+}
 
-  const int kMethodsFooSize = arraysize(kMethodsFoo);
+JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_foo_Foo(JNIEnv* env) {
+
+  const int kMethodsFooSize =
+      arraysize(kMethodsFoo);
 
   if (env->RegisterNatives(Foo_clazz(env),
                            kMethodsFoo,
diff --git a/base/android/jni_generator/testNatives.golden b/base/android/jni_generator/testNatives.golden
index 3362c928..0bfbe04 100644
--- a/base/android/jni_generator/testNatives.golden
+++ b/base/android/jni_generator/testNatives.golden
@@ -320,11 +320,16 @@
     },
 };
 
+// TODO(agrieve): Remove these empty registration functions and functions
+// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  if (jni_generator::ShouldSkipJniRegistration(false))
-    return true;
+  return true;
+}
 
-  const int kMethodsTestJniSize = arraysize(kMethodsTestJni);
+JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_TestJni(JNIEnv* env) {
+
+  const int kMethodsTestJniSize =
+      arraysize(kMethodsTestJni);
 
   if (env->RegisterNatives(TestJni_clazz(env),
                            kMethodsTestJni,
diff --git a/base/android/jni_generator/testNativesLong.golden b/base/android/jni_generator/testNativesLong.golden
index ec029ce..201a066 100644
--- a/base/android/jni_generator/testNativesLong.golden
+++ b/base/android/jni_generator/testNativesLong.golden
@@ -46,11 +46,16 @@
 "V", reinterpret_cast<void*>(Java_org_chromium_TestJni_nativeDestroy) },
 };
 
+// TODO(agrieve): Remove these empty registration functions and functions
+// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  if (jni_generator::ShouldSkipJniRegistration(false))
-    return true;
+  return true;
+}
 
-  const int kMethodsTestJniSize = arraysize(kMethodsTestJni);
+JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_TestJni(JNIEnv* env) {
+
+  const int kMethodsTestJniSize =
+      arraysize(kMethodsTestJni);
 
   if (env->RegisterNatives(TestJni_clazz(env),
                            kMethodsTestJni,
diff --git a/base/android/jni_generator/testNonMainDexFile.golden b/base/android/jni_generator/testNonMainDexFile.golden
deleted file mode 100644
index 533241e..0000000
--- a/base/android/jni_generator/testNonMainDexFile.golden
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2014 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.
-
-// This file is autogenerated by
-//     base/android/jni_generator/jni_generator.py
-// For
-//     org/chromium/foo/Bar
-
-#ifndef org_chromium_foo_Bar_JNI
-#define org_chromium_foo_Bar_JNI
-
-#include <jni.h>
-
-#include "base/android/jni_generator/jni_generator_helper.h"
-
-#include "base/android/jni_int_wrapper.h"
-
-// Step 1: forward declarations.
-namespace {
-const char kBarClassPath[] = "org/chromium/foo/Bar";
-// Leaking this jclass as we cannot use LazyInstance from some threads.
-base::subtle::AtomicWord g_Bar_clazz __attribute__((unused)) = 0;
-#define Bar_clazz(env) base::android::LazyGetClass(env, kBarClassPath, &g_Bar_clazz)
-
-}  // namespace
-
-// Step 2: method stubs.
-JNI_GENERATOR_EXPORT jint Java_org_chromium_foo_Bar_nativeStaticMethod(JNIEnv*
-    env, jobject jcaller,
-    jlong nativeTest,
-    jint arg1) {
-  Test* native = reinterpret_cast<Test*>(nativeTest);
-  CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0);
-  return native->StaticMethod(env, base::android::JavaParamRef<jobject>(env,
-      jcaller), arg1);
-}
-
-// Step 3: RegisterNatives.
-
-static const JNINativeMethod kMethodsBar[] = {
-    { "nativeStaticMethod",
-"("
-"J"
-"I"
-")"
-"I", reinterpret_cast<void*>(Java_org_chromium_foo_Bar_nativeStaticMethod) },
-};
-
-static bool RegisterNativesImpl(JNIEnv* env) {
-  if (jni_generator::ShouldSkipJniRegistration(false))
-    return true;
-
-  const int kMethodsBarSize = arraysize(kMethodsBar);
-
-  if (env->RegisterNatives(Bar_clazz(env),
-                           kMethodsBar,
-                           kMethodsBarSize) < 0) {
-    jni_generator::HandleRegistrationError(
-        env, Bar_clazz(env), __FILE__);
-    return false;
-  }
-
-  return true;
-}
-
-#endif  // org_chromium_foo_Bar_JNI
diff --git a/base/android/jni_generator/testSingleJNIAdditionalImport.golden b/base/android/jni_generator/testSingleJNIAdditionalImport.golden
index ef618da8..a367ae7 100644
--- a/base/android/jni_generator/testSingleJNIAdditionalImport.golden
+++ b/base/android/jni_generator/testSingleJNIAdditionalImport.golden
@@ -69,11 +69,16 @@
 "V", reinterpret_cast<void*>(Java_org_chromium_foo_Foo_nativeDoSomething) },
 };
 
+// TODO(agrieve): Remove these empty registration functions and functions
+// calling them. https://crbug.com/683256.
 static bool RegisterNativesImpl(JNIEnv* env) {
-  if (jni_generator::ShouldSkipJniRegistration(false))
-    return true;
+  return true;
+}
 
-  const int kMethodsFooSize = arraysize(kMethodsFoo);
+JNI_REGISTRATION_EXPORT bool RegisterNative_org_chromium_foo_Foo(JNIEnv* env) {
+
+  const int kMethodsFooSize =
+      arraysize(kMethodsFoo);
 
   if (env->RegisterNatives(Foo_clazz(env),
                            kMethodsFoo,
diff --git a/build/android/gradle/generate_gradle.py b/build/android/gradle/generate_gradle.py
index e7bf827..ba2311a 100755
--- a/build/android/gradle/generate_gradle.py
+++ b/build/android/gradle/generate_gradle.py
@@ -211,7 +211,7 @@
 
   def JavaFiles(self):
     if self._java_files is None:
-      java_sources_file = self.Gradle().get('java_sources_file')
+      java_sources_file = self.DepsInfo().get('java_sources_file')
       java_files = []
       if java_sources_file:
         java_sources_file = _RebasePath(java_sources_file)
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index c085176..6eab4b2 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -413,7 +413,7 @@
     gradle['android_manifest'] = options.android_manifest
   if options.type in ('java_binary', 'java_library', 'android_apk'):
     if options.java_sources_file:
-      gradle['java_sources_file'] = options.java_sources_file
+      deps_info['java_sources_file'] = options.java_sources_file
     if options.bundled_srcjars:
       gradle['bundled_srcjars'] = (
           build_utils.ParseGnList(options.bundled_srcjars))
@@ -436,6 +436,14 @@
         gradle['dependent_java_projects'].append(c['path'])
 
 
+  if options.type == 'android_apk':
+    config['jni'] = {}
+    all_java_sources = [c['java_sources_file'] for c in all_library_deps
+                        if 'java_sources_file' in c]
+    if options.java_sources_file:
+      all_java_sources.append(options.java_sources_file)
+    config['jni']['all_source'] = all_java_sources
+
   if (options.type in ('java_binary', 'java_library')):
     deps_info['requires_android'] = options.requires_android
     deps_info['supports_android'] = options.supports_android
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 38032b768..ceffd06 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -192,7 +192,6 @@
         "--depfile",
         rebase_path(depfile, root_build_dir),
         "--input_file={{source}}",
-        "--optimize_generation=1",
         "--ptr_type=long",
         "--output_dir",
         rebase_path(jni_output_dir, root_build_dir),
@@ -302,7 +301,6 @@
           rebase_path(jar_file, root_build_dir),
           "--input_file",
           class,
-          "--optimize_generation=1",
           "--ptr_type=long",
           "--output_dir",
           rebase_path(jni_output_dir, root_build_dir),
@@ -334,6 +332,61 @@
     }
   }
 
+  # Declare a jni registration target.
+  #
+  # This target generates a header file calling JNI registration functions
+  # created by generate_jni and generate_jar_jni.
+  #
+  # See base/android/jni_generator/jni_registration_generator.py for more info
+  # about the format of the header file.
+  #
+  # Variables
+  #   target: The Apk target to generate registrations for.
+  #   output: Path to the generated .h file.
+  #   exception_files: List of .java files that should be ignored when searching
+  #   for native methods. (optional)
+  #
+  # Example
+  #   generate_jni_registration("chrome_jni_registration") {
+  #     target = ":chrome_public_apk"
+  #     output = "$root_gen_dir/chrome/browser/android/${target_name}.h"
+  #     exception_files = [
+  #       "//base/android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
+  #       "//base/android/java/src/org/chromium/base/library_loader/Linker.java",
+  #       "//base/android/java/src/org/chromium/base/library_loader/ModernLinker.java",
+  #     ]
+  #   }
+  template("generate_jni_registration") {
+    action(target_name) {
+      forward_variables_from(invoker, [ "testonly" ])
+      _build_config = get_label_info(invoker.target, "target_gen_dir") + "/" +
+                      get_label_info(invoker.target, "name") + ".build_config"
+      _rebased_build_config = rebase_path(_build_config, root_build_dir)
+
+      _rebase_exception_java_files =
+          rebase_path(invoker.exception_files, root_build_dir)
+
+      script = "//base/android/jni_generator/jni_registration_generator.py"
+      deps = [
+        "${invoker.target}__build_config",
+      ]
+      inputs = [
+        _build_config,
+      ]
+      outputs = [
+        invoker.output,
+      ]
+
+      args = [
+        # This is a list of .sources files.
+        "--sources_files=@FileArg($_rebased_build_config:jni:all_source)",
+        "--output",
+        rebase_path(invoker.output, root_build_dir),
+        "--no_register_java=$_rebase_exception_java_files",
+      ]
+    }
+  }
+
   # Declare a target for c-preprocessor-generated java files
   #
   # NOTE: For generating Java conterparts to enums prefer using the java_cpp_enum
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index d8fe989..1c3aa01 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1765,6 +1765,7 @@
     # find the native side functions.
     if (is_android && is_component_build) {
       deps += [
+        "//device/gamepad",
         "//device/generic_sensor",
         "//device/sensors",
       ]
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index b80a1d6f..c59688c1 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//base/android/jni_generator/jni_exception_list.gni")
 import("//build/config/android/config.gni")
 import("//build/config/android/rules.gni")
 import("//build/util/process_version.gni")
@@ -672,6 +673,7 @@
     "../browser/android/chrome_entry_point.cc",
   ]
   deps = [
+    ":chrome_jni_registration($default_toolchain)",
     "//build/config:exe_and_shlib_deps",
     "//chrome:chrome_android_core",
   ]
@@ -696,8 +698,22 @@
 }
 
 # Ensure that .pak files are built only once (build them in the default
-# toolchain).
+# toolchain). The central header file calling JNI registration functions
+# is generated from Java code so it just needs to be generated once.
 if (current_toolchain == default_toolchain) {
+  generate_jni_registration("chrome_jni_registration") {
+    target = ":chrome_public_apk"
+    output = "$root_gen_dir/chrome/browser/android/${target_name}.h"
+    exception_files = jni_exception_files
+  }
+
+  generate_jni_registration("chrome_sync_shell_jni_registration") {
+    testonly = true
+    target = ":chrome_sync_shell_apk"
+    output = "$root_gen_dir/chrome/browser/android/${target_name}.h"
+    exception_files = jni_exception_files
+  }
+
   if (enable_resource_whitelist_generation) {
     generate_resource_whitelist("monochrome_resource_whitelist") {
       # Always use the 32-bit library's whitelist since the 64-bit one is
@@ -793,12 +809,13 @@
 shared_library("chrome_sync_shell") {
   testonly = true
   sources = [
-    "../browser/android/chrome_entry_point.cc",
+    "../browser/android/chrome_sync_shell_entry_point.cc",
     "../browser/android/chrome_sync_shell_main_delegate.cc",
     "../browser/android/chrome_sync_shell_main_delegate.h",
     "../browser/android/chrome_sync_shell_main_delegate_initializer.cc",
   ]
   deps = [
+    ":chrome_sync_shell_jni_registration($default_toolchain)",
     "//build/config:exe_and_shlib_deps",
     "//chrome:chrome_android_core",
     "//components/sync",
@@ -879,6 +896,9 @@
   apk_name = "ChromeSyncShell"
   shared_libraries = [ ":chrome_sync_shell" ]
 
+  # This exists here rather than in chrome_sync_shell_test_apk for JNI
+  # registration to be able to find the native side functions.
+  java_files = [ "sync_shell/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java" ]
   deps = [
     ":chrome_sync_shell_apk_template_resources",
 
@@ -886,6 +906,7 @@
     # but that code is stripped out via proguard. Adding this deps adds
     # usages and prevents removal of the proto code.
     "//components/sync:test_support_proto_java",
+    "//third_party/android_protobuf:protobuf_nano_javalib",
   ]
 }
 
@@ -979,11 +1000,23 @@
   }
 }
 
-android_library("chrome_sync_shell_test_apk_java") {
-  testonly = true
-
-  # From java_sources.jni.
-  java_files = sync_shell_test_java_sources
+instrumentation_test_apk("chrome_sync_shell_test_apk") {
+  apk_name = "ChromeSyncShellTest"
+  apk_under_test = ":chrome_sync_shell_apk"
+  android_manifest = chrome_sync_shell_test_apk_manifest
+  android_manifest_dep = ":chrome_sync_shell_test_apk_manifest"
+  java_files = [
+    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/AutofillTest.java",
+    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java",
+    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/FirstRunTest.java",
+    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/GmsCoreSyncListenerTest.java",
+    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/OpenTabsTest.java",
+    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java",
+    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTest.java",
+    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java",
+    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/TypedUrlsTest.java",
+    "sync_shell/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragmentTest.java",
+  ]
 
   deps = [
     "//base:base_java",
@@ -999,21 +1032,9 @@
     "//components/sync/android:sync_java",
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
-    "//third_party/android_protobuf:protobuf_nano_javalib",
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/android_tools:android_support_v7_appcompat_java",
     "//ui/android:ui_java",
   ]
-}
-
-instrumentation_test_apk("chrome_sync_shell_test_apk") {
-  apk_name = "ChromeSyncShellTest"
-  apk_under_test = ":chrome_sync_shell_apk"
-  android_manifest = chrome_sync_shell_test_apk_manifest
-  android_manifest_dep = ":chrome_sync_shell_test_apk_manifest"
-  deps = [
-    ":chrome_sync_shell_test_apk_java",
-    "//third_party/android_support_test_runner:runner_java",
-  ]
   proguard_enabled = !is_java_debug
 }
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index fe6f03b..c76bfb3a 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1819,20 +1819,6 @@
   "junit/src/org/chromium/chrome/browser/widget/selection/SelectionDelegateTest.java",
 ]
 
-sync_shell_test_java_sources = [
-  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/AutofillTest.java",
-  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/BookmarksTest.java",
-  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/FakeServerHelper.java",
-  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/FirstRunTest.java",
-  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/GmsCoreSyncListenerTest.java",
-  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/OpenTabsTest.java",
-  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncCustomizationFragmentTest.java",
-  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTest.java",
-  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/SyncTestBase.java",
-  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/TypedUrlsTest.java",
-  "sync_shell/javatests/src/org/chromium/chrome/browser/sync/ui/PassphraseTypeDialogFragmentTest.java",
-]
-
 # Only used for testing, should not be shipped to end users.
 if (enable_offline_pages_harness) {
   chrome_java_sources += [ "java/src/org/chromium/chrome/browser/offlinepages/evaluation/OfflinePageEvaluationBridge.java" ]
diff --git a/chrome/browser/android/DEPS b/chrome/browser/android/DEPS
index bb55a8c..8f032b7 100644
--- a/chrome/browser/android/DEPS
+++ b/chrome/browser/android/DEPS
@@ -2,6 +2,7 @@
   "-components/devtools_bridge",
   "+cc/layers/layer.h",
   "+cc/output/context_provider.h",
+  "+chrome_jni_registration/chrome_jni_registration.h",
   "+components/doodle",
   "+components/ntp_snippets",
   "+components/spellcheck/browser",
diff --git a/chrome/browser/android/chrome_entry_point.cc b/chrome/browser/android/chrome_entry_point.cc
index ee46cd6c..079358b 100644
--- a/chrome/browser/android/chrome_entry_point.cc
+++ b/chrome/browser/android/chrome_entry_point.cc
@@ -7,6 +7,7 @@
 #include "base/android/library_loader/library_loader_hooks.h"
 #include "base/bind.h"
 #include "chrome/app/android/chrome_jni_onload.h"
+#include "chrome/browser/android/chrome_jni_registration.h"
 
 namespace {
 
@@ -23,6 +24,19 @@
   // Java side and only register a subset of JNI methods.
   base::android::InitVM(vm);
   JNIEnv* env = base::android::AttachCurrentThread();
+
+  if (!base::android::IsSelectiveJniRegistrationEnabled(env)) {
+    if (!RegisterNonMainDexNatives(env)) {
+      return -1;
+    }
+  }
+
+  if (!RegisterMainDexNatives(env)) {
+    return -1;
+  }
+
+  // TODO(agrieve): Delete this block, this is a no-op now.
+  // https://crbug.com/683256.
   if (base::android::IsSelectiveJniRegistrationEnabled(env)) {
     base::android::SetJniRegistrationType(
         base::android::SELECTIVE_JNI_REGISTRATION);
diff --git a/chrome/browser/android/chrome_sync_shell_entry_point.cc b/chrome/browser/android/chrome_sync_shell_entry_point.cc
new file mode 100644
index 0000000..e14ea511
--- /dev/null
+++ b/chrome/browser/android/chrome_sync_shell_entry_point.cc
@@ -0,0 +1,49 @@
+// Copyright 2017 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/android/jni_android.h"
+#include "base/android/jni_utils.h"
+#include "base/android/library_loader/library_loader_hooks.h"
+#include "base/bind.h"
+#include "chrome/app/android/chrome_jni_onload.h"
+#include "chrome/browser/android/chrome_sync_shell_jni_registration.h"
+
+namespace {
+
+bool NativeInit() {
+  return android::OnJNIOnLoadInit();
+}
+
+}  // namespace
+
+// This is called by the VM when the shared library is first loaded.
+JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+  // By default, all JNI methods are registered. However, since render processes
+  // don't need very much Java code, we enable selective JNI registration on the
+  // Java side and only register a subset of JNI methods.
+  base::android::InitVM(vm);
+  JNIEnv* env = base::android::AttachCurrentThread();
+
+  if (!base::android::IsSelectiveJniRegistrationEnabled(env)) {
+    if (!RegisterNonMainDexNatives(env)) {
+      return -1;
+    }
+  }
+
+  if (!RegisterMainDexNatives(env)) {
+    return -1;
+  }
+
+  // TODO(agrieve): Delete this block, this is a no-op now.
+  // https://crbug.com/683256.
+  if (base::android::IsSelectiveJniRegistrationEnabled(env)) {
+    base::android::SetJniRegistrationType(
+        base::android::SELECTIVE_JNI_REGISTRATION);
+  }
+  if (!android::OnJNIOnLoadRegisterJNI(env)) {
+    return -1;
+  }
+  base::android::SetNativeInitializationHook(NativeInit);
+  return JNI_VERSION_1_4;
+}
diff --git a/chrome/browser/media/android/remote/remote_media_player_bridge.cc b/chrome/browser/media/android/remote/remote_media_player_bridge.cc
index 531e3f4..6c3e609 100644
--- a/chrome/browser/media/android/remote/remote_media_player_bridge.cc
+++ b/chrome/browser/media/android/remote/remote_media_player_bridge.cc
@@ -262,7 +262,6 @@
 // static
 bool RemoteMediaPlayerBridge::RegisterRemoteMediaPlayerBridge(JNIEnv* env) {
   bool ret = RegisterNativesImpl(env);
-  DCHECK(g_RemoteMediaPlayerBridge_clazz);
   return ret;
 }
 
diff --git a/chrome/browser/media/android/router/media_router_android_bridge.cc b/chrome/browser/media/android/router/media_router_android_bridge.cc
index e56bbb5..4c57cde 100644
--- a/chrome/browser/media/android/router/media_router_android_bridge.cc
+++ b/chrome/browser/media/android/router/media_router_android_bridge.cc
@@ -29,7 +29,6 @@
 // static
 bool MediaRouterAndroidBridge::Register(JNIEnv* env) {
   bool ret = RegisterNativesImpl(env);
-  DCHECK(g_ChromeMediaRouter_clazz);
   return ret;
 }
 
diff --git a/chromecast/BUILD.gn b/chromecast/BUILD.gn
index 08cbad2..3d10c9cd 100644
--- a/chromecast/BUILD.gn
+++ b/chromecast/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//base/android/jni_generator/jni_exception_list.gni")
 import("//build/buildflag_header.gni")
 import("//build/config/features.gni")
 import("//build/config/sanitizers/sanitizers.gni")
@@ -481,6 +482,11 @@
 }
 
 if (is_android) {
+  generate_jni_registration("cast_shell_jni_registration") {
+    target = ":cast_shell_apk"
+    output = "$root_gen_dir/chromecast/android/${target_name}.h"
+    exception_files = jni_exception_files
+  }
   android_assets("cast_shell_apk_assets") {
     assert(v8_use_external_startup_data)
 
diff --git a/chromecast/android/BUILD.gn b/chromecast/android/BUILD.gn
index 1d29974..321a3d2 100644
--- a/chromecast/android/BUILD.gn
+++ b/chromecast/android/BUILD.gn
@@ -27,6 +27,7 @@
   deps = [
     ":platform_jni_loader",
     "//base",
+    "//chromecast:cast_shell_jni_registration",
     "//chromecast:cast_shell_lib",
     "//chromecast:chromecast_features",
     "//chromecast/app",
diff --git a/chromecast/app/android/cast_jni_loader.cc b/chromecast/app/android/cast_jni_loader.cc
index 79a32fe..73b829bb 100644
--- a/chromecast/app/android/cast_jni_loader.cc
+++ b/chromecast/app/android/cast_jni_loader.cc
@@ -6,6 +6,7 @@
 #include "base/android/library_loader/library_loader_hooks.h"
 #include "base/bind.h"
 #include "chromecast/android/cast_jni_registrar.h"
+#include "chromecast/android/cast_shell_jni_registration.h"
 #include "chromecast/android/platform_jni_loader.h"
 #include "chromecast/app/cast_main_delegate.h"
 #include "chromecast/browser/android/jni_registrar.h"
@@ -42,6 +43,12 @@
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
   base::android::InitVM(vm);
   JNIEnv* env = base::android::AttachCurrentThread();
+  if (!RegisterMainDexNatives(env) || !RegisterNonMainDexNatives(env)) {
+    return -1;
+  }
+
+  // TODO(agrieve): Delete this block, this is a no-op now.
+  // https://crbug.com/683256.
   if (!content::android::OnJNIOnLoadRegisterJNI(env) || !RegisterJNI(env) ||
       !NativeInit()) {
     return -1;
diff --git a/components/cronet/android/BUILD.gn b/components/cronet/android/BUILD.gn
index b5f646d..230ecf0 100644
--- a/components/cronet/android/BUILD.gn
+++ b/components/cronet/android/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//base/android/jni_generator/jni_exception_list.gni")
 import("//build/buildflag_header.gni")
 import("//build/config/android/config.gni")
 import("//build/config/android/rules.gni")
@@ -25,6 +26,12 @@
   jni_package = "cronet"
 }
 
+generate_jni_registration("cronet_jni_registration") {
+  target = ":cronet_sample_apk"
+  output = "$root_gen_dir/components/cronet/android/${target_name}.h"
+  exception_files = jni_exception_files
+}
+
 java_cpp_enum("effective_connection_type_java") {
   sources = [
     "//net/nqe/effective_connection_type.h",
@@ -165,6 +172,7 @@
     deps = [
       ":cronet_android_cert_proto",
       ":cronet_jni_headers",
+      ":cronet_jni_registration",
       ":cronet_version_header",
       "//base",
       "//base/third_party/dynamic_annotations",
@@ -532,6 +540,9 @@
   ]
 
   include_dirs = [ _cronet_version_header_include_dir ]
+
+  configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
+  configs += [ "//build/config/android:hide_all_but_jni" ]
 }
 
 android_resources("cronet_test_apk_resources") {
diff --git a/components/cronet/android/cronet_library_loader.cc b/components/cronet/android/cronet_library_loader.cc
index 72c416e..b7aa07a 100644
--- a/components/cronet/android/cronet_library_loader.cc
+++ b/components/cronet/android/cronet_library_loader.cc
@@ -19,6 +19,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/statistics_recorder.h"
 #include "components/cronet/android/cronet_bidirectional_stream_adapter.h"
+#include "components/cronet/android/cronet_jni_registration.h"
 #include "components/cronet/android/cronet_upload_data_stream_adapter.h"
 #include "components/cronet/android/cronet_url_request_adapter.h"
 #include "components/cronet/android/cronet_url_request_context_adapter.h"
@@ -84,6 +85,11 @@
 jint CronetOnLoad(JavaVM* vm, void* reserved) {
   base::android::InitVM(vm);
   JNIEnv* env = base::android::AttachCurrentThread();
+  if (!RegisterMainDexNatives(env) || !RegisterNonMainDexNatives(env)) {
+    return -1;
+  }
+  // TODO(agrieve): Delete this block, this is a no-op now.
+  // https://crbug.com/683256.
   if (!RegisterJNI(env) || !NativeInit()) {
     return -1;
   }
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn
index 2a0443ee..2c140859 100644
--- a/content/shell/android/BUILD.gn
+++ b/content/shell/android/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//base/android/jni_generator/jni_exception_list.gni")
 import("//build/config/android/config.gni")
 import("//build/config/android/rules.gni")
 import("//third_party/icu/config.gni")
@@ -15,10 +16,18 @@
   ]
 }
 
+generate_jni_registration("content_shell_jni_registration") {
+  testonly = true
+  target = ":content_shell_apk"
+  output = "$root_gen_dir/content/shell/android/${target_name}.h"
+  exception_files = jni_exception_files
+}
+
 shared_library("libcontent_shell_content_view") {
   testonly = true
   deps = [
     ":content_shell_jni_headers",
+    ":content_shell_jni_registration",
     "//build/config:exe_and_shlib_deps",
     "//components/crash/content/browser",
     "//content/shell:content_shell_lib",
@@ -26,6 +35,17 @@
     "//media",
     "//skia",
   ]
+
+  # Explicit dependency required for JNI registration to be able to
+  # find the native side functions.
+  if (is_component_build) {
+    deps += [
+      "//device/gamepad",
+      "//device/generic_sensor",
+      "//device/sensors",
+      "//media/midi",
+    ]
+  }
   sources = [
     "shell_library_loader.cc",
   ]
@@ -242,6 +262,7 @@
 
     deps = [
       ":linker_test_jni_headers",
+      ":linker_test_jni_registration",
       "//build/config:exe_and_shlib_deps",
       "//content/shell:content_shell_lib",
 
@@ -250,6 +271,17 @@
       "//skia",
       "//third_party/re2",
     ]
+
+    # Explicit dependency required for JNI registration to be able to
+    # find the native side functions.
+    if (is_component_build) {
+      deps += [
+        "//device/gamepad",
+        "//device/generic_sensor",
+        "//device/sensors",
+        "//media/midi",
+      ]
+    }
   }
 
   generate_jni("linker_test_jni_headers") {
@@ -259,6 +291,14 @@
       "linker_test_apk/src/org/chromium/chromium_linker_test_apk/LinkerTests.java",
     ]
   }
+
+  generate_jni_registration("linker_test_jni_registration") {
+    testonly = true
+    target = ":chromium_linker_test_apk__apk"
+    output =
+        "$root_gen_dir/content/shell/android/linker_test_apk/${target_name}.h"
+    exception_files = jni_exception_files
+  }
 }
 
 android_library("content_shell_browsertests_java") {
diff --git a/content/shell/android/linker_test_apk/chromium_linker_test_android.cc b/content/shell/android/linker_test_apk/chromium_linker_test_android.cc
index 19f9c47..310bb2a 100644
--- a/content/shell/android/linker_test_apk/chromium_linker_test_android.cc
+++ b/content/shell/android/linker_test_apk/chromium_linker_test_android.cc
@@ -9,6 +9,7 @@
 #include "content/public/app/content_main.h"
 #include "content/public/browser/android/compositor.h"
 #include "content/shell/android/linker_test_apk/chromium_linker_test_linker_tests.h"
+#include "content/shell/android/linker_test_apk/linker_test_jni_registration.h"
 #include "content/shell/android/shell_jni_registrar.h"
 #include "content/shell/app/shell_main_delegate.h"
 
@@ -40,6 +41,11 @@
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
   base::android::InitVM(vm);
   JNIEnv* env = base::android::AttachCurrentThread();
+  if (!RegisterMainDexNatives(env) || !RegisterNonMainDexNatives(env)) {
+    return -1;
+  }
+  // TODO(agrieve): Delete this block, this is a no-op now.
+  // https://crbug.com/683256.
   if (!content::android::OnJNIOnLoadRegisterJNI(env) || !RegisterJNI(env) ||
       !NativeInit()) {
     return -1;
diff --git a/content/shell/android/shell_library_loader.cc b/content/shell/android/shell_library_loader.cc
index dc88bd67..aaf1b55a 100644
--- a/content/shell/android/shell_library_loader.cc
+++ b/content/shell/android/shell_library_loader.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2012 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.
 
@@ -8,6 +8,7 @@
 #include "content/public/app/content_jni_onload.h"
 #include "content/public/app/content_main.h"
 #include "content/public/browser/android/compositor.h"
+#include "content/shell/android/content_shell_jni_registration.h"
 #include "content/shell/android/shell_jni_registrar.h"
 #include "content/shell/app/shell_main_delegate.h"
 
@@ -33,6 +34,12 @@
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
   base::android::InitVM(vm);
   JNIEnv* env = base::android::AttachCurrentThread();
+  if (!RegisterMainDexNatives(env) || !RegisterNonMainDexNatives(env)) {
+    return -1;
+  }
+
+  // TODO(agrieve): Delete this block, this is a no-op now.
+  // https://crbug.com/683256.
   if (!content::android::OnJNIOnLoadRegisterJNI(env) || !RegisterJNI(env) ||
       !NativeInit()) {
     return -1;
diff --git a/mojo/android/BUILD.gn b/mojo/android/BUILD.gn
index 525e7fc..179eb6f 100644
--- a/mojo/android/BUILD.gn
+++ b/mojo/android/BUILD.gn
@@ -140,6 +140,8 @@
     "//mojo/public/cpp/test_support:test_utils",
   ]
   defines = [ "UNIT_TEST" ]
+  configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
+  configs += [ "//build/config/android:hide_all_but_jni" ]
 }
 
 instrumentation_test_apk("mojo_test_apk") {
diff --git a/net/android/BUILD.gn b/net/android/BUILD.gn
index f855f47..8ef6f7c 100644
--- a/net/android/BUILD.gn
+++ b/net/android/BUILD.gn
@@ -109,6 +109,9 @@
     ":java_test_native_support",
     "//net:test_support",
   ]
+
+  configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
+  configs += [ "//build/config/android:hide_all_but_jni" ]
 }
 
 android_apk("net_test_support_apk") {
diff --git a/remoting/android/BUILD.gn b/remoting/android/BUILD.gn
index 248c1ea0..46798cb 100644
--- a/remoting/android/BUILD.gn
+++ b/remoting/android/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//base/android/jni_generator/jni_exception_list.gni")
 import("//build/config/android/config.gni")
 import("//build/config/android/rules.gni")
 import("//remoting/android/client_java_tmpl.gni")
@@ -20,6 +21,12 @@
   jni_package = "remoting"
 }
 
+generate_jni_registration("remoting_jni_registration") {
+  target = ":remoting_apk"
+  output = "$root_gen_dir/remoting/client/${target_name}.h"
+  exception_files = jni_exception_files
+}
+
 _raw_resources_base_dir = "$target_gen_dir/credits_resources_raw/res"
 
 # The target is named this way, instead of "..._raw_resources", specifically
diff --git a/remoting/client/jni/BUILD.gn b/remoting/client/jni/BUILD.gn
index f0f8082..6f10926 100644
--- a/remoting/client/jni/BUILD.gn
+++ b/remoting/client/jni/BUILD.gn
@@ -19,6 +19,7 @@
 shared_library("remoting_client_jni") {
   deps = [
     "//remoting/android:jni_headers",
+    "//remoting/android:remoting_jni_registration",
     "//remoting/base",
     "//remoting/client",
     "//remoting/client/display",
diff --git a/remoting/client/jni/remoting_jni_onload.cc b/remoting/client/jni/remoting_jni_onload.cc
index 9db79a8..dd1a283d 100644
--- a/remoting/client/jni/remoting_jni_onload.cc
+++ b/remoting/client/jni/remoting_jni_onload.cc
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "net/android/net_jni_registrar.h"
 #include "remoting/client/jni/remoting_jni_registrar.h"
+#include "remoting/client/remoting_jni_registration.h"
 #include "ui/gfx/android/gfx_jni_registrar.h"
 
 namespace {
@@ -32,6 +33,12 @@
 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
   base::android::InitVM(vm);
   JNIEnv* env = base::android::AttachCurrentThread();
+  if (!RegisterMainDexNatives(env) || !RegisterNonMainDexNatives(env)) {
+    return -1;
+  }
+
+  // TODO(agrieve): Delete this block, this is a no-op now.
+  // https://crbug.com/683256.
   if (!RegisterJNI(env) || !base::android::OnJNIOnLoadInit()) {
     return -1;
   }
diff --git a/testing/test.gni b/testing/test.gni
index 36e0d91..64619b3 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -63,7 +63,7 @@
         # the default shared_library configs rather than executable configs.
         configs -= [
           "//build/config:shared_library_config",
-          "//build/config/android:hide_all_but_jni_onload",
+          "//build/config/android:hide_all_but_jni",
         ]
         configs += [ "//build/config:executable_config" ]
 
@@ -321,6 +321,8 @@
 set_defaults("test") {
   if (is_android) {
     configs = default_shared_library_configs
+    configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
+    configs += [ "//build/config/android:hide_all_but_jni" ]
   } else {
     configs = default_executable_configs
   }