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