···11+// Copyright 2020, Collabora, Ltd.
22+// SPDX-License-Identifier: BSL-1.0
33+// Author: Ryan Pavlik <ryan.pavlik@collabora.com>
44+55+#pragma once
66+#include <jni.h>
77+#include <jnipp.h>
88+99+namespace wrap {
1010+1111+/*!
1212+ * Base class for types wrapping Java types.
1313+ *
1414+ * Derived types are encouraged to have a nested `struct Meta`, inheriting
1515+ * publicly from MetaBaseDroppable or MetaBase, with a singleton accessor named
1616+ * `data()`, and a private constructor (implemented in a .cpp file, not the
1717+ * header) that populates jni::method_t, jni::field_t, etc. members for each
1818+ * method, etc. of interest.
1919+ */
2020+class ObjectWrapperBase {
2121+ public:
2222+ /*!
2323+ * Default constructor.
2424+ */
2525+ ObjectWrapperBase() = default;
2626+2727+ /*!
2828+ * Construct from a jni::Object.
2929+ */
3030+ explicit ObjectWrapperBase(jni::Object obj) : obj_(std::move(obj)) {}
3131+3232+ /*!
3333+ * Construct from a nullptr.
3434+ */
3535+ explicit ObjectWrapperBase(std::nullptr_t const &) : obj_() {}
3636+3737+ /*!
3838+ * Evaluate if this is non-null
3939+ */
4040+ explicit operator bool() const noexcept { return !obj_.isNull(); }
4141+4242+ /*!
4343+ * Is this object null?
4444+ */
4545+ bool isNull() const noexcept { return obj_.isNull(); }
4646+4747+ /*!
4848+ * Get the wrapped jni::Object
4949+ */
5050+ jni::Object &object() noexcept { return obj_; }
5151+5252+ /*!
5353+ * Get the wrapped jni::Object (const overload)
5454+ */
5555+ jni::Object const &object() const noexcept { return obj_; }
5656+5757+ private:
5858+ jni::Object obj_;
5959+};
6060+6161+/*!
6262+ * Equality comparison for a wrapped Java object.
6363+ */
6464+static inline bool operator==(ObjectWrapperBase const &lhs,
6565+ ObjectWrapperBase const &rhs) noexcept {
6666+ return lhs.object() == rhs.object();
6767+}
6868+6969+/*!
7070+ * Inequality comparison for a wrapped Java object.
7171+ */
7272+static inline bool operator!=(ObjectWrapperBase const &lhs,
7373+ ObjectWrapperBase const &rhs) noexcept {
7474+ return lhs.object() != rhs.object();
7575+}
7676+7777+/*!
7878+ * Equality comparison between a wrapped Java object and @p nullptr.
7979+ */
8080+static inline bool operator==(ObjectWrapperBase const &obj,
8181+ std::nullptr_t) noexcept {
8282+ return obj.isNull();
8383+}
8484+8585+/*!
8686+ * Equality comparison between a wrapped Java object and @p nullptr.
8787+ */
8888+static inline bool operator==(std::nullptr_t,
8989+ ObjectWrapperBase const &obj) noexcept {
9090+ return obj.isNull();
9191+}
9292+9393+/*!
9494+ * Inequality comparison between a wrapped Java object and @p nullptr.
9595+ */
9696+static inline bool operator!=(ObjectWrapperBase const &obj,
9797+ std::nullptr_t) noexcept {
9898+ return !(obj == nullptr);
9999+}
100100+101101+/*!
102102+ * Inequality comparison between a wrapped Java object and @p nullptr.
103103+ */
104104+static inline bool operator!=(std::nullptr_t,
105105+ ObjectWrapperBase const &obj) noexcept {
106106+ return !(obj == nullptr);
107107+}
108108+109109+/*!
110110+ * Base class for Meta structs where you want the reference to the Class object
111111+ * to persist (indefinitely).
112112+ *
113113+ * Mostly for classes that would stick around anyway (e.g.
114114+ * @p java.lang.ClassLoader ) where many operations are on static
115115+ * methods/fields. Use of a non-static method or field does not require such a
116116+ * reference, use MetaBaseDroppable in those cases.
117117+ */
118118+class MetaBase {
119119+ public:
120120+ /*!
121121+ * Gets a reference to the class object.
122122+ *
123123+ * Unlike MetaBaseDroppable, here we know that the class object ref is
124124+ * alive.
125125+ */
126126+ jni::Class const &clazz() const noexcept { return clazz_; }
127127+ /*!
128128+ * Gets a reference to the class object.
129129+ *
130130+ * Provided here for parallel API to MetaBaseDroppable, despite being
131131+ * synonymous with clazz() here.
132132+ */
133133+ jni::Class const &classRef() const noexcept { return clazz_; }
134134+135135+ /*!
136136+ * Get the class name, with namespaces delimited by `/`.
137137+ */
138138+ const char *className() const noexcept { return classname_; }
139139+140140+ protected:
141141+ /*!
142142+ * Construct.
143143+ *
144144+ * @param classname The class name, fully qualified, with namespaces
145145+ * delimited by `/`.
146146+ */
147147+ explicit MetaBase(const char *classname)
148148+ : classname_(classname), clazz_(classname_) {}
149149+150150+ private:
151151+ const char *classname_;
152152+ jni::Class clazz_;
153153+};
154154+155155+/*!
156156+ * Base class for Meta structs where you don't need the reference to the Class
157157+ * object to persist. (This is most uses.)
158158+ */
159159+class MetaBaseDroppable {
160160+ public:
161161+ /*!
162162+ * Gets the class object.
163163+ *
164164+ * Works regardless of whether dropClassRef() has been called - it's just
165165+ * slower if it has.
166166+ */
167167+ jni::Class clazz() const {
168168+ if (clazz_.isNull()) {
169169+ return {classname_};
170170+ }
171171+ return clazz_;
172172+ }
173173+174174+ /*!
175175+ * Get the class name, with namespaces delimited by `/`.
176176+ */
177177+ const char *className() const noexcept { return classname_; }
178178+179179+ /*!
180180+ * May be called in/after the derived constructor, to drop the reference to
181181+ * the class object if it's no longer needed.
182182+ */
183183+ void dropClassRef() { clazz_ = jni::Class{}; }
184184+185185+ protected:
186186+ /*!
187187+ * Construct.
188188+ *
189189+ * Once you are done constructing your derived struct, you may call
190190+ * dropClassRef() and still safely use non-static method and field IDs
191191+ * retrieved.
192192+ *
193193+ * @param classname The class name, fully qualified, with namespaces
194194+ * delimited by `/`.
195195+ */
196196+ explicit MetaBaseDroppable(const char *classname)
197197+ : classname_(classname), clazz_(classname_) {}
198198+199199+ /*!
200200+ * Gets a reference to the class object, but is non-null only if it's still
201201+ * cached.
202202+ *
203203+ * Only for used in derived constructors/initializers, where you know you
204204+ * haven't dropped this yet.
205205+ */
206206+ jni::Class const &classRef() const { return clazz_; }
207207+208208+ private:
209209+ const char *classname_;
210210+ jni::Class clazz_;
211211+};
212212+213213+/*!
214214+ * Implementation namespace for these JNI wrappers.
215215+ *
216216+ * They can be ignored if you aren't adding/extending wrappers.
217217+ */
218218+namespace impl {
219219+/*!
220220+ * Type-aware wrapper for a field ID.
221221+ *
222222+ * This is a smarter alternative to just using jni::field_t since it avoids
223223+ * having to repeat the type in the accessor, without using any more storage.
224224+ *
225225+ * @see StaticFieldId for the equivalent for static fields.
226226+ * @see WrappedFieldId for the equivalent for structures that we wrap.
227227+ */
228228+template <typename T> struct FieldId {
229229+ public:
230230+ FieldId(jni::Class const &clazz, const char *name)
231231+ : id(clazz.getField<T>(name)) {}
232232+233233+ const jni::field_t id;
234234+};
235235+236236+/*!
237237+ * Get the value of field @p field in Java object @p obj.
238238+ *
239239+ * This is found by argument-dependent lookup and can be used unqualified.
240240+ *
241241+ * @relates FieldId
242242+ */
243243+template <typename T>
244244+static inline T get(FieldId<T> const &field, jni::Object const &obj) {
245245+ assert(!obj.isNull());
246246+ return obj.get<T>(field.id);
247247+}
248248+/*!
249249+ * Type-aware wrapper for a static field ID.
250250+ *
251251+ * This is a smarter alternative to just using jni::field_t since it avoids
252252+ * having to repeat the type in the accessor, without using any more storage.
253253+ *
254254+ * @see FieldId
255255+ */
256256+template <typename T> struct StaticFieldId {
257257+ public:
258258+ StaticFieldId(jni::Class const &clazz, const char *name)
259259+ : id(clazz.getStaticField<T>(name)) {}
260260+261261+ const jni::field_t id;
262262+};
263263+264264+/*!
265265+ * Get the value of static field @p field in Java type @p clazz.
266266+ *
267267+ * This is found by argument-dependent lookup and can be used unqualified.
268268+ *
269269+ * @relates FieldId
270270+ */
271271+template <typename T>
272272+static inline T get(StaticFieldId<T> const &field, jni::Class const &clazz) {
273273+ assert(!clazz.isNull());
274274+ return clazz.get<T>(field.id);
275275+}
276276+277277+/*!
278278+ * Type-aware wrapper for a field ID of a wrapped structure type.
279279+ *
280280+ * This is a smarter alternative to just using jni::field_t since it avoids
281281+ * having to repeat the type in the accessor, without using any more storage.
282282+ *
283283+ * Requires that the structure wrapper provides
284284+ * `static constexpr const char *getTypeName() noexcept;`
285285+ *
286286+ * @see FieldId
287287+ */
288288+template <typename T> struct WrappedFieldId {
289289+ public:
290290+ WrappedFieldId(jni::Class const &clazz, const char *name)
291291+ : id(lookupField(clazz, name)) {}
292292+293293+ const jni::field_t id;
294294+295295+ private:
296296+ /*!
297297+ * Helper for field ID lookup, mostly to avoid calling c_str() on a string
298298+ * temporary.
299299+ */
300300+ static jni::field_t lookupField(jni::Class const &clazz, const char *name) {
301301+ std::string fullType = std::string("L") + T::getTypeName() + ";";
302302+ return clazz.getField(name, fullType.c_str());
303303+ }
304304+};
305305+306306+/*!
307307+ * Get the value of field @p field in Java object @p obj.
308308+ *
309309+ * This is found by argument-dependent lookup and can be used unqualified.
310310+ *
311311+ * @relates WrappedFieldId
312312+ */
313313+template <typename T>
314314+static inline T get(WrappedFieldId<T> const &field, jni::Object const &obj) {
315315+ assert(!obj.isNull());
316316+ return T{obj.get<jni::Object>(field.id)};
317317+}
318318+} // namespace impl
319319+} // namespace wrap
+29
src/external/android-jni-wrap/wrap/README.md
···11+# About these JNI Wrappers
22+33+<!--
44+Copyright 2020, Collabora, Ltd.
55+SPDX-License-Identifier: BSL-1.0
66+-->
77+88+These are fairly simple wrappers around Java classes, using JNI and JNIPP to allow relatively-painless use of Java classes from C++. They are populated as-needed: if you need a method/property that's missing, add it!
99+1010+## Conventions
1111+1212+All classes derive from ObjectWrapperBase or one of its subclasses. (Yes, you
1313+can directly mirror inheritance in Java with inheritance in these wrappers.)
1414+1515+All should have a public internal struct called `Meta` that derives publicly
1616+from either `MetaBase` or `MetaBaseDroppable` (the more common option, when you
1717+don't often need the class object itself), with a member for each method ID.
1818+Only the `Meta()` constructor should be in the `.cpp` file for a given wrapper:
1919+the rest lives in the header so it may be inlined and thus optimized away.
2020+2121+## Finding signatures
2222+2323+A command like the following can help you get the JNI signatures of methods:
2424+2525+```sh
2626+javap -s -classpath ~/Android/Sdk/platforms/android-26/android.jar android.service.vr.VrListenerService
2727+```
2828+2929+Adjust the location of the SDK and the class under investigation as needed.