1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/android/java/gin_java_bound_object.h" 6 7 #include "base/android/jni_android.h" 8 #include "base/android/jni_string.h" 9 #include "base/android/scoped_java_ref.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "content/browser/android/java/jni_helper.h" 12 13 using base::android::AttachCurrentThread; 14 using base::android::ScopedJavaLocalRef; 15 16 namespace content { 17 18 namespace { 19 20 const char kJavaLangClass[] = "java/lang/Class"; 21 const char kJavaLangObject[] = "java/lang/Object"; 22 const char kJavaLangReflectMethod[] = "java/lang/reflect/Method"; 23 const char kGetClass[] = "getClass"; 24 const char kGetMethods[] = "getMethods"; 25 const char kIsAnnotationPresent[] = "isAnnotationPresent"; 26 const char kReturningJavaLangClass[] = "()Ljava/lang/Class;"; 27 const char kReturningJavaLangReflectMethodArray[] = 28 "()[Ljava/lang/reflect/Method;"; 29 const char kTakesJavaLangClassReturningBoolean[] = "(Ljava/lang/Class;)Z"; 30 31 } // namespace 32 33 34 // static 35 GinJavaBoundObject* GinJavaBoundObject::CreateNamed( 36 const JavaObjectWeakGlobalRef& ref, 37 const base::android::JavaRef<jclass>& safe_annotation_clazz) { 38 return new GinJavaBoundObject(ref, safe_annotation_clazz); 39 } 40 41 // static 42 GinJavaBoundObject* GinJavaBoundObject::CreateTransient( 43 const JavaObjectWeakGlobalRef& ref, 44 const base::android::JavaRef<jclass>& safe_annotation_clazz, 45 int32 holder) { 46 std::set<int32> holders; 47 holders.insert(holder); 48 return new GinJavaBoundObject(ref, safe_annotation_clazz, holders); 49 } 50 51 GinJavaBoundObject::GinJavaBoundObject( 52 const JavaObjectWeakGlobalRef& ref, 53 const base::android::JavaRef<jclass>& safe_annotation_clazz) 54 : ref_(ref), 55 names_count_(1), 56 object_get_class_method_id_(NULL), 57 are_methods_set_up_(false), 58 safe_annotation_clazz_(safe_annotation_clazz) { 59 } 60 61 GinJavaBoundObject::GinJavaBoundObject( 62 const JavaObjectWeakGlobalRef& ref, 63 const base::android::JavaRef<jclass>& safe_annotation_clazz, 64 const std::set<int32>& holders) 65 : ref_(ref), 66 names_count_(0), 67 holders_(holders), 68 object_get_class_method_id_(NULL), 69 are_methods_set_up_(false), 70 safe_annotation_clazz_(safe_annotation_clazz) { 71 } 72 73 GinJavaBoundObject::~GinJavaBoundObject() { 74 } 75 76 std::set<std::string> GinJavaBoundObject::GetMethodNames() { 77 EnsureMethodsAreSetUp(); 78 std::set<std::string> result; 79 for (JavaMethodMap::const_iterator it = methods_.begin(); 80 it != methods_.end(); 81 ++it) { 82 result.insert(it->first); 83 } 84 return result; 85 } 86 87 bool GinJavaBoundObject::HasMethod(const std::string& method_name) { 88 EnsureMethodsAreSetUp(); 89 return methods_.find(method_name) != methods_.end(); 90 } 91 92 const JavaMethod* GinJavaBoundObject::FindMethod( 93 const std::string& method_name, 94 size_t num_parameters) { 95 EnsureMethodsAreSetUp(); 96 97 // Get all methods with the correct name. 98 std::pair<JavaMethodMap::const_iterator, JavaMethodMap::const_iterator> 99 iters = methods_.equal_range(method_name); 100 if (iters.first == iters.second) { 101 return NULL; 102 } 103 104 // LIVECONNECT_COMPLIANCE: We just take the first method with the correct 105 // number of arguments, while the spec proposes using cost-based algorithm: 106 // https://jdk6.java.net/plugin2/liveconnect/#OVERLOADED_METHODS 107 for (JavaMethodMap::const_iterator iter = iters.first; iter != iters.second; 108 ++iter) { 109 if (iter->second->num_parameters() == num_parameters) { 110 return iter->second.get(); 111 } 112 } 113 114 return NULL; 115 } 116 117 bool GinJavaBoundObject::IsObjectGetClassMethod(const JavaMethod* method) { 118 EnsureMethodsAreSetUp(); 119 // As java.lang.Object.getClass is declared to be final, it is sufficient to 120 // compare methodIDs. 121 return method->id() == object_get_class_method_id_; 122 } 123 124 const base::android::JavaRef<jclass>& 125 GinJavaBoundObject::GetSafeAnnotationClass() { 126 return safe_annotation_clazz_; 127 } 128 129 base::android::ScopedJavaLocalRef<jclass> GinJavaBoundObject::GetLocalClassRef( 130 JNIEnv* env) { 131 if (!object_get_class_method_id_) { 132 object_get_class_method_id_ = GetMethodIDFromClassName( 133 env, kJavaLangObject, kGetClass, kReturningJavaLangClass); 134 } 135 ScopedJavaLocalRef<jobject> obj = GetLocalRef(env); 136 if (obj.obj()) { 137 return base::android::ScopedJavaLocalRef<jclass>( 138 env, 139 static_cast<jclass>( 140 env->CallObjectMethod(obj.obj(), object_get_class_method_id_))); 141 } else { 142 return base::android::ScopedJavaLocalRef<jclass>(); 143 } 144 } 145 146 void GinJavaBoundObject::EnsureMethodsAreSetUp() { 147 if (are_methods_set_up_) 148 return; 149 are_methods_set_up_ = true; 150 151 JNIEnv* env = AttachCurrentThread(); 152 153 ScopedJavaLocalRef<jclass> clazz = GetLocalClassRef(env); 154 if (clazz.is_null()) { 155 return; 156 } 157 158 ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>( 159 env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName( 160 env, 161 kJavaLangClass, 162 kGetMethods, 163 kReturningJavaLangReflectMethodArray)))); 164 165 size_t num_methods = env->GetArrayLength(methods.obj()); 166 // Java objects always have public methods. 167 DCHECK(num_methods); 168 169 for (size_t i = 0; i < num_methods; ++i) { 170 ScopedJavaLocalRef<jobject> java_method( 171 env, 172 env->GetObjectArrayElement(methods.obj(), i)); 173 174 if (!safe_annotation_clazz_.is_null()) { 175 jboolean safe = env->CallBooleanMethod(java_method.obj(), 176 GetMethodIDFromClassName( 177 env, 178 kJavaLangReflectMethod, 179 kIsAnnotationPresent, 180 kTakesJavaLangClassReturningBoolean), 181 safe_annotation_clazz_.obj()); 182 183 if (!safe) 184 continue; 185 } 186 187 JavaMethod* method = new JavaMethod(java_method); 188 methods_.insert(std::make_pair(method->name(), method)); 189 } 190 } 191 192 } // namespace content 193