| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * 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. |
| */ |
| |
| #include <jni.h> |
| |
| #include <memory> |
| #include <unordered_map> |
| #include <string> |
| |
| #include "base/utilities.h" |
| #include "core/value.h" |
| |
| #ifndef ANDROID_FILTERFW_JNI_JNI_UTIL_H |
| #define ANDROID_FILTERFW_JNI_JNI_UTIL_H |
| |
| // We add this JNI_NULL macro to allow consistent code separation of Java and |
| // C++ types. |
| #define JNI_NULL NULL |
| |
| #if 0 |
| // Pointer to current JavaVM. Do not use this directly. Instead use the funciton |
| // GetCurrentJavaVM(). |
| extern JavaVM* g_current_java_vm_; |
| |
| // Wrapper around a java object pointer, which includes the environment |
| // pointer in which the object "lives". This is used for passing down Java |
| // objects from the Java layer to C++. |
| // While an instance of this class does not own the underlying java object, it |
| // does hold a global reference to it, so that the Java garbage collector does |
| // not destroy it. It uses reference counting to determine when it can destroy |
| // the reference. |
| // TODO: Add multi-thread support! |
| class JavaObject { |
| public: |
| // Creates a NULL JavaObject. |
| JavaObject(); |
| |
| // Creates a wrapper around the given object in the given JNI environment. |
| JavaObject(jobject object, JNIEnv* env); |
| |
| // Copy constructor. |
| JavaObject(const JavaObject& java_obj); |
| |
| // Destructor. |
| ~JavaObject(); |
| |
| // Assignment operator. |
| JavaObject& operator=(const JavaObject& java_obj); |
| |
| // Access to the object (non-const as JNI functions are non-const). |
| jobject object() const { |
| return object_; |
| } |
| |
| // Resets this object to the NULL JavaObject. |
| void Reset(); |
| |
| private: |
| // Retain the instance, i.e. increase reference count. |
| void Retain(); |
| |
| // Release the instance, i.e. decrease reference count. |
| void Release(); |
| |
| // The object pointer (not owned). |
| jobject object_; |
| |
| // The reference count of this object |
| int* ref_count_; |
| }; |
| #endif |
| |
| // ObjectPool template class. This class keeps track of C++ instances that are |
| // coupled to Java objects. This is done by using an "id" field in the Java |
| // object, which is then mapped to the correct instance here. It should not be |
| // necessary to use this class directly. Instead, the convenience functions |
| // below can be used. |
| template<class T> |
| class ObjectPool { |
| public: |
| // Create a new ObjectPool for a specific object type. Pass the path to the |
| // Java equivalent class of the C++ class, and the name of the java member |
| // field that will store the object's ID. |
| static void Setup(const std::string& jclass_name, |
| const std::string& id_fld_name) { |
| instance_ = new ObjectPool<T>(jclass_name, id_fld_name); |
| } |
| |
| // Return the shared instance to this type's pool. |
| static ObjectPool* Instance() { |
| return instance_; |
| } |
| |
| // Delete this type's pool. |
| static void TearDown() { |
| delete instance_; |
| } |
| |
| // Register a new C++ object with the pool. This does not affect the Java |
| // layer. Use WrapObject() instead to perform the necessary Java-side |
| // assignments. Pass true to owns if the object pool owns the object. |
| int RegisterObject(T* object, bool owns) { |
| const int id = next_id_; |
| objects_[id] = object; |
| owns_[id] = owns; |
| ++next_id_; |
| return id; |
| } |
| |
| // Return the object in the pool with the specified ID. |
| T* ObjectWithID(int obj_id) const { |
| typename CObjMap::const_iterator iter = objects_.find(obj_id); |
| return iter == objects_.end() ? NULL : iter->second; |
| } |
| |
| // Get the ID of a Java object. This ID can be used to look-up the C++ |
| // object. |
| int GetObjectID(JNIEnv* env, jobject j_object) { |
| jclass cls = env->GetObjectClass(j_object); |
| jfieldID id_field = env->GetFieldID(cls, id_field_name_.c_str(), "I"); |
| const int result = env->GetIntField(j_object, id_field); |
| env->DeleteLocalRef(cls); |
| return result; |
| } |
| |
| // Take a C++ object and wrap it with a given Java object. This will |
| // essentially set the ID member of the Java object to the ID of the C++ |
| // object. Pass true to owns if the object pool owns the object. |
| bool WrapObject(T* c_object, JNIEnv* env, jobject j_object, bool owns) { |
| const int id = RegisterObject(c_object, owns); |
| jclass cls = env->GetObjectClass(j_object); |
| jfieldID id_field = env->GetFieldID(cls, id_field_name_.c_str(), "I"); |
| env->SetIntField(j_object, id_field, id); |
| env->DeleteLocalRef(cls); |
| return true; |
| } |
| |
| // Remove the object with the given ID from this pool, and delete it. This |
| // does not affect the Java layer. |
| bool DeleteObjectWithID(int obj_id) { |
| typename CObjMap::iterator iter = objects_.find(obj_id); |
| const bool found = iter != objects_.end(); |
| if (found) { |
| if (owns_[obj_id]) |
| delete iter->second; |
| objects_.erase(iter); |
| } |
| return found; |
| } |
| |
| // Instantiates a new java object for this class. The Java class must have |
| // a default constructor for this to succeed. |
| jobject CreateJavaObject(JNIEnv* env) { |
| jclass cls = env->FindClass(jclass_name_.c_str()); |
| jmethodID constructor = env->GetMethodID( |
| cls, |
| "<init>", |
| "(Landroid/filterfw/core/NativeAllocatorTag;)V"); |
| jobject result = env->NewObject(cls, constructor, JNI_NULL); |
| env->DeleteLocalRef(cls); |
| return result; |
| } |
| |
| int GetObjectCount() const { |
| return objects_.size(); |
| } |
| |
| const std::string& GetJavaClassName() const { |
| return jclass_name_; |
| } |
| |
| private: |
| explicit ObjectPool(const std::string& jclass_name, |
| const std::string& id_fld_name) |
| : jclass_name_(jclass_name), |
| id_field_name_(id_fld_name), |
| next_id_(0) { } |
| |
| typedef std::unordered_map<int, T*> CObjMap; |
| typedef std::unordered_map<int, bool> FlagMap; |
| static ObjectPool* instance_; |
| std::string jclass_name_; |
| std::string id_field_name_; |
| int next_id_; |
| CObjMap objects_; |
| FlagMap owns_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ObjectPool); |
| }; |
| |
| template<typename T> ObjectPool<T>* ObjectPool<T>::instance_ = NULL; |
| |
| // Convenience Functions /////////////////////////////////////////////////////// |
| |
| // This function "links" the C++ instance and the Java instance, so that they |
| // can be mapped to one another. This must be called for every C++ instance |
| // which is wrapped by a Java front-end interface. Pass true to owns, if the |
| // Java layer should own the object. |
| template<typename T> |
| bool WrapObjectInJava(T* c_object, JNIEnv* env, jobject j_object, bool owns) { |
| ObjectPool<T>* pool = ObjectPool<T>::Instance(); |
| return pool ? pool->WrapObject(c_object, env, j_object, owns) : false; |
| } |
| |
| // Calls WrapObjectInJava, safely freeing c_object if object creation fails. |
| template<typename T> |
| bool WrapOwnedObjectInJava(std::unique_ptr<T> c_object, JNIEnv* env, |
| jobject j_object, bool owns) { |
| if (!WrapObjectInJava<T>(c_object.get(), env, j_object, owns)) |
| return false; |
| // If we succeeded, a Java object now owns our c object; don't free it. |
| c_object.release(); |
| return true; |
| } |
| |
| // Creates a new Java instance, which wraps the passed C++ instance. Returns |
| // the wrapped object or JNI_NULL if there was an error. Pass true to owns, if |
| // the Java layer should own the object. |
| template<typename T> |
| jobject WrapNewObjectInJava(T* c_object, JNIEnv* env, bool owns) { |
| ObjectPool<T>* pool = ObjectPool<T>::Instance(); |
| if (pool) { |
| jobject result = pool->CreateJavaObject(env); |
| if (WrapObjectInJava(c_object, env, result, owns)) |
| return result; |
| } |
| return JNI_NULL; |
| } |
| |
| // Use ConvertFromJava to obtain a C++ instance given a Java object. This |
| // instance must have been wrapped in Java using the WrapObjectInJava() |
| // function. |
| template<typename T> |
| T* ConvertFromJava(JNIEnv* env, jobject j_object) { |
| ObjectPool<T>* pool = ObjectPool<T>::Instance(); |
| return pool && j_object |
| ? pool->ObjectWithID(pool->GetObjectID(env, j_object)) |
| : NULL; |
| } |
| |
| // Delete the native object given a Java instance. This should be called from |
| // the Java object's finalizer. |
| template<typename T> |
| bool DeleteNativeObject(JNIEnv* env, jobject j_object) { |
| ObjectPool<T>* pool = ObjectPool<T>::Instance(); |
| return pool && j_object |
| ? pool->DeleteObjectWithID(pool->GetObjectID(env, j_object)) |
| : false; |
| } |
| |
| #if 0 |
| // Get the current JNI VM, or NULL if there is no current VM |
| JavaVM* GetCurrentJavaVM(); |
| |
| // Get the current JNI environment, or NULL if this is not a JNI thread |
| JNIEnv* GetCurrentJNIEnv(); |
| #endif |
| |
| // Convert C++ boolean to Java boolean. |
| jboolean ToJBool(bool value); |
| |
| // Convert Java boolean to C++ boolean. |
| bool ToCppBool(jboolean value); |
| |
| // Convert Java String to C++ string. |
| jstring ToJString(JNIEnv* env, const std::string& value); |
| |
| // Convert C++ string to Java String. |
| std::string ToCppString(JNIEnv* env, jstring value); |
| |
| // Convert Java object to a (C) Value object. |
| Value ToCValue(JNIEnv* env, jobject object); |
| |
| // Convert a (C) Value object to a Java object. |
| jobject ToJObject(JNIEnv* env, const Value& value); |
| |
| // Returns true, iff the passed object is an instance of the class specified |
| // by its fully qualified class name. |
| bool IsJavaInstanceOf(JNIEnv* env, jobject object, |
| const std::string& class_name); |
| |
| #endif // ANDROID_FILTERFW_JNI_JNI_UTIL_H |