| /* |
| * |
| * Copyright (c) 2021 Project CHIP Authors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include <jni.h> |
| #include <lib/core/CHIPError.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/Span.h> |
| #include <lib/support/TypeTraits.h> |
| #include <string> |
| |
| #define JNI_LOCAL_REF_COUNT 256 |
| |
| namespace chip { |
| class JniLocalReferenceScope |
| { |
| public: |
| explicit JniLocalReferenceScope(JNIEnv * env) : mEnv(env) |
| { |
| if (mEnv->PushLocalFrame(JNI_LOCAL_REF_COUNT) == 0) |
| { |
| mlocalFramePushed = true; |
| } |
| } |
| |
| ~JniLocalReferenceScope() |
| { |
| if (mlocalFramePushed) |
| { |
| mEnv->PopLocalFrame(nullptr); |
| mlocalFramePushed = false; |
| } |
| } |
| |
| // Delete copy constructor and copy assignment operator |
| JniLocalReferenceScope(const JniLocalReferenceScope &) = delete; |
| JniLocalReferenceScope & operator=(const JniLocalReferenceScope &) = delete; |
| |
| private: |
| JNIEnv * const mEnv; |
| bool mlocalFramePushed = false; |
| }; |
| |
| class JniGlobalReference |
| { |
| public: |
| JniGlobalReference() {} |
| |
| CHIP_ERROR Init(jobject aObjectRef); |
| |
| JniGlobalReference(JniGlobalReference && aOther) |
| { |
| mObjectRef = aOther.mObjectRef; |
| aOther.mObjectRef = nullptr; |
| } |
| |
| ~JniGlobalReference() { Reset(); } |
| |
| void Reset(); |
| |
| jobject ObjectRef() const { return mObjectRef; } |
| |
| bool HasValidObjectRef() const { return mObjectRef != nullptr; } |
| |
| private: |
| jobject mObjectRef = nullptr; |
| }; |
| |
| class JniReferences |
| { |
| public: |
| // No copy, move or assignment. |
| JniReferences(const JniReferences &) = delete; |
| JniReferences(const JniReferences &&) = delete; |
| JniReferences & operator=(const JniReferences &) = delete; |
| |
| static JniReferences & GetInstance() |
| { |
| static JniReferences jniReferences; |
| return jniReferences; |
| } |
| |
| /** |
| * Set the JavaVM. |
| * |
| * we need clsType in context to get ClassLoader |
| * |
| * This must be called before GetEnvForCurrentThread(). |
| */ |
| void SetJavaVm(JavaVM * jvm, const char * clsType); |
| |
| /** |
| * Returns a JNIEnv for the current thread. |
| * |
| * This must be called after SetJavaVm(). If the current thread is not attached to the JVM, this method will attach the thread |
| * first, then retrieve the JNIEnv. |
| */ |
| JNIEnv * GetEnvForCurrentThread(); |
| |
| /** |
| * @brief |
| * Creates a local jclass reference to the given class type. |
| * |
| * This must be called after SetJavaVm(). |
| * |
| * @param[in] env The JNIEnv for finding a Java class and creating a new Java reference. |
| * @param[in] clsType The fully-qualified Java class name to find, e.g. java/lang/IllegalStateException. |
| * @param[out] outCls A Java reference to the class matching clsType. |
| */ |
| CHIP_ERROR GetLocalClassRef(JNIEnv * env, const char * clsType, jclass & outCls); |
| |
| CHIP_ERROR FindMethod(JNIEnv * env, jobject object, const char * methodName, const char * methodSignature, |
| jmethodID * methodId); |
| |
| CHIP_ERROR FindMethod(JNIEnv * env, jclass javaClass, const char * methodName, const char * methodSignature, |
| jmethodID * methodId); |
| |
| void CallVoidInt(JNIEnv * env, jobject object, const char * methodName, jint argument); |
| |
| CHIP_ERROR N2J_ByteArray(JNIEnv * env, const uint8_t * inArray, jsize inArrayLen, jbyteArray & outArray); |
| |
| void ReportError(JNIEnv * env, CHIP_ERROR cbErr, const char * functName); |
| |
| void ThrowError(JNIEnv * env, jclass exceptionCls, CHIP_ERROR errToThrow); |
| |
| void ThrowError(JNIEnv * env, JniGlobalReference & exceptionCls, CHIP_ERROR errToThrow); |
| /** |
| * Creates a java.util.Optional wrapping the specified jobject. If the wrapped jobject is null, an empty |
| * Optional will be returned. |
| */ |
| CHIP_ERROR CreateOptional(jobject objectToWrap, jobject & outOptional); |
| |
| /** |
| * Retrieve the value of a java.util.Optional, or nullptr if the Optional is empty. |
| */ |
| CHIP_ERROR GetOptionalValue(jobject optionalObj, jobject & optionalValue); |
| |
| /** |
| * Get a primitive jint from the Java boxed type Integer, using intValue(). |
| */ |
| jint IntegerToPrimitive(jobject boxedObject); |
| |
| /** |
| * Get a primitive jlong from the Java boxed type Long, using longValue(). |
| */ |
| jlong LongToPrimitive(jobject boxedObject); |
| |
| /** |
| * Get a primitive jboolean from the Java boxed type Booelan, using booleanValue(). |
| */ |
| jboolean BooleanToPrimitive(jobject boxedObject); |
| |
| /** |
| * Get a primitive jfloat from the Java boxed type Float, using floatValue(). |
| */ |
| jfloat FloatToPrimitive(jobject boxedObject); |
| |
| /** |
| * Get a primitive jfloat from the Java boxed type Double, using doubleValue(). |
| */ |
| jdouble DoubleToPrimitive(jobject boxedObject); |
| |
| CHIP_ERROR CreateArrayList(jobject & outList); |
| |
| CHIP_ERROR AddToList(jobject list, jobject objectToAdd); |
| |
| CHIP_ERROR GetListSize(jobject list, jint & size); |
| |
| CHIP_ERROR GetListItem(jobject list, jint index, jobject & outItem); |
| |
| CHIP_ERROR CreateHashMap(jobject & outMap); |
| |
| CHIP_ERROR PutInMap(jobject map, jobject key, jobject value); |
| |
| CHIP_ERROR GetObjectField(jobject objectToRead, const char * name, const char * signature, jobject & outObject); |
| |
| /** |
| * Call a void method with subscriptionId named "OnSubscriptionEstablished" on the provided jobject. |
| */ |
| CHIP_ERROR CallSubscriptionEstablished(jobject javaCallback, long subscriptionId); |
| |
| /** |
| * Creates a boxed type (e.g. java.lang.Integer) based on the the class name ("java/lang/Integer"), constructor JNI signature |
| * ("(I)V"), and value. |
| */ |
| template <class T, typename std::enable_if_t<!std::is_enum<T>::value, int> = 0> |
| CHIP_ERROR CreateBoxedObject(std::string boxedTypeClsName, std::string constructorSignature, T value, jobject & outObj) |
| { |
| JNIEnv * env = GetEnvForCurrentThread(); |
| VerifyOrReturnError(env != nullptr, CHIP_ERROR_INCORRECT_STATE); |
| jclass boxedTypeCls = nullptr; |
| ReturnErrorOnFailure(GetLocalClassRef(env, boxedTypeClsName.c_str(), boxedTypeCls)); |
| |
| jmethodID boxedTypeConstructor = env->GetMethodID(boxedTypeCls, "<init>", constructorSignature.c_str()); |
| outObj = env->NewObject(boxedTypeCls, boxedTypeConstructor, value); |
| return CHIP_NO_ERROR; |
| } |
| |
| /** |
| * Handling for strongly-typed enums. |
| */ |
| template <class T, typename std::enable_if_t<std::is_enum<T>::value, int> = 0> |
| CHIP_ERROR CreateBoxedObject(std::string boxedTypeClsName, std::string constructorSignature, T value, jobject & outObj) |
| { |
| return CreateBoxedObject(boxedTypeClsName, constructorSignature, chip::to_underlying(value), outObj); |
| } |
| |
| /** |
| * Use instead of 'NewStringUTF' function |
| * If the value is not decoded with "UTF-8", the error will be returned. |
| * (The NewStringUTF function crashes when the value can not decoded as "UTF-8".) |
| * |
| * Creates a java string type based on char array. |
| */ |
| CHIP_ERROR CharToStringUTF(const chip::CharSpan & charSpan, jobject & outString); |
| |
| private: |
| JniReferences() {} |
| |
| JavaVM * mJvm = nullptr; |
| JniGlobalReference mClassLoader; |
| jmethodID mFindClassMethod = nullptr; |
| |
| JniGlobalReference mHashMapClass; |
| JniGlobalReference mListClass; |
| JniGlobalReference mArrayListClass; |
| }; |
| |
| } // namespace chip |