Name Date Size #Lines LOC

..--

codegen/H25-Apr-2025-696548

java/src/org/H25-Apr-2025-914472

sample/H25-Apr-2025-324237

test/H25-Apr-2025-9,2157,679

.style.yapfH A D25-Apr-202565 54

Android.bpH A D25-Apr-20257.6 KiB265258

BUILD.gnH A D25-Apr-20251.9 KiB6859

DEPSH A D25-Apr-2025155 86

DIR_METADATAH A D25-Apr-2025114 87

LICENSEH A D25-Apr-20251.4 KiB2824

METADATAH A D25-Apr-2025297 1110

MODULE_LICENSE_BSDHD25-Apr-20250

PRESUBMIT.pyH A D25-Apr-20251.1 KiB3825

README.chromiumH A D25-Apr-2025465 1714

README.mdH A D25-Apr-202515.4 KiB417333

checkdiscard_proguard.flagsH A D25-Apr-2025321 1310

common.pyH A D25-Apr-20252.8 KiB11686

java_lang_classes.pyH A D25-Apr-20252.4 KiB10698

java_types.pyH A D25-Apr-202513 KiB425319

jni_export.hH A D25-Apr-2025403 158

jni_generator.pyH A D25-Apr-202522.8 KiB648498

jni_registration_generator.pyH A D25-Apr-202532 KiB940741

jni_zero.ccH A D25-Apr-20259.9 KiB351287

jni_zero.gniH A D25-Apr-202521.1 KiB636583

jni_zero.hH A D25-Apr-202533.3 KiB952559

jni_zero.pyH A D25-Apr-20257.8 KiB214178

jni_zero.pydepsH A D25-Apr-2025515 1817

jni_zero_internal.hH A D25-Apr-20253.7 KiB11475

logging.ccH A D25-Apr-20252.1 KiB7753

logging.hH A D25-Apr-20252.9 KiB9167

parse.pyH A D25-Apr-202515.3 KiB476353

proguard.flagsH A D25-Apr-2025548 1513

proguard_for_test.flagsH A D25-Apr-2025279 86

proxy.pyH A D25-Apr-20252.6 KiB6633

README.chromium

1Name: JNI Zero
2Short Name: jni_zero
3URL: https://chromium.googlesource.com/chromium/src/+/main/third_party/jni_zero
4Version: 0
5License: BSD
6License File: LICENSE
7Security Critical: yes
8Shipped: yes
9
10Description:
11A Chromium-developed JNI middleware, that is designed to provide a zero cost
12(or better!) abstraction when used with R8.
13
14Local Modifications:
15This entire project is developed in //third_party/jni_zero to make it more
16easily usable outside of Chromium.
17

README.md

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