xref: /aosp_15_r20/external/federated-compute/fcp/jni/jni_util.h (revision 14675a029014e728ec732f129a32e299b2da0601)
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