xref: /aosp_15_r20/external/cronet/base/android/jni_android.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker // Copyright 2012 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker 
5*6777b538SAndroid Build Coastguard Worker #include "base/android/jni_android.h"
6*6777b538SAndroid Build Coastguard Worker 
7*6777b538SAndroid Build Coastguard Worker #include <stddef.h>
8*6777b538SAndroid Build Coastguard Worker #include <sys/prctl.h>
9*6777b538SAndroid Build Coastguard Worker 
10*6777b538SAndroid Build Coastguard Worker #include "base/android/java_exception_reporter.h"
11*6777b538SAndroid Build Coastguard Worker #include "base/android/jni_string.h"
12*6777b538SAndroid Build Coastguard Worker #include "base/android/jni_utils.h"
13*6777b538SAndroid Build Coastguard Worker #include "base/android_runtime_jni_headers/Throwable_jni.h"
14*6777b538SAndroid Build Coastguard Worker #include "base/debug/debugging_buildflags.h"
15*6777b538SAndroid Build Coastguard Worker #include "base/feature_list.h"
16*6777b538SAndroid Build Coastguard Worker #include "base/logging.h"
17*6777b538SAndroid Build Coastguard Worker #include "build/build_config.h"
18*6777b538SAndroid Build Coastguard Worker #include "build/robolectric_buildflags.h"
19*6777b538SAndroid Build Coastguard Worker #include "third_party/abseil-cpp/absl/base/attributes.h"
20*6777b538SAndroid Build Coastguard Worker #include "third_party/jni_zero/jni_zero.h"
21*6777b538SAndroid Build Coastguard Worker 
22*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_ROBOLECTRIC)
23*6777b538SAndroid Build Coastguard Worker #include "base/base_robolectric_jni/JniAndroid_jni.h"  // nogncheck
24*6777b538SAndroid Build Coastguard Worker #else
25*6777b538SAndroid Build Coastguard Worker #include "base/base_jni/JniAndroid_jni.h"
26*6777b538SAndroid Build Coastguard Worker #endif
27*6777b538SAndroid Build Coastguard Worker 
28*6777b538SAndroid Build Coastguard Worker namespace base {
29*6777b538SAndroid Build Coastguard Worker namespace android {
30*6777b538SAndroid Build Coastguard Worker namespace {
31*6777b538SAndroid Build Coastguard Worker 
32*6777b538SAndroid Build Coastguard Worker // If disabled, we LOG(FATAL) immediately in native code when faced with an
33*6777b538SAndroid Build Coastguard Worker // uncaught Java exception (historical behavior). If enabled, we give the Java
34*6777b538SAndroid Build Coastguard Worker // uncaught exception handler a chance to handle the exception first, so that
35*6777b538SAndroid Build Coastguard Worker // the crash is (hopefully) seen as a Java crash, not a native crash.
36*6777b538SAndroid Build Coastguard Worker // TODO(https://crbug.com/1426888): remove this switch once we are confident the
37*6777b538SAndroid Build Coastguard Worker // new behavior is fine.
38*6777b538SAndroid Build Coastguard Worker BASE_FEATURE(kHandleExceptionsInJava,
39*6777b538SAndroid Build Coastguard Worker              "HandleJniExceptionsInJava",
40*6777b538SAndroid Build Coastguard Worker              base::FEATURE_ENABLED_BY_DEFAULT);
41*6777b538SAndroid Build Coastguard Worker 
42*6777b538SAndroid Build Coastguard Worker jclass g_out_of_memory_error_class = nullptr;
43*6777b538SAndroid Build Coastguard Worker 
44*6777b538SAndroid Build Coastguard Worker #if !BUILDFLAG(IS_ROBOLECTRIC)
45*6777b538SAndroid Build Coastguard Worker jmethodID g_class_loader_load_class_method_id = nullptr;
46*6777b538SAndroid Build Coastguard Worker // ClassLoader.loadClass() accepts either slashes or dots on Android, but JVM
47*6777b538SAndroid Build Coastguard Worker // requires dots. We could translate, but there is no need to go through
48*6777b538SAndroid Build Coastguard Worker // ClassLoaders in Robolectric anyways.
49*6777b538SAndroid Build Coastguard Worker // https://cs.android.com/search?q=symbol:DexFile_defineClassNative
GetClassFromSplit(JNIEnv * env,const char * class_name,const char * split_name)50*6777b538SAndroid Build Coastguard Worker jclass GetClassFromSplit(JNIEnv* env,
51*6777b538SAndroid Build Coastguard Worker                          const char* class_name,
52*6777b538SAndroid Build Coastguard Worker                          const char* split_name) {
53*6777b538SAndroid Build Coastguard Worker   return static_cast<jclass>(env->CallObjectMethod(
54*6777b538SAndroid Build Coastguard Worker       GetSplitClassLoader(env, split_name), g_class_loader_load_class_method_id,
55*6777b538SAndroid Build Coastguard Worker       ConvertUTF8ToJavaString(env, class_name).obj()));
56*6777b538SAndroid Build Coastguard Worker }
57*6777b538SAndroid Build Coastguard Worker 
58*6777b538SAndroid Build Coastguard Worker // Must be called before using GetClassFromSplit - we need to set the global,
59*6777b538SAndroid Build Coastguard Worker // and we need to call GetClassLoader at least once to allow the default
60*6777b538SAndroid Build Coastguard Worker // resolver (env->FindClass()) to get our main ClassLoader class instance, which
61*6777b538SAndroid Build Coastguard Worker // we then cache use for all future calls to GetSplitClassLoader.
PrepareClassLoaders(JNIEnv * env)62*6777b538SAndroid Build Coastguard Worker void PrepareClassLoaders(JNIEnv* env) {
63*6777b538SAndroid Build Coastguard Worker   if (g_class_loader_load_class_method_id == nullptr) {
64*6777b538SAndroid Build Coastguard Worker     GetClassLoader(env);
65*6777b538SAndroid Build Coastguard Worker     ScopedJavaLocalRef<jclass> class_loader_clazz = ScopedJavaLocalRef<jclass>(
66*6777b538SAndroid Build Coastguard Worker         env, env->FindClass("java/lang/ClassLoader"));
67*6777b538SAndroid Build Coastguard Worker     CHECK(!ClearException(env));
68*6777b538SAndroid Build Coastguard Worker     g_class_loader_load_class_method_id =
69*6777b538SAndroid Build Coastguard Worker         env->GetMethodID(class_loader_clazz.obj(), "loadClass",
70*6777b538SAndroid Build Coastguard Worker                          "(Ljava/lang/String;)Ljava/lang/Class;");
71*6777b538SAndroid Build Coastguard Worker     CHECK(!ClearException(env));
72*6777b538SAndroid Build Coastguard Worker   }
73*6777b538SAndroid Build Coastguard Worker }
74*6777b538SAndroid Build Coastguard Worker #endif  // !BUILDFLAG(IS_ROBOLECTRIC)
75*6777b538SAndroid Build Coastguard Worker }  // namespace
76*6777b538SAndroid Build Coastguard Worker 
77*6777b538SAndroid Build Coastguard Worker LogFatalCallback g_log_fatal_callback_for_testing = nullptr;
78*6777b538SAndroid Build Coastguard Worker const char kUnableToGetStackTraceMessage[] =
79*6777b538SAndroid Build Coastguard Worker     "Unable to retrieve Java caller stack trace as the exception handler is "
80*6777b538SAndroid Build Coastguard Worker     "being re-entered";
81*6777b538SAndroid Build Coastguard Worker const char kReetrantOutOfMemoryMessage[] =
82*6777b538SAndroid Build Coastguard Worker     "While handling an uncaught Java exception, an OutOfMemoryError "
83*6777b538SAndroid Build Coastguard Worker     "occurred.";
84*6777b538SAndroid Build Coastguard Worker const char kReetrantExceptionMessage[] =
85*6777b538SAndroid Build Coastguard Worker     "While handling an uncaught Java exception, another exception "
86*6777b538SAndroid Build Coastguard Worker     "occurred.";
87*6777b538SAndroid Build Coastguard Worker const char kUncaughtExceptionMessage[] =
88*6777b538SAndroid Build Coastguard Worker     "Uncaught Java exception in native code. Please include the Java exception "
89*6777b538SAndroid Build Coastguard Worker     "stack from the Android log in your crash report.";
90*6777b538SAndroid Build Coastguard Worker const char kUncaughtExceptionHandlerFailedMessage[] =
91*6777b538SAndroid Build Coastguard Worker     "Uncaught Java exception in native code and the Java uncaught exception "
92*6777b538SAndroid Build Coastguard Worker     "handler did not terminate the process. Please include the Java exception "
93*6777b538SAndroid Build Coastguard Worker     "stack from the Android log in your crash report.";
94*6777b538SAndroid Build Coastguard Worker const char kOomInGetJavaExceptionInfoMessage[] =
95*6777b538SAndroid Build Coastguard Worker     "Unable to obtain Java stack trace due to OutOfMemoryError";
96*6777b538SAndroid Build Coastguard Worker 
InitVM(JavaVM * vm)97*6777b538SAndroid Build Coastguard Worker void InitVM(JavaVM* vm) {
98*6777b538SAndroid Build Coastguard Worker   jni_zero::InitVM(vm);
99*6777b538SAndroid Build Coastguard Worker   jni_zero::SetExceptionHandler(CheckException);
100*6777b538SAndroid Build Coastguard Worker   JNIEnv* env = jni_zero::AttachCurrentThread();
101*6777b538SAndroid Build Coastguard Worker #if !BUILDFLAG(IS_ROBOLECTRIC)
102*6777b538SAndroid Build Coastguard Worker   // Warm-up needed for GetClassFromSplit, must be called before we set the
103*6777b538SAndroid Build Coastguard Worker   // resolver, since GetClassFromSplit won't work until after
104*6777b538SAndroid Build Coastguard Worker   // PrepareClassLoaders has happened.
105*6777b538SAndroid Build Coastguard Worker   PrepareClassLoaders(env);
106*6777b538SAndroid Build Coastguard Worker   jni_zero::SetClassResolver(GetClassFromSplit);
107*6777b538SAndroid Build Coastguard Worker #endif
108*6777b538SAndroid Build Coastguard Worker   g_out_of_memory_error_class = static_cast<jclass>(
109*6777b538SAndroid Build Coastguard Worker       env->NewGlobalRef(env->FindClass("java/lang/OutOfMemoryError")));
110*6777b538SAndroid Build Coastguard Worker   DCHECK(g_out_of_memory_error_class);
111*6777b538SAndroid Build Coastguard Worker }
112*6777b538SAndroid Build Coastguard Worker 
113*6777b538SAndroid Build Coastguard Worker 
CheckException(JNIEnv * env)114*6777b538SAndroid Build Coastguard Worker void CheckException(JNIEnv* env) {
115*6777b538SAndroid Build Coastguard Worker   if (!jni_zero::HasException(env)) {
116*6777b538SAndroid Build Coastguard Worker     return;
117*6777b538SAndroid Build Coastguard Worker   }
118*6777b538SAndroid Build Coastguard Worker 
119*6777b538SAndroid Build Coastguard Worker   static thread_local bool g_reentering = false;
120*6777b538SAndroid Build Coastguard Worker   if (g_reentering) {
121*6777b538SAndroid Build Coastguard Worker     // We were handling an uncaught Java exception already, but one of the Java
122*6777b538SAndroid Build Coastguard Worker     // methods we called below threw another exception. (This is unlikely to
123*6777b538SAndroid Build Coastguard Worker     // happen, as we are careful to never throw from these methods, but we
124*6777b538SAndroid Build Coastguard Worker     // can't rule it out entirely. E.g. an OutOfMemoryError when constructing
125*6777b538SAndroid Build Coastguard Worker     // the jstring for the return value of
126*6777b538SAndroid Build Coastguard Worker     // sanitizedStacktraceForUnhandledException().
127*6777b538SAndroid Build Coastguard Worker     env->ExceptionDescribe();
128*6777b538SAndroid Build Coastguard Worker     jthrowable raw_throwable = env->ExceptionOccurred();
129*6777b538SAndroid Build Coastguard Worker     env->ExceptionClear();
130*6777b538SAndroid Build Coastguard Worker     jclass clazz = env->GetObjectClass(raw_throwable);
131*6777b538SAndroid Build Coastguard Worker     bool is_oom_error = env->IsSameObject(clazz, g_out_of_memory_error_class);
132*6777b538SAndroid Build Coastguard Worker     env->Throw(raw_throwable);  // Ensure we don't re-enter Java.
133*6777b538SAndroid Build Coastguard Worker 
134*6777b538SAndroid Build Coastguard Worker     if (is_oom_error) {
135*6777b538SAndroid Build Coastguard Worker       base::android::SetJavaException(kReetrantOutOfMemoryMessage);
136*6777b538SAndroid Build Coastguard Worker       // Use different LOG(FATAL) statements to ensure unique stack traces.
137*6777b538SAndroid Build Coastguard Worker       if (g_log_fatal_callback_for_testing) {
138*6777b538SAndroid Build Coastguard Worker         g_log_fatal_callback_for_testing(kReetrantOutOfMemoryMessage);
139*6777b538SAndroid Build Coastguard Worker       } else {
140*6777b538SAndroid Build Coastguard Worker         LOG(FATAL) << kReetrantOutOfMemoryMessage;
141*6777b538SAndroid Build Coastguard Worker       }
142*6777b538SAndroid Build Coastguard Worker     } else {
143*6777b538SAndroid Build Coastguard Worker       base::android::SetJavaException(kReetrantExceptionMessage);
144*6777b538SAndroid Build Coastguard Worker       if (g_log_fatal_callback_for_testing) {
145*6777b538SAndroid Build Coastguard Worker         g_log_fatal_callback_for_testing(kReetrantExceptionMessage);
146*6777b538SAndroid Build Coastguard Worker       } else {
147*6777b538SAndroid Build Coastguard Worker         LOG(FATAL) << kReetrantExceptionMessage;
148*6777b538SAndroid Build Coastguard Worker       }
149*6777b538SAndroid Build Coastguard Worker     }
150*6777b538SAndroid Build Coastguard Worker     // Needed for tests, which do not terminate from LOG(FATAL).
151*6777b538SAndroid Build Coastguard Worker     return;
152*6777b538SAndroid Build Coastguard Worker   }
153*6777b538SAndroid Build Coastguard Worker   g_reentering = true;
154*6777b538SAndroid Build Coastguard Worker 
155*6777b538SAndroid Build Coastguard Worker   // Log a message to ensure there is something in the log even if the rest of
156*6777b538SAndroid Build Coastguard Worker   // this function goes horribly wrong, and also to provide a convenient marker
157*6777b538SAndroid Build Coastguard Worker   // in the log for where Java exception crash information starts.
158*6777b538SAndroid Build Coastguard Worker   LOG(ERROR) << "Crashing due to uncaught Java exception";
159*6777b538SAndroid Build Coastguard Worker 
160*6777b538SAndroid Build Coastguard Worker   const bool handle_exception_in_java =
161*6777b538SAndroid Build Coastguard Worker       base::FeatureList::IsEnabled(kHandleExceptionsInJava);
162*6777b538SAndroid Build Coastguard Worker 
163*6777b538SAndroid Build Coastguard Worker   if (!handle_exception_in_java) {
164*6777b538SAndroid Build Coastguard Worker     env->ExceptionDescribe();
165*6777b538SAndroid Build Coastguard Worker   }
166*6777b538SAndroid Build Coastguard Worker 
167*6777b538SAndroid Build Coastguard Worker   // We cannot use `ScopedJavaLocalRef` directly because that ends up calling
168*6777b538SAndroid Build Coastguard Worker   // env->GetObjectRefType() when DCHECK is on, and that call is not allowed
169*6777b538SAndroid Build Coastguard Worker   // with a pending exception according to the JNI spec.
170*6777b538SAndroid Build Coastguard Worker   jthrowable raw_throwable = env->ExceptionOccurred();
171*6777b538SAndroid Build Coastguard Worker   // Now that we saved the reference to the throwable, clear the exception.
172*6777b538SAndroid Build Coastguard Worker   //
173*6777b538SAndroid Build Coastguard Worker   // We need to do this as early as possible to remove the risk that code below
174*6777b538SAndroid Build Coastguard Worker   // might accidentally call back into Java, which is not allowed when `env`
175*6777b538SAndroid Build Coastguard Worker   // has an exception set, per the JNI spec. (For example, LOG(FATAL) doesn't
176*6777b538SAndroid Build Coastguard Worker   // work with a JNI exception set, because it calls
177*6777b538SAndroid Build Coastguard Worker   // GetJavaStackTraceIfPresent()).
178*6777b538SAndroid Build Coastguard Worker   env->ExceptionClear();
179*6777b538SAndroid Build Coastguard Worker   // The reference returned by `ExceptionOccurred()` is a local reference.
180*6777b538SAndroid Build Coastguard Worker   // `ExceptionClear()` merely removes the exception information from `env`;
181*6777b538SAndroid Build Coastguard Worker   // it doesn't delete the reference, which is why this call is valid.
182*6777b538SAndroid Build Coastguard Worker   auto throwable = ScopedJavaLocalRef<jthrowable>::Adopt(env, raw_throwable);
183*6777b538SAndroid Build Coastguard Worker 
184*6777b538SAndroid Build Coastguard Worker   if (!handle_exception_in_java) {
185*6777b538SAndroid Build Coastguard Worker     base::android::SetJavaException(
186*6777b538SAndroid Build Coastguard Worker         GetJavaExceptionInfo(env, throwable).c_str());
187*6777b538SAndroid Build Coastguard Worker     if (g_log_fatal_callback_for_testing) {
188*6777b538SAndroid Build Coastguard Worker       g_log_fatal_callback_for_testing(kUncaughtExceptionMessage);
189*6777b538SAndroid Build Coastguard Worker     } else {
190*6777b538SAndroid Build Coastguard Worker       LOG(FATAL) << kUncaughtExceptionMessage;
191*6777b538SAndroid Build Coastguard Worker     }
192*6777b538SAndroid Build Coastguard Worker     // Needed for tests, which do not terminate from LOG(FATAL).
193*6777b538SAndroid Build Coastguard Worker     g_reentering = false;
194*6777b538SAndroid Build Coastguard Worker     return;
195*6777b538SAndroid Build Coastguard Worker   }
196*6777b538SAndroid Build Coastguard Worker 
197*6777b538SAndroid Build Coastguard Worker   // We don't need to call SetJavaException() in this branch because we
198*6777b538SAndroid Build Coastguard Worker   // expect handleException() to eventually call JavaExceptionReporter through
199*6777b538SAndroid Build Coastguard Worker   // the global uncaught exception handler.
200*6777b538SAndroid Build Coastguard Worker 
201*6777b538SAndroid Build Coastguard Worker   const std::string native_stack_trace = base::debug::StackTrace().ToString();
202*6777b538SAndroid Build Coastguard Worker   LOG(ERROR) << "Native stack trace:" << std::endl << native_stack_trace;
203*6777b538SAndroid Build Coastguard Worker 
204*6777b538SAndroid Build Coastguard Worker   ScopedJavaLocalRef<jthrowable> secondary_exception =
205*6777b538SAndroid Build Coastguard Worker       Java_JniAndroid_handleException(
206*6777b538SAndroid Build Coastguard Worker           env, throwable, ConvertUTF8ToJavaString(env, native_stack_trace));
207*6777b538SAndroid Build Coastguard Worker 
208*6777b538SAndroid Build Coastguard Worker   // Ideally handleException() should have terminated the process and we should
209*6777b538SAndroid Build Coastguard Worker   // not get here. This can happen in the case of OutOfMemoryError or if the
210*6777b538SAndroid Build Coastguard Worker   // app that embedded WebView installed an exception handler that does not
211*6777b538SAndroid Build Coastguard Worker   // terminate, or itself threw an exception. We cannot be confident that
212*6777b538SAndroid Build Coastguard Worker   // JavaExceptionReporter ran, so set the java exception explicitly.
213*6777b538SAndroid Build Coastguard Worker   base::android::SetJavaException(
214*6777b538SAndroid Build Coastguard Worker       GetJavaExceptionInfo(
215*6777b538SAndroid Build Coastguard Worker           env, secondary_exception ? secondary_exception : throwable)
216*6777b538SAndroid Build Coastguard Worker           .c_str());
217*6777b538SAndroid Build Coastguard Worker   if (g_log_fatal_callback_for_testing) {
218*6777b538SAndroid Build Coastguard Worker     g_log_fatal_callback_for_testing(kUncaughtExceptionHandlerFailedMessage);
219*6777b538SAndroid Build Coastguard Worker   } else {
220*6777b538SAndroid Build Coastguard Worker     LOG(FATAL) << kUncaughtExceptionHandlerFailedMessage;
221*6777b538SAndroid Build Coastguard Worker   }
222*6777b538SAndroid Build Coastguard Worker   // Needed for tests, which do not terminate from LOG(FATAL).
223*6777b538SAndroid Build Coastguard Worker   g_reentering = false;
224*6777b538SAndroid Build Coastguard Worker }
225*6777b538SAndroid Build Coastguard Worker 
GetJavaExceptionInfo(JNIEnv * env,const JavaRef<jthrowable> & throwable)226*6777b538SAndroid Build Coastguard Worker std::string GetJavaExceptionInfo(JNIEnv* env,
227*6777b538SAndroid Build Coastguard Worker                                  const JavaRef<jthrowable>& throwable) {
228*6777b538SAndroid Build Coastguard Worker   ScopedJavaLocalRef<jstring> sanitized_exception_string =
229*6777b538SAndroid Build Coastguard Worker       Java_JniAndroid_sanitizedStacktraceForUnhandledException(env, throwable);
230*6777b538SAndroid Build Coastguard Worker   // Returns null when PiiElider results in an OutOfMemoryError.
231*6777b538SAndroid Build Coastguard Worker   return sanitized_exception_string
232*6777b538SAndroid Build Coastguard Worker              ? ConvertJavaStringToUTF8(sanitized_exception_string)
233*6777b538SAndroid Build Coastguard Worker              : kOomInGetJavaExceptionInfoMessage;
234*6777b538SAndroid Build Coastguard Worker }
235*6777b538SAndroid Build Coastguard Worker 
GetJavaStackTraceIfPresent()236*6777b538SAndroid Build Coastguard Worker std::string GetJavaStackTraceIfPresent() {
237*6777b538SAndroid Build Coastguard Worker   JNIEnv* env = nullptr;
238*6777b538SAndroid Build Coastguard Worker   JavaVM* jvm = jni_zero::GetVM();
239*6777b538SAndroid Build Coastguard Worker   if (jvm) {
240*6777b538SAndroid Build Coastguard Worker     jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_2);
241*6777b538SAndroid Build Coastguard Worker   }
242*6777b538SAndroid Build Coastguard Worker   if (!env) {
243*6777b538SAndroid Build Coastguard Worker     // JNI has not been initialized on this thread.
244*6777b538SAndroid Build Coastguard Worker     return {};
245*6777b538SAndroid Build Coastguard Worker   }
246*6777b538SAndroid Build Coastguard Worker 
247*6777b538SAndroid Build Coastguard Worker   if (HasException(env)) {
248*6777b538SAndroid Build Coastguard Worker     // This can happen if CheckException() is being re-entered, decided to
249*6777b538SAndroid Build Coastguard Worker     // LOG(FATAL) immediately, and LOG(FATAL) itself is calling us. In that case
250*6777b538SAndroid Build Coastguard Worker     // it is imperative that we don't try to call Java again.
251*6777b538SAndroid Build Coastguard Worker     return kUnableToGetStackTraceMessage;
252*6777b538SAndroid Build Coastguard Worker   }
253*6777b538SAndroid Build Coastguard Worker 
254*6777b538SAndroid Build Coastguard Worker   ScopedJavaLocalRef<jthrowable> throwable =
255*6777b538SAndroid Build Coastguard Worker       JNI_Throwable::Java_Throwable_Constructor(env);
256*6777b538SAndroid Build Coastguard Worker   std::string ret = GetJavaExceptionInfo(env, throwable);
257*6777b538SAndroid Build Coastguard Worker   // Strip the exception message and leave only the "at" lines. Example:
258*6777b538SAndroid Build Coastguard Worker   // java.lang.Throwable:
259*6777b538SAndroid Build Coastguard Worker   // {tab}at Clazz.method(Clazz.java:111)
260*6777b538SAndroid Build Coastguard Worker   // {tab}at ...
261*6777b538SAndroid Build Coastguard Worker   size_t newline_idx = ret.find('\n');
262*6777b538SAndroid Build Coastguard Worker   if (newline_idx == std::string::npos) {
263*6777b538SAndroid Build Coastguard Worker     // There are no java frames.
264*6777b538SAndroid Build Coastguard Worker     return {};
265*6777b538SAndroid Build Coastguard Worker   }
266*6777b538SAndroid Build Coastguard Worker   return ret.substr(newline_idx + 1);
267*6777b538SAndroid Build Coastguard Worker }
268*6777b538SAndroid Build Coastguard Worker 
269*6777b538SAndroid Build Coastguard Worker }  // namespace android
270*6777b538SAndroid Build Coastguard Worker }  // namespace base
271