Marius Renn | 65953da | 2012-03-27 10:44:45 -0700 | [diff] [blame] | 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 | |
George Burgess IV | 315c936 | 2016-11-01 16:06:39 -0700 | [diff] [blame] | 19 | #include <memory> |
Dan Albert | ff1d8a6 | 2014-11-11 19:31:26 -0800 | [diff] [blame] | 20 | #include <unordered_map> |
Marius Renn | 65953da | 2012-03-27 10:44:45 -0700 | [diff] [blame] | 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(). |
| 36 | extern 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! |
| 46 | class 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. |
| 91 | template<class T> |
| 92 | class 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 | |
Dan Albert | ff1d8a6 | 2014-11-11 19:31:26 -0800 | [diff] [blame] | 192 | typedef std::unordered_map<int, T*> CObjMap; |
| 193 | typedef std::unordered_map<int, bool> FlagMap; |
Marius Renn | 65953da | 2012-03-27 10:44:45 -0700 | [diff] [blame] | 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 | DISALLOW_COPY_AND_ASSIGN(ObjectPool); |
| 202 | }; |
| 203 | |
| 204 | template<typename T> ObjectPool<T>* ObjectPool<T>::instance_ = NULL; |
| 205 | |
| 206 | // Convenience Functions /////////////////////////////////////////////////////// |
| 207 | |
| 208 | // This function "links" the C++ instance and the Java instance, so that they |
| 209 | // can be mapped to one another. This must be called for every C++ instance |
| 210 | // which is wrapped by a Java front-end interface. Pass true to owns, if the |
| 211 | // Java layer should own the object. |
| 212 | template<typename T> |
| 213 | bool WrapObjectInJava(T* c_object, JNIEnv* env, jobject j_object, bool owns) { |
| 214 | ObjectPool<T>* pool = ObjectPool<T>::Instance(); |
| 215 | return pool ? pool->WrapObject(c_object, env, j_object, owns) : false; |
| 216 | } |
| 217 | |
George Burgess IV | 315c936 | 2016-11-01 16:06:39 -0700 | [diff] [blame] | 218 | // Calls WrapObjectInJava, safely freeing c_object if object creation fails. |
| 219 | template<typename T> |
| 220 | bool WrapOwnedObjectInJava(std::unique_ptr<T> c_object, JNIEnv* env, |
| 221 | jobject j_object, bool owns) { |
| 222 | if (!WrapObjectInJava<T>(c_object.get(), env, j_object, owns)) |
| 223 | return false; |
| 224 | // If we succeeded, a Java object now owns our c object; don't free it. |
| 225 | c_object.release(); |
| 226 | return true; |
| 227 | } |
| 228 | |
Marius Renn | 65953da | 2012-03-27 10:44:45 -0700 | [diff] [blame] | 229 | // Creates a new Java instance, which wraps the passed C++ instance. Returns |
| 230 | // the wrapped object or JNI_NULL if there was an error. Pass true to owns, if |
| 231 | // the Java layer should own the object. |
| 232 | template<typename T> |
| 233 | jobject WrapNewObjectInJava(T* c_object, JNIEnv* env, bool owns) { |
| 234 | ObjectPool<T>* pool = ObjectPool<T>::Instance(); |
| 235 | if (pool) { |
| 236 | jobject result = pool->CreateJavaObject(env); |
| 237 | if (WrapObjectInJava(c_object, env, result, owns)) |
| 238 | return result; |
| 239 | } |
| 240 | return JNI_NULL; |
| 241 | } |
| 242 | |
| 243 | // Use ConvertFromJava to obtain a C++ instance given a Java object. This |
| 244 | // instance must have been wrapped in Java using the WrapObjectInJava() |
| 245 | // function. |
| 246 | template<typename T> |
| 247 | T* ConvertFromJava(JNIEnv* env, jobject j_object) { |
| 248 | ObjectPool<T>* pool = ObjectPool<T>::Instance(); |
| 249 | return pool && j_object |
| 250 | ? pool->ObjectWithID(pool->GetObjectID(env, j_object)) |
| 251 | : NULL; |
| 252 | } |
| 253 | |
| 254 | // Delete the native object given a Java instance. This should be called from |
| 255 | // the Java object's finalizer. |
| 256 | template<typename T> |
| 257 | bool DeleteNativeObject(JNIEnv* env, jobject j_object) { |
| 258 | ObjectPool<T>* pool = ObjectPool<T>::Instance(); |
| 259 | return pool && j_object |
| 260 | ? pool->DeleteObjectWithID(pool->GetObjectID(env, j_object)) |
| 261 | : false; |
| 262 | } |
| 263 | |
| 264 | #if 0 |
| 265 | // Get the current JNI VM, or NULL if there is no current VM |
| 266 | JavaVM* GetCurrentJavaVM(); |
| 267 | |
| 268 | // Get the current JNI environment, or NULL if this is not a JNI thread |
| 269 | JNIEnv* GetCurrentJNIEnv(); |
| 270 | #endif |
| 271 | |
| 272 | // Convert C++ boolean to Java boolean. |
| 273 | jboolean ToJBool(bool value); |
| 274 | |
| 275 | // Convert Java boolean to C++ boolean. |
| 276 | bool ToCppBool(jboolean value); |
| 277 | |
| 278 | // Convert Java String to C++ string. |
| 279 | jstring ToJString(JNIEnv* env, const std::string& value); |
| 280 | |
| 281 | // Convert C++ string to Java String. |
| 282 | std::string ToCppString(JNIEnv* env, jstring value); |
| 283 | |
| 284 | // Convert Java object to a (C) Value object. |
| 285 | Value ToCValue(JNIEnv* env, jobject object); |
| 286 | |
| 287 | // Convert a (C) Value object to a Java object. |
| 288 | jobject ToJObject(JNIEnv* env, const Value& value); |
| 289 | |
| 290 | // Returns true, iff the passed object is an instance of the class specified |
| 291 | // by its fully qualified class name. |
| 292 | bool IsJavaInstanceOf(JNIEnv* env, jobject object, |
| 293 | const std::string& class_name); |
| 294 | |
| 295 | #endif // ANDROID_FILTERFW_JNI_JNI_UTIL_H |