1# Overview 2JNI (Java Native Interface) is the mechanism that enables Java code to call 3native functions, and native code to call Java functions. 4 5 * Native code calls into Java using apis from `<jni.h>`, which basically mirror 6 Java's reflection APIs. 7 * Java code calls native functions by declaring body-less functions with the 8 `native` keyword, and then calling them as normal Java functions. 9 10`jni_generator` generates boiler-plate code with the goal of making our code: 11 1. easier to write, and 12 2. typesafe. 13 14`jni_generator` uses regular expressions to parse .Java files, so don't do 15anything too fancy. E.g.: 16 * Classes must be either explicitly imported, or are assumed to be in 17the same package. To use `java.lang` classes, add an explicit import. 18 * Inner classes need to be referenced through the outer class. E.g.: 19 `void call(Outer.Inner inner)` 20 21The presense of any JNI within a class will result in ProGuard obfuscation for 22the class to be disabled. 23 24### Exposing Native Methods 25 26There are two ways to have native methods be found by Java: 271) Explicitly register the name -> function pointer mapping using JNI's 28 `RegisterNatives()` function. 292) Export the symbols from the shared library, and let the runtime resolve them 30 on-demand (using `dlsym()`) the first time a native method is called. 31 322) Is generally preferred due to a smaller code size and less up-front work, but 331) is sometimes required (e.g. when OS bugs prevent `dlsym()` from working). 34Both ways are supported by this tool. 35 36### Exposing Java Methods 37 38Java methods just need to be annotated with `@CalledByNative`. The generated 39functions can be put into a namespace using `@JNINamespace("your_namespace")`. 40 41## Usage 42 43Because the generator does not generate any source files, generated headers must 44not be `#included` by multiple sources. If there are Java functions that need to 45be called by multiple sources, one source should be chosen to expose the 46functions to the others via additional wrapper functions. 47 48### Calling Java -> Native 49 50- Declare methods using a nested interface annotated with `@NativeMethods`. 51- The `generate_jni` rule generates a class named `${OriginalClassName}Jni` 52 with a `get()` method that returns an implementation of the annotated 53 interface. The C++ function that it routes to is the same as if it would be 54 in the legacy method. 55- For each JNI method: 56 - C++ stubs are generated that forward to C++ functions that you must write. 57 - If the first parameter is a C++ object (e.g. 58 `long native${OriginalClassName}`), then the bindings will generate the 59 appropriate cast and call into C++ code. 60 61To add JNI to a class: 62 631. Find or add a `generate_jni` target with your .java file, then add this 64 `generate_jni` target to your `srcjar_deps` of your `android_library` target: 65 ```python 66 generate_jni("abcd_jni") 67 ... 68 android_library("abcd_java") { 69 ... 70 srcjar_deps = [ ":abcd_jni" ] 71 ``` 722. Create a nested-interface annotated with `@NativeMethods` that contains 73 the declaration of the corresponding static methods you wish to have 74 implemented. 753. Call native functions using `${OriginalClassName}Jni.get().${method}` 764. In C++ code, #include the header `${OriginalClassName}_jni.h`. (The path will 77 depend on the location of the `generate_jni` BUILD rule that lists your Java 78 source code.) Only include this header from a single `.cc` file as the 79 header defines functions. That `.cc` must implement your native code by 80 defining non-member functions named `JNI_${OriginalClassName}_${UpperCamelCaseMethod}` 81 for static methods and member functions named `${OriginalClassName}::${UpperCamelCaseMethod}` 82 for non-static methods. Member functions need be declared in the header 83 file as well. 84 85Example: 86#### Java 87```java 88class MyClass { 89 // Cannot be private. Must be package or public. 90 @NativeMethods 91 /* package */ interface Natives { 92 void foo(); 93 double bar(int a, int b); 94 // Either the |MyClass| part of the |nativeMyClass| parameter name must 95 // match the native class name exactly, or the method annotation 96 // @NativeClassQualifiedName("MyClass") must be used. 97 // 98 // If the native class is nested, use 99 // @NativeClassQualifiedName("FooClassName::BarClassName") and call the 100 // parameter |nativePointer|. 101 void nonStatic(long nativeMyClass); 102 } 103 104 void callNatives() { 105 // MyClassJni is generated by the generate_jni rule. 106 // Storing MyClassJni.get() in a field defeats some of the desired R8 107 // optimizations, but local variables are fine. 108 Natives jni = MyClassJni.get(); 109 jni.foo(); 110 jni.bar(1,2); 111 jni.nonStatic(mNativePointer); 112 } 113} 114``` 115#### C++ 116```c++ 117#include "base/android/jni_android.h" 118#include "<path to BUILD.gn>/<generate_jni target name>/MyClass_jni.h" 119 120class MyClass { 121public: 122 void NonStatic(JNIEnv* env); 123} 124 125// Notice that unlike Java, function names are capitalized in C++. 126// Static function names should follow this format and don't need to be declared. 127void JNI_MyClass_Foo(JNIEnv* env) { ... } 128void JNI_MyClass_Bar(JNIEnv* env, jint a, jint b) { ... } 129 130// Member functions need to be declared. 131void MyClass::NonStatic(JNIEnv* env) { ... } 132``` 133 134**Using the 'native' keyword** 135 136- The binding generator also looks for `native` JNI method declarations and 137 generates stubs for them. This used to be the norm, but is now obsolete. 138 139#### Testing Mockable Natives 140 1411. Add the `JniMocker` rule to your test. 1422. Call `JniMocker#mock` in a `setUp()` method for each interface you want to 143 stub out. 144 145`JniMocker` will reset the stubs during `tearDown()`. 146 147```java 148/** 149 * Tests for {@link AnimationFrameTimeHistogram} 150 */ 151@RunWith(BaseRobolectricTestRunner.class) 152@Config(manifest = Config.NONE) 153public class AnimationFrameTimeHistogramTest { 154 @Rule 155 public JniMocker mocker = new JniMocker(); 156 157 @Mock 158 AnimationFrameTimeHistogram.Natives mNativeMock; 159 160 @Before 161 public void setUp() { 162 MockitoAnnotations.initMocks(this); 163 mocker.mock(AnimationFrameTimeHistogramJni.TEST_HOOKS, mNativeMock); 164 } 165 166 @Test 167 public void testNatives() { 168 AnimationFrameTimeHistogram hist = new AnimationFrameTimeHistogram("histName"); 169 hist.startRecording(); 170 hist.endRecording(); 171 verify(mNativeMock).saveHistogram(eq("histName"), any(long[].class), anyInt()); 172 } 173} 174``` 175 176If a native method is called without setting a mock in a unit test, an 177`UnsupportedOperationException` will be thrown. 178 179#### Special case: DFMs 180DFMs have their own generated `GEN_JNI`s, which are `<module_name>_GEN_JNI`. In 181order to get your DFM's JNI to use the `<module_name>` prefix, you must add your 182module name into the argument of the `@NativeMethods` annotation. 183 184So, for example, say your module was named `test_module`. You would annotate 185your `Natives` interface with `@NativeMethods("test_module")`, and this would 186result in `test_module_GEN_JNI`. 187 188 189### Testing for readiness: use `get()` 190 191JNI Generator automatically produces asserts that verify that the Natives interface can be safely 192called. These checks are compiled out of Release builds, making these an excellent way to determine 193whether your code is called safely. 194 195It is not sufficient, however, to use `<Class>Jni.get()` to guarantee native is initialized - it is 196only a debugging tool to ensure that you're using native after native is loaded. 197 198If you expect your code to be called by an external caller, it's often helpful to know _ahead of 199time_ that the context is valid (ie. either native libraries are loaded or mocks are installed). 200In this case it is helpful to call `get()` method, that performs all the Debug checks listed 201above, but does not instantiate a new object for interfacing Native libraries. 202Note that the unused value returned by the `get()` method will be optimized away in release builds 203so there's no harm in ignoring it. 204 205#### Addressing `Jni.get()` exceptions. 206 207When you identify a scenario leading to an exception, relocate (or defer) the appropriate call to 208be made to a place where (or time when) you know the native libraries have been initialized (eg. 209`onStartWithNative`, `onNativeInitialized` etc). 210 211Please avoid calling `LibraryLoader.isInitialized()` / `LibraryLoader.isLoaded()` in new code. 212Using `LibraryLoader` calls makes unit-testing more difficult: 213* this call can not verify whether Mock object is used, making the use of mocks more complicated, 214* using `LibraryLoader.setLibrariesLoadedForNativeTests()` alters the state for subsequently 215executed tests, inaccurately reporting flakiness and failures of these victim tests. 216* Introducing `LibraryLoader.is*()` calls in your code immediately affects all callers, forcing 217the authors of the code up the call stack to override `LibraryLoader` internal state in order to be 218able to unit-test their code. 219 220However, if your code is going to be called both before and after native is initialized, you are 221forced to call `LibraryLoader.isInitialized()` to be able to differentiate. Calling 222`<Class>Jni.get()` only provides assertions, and will fail in debug builds if you call it when 223native isn't ready. 224 225### Calling Native -> Java 226 227 * Methods annotated with `@CalledByNative` will have stubs generated for them. 228 * Inner class methods must provide the inner class name explicitly 229 (ex. `@CalledByNative("InnerClassName")`) 230 * Just call the generated stubs defined in generated `.h` files. 231 * For test-only methods you want to call from native, use 232 `@CalledByNativeForTesting` which will ensure that it is stripped in our 233 release binaries. 234 235### Java Objects and Garbage Collection 236 237All pointers to Java objects must be registered with JNI in order to prevent 238garbage collection from invalidating them. 239 240For Strings & Arrays - it's common practice to use the `//base/android/jni_*` 241helpers to convert them to `std::vectors` and `std::strings` as soon as 242possible. 243 244For other objects - use smart pointers to store them: 245 * `ScopedJavaLocalRef<>` - When lifetime is the current function's scope. 246 * `ScopedJavaGlobalRef<>` - When lifetime is longer than the current function's 247 scope. 248 * `JavaObjectWeakGlobalRef<>` - Weak reference (do not prevent garbage 249 collection). 250 * `JavaParamRef<>` - Use to accept any of the above as a parameter to a 251 function without creating a redundant registration. 252 253### Additional Guidelines / Advice 254 255Minimize the surface API between the two sides. Rather than calling multiple 256functions across boundaries, call only one (and then on the other side, call as 257many little functions as required). 258 259If a Java object "owns" a native one, store the pointer via 260`"long mNativeClassName"`. Ensure to eventually call a native method to delete 261the object. For example, have a `close()` that deletes the native object. 262 263The best way to pass "compound" types across in either direction is to 264create an inner class with PODs and a factory function. If possible, mark 265all the fields as "final". 266 267## Build Rules 268 269 * `generate_jni` - Generates a header file with stubs for given `.java` files 270 * `generate_jar_jni` - Generates a header file with stubs for a given `.jar` 271 file 272 * `generate_jni_registration` - Generates a header file with functions to 273 register native-side JNI methods. 274 275Refer to [//build/config/android/rules.gni](https://cs.chromium.org/chromium/src/build/config/android/rules.gni) 276for more about the GN templates. 277 278## Changing `jni_generator` 279 280 * Python tests live in `integration_tests.py` 281 * A working demo app exists as `//third_party/jni_zero/samples:sample_jni_apk` 282