jni_android.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. // Copyright (c) 2012 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. #include "base/android/jni_android.h"
  5. #include <stddef.h>
  6. #include <sys/prctl.h>
  7. #include <map>
  8. #include "base/android/java_exception_reporter.h"
  9. #include "base/android/jni_string.h"
  10. #include "base/android/jni_utils.h"
  11. #include "base/containers/flat_map.h"
  12. #include "base/debug/debugging_buildflags.h"
  13. #include "base/lazy_instance.h"
  14. #include "base/logging.h"
  15. #include "base/no_destructor.h"
  16. #include "base/synchronization/lock.h"
  17. #include "base/threading/thread_local.h"
  18. namespace base {
  19. namespace android {
  20. namespace {
  21. JavaVM* g_jvm = NULL;
  22. base::LazyInstance<ScopedJavaGlobalRef<jobject>>::Leaky g_class_loader =
  23. LAZY_INSTANCE_INITIALIZER;
  24. jmethodID g_class_loader_load_class_method_id = 0;
  25. #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
  26. base::LazyInstance<base::ThreadLocalPointer<void>>::Leaky
  27. g_stack_frame_pointer = LAZY_INSTANCE_INITIALIZER;
  28. #endif
  29. bool g_fatal_exception_occurred = false;
  30. // Returns a ClassLoader instance which will be able to load classes from the
  31. // specified split.
  32. jobject GetCachedClassLoader(JNIEnv* env, const std::string& split_name) {
  33. DCHECK(!split_name.empty());
  34. static base::NoDestructor<base::Lock> lock;
  35. static base::NoDestructor<
  36. base::flat_map<std::string, ScopedJavaGlobalRef<jobject>>>
  37. split_class_loader_map;
  38. base::AutoLock guard(*lock);
  39. auto it = split_class_loader_map->find(split_name);
  40. if (it != split_class_loader_map->end()) {
  41. return it->second.obj();
  42. }
  43. ScopedJavaGlobalRef<jobject> class_loader(
  44. GetSplitClassLoader(env, split_name));
  45. jobject class_loader_obj = class_loader.obj();
  46. split_class_loader_map->insert({split_name, std::move(class_loader)});
  47. return class_loader_obj;
  48. }
  49. ScopedJavaLocalRef<jclass> GetClassInternal(JNIEnv* env,
  50. const char* class_name,
  51. jobject class_loader) {
  52. jclass clazz;
  53. if (class_loader != nullptr) {
  54. // ClassLoader.loadClass expects a classname with components separated by
  55. // dots instead of the slashes that JNIEnv::FindClass expects. The JNI
  56. // generator generates names with slashes, so we have to replace them here.
  57. // TODO(torne): move to an approach where we always use ClassLoader except
  58. // for the special case of base::android::GetClassLoader(), and change the
  59. // JNI generator to generate dot-separated names. http://crbug.com/461773
  60. size_t bufsize = strlen(class_name) + 1;
  61. char dotted_name[bufsize];
  62. memmove(dotted_name, class_name, bufsize);
  63. for (size_t i = 0; i < bufsize; ++i) {
  64. if (dotted_name[i] == '/') {
  65. dotted_name[i] = '.';
  66. }
  67. }
  68. clazz = static_cast<jclass>(
  69. env->CallObjectMethod(class_loader, g_class_loader_load_class_method_id,
  70. ConvertUTF8ToJavaString(env, dotted_name).obj()));
  71. } else {
  72. clazz = env->FindClass(class_name);
  73. }
  74. if (ClearException(env) || !clazz) {
  75. LOG(FATAL) << "Failed to find class " << class_name;
  76. }
  77. return ScopedJavaLocalRef<jclass>(env, clazz);
  78. }
  79. } // namespace
  80. JNIEnv* AttachCurrentThread() {
  81. DCHECK(g_jvm);
  82. JNIEnv* env = nullptr;
  83. jint ret = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_2);
  84. if (ret == JNI_EDETACHED || !env) {
  85. JavaVMAttachArgs args;
  86. args.version = JNI_VERSION_1_2;
  87. args.group = nullptr;
  88. // 16 is the maximum size for thread names on Android.
  89. char thread_name[16];
  90. int err = prctl(PR_GET_NAME, thread_name);
  91. if (err < 0) {
  92. DPLOG(ERROR) << "prctl(PR_GET_NAME)";
  93. args.name = nullptr;
  94. } else {
  95. args.name = thread_name;
  96. }
  97. ret = g_jvm->AttachCurrentThread(&env, &args);
  98. CHECK_EQ(JNI_OK, ret);
  99. }
  100. return env;
  101. }
  102. JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name) {
  103. DCHECK(g_jvm);
  104. JavaVMAttachArgs args;
  105. args.version = JNI_VERSION_1_2;
  106. args.name = thread_name.c_str();
  107. args.group = NULL;
  108. JNIEnv* env = NULL;
  109. jint ret = g_jvm->AttachCurrentThread(&env, &args);
  110. CHECK_EQ(JNI_OK, ret);
  111. return env;
  112. }
  113. void DetachFromVM() {
  114. // Ignore the return value, if the thread is not attached, DetachCurrentThread
  115. // will fail. But it is ok as the native thread may never be attached.
  116. if (g_jvm)
  117. g_jvm->DetachCurrentThread();
  118. }
  119. void InitVM(JavaVM* vm) {
  120. DCHECK(!g_jvm || g_jvm == vm);
  121. g_jvm = vm;
  122. }
  123. bool IsVMInitialized() {
  124. return g_jvm != NULL;
  125. }
  126. void InitReplacementClassLoader(JNIEnv* env,
  127. const JavaRef<jobject>& class_loader) {
  128. DCHECK(g_class_loader.Get().is_null());
  129. DCHECK(!class_loader.is_null());
  130. ScopedJavaLocalRef<jclass> class_loader_clazz =
  131. GetClass(env, "java/lang/ClassLoader");
  132. CHECK(!ClearException(env));
  133. g_class_loader_load_class_method_id =
  134. env->GetMethodID(class_loader_clazz.obj(),
  135. "loadClass",
  136. "(Ljava/lang/String;)Ljava/lang/Class;");
  137. CHECK(!ClearException(env));
  138. DCHECK(env->IsInstanceOf(class_loader.obj(), class_loader_clazz.obj()));
  139. g_class_loader.Get().Reset(class_loader);
  140. }
  141. ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env,
  142. const char* class_name,
  143. const std::string& split_name) {
  144. return GetClassInternal(env, class_name,
  145. GetCachedClassLoader(env, split_name));
  146. }
  147. ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
  148. return GetClassInternal(env, class_name, g_class_loader.Get().obj());
  149. }
  150. // This is duplicated with LazyGetClass below because these are performance
  151. // sensitive.
  152. jclass LazyGetClass(JNIEnv* env,
  153. const char* class_name,
  154. const std::string& split_name,
  155. std::atomic<jclass>* atomic_class_id) {
  156. const jclass value = atomic_class_id->load(std::memory_order_acquire);
  157. if (value)
  158. return value;
  159. ScopedJavaGlobalRef<jclass> clazz;
  160. clazz.Reset(GetClass(env, class_name, split_name));
  161. jclass cas_result = nullptr;
  162. if (atomic_class_id->compare_exchange_strong(cas_result, clazz.obj(),
  163. std::memory_order_acq_rel)) {
  164. // We intentionally leak the global ref since we now storing it as a raw
  165. // pointer in |atomic_class_id|.
  166. return clazz.Release();
  167. } else {
  168. return cas_result;
  169. }
  170. }
  171. // This is duplicated with LazyGetClass above because these are performance
  172. // sensitive.
  173. jclass LazyGetClass(JNIEnv* env,
  174. const char* class_name,
  175. std::atomic<jclass>* atomic_class_id) {
  176. const jclass value = atomic_class_id->load(std::memory_order_acquire);
  177. if (value)
  178. return value;
  179. ScopedJavaGlobalRef<jclass> clazz;
  180. clazz.Reset(GetClass(env, class_name));
  181. jclass cas_result = nullptr;
  182. if (atomic_class_id->compare_exchange_strong(cas_result, clazz.obj(),
  183. std::memory_order_acq_rel)) {
  184. // We intentionally leak the global ref since we now storing it as a raw
  185. // pointer in |atomic_class_id|.
  186. return clazz.Release();
  187. } else {
  188. return cas_result;
  189. }
  190. }
  191. template<MethodID::Type type>
  192. jmethodID MethodID::Get(JNIEnv* env,
  193. jclass clazz,
  194. const char* method_name,
  195. const char* jni_signature) {
  196. auto get_method_ptr = type == MethodID::TYPE_STATIC ?
  197. &JNIEnv::GetStaticMethodID :
  198. &JNIEnv::GetMethodID;
  199. jmethodID id = (env->*get_method_ptr)(clazz, method_name, jni_signature);
  200. if (base::android::ClearException(env) || !id) {
  201. LOG(FATAL) << "Failed to find " <<
  202. (type == TYPE_STATIC ? "static " : "") <<
  203. "method " << method_name << " " << jni_signature;
  204. }
  205. return id;
  206. }
  207. // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
  208. // into ::Get() above. If there's a race, it's ok since the values are the same
  209. // (and the duplicated effort will happen only once).
  210. template <MethodID::Type type>
  211. jmethodID MethodID::LazyGet(JNIEnv* env,
  212. jclass clazz,
  213. const char* method_name,
  214. const char* jni_signature,
  215. std::atomic<jmethodID>* atomic_method_id) {
  216. const jmethodID value = atomic_method_id->load(std::memory_order_acquire);
  217. if (value)
  218. return value;
  219. jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
  220. atomic_method_id->store(id, std::memory_order_release);
  221. return id;
  222. }
  223. // Various template instantiations.
  224. template jmethodID MethodID::Get<MethodID::TYPE_STATIC>(
  225. JNIEnv* env, jclass clazz, const char* method_name,
  226. const char* jni_signature);
  227. template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
  228. JNIEnv* env, jclass clazz, const char* method_name,
  229. const char* jni_signature);
  230. template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>(
  231. JNIEnv* env, jclass clazz, const char* method_name,
  232. const char* jni_signature, std::atomic<jmethodID>* atomic_method_id);
  233. template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
  234. JNIEnv* env, jclass clazz, const char* method_name,
  235. const char* jni_signature, std::atomic<jmethodID>* atomic_method_id);
  236. bool HasException(JNIEnv* env) {
  237. return env->ExceptionCheck() != JNI_FALSE;
  238. }
  239. bool ClearException(JNIEnv* env) {
  240. if (!HasException(env))
  241. return false;
  242. env->ExceptionDescribe();
  243. env->ExceptionClear();
  244. return true;
  245. }
  246. void CheckException(JNIEnv* env) {
  247. if (!HasException(env))
  248. return;
  249. jthrowable java_throwable = env->ExceptionOccurred();
  250. if (java_throwable) {
  251. // Clear the pending exception, since a local reference is now held.
  252. env->ExceptionDescribe();
  253. env->ExceptionClear();
  254. if (g_fatal_exception_occurred) {
  255. // Another exception (probably OOM) occurred during GetJavaExceptionInfo.
  256. base::android::SetJavaException(
  257. "Java OOM'ed in exception handling, check logcat");
  258. } else {
  259. g_fatal_exception_occurred = true;
  260. // RVO should avoid any extra copies of the exception string.
  261. base::android::SetJavaException(
  262. GetJavaExceptionInfo(env, java_throwable).c_str());
  263. }
  264. }
  265. // Now, feel good about it and die.
  266. LOG(FATAL) << "Please include Java exception stack in crash report";
  267. }
  268. std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
  269. ScopedJavaLocalRef<jclass> log_clazz = GetClass(env, "android/util/Log");
  270. jmethodID log_getstacktracestring = MethodID::Get<MethodID::TYPE_STATIC>(
  271. env, log_clazz.obj(), "getStackTraceString",
  272. "(Ljava/lang/Throwable;)Ljava/lang/String;");
  273. // Call Log.getStackTraceString()
  274. ScopedJavaLocalRef<jstring> exception_string(
  275. env, static_cast<jstring>(env->CallStaticObjectMethod(
  276. log_clazz.obj(), log_getstacktracestring, java_throwable)));
  277. CheckException(env);
  278. ScopedJavaLocalRef<jclass> piielider_clazz =
  279. GetClass(env, "org/chromium/base/PiiElider");
  280. jmethodID piielider_sanitize_stacktrace =
  281. MethodID::Get<MethodID::TYPE_STATIC>(
  282. env, piielider_clazz.obj(), "sanitizeStacktrace",
  283. "(Ljava/lang/String;)Ljava/lang/String;");
  284. ScopedJavaLocalRef<jstring> sanitized_exception_string(
  285. env, static_cast<jstring>(env->CallStaticObjectMethod(
  286. piielider_clazz.obj(), piielider_sanitize_stacktrace,
  287. exception_string.obj())));
  288. CheckException(env);
  289. return ConvertJavaStringToUTF8(sanitized_exception_string);
  290. }
  291. #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
  292. JNIStackFrameSaver::JNIStackFrameSaver(void* current_fp) {
  293. previous_fp_ = g_stack_frame_pointer.Pointer()->Get();
  294. g_stack_frame_pointer.Pointer()->Set(current_fp);
  295. }
  296. JNIStackFrameSaver::~JNIStackFrameSaver() {
  297. g_stack_frame_pointer.Pointer()->Set(previous_fp_);
  298. }
  299. void* JNIStackFrameSaver::SavedFrame() {
  300. return g_stack_frame_pointer.Pointer()->Get();
  301. }
  302. #endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
  303. } // namespace android
  304. } // namespace base