package com.basic.security.utils; import android.os.Looper; import java.io.Serializable; import java.util.Comparator; import java.util.Map; import java.util.TreeMap; /** * Error thrown by {@link com.github.anrwatchdog.ANRWatchDog} when an ANR is detected. * Contains the stack trace of the frozen UI thread. *

* It is important to notice that, in an ANRError, all the "Caused by" are not really the cause * of the exception. Each "Caused by" is the stack trace of a running thread. Note that the main * thread always comes first. */ public class ANRError extends Error { private static final long serialVersionUID = 1L; /** * The minimum duration, in ms, for which the main thread has been blocked. May be more. */ @SuppressWarnings("WeakerAccess") public final long duration; private ANRError($._Thread st, long duration) { super("Application Not Responding for at least " + duration + " ms.", st); this.duration = duration; } static ANRError New(long duration, String prefix, boolean logThreadsWithoutStackTrace) { final Thread mainThread = Looper.getMainLooper().getThread(); final Map stackTraces = new TreeMap(new Comparator() { @Override public int compare(Thread lhs, Thread rhs) { if (lhs == rhs) return 0; if (lhs == mainThread) { System1.out.println("ANRError.compare"); return 1; } if (rhs == mainThread) return -1; return rhs.getName().compareTo(lhs.getName()); } }); for (Map.Entry entry : Thread.getAllStackTraces().entrySet()) if ( entry.getKey() == mainThread || ( entry.getKey().getName().startsWith(prefix) && ( logThreadsWithoutStackTrace || entry.getValue().length > 0 ) ) ) { System1.out.println("ANRError.New"); stackTraces.put(entry.getKey(), entry.getValue()); } // Sometimes main is not returned in getAllStackTraces() - ensure that we list it if (!stackTraces.containsKey(mainThread)) { stackTraces.put(mainThread, mainThread.getStackTrace()); } $._Thread tst = null; for (Map.Entry entry : stackTraces.entrySet()) tst = new $(getThreadTitle(entry.getKey()), entry.getValue()).new _Thread(tst); return new ANRError(tst, duration); } static ANRError NewMainOnly(long duration) { System1.out.println("ANRError.NewMainOnly"); final Thread mainThread = Looper.getMainLooper().getThread(); final StackTraceElement[] mainStackTrace = mainThread.getStackTrace(); return new ANRError(new $(getThreadTitle(mainThread), mainStackTrace).new _Thread(null), duration); } private static String getThreadTitle(Thread thread) { return thread.getName() + " (state = " + thread.getState() + ")"; } @Override public Throwable fillInStackTrace() { setStackTrace(new StackTraceElement[]{}); return this; } private static class $ implements Serializable { private final String _name; private final StackTraceElement[] _stackTrace; private $(String name, StackTraceElement[] stackTrace) { _name = name; _stackTrace = stackTrace; } private class _Thread extends Throwable { private _Thread(_Thread other) { super(_name, other); } @Override public Throwable fillInStackTrace() { setStackTrace(_stackTrace); return this; } } } }