1*14675a02SAndroid Build Coastguard Worker /*
2*14675a02SAndroid Build Coastguard Worker * Copyright 2022 Google LLC
3*14675a02SAndroid Build Coastguard Worker *
4*14675a02SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*14675a02SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*14675a02SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*14675a02SAndroid Build Coastguard Worker *
8*14675a02SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*14675a02SAndroid Build Coastguard Worker *
10*14675a02SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*14675a02SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*14675a02SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*14675a02SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*14675a02SAndroid Build Coastguard Worker * limitations under the License.
15*14675a02SAndroid Build Coastguard Worker */
16*14675a02SAndroid Build Coastguard Worker
17*14675a02SAndroid Build Coastguard Worker #ifndef FCP_JNI_JNI_UTIL_H_
18*14675a02SAndroid Build Coastguard Worker #define FCP_JNI_JNI_UTIL_H_
19*14675a02SAndroid Build Coastguard Worker
20*14675a02SAndroid Build Coastguard Worker #include <jni.h>
21*14675a02SAndroid Build Coastguard Worker
22*14675a02SAndroid Build Coastguard Worker #include "absl/cleanup/cleanup.h"
23*14675a02SAndroid Build Coastguard Worker #include "absl/container/fixed_array.h"
24*14675a02SAndroid Build Coastguard Worker #include "fcp/base/monitoring.h"
25*14675a02SAndroid Build Coastguard Worker
26*14675a02SAndroid Build Coastguard Worker namespace fcp {
27*14675a02SAndroid Build Coastguard Worker namespace jni {
28*14675a02SAndroid Build Coastguard Worker
29*14675a02SAndroid Build Coastguard Worker // Creates a JNIEnv via the passed JavaVM*, attaching the current thread if it
30*14675a02SAndroid Build Coastguard Worker // is not already. If an attach was needed, detaches when this is destroyed.
31*14675a02SAndroid Build Coastguard Worker //
32*14675a02SAndroid Build Coastguard Worker // ScopedJniEnv must not be shared among threads and destructs on the same
33*14675a02SAndroid Build Coastguard Worker // thread.
34*14675a02SAndroid Build Coastguard Worker class ScopedJniEnv final {
35*14675a02SAndroid Build Coastguard Worker public:
ScopedJniEnv(JavaVM * jvm)36*14675a02SAndroid Build Coastguard Worker explicit ScopedJniEnv(JavaVM* jvm)
37*14675a02SAndroid Build Coastguard Worker : jvm_(jvm), env_(nullptr), is_attached_(false) {
38*14675a02SAndroid Build Coastguard Worker // We don't make any assumptions about the state of the current thread, and
39*14675a02SAndroid Build Coastguard Worker // we want to leave it in the state we received it with respect to the
40*14675a02SAndroid Build Coastguard Worker // JavaVm. So we only attach and detach when needed, and we always delete
41*14675a02SAndroid Build Coastguard Worker // local references.
42*14675a02SAndroid Build Coastguard Worker jint error = jvm_->GetEnv(reinterpret_cast<void**>(&env_), JNI_VERSION_1_2);
43*14675a02SAndroid Build Coastguard Worker if (error != JNI_OK) {
44*14675a02SAndroid Build Coastguard Worker error = AttachCurrentThread(jvm_, &env_);
45*14675a02SAndroid Build Coastguard Worker FCP_CHECK(error == JNI_OK);
46*14675a02SAndroid Build Coastguard Worker is_attached_ = true;
47*14675a02SAndroid Build Coastguard Worker }
48*14675a02SAndroid Build Coastguard Worker }
49*14675a02SAndroid Build Coastguard Worker
~ScopedJniEnv()50*14675a02SAndroid Build Coastguard Worker virtual ~ScopedJniEnv() {
51*14675a02SAndroid Build Coastguard Worker if (is_attached_) {
52*14675a02SAndroid Build Coastguard Worker (void)jvm_->DetachCurrentThread();
53*14675a02SAndroid Build Coastguard Worker }
54*14675a02SAndroid Build Coastguard Worker }
55*14675a02SAndroid Build Coastguard Worker
env()56*14675a02SAndroid Build Coastguard Worker JNIEnv* env() { return env_; }
57*14675a02SAndroid Build Coastguard Worker
58*14675a02SAndroid Build Coastguard Worker private:
59*14675a02SAndroid Build Coastguard Worker template <typename JNIEnvArgType>
AttachCurrentThreadImpl(JavaVM * vm,jint (JavaVM::* fn)(JNIEnvArgType,void *),JNIEnv ** env)60*14675a02SAndroid Build Coastguard Worker static jint AttachCurrentThreadImpl(JavaVM* vm,
61*14675a02SAndroid Build Coastguard Worker jint (JavaVM::*fn)(JNIEnvArgType, void*),
62*14675a02SAndroid Build Coastguard Worker JNIEnv** env) {
63*14675a02SAndroid Build Coastguard Worker static_assert(std::is_same_v<JNIEnvArgType, void**> ||
64*14675a02SAndroid Build Coastguard Worker std::is_same_v<JNIEnvArgType, JNIEnv**>);
65*14675a02SAndroid Build Coastguard Worker return (vm->*fn)(reinterpret_cast<JNIEnvArgType>(env), nullptr);
66*14675a02SAndroid Build Coastguard Worker }
67*14675a02SAndroid Build Coastguard Worker
AttachCurrentThread(JavaVM * vm,JNIEnv ** env)68*14675a02SAndroid Build Coastguard Worker static jint AttachCurrentThread(JavaVM* vm, JNIEnv** env) {
69*14675a02SAndroid Build Coastguard Worker // The NDK and JDK versions of jni.h disagree on the signatures for the
70*14675a02SAndroid Build Coastguard Worker // JavaVM::AttachCurrentThread member function (the former uses 'JavaVM*'
71*14675a02SAndroid Build Coastguard Worker // and the latter uses 'void**'). To avoid causing linker errors when the
72*14675a02SAndroid Build Coastguard Worker // JDK's jni.h is accidentally put on the include path during an Android
73*14675a02SAndroid Build Coastguard Worker // build, we use the indirection below when calling the function. It's not
74*14675a02SAndroid Build Coastguard Worker // sufficient to #ifdef around __ANDROID__, because whatever is including
75*14675a02SAndroid Build Coastguard Worker // this header file might put the JDK jni.h version on the include path.
76*14675a02SAndroid Build Coastguard Worker return AttachCurrentThreadImpl(vm, &JavaVM::AttachCurrentThread, env);
77*14675a02SAndroid Build Coastguard Worker }
78*14675a02SAndroid Build Coastguard Worker
79*14675a02SAndroid Build Coastguard Worker ScopedJniEnv(const ScopedJniEnv&) = delete;
80*14675a02SAndroid Build Coastguard Worker void operator=(const ScopedJniEnv&) = delete;
81*14675a02SAndroid Build Coastguard Worker
82*14675a02SAndroid Build Coastguard Worker JavaVM* jvm_;
83*14675a02SAndroid Build Coastguard Worker JNIEnv* env_;
84*14675a02SAndroid Build Coastguard Worker bool is_attached_;
85*14675a02SAndroid Build Coastguard Worker };
86*14675a02SAndroid Build Coastguard Worker
87*14675a02SAndroid Build Coastguard Worker // Parses a proto from a Java byte array.
88*14675a02SAndroid Build Coastguard Worker //
89*14675a02SAndroid Build Coastguard Worker // If any JNI calls fail, or if the parsing of the proto fails, then this
90*14675a02SAndroid Build Coastguard Worker // FCP_CHECK-fails.
91*14675a02SAndroid Build Coastguard Worker //
92*14675a02SAndroid Build Coastguard Worker // This method does not call `JNIEnv::DeleteLocalRef` on the given `jbyteArray`.
93*14675a02SAndroid Build Coastguard Worker //
94*14675a02SAndroid Build Coastguard Worker // This is meant to be used as a convenient way to use serialized protobufs as
95*14675a02SAndroid Build Coastguard Worker // part of a JNI API contract, since in such cases we can safely assume that the
96*14675a02SAndroid Build Coastguard Worker // input argument will always be a valid proto (and anything else would be a
97*14675a02SAndroid Build Coastguard Worker // programmer error).
98*14675a02SAndroid Build Coastguard Worker template <typename MessageT>
ParseProtoFromJByteArray(JNIEnv * env,jbyteArray byte_array)99*14675a02SAndroid Build Coastguard Worker static MessageT ParseProtoFromJByteArray(JNIEnv* env, jbyteArray byte_array) {
100*14675a02SAndroid Build Coastguard Worker MessageT out_message;
101*14675a02SAndroid Build Coastguard Worker
102*14675a02SAndroid Build Coastguard Worker jsize length = env->GetArrayLength(byte_array);
103*14675a02SAndroid Build Coastguard Worker FCP_CHECK(!env->ExceptionCheck());
104*14675a02SAndroid Build Coastguard Worker
105*14675a02SAndroid Build Coastguard Worker if (length == 0) {
106*14675a02SAndroid Build Coastguard Worker return std::move(out_message);
107*14675a02SAndroid Build Coastguard Worker }
108*14675a02SAndroid Build Coastguard Worker // This will make a copy of the data into buffer, but generally the proto data
109*14675a02SAndroid Build Coastguard Worker // will small enough that this shouldn't matter.
110*14675a02SAndroid Build Coastguard Worker absl::FixedArray<jbyte> buffer(length);
111*14675a02SAndroid Build Coastguard Worker env->GetByteArrayRegion(byte_array, 0, length, buffer.data());
112*14675a02SAndroid Build Coastguard Worker FCP_CHECK(!env->ExceptionCheck());
113*14675a02SAndroid Build Coastguard Worker
114*14675a02SAndroid Build Coastguard Worker FCP_CHECK(out_message.ParseFromArray(buffer.data(), length));
115*14675a02SAndroid Build Coastguard Worker
116*14675a02SAndroid Build Coastguard Worker return std::move(out_message);
117*14675a02SAndroid Build Coastguard Worker }
118*14675a02SAndroid Build Coastguard Worker
119*14675a02SAndroid Build Coastguard Worker // Serializes a proto to a `jbyteArray`.
120*14675a02SAndroid Build Coastguard Worker //
121*14675a02SAndroid Build Coastguard Worker // The caller must call `JNIEnv::DeleteLocalRef` on the returned `jbyteArray`
122*14675a02SAndroid Build Coastguard Worker // once it is done with it.
123*14675a02SAndroid Build Coastguard Worker //
124*14675a02SAndroid Build Coastguard Worker // If any JNI calls fail, then this FCP_CHECK-fails.
125*14675a02SAndroid Build Coastguard Worker template <typename MessageT>
SerializeProtoToJByteArray(JNIEnv * env,const MessageT & proto)126*14675a02SAndroid Build Coastguard Worker static jbyteArray SerializeProtoToJByteArray(JNIEnv* env,
127*14675a02SAndroid Build Coastguard Worker const MessageT& proto) {
128*14675a02SAndroid Build Coastguard Worker int length = static_cast<int>(proto.ByteSizeLong());
129*14675a02SAndroid Build Coastguard Worker
130*14675a02SAndroid Build Coastguard Worker jbyteArray byte_array = env->NewByteArray(length);
131*14675a02SAndroid Build Coastguard Worker FCP_CHECK(byte_array != nullptr);
132*14675a02SAndroid Build Coastguard Worker FCP_CHECK(!env->ExceptionCheck());
133*14675a02SAndroid Build Coastguard Worker
134*14675a02SAndroid Build Coastguard Worker // This serializes into a buffer and then copies that buffer to the Java byte
135*14675a02SAndroid Build Coastguard Worker // array. The proto data is generally small enough that this extra copy
136*14675a02SAndroid Build Coastguard Worker // shouldn't matter.
137*14675a02SAndroid Build Coastguard Worker absl::FixedArray<jbyte> buffer(length);
138*14675a02SAndroid Build Coastguard Worker proto.SerializeToArray(buffer.data(), length);
139*14675a02SAndroid Build Coastguard Worker
140*14675a02SAndroid Build Coastguard Worker env->SetByteArrayRegion(byte_array, 0, length, buffer.data());
141*14675a02SAndroid Build Coastguard Worker FCP_CHECK(!env->ExceptionCheck());
142*14675a02SAndroid Build Coastguard Worker
143*14675a02SAndroid Build Coastguard Worker return byte_array;
144*14675a02SAndroid Build Coastguard Worker }
145*14675a02SAndroid Build Coastguard Worker
146*14675a02SAndroid Build Coastguard Worker // Describes the method name and JNI method signature of a Java callback.
147*14675a02SAndroid Build Coastguard Worker struct JavaMethodSig {
148*14675a02SAndroid Build Coastguard Worker char const* name;
149*14675a02SAndroid Build Coastguard Worker char const* signature;
150*14675a02SAndroid Build Coastguard Worker };
151*14675a02SAndroid Build Coastguard Worker // Describes the field name and JNI type signature of a Java field.
152*14675a02SAndroid Build Coastguard Worker struct JavaFieldSig {
153*14675a02SAndroid Build Coastguard Worker char const* name;
154*14675a02SAndroid Build Coastguard Worker char const* signature;
155*14675a02SAndroid Build Coastguard Worker };
156*14675a02SAndroid Build Coastguard Worker
157*14675a02SAndroid Build Coastguard Worker // A utility for ensuring that a local JNI reference is deleted once the object
158*14675a02SAndroid Build Coastguard Worker // goes out of scope. This class is only intended to be used inside a function
159*14675a02SAndroid Build Coastguard Worker // body (and not to be returned or passed as an argument).
160*14675a02SAndroid Build Coastguard Worker class LocalRefDeleter {
161*14675a02SAndroid Build Coastguard Worker public:
LocalRefDeleter(JNIEnv * env,jobject local_ref)162*14675a02SAndroid Build Coastguard Worker LocalRefDeleter(JNIEnv* env, jobject local_ref)
163*14675a02SAndroid Build Coastguard Worker : env_(env), local_ref_(local_ref) {}
164*14675a02SAndroid Build Coastguard Worker // Prevent copies & moves, to make it harder to accidentally have this object
165*14675a02SAndroid Build Coastguard Worker // be passed as a parameter or return type.
166*14675a02SAndroid Build Coastguard Worker LocalRefDeleter(LocalRefDeleter& other) = delete;
167*14675a02SAndroid Build Coastguard Worker LocalRefDeleter(LocalRefDeleter&& other) = delete;
~LocalRefDeleter()168*14675a02SAndroid Build Coastguard Worker ~LocalRefDeleter() { env_->DeleteLocalRef(local_ref_); }
169*14675a02SAndroid Build Coastguard Worker
170*14675a02SAndroid Build Coastguard Worker private:
171*14675a02SAndroid Build Coastguard Worker JNIEnv* env_;
172*14675a02SAndroid Build Coastguard Worker jobject local_ref_;
173*14675a02SAndroid Build Coastguard Worker };
174*14675a02SAndroid Build Coastguard Worker
175*14675a02SAndroid Build Coastguard Worker } // namespace jni
176*14675a02SAndroid Build Coastguard Worker } // namespace fcp
177*14675a02SAndroid Build Coastguard Worker
178*14675a02SAndroid Build Coastguard Worker #endif // FCP_JNI_JNI_UTIL_H_
179