1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <jni.h> 18 19#include <hash_map> 20#include <string> 21 22#include "base/utilities.h" 23#include "core/value.h" 24 25#ifndef ANDROID_FILTERFW_JNI_JNI_UTIL_H 26#define ANDROID_FILTERFW_JNI_JNI_UTIL_H 27 28// We add this JNI_NULL macro to allow consistent code separation of Java and 29// C++ types. 30#define JNI_NULL NULL 31 32#if 0 33// Pointer to current JavaVM. Do not use this directly. Instead use the funciton 34// GetCurrentJavaVM(). 35extern JavaVM* g_current_java_vm_; 36 37// Wrapper around a java object pointer, which includes the environment 38// pointer in which the object "lives". This is used for passing down Java 39// objects from the Java layer to C++. 40// While an instance of this class does not own the underlying java object, it 41// does hold a global reference to it, so that the Java garbage collector does 42// not destroy it. It uses reference counting to determine when it can destroy 43// the reference. 44// TODO: Add multi-thread support! 45class JavaObject { 46 public: 47 // Creates a NULL JavaObject. 48 JavaObject(); 49 50 // Creates a wrapper around the given object in the given JNI environment. 51 JavaObject(jobject object, JNIEnv* env); 52 53 // Copy constructor. 54 JavaObject(const JavaObject& java_obj); 55 56 // Destructor. 57 ~JavaObject(); 58 59 // Assignment operator. 60 JavaObject& operator=(const JavaObject& java_obj); 61 62 // Access to the object (non-const as JNI functions are non-const). 63 jobject object() const { 64 return object_; 65 } 66 67 // Resets this object to the NULL JavaObject. 68 void Reset(); 69 70 private: 71 // Retain the instance, i.e. increase reference count. 72 void Retain(); 73 74 // Release the instance, i.e. decrease reference count. 75 void Release(); 76 77 // The object pointer (not owned). 78 jobject object_; 79 80 // The reference count of this object 81 int* ref_count_; 82}; 83#endif 84 85// ObjectPool template class. This class keeps track of C++ instances that are 86// coupled to Java objects. This is done by using an "id" field in the Java 87// object, which is then mapped to the correct instance here. It should not be 88// necessary to use this class directly. Instead, the convenience functions 89// below can be used. 90template<class T> 91class ObjectPool { 92 public: 93 // Create a new ObjectPool for a specific object type. Pass the path to the 94 // Java equivalent class of the C++ class, and the name of the java member 95 // field that will store the object's ID. 96 static void Setup(const std::string& jclass_name, 97 const std::string& id_fld_name) { 98 instance_ = new ObjectPool<T>(jclass_name, id_fld_name); 99 } 100 101 // Return the shared instance to this type's pool. 102 static ObjectPool* Instance() { 103 return instance_; 104 } 105 106 // Delete this type's pool. 107 static void TearDown() { 108 delete instance_; 109 } 110 111 // Register a new C++ object with the pool. This does not affect the Java 112 // layer. Use WrapObject() instead to perform the necessary Java-side 113 // assignments. Pass true to owns if the object pool owns the object. 114 int RegisterObject(T* object, bool owns) { 115 const int id = next_id_; 116 objects_[id] = object; 117 owns_[id] = owns; 118 ++next_id_; 119 return id; 120 } 121 122 // Return the object in the pool with the specified ID. 123 T* ObjectWithID(int obj_id) const { 124 typename CObjMap::const_iterator iter = objects_.find(obj_id); 125 return iter == objects_.end() ? NULL : iter->second; 126 } 127 128 // Get the ID of a Java object. This ID can be used to look-up the C++ 129 // object. 130 int GetObjectID(JNIEnv* env, jobject j_object) { 131 jclass cls = env->GetObjectClass(j_object); 132 jfieldID id_field = env->GetFieldID(cls, id_field_name_.c_str(), "I"); 133 const int result = env->GetIntField(j_object, id_field); 134 env->DeleteLocalRef(cls); 135 return result; 136 } 137 138 // Take a C++ object and wrap it with a given Java object. This will 139 // essentially set the ID member of the Java object to the ID of the C++ 140 // object. Pass true to owns if the object pool owns the object. 141 bool WrapObject(T* c_object, JNIEnv* env, jobject j_object, bool owns) { 142 const int id = RegisterObject(c_object, owns); 143 jclass cls = env->GetObjectClass(j_object); 144 jfieldID id_field = env->GetFieldID(cls, id_field_name_.c_str(), "I"); 145 env->SetIntField(j_object, id_field, id); 146 env->DeleteLocalRef(cls); 147 return true; 148 } 149 150 // Remove the object with the given ID from this pool, and delete it. This 151 // does not affect the Java layer. 152 bool DeleteObjectWithID(int obj_id) { 153 typename CObjMap::iterator iter = objects_.find(obj_id); 154 const bool found = iter != objects_.end(); 155 if (found) { 156 if (owns_[obj_id]) 157 delete iter->second; 158 objects_.erase(iter); 159 } 160 return found; 161 } 162 163 // Instantiates a new java object for this class. The Java class must have 164 // a default constructor for this to succeed. 165 jobject CreateJavaObject(JNIEnv* env) { 166 jclass cls = env->FindClass(jclass_name_.c_str()); 167 jmethodID constructor = env->GetMethodID( 168 cls, 169 "<init>", 170 "(Landroid/filterfw/core/NativeAllocatorTag;)V"); 171 jobject result = env->NewObject(cls, constructor, JNI_NULL); 172 env->DeleteLocalRef(cls); 173 return result; 174 } 175 176 int GetObjectCount() const { 177 return objects_.size(); 178 } 179 180 const std::string& GetJavaClassName() const { 181 return jclass_name_; 182 } 183 184 private: 185 explicit ObjectPool(const std::string& jclass_name, 186 const std::string& id_fld_name) 187 : jclass_name_(jclass_name), 188 id_field_name_(id_fld_name), 189 next_id_(0) { } 190 191 typedef std::hash_map<int, T*> CObjMap; 192 typedef std::hash_map<int, bool> FlagMap; 193 static ObjectPool* instance_; 194 std::string jclass_name_; 195 std::string id_field_name_; 196 int next_id_; 197 CObjMap objects_; 198 FlagMap owns_; 199 200 DISALLOW_COPY_AND_ASSIGN(ObjectPool); 201}; 202 203template<typename T> ObjectPool<T>* ObjectPool<T>::instance_ = NULL; 204 205// Convenience Functions /////////////////////////////////////////////////////// 206 207// This function "links" the C++ instance and the Java instance, so that they 208// can be mapped to one another. This must be called for every C++ instance 209// which is wrapped by a Java front-end interface. Pass true to owns, if the 210// Java layer should own the object. 211template<typename T> 212bool WrapObjectInJava(T* c_object, JNIEnv* env, jobject j_object, bool owns) { 213 ObjectPool<T>* pool = ObjectPool<T>::Instance(); 214 return pool ? pool->WrapObject(c_object, env, j_object, owns) : false; 215} 216 217// Creates a new Java instance, which wraps the passed C++ instance. Returns 218// the wrapped object or JNI_NULL if there was an error. Pass true to owns, if 219// the Java layer should own the object. 220template<typename T> 221jobject WrapNewObjectInJava(T* c_object, JNIEnv* env, bool owns) { 222 ObjectPool<T>* pool = ObjectPool<T>::Instance(); 223 if (pool) { 224 jobject result = pool->CreateJavaObject(env); 225 if (WrapObjectInJava(c_object, env, result, owns)) 226 return result; 227 } 228 return JNI_NULL; 229} 230 231// Use ConvertFromJava to obtain a C++ instance given a Java object. This 232// instance must have been wrapped in Java using the WrapObjectInJava() 233// function. 234template<typename T> 235T* ConvertFromJava(JNIEnv* env, jobject j_object) { 236 ObjectPool<T>* pool = ObjectPool<T>::Instance(); 237 return pool && j_object 238 ? pool->ObjectWithID(pool->GetObjectID(env, j_object)) 239 : NULL; 240} 241 242// Delete the native object given a Java instance. This should be called from 243// the Java object's finalizer. 244template<typename T> 245bool DeleteNativeObject(JNIEnv* env, jobject j_object) { 246 ObjectPool<T>* pool = ObjectPool<T>::Instance(); 247 return pool && j_object 248 ? pool->DeleteObjectWithID(pool->GetObjectID(env, j_object)) 249 : false; 250} 251 252#if 0 253// Get the current JNI VM, or NULL if there is no current VM 254JavaVM* GetCurrentJavaVM(); 255 256// Get the current JNI environment, or NULL if this is not a JNI thread 257JNIEnv* GetCurrentJNIEnv(); 258#endif 259 260// Convert C++ boolean to Java boolean. 261jboolean ToJBool(bool value); 262 263// Convert Java boolean to C++ boolean. 264bool ToCppBool(jboolean value); 265 266// Convert Java String to C++ string. 267jstring ToJString(JNIEnv* env, const std::string& value); 268 269// Convert C++ string to Java String. 270std::string ToCppString(JNIEnv* env, jstring value); 271 272// Convert Java object to a (C) Value object. 273Value ToCValue(JNIEnv* env, jobject object); 274 275// Convert a (C) Value object to a Java object. 276jobject ToJObject(JNIEnv* env, const Value& value); 277 278// Returns true, iff the passed object is an instance of the class specified 279// by its fully qualified class name. 280bool IsJavaInstanceOf(JNIEnv* env, jobject object, 281 const std::string& class_name); 282 283#endif // ANDROID_FILTERFW_JNI_JNI_UTIL_H 284