1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.example.jni_generator;
6
7import android.graphics.Rect;
8
9import org.chromium.base.annotations.AccessedByNative;
10import org.chromium.base.annotations.CalledByNative;
11import org.chromium.base.annotations.CalledByNativeUnchecked;
12import org.chromium.base.annotations.JNINamespace;
13import org.chromium.base.annotations.NativeClassQualifiedName;
14
15import java.util.ArrayList;
16import java.util.Iterator;
17import java.util.List;
18
19// This class serves as a reference test for the bindings generator, and as example documentation
20// for how to use the jni generator.
21// The C++ counter-part is sample_for_tests.cc.
22// jni_generator/BUILD.gn has a jni_generator_tests target that will:
23//   * Generate a header file for the JNI bindings based on this file.
24//   * Compile sample_for_tests.cc using the generated header file.
25//   * link a native executable to prove the generated header + cc file are self-contained.
26// All comments are informational only, and are ignored by the jni generator.
27//
28// Binding C/C++ with Java is not trivial, specially when ownership and object lifetime
29// semantics needs to be managed across boundaries.
30// Following a few guidelines will make the code simpler and less buggy:
31//
32// - Never write any JNI "by hand". Rely on the bindings generator to have a thin
33// layer of type-safety.
34//
35// - Treat the types from the other side as "opaque" as possible. Do not inspect any
36// object directly, but rather, rely on well-defined getters / setters.
37//
38// - Minimize the surface API between the two sides, and rather than calling multiple
39// functions across boundaries, call only one (and then, internally in the other side,
40// call as many little functions as required).
41//
42// - If a Java object "owns" a native object, stash the pointer in a "long mNativeClassName".
43// Note that it needs to have a "destruction path", i.e., it must eventually call a method
44// to delete the native object (for example, the java object has a "close()" method that
45// in turn deletes the native object). Avoid relying on finalizers: those run in a different
46// thread and makes the native lifetime management more difficult.
47//
48// - For native object "owning" java objects:
49//   - If there's a strong 1:1 to relationship between native and java, the best way is to
50//   stash the java object into a base::android::ScopedJavaGlobalRef. This will ensure the
51//   java object can be GC'd once the native object is destroyed but note that this global strong
52//   ref implies a new GC root, so be sure it will not leak and it must never rely on being
53//   triggered (transitively) from a java side GC.
54//   - In all other cases, the native side should keep a JavaObjectWeakGlobalRef, and check whether
55//   that reference is still valid before de-referencing it. Note that you will need another
56//   java-side object to be holding a strong reference to this java object while it is in use, to
57//   avoid unpredictable GC of the object before native side has finished with it.
58//
59// - The best way to pass "compound" datatypes across in either direction is to create an inner
60// class with PODs and a factory function. If possible, make it immutable (i.e., mark all the
61// fields as "final"). See examples with "InnerStructB" below.
62//
63// - It's simpler to create thin wrappers with a well defined JNI interface than to
64// expose a lot of internal details. This is specially significant for system classes where it's
65// simpler to wrap factory methods and a few getters / setters than expose the entire class.
66//
67// - Use static factory functions annotated with @CalledByNative rather than calling the
68// constructors directly.
69//
70// - Iterate over containers where they are originally owned, then create inner structs or
71// directly call methods on the other side. It's much simpler than trying to amalgamate
72// java and stl containers.
73//
74// An important note about qualified class name resolution:
75// The generator doesn't compile the class and have little context about the
76// classes being passed through the JNI layers. It adds a few simple rules:
77//
78// - all classes are either explicitly imported, or they are assumed to be in
79// the same package.
80//
81// - Inner class needs to be done through an import and usage of the
82// outer class, so that the generator knows how to qualify it:
83// import foo.bar.Zoo;
84// void call(Zoo.Inner);
85//
86// - implicitly imported classes aren't supported, so in order to pass
87// things like Runnable, please import java.lang.Runnable;
88//
89// This JNINamespace annotation indicates that all native methods should be
90// generated inside this namespace, including the native class that this
91// object binds to.
92@JNINamespace("base::android")
93class SampleForTests {
94    // Classes can store their C++ pointer counter part as an int that is normally initialized by
95    // calling out a nativeInit() function. Replace "CPPClass" with your particular class name!
96    long mNativeCPPObject;
97
98    // You can define methods and attributes on the java class just like any other.
99    // Methods without the @CalledByNative annotation won't be exposed to JNI.
100    public SampleForTests() {
101    }
102
103    public void startExample() {
104        // Calls C++ Init(...) method and holds a pointer to the C++ class.
105        mNativeCPPObject = nativeInit("myParam");
106    }
107
108    public void doStuff() {
109        // This will call CPPClass::Method() using nativePtr as a pointer to the object. This must
110        // be done to:
111        // * avoid leaks.
112        // * using finalizers are not allowed to destroy the cpp class.
113        nativeMethod(mNativeCPPObject);
114    }
115
116    public void finishExample() {
117        // We're done, so let's destroy nativePtr object.
118        nativeDestroy(mNativeCPPObject);
119    }
120
121    // ---------------------------------------------------------------------------------------------
122    // The following methods demonstrate exporting Java methods for invocation from C++ code.
123    // Java functions are mapping into C global functions by prefixing the method name with
124    // "Java_<Class>_"
125    // This is triggered by the @CalledByNative annotation; the methods may be named as you wish.
126
127    // Exported to C++ as:
128    // Java_SampleForTests_javaMethod(JNIEnv* env, jobject caller, jint foo, jint bar)
129    // Typically the C++ code would have obtained the jobject via the Init() call described above.
130    @CalledByNative
131    public int javaMethod(int foo, int bar) {
132        return 0;
133    }
134
135    // Exported to C++ as Java_SampleForTests_staticJavaMethod(JNIEnv* env)
136    // Note no jobject argument, as it is static.
137    @CalledByNative
138    public static boolean staticJavaMethod() {
139        return true;
140    }
141
142    // No prefix, so this method is package private. It will still be exported.
143    @CalledByNative
144    void packagePrivateJavaMethod() {
145    }
146
147    // Note the "Unchecked" suffix. By default, @CalledByNative will always generate bindings that
148    // call CheckException(). With "@CalledByNativeUnchecked", the client C++ code is responsible to
149    // call ClearException() and act as appropriate.
150    // See more details at the "@CalledByNativeUnchecked" annotation.
151    @CalledByNativeUnchecked
152    void methodThatThrowsException() throws Exception {}
153
154    // The generator is not confused by inline comments:
155    // @CalledByNative void thisShouldNotAppearInTheOutput();
156    // @CalledByNativeUnchecked public static void neitherShouldThis(int foo);
157
158    /**
159     * The generator is not confused by block comments:
160     * @CalledByNative void thisShouldNotAppearInTheOutputEither();
161     * @CalledByNativeUnchecked public static void andDefinitelyNotThis(int foo);
162     */
163
164    // String constants that look like comments don't confuse the generator:
165    private String mArrgh = "*/*";
166
167    // ---------------------------------------------------------------------------------------------
168    // Java fields which are accessed from C++ code only must be annotated with @AccessedByNative to
169    // prevent them being eliminated when unreferenced code is stripped.
170    @AccessedByNative
171    private int mJavaField;
172
173    // ---------------------------------------------------------------------------------------------
174    // The following methods demonstrate declaring methods to call into C++ from Java.
175    // The generator detects the "native" and "static" keywords, the type and name of the first
176    // parameter, and the "native" prefix to the function name to determine the C++ function
177    // signatures. Besides these constraints the methods can be freely named.
178
179    // This declares a C++ function which the application code must implement:
180    // static jint Init(JNIEnv* env, jobject caller);
181    // The jobject parameter refers back to this java side object instance.
182    // The implementation must return the pointer to the C++ object cast to jint.
183    // The caller of this method should store it, and supply it as a the nativeCPPClass param to
184    // subsequent native method calls (see the methods below that take an "int native..." as first
185    // param).
186    private native long nativeInit(String param);
187
188    // This defines a function binding to the associated C++ class member function. The name is
189    // derived from |nativeDestroy| and |nativeCPPClass| to arrive at CPPClass::Destroy() (i.e.
190    // native prefixes stripped).
191    //
192    // The |nativeCPPClass| is automatically cast to type CPPClass*, in order to obtain the object
193    // on
194    // which to invoke the member function. Replace "CPPClass" with your particular class name!
195    private native void nativeDestroy(long nativeCPPClass);
196
197    // This declares a C++ function which the application code must implement:
198    // static jdouble GetDoubleFunction(JNIEnv* env, jobject caller);
199    // The jobject parameter refers back to this java side object instance.
200    private native double nativeGetDoubleFunction();
201
202    // Similar to nativeGetDoubleFunction(), but here the C++ side will receive a jclass rather than
203    // jobject param, as the function is declared static.
204    private static native float nativeGetFloatFunction();
205
206    // This function takes a non-POD datatype. We have a list mapping them to their full classpath
207    // in jni_generator.py JavaParamToJni. If you require a new datatype, make sure you add to that
208    // function.
209    private native void nativeSetNonPODDatatype(Rect rect);
210
211    // This declares a C++ function which the application code must implement:
212    // static ScopedJavaLocalRef<jobject> GetNonPODDatatype(JNIEnv* env, jobject caller);
213    // The jobject parameter refers back to this java side object instance.
214    // Note that it returns a ScopedJavaLocalRef<jobject> so that you don' have to worry about
215    // deleting the JNI local reference. This is similar with Strings and arrays.
216    private native Object nativeGetNonPODDatatype();
217
218    // Similar to nativeDestroy above, this will cast nativeCPPClass into pointer of CPPClass type
219    // and call its Method member function. Replace "CPPClass" with your particular class name!
220    private native int nativeMethod(long nativeCPPClass);
221
222    // Similar to nativeMethod above, but here the C++ fully qualified class name is taken from the
223    // annotation rather than parameter name, which can thus be chosen freely.
224    @NativeClassQualifiedName("CPPClass::InnerClass")
225    private native double nativeMethodOtherP0(long nativePtr);
226
227    // This "struct" will be created by the native side using |createInnerStructA|,
228    // and used by the java-side somehow.
229    // Note that |@CalledByNative| has to contain the inner class name.
230    static class InnerStructA {
231        private final long mLong;
232        private final int mInt;
233        private final String mString;
234
235        private InnerStructA(long l, int i, String s) {
236            mLong = l;
237            mInt = i;
238            mString = s;
239        }
240
241        @CalledByNative("InnerStructA")
242        private static InnerStructA create(long l, int i, String s) {
243            return new InnerStructA(l, i, s);
244        }
245    }
246
247    private List<InnerStructA> mListInnerStructA = new ArrayList<InnerStructA>();
248
249    @CalledByNative
250    private void addStructA(InnerStructA a) {
251        // Called by the native side to append another element.
252        mListInnerStructA.add(a);
253    }
254
255    @CalledByNative
256    private void iterateAndDoSomething() {
257        Iterator<InnerStructA> it = mListInnerStructA.iterator();
258        while (it.hasNext()) {
259            InnerStructA element = it.next();
260            // Now, do something with element.
261        }
262        // Done, clear the list.
263        mListInnerStructA.clear();
264    }
265
266    // This "struct" will be created by the java side passed to native, which
267    // will use its getters.
268    // Note that |@CalledByNative| has to contain the inner class name.
269    static class InnerStructB {
270        private final long mKey;
271        private final String mValue;
272
273        private InnerStructB(long k, String v) {
274            mKey = k;
275            mValue = v;
276        }
277
278        @CalledByNative("InnerStructB")
279        private long getKey() {
280            return mKey;
281        }
282
283        @CalledByNative("InnerStructB")
284        private String getValue() {
285            return mValue;
286        }
287    }
288
289    List<InnerStructB> mListInnerStructB = new ArrayList<InnerStructB>();
290
291    void iterateAndDoSomethingWithMap() {
292        Iterator<InnerStructB> it = mListInnerStructB.iterator();
293        while (it.hasNext()) {
294            InnerStructB element = it.next();
295            // Now, do something with element.
296            nativeAddStructB(mNativeCPPObject, element);
297        }
298        nativeIterateAndDoSomethingWithStructB(mNativeCPPObject);
299    }
300
301    native void nativeAddStructB(long nativeCPPClass, InnerStructB b);
302    native void nativeIterateAndDoSomethingWithStructB(long nativeCPPClass);
303    native String nativeReturnAString(long nativeCPPClass);
304}
305