1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/ANRReporter.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,576 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko; 1.10 + 1.11 +import java.io.BufferedOutputStream; 1.12 +import java.io.BufferedReader; 1.13 +import java.io.File; 1.14 +import java.io.FileInputStream; 1.15 +import java.io.FileOutputStream; 1.16 +import java.io.FileReader; 1.17 +import java.io.IOException; 1.18 +import java.io.InputStreamReader; 1.19 +import java.io.OutputStream; 1.20 +import java.io.Reader; 1.21 +import java.util.UUID; 1.22 +import java.util.regex.Pattern; 1.23 + 1.24 +import org.json.JSONObject; 1.25 +import org.mozilla.gecko.util.ThreadUtils; 1.26 + 1.27 +import android.content.BroadcastReceiver; 1.28 +import android.content.Context; 1.29 +import android.content.Intent; 1.30 +import android.content.IntentFilter; 1.31 +import android.os.Handler; 1.32 +import android.os.Looper; 1.33 +import android.util.Log; 1.34 + 1.35 +public final class ANRReporter extends BroadcastReceiver 1.36 +{ 1.37 + private static final boolean DEBUG = false; 1.38 + private static final String LOGTAG = "GeckoANRReporter"; 1.39 + 1.40 + private static final String ANR_ACTION = "android.intent.action.ANR"; 1.41 + // Number of lines to search traces.txt to decide whether it's a Gecko ANR 1.42 + private static final int LINES_TO_IDENTIFY_TRACES = 10; 1.43 + // ANRs may happen because of memory pressure, 1.44 + // so don't use up too much memory here 1.45 + // Size of buffer to hold one line of text 1.46 + private static final int TRACES_LINE_SIZE = 100; 1.47 + // Size of block to use when processing traces.txt 1.48 + private static final int TRACES_BLOCK_SIZE = 2000; 1.49 + private static final String TRACES_CHARSET = "utf-8"; 1.50 + private static final String PING_CHARSET = "utf-8"; 1.51 + 1.52 + private static final ANRReporter sInstance = new ANRReporter(); 1.53 + private static int sRegisteredCount; 1.54 + private Handler mHandler; 1.55 + private volatile boolean mPendingANR; 1.56 + 1.57 + private static native boolean requestNativeStack(boolean unwind); 1.58 + private static native String getNativeStack(); 1.59 + private static native void releaseNativeStack(); 1.60 + 1.61 + public static void register(Context context) { 1.62 + if (sRegisteredCount++ != 0) { 1.63 + // Already registered 1.64 + return; 1.65 + } 1.66 + sInstance.start(context); 1.67 + } 1.68 + 1.69 + public static void unregister() { 1.70 + if (sRegisteredCount == 0) { 1.71 + Log.w(LOGTAG, "register/unregister mismatch"); 1.72 + return; 1.73 + } 1.74 + if (--sRegisteredCount != 0) { 1.75 + // Should still be registered 1.76 + return; 1.77 + } 1.78 + sInstance.stop(); 1.79 + } 1.80 + 1.81 + private void start(final Context context) { 1.82 + 1.83 + Thread receiverThread = new Thread(new Runnable() { 1.84 + @Override 1.85 + public void run() { 1.86 + Looper.prepare(); 1.87 + synchronized (ANRReporter.this) { 1.88 + mHandler = new Handler(); 1.89 + ANRReporter.this.notify(); 1.90 + } 1.91 + if (DEBUG) { 1.92 + Log.d(LOGTAG, "registering receiver"); 1.93 + } 1.94 + context.registerReceiver(ANRReporter.this, 1.95 + new IntentFilter(ANR_ACTION), 1.96 + null, 1.97 + mHandler); 1.98 + Looper.loop(); 1.99 + 1.100 + if (DEBUG) { 1.101 + Log.d(LOGTAG, "unregistering receiver"); 1.102 + } 1.103 + context.unregisterReceiver(ANRReporter.this); 1.104 + mHandler = null; 1.105 + } 1.106 + }, LOGTAG); 1.107 + 1.108 + receiverThread.setDaemon(true); 1.109 + receiverThread.start(); 1.110 + } 1.111 + 1.112 + private void stop() { 1.113 + synchronized (this) { 1.114 + while (mHandler == null) { 1.115 + try { 1.116 + wait(1000); 1.117 + if (mHandler == null) { 1.118 + // We timed out; just give up. The process is probably 1.119 + // quitting anyways, so we let the OS do the clean up 1.120 + Log.w(LOGTAG, "timed out waiting for handler"); 1.121 + return; 1.122 + } 1.123 + } catch (InterruptedException e) { 1.124 + } 1.125 + } 1.126 + } 1.127 + Looper looper = mHandler.getLooper(); 1.128 + looper.quit(); 1.129 + try { 1.130 + looper.getThread().join(); 1.131 + } catch (InterruptedException e) { 1.132 + } 1.133 + } 1.134 + 1.135 + private ANRReporter() { 1.136 + } 1.137 + 1.138 + // Return the "traces.txt" file, or null if there is no such file 1.139 + private static File getTracesFile() { 1.140 + try { 1.141 + // getprop [prop-name [default-value]] 1.142 + Process propProc = (new ProcessBuilder()) 1.143 + .command("/system/bin/getprop", "dalvik.vm.stack-trace-file") 1.144 + .redirectErrorStream(true) 1.145 + .start(); 1.146 + try { 1.147 + BufferedReader buf = new BufferedReader( 1.148 + new InputStreamReader(propProc.getInputStream()), TRACES_LINE_SIZE); 1.149 + String propVal = buf.readLine(); 1.150 + if (DEBUG) { 1.151 + Log.d(LOGTAG, "getprop returned " + String.valueOf(propVal)); 1.152 + } 1.153 + // getprop can return empty string when the prop value is empty 1.154 + // or prop is undefined, treat both cases the same way 1.155 + if (propVal != null && propVal.length() != 0) { 1.156 + File tracesFile = new File(propVal); 1.157 + if (tracesFile.isFile() && tracesFile.canRead()) { 1.158 + return tracesFile; 1.159 + } else if (DEBUG) { 1.160 + Log.d(LOGTAG, "cannot access traces file"); 1.161 + } 1.162 + } else if (DEBUG) { 1.163 + Log.d(LOGTAG, "empty getprop result"); 1.164 + } 1.165 + } finally { 1.166 + propProc.destroy(); 1.167 + } 1.168 + } catch (IOException e) { 1.169 + Log.w(LOGTAG, e); 1.170 + } catch (ClassCastException e) { 1.171 + Log.w(LOGTAG, e); // Bug 975436 1.172 + } 1.173 + // Check most common location one last time just in case 1.174 + File tracesFile = new File("/data/anr/traces.txt"); 1.175 + if (tracesFile.isFile() && tracesFile.canRead()) { 1.176 + return tracesFile; 1.177 + } 1.178 + return null; 1.179 + } 1.180 + 1.181 + private static File getPingFile() { 1.182 + if (GeckoAppShell.getContext() == null) { 1.183 + return null; 1.184 + } 1.185 + GeckoProfile profile = GeckoAppShell.getGeckoInterface().getProfile(); 1.186 + if (profile == null) { 1.187 + return null; 1.188 + } 1.189 + File profDir = profile.getDir(); 1.190 + if (profDir == null) { 1.191 + return null; 1.192 + } 1.193 + File pingDir = new File(profDir, "saved-telemetry-pings"); 1.194 + pingDir.mkdirs(); 1.195 + if (!(pingDir.exists() && pingDir.isDirectory())) { 1.196 + return null; 1.197 + } 1.198 + return new File(pingDir, UUID.randomUUID().toString()); 1.199 + } 1.200 + 1.201 + // Return true if the traces file corresponds to a Gecko ANR 1.202 + private static boolean isGeckoTraces(String pkgName, File tracesFile) { 1.203 + try { 1.204 + final String END_OF_PACKAGE_NAME = "([^a-zA-Z0-9_]|$)"; 1.205 + // Regex for finding our package name in the traces file 1.206 + Pattern pkgPattern = Pattern.compile(Pattern.quote(pkgName) + END_OF_PACKAGE_NAME); 1.207 + Pattern mangledPattern = null; 1.208 + if (!AppConstants.MANGLED_ANDROID_PACKAGE_NAME.equals(pkgName)) { 1.209 + mangledPattern = Pattern.compile(Pattern.quote( 1.210 + AppConstants.MANGLED_ANDROID_PACKAGE_NAME) + END_OF_PACKAGE_NAME); 1.211 + } 1.212 + if (DEBUG) { 1.213 + Log.d(LOGTAG, "trying to match package: " + pkgName); 1.214 + } 1.215 + BufferedReader traces = new BufferedReader( 1.216 + new FileReader(tracesFile), TRACES_BLOCK_SIZE); 1.217 + try { 1.218 + for (int count = 0; count < LINES_TO_IDENTIFY_TRACES; count++) { 1.219 + String line = traces.readLine(); 1.220 + if (DEBUG) { 1.221 + Log.d(LOGTAG, "identifying line: " + String.valueOf(line)); 1.222 + } 1.223 + if (line == null) { 1.224 + if (DEBUG) { 1.225 + Log.d(LOGTAG, "reached end of traces file"); 1.226 + } 1.227 + return false; 1.228 + } 1.229 + if (pkgPattern.matcher(line).find()) { 1.230 + // traces.txt file contains our package 1.231 + return true; 1.232 + } 1.233 + if (mangledPattern != null && mangledPattern.matcher(line).find()) { 1.234 + // traces.txt file contains our alternate package 1.235 + return true; 1.236 + } 1.237 + } 1.238 + } finally { 1.239 + traces.close(); 1.240 + } 1.241 + } catch (IOException e) { 1.242 + // meh, can't even read from it right. just return false 1.243 + } 1.244 + return false; 1.245 + } 1.246 + 1.247 + private static long getUptimeMins() { 1.248 + 1.249 + long uptimeMins = (new File("/proc/self/stat")).lastModified(); 1.250 + if (uptimeMins != 0L) { 1.251 + uptimeMins = (System.currentTimeMillis() - uptimeMins) / 1000L / 60L; 1.252 + if (DEBUG) { 1.253 + Log.d(LOGTAG, "uptime " + String.valueOf(uptimeMins)); 1.254 + } 1.255 + return uptimeMins; 1.256 + } else if (DEBUG) { 1.257 + Log.d(LOGTAG, "could not get uptime"); 1.258 + } 1.259 + return 0L; 1.260 + } 1.261 + 1.262 + /* 1.263 + a saved telemetry ping file consists of JSON in the following format, 1.264 + { 1.265 + "reason": "android-anr-report", 1.266 + "slug": "<uuid-string>", 1.267 + "payload": <json-object> 1.268 + } 1.269 + for Android ANR, our JSON payload should look like, 1.270 + { 1.271 + "ver": 1, 1.272 + "simpleMeasurements": { 1.273 + "uptime": <uptime> 1.274 + }, 1.275 + "info": { 1.276 + "reason": "android-anr-report", 1.277 + "OS": "Android", 1.278 + ... 1.279 + }, 1.280 + "androidANR": "...", 1.281 + "androidLogcat": "..." 1.282 + } 1.283 + */ 1.284 + 1.285 + private static int writePingPayload(OutputStream ping, 1.286 + String payload) throws IOException { 1.287 + byte [] data = payload.getBytes(PING_CHARSET); 1.288 + ping.write(data); 1.289 + return data.length; 1.290 + } 1.291 + 1.292 + private static void fillPingHeader(OutputStream ping, String slug) 1.293 + throws IOException { 1.294 + 1.295 + // ping file header 1.296 + byte [] data = ("{" + 1.297 + "\"reason\":\"android-anr-report\"," + 1.298 + "\"slug\":" + JSONObject.quote(slug) + "," + 1.299 + "\"payload\":").getBytes(PING_CHARSET); 1.300 + ping.write(data); 1.301 + if (DEBUG) { 1.302 + Log.d(LOGTAG, "wrote ping header, size = " + String.valueOf(data.length)); 1.303 + } 1.304 + 1.305 + // payload start 1.306 + int size = writePingPayload(ping, ("{" + 1.307 + "\"ver\":1," + 1.308 + "\"simpleMeasurements\":{" + 1.309 + "\"uptime\":" + String.valueOf(getUptimeMins()) + 1.310 + "}," + 1.311 + "\"info\":{" + 1.312 + "\"reason\":\"android-anr-report\"," + 1.313 + "\"OS\":" + JSONObject.quote(SysInfo.getName()) + "," + 1.314 + "\"version\":\"" + String.valueOf(SysInfo.getVersion()) + "\"," + 1.315 + "\"appID\":" + JSONObject.quote(AppConstants.MOZ_APP_ID) + "," + 1.316 + "\"appVersion\":" + JSONObject.quote(AppConstants.MOZ_APP_VERSION)+ "," + 1.317 + "\"appName\":" + JSONObject.quote(AppConstants.MOZ_APP_BASENAME) + "," + 1.318 + "\"appBuildID\":" + JSONObject.quote(AppConstants.MOZ_APP_BUILDID) + "," + 1.319 + "\"appUpdateChannel\":" + JSONObject.quote(AppConstants.MOZ_UPDATE_CHANNEL) + "," + 1.320 + // Technically the platform build ID may be different, but we'll never know 1.321 + "\"platformBuildID\":" + JSONObject.quote(AppConstants.MOZ_APP_BUILDID) + "," + 1.322 + "\"locale\":" + JSONObject.quote(SysInfo.getLocale()) + "," + 1.323 + "\"cpucount\":" + String.valueOf(SysInfo.getCPUCount()) + "," + 1.324 + "\"memsize\":" + String.valueOf(SysInfo.getMemSize()) + "," + 1.325 + "\"arch\":" + JSONObject.quote(SysInfo.getArchABI()) + "," + 1.326 + "\"kernel_version\":" + JSONObject.quote(SysInfo.getKernelVersion()) + "," + 1.327 + "\"device\":" + JSONObject.quote(SysInfo.getDevice()) + "," + 1.328 + "\"manufacturer\":" + JSONObject.quote(SysInfo.getManufacturer()) + "," + 1.329 + "\"hardware\":" + JSONObject.quote(SysInfo.getHardware()) + 1.330 + "}," + 1.331 + "\"androidANR\":\"")); 1.332 + if (DEBUG) { 1.333 + Log.d(LOGTAG, "wrote metadata, size = " + String.valueOf(size)); 1.334 + } 1.335 + 1.336 + // We are at the start of ANR data 1.337 + } 1.338 + 1.339 + // Block is a section of the larger input stream, and we want to find pattern within 1.340 + // the stream. This is straightforward if the entire pattern is within one block; 1.341 + // however, if the pattern spans across two blocks, we have to match both the start of 1.342 + // the pattern in the first block and the end of the pattern in the second block. 1.343 + // * If pattern is found in block, this method returns the index at the end of the 1.344 + // found pattern, which must always be > 0. 1.345 + // * If pattern is not found, it returns 0. 1.346 + // * If the start of the pattern matches the end of the block, it returns a number 1.347 + // < 0, which equals the negated value of how many characters in pattern are already 1.348 + // matched; when processing the next block, this number is passed in through 1.349 + // prevIndex, and the rest of the characters in pattern are matched against the 1.350 + // start of this second block. The method returns value > 0 if the rest of the 1.351 + // characters match, or 0 if they do not. 1.352 + private static int getEndPatternIndex(String block, String pattern, int prevIndex) { 1.353 + if (pattern == null || block.length() < pattern.length()) { 1.354 + // Nothing to do 1.355 + return 0; 1.356 + } 1.357 + if (prevIndex < 0) { 1.358 + // Last block ended with a partial start; now match start of block to rest of pattern 1.359 + if (block.startsWith(pattern.substring(-prevIndex, pattern.length()))) { 1.360 + // Rest of pattern matches; return index at end of pattern 1.361 + return pattern.length() + prevIndex; 1.362 + } 1.363 + // Not a match; continue with normal search 1.364 + } 1.365 + // Did not find pattern in last block; see if entire pattern is inside this block 1.366 + int index = block.indexOf(pattern); 1.367 + if (index >= 0) { 1.368 + // Found pattern; return index at end of the pattern 1.369 + return index + pattern.length(); 1.370 + } 1.371 + // Block does not contain the entire pattern, but see if the end of the block 1.372 + // contains the start of pattern. To do that, we see if block ends with the 1.373 + // first n-1 characters of pattern, the first n-2 characters of pattern, etc. 1.374 + for (index = block.length() - pattern.length() + 1; index < block.length(); index++) { 1.375 + // Using index as a start, see if the rest of block contains the start of pattern 1.376 + if (block.charAt(index) == pattern.charAt(0) && 1.377 + block.endsWith(pattern.substring(0, block.length() - index))) { 1.378 + // Found partial match; return -(number of characters matched), 1.379 + // i.e. -1 for 1 character matched, -2 for 2 characters matched, etc. 1.380 + return index - block.length(); 1.381 + } 1.382 + } 1.383 + return 0; 1.384 + } 1.385 + 1.386 + // Copy the content of reader to ping; 1.387 + // copying stops when endPattern is found in the input stream 1.388 + private static int fillPingBlock(OutputStream ping, 1.389 + Reader reader, String endPattern) 1.390 + throws IOException { 1.391 + 1.392 + int total = 0; 1.393 + int endIndex = 0; 1.394 + char [] block = new char[TRACES_BLOCK_SIZE]; 1.395 + for (int size = reader.read(block); size >= 0; size = reader.read(block)) { 1.396 + String stringBlock = new String(block, 0, size); 1.397 + endIndex = getEndPatternIndex(stringBlock, endPattern, endIndex); 1.398 + if (endIndex > 0) { 1.399 + // Found end pattern; clip the string 1.400 + stringBlock = stringBlock.substring(0, endIndex); 1.401 + } 1.402 + String quoted = JSONObject.quote(stringBlock); 1.403 + total += writePingPayload(ping, quoted.substring(1, quoted.length() - 1)); 1.404 + if (endIndex > 0) { 1.405 + // End pattern already found; return now 1.406 + break; 1.407 + } 1.408 + } 1.409 + return total; 1.410 + } 1.411 + 1.412 + private static void fillPingFooter(OutputStream ping, 1.413 + boolean haveNativeStack) 1.414 + throws IOException { 1.415 + 1.416 + // We are at the end of ANR data 1.417 + 1.418 + int total = writePingPayload(ping, ("\"," + 1.419 + "\"androidLogcat\":\"")); 1.420 + 1.421 + try { 1.422 + // get the last 200 lines of logcat 1.423 + Process proc = (new ProcessBuilder()) 1.424 + .command("/system/bin/logcat", "-v", "threadtime", "-t", "200", "-d", "*:D") 1.425 + .redirectErrorStream(true) 1.426 + .start(); 1.427 + try { 1.428 + Reader procOut = new InputStreamReader(proc.getInputStream(), TRACES_CHARSET); 1.429 + int size = fillPingBlock(ping, procOut, null); 1.430 + if (DEBUG) { 1.431 + Log.d(LOGTAG, "wrote logcat, size = " + String.valueOf(size)); 1.432 + } 1.433 + } finally { 1.434 + proc.destroy(); 1.435 + } 1.436 + } catch (IOException e) { 1.437 + // ignore because logcat is not essential 1.438 + Log.w(LOGTAG, e); 1.439 + } 1.440 + 1.441 + if (haveNativeStack) { 1.442 + total += writePingPayload(ping, ("\"," + 1.443 + "\"androidNativeStack\":")); 1.444 + 1.445 + String nativeStack = String.valueOf(getNativeStack()); 1.446 + int size = writePingPayload(ping, nativeStack); 1.447 + if (DEBUG) { 1.448 + Log.d(LOGTAG, "wrote native stack, size = " + String.valueOf(size)); 1.449 + } 1.450 + total += size + writePingPayload(ping, "}"); 1.451 + } else { 1.452 + total += writePingPayload(ping, "\"}"); 1.453 + } 1.454 + 1.455 + byte [] data = ( 1.456 + "}").getBytes(PING_CHARSET); 1.457 + ping.write(data); 1.458 + if (DEBUG) { 1.459 + Log.d(LOGTAG, "wrote ping footer, size = " + String.valueOf(data.length + total)); 1.460 + } 1.461 + } 1.462 + 1.463 + private static void processTraces(Reader traces, File pingFile) { 1.464 + 1.465 + // Unwinding is memory intensive; only unwind if we have enough memory 1.466 + boolean haveNativeStack = requestNativeStack( 1.467 + /* unwind */ SysInfo.getMemSize() >= 640); 1.468 + try { 1.469 + OutputStream ping = new BufferedOutputStream( 1.470 + new FileOutputStream(pingFile), TRACES_BLOCK_SIZE); 1.471 + try { 1.472 + fillPingHeader(ping, pingFile.getName()); 1.473 + // Traces file has the format 1.474 + // ----- pid xxx at xxx ----- 1.475 + // Cmd line: org.mozilla.xxx 1.476 + // * stack trace * 1.477 + // ----- end xxx ----- 1.478 + // ----- pid xxx at xxx ----- 1.479 + // Cmd line: com.android.xxx 1.480 + // * stack trace * 1.481 + // ... 1.482 + // If we end the stack dump at the first end marker, 1.483 + // only Fennec stacks will be dumped 1.484 + int size = fillPingBlock(ping, traces, "\n----- end"); 1.485 + if (DEBUG) { 1.486 + Log.d(LOGTAG, "wrote traces, size = " + String.valueOf(size)); 1.487 + } 1.488 + fillPingFooter(ping, haveNativeStack); 1.489 + if (DEBUG) { 1.490 + Log.d(LOGTAG, "finished creating ping file"); 1.491 + } 1.492 + return; 1.493 + } finally { 1.494 + ping.close(); 1.495 + if (haveNativeStack) { 1.496 + releaseNativeStack(); 1.497 + } 1.498 + } 1.499 + } catch (IOException e) { 1.500 + Log.w(LOGTAG, e); 1.501 + } 1.502 + // exception; delete ping file 1.503 + if (pingFile.exists()) { 1.504 + pingFile.delete(); 1.505 + } 1.506 + } 1.507 + 1.508 + private static void processTraces(File tracesFile, File pingFile) { 1.509 + try { 1.510 + Reader traces = new InputStreamReader( 1.511 + new FileInputStream(tracesFile), TRACES_CHARSET); 1.512 + try { 1.513 + processTraces(traces, pingFile); 1.514 + } finally { 1.515 + traces.close(); 1.516 + } 1.517 + } catch (IOException e) { 1.518 + Log.w(LOGTAG, e); 1.519 + } 1.520 + } 1.521 + 1.522 + @Override 1.523 + public void onReceive(Context context, Intent intent) { 1.524 + if (mPendingANR) { 1.525 + // we already processed an ANR without getting unstuck; skip this one 1.526 + if (DEBUG) { 1.527 + Log.d(LOGTAG, "skipping duplicate ANR"); 1.528 + } 1.529 + return; 1.530 + } 1.531 + if (ThreadUtils.getUiHandler() != null) { 1.532 + mPendingANR = true; 1.533 + // detect when the main thread gets unstuck 1.534 + ThreadUtils.postToUiThread(new Runnable() { 1.535 + @Override 1.536 + public void run() { 1.537 + // okay to reset mPendingANR on main thread 1.538 + mPendingANR = false; 1.539 + if (DEBUG) { 1.540 + Log.d(LOGTAG, "yay we got unstuck!"); 1.541 + } 1.542 + } 1.543 + }); 1.544 + } 1.545 + if (DEBUG) { 1.546 + Log.d(LOGTAG, "receiving " + String.valueOf(intent)); 1.547 + } 1.548 + if (!ANR_ACTION.equals(intent.getAction())) { 1.549 + return; 1.550 + } 1.551 + 1.552 + // make sure we have a good save location first 1.553 + File pingFile = getPingFile(); 1.554 + if (DEBUG) { 1.555 + Log.d(LOGTAG, "using ping file: " + String.valueOf(pingFile)); 1.556 + } 1.557 + if (pingFile == null) { 1.558 + return; 1.559 + } 1.560 + 1.561 + File tracesFile = getTracesFile(); 1.562 + if (DEBUG) { 1.563 + Log.d(LOGTAG, "using traces file: " + String.valueOf(tracesFile)); 1.564 + } 1.565 + if (tracesFile == null) { 1.566 + return; 1.567 + } 1.568 + 1.569 + // We get ANR intents from all ANRs in the system, but we only want Gecko ANRs 1.570 + if (!isGeckoTraces(context.getPackageName(), tracesFile)) { 1.571 + if (DEBUG) { 1.572 + Log.d(LOGTAG, "traces is not Gecko ANR"); 1.573 + } 1.574 + return; 1.575 + } 1.576 + Log.i(LOGTAG, "processing Gecko ANR"); 1.577 + processTraces(tracesFile, pingFile); 1.578 + } 1.579 +}