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