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 <unordered_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::unordered_map<int, T*>    CObjMap;
192    typedef std::unordered_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