/* * * 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 #include #include #include #include #include #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); void CallVoidLong(JNIEnv * env, jobject object, const char * methodName, jlong 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); /** * Get a primitive jshort from the Java boxed type Short, using shortValue(). */ jshort ShortToPrimitive(jobject boxedShort); 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 ::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, "", constructorSignature.c_str()); outObj = env->NewObject(boxedTypeCls, boxedTypeConstructor, value); return CHIP_NO_ERROR; } /** * Handling for strongly-typed enums. */ template ::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