Home | History | Annotate | Download | only in jni
      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().
     35 extern 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!
     45 class 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.
     90 template<class T>
     91 class 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 
    203 template<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.
    211 template<typename T>
    212 bool 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.
    220 template<typename T>
    221 jobject 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.
    234 template<typename T>
    235 T* 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.
    244 template<typename T>
    245 bool 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
    254 JavaVM* GetCurrentJavaVM();
    255 
    256 // Get the current JNI environment, or NULL if this is not a JNI thread
    257 JNIEnv* GetCurrentJNIEnv();
    258 #endif
    259 
    260 // Convert C++ boolean to Java boolean.
    261 jboolean ToJBool(bool value);
    262 
    263 // Convert Java boolean to C++ boolean.
    264 bool ToCppBool(jboolean value);
    265 
    266 // Convert Java String to C++ string.
    267 jstring ToJString(JNIEnv* env, const std::string& value);
    268 
    269 // Convert C++ string to Java String.
    270 std::string ToCppString(JNIEnv* env, jstring value);
    271 
    272 // Convert Java object to a (C) Value object.
    273 Value ToCValue(JNIEnv* env, jobject object);
    274 
    275 // Convert a (C) Value object to a Java object.
    276 jobject 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.
    280 bool IsJavaInstanceOf(JNIEnv* env, jobject object,
    281                       const std::string& class_name);
    282 
    283 #endif // ANDROID_FILTERFW_JNI_JNI_UTIL_H
    284