1*6777b538SAndroid Build Coastguard Worker# JNI Zero 2*6777b538SAndroid Build Coastguard WorkerA zero-overhead (or better!) middleware for JNI. 3*6777b538SAndroid Build Coastguard Worker 4*6777b538SAndroid Build Coastguard Worker## Overview 5*6777b538SAndroid Build Coastguard WorkerJNI (Java Native Interface) is the mechanism that enables Java code to call 6*6777b538SAndroid Build Coastguard Workernative functions, and native code to call Java functions. 7*6777b538SAndroid Build Coastguard Worker 8*6777b538SAndroid Build Coastguard Worker * Native code calls into Java using apis from `<jni.h>`, which basically mirror 9*6777b538SAndroid Build Coastguard Worker Java's reflection APIs. 10*6777b538SAndroid Build Coastguard Worker * Java code calls native functions by declaring body-less functions with the 11*6777b538SAndroid Build Coastguard Worker `native` keyword, and then calling them as normal Java functions. 12*6777b538SAndroid Build Coastguard Worker 13*6777b538SAndroid Build Coastguard WorkerJNI Zero generates boiler-plate code with the goal of making our code: 14*6777b538SAndroid Build Coastguard Worker 1. easier to write, 15*6777b538SAndroid Build Coastguard Worker 2. typesafe. 16*6777b538SAndroid Build Coastguard Worker 3. more optimizable. 17*6777b538SAndroid Build Coastguard Worker 18*6777b538SAndroid Build Coastguard WorkerJNI Zero uses regular expressions to parse .Java files, so don't do 19*6777b538SAndroid Build Coastguard Workeranything too fancy. E.g.: 20*6777b538SAndroid Build Coastguard Worker * Classes must be either explicitly imported, or are assumed to be in 21*6777b538SAndroid Build Coastguard Workerthe same package. To use `java.lang` classes, add an explicit import. 22*6777b538SAndroid Build Coastguard Worker * Inner classes need to be referenced through the outer class. E.g.: 23*6777b538SAndroid Build Coastguard Worker `void call(Outer.Inner inner)` 24*6777b538SAndroid Build Coastguard Worker 25*6777b538SAndroid Build Coastguard Worker### Exposing Native Methods 26*6777b538SAndroid Build Coastguard Worker 27*6777b538SAndroid Build Coastguard WorkerThere are two ways to have native methods be found by Java: 28*6777b538SAndroid Build Coastguard Worker1) Explicitly register the name -> function pointer mapping using JNI's 29*6777b538SAndroid Build Coastguard Worker `RegisterNatives()` function. 30*6777b538SAndroid Build Coastguard Worker2) Export the symbols from the shared library, and let the runtime resolve them 31*6777b538SAndroid Build Coastguard Worker on-demand (using `dlsym()`) the first time a native method is called. 32*6777b538SAndroid Build Coastguard Worker 33*6777b538SAndroid Build Coastguard Worker2) Is generally preferred due to a smaller code size and less up-front work, but 34*6777b538SAndroid Build Coastguard Worker1) is sometimes required (e.g. when OS bugs prevent `dlsym()` from working). 35*6777b538SAndroid Build Coastguard WorkerBoth ways are supported by this tool. 36*6777b538SAndroid Build Coastguard Worker 37*6777b538SAndroid Build Coastguard Worker### Exposing Java Methods 38*6777b538SAndroid Build Coastguard Worker 39*6777b538SAndroid Build Coastguard WorkerJava methods just need to be annotated with `@CalledByNative`. By default the 40*6777b538SAndroid Build Coastguard Workergenerated method stubs on the native side are not namespaced. The generated 41*6777b538SAndroid Build Coastguard Workerfunctions can be put into a namespace using `@JNINamespace("your_namespace")`. 42*6777b538SAndroid Build Coastguard Worker 43*6777b538SAndroid Build Coastguard Worker## Usage 44*6777b538SAndroid Build Coastguard Worker 45*6777b538SAndroid Build Coastguard Worker### Writing Build Rules 46*6777b538SAndroid Build Coastguard Worker1. Find or add a `generate_jni` target with your .java file, then add this 47*6777b538SAndroid Build Coastguard Worker `generate_jni` target to your `srcjar_deps` of your `android_library` target: 48*6777b538SAndroid Build Coastguard Worker 49*6777b538SAndroid Build Coastguard Worker ```python 50*6777b538SAndroid Build Coastguard Worker generate_jni("abcd_jni") { 51*6777b538SAndroid Build Coastguard Worker sources = [ "path/to/java/sources/with/jni/Annotations.java" ] 52*6777b538SAndroid Build Coastguard Worker } 53*6777b538SAndroid Build Coastguard Worker 54*6777b538SAndroid Build Coastguard Worker android_library("abcd_java") { 55*6777b538SAndroid Build Coastguard Worker ... 56*6777b538SAndroid Build Coastguard Worker # Allows the java files to see the generated `${OriginalClassName}Jni` 57*6777b538SAndroid Build Coastguard Worker # classes. 58*6777b538SAndroid Build Coastguard Worker srcjar_deps = [ ":abcd_jni" ] 59*6777b538SAndroid Build Coastguard Worker } 60*6777b538SAndroid Build Coastguard Worker 61*6777b538SAndroid Build Coastguard Worker source_set("abcd") { 62*6777b538SAndroid Build Coastguard Worker ... 63*6777b538SAndroid Build Coastguard Worker # Allows the cpp files to include the generated `${OriginalClassName}_jni.h` 64*6777b538SAndroid Build Coastguard Worker # headers. 65*6777b538SAndroid Build Coastguard Worker deps = [ ":abcd_jni" ] 66*6777b538SAndroid Build Coastguard Worker } 67*6777b538SAndroid Build Coastguard Worker ``` 68*6777b538SAndroid Build Coastguard Worker 69*6777b538SAndroid Build Coastguard Worker### Calling Java -> Native 70*6777b538SAndroid Build Coastguard Worker 71*6777b538SAndroid Build Coastguard Worker- For each JNI method: 72*6777b538SAndroid Build Coastguard Worker - C++ stubs are generated that forward to C++ functions that you must write. 73*6777b538SAndroid Build Coastguard Worker By default the c++ functions you are expected to implement are not 74*6777b538SAndroid Build Coastguard Worker associated with a class. 75*6777b538SAndroid Build Coastguard Worker - If the first parameter is a C++ object (e.g. 76*6777b538SAndroid Build Coastguard Worker `long native${OriginalClassName}`), then the bindings will not call a static 77*6777b538SAndroid Build Coastguard Worker function but instead cast the variable into a cpp `${OriginalClassName}` 78*6777b538SAndroid Build Coastguard Worker pointer type and then call a member method with that name on said object. 79*6777b538SAndroid Build Coastguard Worker 80*6777b538SAndroid Build Coastguard WorkerTo add JNI to a class: 81*6777b538SAndroid Build Coastguard Worker 82*6777b538SAndroid Build Coastguard Worker1. Create a nested-interface annotated with `@NativeMethods` that contains 83*6777b538SAndroid Build Coastguard Worker the declaration of the corresponding static methods you wish to have 84*6777b538SAndroid Build Coastguard Worker implemented. 85*6777b538SAndroid Build Coastguard Worker2. Call native functions using `${OriginalClassName}Jni.get().${method}` 86*6777b538SAndroid Build Coastguard Worker3. In C++ code, #include the header `${OriginalClassName}_jni.h`. (The path will 87*6777b538SAndroid Build Coastguard Worker depend on the location of the `generate_jni` BUILD rule that lists your Java 88*6777b538SAndroid Build Coastguard Worker source code.) Only include this header from a single `.cc` file as the 89*6777b538SAndroid Build Coastguard Worker header defines functions. That `.cc` must implement your native code by 90*6777b538SAndroid Build Coastguard Worker defining non-member functions named `JNI_${OriginalClassName}_${UpperCamelCaseMethod}` 91*6777b538SAndroid Build Coastguard Worker for static methods and member functions named `${OriginalClassName}::${UpperCamelCaseMethod}` 92*6777b538SAndroid Build Coastguard Worker for non-static methods. Member functions need be declared in the header 93*6777b538SAndroid Build Coastguard Worker file as well. 94*6777b538SAndroid Build Coastguard Worker 95*6777b538SAndroid Build Coastguard WorkerExample: 96*6777b538SAndroid Build Coastguard Worker#### Java 97*6777b538SAndroid Build Coastguard Worker```java 98*6777b538SAndroid Build Coastguard Workerclass MyClass { 99*6777b538SAndroid Build Coastguard Worker // Cannot be private. Must be package or public. 100*6777b538SAndroid Build Coastguard Worker @NativeMethods 101*6777b538SAndroid Build Coastguard Worker /* package */ interface Natives { 102*6777b538SAndroid Build Coastguard Worker void foo(); 103*6777b538SAndroid Build Coastguard Worker double bar(int a, int b); 104*6777b538SAndroid Build Coastguard Worker // Either the |MyClass| part of the |nativeMyClass| parameter name must 105*6777b538SAndroid Build Coastguard Worker // match the native class name exactly, or the method annotation 106*6777b538SAndroid Build Coastguard Worker // @NativeClassQualifiedName("MyClass") must be used. 107*6777b538SAndroid Build Coastguard Worker // 108*6777b538SAndroid Build Coastguard Worker // If the native class is nested, use 109*6777b538SAndroid Build Coastguard Worker // @NativeClassQualifiedName("FooClassName::BarClassName") and call the 110*6777b538SAndroid Build Coastguard Worker // parameter |nativePointer|. 111*6777b538SAndroid Build Coastguard Worker void nonStatic(long nativeMyClass); 112*6777b538SAndroid Build Coastguard Worker } 113*6777b538SAndroid Build Coastguard Worker 114*6777b538SAndroid Build Coastguard Worker void callNatives() { 115*6777b538SAndroid Build Coastguard Worker // MyClassJni is generated by the generate_jni rule. 116*6777b538SAndroid Build Coastguard Worker // Storing MyClassJni.get() in a field defeats some of the desired R8 117*6777b538SAndroid Build Coastguard Worker // optimizations, but local variables are fine. 118*6777b538SAndroid Build Coastguard Worker Natives jni = MyClassJni.get(); 119*6777b538SAndroid Build Coastguard Worker jni.foo(); 120*6777b538SAndroid Build Coastguard Worker jni.bar(1,2); 121*6777b538SAndroid Build Coastguard Worker jni.nonStatic(mNativePointer); 122*6777b538SAndroid Build Coastguard Worker } 123*6777b538SAndroid Build Coastguard Worker} 124*6777b538SAndroid Build Coastguard Worker``` 125*6777b538SAndroid Build Coastguard Worker#### C++ 126*6777b538SAndroid Build Coastguard Worker```c++ 127*6777b538SAndroid Build Coastguard Worker#include "third_party/jni_zero/jni_zero.h" 128*6777b538SAndroid Build Coastguard Worker#include "<path to BUILD.gn>/<generate_jni target name>/MyClass_jni.h" 129*6777b538SAndroid Build Coastguard Worker 130*6777b538SAndroid Build Coastguard Workerclass MyClass { 131*6777b538SAndroid Build Coastguard Workerpublic: 132*6777b538SAndroid Build Coastguard Worker void NonStatic(JNIEnv* env); 133*6777b538SAndroid Build Coastguard Worker} 134*6777b538SAndroid Build Coastguard Worker 135*6777b538SAndroid Build Coastguard Worker// Notice that unlike Java, function names are capitalized in C++. 136*6777b538SAndroid Build Coastguard Worker// Static function names should follow this format and don't need to be declared. 137*6777b538SAndroid Build Coastguard Workervoid JNI_MyClass_Foo(JNIEnv* env) { ... } 138*6777b538SAndroid Build Coastguard Workervoid JNI_MyClass_Bar(JNIEnv* env, jint a, jint b) { ... } 139*6777b538SAndroid Build Coastguard Worker 140*6777b538SAndroid Build Coastguard Worker// Member functions need to be declared. 141*6777b538SAndroid Build Coastguard Workervoid MyClass::NonStatic(JNIEnv* env) { ... } 142*6777b538SAndroid Build Coastguard Worker``` 143*6777b538SAndroid Build Coastguard Worker 144*6777b538SAndroid Build Coastguard Worker### Calling Native -> Java 145*6777b538SAndroid Build Coastguard Worker 146*6777b538SAndroid Build Coastguard WorkerBecause the generated header files contain definitions as well as declarations, 147*6777b538SAndroid Build Coastguard Workerthe must not be `#included` by multiple sources. If there are Java functions 148*6777b538SAndroid Build Coastguard Workerthat need to be called by multiple sources, one source should be chosen to 149*6777b538SAndroid Build Coastguard Workerexpose the functions to the others via additional wrapper functions. 150*6777b538SAndroid Build Coastguard Worker 151*6777b538SAndroid Build Coastguard Worker1. Annotate some methods with `@CalledByNative`, the generator will now generate 152*6777b538SAndroid Build Coastguard Worker stubs in `${OriginalClassName}_jni.h` header to call into those java methods 153*6777b538SAndroid Build Coastguard Worker from cpp. 154*6777b538SAndroid Build Coastguard Worker * Inner class methods must provide the inner class name explicitly 155*6777b538SAndroid Build Coastguard Worker (ex. `@CalledByNative("InnerClassName")`) 156*6777b538SAndroid Build Coastguard Worker 157*6777b538SAndroid Build Coastguard Worker2. In C++ code, `#include` the header `${OriginalClassName}_jni.h`. (The path 158*6777b538SAndroid Build Coastguard Worker will depend on the location of the `generate_jni` build rule that lists your 159*6777b538SAndroid Build Coastguard Worker Java source code). That `.cc` can call the stubs with their generated name 160*6777b538SAndroid Build Coastguard Worker `JAVA_${OriginalClassName}_${UpperCamelCaseMethod}`. 161*6777b538SAndroid Build Coastguard Worker 162*6777b538SAndroid Build Coastguard WorkerNote: For test-only methods, use `@CalledByNativeForTesting` which will ensure 163*6777b538SAndroid Build Coastguard Workerthat it is stripped in our release binaries. 164*6777b538SAndroid Build Coastguard Worker 165*6777b538SAndroid Build Coastguard Worker### Automatic Type Conversions using @JniType 166*6777b538SAndroid Build Coastguard Worker 167*6777b538SAndroid Build Coastguard WorkerNormally, Java types map to C++ types from `<jni.h>` (e.g. `jstring` for 168*6777b538SAndroid Build Coastguard Worker`java.lang.String`). The first thing most people do is convert the jni spec 169*6777b538SAndroid Build Coastguard Workertypes into standard C++ types. 170*6777b538SAndroid Build Coastguard Worker 171*6777b538SAndroid Build Coastguard Worker`@JniType` to the rescue. By annotating a parameter or a return type with 172*6777b538SAndroid Build Coastguard Worker`@JniType("cpp_type_here")` the generated code will automatically convert from 173*6777b538SAndroid Build Coastguard Workerthe jni type to the type listed inside the annotation. See example: 174*6777b538SAndroid Build Coastguard Worker 175*6777b538SAndroid Build Coastguard Worker#### Original Code: 176*6777b538SAndroid Build Coastguard Worker```java 177*6777b538SAndroid Build Coastguard Workerclass MyClass { 178*6777b538SAndroid Build Coastguard Worker @NativeMethods 179*6777b538SAndroid Build Coastguard Worker interface Natives { 180*6777b538SAndroid Build Coastguard Worker void foo( 181*6777b538SAndroid Build Coastguard Worker String string, 182*6777b538SAndroid Build Coastguard Worker String[] strings, 183*6777b538SAndroid Build Coastguard Worker MyClass obj, 184*6777b538SAndroid Build Coastguard Worker MyClass[] objs) 185*6777b538SAndroid Build Coastguard Worker } 186*6777b538SAndroid Build Coastguard Worker} 187*6777b538SAndroid Build Coastguard Worker``` 188*6777b538SAndroid Build Coastguard Worker 189*6777b538SAndroid Build Coastguard Worker```c++ 190*6777b538SAndroid Build Coastguard Worker#include "third_party/jni_zero/jni_zero.h" 191*6777b538SAndroid Build Coastguard Worker#include "<path to BUILD.gn>/<generate_jni target name>/MyClass_jni.h" 192*6777b538SAndroid Build Coastguard Worker 193*6777b538SAndroid Build Coastguard Workervoid JNI_MyClass_Foo(JNIEnv* env, const JavaParamRef<jstring>&, const JavaParamRef<jobjectArray>&, const JavaParamRef<jobject>&, JavaParamRef<jobjectArray>&) {...} 194*6777b538SAndroid Build Coastguard Worker``` 195*6777b538SAndroid Build Coastguard Worker 196*6777b538SAndroid Build Coastguard Worker#### After using `@JniType` 197*6777b538SAndroid Build Coastguard Worker```java 198*6777b538SAndroid Build Coastguard Workerclass MyClass { 199*6777b538SAndroid Build Coastguard Worker @NativeMethods 200*6777b538SAndroid Build Coastguard Worker interface Natives { 201*6777b538SAndroid Build Coastguard Worker void foo( 202*6777b538SAndroid Build Coastguard Worker @JniType("std::string") String convertedString, 203*6777b538SAndroid Build Coastguard Worker @JniType("std::vector<std::string>") String[] convertedStrings, 204*6777b538SAndroid Build Coastguard Worker @JniType("myModule::CPPClass") MyClass convertedObj, 205*6777b538SAndroid Build Coastguard Worker @JniType("std::vector<myModule::CPPClass>") MyClass[] convertedObjects); 206*6777b538SAndroid Build Coastguard Worker } 207*6777b538SAndroid Build Coastguard Worker} 208*6777b538SAndroid Build Coastguard Worker``` 209*6777b538SAndroid Build Coastguard Worker```c++ 210*6777b538SAndroid Build Coastguard Worker#include "third_party/jni_zero/jni_zero.h" 211*6777b538SAndroid Build Coastguard Worker#include "<path to BUILD.gn>/<generate_jni target name>/MyClass_jni.h" 212*6777b538SAndroid Build Coastguard Worker 213*6777b538SAndroid Build Coastguard Workervoid JNI_MyClass_Foo(JNIEnv* env, std::string&, std::vector<std::string>>&, myModule::CPPClass&, std::vector<myModule::CPPClass>&) {...} 214*6777b538SAndroid Build Coastguard Worker``` 215*6777b538SAndroid Build Coastguard Worker 216*6777b538SAndroid Build Coastguard Worker#### Implementing Conversion Functions 217*6777b538SAndroid Build Coastguard Worker 218*6777b538SAndroid Build Coastguard WorkerConversion functions must be defined for all types that appear in `@JniType`. 219*6777b538SAndroid Build Coastguard WorkerForgetting to add one will result in errors at link time. 220*6777b538SAndroid Build Coastguard Worker 221*6777b538SAndroid Build Coastguard Worker```c++ 222*6777b538SAndroid Build Coastguard Worker// The conversion function primary templates. 223*6777b538SAndroid Build Coastguard Workertemplate <typename O> 224*6777b538SAndroid Build Coastguard WorkerO FromJniType(JNIEnv*, const JavaRef<jobject>&); 225*6777b538SAndroid Build Coastguard Workertemplate <typename O> 226*6777b538SAndroid Build Coastguard WorkerO FromJniType(JNIEnv*, const JavaRef<jstring>&); 227*6777b538SAndroid Build Coastguard Workertemplate <typename O> 228*6777b538SAndroid Build Coastguard WorkerScopedJavaLocalRef<jobject> ToJniType(JNIEnv*, const O&); 229*6777b538SAndroid Build Coastguard Worker``` 230*6777b538SAndroid Build Coastguard Worker 231*6777b538SAndroid Build Coastguard WorkerAn example conversion function can look like: 232*6777b538SAndroid Build Coastguard Worker```c++ 233*6777b538SAndroid Build Coastguard Worker#include "third_party/jni_zero/jni_zero.h" 234*6777b538SAndroid Build Coastguard Worker 235*6777b538SAndroid Build Coastguard Workernamespace jni_zero { 236*6777b538SAndroid Build Coastguard Workertemplate <> 237*6777b538SAndroid Build Coastguard WorkerEXPORT std::string FromJniType<std::string>( 238*6777b538SAndroid Build Coastguard Worker JNIEnv* env, 239*6777b538SAndroid Build Coastguard Worker const JavaRef<jstring>& input) { 240*6777b538SAndroid Build Coastguard Worker // Do the actual conversion to std::string. 241*6777b538SAndroid Build Coastguard Worker} 242*6777b538SAndroid Build Coastguard Worker 243*6777b538SAndroid Build Coastguard Workertemplate <> 244*6777b538SAndroid Build Coastguard WorkerEXPORT ScopedJavaLocalRef<jstring> ToJniType<std::string>( 245*6777b538SAndroid Build Coastguard Worker JNIEnv* env, 246*6777b538SAndroid Build Coastguard Worker const std::string& input) { 247*6777b538SAndroid Build Coastguard Worker // Do the actual conversion from std::string. 248*6777b538SAndroid Build Coastguard Worker} 249*6777b538SAndroid Build Coastguard Worker} // namespace jni_zero 250*6777b538SAndroid Build Coastguard Worker``` 251*6777b538SAndroid Build Coastguard Worker 252*6777b538SAndroid Build Coastguard WorkerIf a conversion function is missing, you will get a linker error since we 253*6777b538SAndroid Build Coastguard Workerforward declare the conversion functions before using them. 254*6777b538SAndroid Build Coastguard Worker 255*6777b538SAndroid Build Coastguard Worker#### Array Conversion Functions 256*6777b538SAndroid Build Coastguard Worker 257*6777b538SAndroid Build Coastguard WorkerArray conversion functions look different due to the partial specializations. 258*6777b538SAndroid Build Coastguard WorkerThe `ToJniType` direction also takes a `jclass` parameter which is the class of the 259*6777b538SAndroid Build Coastguard Workerarray elements, because java requires it when creating a non-primitive array. 260*6777b538SAndroid Build Coastguard Worker 261*6777b538SAndroid Build Coastguard Worker```c++ 262*6777b538SAndroid Build Coastguard Workertemplate <typename O> 263*6777b538SAndroid Build Coastguard Workerstruct ConvertArray { 264*6777b538SAndroid Build Coastguard Worker static O FromJniType(JNIEnv*, const JavaRef<jobjectArray>&); 265*6777b538SAndroid Build Coastguard Worker static ScopedJavaLocalRef<jobjectArray> ToJniType(JNIEnv*, const O&, jclass); 266*6777b538SAndroid Build Coastguard Worker}; 267*6777b538SAndroid Build Coastguard Worker``` 268*6777b538SAndroid Build Coastguard Worker 269*6777b538SAndroid Build Coastguard WorkerJniZero provides implementations for partial specializations to wrap and unwrap 270*6777b538SAndroid Build Coastguard Worker`std::vector` for object arrays and some primitive arrays. 271*6777b538SAndroid Build Coastguard Worker 272*6777b538SAndroid Build Coastguard Worker#### Nullability 273*6777b538SAndroid Build Coastguard Worker 274*6777b538SAndroid Build Coastguard WorkerAll non-primitive default JNI C++ types (e.g. `jstring`, `jobject`) are pointer 275*6777b538SAndroid Build Coastguard Workertypes (i.e. nullable). Some C++ types (e.g. `std::string`) are not pointer types 276*6777b538SAndroid Build Coastguard Workerand thus cannot be `nullptr`. This means some conversion functions that return 277*6777b538SAndroid Build Coastguard Workernon-nullable types have to handle the situation where the passed in java type is 278*6777b538SAndroid Build Coastguard Workernull. 279*6777b538SAndroid Build Coastguard Worker 280*6777b538SAndroid Build Coastguard WorkerYou can get around this by having the conversion be to `std::optional<T>` rather 281*6777b538SAndroid Build Coastguard Workerthan just `T` if `T` is not a nullable type. 282*6777b538SAndroid Build Coastguard Worker 283*6777b538SAndroid Build Coastguard Worker 284*6777b538SAndroid Build Coastguard Worker### Testing Mockable Natives 285*6777b538SAndroid Build Coastguard Worker 286*6777b538SAndroid Build Coastguard Worker1. Add the `JniMocker` rule to your test. 287*6777b538SAndroid Build Coastguard Worker2. Call `JniMocker#mock` in a `setUp()` method for each interface you want to 288*6777b538SAndroid Build Coastguard Worker stub out. 289*6777b538SAndroid Build Coastguard Worker 290*6777b538SAndroid Build Coastguard Worker`JniMocker` will reset the stubs during `tearDown()`. 291*6777b538SAndroid Build Coastguard Worker 292*6777b538SAndroid Build Coastguard Worker```java 293*6777b538SAndroid Build Coastguard Worker/** 294*6777b538SAndroid Build Coastguard Worker * Tests for {@link AnimationFrameTimeHistogram} 295*6777b538SAndroid Build Coastguard Worker */ 296*6777b538SAndroid Build Coastguard Worker@RunWith(BaseRobolectricTestRunner.class) 297*6777b538SAndroid Build Coastguard Worker@Config(manifest = Config.NONE) 298*6777b538SAndroid Build Coastguard Workerpublic class AnimationFrameTimeHistogramTest { 299*6777b538SAndroid Build Coastguard Worker @Rule 300*6777b538SAndroid Build Coastguard Worker public JniMocker mocker = new JniMocker(); 301*6777b538SAndroid Build Coastguard Worker 302*6777b538SAndroid Build Coastguard Worker @Mock 303*6777b538SAndroid Build Coastguard Worker AnimationFrameTimeHistogram.Natives mNativeMock; 304*6777b538SAndroid Build Coastguard Worker 305*6777b538SAndroid Build Coastguard Worker @Before 306*6777b538SAndroid Build Coastguard Worker public void setUp() { 307*6777b538SAndroid Build Coastguard Worker MockitoAnnotations.initMocks(this); 308*6777b538SAndroid Build Coastguard Worker mocker.mock(AnimationFrameTimeHistogramJni.TEST_HOOKS, mNativeMock); 309*6777b538SAndroid Build Coastguard Worker } 310*6777b538SAndroid Build Coastguard Worker 311*6777b538SAndroid Build Coastguard Worker @Test 312*6777b538SAndroid Build Coastguard Worker public void testNatives() { 313*6777b538SAndroid Build Coastguard Worker AnimationFrameTimeHistogram hist = new AnimationFrameTimeHistogram("histName"); 314*6777b538SAndroid Build Coastguard Worker hist.startRecording(); 315*6777b538SAndroid Build Coastguard Worker hist.endRecording(); 316*6777b538SAndroid Build Coastguard Worker verify(mNativeMock).saveHistogram(eq("histName"), any(long[].class), anyInt()); 317*6777b538SAndroid Build Coastguard Worker } 318*6777b538SAndroid Build Coastguard Worker} 319*6777b538SAndroid Build Coastguard Worker``` 320*6777b538SAndroid Build Coastguard Worker 321*6777b538SAndroid Build Coastguard WorkerIf a native method is called without setting a mock in a unit test, an 322*6777b538SAndroid Build Coastguard Worker`UnsupportedOperationException` will be thrown. 323*6777b538SAndroid Build Coastguard Worker 324*6777b538SAndroid Build Coastguard Worker### Special case: DFMs 325*6777b538SAndroid Build Coastguard WorkerDFMs have their own generated `GEN_JNI`s, which are `<module_name>_GEN_JNI`. In 326*6777b538SAndroid Build Coastguard Workerorder to get your DFM's JNI to use the `<module_name>` prefix, you must add your 327*6777b538SAndroid Build Coastguard Workermodule name into the argument of the `@NativeMethods` annotation. 328*6777b538SAndroid Build Coastguard Worker 329*6777b538SAndroid Build Coastguard WorkerSo, for example, say your module was named `test_module`. You would annotate 330*6777b538SAndroid Build Coastguard Workeryour `Natives` interface with `@NativeMethods("test_module")`, and this would 331*6777b538SAndroid Build Coastguard Workerresult in `test_module_GEN_JNI`. 332*6777b538SAndroid Build Coastguard Worker 333*6777b538SAndroid Build Coastguard Worker 334*6777b538SAndroid Build Coastguard Worker### Testing for readiness: use `get()` 335*6777b538SAndroid Build Coastguard Worker 336*6777b538SAndroid Build Coastguard WorkerJNI Generator automatically produces asserts that verify that the Natives interface can be safely 337*6777b538SAndroid Build Coastguard Workercalled. These checks are compiled out of Release builds, making these an excellent way to determine 338*6777b538SAndroid Build Coastguard Workerwhether your code is called safely. 339*6777b538SAndroid Build Coastguard Worker 340*6777b538SAndroid Build Coastguard WorkerIt is not sufficient, however, to use `<Class>Jni.get()` to guarantee native is initialized - it is 341*6777b538SAndroid Build Coastguard Workeronly a debugging tool to ensure that you're using native after native is loaded. 342*6777b538SAndroid Build Coastguard Worker 343*6777b538SAndroid Build Coastguard WorkerIf you expect your code to be called by an external caller, it's often helpful to know _ahead of 344*6777b538SAndroid Build Coastguard Workertime_ that the context is valid (ie. either native libraries are loaded or mocks are installed). 345*6777b538SAndroid Build Coastguard WorkerIn this case it is helpful to call `get()` method, that performs all the Debug checks listed 346*6777b538SAndroid Build Coastguard Workerabove, but does not instantiate a new object for interfacing Native libraries. 347*6777b538SAndroid Build Coastguard WorkerNote that the unused value returned by the `get()` method will be optimized away in release builds 348*6777b538SAndroid Build Coastguard Workerso there's no harm in ignoring it. 349*6777b538SAndroid Build Coastguard Worker 350*6777b538SAndroid Build Coastguard Worker#### Addressing `Jni.get()` exceptions. 351*6777b538SAndroid Build Coastguard Worker 352*6777b538SAndroid Build Coastguard WorkerWhen you identify a scenario leading to an exception, relocate (or defer) the appropriate call to 353*6777b538SAndroid Build Coastguard Workerbe made to a place where (or time when) you know the native libraries have been initialized (eg. 354*6777b538SAndroid Build Coastguard Worker`onStartWithNative`, `onNativeInitialized` etc). 355*6777b538SAndroid Build Coastguard Worker 356*6777b538SAndroid Build Coastguard WorkerPlease avoid calling `LibraryLoader.isInitialized()` / `LibraryLoader.isLoaded()` in new code. 357*6777b538SAndroid Build Coastguard WorkerUsing `LibraryLoader` calls makes unit-testing more difficult: 358*6777b538SAndroid Build Coastguard Worker* this call can not verify whether Mock object is used, making the use of mocks more complicated, 359*6777b538SAndroid Build Coastguard Worker* using `LibraryLoader.setLibrariesLoadedForNativeTests()` alters the state for subsequently 360*6777b538SAndroid Build Coastguard Workerexecuted tests, inaccurately reporting flakiness and failures of these victim tests. 361*6777b538SAndroid Build Coastguard Worker* Introducing `LibraryLoader.is*()` calls in your code immediately affects all callers, forcing 362*6777b538SAndroid Build Coastguard Workerthe authors of the code up the call stack to override `LibraryLoader` internal state in order to be 363*6777b538SAndroid Build Coastguard Workerable to unit-test their code. 364*6777b538SAndroid Build Coastguard Worker 365*6777b538SAndroid Build Coastguard WorkerHowever, if your code is going to be called both before and after native is initialized, you are 366*6777b538SAndroid Build Coastguard Workerforced to call `LibraryLoader.isInitialized()` to be able to differentiate. Calling 367*6777b538SAndroid Build Coastguard Worker`<Class>Jni.get()` only provides assertions, and will fail in debug builds if you call it when 368*6777b538SAndroid Build Coastguard Workernative isn't ready. 369*6777b538SAndroid Build Coastguard Worker 370*6777b538SAndroid Build Coastguard Worker### Java Objects and Garbage Collection 371*6777b538SAndroid Build Coastguard Worker 372*6777b538SAndroid Build Coastguard WorkerAll pointers to Java objects must be registered with JNI in order to prevent 373*6777b538SAndroid Build Coastguard Workergarbage collection from invalidating them. 374*6777b538SAndroid Build Coastguard Worker 375*6777b538SAndroid Build Coastguard WorkerFor Strings & Arrays - it's common practice to use the `//base/android/jni_*` 376*6777b538SAndroid Build Coastguard Workerhelpers to convert them to `std::vectors` and `std::strings` as soon as 377*6777b538SAndroid Build Coastguard Workerpossible. 378*6777b538SAndroid Build Coastguard Worker 379*6777b538SAndroid Build Coastguard WorkerFor other objects - use smart pointers to store them: 380*6777b538SAndroid Build Coastguard Worker * `ScopedJavaLocalRef<>` - When lifetime is the current function's scope. 381*6777b538SAndroid Build Coastguard Worker * `ScopedJavaGlobalRef<>` - When lifetime is longer than the current function's 382*6777b538SAndroid Build Coastguard Worker scope. 383*6777b538SAndroid Build Coastguard Worker * `JavaObjectWeakGlobalRef<>` - Weak reference (do not prevent garbage 384*6777b538SAndroid Build Coastguard Worker collection). 385*6777b538SAndroid Build Coastguard Worker * `JavaParamRef<>` - Use to accept any of the above as a parameter to a 386*6777b538SAndroid Build Coastguard Worker function without creating a redundant registration. 387*6777b538SAndroid Build Coastguard Worker 388*6777b538SAndroid Build Coastguard Worker### Additional Guidelines / Advice 389*6777b538SAndroid Build Coastguard Worker 390*6777b538SAndroid Build Coastguard WorkerMinimize the surface API between the two sides. Rather than calling multiple 391*6777b538SAndroid Build Coastguard Workerfunctions across boundaries, call only one (and then on the other side, call as 392*6777b538SAndroid Build Coastguard Workermany little functions as required). 393*6777b538SAndroid Build Coastguard Worker 394*6777b538SAndroid Build Coastguard WorkerIf a Java object "owns" a native one, store the pointer via 395*6777b538SAndroid Build Coastguard Worker`"long mNativeClassName"`. Ensure to eventually call a native method to delete 396*6777b538SAndroid Build Coastguard Workerthe object. For example, have a `close()` that deletes the native object. 397*6777b538SAndroid Build Coastguard Worker 398*6777b538SAndroid Build Coastguard WorkerThe best way to pass "compound" types across in either direction is to 399*6777b538SAndroid Build Coastguard Workercreate an inner class with PODs and a factory function. If possible, mark 400*6777b538SAndroid Build Coastguard Workerall the fields as "final". 401*6777b538SAndroid Build Coastguard Worker 402*6777b538SAndroid Build Coastguard Worker## Build Rules 403*6777b538SAndroid Build Coastguard Worker 404*6777b538SAndroid Build Coastguard Worker * `generate_jni` - Generates a header file with stubs for given `.java` files 405*6777b538SAndroid Build Coastguard Worker * `generate_jar_jni` - Generates a header file with stubs for a given `.jar` 406*6777b538SAndroid Build Coastguard Worker file 407*6777b538SAndroid Build Coastguard Worker * `generate_jni_registration` - Generates a header file with functions to 408*6777b538SAndroid Build Coastguard Worker register native-side JNI methods. 409*6777b538SAndroid Build Coastguard Worker 410*6777b538SAndroid Build Coastguard WorkerRefer to [//build/config/android/rules.gni](https://cs.chromium.org/chromium/src/build/config/android/rules.gni) 411*6777b538SAndroid Build Coastguard Workerfor more about the GN templates. 412*6777b538SAndroid Build Coastguard Worker 413*6777b538SAndroid Build Coastguard Worker## Changing JNI Zero 414*6777b538SAndroid Build Coastguard Worker 415*6777b538SAndroid Build Coastguard Worker * Python tests live in `test/integration_tests.py` 416*6777b538SAndroid Build Coastguard Worker * A working demo app exists as `//third_party/jni_zero/sample:jni_zero_sample_apk` 417