• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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().
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.
Setup(const std::string & jclass_name,const std::string & id_fld_name)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.
Instance()103     static ObjectPool* Instance() {
104       return instance_;
105     }
106 
107     // Delete this type's pool.
TearDown()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.
RegisterObject(T * object,bool owns)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.
ObjectWithID(int obj_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.
GetObjectID(JNIEnv * env,jobject j_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.
WrapObject(T * c_object,JNIEnv * env,jobject j_object,bool owns)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.
DeleteObjectWithID(int obj_id)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.
CreateJavaObject(JNIEnv * env)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 
GetObjectCount()177     int GetObjectCount() const {
178       return objects_.size();
179     }
180 
GetJavaClassName()181     const std::string& GetJavaClassName() const {
182       return jclass_name_;
183     }
184 
185   private:
ObjectPool(const std::string & jclass_name,const std::string & id_fld_name)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 
205 template<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.
213 template<typename T>
WrapObjectInJava(T * c_object,JNIEnv * env,jobject j_object,bool owns)214 bool 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.
220 template<typename T>
WrapOwnedObjectInJava(std::unique_ptr<T> c_object,JNIEnv * env,jobject j_object,bool owns)221 bool 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.
233 template<typename T>
WrapNewObjectInJava(T * c_object,JNIEnv * env,bool owns)234 jobject 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.
247 template<typename T>
ConvertFromJava(JNIEnv * env,jobject j_object)248 T* 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.
257 template<typename T>
DeleteNativeObject(JNIEnv * env,jobject j_object)258 bool 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
267 JavaVM* GetCurrentJavaVM();
268 
269 // Get the current JNI environment, or NULL if this is not a JNI thread
270 JNIEnv* GetCurrentJNIEnv();
271 #endif
272 
273 // Convert C++ boolean to Java boolean.
274 jboolean ToJBool(bool value);
275 
276 // Convert Java boolean to C++ boolean.
277 bool ToCppBool(jboolean value);
278 
279 // Convert Java String to C++ string.
280 jstring ToJString(JNIEnv* env, const std::string& value);
281 
282 // Convert C++ string to Java String.
283 std::string ToCppString(JNIEnv* env, jstring value);
284 
285 // Convert Java object to a (C) Value object.
286 Value ToCValue(JNIEnv* env, jobject object);
287 
288 // Convert a (C) Value object to a Java object.
289 jobject 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.
293 bool IsJavaInstanceOf(JNIEnv* env, jobject object,
294                       const std::string& class_name);
295 
296 #endif // ANDROID_FILTERFW_JNI_JNI_UTIL_H
297