/* * 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 #include #include #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 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(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, "", "(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::hash_map CObjMap; typedef std::hash_map 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 ObjectPool* ObjectPool::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 bool WrapObjectInJava(T* c_object, JNIEnv* env, jobject j_object, bool owns) { ObjectPool* pool = ObjectPool::Instance(); return pool ? pool->WrapObject(c_object, env, j_object, owns) : false; } // 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 jobject WrapNewObjectInJava(T* c_object, JNIEnv* env, bool owns) { ObjectPool* pool = ObjectPool::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 T* ConvertFromJava(JNIEnv* env, jobject j_object) { ObjectPool* pool = ObjectPool::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 bool DeleteNativeObject(JNIEnv* env, jobject j_object) { ObjectPool* pool = ObjectPool::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