Initial commit
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import com.squareup.leakcanary.internal.ForegroundService;
|
||||
import java.io.File;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public abstract class AbstractAnalysisResultService extends ForegroundService {
|
||||
private static final String ANALYZED_HEAP_PATH_EXTRA = "analyzed_heap_path_extra";
|
||||
|
||||
public AbstractAnalysisResultService() {
|
||||
super(AbstractAnalysisResultService.class.getName(), R.string.leak_canary_notification_reporting);
|
||||
}
|
||||
|
||||
public static void sendResultToListener(Context context, String str, HeapDump heapDump, AnalysisResult analysisResult) {
|
||||
try {
|
||||
Intent intent = new Intent(context, Class.forName(str));
|
||||
File save = AnalyzedHeap.save(heapDump, analysisResult);
|
||||
if (save != null) {
|
||||
intent.putExtra(ANALYZED_HEAP_PATH_EXTRA, save.getAbsolutePath());
|
||||
}
|
||||
ContextCompat.a(context, intent);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onAnalysisResultFailure(String str) {
|
||||
CanaryLog.d(str, new Object[0]);
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.internal.ForegroundService
|
||||
protected final void onHandleIntentInForeground(Intent intent) {
|
||||
if (intent == null) {
|
||||
CanaryLog.d("AbstractAnalysisResultService received a null intent, ignoring.", new Object[0]);
|
||||
return;
|
||||
}
|
||||
if (!intent.hasExtra(ANALYZED_HEAP_PATH_EXTRA)) {
|
||||
onAnalysisResultFailure(getString(R.string.leak_canary_result_failure_no_disk_space));
|
||||
return;
|
||||
}
|
||||
AnalyzedHeap load = AnalyzedHeap.load(new File(intent.getStringExtra(ANALYZED_HEAP_PATH_EXTRA)));
|
||||
if (load == null) {
|
||||
onAnalysisResultFailure(getString(R.string.leak_canary_result_failure_no_file));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
onHeapAnalyzed(load);
|
||||
} finally {
|
||||
load.heapDump.heapDumpFile.delete();
|
||||
load.selfFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onHeapAnalyzed(AnalyzedHeap analyzedHeap) {
|
||||
onHeapAnalyzed(analyzedHeap.heapDump, analyzedHeap.result);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected void onHeapAnalyzed(HeapDump heapDump, AnalysisResult analysisResult) {
|
||||
}
|
||||
}
|
42
sources/com/squareup/leakcanary/ActivityRefWatcher.java
Normal file
42
sources/com/squareup/leakcanary/ActivityRefWatcher.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import com.squareup.leakcanary.internal.ActivityLifecycleCallbacksAdapter;
|
||||
|
||||
@Deprecated
|
||||
/* loaded from: classes.dex */
|
||||
public final class ActivityRefWatcher {
|
||||
private final Application application;
|
||||
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { // from class: com.squareup.leakcanary.ActivityRefWatcher.1
|
||||
@Override // com.squareup.leakcanary.internal.ActivityLifecycleCallbacksAdapter, android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
ActivityRefWatcher.this.refWatcher.watch(activity);
|
||||
}
|
||||
};
|
||||
private final RefWatcher refWatcher;
|
||||
|
||||
private ActivityRefWatcher(Application application, RefWatcher refWatcher) {
|
||||
this.application = application;
|
||||
this.refWatcher = refWatcher;
|
||||
}
|
||||
|
||||
public static void install(Context context, RefWatcher refWatcher) {
|
||||
Application application = (Application) context.getApplicationContext();
|
||||
application.registerActivityLifecycleCallbacks(new ActivityRefWatcher(application, refWatcher).lifecycleCallbacks);
|
||||
}
|
||||
|
||||
public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {
|
||||
install(application, refWatcher);
|
||||
}
|
||||
|
||||
public void stopWatchingActivities() {
|
||||
this.application.unregisterActivityLifecycleCallbacks(this.lifecycleCallbacks);
|
||||
}
|
||||
|
||||
public void watchActivities() {
|
||||
stopWatchingActivities();
|
||||
this.application.registerActivityLifecycleCallbacks(this.lifecycleCallbacks);
|
||||
}
|
||||
}
|
63
sources/com/squareup/leakcanary/AnalysisResult.java
Normal file
63
sources/com/squareup/leakcanary/AnalysisResult.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class AnalysisResult implements Serializable {
|
||||
public static final long RETAINED_HEAP_SKIPPED = -1;
|
||||
public final long analysisDurationMs;
|
||||
public final String className;
|
||||
public final boolean excludedLeak;
|
||||
public final Throwable failure;
|
||||
public final boolean leakFound;
|
||||
public final LeakTrace leakTrace;
|
||||
public final long retainedHeapSize;
|
||||
|
||||
private AnalysisResult(boolean z, boolean z2, String str, LeakTrace leakTrace, Throwable th, long j, long j2) {
|
||||
this.leakFound = z;
|
||||
this.excludedLeak = z2;
|
||||
this.className = str;
|
||||
this.leakTrace = leakTrace;
|
||||
this.failure = th;
|
||||
this.retainedHeapSize = j;
|
||||
this.analysisDurationMs = j2;
|
||||
}
|
||||
|
||||
private String classSimpleName(String str) {
|
||||
int lastIndexOf = str.lastIndexOf(46);
|
||||
return lastIndexOf == -1 ? str : str.substring(lastIndexOf + 1);
|
||||
}
|
||||
|
||||
public static AnalysisResult failure(Throwable th, long j) {
|
||||
return new AnalysisResult(false, false, null, null, th, 0L, j);
|
||||
}
|
||||
|
||||
public static AnalysisResult leakDetected(boolean z, String str, LeakTrace leakTrace, long j, long j2) {
|
||||
return new AnalysisResult(true, z, str, leakTrace, null, j, j2);
|
||||
}
|
||||
|
||||
public static AnalysisResult noLeak(String str, long j) {
|
||||
return new AnalysisResult(false, false, str, null, null, 0L, j);
|
||||
}
|
||||
|
||||
public RuntimeException leakTraceAsFakeException() {
|
||||
if (!this.leakFound) {
|
||||
throw new UnsupportedOperationException("leakTraceAsFakeException() can only be called when leakFound is true");
|
||||
}
|
||||
int i = 0;
|
||||
LeakTraceElement leakTraceElement = this.leakTrace.elements.get(0);
|
||||
String classSimpleName = classSimpleName(leakTraceElement.className);
|
||||
RuntimeException runtimeException = new RuntimeException(classSimpleName(this.className) + " leak from " + classSimpleName + " (holder=" + leakTraceElement.holder + ", type=" + leakTraceElement.type + ")");
|
||||
StackTraceElement[] stackTraceElementArr = new StackTraceElement[this.leakTrace.elements.size()];
|
||||
for (LeakTraceElement leakTraceElement2 : this.leakTrace.elements) {
|
||||
String str = leakTraceElement2.referenceName;
|
||||
if (str == null) {
|
||||
str = "leaking";
|
||||
}
|
||||
stackTraceElementArr[i] = new StackTraceElement(leakTraceElement2.className, str, classSimpleName(leakTraceElement2.className) + ".java", 42);
|
||||
i++;
|
||||
}
|
||||
runtimeException.setStackTrace(stackTraceElementArr);
|
||||
return runtimeException;
|
||||
}
|
||||
}
|
151
sources/com/squareup/leakcanary/AnalyzedHeap.java
Normal file
151
sources/com/squareup/leakcanary/AnalyzedHeap.java
Normal file
@@ -0,0 +1,151 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class AnalyzedHeap {
|
||||
public final HeapDump heapDump;
|
||||
public final boolean heapDumpFileExists;
|
||||
public final AnalysisResult result;
|
||||
public final File selfFile;
|
||||
public final long selfLastModified;
|
||||
|
||||
public AnalyzedHeap(HeapDump heapDump, AnalysisResult analysisResult, File file) {
|
||||
this.heapDump = heapDump;
|
||||
this.result = analysisResult;
|
||||
this.selfFile = file;
|
||||
this.heapDumpFileExists = heapDump.heapDumpFile.exists();
|
||||
this.selfLastModified = file.lastModified();
|
||||
}
|
||||
|
||||
/* JADX WARN: Removed duplicated region for block: B:17:0x0033 A[Catch: all -> 0x004c, TryCatch #7 {all -> 0x004c, blocks: (B:6:0x0006, B:15:0x002b, B:17:0x0033, B:24:0x003d), top: B:2:0x0001 }] */
|
||||
/* JADX WARN: Removed duplicated region for block: B:20:0x0048 A[EXC_TOP_SPLITTER, SYNTHETIC] */
|
||||
/* JADX WARN: Removed duplicated region for block: B:24:0x003d A[Catch: all -> 0x004c, TRY_LEAVE, TryCatch #7 {all -> 0x004c, blocks: (B:6:0x0006, B:15:0x002b, B:17:0x0033, B:24:0x003d), top: B:2:0x0001 }] */
|
||||
/* JADX WARN: Removed duplicated region for block: B:29:0x004f A[EXC_TOP_SPLITTER, SYNTHETIC] */
|
||||
/*
|
||||
Code decompiled incorrectly, please refer to instructions dump.
|
||||
To view partially-correct code enable 'Show inconsistent code' option in preferences
|
||||
*/
|
||||
public static com.squareup.leakcanary.AnalyzedHeap load(java.io.File r6) {
|
||||
/*
|
||||
r0 = 0
|
||||
java.io.FileInputStream r1 = new java.io.FileInputStream // Catch: java.lang.Throwable -> L24 java.lang.ClassNotFoundException -> L27 java.io.IOException -> L29
|
||||
r1.<init>(r6) // Catch: java.lang.Throwable -> L24 java.lang.ClassNotFoundException -> L27 java.io.IOException -> L29
|
||||
java.io.ObjectInputStream r2 = new java.io.ObjectInputStream // Catch: java.lang.ClassNotFoundException -> L20 java.io.IOException -> L22 java.lang.Throwable -> L4c
|
||||
r2.<init>(r1) // Catch: java.lang.ClassNotFoundException -> L20 java.io.IOException -> L22 java.lang.Throwable -> L4c
|
||||
java.lang.Object r3 = r2.readObject() // Catch: java.lang.ClassNotFoundException -> L20 java.io.IOException -> L22 java.lang.Throwable -> L4c
|
||||
com.squareup.leakcanary.HeapDump r3 = (com.squareup.leakcanary.HeapDump) r3 // Catch: java.lang.ClassNotFoundException -> L20 java.io.IOException -> L22 java.lang.Throwable -> L4c
|
||||
java.lang.Object r2 = r2.readObject() // Catch: java.lang.ClassNotFoundException -> L20 java.io.IOException -> L22 java.lang.Throwable -> L4c
|
||||
com.squareup.leakcanary.AnalysisResult r2 = (com.squareup.leakcanary.AnalysisResult) r2 // Catch: java.lang.ClassNotFoundException -> L20 java.io.IOException -> L22 java.lang.Throwable -> L4c
|
||||
com.squareup.leakcanary.AnalyzedHeap r4 = new com.squareup.leakcanary.AnalyzedHeap // Catch: java.lang.ClassNotFoundException -> L20 java.io.IOException -> L22 java.lang.Throwable -> L4c
|
||||
r4.<init>(r3, r2, r6) // Catch: java.lang.ClassNotFoundException -> L20 java.io.IOException -> L22 java.lang.Throwable -> L4c
|
||||
r1.close() // Catch: java.io.IOException -> L1f
|
||||
L1f:
|
||||
return r4
|
||||
L20:
|
||||
r2 = move-exception
|
||||
goto L2b
|
||||
L22:
|
||||
r2 = move-exception
|
||||
goto L2b
|
||||
L24:
|
||||
r6 = move-exception
|
||||
r1 = r0
|
||||
goto L4d
|
||||
L27:
|
||||
r2 = move-exception
|
||||
goto L2a
|
||||
L29:
|
||||
r2 = move-exception
|
||||
L2a:
|
||||
r1 = r0
|
||||
L2b:
|
||||
boolean r3 = r6.delete() // Catch: java.lang.Throwable -> L4c
|
||||
r4 = 0
|
||||
r5 = 1
|
||||
if (r3 == 0) goto L3d
|
||||
java.lang.String r3 = "Could not read result file %s, deleted it."
|
||||
java.lang.Object[] r5 = new java.lang.Object[r5] // Catch: java.lang.Throwable -> L4c
|
||||
r5[r4] = r6 // Catch: java.lang.Throwable -> L4c
|
||||
com.squareup.leakcanary.CanaryLog.d(r2, r3, r5) // Catch: java.lang.Throwable -> L4c
|
||||
goto L46
|
||||
L3d:
|
||||
java.lang.String r3 = "Could not read result file %s, could not delete it either."
|
||||
java.lang.Object[] r5 = new java.lang.Object[r5] // Catch: java.lang.Throwable -> L4c
|
||||
r5[r4] = r6 // Catch: java.lang.Throwable -> L4c
|
||||
com.squareup.leakcanary.CanaryLog.d(r2, r3, r5) // Catch: java.lang.Throwable -> L4c
|
||||
L46:
|
||||
if (r1 == 0) goto L4b
|
||||
r1.close() // Catch: java.io.IOException -> L4b
|
||||
L4b:
|
||||
return r0
|
||||
L4c:
|
||||
r6 = move-exception
|
||||
L4d:
|
||||
if (r1 == 0) goto L52
|
||||
r1.close() // Catch: java.io.IOException -> L52
|
||||
L52:
|
||||
throw r6
|
||||
*/
|
||||
throw new UnsupportedOperationException("Method not decompiled: com.squareup.leakcanary.AnalyzedHeap.load(java.io.File):com.squareup.leakcanary.AnalyzedHeap");
|
||||
}
|
||||
|
||||
/* JADX WARN: Removed duplicated region for block: B:25:0x004f A[EXC_TOP_SPLITTER, SYNTHETIC] */
|
||||
/*
|
||||
Code decompiled incorrectly, please refer to instructions dump.
|
||||
To view partially-correct code enable 'Show inconsistent code' option in preferences
|
||||
*/
|
||||
public static java.io.File save(com.squareup.leakcanary.HeapDump r4, com.squareup.leakcanary.AnalysisResult r5) {
|
||||
/*
|
||||
java.io.File r0 = new java.io.File
|
||||
java.io.File r1 = r4.heapDumpFile
|
||||
java.io.File r1 = r1.getParentFile()
|
||||
java.lang.StringBuilder r2 = new java.lang.StringBuilder
|
||||
r2.<init>()
|
||||
java.io.File r3 = r4.heapDumpFile
|
||||
java.lang.String r3 = r3.getName()
|
||||
r2.append(r3)
|
||||
java.lang.String r3 = ".result"
|
||||
r2.append(r3)
|
||||
java.lang.String r2 = r2.toString()
|
||||
r0.<init>(r1, r2)
|
||||
r1 = 0
|
||||
java.io.FileOutputStream r2 = new java.io.FileOutputStream // Catch: java.lang.Throwable -> L39 java.io.IOException -> L3c
|
||||
r2.<init>(r0) // Catch: java.lang.Throwable -> L39 java.io.IOException -> L3c
|
||||
java.io.ObjectOutputStream r3 = new java.io.ObjectOutputStream // Catch: java.io.IOException -> L37 java.lang.Throwable -> L4c
|
||||
r3.<init>(r2) // Catch: java.io.IOException -> L37 java.lang.Throwable -> L4c
|
||||
r3.writeObject(r4) // Catch: java.io.IOException -> L37 java.lang.Throwable -> L4c
|
||||
r3.writeObject(r5) // Catch: java.io.IOException -> L37 java.lang.Throwable -> L4c
|
||||
r2.close() // Catch: java.io.IOException -> L36
|
||||
L36:
|
||||
return r0
|
||||
L37:
|
||||
r4 = move-exception
|
||||
goto L3e
|
||||
L39:
|
||||
r4 = move-exception
|
||||
r2 = r1
|
||||
goto L4d
|
||||
L3c:
|
||||
r4 = move-exception
|
||||
r2 = r1
|
||||
L3e:
|
||||
java.lang.String r5 = "Could not save leak analysis result to disk."
|
||||
r0 = 0
|
||||
java.lang.Object[] r0 = new java.lang.Object[r0] // Catch: java.lang.Throwable -> L4c
|
||||
com.squareup.leakcanary.CanaryLog.d(r4, r5, r0) // Catch: java.lang.Throwable -> L4c
|
||||
if (r2 == 0) goto L4b
|
||||
r2.close() // Catch: java.io.IOException -> L4b
|
||||
L4b:
|
||||
return r1
|
||||
L4c:
|
||||
r4 = move-exception
|
||||
L4d:
|
||||
if (r2 == 0) goto L52
|
||||
r2.close() // Catch: java.io.IOException -> L52
|
||||
L52:
|
||||
throw r4
|
||||
*/
|
||||
throw new UnsupportedOperationException("Method not decompiled: com.squareup.leakcanary.AnalyzedHeap.save(com.squareup.leakcanary.HeapDump, com.squareup.leakcanary.AnalysisResult):java.io.File");
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public interface AnalyzerProgressListener {
|
||||
public static final AnalyzerProgressListener NONE = new AnalyzerProgressListener() { // from class: com.squareup.leakcanary.AnalyzerProgressListener.1
|
||||
@Override // com.squareup.leakcanary.AnalyzerProgressListener
|
||||
public void onProgressUpdate(Step step) {
|
||||
}
|
||||
};
|
||||
|
||||
public enum Step {
|
||||
READING_HEAP_DUMP_FILE,
|
||||
PARSING_HEAP_DUMP,
|
||||
DEDUPLICATING_GC_ROOTS,
|
||||
FINDING_LEAKING_REF,
|
||||
FINDING_SHORTEST_PATH,
|
||||
BUILDING_LEAK_TRACE,
|
||||
COMPUTING_DOMINATORS,
|
||||
COMPUTING_BITMAP_SIZE
|
||||
}
|
||||
|
||||
void onProgressUpdate(Step step);
|
||||
}
|
11
sources/com/squareup/leakcanary/AndroidDebuggerControl.java
Normal file
11
sources/com/squareup/leakcanary/AndroidDebuggerControl.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import android.os.Debug;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class AndroidDebuggerControl implements DebuggerControl {
|
||||
@Override // com.squareup.leakcanary.DebuggerControl
|
||||
public boolean isDebuggerAttached() {
|
||||
return Debug.isDebuggerConnected();
|
||||
}
|
||||
}
|
441
sources/com/squareup/leakcanary/AndroidExcludedRefs.java
Normal file
441
sources/com/squareup/leakcanary/AndroidExcludedRefs.java
Normal file
@@ -0,0 +1,441 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import android.os.Build;
|
||||
import com.squareup.leakcanary.ExcludedRefs;
|
||||
import com.squareup.leakcanary.internal.LeakCanaryInternals;
|
||||
import java.lang.ref.PhantomReference;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
/* JADX WARN: Enum visitor error
|
||||
jadx.core.utils.exceptions.JadxRuntimeException: Init of enum field 'ACTIVITY_CLIENT_RECORD__NEXT_IDLE' uses external variables
|
||||
at jadx.core.dex.visitors.EnumVisitor.createEnumFieldByConstructor(EnumVisitor.java:451)
|
||||
at jadx.core.dex.visitors.EnumVisitor.processEnumFieldByField(EnumVisitor.java:372)
|
||||
at jadx.core.dex.visitors.EnumVisitor.processEnumFieldByWrappedInsn(EnumVisitor.java:337)
|
||||
at jadx.core.dex.visitors.EnumVisitor.extractEnumFieldsFromFilledArray(EnumVisitor.java:322)
|
||||
at jadx.core.dex.visitors.EnumVisitor.extractEnumFieldsFromInsn(EnumVisitor.java:262)
|
||||
at jadx.core.dex.visitors.EnumVisitor.convertToEnum(EnumVisitor.java:151)
|
||||
at jadx.core.dex.visitors.EnumVisitor.visit(EnumVisitor.java:100)
|
||||
*/
|
||||
/* JADX WARN: Failed to restore enum class, 'enum' modifier and super class removed */
|
||||
/* loaded from: classes.dex */
|
||||
public abstract class AndroidExcludedRefs {
|
||||
private static final /* synthetic */ AndroidExcludedRefs[] $VALUES;
|
||||
public static final AndroidExcludedRefs ACCESSIBILITY_NODE_INFO__MORIGINALTEXT;
|
||||
public static final AndroidExcludedRefs ACCOUNT_MANAGER;
|
||||
public static final AndroidExcludedRefs ACTIVITY_CHOOSE_MODEL;
|
||||
public static final AndroidExcludedRefs ACTIVITY_CLIENT_RECORD__NEXT_IDLE;
|
||||
public static final AndroidExcludedRefs ACTIVITY_MANAGER_MCONTEXT;
|
||||
public static final AndroidExcludedRefs APP_WIDGET_HOST_CALLBACKS;
|
||||
public static final AndroidExcludedRefs AUDIO_MANAGER;
|
||||
public static final AndroidExcludedRefs AUDIO_MANAGER__MCONTEXT_STATIC;
|
||||
public static final AndroidExcludedRefs AW_RESOURCE__SRESOURCES;
|
||||
public static final AndroidExcludedRefs BACKDROP_FRAME_RENDERER__MDECORVIEW;
|
||||
public static final AndroidExcludedRefs BLOCKING_QUEUE;
|
||||
public static final AndroidExcludedRefs BUBBLE_POPUP_HELPER__SHELPER;
|
||||
public static final AndroidExcludedRefs CLIPBOARD_UI_MANAGER__SINSTANCE;
|
||||
public static final AndroidExcludedRefs CONNECTIVITY_MANAGER__SINSTANCE;
|
||||
public static final AndroidExcludedRefs DEVICE_POLICY_MANAGER__SETTINGS_OBSERVER;
|
||||
public static final AndroidExcludedRefs EDITTEXT_BLINK_MESSAGEQUEUE;
|
||||
public static final AndroidExcludedRefs EVENT_RECEIVER__MMESSAGE_QUEUE;
|
||||
public static final AndroidExcludedRefs FINALIZER_WATCHDOG_DAEMON;
|
||||
public static final AndroidExcludedRefs GESTURE_BOOST_MANAGER;
|
||||
public static final AndroidExcludedRefs INPUT_METHOD_MANAGER__LAST_SERVED_VIEW;
|
||||
public static final AndroidExcludedRefs INPUT_METHOD_MANAGER__ROOT_VIEW;
|
||||
public static final AndroidExcludedRefs INPUT_METHOD_MANAGER__SERVED_VIEW;
|
||||
public static final AndroidExcludedRefs INSTRUMENTATION_RECOMMEND_ACTIVITY;
|
||||
public static final AndroidExcludedRefs LAYOUT_TRANSITION;
|
||||
public static final AndroidExcludedRefs LEAK_CANARY_THREAD;
|
||||
public static final AndroidExcludedRefs LGCONTEXT__MCONTEXT;
|
||||
public static final AndroidExcludedRefs MAIN;
|
||||
public static final AndroidExcludedRefs MAPPER_CLIENT;
|
||||
public static final AndroidExcludedRefs MEDIA_SCANNER_CONNECTION;
|
||||
public static final AndroidExcludedRefs MEDIA_SESSION_LEGACY_HELPER__SINSTANCE;
|
||||
public static final AndroidExcludedRefs PERSONA_MANAGER;
|
||||
public static final AndroidExcludedRefs RESOURCES__MCONTEXT;
|
||||
public static final AndroidExcludedRefs SEM_CLIPBOARD_MANAGER__MCONTEXT;
|
||||
public static final AndroidExcludedRefs SEM_EMERGENCY_MANAGER__MCONTEXT;
|
||||
public static final AndroidExcludedRefs SOFT_REFERENCES;
|
||||
public static final AndroidExcludedRefs SPAN_CONTROLLER;
|
||||
public static final AndroidExcludedRefs SPEECH_RECOGNIZER;
|
||||
public static final AndroidExcludedRefs SPELL_CHECKER;
|
||||
public static final AndroidExcludedRefs SPELL_CHECKER_SESSION;
|
||||
public static final AndroidExcludedRefs SPEN_GESTURE_MANAGER;
|
||||
public static final AndroidExcludedRefs SYSTEM_SENSOR_MANAGER__MAPPCONTEXTIMPL;
|
||||
public static final AndroidExcludedRefs TEXT_LINE__SCACHED;
|
||||
public static final AndroidExcludedRefs TEXT_VIEW__MLAST_HOVERED_VIEW;
|
||||
public static final AndroidExcludedRefs USER_MANAGER__SINSTANCE;
|
||||
public static final AndroidExcludedRefs VIEWLOCATIONHOLDER_ROOT;
|
||||
public static final AndroidExcludedRefs VIEW_CONFIGURATION__MCONTEXT;
|
||||
final boolean applies;
|
||||
|
||||
static {
|
||||
int i;
|
||||
int i2;
|
||||
int i3;
|
||||
int i4;
|
||||
int i5;
|
||||
int i6;
|
||||
int i7;
|
||||
int i8;
|
||||
int i9;
|
||||
int i10 = Build.VERSION.SDK_INT;
|
||||
int i11 = 21;
|
||||
int i12 = 19;
|
||||
int i13 = 1;
|
||||
ACTIVITY_CLIENT_RECORD__NEXT_IDLE = new AndroidExcludedRefs("ACTIVITY_CLIENT_RECORD__NEXT_IDLE", 0, i10 >= 19 && i10 <= 21) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.1
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.app.ActivityThread$ActivityClientRecord", "nextIdle").reason("Android AOSP sometimes keeps a reference to a destroyed activity as a nextIdle client record in the android.app.ActivityThread.mActivities map. Not sure what's going on there, input welcome.");
|
||||
}
|
||||
};
|
||||
SPAN_CONTROLLER = new AndroidExcludedRefs("SPAN_CONTROLLER", i13, Build.VERSION.SDK_INT <= 19) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.2
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.widget.Editor$EasyEditSpanController", "this$0").reason("Editor inserts a special span, which has a reference to the EditText. That span is a NoCopySpan, which makes sure it gets dropped when creating a new SpannableStringBuilder from a given CharSequence. TextView.onSaveInstanceState() does a copy of its mText before saving it in the bundle. Prior to KitKat, that copy was done using the SpannableString constructor, instead of SpannableStringBuilder. The SpannableString constructor does not drop NoCopySpan spans. So we end up with a saved state that holds a reference to the textview and therefore the entire view hierarchy & activity context. Fix: https://github.com/android/platform_frameworks_base/commit/af7dcdf35a37d7a7dbaad7d9869c1c91bce2272b . To fix this, you could override TextView.onSaveInstanceState(), and then use reflection to access TextView.SavedState.mText and clear the NoCopySpan spans.");
|
||||
builder.instanceField("android.widget.Editor$SpanController", "this$0").reason("Editor inserts a special span, which has a reference to the EditText. That span is a NoCopySpan, which makes sure it gets dropped when creating a new SpannableStringBuilder from a given CharSequence. TextView.onSaveInstanceState() does a copy of its mText before saving it in the bundle. Prior to KitKat, that copy was done using the SpannableString constructor, instead of SpannableStringBuilder. The SpannableString constructor does not drop NoCopySpan spans. So we end up with a saved state that holds a reference to the textview and therefore the entire view hierarchy & activity context. Fix: https://github.com/android/platform_frameworks_base/commit/af7dcdf35a37d7a7dbaad7d9869c1c91bce2272b . To fix this, you could override TextView.onSaveInstanceState(), and then use reflection to access TextView.SavedState.mText and clear the NoCopySpan spans.");
|
||||
}
|
||||
};
|
||||
MEDIA_SESSION_LEGACY_HELPER__SINSTANCE = new AndroidExcludedRefs("MEDIA_SESSION_LEGACY_HELPER__SINSTANCE", 2, Build.VERSION.SDK_INT == 21) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.3
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.staticField("android.media.session.MediaSessionLegacyHelper", "sInstance").reason("MediaSessionLegacyHelper is a static singleton that is lazily instantiated and keeps a reference to the context it's given the first time MediaSessionLegacyHelper.getHelper() is called. This leak was introduced in android-5.0.1_r1 and fixed in Android 5.1.0_r1 by calling context.getApplicationContext(). Fix: https://github.com/android/platform_frameworks_base/commit/9b5257c9c99c4cb541d8e8e78fb04f008b1a9091 To fix this, you could call MediaSessionLegacyHelper.getHelper() early in Application.onCreate() and pass it the application context.");
|
||||
}
|
||||
};
|
||||
int i14 = 22;
|
||||
TEXT_LINE__SCACHED = new AndroidExcludedRefs("TEXT_LINE__SCACHED", 3, Build.VERSION.SDK_INT <= 22) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.4
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.staticField("android.text.TextLine", "sCached").reason("TextLine.sCached is a pool of 3 TextLine instances. TextLine.recycle() has had at least two bugs that created memory leaks by not correctly clearing the recycled TextLine instances. The first was fixed in android-5.1.0_r1: https://github.com/android/platform_frameworks_base/commit/893d6fe48d37f71e683f722457bea646994a10 The second was fixed, not released yet: https://github.com/android/platform_frameworks_base/commit/b3a9bc038d3a218b1dbdf7b5668e3d6c12be5e To fix this, you could access TextLine.sCached and clear the pool every now and then (e.g. on activity destroy).");
|
||||
}
|
||||
};
|
||||
BLOCKING_QUEUE = new AndroidExcludedRefs("BLOCKING_QUEUE", 4) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.5
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.os.Message", "obj").reason("A thread waiting on a blocking queue will leak the last dequeued object as a stack local reference. So when a HandlerThread becomes idle, it keeps a local reference to the last message it received. That message then gets recycled and can be used again. As long as all messages are recycled after being used, this won't be a problem, because these references are cleared when being recycled. However, dialogs create template Message instances to be copied when a message needs to be sent. These Message templates holds references to the dialog listeners, which most likely leads to holding a reference onto the activity in some way. Dialogs never recycle their template Message, assuming these Message instances will get GCed when the dialog is GCed. The combination of these two things creates a high potential for memory leaks as soon as you use dialogs. These memory leaks might be temporary, but some handler threads sleep for a long time. To fix this, you could post empty messages to the idle handler threads from time to time. This won't be easy because you cannot access all handler threads, but a library that is widely used should consider doing this for its own handler threads. This leaks has been shown to happen in both Dalvik and ART.");
|
||||
builder.instanceField("android.os.Message", "next").reason("A thread waiting on a blocking queue will leak the last dequeued object as a stack local reference. So when a HandlerThread becomes idle, it keeps a local reference to the last message it received. That message then gets recycled and can be used again. As long as all messages are recycled after being used, this won't be a problem, because these references are cleared when being recycled. However, dialogs create template Message instances to be copied when a message needs to be sent. These Message templates holds references to the dialog listeners, which most likely leads to holding a reference onto the activity in some way. Dialogs never recycle their template Message, assuming these Message instances will get GCed when the dialog is GCed. The combination of these two things creates a high potential for memory leaks as soon as you use dialogs. These memory leaks might be temporary, but some handler threads sleep for a long time. To fix this, you could post empty messages to the idle handler threads from time to time. This won't be easy because you cannot access all handler threads, but a library that is widely used should consider doing this for its own handler threads. This leaks has been shown to happen in both Dalvik and ART.");
|
||||
builder.instanceField("android.os.Message", "target").reason("A thread waiting on a blocking queue will leak the last dequeued object as a stack local reference. So when a HandlerThread becomes idle, it keeps a local reference to the last message it received. That message then gets recycled and can be used again. As long as all messages are recycled after being used, this won't be a problem, because these references are cleared when being recycled. However, dialogs create template Message instances to be copied when a message needs to be sent. These Message templates holds references to the dialog listeners, which most likely leads to holding a reference onto the activity in some way. Dialogs never recycle their template Message, assuming these Message instances will get GCed when the dialog is GCed. The combination of these two things creates a high potential for memory leaks as soon as you use dialogs. These memory leaks might be temporary, but some handler threads sleep for a long time. To fix this, you could post empty messages to the idle handler threads from time to time. This won't be easy because you cannot access all handler threads, but a library that is widely used should consider doing this for its own handler threads. This leaks has been shown to happen in both Dalvik and ART.");
|
||||
}
|
||||
};
|
||||
int i15 = 5;
|
||||
int i16 = Build.VERSION.SDK_INT;
|
||||
int i17 = 15;
|
||||
int i18 = 27;
|
||||
INPUT_METHOD_MANAGER__SERVED_VIEW = new AndroidExcludedRefs("INPUT_METHOD_MANAGER__SERVED_VIEW", i15, i16 >= 15 && i16 <= 27) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.6
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.view.inputmethod.InputMethodManager", "mNextServedView").reason("When we detach a view that receives keyboard input, the InputMethodManager leaks a reference to it until a new view asks for keyboard input. Tracked here: https://code.google.com/p/android/issues/detail?id=171190 Hack: https://gist.github.com/pyricau/4df64341cc978a7de414");
|
||||
builder.instanceField("android.view.inputmethod.InputMethodManager", "mServedView").reason("When we detach a view that receives keyboard input, the InputMethodManager leaks a reference to it until a new view asks for keyboard input. Tracked here: https://code.google.com/p/android/issues/detail?id=171190 Hack: https://gist.github.com/pyricau/4df64341cc978a7de414");
|
||||
builder.instanceField("android.view.inputmethod.InputMethodManager", "mServedInputConnection").reason("When we detach a view that receives keyboard input, the InputMethodManager leaks a reference to it until a new view asks for keyboard input. Tracked here: https://code.google.com/p/android/issues/detail?id=171190 Hack: https://gist.github.com/pyricau/4df64341cc978a7de414");
|
||||
}
|
||||
};
|
||||
int i19 = 6;
|
||||
int i20 = Build.VERSION.SDK_INT;
|
||||
INPUT_METHOD_MANAGER__ROOT_VIEW = new AndroidExcludedRefs("INPUT_METHOD_MANAGER__ROOT_VIEW", i19, i20 >= 15 && i20 <= 27) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.7
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.view.inputmethod.InputMethodManager", "mCurRootView").reason("The singleton InputMethodManager is holding a reference to mCurRootView long after the activity has been destroyed. Observed on ICS MR1: https://github.com/square/leakcanary/issues/1#issuecomment-100579429 Hack: https://gist.github.com/pyricau/4df64341cc978a7de414");
|
||||
}
|
||||
};
|
||||
int i21 = 7;
|
||||
int i22 = Build.VERSION.SDK_INT;
|
||||
int i23 = 14;
|
||||
LAYOUT_TRANSITION = new AndroidExcludedRefs("LAYOUT_TRANSITION", i21, i22 >= 14 && i22 <= 22) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.8
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.animation.LayoutTransition$1", "val$parent").reason("LayoutTransition leaks parent ViewGroup through ViewTreeObserver.OnPreDrawListener When triggered, this leaks stays until the window is destroyed. Tracked here: https://code.google.com/p/android/issues/detail?id=171830");
|
||||
}
|
||||
};
|
||||
int i24 = 8;
|
||||
int i25 = Build.VERSION.SDK_INT;
|
||||
int i26 = 16;
|
||||
int i27 = 24;
|
||||
SPELL_CHECKER_SESSION = new AndroidExcludedRefs("SPELL_CHECKER_SESSION", i24, i25 >= 16 && i25 <= 24) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.9
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.view.textservice.SpellCheckerSession$1", "this$0").reason("SpellCheckerSessionListenerImpl.mHandler is leaking destroyed Activity when the SpellCheckerSession is closed before the service is connected. Tracked here: https://code.google.com/p/android/issues/detail?id=172542");
|
||||
}
|
||||
};
|
||||
SPELL_CHECKER = new AndroidExcludedRefs("SPELL_CHECKER", 9, Build.VERSION.SDK_INT == 22) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.10
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.widget.SpellChecker$1", "this$0").reason("SpellChecker holds on to a detached view that points to a destroyed activity. mSpellRunnable is being enqueued, and that callback should be removed when closeSession() is called. Maybe closeSession() wasn't called, or maybe it was called after the view was detached.");
|
||||
}
|
||||
};
|
||||
int i28 = 10;
|
||||
int i29 = Build.VERSION.SDK_INT;
|
||||
ACTIVITY_CHOOSE_MODEL = new AndroidExcludedRefs("ACTIVITY_CHOOSE_MODEL", i28, i29 > 14 && i29 <= 22) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.11
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("androidx.appcompat.internal.widget.ActivityChooserModel", "mActivityChoserModelPolicy").reason("ActivityChooserModel holds a static reference to the last set ActivityChooserModelPolicy which can be an activity context. Tracked here: https://code.google.com/p/android/issues/detail?id=172659 Hack: https://gist.github.com/andaag/b05ab66ed0f06167d6e0");
|
||||
builder.instanceField("android.widget.ActivityChooserModel", "mActivityChoserModelPolicy").reason("ActivityChooserModel holds a static reference to the last set ActivityChooserModelPolicy which can be an activity context. Tracked here: https://code.google.com/p/android/issues/detail?id=172659 Hack: https://gist.github.com/andaag/b05ab66ed0f06167d6e0");
|
||||
}
|
||||
};
|
||||
SPEECH_RECOGNIZER = new AndroidExcludedRefs("SPEECH_RECOGNIZER", 11, Build.VERSION.SDK_INT < 21) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.12
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.speech.SpeechRecognizer$InternalListener", "this$0").reason("Prior to Android 5, SpeechRecognizer.InternalListener was a non static inner class and leaked the SpeechRecognizer which leaked an activity context. Fixed in AOSP: https://github.com/android/platform_frameworks_base/commit /b37866db469e81aca534ff6186bdafd44352329b");
|
||||
}
|
||||
};
|
||||
ACCOUNT_MANAGER = new AndroidExcludedRefs("ACCOUNT_MANAGER", 12, Build.VERSION.SDK_INT <= 27) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.13
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.accounts.AccountManager$AmsTask$Response", "this$1").reason("AccountManager$AmsTask$Response is a stub and is held in memory by native code, probably because the reference to the response in the other process hasn't been cleared. AccountManager$AmsTask is holding on to the activity reference to use for launching a new sub- Activity. Tracked here: https://code.google.com/p/android/issues/detail?id=173689 Fix: Pass a null activity reference to the AccountManager methods and then deal with the returned future to to get the result and correctly start an activity when it's available.");
|
||||
}
|
||||
};
|
||||
MEDIA_SCANNER_CONNECTION = new AndroidExcludedRefs("MEDIA_SCANNER_CONNECTION", 13, Build.VERSION.SDK_INT <= 22) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.14
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.media.MediaScannerConnection", "mContext").reason("The static method MediaScannerConnection.scanFile() takes an activity context but the service might not disconnect after the activity has been destroyed. Tracked here: https://code.google.com/p/android/issues/detail?id=173788 Fix: Create an instance of MediaScannerConnection yourself and pass in the application context. Call connect() and disconnect() manually.");
|
||||
}
|
||||
};
|
||||
int i30 = Build.VERSION.SDK_INT;
|
||||
int i31 = 18;
|
||||
int i32 = 26;
|
||||
USER_MANAGER__SINSTANCE = new AndroidExcludedRefs("USER_MANAGER__SINSTANCE", i23, i30 >= 18 && i30 < 26) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.15
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.os.UserManager", "mContext").reason("UserManager has a static sInstance field that creates an instance and caches it the first time UserManager.get() is called. This instance is created with the outer context (which is an activity base context). Tracked here: https://code.google.com/p/android/issues/detail?id=173789 Introduced by: https://github.com/android/platform_frameworks_base/commit/27db46850b708070452c0ce49daf5f79503fbde6 Fix: trigger a call to UserManager.get() in Application.onCreate(), so that the UserManager instance gets cached with a reference to the application context.");
|
||||
}
|
||||
};
|
||||
APP_WIDGET_HOST_CALLBACKS = new AndroidExcludedRefs("APP_WIDGET_HOST_CALLBACKS", i17, Build.VERSION.SDK_INT < 22) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.16
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.appwidget.AppWidgetHost$Callbacks", "this$0").reason("android.appwidget.AppWidgetHost$Callbacks is a stub and is held in memory native code. The reference to the `mContext` was not being cleared, which caused the Callbacks instance to retain this reference Fixed in AOSP: https://github.com/android/platform_frameworks_base/commit/7a96f3c917e0001ee739b65da37b2fadec7d7765");
|
||||
}
|
||||
};
|
||||
AUDIO_MANAGER = new AndroidExcludedRefs("AUDIO_MANAGER", i26, Build.VERSION.SDK_INT <= 22) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.17
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.media.AudioManager$1", "this$0").reason("Prior to Android M, VideoView required audio focus from AudioManager and never abandoned it, which leaks the Activity context through the AudioManager. The root of the problem is that AudioManager uses whichever context it receives, which in the case of the VideoView example is an Activity, even though it only needs the application's context. The issue is fixed in Android M, and the AudioManager now uses the application's context. Tracked here: https://code.google.com/p/android/issues/detail?id=152173 Fix: https://gist.github.com/jankovd/891d96f476f7a9ce24e2");
|
||||
}
|
||||
};
|
||||
EDITTEXT_BLINK_MESSAGEQUEUE = new AndroidExcludedRefs("EDITTEXT_BLINK_MESSAGEQUEUE", 17, Build.VERSION.SDK_INT <= 22) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.18
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.widget.Editor$Blink", "this$0").reason("The EditText Blink of the Cursor is implemented using a callback and Messages, which trigger the display of the Cursor. If an AlertDialog or DialogFragment that contains a blinking cursor is detached, a message is posted with a delay after the dialog has been closed and as a result leaks the Activity. This can be fixed manually by calling TextView.setCursorVisible(false) in the dismiss() method of the dialog. Tracked here: https://code.google.com/p/android/issues/detail?id=188551 Fixed in AOSP: https://android.googlesource.com/platform/frameworks/base/+/5b734f2430e9f26c769d6af8ea5645e390fcf5af%5E%21/");
|
||||
}
|
||||
};
|
||||
int i33 = 23;
|
||||
CONNECTIVITY_MANAGER__SINSTANCE = new AndroidExcludedRefs("CONNECTIVITY_MANAGER__SINSTANCE", i31, Build.VERSION.SDK_INT <= 23) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.19
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.net.ConnectivityManager", "sInstance").reason("ConnectivityManager has a sInstance field that is set when the first ConnectivityManager instance is created. ConnectivityManager has a mContext field. When calling activity.getSystemService(Context.CONNECTIVITY_SERVICE) , the first ConnectivityManager instance is created with the activity context and stored in sInstance. That activity context then leaks forever. Until this is fixed, app developers can prevent this leak by making sure the ConnectivityManager is first created with an App Context. E.g. in some static init do: context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE) Tracked here: https://code.google.com/p/android/issues/detail?id=198852 Introduced here: https://github.com/android/platform_frameworks_base/commit/e0bef71662d81caaaa0d7214fb0bef5d39996a69");
|
||||
}
|
||||
};
|
||||
int i34 = Build.VERSION.SDK_INT;
|
||||
ACCESSIBILITY_NODE_INFO__MORIGINALTEXT = new AndroidExcludedRefs("ACCESSIBILITY_NODE_INFO__MORIGINALTEXT", i12, i34 >= 26 && i34 <= 27) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.20
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.view.accessibility.AccessibilityNodeInfo", "mOriginalText").reason("AccessibilityNodeInfo has a static sPool of AccessibilityNodeInfo. When AccessibilityNodeInfo instances are released back in the pool, AccessibilityNodeInfo.clear() does not clear the mOriginalText field, which causes spans to leak which in turns causes TextView.ChangeWatcher to leak and the whole view hierarchy. Introduced here: https://android.googlesource.com/platform/frameworks/base/+/193520e3dff5248ddcf8435203bf99d2ba667219%5E%21/core/java/android/view/accessibility/AccessibilityNodeInfo.java");
|
||||
}
|
||||
};
|
||||
int i35 = 20;
|
||||
int i36 = Build.VERSION.SDK_INT;
|
||||
BACKDROP_FRAME_RENDERER__MDECORVIEW = new AndroidExcludedRefs("BACKDROP_FRAME_RENDERER__MDECORVIEW", i35, i36 >= 24 && i36 <= 26) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.21
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("com.android.internal.policy.BackdropFrameRenderer", "mDecorView").reason("When BackdropFrameRenderer.releaseRenderer() is called, there's an unknown case where mRenderer becomes null but mChoreographer doesn't and the thread doesn't stop and ends up leaking mDecorView which itself holds on to a destroyed activity");
|
||||
}
|
||||
};
|
||||
INSTRUMENTATION_RECOMMEND_ACTIVITY = new AndroidExcludedRefs("INSTRUMENTATION_RECOMMEND_ACTIVITY", i11, LeakCanaryInternals.MEIZU.equals(Build.MANUFACTURER) && (i9 = Build.VERSION.SDK_INT) >= 21 && i9 <= 22) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.22
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.staticField("android.app.Instrumentation", "mRecommendActivity").reason("Instrumentation would leak com.android.internal.app.RecommendActivity (in framework.jar) in Meizu FlymeOS 4.5 and above, which is based on Android 5.0 and above");
|
||||
}
|
||||
};
|
||||
DEVICE_POLICY_MANAGER__SETTINGS_OBSERVER = new AndroidExcludedRefs("DEVICE_POLICY_MANAGER__SETTINGS_OBSERVER", i14, LeakCanaryInternals.MOTOROLA.equals(Build.MANUFACTURER) && (i8 = Build.VERSION.SDK_INT) >= 19 && i8 <= 22) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.23
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
if (LeakCanaryInternals.MOTOROLA.equals(Build.MANUFACTURER) && Build.VERSION.SDK_INT == 19) {
|
||||
builder.instanceField("android.app.admin.DevicePolicyManager$SettingsObserver", "this$0").reason("DevicePolicyManager keeps a reference to the context it has been created with instead of extracting the application context. In this Motorola build, DevicePolicyManager has an inner SettingsObserver class that is a content observer, which is held into memory by a binder transport object.");
|
||||
}
|
||||
}
|
||||
};
|
||||
SPEN_GESTURE_MANAGER = new AndroidExcludedRefs("SPEN_GESTURE_MANAGER", i33, "samsung".equals(Build.MANUFACTURER) && Build.VERSION.SDK_INT == 19) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.24
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.staticField("com.samsung.android.smartclip.SpenGestureManager", "mContext").reason("SpenGestureManager has a static mContext field that leaks a reference to the activity. Yes, a STATIC mContext field.");
|
||||
}
|
||||
};
|
||||
int i37 = 25;
|
||||
GESTURE_BOOST_MANAGER = new AndroidExcludedRefs("GESTURE_BOOST_MANAGER", i27, LeakCanaryInternals.HUAWEI.equals(Build.MANUFACTURER) && (i7 = Build.VERSION.SDK_INT) >= 24 && i7 <= 25) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.25
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.staticField("android.gestureboost.GestureBoostManager", "mContext").reason("GestureBoostManager is a static singleton that leaks an activity context. Fix: https://github.com/square/leakcanary/issues/696#issuecomment-296420756");
|
||||
}
|
||||
};
|
||||
INPUT_METHOD_MANAGER__LAST_SERVED_VIEW = new AndroidExcludedRefs("INPUT_METHOD_MANAGER__LAST_SERVED_VIEW", i37, LeakCanaryInternals.HUAWEI.equals(Build.MANUFACTURER) && (i6 = Build.VERSION.SDK_INT) >= 23 && i6 <= 27) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.26
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.view.inputmethod.InputMethodManager", "mLastSrvView").reason("HUAWEI added a mLastSrvView field to InputMethodManager that leaks a reference to the last served view.");
|
||||
}
|
||||
};
|
||||
CLIPBOARD_UI_MANAGER__SINSTANCE = new AndroidExcludedRefs("CLIPBOARD_UI_MANAGER__SINSTANCE", i32, "samsung".equals(Build.MANUFACTURER) && (i5 = Build.VERSION.SDK_INT) >= 19 && i5 <= 21) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.27
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.sec.clipboard.ClipboardUIManager", "mContext").reason("ClipboardUIManager is a static singleton that leaks an activity context. Fix: trigger a call to ClipboardUIManager.getInstance() in Application.onCreate() , so that the ClipboardUIManager instance gets cached with a reference to the application context. Example: https://gist.github.com/cypressious/91c4fb1455470d803a602838dfcd5774");
|
||||
}
|
||||
};
|
||||
SEM_CLIPBOARD_MANAGER__MCONTEXT = new AndroidExcludedRefs("SEM_CLIPBOARD_MANAGER__MCONTEXT", i18, "samsung".equals(Build.MANUFACTURER) && (i4 = Build.VERSION.SDK_INT) >= 19 && i4 <= 24) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.28
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("com.samsung.android.content.clipboard.SemClipboardManager", "mContext").reason("SemClipboardManager is held in memory by an anonymous inner class implementation of android.os.Binder, thereby leaking an activity context.");
|
||||
}
|
||||
};
|
||||
SEM_EMERGENCY_MANAGER__MCONTEXT = new AndroidExcludedRefs("SEM_EMERGENCY_MANAGER__MCONTEXT", 28, "samsung".equals(Build.MANUFACTURER) && (i3 = Build.VERSION.SDK_INT) >= 19 && i3 <= 24) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.29
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("com.samsung.android.emergencymode.SemEmergencyManager", "mContext").reason("SemEmergencyManager is a static singleton that leaks a DecorContext. Fix: https://gist.github.com/jankovd/a210460b814c04d500eb12025902d60d");
|
||||
}
|
||||
};
|
||||
BUBBLE_POPUP_HELPER__SHELPER = new AndroidExcludedRefs("BUBBLE_POPUP_HELPER__SHELPER", 29, LeakCanaryInternals.LG.equals(Build.MANUFACTURER) && (i2 = Build.VERSION.SDK_INT) >= 19 && i2 <= 21) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.30
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.staticField("android.widget.BubblePopupHelper", "sHelper").reason("A static helper for EditText bubble popups leaks a reference to the latest focused view.");
|
||||
}
|
||||
};
|
||||
LGCONTEXT__MCONTEXT = new AndroidExcludedRefs("LGCONTEXT__MCONTEXT", 30, LeakCanaryInternals.LG.equals(Build.MANUFACTURER) && Build.VERSION.SDK_INT == 21) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.31
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("com.lge.systemservice.core.LGContext", "mContext").reason("LGContext is a static singleton that leaks an activity context.");
|
||||
}
|
||||
};
|
||||
AW_RESOURCE__SRESOURCES = new AndroidExcludedRefs("AW_RESOURCE__SRESOURCES", 31, "samsung".equals(Build.MANUFACTURER) && Build.VERSION.SDK_INT == 19) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.32
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.staticField("com.android.org.chromium.android_webview.AwResource", "sResources");
|
||||
}
|
||||
};
|
||||
MAPPER_CLIENT = new AndroidExcludedRefs("MAPPER_CLIENT", 32, LeakCanaryInternals.NVIDIA.equals(Build.MANUFACTURER) && Build.VERSION.SDK_INT == 19) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.33
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("com.nvidia.ControllerMapper.MapperClient$ServiceClient", "this$0").reason("Not sure exactly what ControllerMapper is about, but there is an anonymous Handler in ControllerMapper.MapperClient.ServiceClient, which leaks ControllerMapper.MapperClient which leaks the activity context.");
|
||||
}
|
||||
};
|
||||
TEXT_VIEW__MLAST_HOVERED_VIEW = new AndroidExcludedRefs("TEXT_VIEW__MLAST_HOVERED_VIEW", 33, "samsung".equals(Build.MANUFACTURER) && (i = Build.VERSION.SDK_INT) >= 19 && i <= 26) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.34
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.staticField("android.widget.TextView", "mLastHoveredView").reason("mLastHoveredView is a static field in TextView that leaks the last hovered view.");
|
||||
}
|
||||
};
|
||||
PERSONA_MANAGER = new AndroidExcludedRefs("PERSONA_MANAGER", 34, "samsung".equals(Build.MANUFACTURER) && Build.VERSION.SDK_INT == 19) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.35
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.os.PersonaManager", "mContext").reason("android.app.LoadedApk.mResources has a reference to android.content.res.Resources.mPersonaManager which has a reference to android.os.PersonaManager.mContext which is an activity.");
|
||||
}
|
||||
};
|
||||
RESOURCES__MCONTEXT = new AndroidExcludedRefs("RESOURCES__MCONTEXT", 35, "samsung".equals(Build.MANUFACTURER) && Build.VERSION.SDK_INT == 19) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.36
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.content.res.Resources", "mContext").reason("In AOSP the Resources class does not have a context. Here we have ZygoteInit.mResources (static field) holding on to a Resources instance that has a context that is the activity. Observed here: https://github.com/square/leakcanary/issues/1#issue-74450184");
|
||||
}
|
||||
};
|
||||
VIEW_CONFIGURATION__MCONTEXT = new AndroidExcludedRefs("VIEW_CONFIGURATION__MCONTEXT", 36, "samsung".equals(Build.MANUFACTURER) && Build.VERSION.SDK_INT == 19) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.37
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.view.ViewConfiguration", "mContext").reason("In AOSP the ViewConfiguration class does not have a context. Here we have ViewConfiguration.sConfigurations (static field) holding on to a ViewConfiguration instance that has a context that is the activity. Observed here: https://github.com/square/leakcanary/issues/1#issuecomment-100324683");
|
||||
}
|
||||
};
|
||||
SYSTEM_SENSOR_MANAGER__MAPPCONTEXTIMPL = new AndroidExcludedRefs("SYSTEM_SENSOR_MANAGER__MAPPCONTEXTIMPL", 37, (LeakCanaryInternals.LENOVO.equals(Build.MANUFACTURER) && Build.VERSION.SDK_INT == 19) || ("vivo".equals(Build.MANUFACTURER) && Build.VERSION.SDK_INT == 22)) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.38
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.staticField("android.hardware.SystemSensorManager", "mAppContextImpl").reason("SystemSensorManager stores a reference to context in a static field in its constructor. Fix: use application context to get SensorManager");
|
||||
}
|
||||
};
|
||||
AUDIO_MANAGER__MCONTEXT_STATIC = new AndroidExcludedRefs("AUDIO_MANAGER__MCONTEXT_STATIC", 38, "samsung".equals(Build.MANUFACTURER) && Build.VERSION.SDK_INT == 19) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.39
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.staticField("android.media.AudioManager", "mContext_static").reason("Samsung added a static mContext_static field to AudioManager, holds a reference to the activity. Observed here: https://github.com/square/leakcanary/issues/32");
|
||||
}
|
||||
};
|
||||
ACTIVITY_MANAGER_MCONTEXT = new AndroidExcludedRefs("ACTIVITY_MANAGER_MCONTEXT", 39, "samsung".equals(Build.MANUFACTURER) && Build.VERSION.SDK_INT == 22) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.40
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.staticField("android.app.ActivityManager", "mContext").reason("Samsung added a static mContext field to ActivityManager, holds a reference to the activity. Observed here: https://github.com/square/leakcanary/issues/177 Fix in comment: https://github.com/square/leakcanary/issues/177#issuecomment-222724283");
|
||||
}
|
||||
};
|
||||
SOFT_REFERENCES = new AndroidExcludedRefs("SOFT_REFERENCES", 40) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.41
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.clazz(WeakReference.class.getName()).alwaysExclude();
|
||||
builder.clazz(SoftReference.class.getName()).alwaysExclude();
|
||||
builder.clazz(PhantomReference.class.getName()).alwaysExclude();
|
||||
builder.clazz("java.lang.ref.Finalizer").alwaysExclude();
|
||||
builder.clazz("java.lang.ref.FinalizerReference").alwaysExclude();
|
||||
}
|
||||
};
|
||||
FINALIZER_WATCHDOG_DAEMON = new AndroidExcludedRefs("FINALIZER_WATCHDOG_DAEMON", 41) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.42
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.thread("FinalizerWatchdogDaemon").alwaysExclude();
|
||||
}
|
||||
};
|
||||
MAIN = new AndroidExcludedRefs("MAIN", 42) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.43
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.thread("main").alwaysExclude();
|
||||
}
|
||||
};
|
||||
LEAK_CANARY_THREAD = new AndroidExcludedRefs("LEAK_CANARY_THREAD", 43) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.44
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.thread("LeakCanary-Heap-Dump").alwaysExclude();
|
||||
}
|
||||
};
|
||||
EVENT_RECEIVER__MMESSAGE_QUEUE = new AndroidExcludedRefs("EVENT_RECEIVER__MMESSAGE_QUEUE", 44) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.45
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.view.Choreographer$FrameDisplayEventReceiver", "mMessageQueue").alwaysExclude();
|
||||
}
|
||||
};
|
||||
VIEWLOCATIONHOLDER_ROOT = new AndroidExcludedRefs("VIEWLOCATIONHOLDER_ROOT", 45, Build.VERSION.SDK_INT == 28) { // from class: com.squareup.leakcanary.AndroidExcludedRefs.46
|
||||
@Override // com.squareup.leakcanary.AndroidExcludedRefs
|
||||
void add(ExcludedRefs.Builder builder) {
|
||||
builder.instanceField("android.view.ViewGroup$ViewLocationHolder", "mRoot");
|
||||
}
|
||||
};
|
||||
$VALUES = new AndroidExcludedRefs[]{ACTIVITY_CLIENT_RECORD__NEXT_IDLE, SPAN_CONTROLLER, MEDIA_SESSION_LEGACY_HELPER__SINSTANCE, TEXT_LINE__SCACHED, BLOCKING_QUEUE, INPUT_METHOD_MANAGER__SERVED_VIEW, INPUT_METHOD_MANAGER__ROOT_VIEW, LAYOUT_TRANSITION, SPELL_CHECKER_SESSION, SPELL_CHECKER, ACTIVITY_CHOOSE_MODEL, SPEECH_RECOGNIZER, ACCOUNT_MANAGER, MEDIA_SCANNER_CONNECTION, USER_MANAGER__SINSTANCE, APP_WIDGET_HOST_CALLBACKS, AUDIO_MANAGER, EDITTEXT_BLINK_MESSAGEQUEUE, CONNECTIVITY_MANAGER__SINSTANCE, ACCESSIBILITY_NODE_INFO__MORIGINALTEXT, BACKDROP_FRAME_RENDERER__MDECORVIEW, INSTRUMENTATION_RECOMMEND_ACTIVITY, DEVICE_POLICY_MANAGER__SETTINGS_OBSERVER, SPEN_GESTURE_MANAGER, GESTURE_BOOST_MANAGER, INPUT_METHOD_MANAGER__LAST_SERVED_VIEW, CLIPBOARD_UI_MANAGER__SINSTANCE, SEM_CLIPBOARD_MANAGER__MCONTEXT, SEM_EMERGENCY_MANAGER__MCONTEXT, BUBBLE_POPUP_HELPER__SHELPER, LGCONTEXT__MCONTEXT, AW_RESOURCE__SRESOURCES, MAPPER_CLIENT, TEXT_VIEW__MLAST_HOVERED_VIEW, PERSONA_MANAGER, RESOURCES__MCONTEXT, VIEW_CONFIGURATION__MCONTEXT, SYSTEM_SENSOR_MANAGER__MAPPCONTEXTIMPL, AUDIO_MANAGER__MCONTEXT_STATIC, ACTIVITY_MANAGER_MCONTEXT, SOFT_REFERENCES, FINALIZER_WATCHDOG_DAEMON, MAIN, LEAK_CANARY_THREAD, EVENT_RECEIVER__MMESSAGE_QUEUE, VIEWLOCATIONHOLDER_ROOT};
|
||||
}
|
||||
|
||||
public static ExcludedRefs.Builder createAndroidDefaults() {
|
||||
return createBuilder(EnumSet.of(SOFT_REFERENCES, FINALIZER_WATCHDOG_DAEMON, MAIN, LEAK_CANARY_THREAD, EVENT_RECEIVER__MMESSAGE_QUEUE));
|
||||
}
|
||||
|
||||
public static ExcludedRefs.Builder createAppDefaults() {
|
||||
return createBuilder(EnumSet.allOf(AndroidExcludedRefs.class));
|
||||
}
|
||||
|
||||
public static ExcludedRefs.Builder createBuilder(EnumSet<AndroidExcludedRefs> enumSet) {
|
||||
ExcludedRefs.Builder builder = ExcludedRefs.builder();
|
||||
Iterator it = enumSet.iterator();
|
||||
while (it.hasNext()) {
|
||||
AndroidExcludedRefs androidExcludedRefs = (AndroidExcludedRefs) it.next();
|
||||
if (androidExcludedRefs.applies) {
|
||||
androidExcludedRefs.add(builder);
|
||||
((ExcludedRefs.BuilderWithParams) builder).named(androidExcludedRefs.name());
|
||||
}
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static AndroidExcludedRefs valueOf(String str) {
|
||||
return (AndroidExcludedRefs) Enum.valueOf(AndroidExcludedRefs.class, str);
|
||||
}
|
||||
|
||||
public static AndroidExcludedRefs[] values() {
|
||||
return (AndroidExcludedRefs[]) $VALUES.clone();
|
||||
}
|
||||
|
||||
abstract void add(ExcludedRefs.Builder builder);
|
||||
|
||||
private AndroidExcludedRefs(String str, int i) {
|
||||
this(str, i, true);
|
||||
}
|
||||
|
||||
private AndroidExcludedRefs(String str, int i, boolean z) {
|
||||
this.applies = z;
|
||||
}
|
||||
}
|
111
sources/com/squareup/leakcanary/AndroidHeapDumper.java
Normal file
111
sources/com/squareup/leakcanary/AndroidHeapDumper.java
Normal file
@@ -0,0 +1,111 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.os.Debug;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.MessageQueue;
|
||||
import android.os.SystemClock;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
import com.squareup.leakcanary.internal.ActivityLifecycleCallbacksAdapter;
|
||||
import com.squareup.leakcanary.internal.FutureResult;
|
||||
import com.squareup.leakcanary.internal.LeakCanaryInternals;
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class AndroidHeapDumper implements HeapDumper {
|
||||
private final Context context;
|
||||
private final LeakDirectoryProvider leakDirectoryProvider;
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
private Activity resumedActivity;
|
||||
|
||||
public AndroidHeapDumper(Context context, LeakDirectoryProvider leakDirectoryProvider) {
|
||||
this.leakDirectoryProvider = leakDirectoryProvider;
|
||||
this.context = context.getApplicationContext();
|
||||
((Application) context.getApplicationContext()).registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacksAdapter() { // from class: com.squareup.leakcanary.AndroidHeapDumper.1
|
||||
@Override // com.squareup.leakcanary.internal.ActivityLifecycleCallbacksAdapter, android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityPaused(Activity activity) {
|
||||
if (AndroidHeapDumper.this.resumedActivity == activity) {
|
||||
AndroidHeapDumper.this.resumedActivity = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.internal.ActivityLifecycleCallbacksAdapter, android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityResumed(Activity activity) {
|
||||
AndroidHeapDumper.this.resumedActivity = activity;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void cancelToast(final Toast toast) {
|
||||
if (toast == null) {
|
||||
return;
|
||||
}
|
||||
this.mainHandler.post(new Runnable() { // from class: com.squareup.leakcanary.AndroidHeapDumper.3
|
||||
@Override // java.lang.Runnable
|
||||
public void run() {
|
||||
toast.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showToast(final FutureResult<Toast> futureResult) {
|
||||
this.mainHandler.post(new Runnable() { // from class: com.squareup.leakcanary.AndroidHeapDumper.2
|
||||
@Override // java.lang.Runnable
|
||||
public void run() {
|
||||
if (AndroidHeapDumper.this.resumedActivity == null) {
|
||||
futureResult.set(null);
|
||||
return;
|
||||
}
|
||||
final Toast toast = new Toast(AndroidHeapDumper.this.resumedActivity);
|
||||
toast.setGravity(16, 0, 0);
|
||||
toast.setDuration(1);
|
||||
toast.setView(LayoutInflater.from(AndroidHeapDumper.this.resumedActivity).inflate(R.layout.leak_canary_heap_dump_toast, (ViewGroup) null));
|
||||
toast.show();
|
||||
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() { // from class: com.squareup.leakcanary.AndroidHeapDumper.2.1
|
||||
@Override // android.os.MessageQueue.IdleHandler
|
||||
public boolean queueIdle() {
|
||||
futureResult.set(toast);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.HeapDumper
|
||||
public File dumpHeap() {
|
||||
File newHeapDumpFile = this.leakDirectoryProvider.newHeapDumpFile();
|
||||
File file = HeapDumper.RETRY_LATER;
|
||||
if (newHeapDumpFile == file) {
|
||||
return file;
|
||||
}
|
||||
FutureResult<Toast> futureResult = new FutureResult<>();
|
||||
showToast(futureResult);
|
||||
if (!futureResult.wait(5L, TimeUnit.SECONDS)) {
|
||||
CanaryLog.d("Did not dump heap, too much time waiting for Toast.", new Object[0]);
|
||||
return HeapDumper.RETRY_LATER;
|
||||
}
|
||||
Notification buildNotification = LeakCanaryInternals.buildNotification(this.context, new Notification.Builder(this.context).setContentTitle(this.context.getString(R.string.leak_canary_notification_dumping)));
|
||||
NotificationManager notificationManager = (NotificationManager) this.context.getSystemService("notification");
|
||||
int uptimeMillis = (int) SystemClock.uptimeMillis();
|
||||
notificationManager.notify(uptimeMillis, buildNotification);
|
||||
Toast toast = futureResult.get();
|
||||
try {
|
||||
Debug.dumpHprofData(newHeapDumpFile.getAbsolutePath());
|
||||
cancelToast(toast);
|
||||
notificationManager.cancel(uptimeMillis);
|
||||
return newHeapDumpFile;
|
||||
} catch (Exception e) {
|
||||
CanaryLog.d(e, "Could not dump heap", new Object[0]);
|
||||
return HeapDumper.RETRY_LATER;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,146 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.app.Dialog;
|
||||
import android.app.Fragment;
|
||||
import android.os.MessageQueue;
|
||||
import android.view.View;
|
||||
import com.squareup.leakcanary.Reachability;
|
||||
import com.unity3d.ads.metadata.MediationMetaData;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public enum AndroidReachabilityInspectors {
|
||||
VIEW(ViewInspector.class),
|
||||
ACTIVITY(ActivityInspector.class),
|
||||
DIALOG(DialogInspector.class),
|
||||
APPLICATION(ApplicationInspector.class),
|
||||
FRAGMENT(FragmentInspector.class),
|
||||
SUPPORT_FRAGMENT(SupportFragmentInspector.class),
|
||||
MESSAGE_QUEUE(MessageQueueInspector.class),
|
||||
MORTAR_PRESENTER(MortarPresenterInspector.class),
|
||||
VIEW_ROOT_IMPL(ViewImplInspector.class),
|
||||
MAIN_THEAD(MainThreadInspector.class),
|
||||
WINDOW(WindowInspector.class);
|
||||
|
||||
private final Class<? extends Reachability.Inspector> inspectorClass;
|
||||
|
||||
public static class ActivityInspector implements Reachability.Inspector {
|
||||
@Override // com.squareup.leakcanary.Reachability.Inspector
|
||||
public Reachability expectedReachability(LeakTraceElement leakTraceElement) {
|
||||
if (!leakTraceElement.isInstanceOf(Activity.class)) {
|
||||
return Reachability.UNKNOWN;
|
||||
}
|
||||
String fieldReferenceValue = leakTraceElement.getFieldReferenceValue("mDestroyed");
|
||||
return fieldReferenceValue == null ? Reachability.UNKNOWN : fieldReferenceValue.equals("true") ? Reachability.UNREACHABLE : Reachability.REACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ApplicationInspector implements Reachability.Inspector {
|
||||
@Override // com.squareup.leakcanary.Reachability.Inspector
|
||||
public Reachability expectedReachability(LeakTraceElement leakTraceElement) {
|
||||
return leakTraceElement.isInstanceOf(Application.class) ? Reachability.REACHABLE : Reachability.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DialogInspector implements Reachability.Inspector {
|
||||
@Override // com.squareup.leakcanary.Reachability.Inspector
|
||||
public Reachability expectedReachability(LeakTraceElement leakTraceElement) {
|
||||
if (!leakTraceElement.isInstanceOf(Dialog.class)) {
|
||||
return Reachability.UNKNOWN;
|
||||
}
|
||||
String fieldReferenceValue = leakTraceElement.getFieldReferenceValue("mDecor");
|
||||
return fieldReferenceValue == null ? Reachability.UNKNOWN : fieldReferenceValue.equals("null") ? Reachability.UNREACHABLE : Reachability.REACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FragmentInspector implements Reachability.Inspector {
|
||||
@Override // com.squareup.leakcanary.Reachability.Inspector
|
||||
public Reachability expectedReachability(LeakTraceElement leakTraceElement) {
|
||||
if (!leakTraceElement.isInstanceOf(Fragment.class)) {
|
||||
return Reachability.UNKNOWN;
|
||||
}
|
||||
String fieldReferenceValue = leakTraceElement.getFieldReferenceValue("mDetached");
|
||||
return fieldReferenceValue == null ? Reachability.UNKNOWN : fieldReferenceValue.equals("true") ? Reachability.UNREACHABLE : Reachability.REACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MainThreadInspector implements Reachability.Inspector {
|
||||
@Override // com.squareup.leakcanary.Reachability.Inspector
|
||||
public Reachability expectedReachability(LeakTraceElement leakTraceElement) {
|
||||
return !leakTraceElement.isInstanceOf(Thread.class) ? Reachability.UNKNOWN : "main".equals(leakTraceElement.getFieldReferenceValue(MediationMetaData.KEY_NAME)) ? Reachability.REACHABLE : Reachability.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MessageQueueInspector implements Reachability.Inspector {
|
||||
@Override // com.squareup.leakcanary.Reachability.Inspector
|
||||
public Reachability expectedReachability(LeakTraceElement leakTraceElement) {
|
||||
return !leakTraceElement.isInstanceOf(MessageQueue.class) ? Reachability.UNKNOWN : "true".equals(leakTraceElement.getFieldReferenceValue("mQuitting")) ? Reachability.UNREACHABLE : Reachability.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MortarPresenterInspector implements Reachability.Inspector {
|
||||
@Override // com.squareup.leakcanary.Reachability.Inspector
|
||||
public Reachability expectedReachability(LeakTraceElement leakTraceElement) {
|
||||
return !leakTraceElement.isInstanceOf("mortar.Presenter") ? Reachability.UNKNOWN : "null".equals(leakTraceElement.getFieldReferenceValue("view")) ? Reachability.UNREACHABLE : Reachability.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SupportFragmentInspector implements Reachability.Inspector {
|
||||
@Override // com.squareup.leakcanary.Reachability.Inspector
|
||||
public Reachability expectedReachability(LeakTraceElement leakTraceElement) {
|
||||
if (!leakTraceElement.isInstanceOf("androidx.fragment.app.Fragment")) {
|
||||
return Reachability.UNKNOWN;
|
||||
}
|
||||
String fieldReferenceValue = leakTraceElement.getFieldReferenceValue("mDetached");
|
||||
return fieldReferenceValue == null ? Reachability.UNKNOWN : fieldReferenceValue.equals("true") ? Reachability.UNREACHABLE : Reachability.REACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ViewImplInspector implements Reachability.Inspector {
|
||||
@Override // com.squareup.leakcanary.Reachability.Inspector
|
||||
public Reachability expectedReachability(LeakTraceElement leakTraceElement) {
|
||||
if (!leakTraceElement.isInstanceOf("android.view.ViewRootImpl")) {
|
||||
return Reachability.UNKNOWN;
|
||||
}
|
||||
String fieldReferenceValue = leakTraceElement.getFieldReferenceValue("mView");
|
||||
return fieldReferenceValue == null ? Reachability.UNKNOWN : fieldReferenceValue.equals("null") ? Reachability.UNREACHABLE : Reachability.REACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ViewInspector implements Reachability.Inspector {
|
||||
@Override // com.squareup.leakcanary.Reachability.Inspector
|
||||
public Reachability expectedReachability(LeakTraceElement leakTraceElement) {
|
||||
if (!leakTraceElement.isInstanceOf(View.class)) {
|
||||
return Reachability.UNKNOWN;
|
||||
}
|
||||
String fieldReferenceValue = leakTraceElement.getFieldReferenceValue("mAttachInfo");
|
||||
return fieldReferenceValue == null ? Reachability.UNKNOWN : fieldReferenceValue.equals("null") ? Reachability.UNREACHABLE : Reachability.REACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
public static class WindowInspector implements Reachability.Inspector {
|
||||
@Override // com.squareup.leakcanary.Reachability.Inspector
|
||||
public Reachability expectedReachability(LeakTraceElement leakTraceElement) {
|
||||
if (!leakTraceElement.isInstanceOf("android.view.Window")) {
|
||||
return Reachability.UNKNOWN;
|
||||
}
|
||||
String fieldReferenceValue = leakTraceElement.getFieldReferenceValue("mDestroyed");
|
||||
return fieldReferenceValue == null ? Reachability.UNKNOWN : fieldReferenceValue.equals("true") ? Reachability.UNREACHABLE : Reachability.REACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
AndroidReachabilityInspectors(Class cls) {
|
||||
this.inspectorClass = cls;
|
||||
}
|
||||
|
||||
public static List<Class<? extends Reachability.Inspector>> defaultAndroidInspectors() {
|
||||
ArrayList arrayList = new ArrayList();
|
||||
for (AndroidReachabilityInspectors androidReachabilityInspectors : values()) {
|
||||
arrayList.add(androidReachabilityInspectors.inspectorClass);
|
||||
}
|
||||
return arrayList;
|
||||
}
|
||||
}
|
102
sources/com/squareup/leakcanary/AndroidRefWatcherBuilder.java
Normal file
102
sources/com/squareup/leakcanary/AndroidRefWatcherBuilder.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import android.content.Context;
|
||||
import com.squareup.leakcanary.HeapDump;
|
||||
import com.squareup.leakcanary.Reachability;
|
||||
import com.squareup.leakcanary.internal.DisplayLeakActivity;
|
||||
import com.squareup.leakcanary.internal.FragmentRefWatcher;
|
||||
import com.squareup.leakcanary.internal.LeakCanaryInternals;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {
|
||||
private static final long DEFAULT_WATCH_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(5);
|
||||
private final Context context;
|
||||
private boolean watchActivities = true;
|
||||
private boolean watchFragments = true;
|
||||
private boolean enableDisplayLeakActivity = false;
|
||||
|
||||
AndroidRefWatcherBuilder(Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
}
|
||||
|
||||
public RefWatcher buildAndInstall() {
|
||||
if (LeakCanaryInternals.installedRefWatcher != null) {
|
||||
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
|
||||
}
|
||||
RefWatcher build = build();
|
||||
if (build != RefWatcher.DISABLED) {
|
||||
if (this.enableDisplayLeakActivity) {
|
||||
LeakCanaryInternals.setEnabledAsync(this.context, DisplayLeakActivity.class, true);
|
||||
}
|
||||
if (this.watchActivities) {
|
||||
ActivityRefWatcher.install(this.context, build);
|
||||
}
|
||||
if (this.watchFragments) {
|
||||
FragmentRefWatcher.Helper.install(this.context, build);
|
||||
}
|
||||
}
|
||||
LeakCanaryInternals.installedRefWatcher = build;
|
||||
return build;
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.RefWatcherBuilder
|
||||
protected DebuggerControl defaultDebuggerControl() {
|
||||
return new AndroidDebuggerControl();
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.RefWatcherBuilder
|
||||
protected ExcludedRefs defaultExcludedRefs() {
|
||||
return AndroidExcludedRefs.createAppDefaults().build();
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.RefWatcherBuilder
|
||||
protected HeapDump.Listener defaultHeapDumpListener() {
|
||||
return new ServiceHeapDumpListener(this.context, DisplayLeakService.class);
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.RefWatcherBuilder
|
||||
protected HeapDumper defaultHeapDumper() {
|
||||
return new AndroidHeapDumper(this.context, LeakCanaryInternals.getLeakDirectoryProvider(this.context));
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.RefWatcherBuilder
|
||||
protected List<Class<? extends Reachability.Inspector>> defaultReachabilityInspectorClasses() {
|
||||
return AndroidReachabilityInspectors.defaultAndroidInspectors();
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.RefWatcherBuilder
|
||||
protected WatchExecutor defaultWatchExecutor() {
|
||||
return new AndroidWatchExecutor(DEFAULT_WATCH_DELAY_MILLIS);
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.RefWatcherBuilder
|
||||
protected boolean isDisabled() {
|
||||
return LeakCanary.isInAnalyzerProcess(this.context);
|
||||
}
|
||||
|
||||
public AndroidRefWatcherBuilder listenerServiceClass(Class<? extends AbstractAnalysisResultService> cls) {
|
||||
this.enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(cls);
|
||||
return heapDumpListener(new ServiceHeapDumpListener(this.context, cls));
|
||||
}
|
||||
|
||||
public AndroidRefWatcherBuilder maxStoredHeapDumps(int i) {
|
||||
LeakCanary.setLeakDirectoryProvider(new DefaultLeakDirectoryProvider(this.context, i));
|
||||
return self();
|
||||
}
|
||||
|
||||
public AndroidRefWatcherBuilder watchActivities(boolean z) {
|
||||
this.watchActivities = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AndroidRefWatcherBuilder watchDelay(long j, TimeUnit timeUnit) {
|
||||
return watchExecutor(new AndroidWatchExecutor(timeUnit.toMillis(j)));
|
||||
}
|
||||
|
||||
public AndroidRefWatcherBuilder watchFragments(boolean z) {
|
||||
this.watchFragments = z;
|
||||
return this;
|
||||
}
|
||||
}
|
66
sources/com/squareup/leakcanary/AndroidWatchExecutor.java
Normal file
66
sources/com/squareup/leakcanary/AndroidWatchExecutor.java
Normal file
@@ -0,0 +1,66 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.MessageQueue;
|
||||
import com.squareup.leakcanary.Retryable;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class AndroidWatchExecutor implements WatchExecutor {
|
||||
static final String LEAK_CANARY_THREAD_NAME = "LeakCanary-Heap-Dump";
|
||||
private final Handler backgroundHandler;
|
||||
private final long initialDelayMillis;
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
private final long maxBackoffFactor;
|
||||
|
||||
public AndroidWatchExecutor(long j) {
|
||||
HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
|
||||
handlerThread.start();
|
||||
this.backgroundHandler = new Handler(handlerThread.getLooper());
|
||||
this.initialDelayMillis = j;
|
||||
this.maxBackoffFactor = Long.MAX_VALUE / j;
|
||||
}
|
||||
|
||||
/* JADX INFO: Access modifiers changed from: private */
|
||||
public void postToBackgroundWithDelay(final Retryable retryable, final int i) {
|
||||
this.backgroundHandler.postDelayed(new Runnable() { // from class: com.squareup.leakcanary.AndroidWatchExecutor.3
|
||||
@Override // java.lang.Runnable
|
||||
public void run() {
|
||||
if (retryable.run() == Retryable.Result.RETRY) {
|
||||
AndroidWatchExecutor.this.postWaitForIdle(retryable, i + 1);
|
||||
}
|
||||
}
|
||||
}, this.initialDelayMillis * ((long) Math.min(Math.pow(2.0d, i), this.maxBackoffFactor)));
|
||||
}
|
||||
|
||||
/* JADX INFO: Access modifiers changed from: private */
|
||||
public void postWaitForIdle(final Retryable retryable, final int i) {
|
||||
this.mainHandler.post(new Runnable() { // from class: com.squareup.leakcanary.AndroidWatchExecutor.1
|
||||
@Override // java.lang.Runnable
|
||||
public void run() {
|
||||
AndroidWatchExecutor.this.waitForIdle(retryable, i);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* JADX INFO: Access modifiers changed from: private */
|
||||
public void waitForIdle(final Retryable retryable, final int i) {
|
||||
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() { // from class: com.squareup.leakcanary.AndroidWatchExecutor.2
|
||||
@Override // android.os.MessageQueue.IdleHandler
|
||||
public boolean queueIdle() {
|
||||
AndroidWatchExecutor.this.postToBackgroundWithDelay(retryable, i);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.WatchExecutor
|
||||
public void execute(Retryable retryable) {
|
||||
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
|
||||
waitForIdle(retryable, 0);
|
||||
} else {
|
||||
postWaitForIdle(retryable, 0);
|
||||
}
|
||||
}
|
||||
}
|
13
sources/com/squareup/leakcanary/BuildConfig.java
Normal file
13
sources/com/squareup/leakcanary/BuildConfig.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class BuildConfig {
|
||||
public static final String APPLICATION_ID = "com.squareup.leakcanary";
|
||||
public static final String BUILD_TYPE = "release";
|
||||
public static final boolean DEBUG = false;
|
||||
public static final String FLAVOR = "";
|
||||
public static final String GIT_SHA = "31007b4";
|
||||
public static final String LIBRARY_VERSION = "1.6.3";
|
||||
public static final int VERSION_CODE = -1;
|
||||
public static final String VERSION_NAME = "";
|
||||
}
|
60
sources/com/squareup/leakcanary/CanaryLog.java
Normal file
60
sources/com/squareup/leakcanary/CanaryLog.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class CanaryLog {
|
||||
private static volatile Logger logger = new DefaultLogger();
|
||||
|
||||
public interface Logger {
|
||||
void d(String str, Object... objArr);
|
||||
|
||||
void d(Throwable th, String str, Object... objArr);
|
||||
}
|
||||
|
||||
private CanaryLog() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static void d(String str, Object... objArr) {
|
||||
Logger logger2 = logger;
|
||||
if (logger2 == null) {
|
||||
return;
|
||||
}
|
||||
logger2.d(str, objArr);
|
||||
}
|
||||
|
||||
public static void setLogger(Logger logger2) {
|
||||
logger = logger2;
|
||||
}
|
||||
|
||||
public static void d(Throwable th, String str, Object... objArr) {
|
||||
Logger logger2 = logger;
|
||||
if (logger2 == null) {
|
||||
return;
|
||||
}
|
||||
logger2.d(th, str, objArr);
|
||||
}
|
||||
|
||||
private static class DefaultLogger implements Logger {
|
||||
DefaultLogger() {
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.CanaryLog.Logger
|
||||
public void d(String str, Object... objArr) {
|
||||
String format = String.format(str, objArr);
|
||||
if (format.length() < 4000) {
|
||||
Log.d("LeakCanary", format);
|
||||
return;
|
||||
}
|
||||
for (String str2 : format.split("\n", -1)) {
|
||||
Log.d("LeakCanary", str2);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.CanaryLog.Logger
|
||||
public void d(Throwable th, String str, Object... objArr) {
|
||||
d(String.format(str, objArr) + '\n' + Log.getStackTraceString(th), new Object[0]);
|
||||
}
|
||||
}
|
||||
}
|
13
sources/com/squareup/leakcanary/DebuggerControl.java
Normal file
13
sources/com/squareup/leakcanary/DebuggerControl.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public interface DebuggerControl {
|
||||
public static final DebuggerControl NONE = new DebuggerControl() { // from class: com.squareup.leakcanary.DebuggerControl.1
|
||||
@Override // com.squareup.leakcanary.DebuggerControl
|
||||
public boolean isDebuggerAttached() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
boolean isDebuggerAttached();
|
||||
}
|
@@ -0,0 +1,164 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import com.squareup.leakcanary.internal.LeakCanaryInternals;
|
||||
import com.squareup.leakcanary.internal.RequestStoragePermissionActivity;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class DefaultLeakDirectoryProvider implements LeakDirectoryProvider {
|
||||
private static final int ANALYSIS_MAX_DURATION_MS = 600000;
|
||||
private static final int DEFAULT_MAX_STORED_HEAP_DUMPS = 7;
|
||||
private static final String HPROF_SUFFIX = ".hprof";
|
||||
private static final String PENDING_HEAPDUMP_SUFFIX = "_pending.hprof";
|
||||
private final Context context;
|
||||
private final int maxStoredHeapDumps;
|
||||
private volatile boolean permissionNotificationDisplayed;
|
||||
private volatile boolean writeExternalStorageGranted;
|
||||
|
||||
public DefaultLeakDirectoryProvider(Context context) {
|
||||
this(context, 7);
|
||||
}
|
||||
|
||||
private File appStorageDirectory() {
|
||||
return new File(this.context.getFilesDir(), "leakcanary");
|
||||
}
|
||||
|
||||
private void cleanupOldHeapDumps() {
|
||||
List<File> listFiles = listFiles(new FilenameFilter() { // from class: com.squareup.leakcanary.DefaultLeakDirectoryProvider.3
|
||||
@Override // java.io.FilenameFilter
|
||||
public boolean accept(File file, String str) {
|
||||
return str.endsWith(DefaultLeakDirectoryProvider.HPROF_SUFFIX);
|
||||
}
|
||||
});
|
||||
int size = listFiles.size() - this.maxStoredHeapDumps;
|
||||
if (size > 0) {
|
||||
CanaryLog.d("Removing %d heap dumps", Integer.valueOf(size));
|
||||
Collections.sort(listFiles, new Comparator<File>() { // from class: com.squareup.leakcanary.DefaultLeakDirectoryProvider.4
|
||||
@Override // java.util.Comparator
|
||||
public int compare(File file, File file2) {
|
||||
return Long.valueOf(file.lastModified()).compareTo(Long.valueOf(file2.lastModified()));
|
||||
}
|
||||
});
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (!listFiles.get(i).delete()) {
|
||||
CanaryLog.d("Could not delete old hprof file %s", listFiles.get(i).getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean directoryWritableAfterMkdirs(File file) {
|
||||
return (file.mkdirs() || file.exists()) && file.canWrite();
|
||||
}
|
||||
|
||||
private File externalStorageDirectory() {
|
||||
return new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "leakcanary-" + this.context.getPackageName());
|
||||
}
|
||||
|
||||
@TargetApi(23)
|
||||
private boolean hasStoragePermission() {
|
||||
if (Build.VERSION.SDK_INT < 23 || this.writeExternalStorageGranted) {
|
||||
return true;
|
||||
}
|
||||
this.writeExternalStorageGranted = this.context.checkSelfPermission("android.permission.WRITE_EXTERNAL_STORAGE") == 0;
|
||||
return this.writeExternalStorageGranted;
|
||||
}
|
||||
|
||||
private void requestWritePermissionNotification() {
|
||||
if (this.permissionNotificationDisplayed) {
|
||||
return;
|
||||
}
|
||||
this.permissionNotificationDisplayed = true;
|
||||
PendingIntent createPendingIntent = RequestStoragePermissionActivity.createPendingIntent(this.context);
|
||||
LeakCanaryInternals.showNotification(this.context, this.context.getString(R.string.leak_canary_permission_notification_title), this.context.getString(R.string.leak_canary_permission_notification_text, this.context.getPackageName()), createPendingIntent, -558907665);
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.LeakDirectoryProvider
|
||||
public void clearLeakDirectory() {
|
||||
for (File file : listFiles(new FilenameFilter() { // from class: com.squareup.leakcanary.DefaultLeakDirectoryProvider.2
|
||||
@Override // java.io.FilenameFilter
|
||||
public boolean accept(File file2, String str) {
|
||||
return !str.endsWith(DefaultLeakDirectoryProvider.PENDING_HEAPDUMP_SUFFIX);
|
||||
}
|
||||
})) {
|
||||
if (!file.delete()) {
|
||||
CanaryLog.d("Could not delete file %s", file.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.LeakDirectoryProvider
|
||||
public List<File> listFiles(FilenameFilter filenameFilter) {
|
||||
if (!hasStoragePermission()) {
|
||||
requestWritePermissionNotification();
|
||||
}
|
||||
ArrayList arrayList = new ArrayList();
|
||||
File[] listFiles = externalStorageDirectory().listFiles(filenameFilter);
|
||||
if (listFiles != null) {
|
||||
arrayList.addAll(Arrays.asList(listFiles));
|
||||
}
|
||||
File[] listFiles2 = appStorageDirectory().listFiles(filenameFilter);
|
||||
if (listFiles2 != null) {
|
||||
arrayList.addAll(Arrays.asList(listFiles2));
|
||||
}
|
||||
return arrayList;
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.LeakDirectoryProvider
|
||||
public File newHeapDumpFile() {
|
||||
Iterator<File> it = listFiles(new FilenameFilter() { // from class: com.squareup.leakcanary.DefaultLeakDirectoryProvider.1
|
||||
@Override // java.io.FilenameFilter
|
||||
public boolean accept(File file, String str) {
|
||||
return str.endsWith(DefaultLeakDirectoryProvider.PENDING_HEAPDUMP_SUFFIX);
|
||||
}
|
||||
}).iterator();
|
||||
while (it.hasNext()) {
|
||||
if (System.currentTimeMillis() - it.next().lastModified() < 600000) {
|
||||
CanaryLog.d("Could not dump heap, previous analysis still is in progress.", new Object[0]);
|
||||
return HeapDumper.RETRY_LATER;
|
||||
}
|
||||
}
|
||||
cleanupOldHeapDumps();
|
||||
File externalStorageDirectory = externalStorageDirectory();
|
||||
if (!directoryWritableAfterMkdirs(externalStorageDirectory)) {
|
||||
if (hasStoragePermission()) {
|
||||
String externalStorageState = Environment.getExternalStorageState();
|
||||
if ("mounted".equals(externalStorageState)) {
|
||||
CanaryLog.d("Could not create heap dump directory in external storage: [%s]", externalStorageDirectory.getAbsolutePath());
|
||||
} else {
|
||||
CanaryLog.d("External storage not mounted, state: %s", externalStorageState);
|
||||
}
|
||||
} else {
|
||||
CanaryLog.d("WRITE_EXTERNAL_STORAGE permission not granted", new Object[0]);
|
||||
requestWritePermissionNotification();
|
||||
}
|
||||
externalStorageDirectory = appStorageDirectory();
|
||||
if (!directoryWritableAfterMkdirs(externalStorageDirectory)) {
|
||||
CanaryLog.d("Could not create heap dump directory in app storage: [%s]", externalStorageDirectory.getAbsolutePath());
|
||||
return HeapDumper.RETRY_LATER;
|
||||
}
|
||||
}
|
||||
return new File(externalStorageDirectory, UUID.randomUUID().toString() + PENDING_HEAPDUMP_SUFFIX);
|
||||
}
|
||||
|
||||
public DefaultLeakDirectoryProvider(Context context, int i) {
|
||||
if (i < 1) {
|
||||
throw new IllegalArgumentException("maxStoredHeapDumps must be at least 1");
|
||||
}
|
||||
this.context = context.getApplicationContext();
|
||||
this.maxStoredHeapDumps = i;
|
||||
}
|
||||
}
|
72
sources/com/squareup/leakcanary/DisplayLeakService.java
Normal file
72
sources/com/squareup/leakcanary/DisplayLeakService.java
Normal file
@@ -0,0 +1,72 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.os.SystemClock;
|
||||
import android.text.format.Formatter;
|
||||
import com.squareup.leakcanary.internal.DisplayLeakActivity;
|
||||
import com.squareup.leakcanary.internal.LeakCanaryInternals;
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public class DisplayLeakService extends AbstractAnalysisResultService {
|
||||
private HeapDump renameHeapdump(HeapDump heapDump) {
|
||||
File file = new File(heapDump.heapDumpFile.getParent(), new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss_SSS'.hprof'", Locale.US).format(new Date()));
|
||||
if (!heapDump.heapDumpFile.renameTo(file)) {
|
||||
CanaryLog.d("Could not rename heap dump file %s to %s", heapDump.heapDumpFile.getPath(), file.getPath());
|
||||
}
|
||||
return heapDump.buildUpon().heapDumpFile(file).build();
|
||||
}
|
||||
|
||||
private boolean saveResult(HeapDump heapDump, AnalysisResult analysisResult) {
|
||||
return AnalyzedHeap.save(heapDump, analysisResult) != null;
|
||||
}
|
||||
|
||||
private void showNotification(PendingIntent pendingIntent, String str, String str2) {
|
||||
LeakCanaryInternals.showNotification(this, str, str2, pendingIntent, (int) (SystemClock.uptimeMillis() / 1000));
|
||||
}
|
||||
|
||||
protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult analysisResult, String str) {
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.AbstractAnalysisResultService
|
||||
protected final void onAnalysisResultFailure(String str) {
|
||||
super.onAnalysisResultFailure(str);
|
||||
showNotification(null, getString(R.string.leak_canary_result_failure_title), str);
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.AbstractAnalysisResultService
|
||||
protected final void onHeapAnalyzed(AnalyzedHeap analyzedHeap) {
|
||||
String string;
|
||||
HeapDump heapDump = analyzedHeap.heapDump;
|
||||
AnalysisResult analysisResult = analyzedHeap.result;
|
||||
String leakInfo = LeakCanary.leakInfo(this, heapDump, analysisResult, true);
|
||||
CanaryLog.d("%s", leakInfo);
|
||||
HeapDump renameHeapdump = renameHeapdump(heapDump);
|
||||
if (saveResult(renameHeapdump, analysisResult)) {
|
||||
PendingIntent createPendingIntent = DisplayLeakActivity.createPendingIntent(this, renameHeapdump.referenceKey);
|
||||
if (analysisResult.failure != null) {
|
||||
string = getString(R.string.leak_canary_analysis_failed);
|
||||
} else {
|
||||
String classSimpleName = LeakCanaryInternals.classSimpleName(analysisResult.className);
|
||||
if (analysisResult.leakFound) {
|
||||
long j = analysisResult.retainedHeapSize;
|
||||
if (j == -1) {
|
||||
string = analysisResult.excludedLeak ? getString(R.string.leak_canary_leak_excluded, new Object[]{classSimpleName}) : getString(R.string.leak_canary_class_has_leaked, new Object[]{classSimpleName});
|
||||
} else {
|
||||
String formatShortFileSize = Formatter.formatShortFileSize(this, j);
|
||||
string = analysisResult.excludedLeak ? getString(R.string.leak_canary_leak_excluded_retaining, new Object[]{classSimpleName, formatShortFileSize}) : getString(R.string.leak_canary_class_has_leaked_retaining, new Object[]{classSimpleName, formatShortFileSize});
|
||||
}
|
||||
} else {
|
||||
string = getString(R.string.leak_canary_class_no_leak, new Object[]{classSimpleName});
|
||||
}
|
||||
}
|
||||
showNotification(createPendingIntent, string, getString(R.string.leak_canary_notification_message));
|
||||
} else {
|
||||
onAnalysisResultFailure(getString(R.string.leak_canary_could_not_save_text));
|
||||
}
|
||||
afterDefaultHandling(renameHeapdump, analysisResult, leakInfo);
|
||||
}
|
||||
}
|
162
sources/com/squareup/leakcanary/ExcludedRefs.java
Normal file
162
sources/com/squareup/leakcanary/ExcludedRefs.java
Normal file
@@ -0,0 +1,162 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class ExcludedRefs implements Serializable {
|
||||
public final Map<String, Exclusion> classNames;
|
||||
public final Map<String, Map<String, Exclusion>> fieldNameByClassName;
|
||||
public final Map<String, Map<String, Exclusion>> staticFieldNameByClassName;
|
||||
public final Map<String, Exclusion> threadNames;
|
||||
|
||||
public interface Builder {
|
||||
ExcludedRefs build();
|
||||
|
||||
BuilderWithParams clazz(String str);
|
||||
|
||||
BuilderWithParams instanceField(String str, String str2);
|
||||
|
||||
BuilderWithParams staticField(String str, String str2);
|
||||
|
||||
BuilderWithParams thread(String str);
|
||||
}
|
||||
|
||||
public static final class BuilderWithParams implements Builder {
|
||||
private ParamsBuilder lastParams;
|
||||
private final Map<String, Map<String, ParamsBuilder>> fieldNameByClassName = new LinkedHashMap();
|
||||
private final Map<String, Map<String, ParamsBuilder>> staticFieldNameByClassName = new LinkedHashMap();
|
||||
private final Map<String, ParamsBuilder> threadNames = new LinkedHashMap();
|
||||
private final Map<String, ParamsBuilder> classNames = new LinkedHashMap();
|
||||
|
||||
BuilderWithParams() {
|
||||
}
|
||||
|
||||
public BuilderWithParams alwaysExclude() {
|
||||
this.lastParams.alwaysExclude = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.ExcludedRefs.Builder
|
||||
public ExcludedRefs build() {
|
||||
return new ExcludedRefs(this);
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.ExcludedRefs.Builder
|
||||
public BuilderWithParams clazz(String str) {
|
||||
Preconditions.checkNotNull(str, "className");
|
||||
this.lastParams = new ParamsBuilder("any subclass of " + str);
|
||||
this.classNames.put(str, this.lastParams);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.ExcludedRefs.Builder
|
||||
public BuilderWithParams instanceField(String str, String str2) {
|
||||
Preconditions.checkNotNull(str, "className");
|
||||
Preconditions.checkNotNull(str2, "fieldName");
|
||||
Map<String, ParamsBuilder> map = this.fieldNameByClassName.get(str);
|
||||
if (map == null) {
|
||||
map = new LinkedHashMap<>();
|
||||
this.fieldNameByClassName.put(str, map);
|
||||
}
|
||||
this.lastParams = new ParamsBuilder("field " + str + "#" + str2);
|
||||
map.put(str2, this.lastParams);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuilderWithParams named(String str) {
|
||||
this.lastParams.name = str;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuilderWithParams reason(String str) {
|
||||
this.lastParams.reason = str;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.ExcludedRefs.Builder
|
||||
public BuilderWithParams staticField(String str, String str2) {
|
||||
Preconditions.checkNotNull(str, "className");
|
||||
Preconditions.checkNotNull(str2, "fieldName");
|
||||
Map<String, ParamsBuilder> map = this.staticFieldNameByClassName.get(str);
|
||||
if (map == null) {
|
||||
map = new LinkedHashMap<>();
|
||||
this.staticFieldNameByClassName.put(str, map);
|
||||
}
|
||||
this.lastParams = new ParamsBuilder("static field " + str + "#" + str2);
|
||||
map.put(str2, this.lastParams);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.ExcludedRefs.Builder
|
||||
public BuilderWithParams thread(String str) {
|
||||
Preconditions.checkNotNull(str, "threadName");
|
||||
this.lastParams = new ParamsBuilder("any threads named " + str);
|
||||
this.threadNames.put(str, this.lastParams);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
static final class ParamsBuilder {
|
||||
boolean alwaysExclude;
|
||||
final String matching;
|
||||
String name;
|
||||
String reason;
|
||||
|
||||
ParamsBuilder(String str) {
|
||||
this.matching = str;
|
||||
}
|
||||
}
|
||||
|
||||
ExcludedRefs(BuilderWithParams builderWithParams) {
|
||||
this.fieldNameByClassName = unmodifiableRefStringMap(builderWithParams.fieldNameByClassName);
|
||||
this.staticFieldNameByClassName = unmodifiableRefStringMap(builderWithParams.staticFieldNameByClassName);
|
||||
this.threadNames = unmodifiableRefMap(builderWithParams.threadNames);
|
||||
this.classNames = unmodifiableRefMap(builderWithParams.classNames);
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new BuilderWithParams();
|
||||
}
|
||||
|
||||
private Map<String, Exclusion> unmodifiableRefMap(Map<String, ParamsBuilder> map) {
|
||||
LinkedHashMap linkedHashMap = new LinkedHashMap();
|
||||
for (Map.Entry<String, ParamsBuilder> entry : map.entrySet()) {
|
||||
linkedHashMap.put(entry.getKey(), new Exclusion(entry.getValue()));
|
||||
}
|
||||
return Collections.unmodifiableMap(linkedHashMap);
|
||||
}
|
||||
|
||||
private Map<String, Map<String, Exclusion>> unmodifiableRefStringMap(Map<String, Map<String, ParamsBuilder>> map) {
|
||||
LinkedHashMap linkedHashMap = new LinkedHashMap();
|
||||
for (Map.Entry<String, Map<String, ParamsBuilder>> entry : map.entrySet()) {
|
||||
linkedHashMap.put(entry.getKey(), unmodifiableRefMap(entry.getValue()));
|
||||
}
|
||||
return Collections.unmodifiableMap(linkedHashMap);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
String str = "";
|
||||
for (Map.Entry<String, Map<String, Exclusion>> entry : this.fieldNameByClassName.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
for (Map.Entry<String, Exclusion> entry2 : entry.getValue().entrySet()) {
|
||||
str = str + "| Field: " + key + "." + entry2.getKey() + (entry2.getValue().alwaysExclude ? " (always)" : "") + "\n";
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, Map<String, Exclusion>> entry3 : this.staticFieldNameByClassName.entrySet()) {
|
||||
String key2 = entry3.getKey();
|
||||
for (Map.Entry<String, Exclusion> entry4 : entry3.getValue().entrySet()) {
|
||||
str = str + "| Static field: " + key2 + "." + entry4.getKey() + (entry4.getValue().alwaysExclude ? " (always)" : "") + "\n";
|
||||
}
|
||||
}
|
||||
for (Map.Entry<String, Exclusion> entry5 : this.threadNames.entrySet()) {
|
||||
str = str + "| Thread:" + entry5.getKey() + (entry5.getValue().alwaysExclude ? " (always)" : "") + "\n";
|
||||
}
|
||||
for (Map.Entry<String, Exclusion> entry6 : this.classNames.entrySet()) {
|
||||
str = str + "| Class:" + entry6.getKey() + (entry6.getValue().alwaysExclude ? " (always)" : "") + "\n";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
19
sources/com/squareup/leakcanary/Exclusion.java
Normal file
19
sources/com/squareup/leakcanary/Exclusion.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import com.squareup.leakcanary.ExcludedRefs;
|
||||
import java.io.Serializable;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class Exclusion implements Serializable {
|
||||
public final boolean alwaysExclude;
|
||||
public final String matching;
|
||||
public final String name;
|
||||
public final String reason;
|
||||
|
||||
Exclusion(ExcludedRefs.ParamsBuilder paramsBuilder) {
|
||||
this.name = paramsBuilder.name;
|
||||
this.reason = paramsBuilder.reason;
|
||||
this.alwaysExclude = paramsBuilder.alwaysExclude;
|
||||
this.matching = paramsBuilder.matching;
|
||||
}
|
||||
}
|
23
sources/com/squareup/leakcanary/GcTrigger.java
Normal file
23
sources/com/squareup/leakcanary/GcTrigger.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public interface GcTrigger {
|
||||
public static final GcTrigger DEFAULT = new GcTrigger() { // from class: com.squareup.leakcanary.GcTrigger.1
|
||||
private void enqueueReferences() {
|
||||
try {
|
||||
Thread.sleep(100L);
|
||||
} catch (InterruptedException unused) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.GcTrigger
|
||||
public void runGc() {
|
||||
Runtime.getRuntime().gc();
|
||||
enqueueReferences();
|
||||
System.runFinalization();
|
||||
}
|
||||
};
|
||||
|
||||
void runGc();
|
||||
}
|
140
sources/com/squareup/leakcanary/HahaHelper.java
Normal file
140
sources/com/squareup/leakcanary/HahaHelper.java
Normal file
@@ -0,0 +1,140 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import com.baidu.cloud.media.player.BDCloudMediaPlayer;
|
||||
import com.squareup.haha.perflib.ArrayInstance;
|
||||
import com.squareup.haha.perflib.ClassInstance;
|
||||
import com.squareup.haha.perflib.ClassObj;
|
||||
import com.squareup.haha.perflib.Instance;
|
||||
import com.squareup.haha.perflib.Type;
|
||||
import com.unity3d.ads.metadata.MediationMetaData;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class HahaHelper {
|
||||
private static final Set<String> WRAPPER_TYPES = new HashSet(Arrays.asList(Boolean.class.getName(), Character.class.getName(), Float.class.getName(), Double.class.getName(), Byte.class.getName(), Short.class.getName(), Integer.class.getName(), Long.class.getName()));
|
||||
|
||||
private HahaHelper() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
static String asString(Object obj) {
|
||||
Preconditions.checkNotNull(obj, "stringObject");
|
||||
Instance instance = (Instance) obj;
|
||||
List<ClassInstance.FieldValue> classInstanceValues = classInstanceValues(instance);
|
||||
Integer num = (Integer) fieldValue(classInstanceValues, "count");
|
||||
Preconditions.checkNotNull(num, "count");
|
||||
if (num.intValue() == 0) {
|
||||
return "";
|
||||
}
|
||||
Object fieldValue = fieldValue(classInstanceValues, "value");
|
||||
Preconditions.checkNotNull(fieldValue, "value");
|
||||
if (isCharArray(fieldValue)) {
|
||||
ArrayInstance arrayInstance = (ArrayInstance) fieldValue;
|
||||
Integer num2 = 0;
|
||||
if (hasField(classInstanceValues, BDCloudMediaPlayer.OnNativeInvokeListener.ARG_OFFSET)) {
|
||||
num2 = (Integer) fieldValue(classInstanceValues, BDCloudMediaPlayer.OnNativeInvokeListener.ARG_OFFSET);
|
||||
Preconditions.checkNotNull(num2, BDCloudMediaPlayer.OnNativeInvokeListener.ARG_OFFSET);
|
||||
}
|
||||
return new String(arrayInstance.asCharArray(num2.intValue(), num.intValue()));
|
||||
}
|
||||
if (!isByteArray(fieldValue)) {
|
||||
throw new UnsupportedOperationException("Could not find char array in " + instance);
|
||||
}
|
||||
ArrayInstance arrayInstance2 = (ArrayInstance) fieldValue;
|
||||
try {
|
||||
Method declaredMethod = ArrayInstance.class.getDeclaredMethod("asRawByteArray", Integer.TYPE, Integer.TYPE);
|
||||
declaredMethod.setAccessible(true);
|
||||
return new String((byte[]) declaredMethod.invoke(arrayInstance2, 0, num), Charset.forName("UTF-8"));
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (NoSuchMethodException e2) {
|
||||
throw new RuntimeException(e2);
|
||||
} catch (InvocationTargetException e3) {
|
||||
throw new RuntimeException(e3);
|
||||
}
|
||||
}
|
||||
|
||||
static List<ClassInstance.FieldValue> classInstanceValues(Instance instance) {
|
||||
return ((ClassInstance) instance).getValues();
|
||||
}
|
||||
|
||||
static boolean extendsThread(ClassObj classObj) {
|
||||
while (classObj.getSuperClassObj() != null) {
|
||||
if (classObj.getClassName().equals(Thread.class.getName())) {
|
||||
return true;
|
||||
}
|
||||
classObj = classObj.getSuperClassObj();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static <T> T fieldValue(List<ClassInstance.FieldValue> list, String str) {
|
||||
for (ClassInstance.FieldValue fieldValue : list) {
|
||||
if (fieldValue.getField().getName().equals(str)) {
|
||||
return (T) fieldValue.getValue();
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Field " + str + " does not exists");
|
||||
}
|
||||
|
||||
static boolean hasField(List<ClassInstance.FieldValue> list, String str) {
|
||||
Iterator<ClassInstance.FieldValue> it = list.iterator();
|
||||
while (it.hasNext()) {
|
||||
if (it.next().getField().getName().equals(str)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isByteArray(Object obj) {
|
||||
return (obj instanceof ArrayInstance) && ((ArrayInstance) obj).getArrayType() == Type.BYTE;
|
||||
}
|
||||
|
||||
private static boolean isCharArray(Object obj) {
|
||||
return (obj instanceof ArrayInstance) && ((ArrayInstance) obj).getArrayType() == Type.CHAR;
|
||||
}
|
||||
|
||||
public static boolean isPrimitiveOrWrapperArray(Object obj) {
|
||||
if (!(obj instanceof ArrayInstance)) {
|
||||
return false;
|
||||
}
|
||||
ArrayInstance arrayInstance = (ArrayInstance) obj;
|
||||
if (arrayInstance.getArrayType() != Type.OBJECT) {
|
||||
return true;
|
||||
}
|
||||
return WRAPPER_TYPES.contains(arrayInstance.getClassObj().getClassName());
|
||||
}
|
||||
|
||||
public static boolean isPrimitiveWrapper(Object obj) {
|
||||
if (obj instanceof ClassInstance) {
|
||||
return WRAPPER_TYPES.contains(((ClassInstance) obj).getClassObj().getClassName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static String threadName(Instance instance) {
|
||||
Object fieldValue = fieldValue(classInstanceValues(instance), MediationMetaData.KEY_NAME);
|
||||
return fieldValue == null ? "Thread name not available" : asString(fieldValue);
|
||||
}
|
||||
|
||||
static String valueAsString(Object obj) {
|
||||
if (obj == null) {
|
||||
return "null";
|
||||
}
|
||||
if (!(obj instanceof ClassInstance)) {
|
||||
return obj.toString();
|
||||
}
|
||||
if (!((ClassInstance) obj).getClassObj().getClassName().equals(String.class.getName())) {
|
||||
return obj.toString();
|
||||
}
|
||||
return '\"' + asString(obj) + '\"';
|
||||
}
|
||||
}
|
352
sources/com/squareup/leakcanary/HeapAnalyzer.java
Normal file
352
sources/com/squareup/leakcanary/HeapAnalyzer.java
Normal file
@@ -0,0 +1,352 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import android.os.Build;
|
||||
import com.squareup.haha.perflib.ArrayInstance;
|
||||
import com.squareup.haha.perflib.ClassInstance;
|
||||
import com.squareup.haha.perflib.ClassObj;
|
||||
import com.squareup.haha.perflib.Field;
|
||||
import com.squareup.haha.perflib.HprofParser;
|
||||
import com.squareup.haha.perflib.Instance;
|
||||
import com.squareup.haha.perflib.RootObj;
|
||||
import com.squareup.haha.perflib.RootType;
|
||||
import com.squareup.haha.perflib.Snapshot;
|
||||
import com.squareup.haha.perflib.Type;
|
||||
import com.squareup.haha.perflib.io.MemoryMappedFileBuffer;
|
||||
import com.squareup.leakcanary.AnalyzerProgressListener;
|
||||
import com.squareup.leakcanary.LeakTraceElement;
|
||||
import com.squareup.leakcanary.Reachability;
|
||||
import com.squareup.leakcanary.ShortestPathFinder;
|
||||
import com.unity3d.ads.metadata.MediationMetaData;
|
||||
import gnu.trove.THashMap;
|
||||
import gnu.trove.TObjectProcedure;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class HeapAnalyzer {
|
||||
private static final String ANONYMOUS_CLASS_NAME_PATTERN = "^.+\\$\\d+$";
|
||||
private final ExcludedRefs excludedRefs;
|
||||
private final AnalyzerProgressListener listener;
|
||||
private final List<Reachability.Inspector> reachabilityInspectors;
|
||||
|
||||
@Deprecated
|
||||
public HeapAnalyzer(ExcludedRefs excludedRefs) {
|
||||
this(excludedRefs, AnalyzerProgressListener.NONE, Collections.emptyList());
|
||||
}
|
||||
|
||||
private LeakTraceElement buildLeakElement(LeakNode leakNode) {
|
||||
LeakTraceElement.Holder holder;
|
||||
String str;
|
||||
LeakTraceElement.Holder holder2;
|
||||
LeakNode leakNode2 = leakNode.parent;
|
||||
String str2 = null;
|
||||
if (leakNode2 == null) {
|
||||
return null;
|
||||
}
|
||||
Instance instance = leakNode2.instance;
|
||||
if (instance instanceof RootObj) {
|
||||
return null;
|
||||
}
|
||||
List<LeakReference> describeFields = describeFields(instance);
|
||||
String className = getClassName(instance);
|
||||
ArrayList arrayList = new ArrayList();
|
||||
arrayList.add(className);
|
||||
String name = Object.class.getName();
|
||||
if (instance instanceof ClassInstance) {
|
||||
ClassObj classObj = instance.getClassObj();
|
||||
while (true) {
|
||||
classObj = classObj.getSuperClassObj();
|
||||
if (classObj.getClassName().equals(name)) {
|
||||
break;
|
||||
}
|
||||
arrayList.add(classObj.getClassName());
|
||||
}
|
||||
}
|
||||
if (instance instanceof ClassObj) {
|
||||
holder = LeakTraceElement.Holder.CLASS;
|
||||
} else if (instance instanceof ArrayInstance) {
|
||||
holder = LeakTraceElement.Holder.ARRAY;
|
||||
} else {
|
||||
ClassObj classObj2 = instance.getClassObj();
|
||||
if (HahaHelper.extendsThread(classObj2)) {
|
||||
LeakTraceElement.Holder holder3 = LeakTraceElement.Holder.THREAD;
|
||||
str = "(named '" + HahaHelper.threadName(instance) + "')";
|
||||
holder2 = holder3;
|
||||
return new LeakTraceElement(leakNode.leakReference, holder2, arrayList, str, leakNode.exclusion, describeFields);
|
||||
}
|
||||
if (className.matches(ANONYMOUS_CLASS_NAME_PATTERN)) {
|
||||
String className2 = classObj2.getSuperClassObj().getClassName();
|
||||
if (name.equals(className2)) {
|
||||
holder = LeakTraceElement.Holder.OBJECT;
|
||||
try {
|
||||
Class<?>[] interfaces = Class.forName(classObj2.getClassName()).getInterfaces();
|
||||
if (interfaces.length > 0) {
|
||||
str2 = "(anonymous implementation of " + interfaces[0].getName() + ")";
|
||||
} else {
|
||||
str2 = "(anonymous subclass of java.lang.Object)";
|
||||
}
|
||||
} catch (ClassNotFoundException unused) {
|
||||
}
|
||||
} else {
|
||||
str2 = "(anonymous subclass of " + className2 + ")";
|
||||
holder = LeakTraceElement.Holder.OBJECT;
|
||||
}
|
||||
} else {
|
||||
holder = LeakTraceElement.Holder.OBJECT;
|
||||
}
|
||||
}
|
||||
holder2 = holder;
|
||||
str = str2;
|
||||
return new LeakTraceElement(leakNode.leakReference, holder2, arrayList, str, leakNode.exclusion, describeFields);
|
||||
}
|
||||
|
||||
private LeakTrace buildLeakTrace(LeakNode leakNode) {
|
||||
ArrayList arrayList = new ArrayList();
|
||||
for (LeakNode leakNode2 = new LeakNode(null, null, leakNode, null); leakNode2 != null; leakNode2 = leakNode2.parent) {
|
||||
LeakTraceElement buildLeakElement = buildLeakElement(leakNode2);
|
||||
if (buildLeakElement != null) {
|
||||
arrayList.add(0, buildLeakElement);
|
||||
}
|
||||
}
|
||||
return new LeakTrace(arrayList, computeExpectedReachability(arrayList));
|
||||
}
|
||||
|
||||
private List<Reachability> computeExpectedReachability(List<LeakTraceElement> list) {
|
||||
Reachability expectedReachability;
|
||||
int i = 1;
|
||||
int size = list.size() - 1;
|
||||
int i2 = 0;
|
||||
int i3 = 0;
|
||||
loop0: while (true) {
|
||||
if (i >= size) {
|
||||
break;
|
||||
}
|
||||
LeakTraceElement leakTraceElement = list.get(i);
|
||||
Iterator<Reachability.Inspector> it = this.reachabilityInspectors.iterator();
|
||||
do {
|
||||
if (!it.hasNext()) {
|
||||
break;
|
||||
}
|
||||
expectedReachability = it.next().expectedReachability(leakTraceElement);
|
||||
if (expectedReachability == Reachability.REACHABLE) {
|
||||
i3 = i;
|
||||
}
|
||||
i++;
|
||||
} while (expectedReachability != Reachability.UNREACHABLE);
|
||||
size = i;
|
||||
break loop0;
|
||||
}
|
||||
ArrayList arrayList = new ArrayList();
|
||||
while (i2 < list.size()) {
|
||||
arrayList.add(i2 <= i3 ? Reachability.REACHABLE : i2 >= size ? Reachability.UNREACHABLE : Reachability.UNKNOWN);
|
||||
i2++;
|
||||
}
|
||||
return arrayList;
|
||||
}
|
||||
|
||||
private long computeIgnoredBitmapRetainedSize(Snapshot snapshot, Instance instance) {
|
||||
ArrayInstance arrayInstance;
|
||||
long j = 0;
|
||||
for (Instance instance2 : snapshot.findClass("android.graphics.Bitmap").getInstancesList()) {
|
||||
if (isIgnoredDominator(instance, instance2) && (arrayInstance = (ArrayInstance) HahaHelper.fieldValue(HahaHelper.classInstanceValues(instance2), "mBuffer")) != null) {
|
||||
long totalRetainedSize = arrayInstance.getTotalRetainedSize();
|
||||
long totalRetainedSize2 = instance2.getTotalRetainedSize();
|
||||
if (totalRetainedSize2 < totalRetainedSize) {
|
||||
totalRetainedSize2 += totalRetainedSize;
|
||||
}
|
||||
j += totalRetainedSize2;
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
private List<LeakReference> describeFields(Instance instance) {
|
||||
ArrayList arrayList = new ArrayList();
|
||||
if (instance instanceof ClassObj) {
|
||||
for (Map.Entry<Field, Object> entry : ((ClassObj) instance).getStaticFieldValues().entrySet()) {
|
||||
arrayList.add(new LeakReference(LeakTraceElement.Type.STATIC_FIELD, entry.getKey().getName(), HahaHelper.valueAsString(entry.getValue())));
|
||||
}
|
||||
} else if (instance instanceof ArrayInstance) {
|
||||
ArrayInstance arrayInstance = (ArrayInstance) instance;
|
||||
if (arrayInstance.getArrayType() == Type.OBJECT) {
|
||||
Object[] values = arrayInstance.getValues();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
arrayList.add(new LeakReference(LeakTraceElement.Type.ARRAY_ENTRY, Integer.toString(i), HahaHelper.valueAsString(values[i])));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Map.Entry<Field, Object> entry2 : instance.getClassObj().getStaticFieldValues().entrySet()) {
|
||||
arrayList.add(new LeakReference(LeakTraceElement.Type.STATIC_FIELD, entry2.getKey().getName(), HahaHelper.valueAsString(entry2.getValue())));
|
||||
}
|
||||
for (ClassInstance.FieldValue fieldValue : ((ClassInstance) instance).getValues()) {
|
||||
arrayList.add(new LeakReference(LeakTraceElement.Type.INSTANCE_FIELD, fieldValue.getField().getName(), HahaHelper.valueAsString(fieldValue.getValue())));
|
||||
}
|
||||
}
|
||||
return arrayList;
|
||||
}
|
||||
|
||||
private AnalysisResult findLeakTrace(long j, Snapshot snapshot, Instance instance, boolean z) {
|
||||
long j2;
|
||||
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.FINDING_SHORTEST_PATH);
|
||||
ShortestPathFinder.Result findPath = new ShortestPathFinder(this.excludedRefs).findPath(snapshot, instance);
|
||||
String className = instance.getClassObj().getClassName();
|
||||
if (findPath.leakingNode == null) {
|
||||
return AnalysisResult.noLeak(className, since(j));
|
||||
}
|
||||
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.BUILDING_LEAK_TRACE);
|
||||
LeakTrace buildLeakTrace = buildLeakTrace(findPath.leakingNode);
|
||||
if (z) {
|
||||
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.COMPUTING_DOMINATORS);
|
||||
snapshot.computeDominators();
|
||||
Instance instance2 = findPath.leakingNode.instance;
|
||||
j2 = instance2.getTotalRetainedSize();
|
||||
if (Build.VERSION.SDK_INT <= 25) {
|
||||
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.COMPUTING_BITMAP_SIZE);
|
||||
j2 += computeIgnoredBitmapRetainedSize(snapshot, instance2);
|
||||
}
|
||||
} else {
|
||||
j2 = -1;
|
||||
}
|
||||
return AnalysisResult.leakDetected(findPath.excludingKnownLeaks, className, buildLeakTrace, j2, since(j));
|
||||
}
|
||||
|
||||
private Instance findLeakingReference(String str, Snapshot snapshot) {
|
||||
ClassObj findClass = snapshot.findClass(KeyedWeakReference.class.getName());
|
||||
if (findClass == null) {
|
||||
throw new IllegalStateException("Could not find the " + KeyedWeakReference.class.getName() + " class in the heap dump.");
|
||||
}
|
||||
ArrayList arrayList = new ArrayList();
|
||||
Iterator<Instance> it = findClass.getInstancesList().iterator();
|
||||
while (it.hasNext()) {
|
||||
List<ClassInstance.FieldValue> classInstanceValues = HahaHelper.classInstanceValues(it.next());
|
||||
Object fieldValue = HahaHelper.fieldValue(classInstanceValues, "key");
|
||||
if (fieldValue == null) {
|
||||
arrayList.add(null);
|
||||
} else {
|
||||
String asString = HahaHelper.asString(fieldValue);
|
||||
if (asString.equals(str)) {
|
||||
return (Instance) HahaHelper.fieldValue(classInstanceValues, "referent");
|
||||
}
|
||||
arrayList.add(asString);
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Could not find weak reference with key " + str + " in " + arrayList);
|
||||
}
|
||||
|
||||
private String generateRootKey(RootObj rootObj) {
|
||||
return String.format("%s@0x%08x", rootObj.getRootType().getName(), Long.valueOf(rootObj.getId()));
|
||||
}
|
||||
|
||||
private String getClassName(Instance instance) {
|
||||
return instance instanceof ClassObj ? ((ClassObj) instance).getClassName() : instance instanceof ArrayInstance ? ((ArrayInstance) instance).getClassObj().getClassName() : instance.getClassObj().getClassName();
|
||||
}
|
||||
|
||||
private boolean isIgnoredDominator(Instance instance, Instance instance2) {
|
||||
boolean z = false;
|
||||
do {
|
||||
Instance immediateDominator = instance2.getImmediateDominator();
|
||||
if ((immediateDominator instanceof RootObj) && ((RootObj) immediateDominator).getRootType() == RootType.UNKNOWN) {
|
||||
instance2 = instance2.getNextInstanceToGcRoot();
|
||||
z = true;
|
||||
} else {
|
||||
instance2 = immediateDominator;
|
||||
}
|
||||
if (instance2 == null) {
|
||||
return false;
|
||||
}
|
||||
} while (instance2 != instance);
|
||||
return z;
|
||||
}
|
||||
|
||||
private long since(long j) {
|
||||
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - j);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public AnalysisResult checkForLeak(File file, String str) {
|
||||
return checkForLeak(file, str, true);
|
||||
}
|
||||
|
||||
void deduplicateGcRoots(Snapshot snapshot) {
|
||||
final THashMap tHashMap = new THashMap();
|
||||
final Collection<RootObj> gCRoots = snapshot.getGCRoots();
|
||||
for (RootObj rootObj : gCRoots) {
|
||||
String generateRootKey = generateRootKey(rootObj);
|
||||
if (!tHashMap.containsKey(generateRootKey)) {
|
||||
tHashMap.put(generateRootKey, rootObj);
|
||||
}
|
||||
}
|
||||
gCRoots.clear();
|
||||
tHashMap.forEach(new TObjectProcedure<String>() { // from class: com.squareup.leakcanary.HeapAnalyzer.1
|
||||
@Override // gnu.trove.TObjectProcedure
|
||||
public boolean execute(String str) {
|
||||
return gCRoots.add(tHashMap.get(str));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public List<TrackedReference> findTrackedReferences(File file) {
|
||||
if (!file.exists()) {
|
||||
throw new IllegalArgumentException("File does not exist: " + file);
|
||||
}
|
||||
try {
|
||||
Snapshot parse = new HprofParser(new MemoryMappedFileBuffer(file)).parse();
|
||||
deduplicateGcRoots(parse);
|
||||
ClassObj findClass = parse.findClass(KeyedWeakReference.class.getName());
|
||||
ArrayList arrayList = new ArrayList();
|
||||
Iterator<Instance> it = findClass.getInstancesList().iterator();
|
||||
while (it.hasNext()) {
|
||||
List<ClassInstance.FieldValue> classInstanceValues = HahaHelper.classInstanceValues(it.next());
|
||||
String asString = HahaHelper.asString(HahaHelper.fieldValue(classInstanceValues, "key"));
|
||||
String asString2 = HahaHelper.hasField(classInstanceValues, MediationMetaData.KEY_NAME) ? HahaHelper.asString(HahaHelper.fieldValue(classInstanceValues, MediationMetaData.KEY_NAME)) : "(No name field)";
|
||||
Instance instance = (Instance) HahaHelper.fieldValue(classInstanceValues, "referent");
|
||||
if (instance != null) {
|
||||
arrayList.add(new TrackedReference(asString, asString2, getClassName(instance), describeFields(instance)));
|
||||
}
|
||||
}
|
||||
return arrayList;
|
||||
} catch (Throwable th) {
|
||||
throw new RuntimeException(th);
|
||||
}
|
||||
}
|
||||
|
||||
public AnalysisResult checkForLeak(File file, String str, boolean z) {
|
||||
long nanoTime = System.nanoTime();
|
||||
if (!file.exists()) {
|
||||
return AnalysisResult.failure(new IllegalArgumentException("File does not exist: " + file), since(nanoTime));
|
||||
}
|
||||
try {
|
||||
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.READING_HEAP_DUMP_FILE);
|
||||
HprofParser hprofParser = new HprofParser(new MemoryMappedFileBuffer(file));
|
||||
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.PARSING_HEAP_DUMP);
|
||||
Snapshot parse = hprofParser.parse();
|
||||
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.DEDUPLICATING_GC_ROOTS);
|
||||
deduplicateGcRoots(parse);
|
||||
this.listener.onProgressUpdate(AnalyzerProgressListener.Step.FINDING_LEAKING_REF);
|
||||
Instance findLeakingReference = findLeakingReference(str, parse);
|
||||
return findLeakingReference == null ? AnalysisResult.noLeak(findLeakingReference.getClassObj().getClassName(), since(nanoTime)) : findLeakTrace(nanoTime, parse, findLeakingReference, z);
|
||||
} catch (Throwable th) {
|
||||
return AnalysisResult.failure(th, since(nanoTime));
|
||||
}
|
||||
}
|
||||
|
||||
public HeapAnalyzer(ExcludedRefs excludedRefs, AnalyzerProgressListener analyzerProgressListener, List<Class<? extends Reachability.Inspector>> list) {
|
||||
this.excludedRefs = excludedRefs;
|
||||
this.listener = analyzerProgressListener;
|
||||
this.reachabilityInspectors = new ArrayList();
|
||||
Iterator<Class<? extends Reachability.Inspector>> it = list.iterator();
|
||||
while (it.hasNext()) {
|
||||
try {
|
||||
this.reachabilityInspectors.add(it.next().getDeclaredConstructor(new Class[0]).newInstance(new Object[0]));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
146
sources/com/squareup/leakcanary/HeapDump.java
Normal file
146
sources/com/squareup/leakcanary/HeapDump.java
Normal file
@@ -0,0 +1,146 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import com.squareup.leakcanary.Reachability;
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class HeapDump implements Serializable {
|
||||
public final boolean computeRetainedHeapSize;
|
||||
public final ExcludedRefs excludedRefs;
|
||||
public final long gcDurationMs;
|
||||
public final long heapDumpDurationMs;
|
||||
public final File heapDumpFile;
|
||||
public final List<Class<? extends Reachability.Inspector>> reachabilityInspectorClasses;
|
||||
public final String referenceKey;
|
||||
public final String referenceName;
|
||||
public final long watchDurationMs;
|
||||
|
||||
public interface Listener {
|
||||
public static final Listener NONE = new Listener() { // from class: com.squareup.leakcanary.HeapDump.Listener.1
|
||||
@Override // com.squareup.leakcanary.HeapDump.Listener
|
||||
public void analyze(HeapDump heapDump) {
|
||||
}
|
||||
};
|
||||
|
||||
void analyze(HeapDump heapDump);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public HeapDump(File file, String str, String str2, ExcludedRefs excludedRefs, long j, long j2, long j3) {
|
||||
this(new Builder().heapDumpFile(file).referenceKey(str).referenceName(str2).excludedRefs(excludedRefs).computeRetainedHeapSize(true).watchDurationMs(j).gcDurationMs(j2).heapDumpDurationMs(j3));
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public Builder buildUpon() {
|
||||
return new Builder(this);
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
boolean computeRetainedHeapSize;
|
||||
ExcludedRefs excludedRefs;
|
||||
long gcDurationMs;
|
||||
long heapDumpDurationMs;
|
||||
File heapDumpFile;
|
||||
List<Class<? extends Reachability.Inspector>> reachabilityInspectorClasses;
|
||||
String referenceKey;
|
||||
String referenceName;
|
||||
long watchDurationMs;
|
||||
|
||||
Builder() {
|
||||
this.heapDumpFile = null;
|
||||
this.referenceKey = null;
|
||||
this.referenceName = "";
|
||||
this.excludedRefs = null;
|
||||
this.watchDurationMs = 0L;
|
||||
this.gcDurationMs = 0L;
|
||||
this.heapDumpDurationMs = 0L;
|
||||
this.computeRetainedHeapSize = false;
|
||||
this.reachabilityInspectorClasses = null;
|
||||
}
|
||||
|
||||
public HeapDump build() {
|
||||
Preconditions.checkNotNull(this.excludedRefs, "excludedRefs");
|
||||
Preconditions.checkNotNull(this.heapDumpFile, "heapDumpFile");
|
||||
Preconditions.checkNotNull(this.referenceKey, "referenceKey");
|
||||
Preconditions.checkNotNull(this.reachabilityInspectorClasses, "reachabilityInspectorClasses");
|
||||
return new HeapDump(this);
|
||||
}
|
||||
|
||||
public Builder computeRetainedHeapSize(boolean z) {
|
||||
this.computeRetainedHeapSize = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder excludedRefs(ExcludedRefs excludedRefs) {
|
||||
this.excludedRefs = (ExcludedRefs) Preconditions.checkNotNull(excludedRefs, "excludedRefs");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder gcDurationMs(long j) {
|
||||
this.gcDurationMs = j;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder heapDumpDurationMs(long j) {
|
||||
this.heapDumpDurationMs = j;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder heapDumpFile(File file) {
|
||||
this.heapDumpFile = (File) Preconditions.checkNotNull(file, "heapDumpFile");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder reachabilityInspectorClasses(List<Class<? extends Reachability.Inspector>> list) {
|
||||
Preconditions.checkNotNull(list, "reachabilityInspectorClasses");
|
||||
this.reachabilityInspectorClasses = Collections.unmodifiableList(new ArrayList(list));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder referenceKey(String str) {
|
||||
this.referenceKey = (String) Preconditions.checkNotNull(str, "referenceKey");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder referenceName(String str) {
|
||||
this.referenceName = (String) Preconditions.checkNotNull(str, "referenceName");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder watchDurationMs(long j) {
|
||||
this.watchDurationMs = j;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder(HeapDump heapDump) {
|
||||
this.heapDumpFile = heapDump.heapDumpFile;
|
||||
this.referenceKey = heapDump.referenceKey;
|
||||
this.referenceName = heapDump.referenceName;
|
||||
this.excludedRefs = heapDump.excludedRefs;
|
||||
this.computeRetainedHeapSize = heapDump.computeRetainedHeapSize;
|
||||
this.watchDurationMs = heapDump.watchDurationMs;
|
||||
this.gcDurationMs = heapDump.gcDurationMs;
|
||||
this.heapDumpDurationMs = heapDump.heapDumpDurationMs;
|
||||
this.reachabilityInspectorClasses = heapDump.reachabilityInspectorClasses;
|
||||
}
|
||||
}
|
||||
|
||||
HeapDump(Builder builder) {
|
||||
this.heapDumpFile = builder.heapDumpFile;
|
||||
this.referenceKey = builder.referenceKey;
|
||||
this.referenceName = builder.referenceName;
|
||||
this.excludedRefs = builder.excludedRefs;
|
||||
this.computeRetainedHeapSize = builder.computeRetainedHeapSize;
|
||||
this.watchDurationMs = builder.watchDurationMs;
|
||||
this.gcDurationMs = builder.gcDurationMs;
|
||||
this.heapDumpDurationMs = builder.heapDumpDurationMs;
|
||||
this.reachabilityInspectorClasses = builder.reachabilityInspectorClasses;
|
||||
}
|
||||
}
|
16
sources/com/squareup/leakcanary/HeapDumper.java
Normal file
16
sources/com/squareup/leakcanary/HeapDumper.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public interface HeapDumper {
|
||||
public static final HeapDumper NONE = new HeapDumper() { // from class: com.squareup.leakcanary.HeapDumper.1
|
||||
@Override // com.squareup.leakcanary.HeapDumper
|
||||
public File dumpHeap() {
|
||||
return HeapDumper.RETRY_LATER;
|
||||
}
|
||||
};
|
||||
public static final File RETRY_LATER = null;
|
||||
|
||||
File dumpHeap();
|
||||
}
|
17
sources/com/squareup/leakcanary/KeyedWeakReference.java
Normal file
17
sources/com/squareup/leakcanary/KeyedWeakReference.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import com.unity3d.ads.metadata.MediationMetaData;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
final class KeyedWeakReference extends WeakReference<Object> {
|
||||
public final String key;
|
||||
public final String name;
|
||||
|
||||
KeyedWeakReference(Object obj, String str, String str2, ReferenceQueue<Object> referenceQueue) {
|
||||
super(Preconditions.checkNotNull(obj, "referent"), (ReferenceQueue) Preconditions.checkNotNull(referenceQueue, "referenceQueue"));
|
||||
this.key = (String) Preconditions.checkNotNull(str, "key");
|
||||
this.name = (String) Preconditions.checkNotNull(str2, MediationMetaData.KEY_NAME);
|
||||
}
|
||||
}
|
91
sources/com/squareup/leakcanary/LeakCanary.java
Normal file
91
sources/com/squareup/leakcanary/LeakCanary.java
Normal file
@@ -0,0 +1,91 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.text.format.Formatter;
|
||||
import android.util.Log;
|
||||
import com.squareup.leakcanary.internal.DisplayLeakActivity;
|
||||
import com.squareup.leakcanary.internal.HeapAnalyzerService;
|
||||
import com.squareup.leakcanary.internal.LeakCanaryInternals;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class LeakCanary {
|
||||
private LeakCanary() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static void enableDisplayLeakActivity(Context context) {
|
||||
LeakCanaryInternals.setEnabledBlocking(context, DisplayLeakActivity.class, true);
|
||||
}
|
||||
|
||||
public static RefWatcher install(Application application) {
|
||||
return refWatcher(application).listenerServiceClass(DisplayLeakService.class).excludedRefs(AndroidExcludedRefs.createAppDefaults().build()).buildAndInstall();
|
||||
}
|
||||
|
||||
public static RefWatcher installedRefWatcher() {
|
||||
RefWatcher refWatcher = LeakCanaryInternals.installedRefWatcher;
|
||||
return refWatcher == null ? RefWatcher.DISABLED : refWatcher;
|
||||
}
|
||||
|
||||
public static boolean isInAnalyzerProcess(Context context) {
|
||||
Boolean bool = LeakCanaryInternals.isInAnalyzerProcess;
|
||||
if (bool == null) {
|
||||
bool = Boolean.valueOf(LeakCanaryInternals.isInServiceProcess(context, HeapAnalyzerService.class));
|
||||
LeakCanaryInternals.isInAnalyzerProcess = bool;
|
||||
}
|
||||
return bool.booleanValue();
|
||||
}
|
||||
|
||||
public static String leakInfo(Context context, HeapDump heapDump, AnalysisResult analysisResult, boolean z) {
|
||||
String str;
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
String packageName = context.getPackageName();
|
||||
try {
|
||||
PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
|
||||
String str2 = "In " + packageName + ":" + packageInfo.versionName + ":" + packageInfo.versionCode + ".\n";
|
||||
String str3 = "";
|
||||
if (analysisResult.leakFound) {
|
||||
if (analysisResult.excludedLeak) {
|
||||
str2 = str2 + "* EXCLUDED LEAK.\n";
|
||||
}
|
||||
String str4 = str2 + "* " + analysisResult.className;
|
||||
if (!heapDump.referenceName.equals("")) {
|
||||
str4 = str4 + " (" + heapDump.referenceName + ")";
|
||||
}
|
||||
str = str4 + " has leaked:\n" + analysisResult.leakTrace.toString() + "\n";
|
||||
if (analysisResult.retainedHeapSize != -1) {
|
||||
str = str + "* Retaining: " + Formatter.formatShortFileSize(context, analysisResult.retainedHeapSize) + ".\n";
|
||||
}
|
||||
if (z) {
|
||||
str3 = "\n* Details:\n" + analysisResult.leakTrace.toDetailedString();
|
||||
}
|
||||
} else if (analysisResult.failure != null) {
|
||||
str = str2 + "* FAILURE in 1.6.3 31007b4:" + Log.getStackTraceString(analysisResult.failure) + "\n";
|
||||
} else {
|
||||
str = str2 + "* NO LEAK FOUND.\n\n";
|
||||
}
|
||||
if (z) {
|
||||
str3 = str3 + "* Excluded Refs:\n" + heapDump.excludedRefs;
|
||||
}
|
||||
return str + "* Reference Key: " + heapDump.referenceKey + "\n* Device: " + Build.MANUFACTURER + " " + Build.BRAND + " " + Build.MODEL + " " + Build.PRODUCT + "\n* Android Version: " + Build.VERSION.RELEASE + " API: " + Build.VERSION.SDK_INT + " LeakCanary: " + BuildConfig.LIBRARY_VERSION + " " + BuildConfig.GIT_SHA + "\n* Durations: watch=" + heapDump.watchDurationMs + "ms, gc=" + heapDump.gcDurationMs + "ms, heap dump=" + heapDump.heapDumpDurationMs + "ms, analysis=" + analysisResult.analysisDurationMs + "ms\n" + str3;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static AndroidRefWatcherBuilder refWatcher(Context context) {
|
||||
return new AndroidRefWatcherBuilder(context);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void setDisplayLeakActivityDirectoryProvider(LeakDirectoryProvider leakDirectoryProvider) {
|
||||
setLeakDirectoryProvider(leakDirectoryProvider);
|
||||
}
|
||||
|
||||
public static void setLeakDirectoryProvider(LeakDirectoryProvider leakDirectoryProvider) {
|
||||
LeakCanaryInternals.setLeakDirectoryProvider(leakDirectoryProvider);
|
||||
}
|
||||
}
|
14
sources/com/squareup/leakcanary/LeakDirectoryProvider.java
Normal file
14
sources/com/squareup/leakcanary/LeakDirectoryProvider.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.List;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public interface LeakDirectoryProvider {
|
||||
void clearLeakDirectory();
|
||||
|
||||
List<File> listFiles(FilenameFilter filenameFilter);
|
||||
|
||||
File newHeapDumpFile();
|
||||
}
|
18
sources/com/squareup/leakcanary/LeakNode.java
Normal file
18
sources/com/squareup/leakcanary/LeakNode.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import com.squareup.haha.perflib.Instance;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
final class LeakNode {
|
||||
final Exclusion exclusion;
|
||||
final Instance instance;
|
||||
final LeakReference leakReference;
|
||||
final LeakNode parent;
|
||||
|
||||
LeakNode(Exclusion exclusion, Instance instance, LeakNode leakNode, LeakReference leakReference) {
|
||||
this.exclusion = exclusion;
|
||||
this.instance = instance;
|
||||
this.parent = leakNode;
|
||||
this.leakReference = leakReference;
|
||||
}
|
||||
}
|
71
sources/com/squareup/leakcanary/LeakReference.java
Normal file
71
sources/com/squareup/leakcanary/LeakReference.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import com.squareup.leakcanary.LeakTraceElement;
|
||||
import java.io.Serializable;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class LeakReference implements Serializable {
|
||||
public final String name;
|
||||
public final LeakTraceElement.Type type;
|
||||
public final String value;
|
||||
|
||||
/* renamed from: com.squareup.leakcanary.LeakReference$1, reason: invalid class name */
|
||||
static /* synthetic */ class AnonymousClass1 {
|
||||
static final /* synthetic */ int[] $SwitchMap$com$squareup$leakcanary$LeakTraceElement$Type = new int[LeakTraceElement.Type.values().length];
|
||||
|
||||
static {
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$LeakTraceElement$Type[LeakTraceElement.Type.ARRAY_ENTRY.ordinal()] = 1;
|
||||
} catch (NoSuchFieldError unused) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$LeakTraceElement$Type[LeakTraceElement.Type.STATIC_FIELD.ordinal()] = 2;
|
||||
} catch (NoSuchFieldError unused2) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$LeakTraceElement$Type[LeakTraceElement.Type.INSTANCE_FIELD.ordinal()] = 3;
|
||||
} catch (NoSuchFieldError unused3) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$LeakTraceElement$Type[LeakTraceElement.Type.LOCAL.ordinal()] = 4;
|
||||
} catch (NoSuchFieldError unused4) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LeakReference(LeakTraceElement.Type type, String str, String str2) {
|
||||
this.type = type;
|
||||
this.name = str;
|
||||
this.value = str2;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
int i = AnonymousClass1.$SwitchMap$com$squareup$leakcanary$LeakTraceElement$Type[this.type.ordinal()];
|
||||
if (i == 1) {
|
||||
return "[" + this.name + "]";
|
||||
}
|
||||
if (i == 2 || i == 3) {
|
||||
return this.name;
|
||||
}
|
||||
if (i == 4) {
|
||||
return "<Java Local>";
|
||||
}
|
||||
throw new IllegalStateException("Unexpected type " + this.type + " name = " + this.name + " value = " + this.value);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
int i = AnonymousClass1.$SwitchMap$com$squareup$leakcanary$LeakTraceElement$Type[this.type.ordinal()];
|
||||
if (i != 1) {
|
||||
if (i == 2) {
|
||||
return "static " + getDisplayName() + " = " + this.value;
|
||||
}
|
||||
if (i != 3) {
|
||||
if (i == 4) {
|
||||
return getDisplayName();
|
||||
}
|
||||
throw new IllegalStateException("Unexpected type " + this.type + " name = " + this.name + " value = " + this.value);
|
||||
}
|
||||
}
|
||||
return getDisplayName() + " = " + this.value;
|
||||
}
|
||||
}
|
44
sources/com/squareup/leakcanary/LeakTrace.java
Normal file
44
sources/com/squareup/leakcanary/LeakTrace.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class LeakTrace implements Serializable {
|
||||
public final List<LeakTraceElement> elements;
|
||||
public final List<Reachability> expectedReachability;
|
||||
|
||||
LeakTrace(List<LeakTraceElement> list, List<Reachability> list2) {
|
||||
this.elements = list;
|
||||
this.expectedReachability = list2;
|
||||
}
|
||||
|
||||
public String toDetailedString() {
|
||||
Iterator<LeakTraceElement> it = this.elements.iterator();
|
||||
String str = "";
|
||||
while (it.hasNext()) {
|
||||
str = str + it.next().toDetailedString();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < this.elements.size(); i++) {
|
||||
LeakTraceElement leakTraceElement = this.elements.get(i);
|
||||
sb.append("* ");
|
||||
if (i != 0) {
|
||||
sb.append("↳ ");
|
||||
}
|
||||
Reachability reachability = this.expectedReachability.get(i);
|
||||
boolean z = true;
|
||||
if (reachability != Reachability.UNKNOWN && (reachability != Reachability.REACHABLE || (i < this.elements.size() - 1 && this.expectedReachability.get(i + 1) == Reachability.REACHABLE))) {
|
||||
z = false;
|
||||
}
|
||||
sb.append(leakTraceElement.toString(z));
|
||||
sb.append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
138
sources/com/squareup/leakcanary/LeakTraceElement.java
Normal file
138
sources/com/squareup/leakcanary/LeakTraceElement.java
Normal file
@@ -0,0 +1,138 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class LeakTraceElement implements Serializable {
|
||||
public final List<String> classHierarchy;
|
||||
public final String className;
|
||||
public final Exclusion exclusion;
|
||||
public final String extra;
|
||||
public final List<LeakReference> fieldReferences;
|
||||
|
||||
@Deprecated
|
||||
public final List<String> fields;
|
||||
public final Holder holder;
|
||||
public final LeakReference reference;
|
||||
|
||||
@Deprecated
|
||||
public final String referenceName;
|
||||
|
||||
@Deprecated
|
||||
public final Type type;
|
||||
|
||||
public enum Holder {
|
||||
OBJECT,
|
||||
CLASS,
|
||||
THREAD,
|
||||
ARRAY
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
INSTANCE_FIELD,
|
||||
STATIC_FIELD,
|
||||
LOCAL,
|
||||
ARRAY_ENTRY
|
||||
}
|
||||
|
||||
LeakTraceElement(LeakReference leakReference, Holder holder, List<String> list, String str, Exclusion exclusion, List<LeakReference> list2) {
|
||||
this.reference = leakReference;
|
||||
this.referenceName = leakReference == null ? null : leakReference.getDisplayName();
|
||||
this.type = leakReference != null ? leakReference.type : null;
|
||||
this.holder = holder;
|
||||
this.classHierarchy = Collections.unmodifiableList(new ArrayList(list));
|
||||
this.className = list.get(0);
|
||||
this.extra = str;
|
||||
this.exclusion = exclusion;
|
||||
this.fieldReferences = Collections.unmodifiableList(new ArrayList(list2));
|
||||
ArrayList arrayList = new ArrayList();
|
||||
Iterator<LeakReference> it = list2.iterator();
|
||||
while (it.hasNext()) {
|
||||
arrayList.add(it.next().toString());
|
||||
}
|
||||
this.fields = Collections.unmodifiableList(arrayList);
|
||||
}
|
||||
|
||||
public String getFieldReferenceValue(String str) {
|
||||
for (LeakReference leakReference : this.fieldReferences) {
|
||||
if (leakReference.name.equals(str)) {
|
||||
return leakReference.value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getSimpleClassName() {
|
||||
int lastIndexOf = this.className.lastIndexOf(46);
|
||||
return lastIndexOf == -1 ? this.className : this.className.substring(lastIndexOf + 1);
|
||||
}
|
||||
|
||||
public boolean isInstanceOf(Class<?> cls) {
|
||||
return isInstanceOf(cls.getName());
|
||||
}
|
||||
|
||||
public String toDetailedString() {
|
||||
String str;
|
||||
Holder holder = this.holder;
|
||||
if (holder == Holder.ARRAY) {
|
||||
str = "* Array of";
|
||||
} else if (holder == Holder.CLASS) {
|
||||
str = "* Class";
|
||||
} else {
|
||||
str = "* Instance of";
|
||||
}
|
||||
String str2 = str + " " + this.className + "\n";
|
||||
Iterator<LeakReference> it = this.fieldReferences.iterator();
|
||||
while (it.hasNext()) {
|
||||
str2 = str2 + "| " + it.next() + "\n";
|
||||
}
|
||||
return str2;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return toString(false);
|
||||
}
|
||||
|
||||
public boolean isInstanceOf(String str) {
|
||||
Iterator<String> it = this.classHierarchy.iterator();
|
||||
while (it.hasNext()) {
|
||||
if (it.next().equals(str)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toString(boolean z) {
|
||||
LeakReference leakReference = this.reference;
|
||||
String str = "";
|
||||
if (leakReference != null && leakReference.type == Type.STATIC_FIELD) {
|
||||
str = "static ";
|
||||
}
|
||||
Holder holder = this.holder;
|
||||
if (holder == Holder.ARRAY || holder == Holder.THREAD) {
|
||||
str = str + this.holder.name().toLowerCase(Locale.US) + " ";
|
||||
}
|
||||
String str2 = str + getSimpleClassName();
|
||||
LeakReference leakReference2 = this.reference;
|
||||
if (leakReference2 != null) {
|
||||
String displayName = leakReference2.getDisplayName();
|
||||
if (z) {
|
||||
displayName = "!(" + displayName + ")!";
|
||||
}
|
||||
str2 = str2 + "." + displayName;
|
||||
}
|
||||
if (this.extra != null) {
|
||||
str2 = str2 + " " + this.extra;
|
||||
}
|
||||
if (this.exclusion == null) {
|
||||
return str2;
|
||||
}
|
||||
return str2 + " , matching exclusion " + this.exclusion.matching;
|
||||
}
|
||||
}
|
15
sources/com/squareup/leakcanary/Preconditions.java
Normal file
15
sources/com/squareup/leakcanary/Preconditions.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
final class Preconditions {
|
||||
private Preconditions() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
static <T> T checkNotNull(T t, String str) {
|
||||
if (t != null) {
|
||||
return t;
|
||||
}
|
||||
throw new NullPointerException(str + " must not be null");
|
||||
}
|
||||
}
|
271
sources/com/squareup/leakcanary/R.java
Normal file
271
sources/com/squareup/leakcanary/R.java
Normal file
@@ -0,0 +1,271 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class R {
|
||||
|
||||
public static final class attr {
|
||||
public static final int font = 0x7f0400fb;
|
||||
public static final int fontProviderAuthority = 0x7f0400fd;
|
||||
public static final int fontProviderCerts = 0x7f0400fe;
|
||||
public static final int fontProviderFetchStrategy = 0x7f0400ff;
|
||||
public static final int fontProviderFetchTimeout = 0x7f040100;
|
||||
public static final int fontProviderPackage = 0x7f040101;
|
||||
public static final int fontProviderQuery = 0x7f040102;
|
||||
public static final int fontStyle = 0x7f040103;
|
||||
public static final int fontWeight = 0x7f040105;
|
||||
public static final int leak_canary_plus_color = 0x7f040195;
|
||||
|
||||
private attr() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class bool {
|
||||
public static final int abc_action_bar_embed_tabs = 0x7f050000;
|
||||
|
||||
private bool() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class color {
|
||||
public static final int leak_canary_background_color = 0x7f0600e4;
|
||||
public static final int leak_canary_class_name = 0x7f0600e5;
|
||||
public static final int leak_canary_extra = 0x7f0600e6;
|
||||
public static final int leak_canary_help = 0x7f0600e7;
|
||||
public static final int leak_canary_icon_background = 0x7f0600e8;
|
||||
public static final int leak_canary_leak = 0x7f0600e9;
|
||||
public static final int leak_canary_reference = 0x7f0600ea;
|
||||
public static final int notification_action_color_filter = 0x7f060116;
|
||||
public static final int notification_icon_bg_color = 0x7f060117;
|
||||
public static final int ripple_material_light = 0x7f06012a;
|
||||
public static final int secondary_text_default_material_light = 0x7f06012c;
|
||||
|
||||
private color() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class dimen {
|
||||
public static final int compat_button_inset_horizontal_material = 0x7f07013f;
|
||||
public static final int compat_button_inset_vertical_material = 0x7f070140;
|
||||
public static final int compat_button_padding_horizontal_material = 0x7f070141;
|
||||
public static final int compat_button_padding_vertical_material = 0x7f070142;
|
||||
public static final int compat_control_corner_material = 0x7f070143;
|
||||
public static final int leak_canary_connector_center_y = 0x7f071275;
|
||||
public static final int leak_canary_connector_leak_dash_gap = 0x7f071276;
|
||||
public static final int leak_canary_connector_leak_dash_line = 0x7f071277;
|
||||
public static final int leak_canary_connector_stroke_size = 0x7f071278;
|
||||
public static final int leak_canary_connector_width = 0x7f071279;
|
||||
public static final int leak_canary_more_margin_top = 0x7f07127a;
|
||||
public static final int leak_canary_more_size = 0x7f07127b;
|
||||
public static final int leak_canary_more_stroke_width = 0x7f07127c;
|
||||
public static final int leak_canary_row_margins = 0x7f07127d;
|
||||
public static final int leak_canary_row_min = 0x7f07127e;
|
||||
public static final int leak_canary_row_title_margin_top = 0x7f07127f;
|
||||
public static final int leak_canary_squiggly_span_amplitude = 0x7f071280;
|
||||
public static final int leak_canary_squiggly_span_period_degrees = 0x7f071281;
|
||||
public static final int leak_canary_squiggly_span_stroke_width = 0x7f071282;
|
||||
public static final int notification_action_icon_size = 0x7f071aaa;
|
||||
public static final int notification_action_text_size = 0x7f071aab;
|
||||
public static final int notification_big_circle_margin = 0x7f071aac;
|
||||
public static final int notification_content_margin_start = 0x7f071aad;
|
||||
public static final int notification_large_icon_height = 0x7f071aae;
|
||||
public static final int notification_large_icon_width = 0x7f071aaf;
|
||||
public static final int notification_main_column_padding_top = 0x7f071ab0;
|
||||
public static final int notification_media_narrow_margin = 0x7f071ab1;
|
||||
public static final int notification_right_icon_size = 0x7f071ab2;
|
||||
public static final int notification_right_side_padding_top = 0x7f071ab3;
|
||||
public static final int notification_small_icon_background_padding = 0x7f071ab4;
|
||||
public static final int notification_small_icon_size_as_large = 0x7f071ab5;
|
||||
public static final int notification_subtext_size = 0x7f071ab6;
|
||||
public static final int notification_top_pad = 0x7f071ab7;
|
||||
public static final int notification_top_pad_large_text = 0x7f071ab8;
|
||||
|
||||
private dimen() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class drawable {
|
||||
public static final int leak_canary_icon_foreground = 0x7f0802fb;
|
||||
public static final int leak_canary_notification = 0x7f0802fc;
|
||||
public static final int leak_canary_toast_background = 0x7f0802fd;
|
||||
public static final int notification_action_background = 0x7f080330;
|
||||
public static final int notification_bg = 0x7f080331;
|
||||
public static final int notification_bg_low = 0x7f080332;
|
||||
public static final int notification_bg_low_normal = 0x7f080333;
|
||||
public static final int notification_bg_low_pressed = 0x7f080334;
|
||||
public static final int notification_bg_normal = 0x7f080335;
|
||||
public static final int notification_bg_normal_pressed = 0x7f080336;
|
||||
public static final int notification_icon_background = 0x7f080337;
|
||||
public static final int notification_template_icon_bg = 0x7f080338;
|
||||
public static final int notification_template_icon_low_bg = 0x7f080339;
|
||||
public static final int notification_tile_bg = 0x7f08033a;
|
||||
public static final int notify_panel_notification_icon_bg = 0x7f08033b;
|
||||
|
||||
private drawable() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class id {
|
||||
public static final int action_container = 0x7f090017;
|
||||
public static final int action_divider = 0x7f090019;
|
||||
public static final int action_image = 0x7f09001a;
|
||||
public static final int action_text = 0x7f090020;
|
||||
public static final int actions = 0x7f090021;
|
||||
public static final int async = 0x7f090046;
|
||||
public static final int blocking = 0x7f090057;
|
||||
public static final int chronometer = 0x7f0900c4;
|
||||
public static final int forever = 0x7f090184;
|
||||
public static final int icon = 0x7f0901bd;
|
||||
public static final int icon_group = 0x7f0901be;
|
||||
public static final int info = 0x7f0902cb;
|
||||
public static final int italic = 0x7f0902d4;
|
||||
public static final int leak_canary_action = 0x7f090325;
|
||||
public static final int leak_canary_display_leak_failure = 0x7f090326;
|
||||
public static final int leak_canary_display_leak_list = 0x7f090327;
|
||||
public static final int leak_canary_row_connector = 0x7f090328;
|
||||
public static final int leak_canary_row_details = 0x7f090329;
|
||||
public static final int leak_canary_row_layout = 0x7f09032a;
|
||||
public static final int leak_canary_row_more = 0x7f09032b;
|
||||
public static final int leak_canary_row_text = 0x7f09032c;
|
||||
public static final int leak_canary_row_time = 0x7f09032d;
|
||||
public static final int leak_canary_row_title = 0x7f09032e;
|
||||
public static final int line1 = 0x7f09033f;
|
||||
public static final int line3 = 0x7f090340;
|
||||
public static final int normal = 0x7f0903aa;
|
||||
public static final int notification_background = 0x7f0903ab;
|
||||
public static final int notification_main_column = 0x7f0903ac;
|
||||
public static final int notification_main_column_container = 0x7f0903ad;
|
||||
public static final int right_icon = 0x7f09041c;
|
||||
public static final int right_side = 0x7f09041d;
|
||||
public static final int text = 0x7f090501;
|
||||
public static final int text2 = 0x7f090502;
|
||||
public static final int time = 0x7f09050f;
|
||||
public static final int title = 0x7f090513;
|
||||
|
||||
private id() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class integer {
|
||||
public static final int status_bar_notification_info_maxnum = 0x7f0a0011;
|
||||
|
||||
private integer() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class layout {
|
||||
public static final int leak_canary_display_leak = 0x7f0c0199;
|
||||
public static final int leak_canary_heap_dump_toast = 0x7f0c019a;
|
||||
public static final int leak_canary_leak_row = 0x7f0c019b;
|
||||
public static final int leak_canary_ref_row = 0x7f0c019c;
|
||||
public static final int leak_canary_ref_top_row = 0x7f0c019d;
|
||||
public static final int notification_action = 0x7f0c01a8;
|
||||
public static final int notification_action_tombstone = 0x7f0c01a9;
|
||||
public static final int notification_template_custom_big = 0x7f0c01b0;
|
||||
public static final int notification_template_icon_group = 0x7f0c01b1;
|
||||
public static final int notification_template_part_chronometer = 0x7f0c01b5;
|
||||
public static final int notification_template_part_time = 0x7f0c01b6;
|
||||
|
||||
private layout() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class mipmap {
|
||||
public static final int leak_canary_icon = 0x7f0e008a;
|
||||
|
||||
private mipmap() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class string {
|
||||
public static final int leak_canary_analysis_failed = 0x7f110281;
|
||||
public static final int leak_canary_class_has_leaked = 0x7f110282;
|
||||
public static final int leak_canary_class_has_leaked_retaining = 0x7f110283;
|
||||
public static final int leak_canary_class_no_leak = 0x7f110284;
|
||||
public static final int leak_canary_could_not_save_text = 0x7f110285;
|
||||
public static final int leak_canary_delete = 0x7f110286;
|
||||
public static final int leak_canary_delete_all = 0x7f110287;
|
||||
public static final int leak_canary_delete_all_leaks_title = 0x7f110288;
|
||||
public static final int leak_canary_display_activity_label = 0x7f110289;
|
||||
public static final int leak_canary_download_dump = 0x7f11028a;
|
||||
public static final int leak_canary_excluded_row = 0x7f11028b;
|
||||
public static final int leak_canary_failure_report = 0x7f11028c;
|
||||
public static final int leak_canary_help_detail = 0x7f11028d;
|
||||
public static final int leak_canary_help_title = 0x7f11028e;
|
||||
public static final int leak_canary_leak_excluded = 0x7f11028f;
|
||||
public static final int leak_canary_leak_excluded_retaining = 0x7f110290;
|
||||
public static final int leak_canary_leak_list_title = 0x7f110291;
|
||||
public static final int leak_canary_no_leak_details = 0x7f110292;
|
||||
public static final int leak_canary_notification_analysing = 0x7f110293;
|
||||
public static final int leak_canary_notification_channel = 0x7f110294;
|
||||
public static final int leak_canary_notification_dumping = 0x7f110295;
|
||||
public static final int leak_canary_notification_foreground_text = 0x7f110296;
|
||||
public static final int leak_canary_notification_message = 0x7f110297;
|
||||
public static final int leak_canary_notification_reporting = 0x7f110298;
|
||||
public static final int leak_canary_permission_not_granted = 0x7f110299;
|
||||
public static final int leak_canary_permission_notification_text = 0x7f11029a;
|
||||
public static final int leak_canary_permission_notification_title = 0x7f11029b;
|
||||
public static final int leak_canary_result_failure_no_disk_space = 0x7f11029c;
|
||||
public static final int leak_canary_result_failure_no_file = 0x7f11029d;
|
||||
public static final int leak_canary_result_failure_title = 0x7f11029e;
|
||||
public static final int leak_canary_share_heap_dump = 0x7f11029f;
|
||||
public static final int leak_canary_share_leak = 0x7f1102a0;
|
||||
public static final int leak_canary_share_with = 0x7f1102a1;
|
||||
public static final int leak_canary_storage_permission_activity_label = 0x7f1102a2;
|
||||
public static final int leak_canary_toast_heap_dump = 0x7f1102a3;
|
||||
public static final int status_bar_notification_info_overflow = 0x7f110427;
|
||||
|
||||
private string() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class style {
|
||||
public static final int TextAppearance_Compat_Notification = 0x7f12013b;
|
||||
public static final int TextAppearance_Compat_Notification_Info = 0x7f12013c;
|
||||
public static final int TextAppearance_Compat_Notification_Line2 = 0x7f12013e;
|
||||
public static final int TextAppearance_Compat_Notification_Time = 0x7f120141;
|
||||
public static final int TextAppearance_Compat_Notification_Title = 0x7f120143;
|
||||
public static final int Widget_Compat_NotificationActionContainer = 0x7f1201ee;
|
||||
public static final int Widget_Compat_NotificationActionText = 0x7f1201ef;
|
||||
public static final int leak_canary_LeakCanary_Base = 0x7f120267;
|
||||
public static final int leak_canary_Theme_Transparent = 0x7f120268;
|
||||
|
||||
private style() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class styleable {
|
||||
public static final int FontFamilyFont_android_font = 0x00000000;
|
||||
public static final int FontFamilyFont_android_fontStyle = 0x00000002;
|
||||
public static final int FontFamilyFont_android_fontVariationSettings = 0x00000004;
|
||||
public static final int FontFamilyFont_android_fontWeight = 0x00000001;
|
||||
public static final int FontFamilyFont_android_ttcIndex = 0x00000003;
|
||||
public static final int FontFamilyFont_font = 0x00000005;
|
||||
public static final int FontFamilyFont_fontStyle = 0x00000006;
|
||||
public static final int FontFamilyFont_fontVariationSettings = 0x00000007;
|
||||
public static final int FontFamilyFont_fontWeight = 0x00000008;
|
||||
public static final int FontFamilyFont_ttcIndex = 0x00000009;
|
||||
public static final int FontFamily_fontProviderAuthority = 0x00000000;
|
||||
public static final int FontFamily_fontProviderCerts = 0x00000001;
|
||||
public static final int FontFamily_fontProviderFetchStrategy = 0x00000002;
|
||||
public static final int FontFamily_fontProviderFetchTimeout = 0x00000003;
|
||||
public static final int FontFamily_fontProviderPackage = 0x00000004;
|
||||
public static final int FontFamily_fontProviderQuery = 0x00000005;
|
||||
public static final int leak_canary_MoreDetailsView_leak_canary_plus_color = 0;
|
||||
public static final int[] FontFamily = {com.ubt.jimu.R.attr.fontProviderAuthority, com.ubt.jimu.R.attr.fontProviderCerts, com.ubt.jimu.R.attr.fontProviderFetchStrategy, com.ubt.jimu.R.attr.fontProviderFetchTimeout, com.ubt.jimu.R.attr.fontProviderPackage, com.ubt.jimu.R.attr.fontProviderQuery};
|
||||
public static final int[] FontFamilyFont = {android.R.attr.font, android.R.attr.fontWeight, android.R.attr.fontStyle, android.R.attr.ttcIndex, android.R.attr.fontVariationSettings, com.ubt.jimu.R.attr.font, com.ubt.jimu.R.attr.fontStyle, com.ubt.jimu.R.attr.fontVariationSettings, com.ubt.jimu.R.attr.fontWeight, com.ubt.jimu.R.attr.ttcIndex};
|
||||
public static final int[] leak_canary_MoreDetailsView = {com.ubt.jimu.R.attr.leak_canary_plus_color};
|
||||
|
||||
private styleable() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class xml {
|
||||
public static final int leak_canary_file_paths = 0x7f150001;
|
||||
|
||||
private xml() {
|
||||
}
|
||||
}
|
||||
|
||||
private R() {
|
||||
}
|
||||
}
|
12
sources/com/squareup/leakcanary/Reachability.java
Normal file
12
sources/com/squareup/leakcanary/Reachability.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public enum Reachability {
|
||||
REACHABLE,
|
||||
UNREACHABLE,
|
||||
UNKNOWN;
|
||||
|
||||
public interface Inspector {
|
||||
Reachability expectedReachability(LeakTraceElement leakTraceElement);
|
||||
}
|
||||
}
|
114
sources/com/squareup/leakcanary/RefWatcher.java
Normal file
114
sources/com/squareup/leakcanary/RefWatcher.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import com.squareup.leakcanary.HeapDump;
|
||||
import com.squareup.leakcanary.Retryable;
|
||||
import java.io.File;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class RefWatcher {
|
||||
public static final RefWatcher DISABLED = new RefWatcherBuilder().build();
|
||||
private final DebuggerControl debuggerControl;
|
||||
private final GcTrigger gcTrigger;
|
||||
private final HeapDump.Builder heapDumpBuilder;
|
||||
private final HeapDumper heapDumper;
|
||||
private final HeapDump.Listener heapdumpListener;
|
||||
private final WatchExecutor watchExecutor;
|
||||
private final Set<String> retainedKeys = new CopyOnWriteArraySet();
|
||||
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
|
||||
|
||||
RefWatcher(WatchExecutor watchExecutor, DebuggerControl debuggerControl, GcTrigger gcTrigger, HeapDumper heapDumper, HeapDump.Listener listener, HeapDump.Builder builder) {
|
||||
this.watchExecutor = (WatchExecutor) Preconditions.checkNotNull(watchExecutor, "watchExecutor");
|
||||
this.debuggerControl = (DebuggerControl) Preconditions.checkNotNull(debuggerControl, "debuggerControl");
|
||||
this.gcTrigger = (GcTrigger) Preconditions.checkNotNull(gcTrigger, "gcTrigger");
|
||||
this.heapDumper = (HeapDumper) Preconditions.checkNotNull(heapDumper, "heapDumper");
|
||||
this.heapdumpListener = (HeapDump.Listener) Preconditions.checkNotNull(listener, "heapdumpListener");
|
||||
this.heapDumpBuilder = builder;
|
||||
}
|
||||
|
||||
private void ensureGoneAsync(final long j, final KeyedWeakReference keyedWeakReference) {
|
||||
this.watchExecutor.execute(new Retryable() { // from class: com.squareup.leakcanary.RefWatcher.1
|
||||
@Override // com.squareup.leakcanary.Retryable
|
||||
public Retryable.Result run() {
|
||||
return RefWatcher.this.ensureGone(keyedWeakReference, j);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean gone(KeyedWeakReference keyedWeakReference) {
|
||||
return !this.retainedKeys.contains(keyedWeakReference.key);
|
||||
}
|
||||
|
||||
private void removeWeaklyReachableReferences() {
|
||||
while (true) {
|
||||
KeyedWeakReference keyedWeakReference = (KeyedWeakReference) this.queue.poll();
|
||||
if (keyedWeakReference == null) {
|
||||
return;
|
||||
} else {
|
||||
this.retainedKeys.remove(keyedWeakReference.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clearWatchedReferences() {
|
||||
this.retainedKeys.clear();
|
||||
}
|
||||
|
||||
Retryable.Result ensureGone(KeyedWeakReference keyedWeakReference, long j) {
|
||||
long nanoTime = System.nanoTime();
|
||||
long millis = TimeUnit.NANOSECONDS.toMillis(nanoTime - j);
|
||||
removeWeaklyReachableReferences();
|
||||
if (this.debuggerControl.isDebuggerAttached()) {
|
||||
return Retryable.Result.RETRY;
|
||||
}
|
||||
if (gone(keyedWeakReference)) {
|
||||
return Retryable.Result.DONE;
|
||||
}
|
||||
this.gcTrigger.runGc();
|
||||
removeWeaklyReachableReferences();
|
||||
if (!gone(keyedWeakReference)) {
|
||||
long nanoTime2 = System.nanoTime();
|
||||
long millis2 = TimeUnit.NANOSECONDS.toMillis(nanoTime2 - nanoTime);
|
||||
File dumpHeap = this.heapDumper.dumpHeap();
|
||||
if (dumpHeap == HeapDumper.RETRY_LATER) {
|
||||
return Retryable.Result.RETRY;
|
||||
}
|
||||
this.heapdumpListener.analyze(this.heapDumpBuilder.heapDumpFile(dumpHeap).referenceKey(keyedWeakReference.key).referenceName(keyedWeakReference.name).watchDurationMs(millis).gcDurationMs(millis2).heapDumpDurationMs(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - nanoTime2)).build());
|
||||
}
|
||||
return Retryable.Result.DONE;
|
||||
}
|
||||
|
||||
HeapDump.Builder getHeapDumpBuilder() {
|
||||
return this.heapDumpBuilder;
|
||||
}
|
||||
|
||||
Set<String> getRetainedKeys() {
|
||||
return new HashSet(this.retainedKeys);
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
removeWeaklyReachableReferences();
|
||||
return this.retainedKeys.isEmpty();
|
||||
}
|
||||
|
||||
public void watch(Object obj) {
|
||||
watch(obj, "");
|
||||
}
|
||||
|
||||
public void watch(Object obj, String str) {
|
||||
if (this == DISABLED) {
|
||||
return;
|
||||
}
|
||||
Preconditions.checkNotNull(obj, "watchedReference");
|
||||
Preconditions.checkNotNull(str, "referenceName");
|
||||
long nanoTime = System.nanoTime();
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
this.retainedKeys.add(uuid);
|
||||
ensureGoneAsync(nanoTime, new KeyedWeakReference(obj, uuid, str, this.queue));
|
||||
}
|
||||
}
|
133
sources/com/squareup/leakcanary/RefWatcherBuilder.java
Normal file
133
sources/com/squareup/leakcanary/RefWatcherBuilder.java
Normal file
@@ -0,0 +1,133 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import com.squareup.leakcanary.HeapDump;
|
||||
import com.squareup.leakcanary.Reachability;
|
||||
import com.squareup.leakcanary.RefWatcherBuilder;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public class RefWatcherBuilder<T extends RefWatcherBuilder<T>> {
|
||||
private DebuggerControl debuggerControl;
|
||||
private GcTrigger gcTrigger;
|
||||
private final HeapDump.Builder heapDumpBuilder = new HeapDump.Builder();
|
||||
private HeapDump.Listener heapDumpListener;
|
||||
private HeapDumper heapDumper;
|
||||
private WatchExecutor watchExecutor;
|
||||
|
||||
public final RefWatcher build() {
|
||||
if (isDisabled()) {
|
||||
return RefWatcher.DISABLED;
|
||||
}
|
||||
HeapDump.Builder builder = this.heapDumpBuilder;
|
||||
if (builder.excludedRefs == null) {
|
||||
builder.excludedRefs(defaultExcludedRefs());
|
||||
}
|
||||
HeapDump.Listener listener = this.heapDumpListener;
|
||||
if (listener == null) {
|
||||
listener = defaultHeapDumpListener();
|
||||
}
|
||||
HeapDump.Listener listener2 = listener;
|
||||
DebuggerControl debuggerControl = this.debuggerControl;
|
||||
if (debuggerControl == null) {
|
||||
debuggerControl = defaultDebuggerControl();
|
||||
}
|
||||
DebuggerControl debuggerControl2 = debuggerControl;
|
||||
HeapDumper heapDumper = this.heapDumper;
|
||||
if (heapDumper == null) {
|
||||
heapDumper = defaultHeapDumper();
|
||||
}
|
||||
HeapDumper heapDumper2 = heapDumper;
|
||||
WatchExecutor watchExecutor = this.watchExecutor;
|
||||
if (watchExecutor == null) {
|
||||
watchExecutor = defaultWatchExecutor();
|
||||
}
|
||||
WatchExecutor watchExecutor2 = watchExecutor;
|
||||
GcTrigger gcTrigger = this.gcTrigger;
|
||||
if (gcTrigger == null) {
|
||||
gcTrigger = defaultGcTrigger();
|
||||
}
|
||||
GcTrigger gcTrigger2 = gcTrigger;
|
||||
HeapDump.Builder builder2 = this.heapDumpBuilder;
|
||||
if (builder2.reachabilityInspectorClasses == null) {
|
||||
builder2.reachabilityInspectorClasses(defaultReachabilityInspectorClasses());
|
||||
}
|
||||
return new RefWatcher(watchExecutor2, debuggerControl2, gcTrigger2, heapDumper2, listener2, this.heapDumpBuilder);
|
||||
}
|
||||
|
||||
public final T computeRetainedHeapSize(boolean z) {
|
||||
this.heapDumpBuilder.computeRetainedHeapSize(z);
|
||||
return self();
|
||||
}
|
||||
|
||||
public final T debuggerControl(DebuggerControl debuggerControl) {
|
||||
this.debuggerControl = debuggerControl;
|
||||
return self();
|
||||
}
|
||||
|
||||
protected DebuggerControl defaultDebuggerControl() {
|
||||
return DebuggerControl.NONE;
|
||||
}
|
||||
|
||||
protected ExcludedRefs defaultExcludedRefs() {
|
||||
return ExcludedRefs.builder().build();
|
||||
}
|
||||
|
||||
protected GcTrigger defaultGcTrigger() {
|
||||
return GcTrigger.DEFAULT;
|
||||
}
|
||||
|
||||
protected HeapDump.Listener defaultHeapDumpListener() {
|
||||
return HeapDump.Listener.NONE;
|
||||
}
|
||||
|
||||
protected HeapDumper defaultHeapDumper() {
|
||||
return HeapDumper.NONE;
|
||||
}
|
||||
|
||||
protected List<Class<? extends Reachability.Inspector>> defaultReachabilityInspectorClasses() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
protected WatchExecutor defaultWatchExecutor() {
|
||||
return WatchExecutor.NONE;
|
||||
}
|
||||
|
||||
public final T excludedRefs(ExcludedRefs excludedRefs) {
|
||||
this.heapDumpBuilder.excludedRefs(excludedRefs);
|
||||
return self();
|
||||
}
|
||||
|
||||
public final T gcTrigger(GcTrigger gcTrigger) {
|
||||
this.gcTrigger = gcTrigger;
|
||||
return self();
|
||||
}
|
||||
|
||||
public final T heapDumpListener(HeapDump.Listener listener) {
|
||||
this.heapDumpListener = listener;
|
||||
return self();
|
||||
}
|
||||
|
||||
public final T heapDumper(HeapDumper heapDumper) {
|
||||
this.heapDumper = heapDumper;
|
||||
return self();
|
||||
}
|
||||
|
||||
protected boolean isDisabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected final T self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public final T stethoscopeClasses(List<Class<? extends Reachability.Inspector>> list) {
|
||||
this.heapDumpBuilder.reachabilityInspectorClasses(list);
|
||||
return self();
|
||||
}
|
||||
|
||||
public final T watchExecutor(WatchExecutor watchExecutor) {
|
||||
this.watchExecutor = watchExecutor;
|
||||
return self();
|
||||
}
|
||||
}
|
12
sources/com/squareup/leakcanary/Retryable.java
Normal file
12
sources/com/squareup/leakcanary/Retryable.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public interface Retryable {
|
||||
|
||||
public enum Result {
|
||||
DONE,
|
||||
RETRY
|
||||
}
|
||||
|
||||
Result run();
|
||||
}
|
22
sources/com/squareup/leakcanary/ServiceHeapDumpListener.java
Normal file
22
sources/com/squareup/leakcanary/ServiceHeapDumpListener.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import android.content.Context;
|
||||
import com.squareup.leakcanary.HeapDump;
|
||||
import com.squareup.leakcanary.internal.HeapAnalyzerService;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class ServiceHeapDumpListener implements HeapDump.Listener {
|
||||
private final Context context;
|
||||
private final Class<? extends AbstractAnalysisResultService> listenerServiceClass;
|
||||
|
||||
public ServiceHeapDumpListener(Context context, Class<? extends AbstractAnalysisResultService> cls) {
|
||||
this.listenerServiceClass = (Class) Preconditions.checkNotNull(cls, "listenerServiceClass");
|
||||
this.context = ((Context) Preconditions.checkNotNull(context, "context")).getApplicationContext();
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.HeapDump.Listener
|
||||
public void analyze(HeapDump heapDump) {
|
||||
Preconditions.checkNotNull(heapDump, "heapDump");
|
||||
HeapAnalyzerService.runAnalysis(this.context, heapDump, this.listenerServiceClass);
|
||||
}
|
||||
}
|
382
sources/com/squareup/leakcanary/ShortestPathFinder.java
Normal file
382
sources/com/squareup/leakcanary/ShortestPathFinder.java
Normal file
@@ -0,0 +1,382 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import com.squareup.haha.perflib.ArrayInstance;
|
||||
import com.squareup.haha.perflib.ClassInstance;
|
||||
import com.squareup.haha.perflib.ClassObj;
|
||||
import com.squareup.haha.perflib.Field;
|
||||
import com.squareup.haha.perflib.HahaSpy;
|
||||
import com.squareup.haha.perflib.Instance;
|
||||
import com.squareup.haha.perflib.RootObj;
|
||||
import com.squareup.haha.perflib.RootType;
|
||||
import com.squareup.haha.perflib.Snapshot;
|
||||
import com.squareup.haha.perflib.Type;
|
||||
import com.squareup.leakcanary.LeakTraceElement;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
final class ShortestPathFinder {
|
||||
private boolean canIgnoreStrings;
|
||||
private final ExcludedRefs excludedRefs;
|
||||
private final Deque<LeakNode> toVisitQueue = new ArrayDeque();
|
||||
private final Deque<LeakNode> toVisitIfNoPathQueue = new ArrayDeque();
|
||||
private final LinkedHashSet<Instance> toVisitSet = new LinkedHashSet<>();
|
||||
private final LinkedHashSet<Instance> toVisitIfNoPathSet = new LinkedHashSet<>();
|
||||
private final LinkedHashSet<Instance> visitedSet = new LinkedHashSet<>();
|
||||
|
||||
/* renamed from: com.squareup.leakcanary.ShortestPathFinder$1, reason: invalid class name */
|
||||
static /* synthetic */ class AnonymousClass1 {
|
||||
static final /* synthetic */ int[] $SwitchMap$com$squareup$haha$perflib$RootType = new int[RootType.values().length];
|
||||
|
||||
static {
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.JAVA_LOCAL.ordinal()] = 1;
|
||||
} catch (NoSuchFieldError unused) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.INTERNED_STRING.ordinal()] = 2;
|
||||
} catch (NoSuchFieldError unused2) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.DEBUGGER.ordinal()] = 3;
|
||||
} catch (NoSuchFieldError unused3) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.INVALID_TYPE.ordinal()] = 4;
|
||||
} catch (NoSuchFieldError unused4) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.UNREACHABLE.ordinal()] = 5;
|
||||
} catch (NoSuchFieldError unused5) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.UNKNOWN.ordinal()] = 6;
|
||||
} catch (NoSuchFieldError unused6) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.FINALIZING.ordinal()] = 7;
|
||||
} catch (NoSuchFieldError unused7) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.SYSTEM_CLASS.ordinal()] = 8;
|
||||
} catch (NoSuchFieldError unused8) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.VM_INTERNAL.ordinal()] = 9;
|
||||
} catch (NoSuchFieldError unused9) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.NATIVE_LOCAL.ordinal()] = 10;
|
||||
} catch (NoSuchFieldError unused10) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.NATIVE_STATIC.ordinal()] = 11;
|
||||
} catch (NoSuchFieldError unused11) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.THREAD_BLOCK.ordinal()] = 12;
|
||||
} catch (NoSuchFieldError unused12) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.BUSY_MONITOR.ordinal()] = 13;
|
||||
} catch (NoSuchFieldError unused13) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.NATIVE_MONITOR.ordinal()] = 14;
|
||||
} catch (NoSuchFieldError unused14) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.REFERENCE_CLEANUP.ordinal()] = 15;
|
||||
} catch (NoSuchFieldError unused15) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.NATIVE_STACK.ordinal()] = 16;
|
||||
} catch (NoSuchFieldError unused16) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$haha$perflib$RootType[RootType.JAVA_STATIC.ordinal()] = 17;
|
||||
} catch (NoSuchFieldError unused17) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final class Result {
|
||||
final boolean excludingKnownLeaks;
|
||||
final LeakNode leakingNode;
|
||||
|
||||
Result(LeakNode leakNode, boolean z) {
|
||||
this.leakingNode = leakNode;
|
||||
this.excludingKnownLeaks = z;
|
||||
}
|
||||
}
|
||||
|
||||
ShortestPathFinder(ExcludedRefs excludedRefs) {
|
||||
this.excludedRefs = excludedRefs;
|
||||
}
|
||||
|
||||
private boolean checkSeen(LeakNode leakNode) {
|
||||
return !this.visitedSet.add(leakNode.instance);
|
||||
}
|
||||
|
||||
private void clearState() {
|
||||
this.toVisitQueue.clear();
|
||||
this.toVisitIfNoPathQueue.clear();
|
||||
this.toVisitSet.clear();
|
||||
this.toVisitIfNoPathSet.clear();
|
||||
this.visitedSet.clear();
|
||||
}
|
||||
|
||||
private void enqueue(Exclusion exclusion, LeakNode leakNode, Instance instance, LeakReference leakReference) {
|
||||
if (instance == null || HahaHelper.isPrimitiveOrWrapperArray(instance) || HahaHelper.isPrimitiveWrapper(instance) || this.toVisitSet.contains(instance)) {
|
||||
return;
|
||||
}
|
||||
boolean z = exclusion == null;
|
||||
if (z || !this.toVisitIfNoPathSet.contains(instance)) {
|
||||
if ((this.canIgnoreStrings && isString(instance)) || this.visitedSet.contains(instance)) {
|
||||
return;
|
||||
}
|
||||
LeakNode leakNode2 = new LeakNode(exclusion, instance, leakNode, leakReference);
|
||||
if (z) {
|
||||
this.toVisitSet.add(instance);
|
||||
this.toVisitQueue.add(leakNode2);
|
||||
} else {
|
||||
this.toVisitIfNoPathSet.add(instance);
|
||||
this.toVisitIfNoPathQueue.add(leakNode2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void enqueueGcRoots(Snapshot snapshot) {
|
||||
for (RootObj rootObj : HahaSpy.allGcRoots(snapshot)) {
|
||||
switch (AnonymousClass1.$SwitchMap$com$squareup$haha$perflib$RootType[rootObj.getRootType().ordinal()]) {
|
||||
case 1:
|
||||
Exclusion exclusion = this.excludedRefs.threadNames.get(HahaHelper.threadName(HahaSpy.allocatingThread(rootObj)));
|
||||
if (exclusion == null || !exclusion.alwaysExclude) {
|
||||
enqueue(exclusion, null, rootObj, null);
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
break;
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
case 16:
|
||||
case 17:
|
||||
enqueue(null, null, rootObj, null);
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown root type:" + rootObj.getRootType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isString(Instance instance) {
|
||||
return instance.getClassObj() != null && instance.getClassObj().getClassName().equals(String.class.getName());
|
||||
}
|
||||
|
||||
private void visitArrayInstance(LeakNode leakNode) {
|
||||
ArrayInstance arrayInstance = (ArrayInstance) leakNode.instance;
|
||||
if (arrayInstance.getArrayType() == Type.OBJECT) {
|
||||
Object[] values = arrayInstance.getValues();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
Instance instance = (Instance) values[i];
|
||||
enqueue(null, leakNode, instance, new LeakReference(LeakTraceElement.Type.ARRAY_ENTRY, Integer.toString(i), instance == null ? "null" : instance.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void visitClassInstance(LeakNode leakNode) {
|
||||
ClassInstance classInstance = (ClassInstance) leakNode.instance;
|
||||
LinkedHashMap linkedHashMap = new LinkedHashMap();
|
||||
Exclusion exclusion = null;
|
||||
for (ClassObj classObj = classInstance.getClassObj(); classObj != null; classObj = classObj.getSuperClassObj()) {
|
||||
Exclusion exclusion2 = this.excludedRefs.classNames.get(classObj.getClassName());
|
||||
if (exclusion2 != null && (exclusion == null || !exclusion.alwaysExclude)) {
|
||||
exclusion = exclusion2;
|
||||
}
|
||||
Map<String, Exclusion> map = this.excludedRefs.fieldNameByClassName.get(classObj.getClassName());
|
||||
if (map != null) {
|
||||
linkedHashMap.putAll(map);
|
||||
}
|
||||
}
|
||||
if (exclusion == null || !exclusion.alwaysExclude) {
|
||||
for (ClassInstance.FieldValue fieldValue : classInstance.getValues()) {
|
||||
Field field = fieldValue.getField();
|
||||
if (field.getType() == Type.OBJECT) {
|
||||
Instance instance = (Instance) fieldValue.getValue();
|
||||
String name = field.getName();
|
||||
Exclusion exclusion3 = (Exclusion) linkedHashMap.get(name);
|
||||
if (exclusion3 == null || (exclusion != null && (!exclusion3.alwaysExclude || exclusion.alwaysExclude))) {
|
||||
exclusion3 = exclusion;
|
||||
}
|
||||
enqueue(exclusion3, leakNode, instance, new LeakReference(LeakTraceElement.Type.INSTANCE_FIELD, name, fieldValue.getValue() == null ? "null" : fieldValue.getValue().toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void visitClassObj(LeakNode leakNode) {
|
||||
Exclusion exclusion;
|
||||
ClassObj classObj = (ClassObj) leakNode.instance;
|
||||
Map<String, Exclusion> map = this.excludedRefs.staticFieldNameByClassName.get(classObj.getClassName());
|
||||
for (Map.Entry<Field, Object> entry : classObj.getStaticFieldValues().entrySet()) {
|
||||
Field key = entry.getKey();
|
||||
if (key.getType() == Type.OBJECT) {
|
||||
String name = key.getName();
|
||||
if (!name.equals("$staticOverhead")) {
|
||||
Instance instance = (Instance) entry.getValue();
|
||||
boolean z = true;
|
||||
LeakReference leakReference = new LeakReference(LeakTraceElement.Type.STATIC_FIELD, name, entry.getValue() == null ? "null" : entry.getValue().toString());
|
||||
if (map != null && (exclusion = map.get(name)) != null) {
|
||||
z = false;
|
||||
if (!exclusion.alwaysExclude) {
|
||||
enqueue(exclusion, leakNode, instance, leakReference);
|
||||
}
|
||||
}
|
||||
if (z) {
|
||||
enqueue(null, leakNode, instance, leakReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void visitRootObj(LeakNode leakNode) {
|
||||
RootObj rootObj = (RootObj) leakNode.instance;
|
||||
Instance referredInstance = rootObj.getReferredInstance();
|
||||
if (rootObj.getRootType() != RootType.JAVA_LOCAL) {
|
||||
enqueue(null, leakNode, referredInstance, null);
|
||||
return;
|
||||
}
|
||||
Instance allocatingThread = HahaSpy.allocatingThread(rootObj);
|
||||
Exclusion exclusion = leakNode.exclusion;
|
||||
if (exclusion == null) {
|
||||
exclusion = null;
|
||||
}
|
||||
enqueue(exclusion, new LeakNode(null, allocatingThread, null, null), referredInstance, new LeakReference(LeakTraceElement.Type.LOCAL, null, null));
|
||||
}
|
||||
|
||||
/* JADX WARN: Code restructure failed: missing block: B:9:0x004e, code lost:
|
||||
|
||||
return new com.squareup.leakcanary.ShortestPathFinder.Result(r7, r2);
|
||||
*/
|
||||
/*
|
||||
Code decompiled incorrectly, please refer to instructions dump.
|
||||
To view partially-correct code enable 'Show inconsistent code' option in preferences
|
||||
*/
|
||||
com.squareup.leakcanary.ShortestPathFinder.Result findPath(com.squareup.haha.perflib.Snapshot r7, com.squareup.haha.perflib.Instance r8) {
|
||||
/*
|
||||
r6 = this;
|
||||
r6.clearState()
|
||||
boolean r0 = r6.isString(r8)
|
||||
r1 = 1
|
||||
r0 = r0 ^ r1
|
||||
r6.canIgnoreStrings = r0
|
||||
r6.enqueueGcRoots(r7)
|
||||
r7 = 0
|
||||
r0 = 0
|
||||
L10:
|
||||
java.util.Deque<com.squareup.leakcanary.LeakNode> r2 = r6.toVisitQueue
|
||||
boolean r2 = r2.isEmpty()
|
||||
if (r2 == 0) goto L24
|
||||
java.util.Deque<com.squareup.leakcanary.LeakNode> r2 = r6.toVisitIfNoPathQueue
|
||||
boolean r2 = r2.isEmpty()
|
||||
if (r2 != 0) goto L21
|
||||
goto L24
|
||||
L21:
|
||||
r2 = r7
|
||||
r7 = r0
|
||||
goto L49
|
||||
L24:
|
||||
java.util.Deque<com.squareup.leakcanary.LeakNode> r2 = r6.toVisitQueue
|
||||
boolean r2 = r2.isEmpty()
|
||||
if (r2 != 0) goto L38
|
||||
java.util.Deque<com.squareup.leakcanary.LeakNode> r2 = r6.toVisitQueue
|
||||
java.lang.Object r2 = r2.poll()
|
||||
com.squareup.leakcanary.LeakNode r2 = (com.squareup.leakcanary.LeakNode) r2
|
||||
r5 = r2
|
||||
r2 = r7
|
||||
r7 = r5
|
||||
goto L45
|
||||
L38:
|
||||
java.util.Deque<com.squareup.leakcanary.LeakNode> r7 = r6.toVisitIfNoPathQueue
|
||||
java.lang.Object r7 = r7.poll()
|
||||
com.squareup.leakcanary.LeakNode r7 = (com.squareup.leakcanary.LeakNode) r7
|
||||
com.squareup.leakcanary.Exclusion r2 = r7.exclusion
|
||||
if (r2 == 0) goto L92
|
||||
r2 = 1
|
||||
L45:
|
||||
com.squareup.haha.perflib.Instance r3 = r7.instance
|
||||
if (r3 != r8) goto L4f
|
||||
L49:
|
||||
com.squareup.leakcanary.ShortestPathFinder$Result r8 = new com.squareup.leakcanary.ShortestPathFinder$Result
|
||||
r8.<init>(r7, r2)
|
||||
return r8
|
||||
L4f:
|
||||
boolean r3 = r6.checkSeen(r7)
|
||||
if (r3 == 0) goto L56
|
||||
goto L77
|
||||
L56:
|
||||
com.squareup.haha.perflib.Instance r3 = r7.instance
|
||||
boolean r4 = r3 instanceof com.squareup.haha.perflib.RootObj
|
||||
if (r4 == 0) goto L60
|
||||
r6.visitRootObj(r7)
|
||||
goto L77
|
||||
L60:
|
||||
boolean r4 = r3 instanceof com.squareup.haha.perflib.ClassObj
|
||||
if (r4 == 0) goto L68
|
||||
r6.visitClassObj(r7)
|
||||
goto L77
|
||||
L68:
|
||||
boolean r4 = r3 instanceof com.squareup.haha.perflib.ClassInstance
|
||||
if (r4 == 0) goto L70
|
||||
r6.visitClassInstance(r7)
|
||||
goto L77
|
||||
L70:
|
||||
boolean r3 = r3 instanceof com.squareup.haha.perflib.ArrayInstance
|
||||
if (r3 == 0) goto L79
|
||||
r6.visitArrayInstance(r7)
|
||||
L77:
|
||||
r7 = r2
|
||||
goto L10
|
||||
L79:
|
||||
java.lang.IllegalStateException r8 = new java.lang.IllegalStateException
|
||||
java.lang.StringBuilder r0 = new java.lang.StringBuilder
|
||||
r0.<init>()
|
||||
java.lang.String r1 = "Unexpected type for "
|
||||
r0.append(r1)
|
||||
com.squareup.haha.perflib.Instance r7 = r7.instance
|
||||
r0.append(r7)
|
||||
java.lang.String r7 = r0.toString()
|
||||
r8.<init>(r7)
|
||||
throw r8
|
||||
L92:
|
||||
java.lang.IllegalStateException r8 = new java.lang.IllegalStateException
|
||||
java.lang.StringBuilder r0 = new java.lang.StringBuilder
|
||||
r0.<init>()
|
||||
java.lang.String r1 = "Expected node to have an exclusion "
|
||||
r0.append(r1)
|
||||
r0.append(r7)
|
||||
java.lang.String r7 = r0.toString()
|
||||
r8.<init>(r7)
|
||||
throw r8
|
||||
*/
|
||||
throw new UnsupportedOperationException("Method not decompiled: com.squareup.leakcanary.ShortestPathFinder.findPath(com.squareup.haha.perflib.Snapshot, com.squareup.haha.perflib.Instance):com.squareup.leakcanary.ShortestPathFinder$Result");
|
||||
}
|
||||
}
|
20
sources/com/squareup/leakcanary/TrackedReference.java
Normal file
20
sources/com/squareup/leakcanary/TrackedReference.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public class TrackedReference {
|
||||
public final String className;
|
||||
public final List<LeakReference> fields;
|
||||
public final String key;
|
||||
public final String name;
|
||||
|
||||
public TrackedReference(String str, String str2, String str3, List<LeakReference> list) {
|
||||
this.key = str;
|
||||
this.name = str2;
|
||||
this.className = str3;
|
||||
this.fields = Collections.unmodifiableList(new ArrayList(list));
|
||||
}
|
||||
}
|
12
sources/com/squareup/leakcanary/WatchExecutor.java
Normal file
12
sources/com/squareup/leakcanary/WatchExecutor.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.squareup.leakcanary;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public interface WatchExecutor {
|
||||
public static final WatchExecutor NONE = new WatchExecutor() { // from class: com.squareup.leakcanary.WatchExecutor.1
|
||||
@Override // com.squareup.leakcanary.WatchExecutor
|
||||
public void execute(Retryable retryable) {
|
||||
}
|
||||
};
|
||||
|
||||
void execute(Retryable retryable);
|
||||
}
|
7
sources/com/squareup/leakcanary/analyzer/R.java
Normal file
7
sources/com/squareup/leakcanary/analyzer/R.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package com.squareup.leakcanary.analyzer;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class R {
|
||||
private R() {
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.os.Bundle;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public abstract class ActivityLifecycleCallbacksAdapter implements Application.ActivityLifecycleCallbacks {
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityCreated(Activity activity, Bundle bundle) {
|
||||
}
|
||||
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
}
|
||||
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityPaused(Activity activity) {
|
||||
}
|
||||
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityResumed(Activity activity) {
|
||||
}
|
||||
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
|
||||
}
|
||||
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityStarted(Activity activity) {
|
||||
}
|
||||
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityStopped(Activity activity) {
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.view.View;
|
||||
import com.squareup.leakcanary.RefWatcher;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
class AndroidOFragmentRefWatcher implements FragmentRefWatcher {
|
||||
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks = new FragmentManager.FragmentLifecycleCallbacks() { // from class: com.squareup.leakcanary.internal.AndroidOFragmentRefWatcher.1
|
||||
@Override // android.app.FragmentManager.FragmentLifecycleCallbacks
|
||||
public void onFragmentDestroyed(FragmentManager fragmentManager, Fragment fragment) {
|
||||
AndroidOFragmentRefWatcher.this.refWatcher.watch(fragment);
|
||||
}
|
||||
|
||||
@Override // android.app.FragmentManager.FragmentLifecycleCallbacks
|
||||
public void onFragmentViewDestroyed(FragmentManager fragmentManager, Fragment fragment) {
|
||||
View view = fragment.getView();
|
||||
if (view != null) {
|
||||
AndroidOFragmentRefWatcher.this.refWatcher.watch(view);
|
||||
}
|
||||
}
|
||||
};
|
||||
private final RefWatcher refWatcher;
|
||||
|
||||
AndroidOFragmentRefWatcher(RefWatcher refWatcher) {
|
||||
this.refWatcher = refWatcher;
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.internal.FragmentRefWatcher
|
||||
public void watchFragments(Activity activity) {
|
||||
activity.getFragmentManager().registerFragmentLifecycleCallbacks(this.fragmentLifecycleCallbacks, true);
|
||||
}
|
||||
}
|
@@ -0,0 +1,470 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ActionBar;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Formatter;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import androidx.core.content.FileProvider;
|
||||
import com.squareup.leakcanary.AnalysisResult;
|
||||
import com.squareup.leakcanary.AnalyzedHeap;
|
||||
import com.squareup.leakcanary.BuildConfig;
|
||||
import com.squareup.leakcanary.CanaryLog;
|
||||
import com.squareup.leakcanary.HeapDump;
|
||||
import com.squareup.leakcanary.LeakCanary;
|
||||
import com.squareup.leakcanary.LeakDirectoryProvider;
|
||||
import com.squareup.leakcanary.R;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class DisplayLeakActivity extends Activity {
|
||||
private static final String SHOW_LEAK_EXTRA = "show_latest";
|
||||
private Button actionButton;
|
||||
private TextView failureView;
|
||||
List<AnalyzedHeap> leaks;
|
||||
private ListView listView;
|
||||
String visibleLeakRefKey;
|
||||
|
||||
class LeakListAdapter extends BaseAdapter {
|
||||
LeakListAdapter() {
|
||||
}
|
||||
|
||||
@Override // android.widget.Adapter
|
||||
public int getCount() {
|
||||
return DisplayLeakActivity.this.leaks.size();
|
||||
}
|
||||
|
||||
@Override // android.widget.Adapter
|
||||
public long getItemId(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override // android.widget.Adapter
|
||||
public View getView(int i, View view, ViewGroup viewGroup) {
|
||||
String str;
|
||||
String string;
|
||||
if (view == null) {
|
||||
view = LayoutInflater.from(DisplayLeakActivity.this).inflate(R.layout.leak_canary_leak_row, viewGroup, false);
|
||||
}
|
||||
TextView textView = (TextView) view.findViewById(R.id.leak_canary_row_text);
|
||||
TextView textView2 = (TextView) view.findViewById(R.id.leak_canary_row_time);
|
||||
AnalyzedHeap item = getItem(i);
|
||||
String str2 = (DisplayLeakActivity.this.leaks.size() - i) + ". ";
|
||||
AnalysisResult analysisResult = item.result;
|
||||
if (analysisResult.failure != null) {
|
||||
str = str2 + item.result.failure.getClass().getSimpleName() + " " + item.result.failure.getMessage();
|
||||
} else {
|
||||
String classSimpleName = DisplayLeakActivity.classSimpleName(analysisResult.className);
|
||||
AnalysisResult analysisResult2 = item.result;
|
||||
if (analysisResult2.leakFound) {
|
||||
long j = analysisResult2.retainedHeapSize;
|
||||
if (j == -1) {
|
||||
string = DisplayLeakActivity.this.getString(R.string.leak_canary_class_has_leaked, new Object[]{classSimpleName});
|
||||
} else {
|
||||
string = DisplayLeakActivity.this.getString(R.string.leak_canary_class_has_leaked_retaining, new Object[]{classSimpleName, Formatter.formatShortFileSize(DisplayLeakActivity.this, j)});
|
||||
}
|
||||
if (item.result.excludedLeak) {
|
||||
string = DisplayLeakActivity.this.getString(R.string.leak_canary_excluded_row, new Object[]{string});
|
||||
}
|
||||
str = str2 + string;
|
||||
} else {
|
||||
str = str2 + DisplayLeakActivity.this.getString(R.string.leak_canary_class_no_leak, new Object[]{classSimpleName});
|
||||
}
|
||||
}
|
||||
textView.setText(str);
|
||||
textView2.setText(DateUtils.formatDateTime(DisplayLeakActivity.this, item.selfLastModified, 17));
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override // android.widget.Adapter
|
||||
public AnalyzedHeap getItem(int i) {
|
||||
return DisplayLeakActivity.this.leaks.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
static class LoadLeaks implements Runnable {
|
||||
DisplayLeakActivity activityOrNull;
|
||||
private final LeakDirectoryProvider leakDirectoryProvider;
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
static final List<LoadLeaks> inFlight = new ArrayList();
|
||||
static final Executor backgroundExecutor = LeakCanaryInternals.newSingleThreadExecutor("LoadLeaks");
|
||||
|
||||
LoadLeaks(DisplayLeakActivity displayLeakActivity, LeakDirectoryProvider leakDirectoryProvider) {
|
||||
this.activityOrNull = displayLeakActivity;
|
||||
this.leakDirectoryProvider = leakDirectoryProvider;
|
||||
}
|
||||
|
||||
static void forgetActivity() {
|
||||
Iterator<LoadLeaks> it = inFlight.iterator();
|
||||
while (it.hasNext()) {
|
||||
it.next().activityOrNull = null;
|
||||
}
|
||||
inFlight.clear();
|
||||
}
|
||||
|
||||
static void load(DisplayLeakActivity displayLeakActivity, LeakDirectoryProvider leakDirectoryProvider) {
|
||||
LoadLeaks loadLeaks = new LoadLeaks(displayLeakActivity, leakDirectoryProvider);
|
||||
inFlight.add(loadLeaks);
|
||||
backgroundExecutor.execute(loadLeaks);
|
||||
}
|
||||
|
||||
@Override // java.lang.Runnable
|
||||
public void run() {
|
||||
final ArrayList arrayList = new ArrayList();
|
||||
Iterator<File> it = this.leakDirectoryProvider.listFiles(new FilenameFilter() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.LoadLeaks.1
|
||||
@Override // java.io.FilenameFilter
|
||||
public boolean accept(File file, String str) {
|
||||
return str.endsWith(".result");
|
||||
}
|
||||
}).iterator();
|
||||
while (it.hasNext()) {
|
||||
AnalyzedHeap load = AnalyzedHeap.load(it.next());
|
||||
if (load != null) {
|
||||
arrayList.add(load);
|
||||
}
|
||||
}
|
||||
Collections.sort(arrayList, new Comparator<AnalyzedHeap>() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.LoadLeaks.2
|
||||
@Override // java.util.Comparator
|
||||
public int compare(AnalyzedHeap analyzedHeap, AnalyzedHeap analyzedHeap2) {
|
||||
return Long.valueOf(analyzedHeap2.selfFile.lastModified()).compareTo(Long.valueOf(analyzedHeap.selfFile.lastModified()));
|
||||
}
|
||||
});
|
||||
this.mainHandler.post(new Runnable() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.LoadLeaks.3
|
||||
@Override // java.lang.Runnable
|
||||
public void run() {
|
||||
LoadLeaks.inFlight.remove(LoadLeaks.this);
|
||||
DisplayLeakActivity displayLeakActivity = LoadLeaks.this.activityOrNull;
|
||||
if (displayLeakActivity != null) {
|
||||
displayLeakActivity.leaks = arrayList;
|
||||
displayLeakActivity.updateUi();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static String classSimpleName(String str) {
|
||||
int lastIndexOf = str.lastIndexOf(46);
|
||||
return lastIndexOf == -1 ? str : str.substring(lastIndexOf + 1);
|
||||
}
|
||||
|
||||
public static PendingIntent createPendingIntent(Context context) {
|
||||
return createPendingIntent(context, null);
|
||||
}
|
||||
|
||||
private void setDisplayHomeAsUpEnabled(boolean z) {
|
||||
ActionBar actionBar = getActionBar();
|
||||
if (actionBar == null) {
|
||||
return;
|
||||
}
|
||||
actionBar.setDisplayHomeAsUpEnabled(z);
|
||||
}
|
||||
|
||||
/* JADX INFO: Access modifiers changed from: private */
|
||||
public void startShareIntentChooser(Uri uri) {
|
||||
Intent intent = new Intent("android.intent.action.SEND");
|
||||
intent.setType("application/octet-stream");
|
||||
intent.putExtra("android.intent.extra.STREAM", uri);
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.leak_canary_share_with)));
|
||||
}
|
||||
|
||||
void deleteAllLeaks() {
|
||||
final LeakDirectoryProvider leakDirectoryProvider = LeakCanaryInternals.getLeakDirectoryProvider(this);
|
||||
AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.5
|
||||
@Override // java.lang.Runnable
|
||||
public void run() {
|
||||
leakDirectoryProvider.clearLeakDirectory();
|
||||
}
|
||||
});
|
||||
this.leaks = Collections.emptyList();
|
||||
updateUi();
|
||||
}
|
||||
|
||||
void deleteVisibleLeak() {
|
||||
final AnalyzedHeap visibleLeak = getVisibleLeak();
|
||||
AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.4
|
||||
@Override // java.lang.Runnable
|
||||
public void run() {
|
||||
AnalyzedHeap analyzedHeap = visibleLeak;
|
||||
File file = analyzedHeap.heapDump.heapDumpFile;
|
||||
File file2 = analyzedHeap.selfFile;
|
||||
if (!file2.delete()) {
|
||||
CanaryLog.d("Could not delete result file %s", file2.getPath());
|
||||
}
|
||||
if (file.delete()) {
|
||||
return;
|
||||
}
|
||||
CanaryLog.d("Could not delete heap dump file %s", file.getPath());
|
||||
}
|
||||
});
|
||||
this.visibleLeakRefKey = null;
|
||||
this.leaks.remove(visibleLeak);
|
||||
updateUi();
|
||||
}
|
||||
|
||||
AnalyzedHeap getVisibleLeak() {
|
||||
List<AnalyzedHeap> list = this.leaks;
|
||||
if (list == null) {
|
||||
return null;
|
||||
}
|
||||
for (AnalyzedHeap analyzedHeap : list) {
|
||||
if (analyzedHeap.heapDump.referenceKey.equals(this.visibleLeakRefKey)) {
|
||||
return analyzedHeap;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override // android.app.Activity
|
||||
public void onBackPressed() {
|
||||
if (this.visibleLeakRefKey == null) {
|
||||
super.onBackPressed();
|
||||
} else {
|
||||
this.visibleLeakRefKey = null;
|
||||
updateUi();
|
||||
}
|
||||
}
|
||||
|
||||
@Override // android.app.Activity
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
if (bundle != null) {
|
||||
this.visibleLeakRefKey = bundle.getString("visibleLeakRefKey");
|
||||
} else {
|
||||
Intent intent = getIntent();
|
||||
if (intent.hasExtra(SHOW_LEAK_EXTRA)) {
|
||||
this.visibleLeakRefKey = intent.getStringExtra(SHOW_LEAK_EXTRA);
|
||||
}
|
||||
}
|
||||
this.leaks = (List) getLastNonConfigurationInstance();
|
||||
setContentView(R.layout.leak_canary_display_leak);
|
||||
this.listView = (ListView) findViewById(R.id.leak_canary_display_leak_list);
|
||||
this.failureView = (TextView) findViewById(R.id.leak_canary_display_leak_failure);
|
||||
this.actionButton = (Button) findViewById(R.id.leak_canary_action);
|
||||
updateUi();
|
||||
}
|
||||
|
||||
@Override // android.app.Activity
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
AnalyzedHeap visibleLeak = getVisibleLeak();
|
||||
if (visibleLeak == null) {
|
||||
return false;
|
||||
}
|
||||
menu.add(R.string.leak_canary_share_leak).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.1
|
||||
@Override // android.view.MenuItem.OnMenuItemClickListener
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
DisplayLeakActivity.this.shareLeak();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (!visibleLeak.heapDumpFileExists) {
|
||||
return true;
|
||||
}
|
||||
menu.add(R.string.leak_canary_share_heap_dump).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.2
|
||||
@Override // android.view.MenuItem.OnMenuItemClickListener
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
DisplayLeakActivity.this.shareHeapDump();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override // android.app.Activity
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
LoadLeaks.forgetActivity();
|
||||
}
|
||||
|
||||
@Override // android.app.Activity
|
||||
public boolean onOptionsItemSelected(MenuItem menuItem) {
|
||||
if (menuItem.getItemId() != 16908332) {
|
||||
return true;
|
||||
}
|
||||
this.visibleLeakRefKey = null;
|
||||
updateUi();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override // android.app.Activity
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
LoadLeaks.load(this, LeakCanaryInternals.getLeakDirectoryProvider(this));
|
||||
}
|
||||
|
||||
@Override // android.app.Activity
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
return this.leaks;
|
||||
}
|
||||
|
||||
@Override // android.app.Activity
|
||||
protected void onSaveInstanceState(Bundle bundle) {
|
||||
super.onSaveInstanceState(bundle);
|
||||
bundle.putString("visibleLeakRefKey", this.visibleLeakRefKey);
|
||||
}
|
||||
|
||||
@Override // android.app.Activity, android.view.ContextThemeWrapper, android.content.ContextWrapper, android.content.Context
|
||||
public void setTheme(int i) {
|
||||
if (i != R.style.leak_canary_LeakCanary_Base) {
|
||||
return;
|
||||
}
|
||||
super.setTheme(i);
|
||||
}
|
||||
|
||||
@SuppressLint({"SetWorldReadable"})
|
||||
void shareHeapDump() {
|
||||
final File file = getVisibleLeak().heapDump.heapDumpFile;
|
||||
AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.3
|
||||
@Override // java.lang.Runnable
|
||||
public void run() {
|
||||
file.setReadable(true, false);
|
||||
final Uri uriForFile = FileProvider.getUriForFile(DisplayLeakActivity.this.getBaseContext(), "com.squareup.leakcanary.fileprovider." + DisplayLeakActivity.this.getApplication().getPackageName(), file);
|
||||
DisplayLeakActivity.this.runOnUiThread(new Runnable() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.3.1
|
||||
@Override // java.lang.Runnable
|
||||
public void run() {
|
||||
DisplayLeakActivity.this.startShareIntentChooser(uriForFile);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void shareLeak() {
|
||||
AnalyzedHeap visibleLeak = getVisibleLeak();
|
||||
String leakInfo = LeakCanary.leakInfo(this, visibleLeak.heapDump, visibleLeak.result, true);
|
||||
Intent intent = new Intent("android.intent.action.SEND");
|
||||
intent.setType("text/plain");
|
||||
intent.putExtra("android.intent.extra.TEXT", leakInfo);
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.leak_canary_share_with)));
|
||||
}
|
||||
|
||||
void updateUi() {
|
||||
String string;
|
||||
List<AnalyzedHeap> list = this.leaks;
|
||||
if (list == null) {
|
||||
setTitle("Loading leaks...");
|
||||
return;
|
||||
}
|
||||
if (list.isEmpty()) {
|
||||
this.visibleLeakRefKey = null;
|
||||
}
|
||||
AnalyzedHeap visibleLeak = getVisibleLeak();
|
||||
if (visibleLeak == null) {
|
||||
this.visibleLeakRefKey = null;
|
||||
}
|
||||
ListAdapter adapter = this.listView.getAdapter();
|
||||
this.listView.setVisibility(0);
|
||||
this.failureView.setVisibility(8);
|
||||
if (visibleLeak == null) {
|
||||
if (adapter instanceof LeakListAdapter) {
|
||||
((LeakListAdapter) adapter).notifyDataSetChanged();
|
||||
} else {
|
||||
this.listView.setAdapter((ListAdapter) new LeakListAdapter());
|
||||
this.listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.8
|
||||
@Override // android.widget.AdapterView.OnItemClickListener
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long j) {
|
||||
DisplayLeakActivity displayLeakActivity = DisplayLeakActivity.this;
|
||||
displayLeakActivity.visibleLeakRefKey = displayLeakActivity.leaks.get(i).heapDump.referenceKey;
|
||||
DisplayLeakActivity.this.updateUi();
|
||||
}
|
||||
});
|
||||
invalidateOptionsMenu();
|
||||
setTitle(getString(R.string.leak_canary_leak_list_title, new Object[]{getPackageName()}));
|
||||
setDisplayHomeAsUpEnabled(false);
|
||||
this.actionButton.setText(R.string.leak_canary_delete_all);
|
||||
this.actionButton.setOnClickListener(new View.OnClickListener() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.9
|
||||
@Override // android.view.View.OnClickListener
|
||||
public void onClick(View view) {
|
||||
new AlertDialog.Builder(DisplayLeakActivity.this).setIcon(android.R.drawable.ic_dialog_alert).setTitle(R.string.leak_canary_delete_all).setMessage(R.string.leak_canary_delete_all_leaks_title).setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.9.1
|
||||
@Override // android.content.DialogInterface.OnClickListener
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
DisplayLeakActivity.this.deleteAllLeaks();
|
||||
}
|
||||
}).setNegativeButton(android.R.string.cancel, (DialogInterface.OnClickListener) null).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
this.actionButton.setVisibility(this.leaks.size() == 0 ? 8 : 0);
|
||||
return;
|
||||
}
|
||||
AnalysisResult analysisResult = visibleLeak.result;
|
||||
this.actionButton.setVisibility(0);
|
||||
this.actionButton.setText(R.string.leak_canary_delete);
|
||||
this.actionButton.setOnClickListener(new View.OnClickListener() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.6
|
||||
@Override // android.view.View.OnClickListener
|
||||
public void onClick(View view) {
|
||||
DisplayLeakActivity.this.deleteVisibleLeak();
|
||||
}
|
||||
});
|
||||
invalidateOptionsMenu();
|
||||
setDisplayHomeAsUpEnabled(true);
|
||||
if (analysisResult.leakFound) {
|
||||
final DisplayLeakAdapter displayLeakAdapter = new DisplayLeakAdapter(getResources());
|
||||
this.listView.setAdapter((ListAdapter) displayLeakAdapter);
|
||||
this.listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { // from class: com.squareup.leakcanary.internal.DisplayLeakActivity.7
|
||||
@Override // android.widget.AdapterView.OnItemClickListener
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long j) {
|
||||
displayLeakAdapter.toggleRow(i);
|
||||
}
|
||||
});
|
||||
HeapDump heapDump = visibleLeak.heapDump;
|
||||
displayLeakAdapter.update(analysisResult.leakTrace, heapDump.referenceKey, heapDump.referenceName);
|
||||
long j = analysisResult.retainedHeapSize;
|
||||
if (j == -1) {
|
||||
setTitle(getString(R.string.leak_canary_class_has_leaked, new Object[]{classSimpleName(analysisResult.className)}));
|
||||
return;
|
||||
} else {
|
||||
setTitle(getString(R.string.leak_canary_class_has_leaked_retaining, new Object[]{classSimpleName(analysisResult.className), Formatter.formatShortFileSize(this, j)}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.listView.setVisibility(8);
|
||||
this.failureView.setVisibility(0);
|
||||
this.listView.setAdapter((ListAdapter) null);
|
||||
if (analysisResult.failure != null) {
|
||||
setTitle(R.string.leak_canary_analysis_failed);
|
||||
string = getString(R.string.leak_canary_failure_report) + BuildConfig.LIBRARY_VERSION + " " + BuildConfig.GIT_SHA + "\n" + Log.getStackTraceString(analysisResult.failure);
|
||||
} else {
|
||||
setTitle(getString(R.string.leak_canary_class_no_leak, new Object[]{classSimpleName(analysisResult.className)}));
|
||||
string = getString(R.string.leak_canary_no_leak_details);
|
||||
}
|
||||
this.failureView.setText(string + "\n\n" + getString(R.string.leak_canary_download_dump, new Object[]{visibleLeak.heapDump.heapDumpFile.getAbsolutePath()}));
|
||||
}
|
||||
|
||||
public static PendingIntent createPendingIntent(Context context, String str) {
|
||||
LeakCanaryInternals.setEnabledBlocking(context, DisplayLeakActivity.class, true);
|
||||
Intent intent = new Intent(context, (Class<?>) DisplayLeakActivity.class);
|
||||
intent.putExtra(SHOW_LEAK_EXTRA, str);
|
||||
intent.setFlags(335544320);
|
||||
return PendingIntent.getActivity(context, 1, intent, 134217728);
|
||||
}
|
||||
}
|
250
sources/com/squareup/leakcanary/internal/DisplayLeakAdapter.java
Normal file
250
sources/com/squareup/leakcanary/internal/DisplayLeakAdapter.java
Normal file
@@ -0,0 +1,250 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.text.Html;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
import com.squareup.leakcanary.Exclusion;
|
||||
import com.squareup.leakcanary.LeakReference;
|
||||
import com.squareup.leakcanary.LeakTrace;
|
||||
import com.squareup.leakcanary.LeakTraceElement;
|
||||
import com.squareup.leakcanary.R;
|
||||
import com.squareup.leakcanary.Reachability;
|
||||
import com.squareup.leakcanary.internal.DisplayLeakConnectorView;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
final class DisplayLeakAdapter extends BaseAdapter {
|
||||
private static final int NORMAL_ROW = 1;
|
||||
private static final int TOP_ROW = 0;
|
||||
private final String classNameColorHexString;
|
||||
private final String extraColorHexString;
|
||||
private final String helpColorHexString;
|
||||
private final String leakColorHexString;
|
||||
private final String referenceColorHexString;
|
||||
private String referenceKey;
|
||||
private boolean[] opened = new boolean[0];
|
||||
private LeakTrace leakTrace = null;
|
||||
private String referenceName = "";
|
||||
|
||||
/* renamed from: com.squareup.leakcanary.internal.DisplayLeakAdapter$1, reason: invalid class name */
|
||||
static /* synthetic */ class AnonymousClass1 {
|
||||
static final /* synthetic */ int[] $SwitchMap$com$squareup$leakcanary$Reachability = new int[Reachability.values().length];
|
||||
|
||||
static {
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$Reachability[Reachability.UNKNOWN.ordinal()] = 1;
|
||||
} catch (NoSuchFieldError unused) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$Reachability[Reachability.REACHABLE.ordinal()] = 2;
|
||||
} catch (NoSuchFieldError unused2) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$Reachability[Reachability.UNREACHABLE.ordinal()] = 3;
|
||||
} catch (NoSuchFieldError unused3) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DisplayLeakAdapter(Resources resources) {
|
||||
this.classNameColorHexString = hexStringColor(resources, R.color.leak_canary_class_name);
|
||||
this.leakColorHexString = hexStringColor(resources, R.color.leak_canary_leak);
|
||||
this.referenceColorHexString = hexStringColor(resources, R.color.leak_canary_reference);
|
||||
this.extraColorHexString = hexStringColor(resources, R.color.leak_canary_extra);
|
||||
this.helpColorHexString = hexStringColor(resources, R.color.leak_canary_help);
|
||||
}
|
||||
|
||||
private int elementIndex(int i) {
|
||||
return i - 2;
|
||||
}
|
||||
|
||||
private static <T extends View> T findById(View view, int i) {
|
||||
return (T) view.findViewById(i);
|
||||
}
|
||||
|
||||
private DisplayLeakConnectorView.Type getConnectorType(int i) {
|
||||
if (i == 1) {
|
||||
return DisplayLeakConnectorView.Type.HELP;
|
||||
}
|
||||
if (i == 2) {
|
||||
return this.leakTrace.expectedReachability.size() == 1 ? DisplayLeakConnectorView.Type.START_LAST_REACHABLE : this.leakTrace.expectedReachability.get(elementIndex(i + 1)) != Reachability.REACHABLE ? DisplayLeakConnectorView.Type.START_LAST_REACHABLE : DisplayLeakConnectorView.Type.START;
|
||||
}
|
||||
if (i == getCount() - 1) {
|
||||
return this.leakTrace.expectedReachability.get(elementIndex(i - 1)) != Reachability.UNREACHABLE ? DisplayLeakConnectorView.Type.END_FIRST_UNREACHABLE : DisplayLeakConnectorView.Type.END;
|
||||
}
|
||||
Reachability reachability = this.leakTrace.expectedReachability.get(elementIndex(i));
|
||||
int i2 = AnonymousClass1.$SwitchMap$com$squareup$leakcanary$Reachability[reachability.ordinal()];
|
||||
if (i2 == 1) {
|
||||
return DisplayLeakConnectorView.Type.NODE_UNKNOWN;
|
||||
}
|
||||
if (i2 == 2) {
|
||||
return this.leakTrace.expectedReachability.get(elementIndex(i + 1)) != Reachability.REACHABLE ? DisplayLeakConnectorView.Type.NODE_LAST_REACHABLE : DisplayLeakConnectorView.Type.NODE_REACHABLE;
|
||||
}
|
||||
if (i2 == 3) {
|
||||
return this.leakTrace.expectedReachability.get(elementIndex(i - 1)) != Reachability.UNREACHABLE ? DisplayLeakConnectorView.Type.NODE_FIRST_UNREACHABLE : DisplayLeakConnectorView.Type.NODE_UNREACHABLE;
|
||||
}
|
||||
throw new IllegalStateException("Unknown value: " + reachability);
|
||||
}
|
||||
|
||||
private static String hexStringColor(Resources resources, int i) {
|
||||
return String.format("#%06X", Integer.valueOf(resources.getColor(i) & 16777215));
|
||||
}
|
||||
|
||||
private Spanned htmlDetails(boolean z, LeakTraceElement leakTraceElement) {
|
||||
String str;
|
||||
if (leakTraceElement.extra != null) {
|
||||
str = " <font color='" + this.extraColorHexString + "'>" + leakTraceElement.extra + "</font>";
|
||||
} else {
|
||||
str = "";
|
||||
}
|
||||
Exclusion exclusion = leakTraceElement.exclusion;
|
||||
if (exclusion != null) {
|
||||
String str2 = str + "<br/><br/>Excluded by rule";
|
||||
if (exclusion.name != null) {
|
||||
str2 = str2 + " <font color='#ffffff'>" + exclusion.name + "</font>";
|
||||
}
|
||||
str = str2 + " matching <font color='#f3cf83'>" + exclusion.matching + "</font>";
|
||||
if (exclusion.reason != null) {
|
||||
str = str + " because <font color='#f3cf83'>" + exclusion.reason + "</font>";
|
||||
}
|
||||
}
|
||||
String str3 = str + "<br><font color='" + this.extraColorHexString + "'>" + leakTraceElement.toDetailedString().replace("\n", "<br>") + "</font>";
|
||||
if (z && !this.referenceName.equals("")) {
|
||||
str3 = str3 + " <font color='" + this.extraColorHexString + "'>" + this.referenceName + "</font>";
|
||||
}
|
||||
return Html.fromHtml(str3);
|
||||
}
|
||||
|
||||
private Spanned htmlTitle(LeakTraceElement leakTraceElement, boolean z, Resources resources) {
|
||||
String str;
|
||||
String str2;
|
||||
String str3 = "<font color='" + this.classNameColorHexString + "'>" + leakTraceElement.getSimpleClassName().replace("[]", "[ ]") + "</font>";
|
||||
LeakReference leakReference = leakTraceElement.reference;
|
||||
if (leakReference != null) {
|
||||
String replaceAll = leakReference.getDisplayName().replaceAll("<", "<").replaceAll(">", ">");
|
||||
if (z) {
|
||||
str2 = "<u><font color='" + this.leakColorHexString + "'>" + replaceAll + "</font></u>";
|
||||
} else {
|
||||
str2 = "<font color='" + this.referenceColorHexString + "'>" + replaceAll + "</font>";
|
||||
}
|
||||
if (leakTraceElement.reference.type == LeakTraceElement.Type.STATIC_FIELD) {
|
||||
str2 = "<i>" + str2 + "</i>";
|
||||
}
|
||||
String str4 = str3 + "." + str2;
|
||||
if (z) {
|
||||
str4 = "<b>" + str4 + "</b>";
|
||||
}
|
||||
str = "" + str4;
|
||||
} else {
|
||||
str = "" + str3;
|
||||
}
|
||||
if (leakTraceElement.exclusion != null) {
|
||||
str = str + " (excluded)";
|
||||
}
|
||||
SpannableStringBuilder spannableStringBuilder = (SpannableStringBuilder) Html.fromHtml(str);
|
||||
if (z) {
|
||||
SquigglySpan.replaceUnderlineSpans(spannableStringBuilder, resources);
|
||||
}
|
||||
return spannableStringBuilder;
|
||||
}
|
||||
|
||||
@Override // android.widget.Adapter
|
||||
public int getCount() {
|
||||
LeakTrace leakTrace = this.leakTrace;
|
||||
if (leakTrace == null) {
|
||||
return 2;
|
||||
}
|
||||
return leakTrace.elements.size() + 2;
|
||||
}
|
||||
|
||||
@Override // android.widget.Adapter
|
||||
public long getItemId(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override // android.widget.BaseAdapter, android.widget.Adapter
|
||||
public int getItemViewType(int i) {
|
||||
return i == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
@Override // android.widget.Adapter
|
||||
public View getView(int i, View view, ViewGroup viewGroup) {
|
||||
Context context = viewGroup.getContext();
|
||||
boolean z = false;
|
||||
if (getItemViewType(i) == 0) {
|
||||
if (view == null) {
|
||||
view = LayoutInflater.from(context).inflate(R.layout.leak_canary_ref_top_row, viewGroup, false);
|
||||
}
|
||||
((TextView) findById(view, R.id.leak_canary_row_text)).setText(context.getPackageName());
|
||||
} else {
|
||||
if (view == null) {
|
||||
view = LayoutInflater.from(context).inflate(R.layout.leak_canary_ref_row, viewGroup, false);
|
||||
}
|
||||
TextView textView = (TextView) findById(view, R.id.leak_canary_row_title);
|
||||
TextView textView2 = (TextView) findById(view, R.id.leak_canary_row_details);
|
||||
DisplayLeakConnectorView displayLeakConnectorView = (DisplayLeakConnectorView) findById(view, R.id.leak_canary_row_connector);
|
||||
MoreDetailsView moreDetailsView = (MoreDetailsView) findById(view, R.id.leak_canary_row_more);
|
||||
displayLeakConnectorView.setType(getConnectorType(i));
|
||||
moreDetailsView.setOpened(this.opened[i]);
|
||||
if (this.opened[i]) {
|
||||
textView2.setVisibility(0);
|
||||
} else {
|
||||
textView2.setVisibility(8);
|
||||
}
|
||||
Resources resources = view.getResources();
|
||||
if (i == 1) {
|
||||
textView.setText(Html.fromHtml("<font color='" + this.helpColorHexString + "'><b>" + resources.getString(R.string.leak_canary_help_title) + "</b></font>"));
|
||||
SpannableStringBuilder spannableStringBuilder = (SpannableStringBuilder) Html.fromHtml(resources.getString(R.string.leak_canary_help_detail));
|
||||
SquigglySpan.replaceUnderlineSpans(spannableStringBuilder, resources);
|
||||
textView2.setText(spannableStringBuilder);
|
||||
} else {
|
||||
boolean z2 = i == getCount() - 1;
|
||||
LeakTraceElement item = getItem(i);
|
||||
Reachability reachability = this.leakTrace.expectedReachability.get(elementIndex(i));
|
||||
if (!z2 && reachability != Reachability.UNREACHABLE && this.leakTrace.expectedReachability.get(elementIndex(i + 1)) != Reachability.REACHABLE) {
|
||||
z = true;
|
||||
}
|
||||
textView.setText(htmlTitle(item, z, resources));
|
||||
if (this.opened[i]) {
|
||||
textView2.setText(htmlDetails(z2, item));
|
||||
}
|
||||
}
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override // android.widget.BaseAdapter, android.widget.Adapter
|
||||
public int getViewTypeCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
public void toggleRow(int i) {
|
||||
this.opened[i] = !r0[i];
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void update(LeakTrace leakTrace, String str, String str2) {
|
||||
if (str.equals(this.referenceKey)) {
|
||||
return;
|
||||
}
|
||||
this.referenceKey = str;
|
||||
this.referenceName = str2;
|
||||
this.leakTrace = leakTrace;
|
||||
this.opened = new boolean[leakTrace.elements.size() + 2];
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override // android.widget.Adapter
|
||||
public LeakTraceElement getItem(int i) {
|
||||
if (getItemViewType(i) == 0 || i == 1) {
|
||||
return null;
|
||||
}
|
||||
return this.leakTrace.elements.get(elementIndex(i));
|
||||
}
|
||||
}
|
@@ -0,0 +1,228 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.DashPathEffect;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import com.squareup.leakcanary.R;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class DisplayLeakConnectorView extends View {
|
||||
private Bitmap cache;
|
||||
private final float circleY;
|
||||
private final Paint classNamePaint;
|
||||
private final Paint clearPaint;
|
||||
private final Paint leakPaint;
|
||||
private final Paint referencePaint;
|
||||
private final float strokeSize;
|
||||
private Type type;
|
||||
private static final float SQRT_TWO = (float) Math.sqrt(2.0d);
|
||||
private static final PorterDuffXfermode CLEAR_XFER_MODE = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
|
||||
|
||||
/* renamed from: com.squareup.leakcanary.internal.DisplayLeakConnectorView$1, reason: invalid class name */
|
||||
static /* synthetic */ class AnonymousClass1 {
|
||||
static final /* synthetic */ int[] $SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type = new int[Type.values().length];
|
||||
|
||||
static {
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.NODE_UNKNOWN.ordinal()] = 1;
|
||||
} catch (NoSuchFieldError unused) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.NODE_UNREACHABLE.ordinal()] = 2;
|
||||
} catch (NoSuchFieldError unused2) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.NODE_REACHABLE.ordinal()] = 3;
|
||||
} catch (NoSuchFieldError unused3) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.NODE_FIRST_UNREACHABLE.ordinal()] = 4;
|
||||
} catch (NoSuchFieldError unused4) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.NODE_LAST_REACHABLE.ordinal()] = 5;
|
||||
} catch (NoSuchFieldError unused5) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.START.ordinal()] = 6;
|
||||
} catch (NoSuchFieldError unused6) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.START_LAST_REACHABLE.ordinal()] = 7;
|
||||
} catch (NoSuchFieldError unused7) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.END.ordinal()] = 8;
|
||||
} catch (NoSuchFieldError unused8) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.END_FIRST_UNREACHABLE.ordinal()] = 9;
|
||||
} catch (NoSuchFieldError unused9) {
|
||||
}
|
||||
try {
|
||||
$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[Type.HELP.ordinal()] = 10;
|
||||
} catch (NoSuchFieldError unused10) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
HELP,
|
||||
START,
|
||||
START_LAST_REACHABLE,
|
||||
NODE_UNKNOWN,
|
||||
NODE_FIRST_UNREACHABLE,
|
||||
NODE_UNREACHABLE,
|
||||
NODE_REACHABLE,
|
||||
NODE_LAST_REACHABLE,
|
||||
END,
|
||||
END_FIRST_UNREACHABLE
|
||||
}
|
||||
|
||||
public DisplayLeakConnectorView(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
Resources resources = getResources();
|
||||
this.type = Type.NODE_UNKNOWN;
|
||||
this.circleY = resources.getDimensionPixelSize(R.dimen.leak_canary_connector_center_y);
|
||||
this.strokeSize = resources.getDimensionPixelSize(R.dimen.leak_canary_connector_stroke_size);
|
||||
this.classNamePaint = new Paint(1);
|
||||
this.classNamePaint.setColor(resources.getColor(R.color.leak_canary_class_name));
|
||||
this.classNamePaint.setStrokeWidth(this.strokeSize);
|
||||
this.leakPaint = new Paint(1);
|
||||
this.leakPaint.setColor(resources.getColor(R.color.leak_canary_leak));
|
||||
this.leakPaint.setStyle(Paint.Style.STROKE);
|
||||
this.leakPaint.setStrokeWidth(this.strokeSize);
|
||||
this.leakPaint.setPathEffect(new DashPathEffect(new float[]{resources.getDimensionPixelSize(R.dimen.leak_canary_connector_leak_dash_line), resources.getDimensionPixelSize(R.dimen.leak_canary_connector_leak_dash_gap)}, 0.0f));
|
||||
this.clearPaint = new Paint(1);
|
||||
this.clearPaint.setColor(0);
|
||||
this.clearPaint.setXfermode(CLEAR_XFER_MODE);
|
||||
this.referencePaint = new Paint(1);
|
||||
this.referencePaint.setColor(resources.getColor(R.color.leak_canary_reference));
|
||||
this.referencePaint.setStrokeWidth(this.strokeSize);
|
||||
}
|
||||
|
||||
private void drawArrowHead(Canvas canvas, Paint paint) {
|
||||
float measuredWidth = getMeasuredWidth();
|
||||
float f = measuredWidth / 2.0f;
|
||||
float f2 = measuredWidth / 3.0f;
|
||||
float f3 = (f / 2.0f) * SQRT_TWO;
|
||||
float f4 = this.strokeSize;
|
||||
float f5 = this.circleY;
|
||||
float f6 = ((f5 - f3) - (f2 * 2.0f)) - f4;
|
||||
canvas.drawLine(f, 0.0f, f, (f5 - f2) - (f4 / 2.0f), paint);
|
||||
canvas.translate(f, f6);
|
||||
canvas.rotate(45.0f);
|
||||
canvas.drawLine(0.0f, f, f + (f4 / 2.0f), f, paint);
|
||||
canvas.drawLine(f, 0.0f, f, f, paint);
|
||||
canvas.rotate(-45.0f);
|
||||
canvas.translate(-f, -f6);
|
||||
}
|
||||
|
||||
private void drawInstanceCircle(Canvas canvas) {
|
||||
float measuredWidth = getMeasuredWidth();
|
||||
canvas.drawCircle(measuredWidth / 2.0f, this.circleY, measuredWidth / 3.0f, this.classNamePaint);
|
||||
}
|
||||
|
||||
private void drawItems(Canvas canvas, Paint paint, Paint paint2) {
|
||||
if (paint != null) {
|
||||
drawArrowHead(canvas, paint);
|
||||
}
|
||||
if (paint2 != null) {
|
||||
drawNextArrowLine(canvas, paint2);
|
||||
}
|
||||
drawInstanceCircle(canvas);
|
||||
}
|
||||
|
||||
private void drawNextArrowLine(Canvas canvas, Paint paint) {
|
||||
float measuredWidth = getMeasuredWidth() / 2.0f;
|
||||
canvas.drawLine(measuredWidth, this.circleY, measuredWidth, getMeasuredHeight(), paint);
|
||||
}
|
||||
|
||||
private void drawRoot(Canvas canvas) {
|
||||
int measuredWidth = getMeasuredWidth();
|
||||
int measuredHeight = getMeasuredHeight();
|
||||
float f = measuredWidth;
|
||||
float f2 = f / 2.0f;
|
||||
float f3 = f2 - (this.strokeSize / 2.0f);
|
||||
canvas.drawRect(0.0f, 0.0f, f, f3, this.classNamePaint);
|
||||
canvas.drawCircle(0.0f, f3, f3, this.clearPaint);
|
||||
canvas.drawCircle(f, f3, f3, this.clearPaint);
|
||||
canvas.drawLine(f2, 0.0f, f2, measuredHeight, this.classNamePaint);
|
||||
}
|
||||
|
||||
private void drawStartLine(Canvas canvas) {
|
||||
float measuredWidth = getMeasuredWidth() / 2.0f;
|
||||
canvas.drawLine(measuredWidth, 0.0f, measuredWidth, this.circleY, this.classNamePaint);
|
||||
}
|
||||
|
||||
@Override // android.view.View
|
||||
protected void onDraw(Canvas canvas) {
|
||||
int measuredWidth = getMeasuredWidth();
|
||||
int measuredHeight = getMeasuredHeight();
|
||||
Bitmap bitmap = this.cache;
|
||||
if (bitmap != null && (bitmap.getWidth() != measuredWidth || this.cache.getHeight() != measuredHeight)) {
|
||||
this.cache.recycle();
|
||||
this.cache = null;
|
||||
}
|
||||
if (this.cache == null) {
|
||||
this.cache = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas2 = new Canvas(this.cache);
|
||||
switch (AnonymousClass1.$SwitchMap$com$squareup$leakcanary$internal$DisplayLeakConnectorView$Type[this.type.ordinal()]) {
|
||||
case 1:
|
||||
Paint paint = this.leakPaint;
|
||||
drawItems(canvas2, paint, paint);
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
Paint paint2 = this.referencePaint;
|
||||
drawItems(canvas2, paint2, paint2);
|
||||
break;
|
||||
case 4:
|
||||
drawItems(canvas2, this.leakPaint, this.referencePaint);
|
||||
break;
|
||||
case 5:
|
||||
drawItems(canvas2, this.referencePaint, this.leakPaint);
|
||||
break;
|
||||
case 6:
|
||||
drawStartLine(canvas2);
|
||||
drawItems(canvas2, null, this.referencePaint);
|
||||
break;
|
||||
case 7:
|
||||
drawStartLine(canvas2);
|
||||
drawItems(canvas2, null, this.leakPaint);
|
||||
break;
|
||||
case 8:
|
||||
drawItems(canvas2, this.referencePaint, null);
|
||||
break;
|
||||
case 9:
|
||||
drawItems(canvas2, this.leakPaint, null);
|
||||
break;
|
||||
case 10:
|
||||
drawRoot(canvas2);
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown type " + this.type);
|
||||
}
|
||||
}
|
||||
canvas.drawBitmap(this.cache, 0.0f, 0.0f, (Paint) null);
|
||||
}
|
||||
|
||||
public void setType(Type type) {
|
||||
if (type != this.type) {
|
||||
this.type = type;
|
||||
Bitmap bitmap = this.cache;
|
||||
if (bitmap != null) {
|
||||
bitmap.recycle();
|
||||
this.cache = null;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.app.Notification;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.os.SystemClock;
|
||||
import com.squareup.leakcanary.R;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public abstract class ForegroundService extends IntentService {
|
||||
private final int notificationContentTitleResId;
|
||||
private final int notificationId;
|
||||
|
||||
public ForegroundService(String str, int i) {
|
||||
super(str);
|
||||
this.notificationContentTitleResId = i;
|
||||
this.notificationId = (int) SystemClock.uptimeMillis();
|
||||
}
|
||||
|
||||
@Override // android.app.IntentService, android.app.Service
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override // android.app.IntentService, android.app.Service
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
showForegroundNotification(100, 0, true, getString(R.string.leak_canary_notification_foreground_text));
|
||||
}
|
||||
|
||||
@Override // android.app.IntentService, android.app.Service
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
stopForeground(true);
|
||||
}
|
||||
|
||||
@Override // android.app.IntentService
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
onHandleIntentInForeground(intent);
|
||||
}
|
||||
|
||||
protected abstract void onHandleIntentInForeground(Intent intent);
|
||||
|
||||
protected void showForegroundNotification(int i, int i2, boolean z, String str) {
|
||||
startForeground(this.notificationId, LeakCanaryInternals.buildNotification(this, new Notification.Builder(this).setContentTitle(getString(this.notificationContentTitleResId)).setContentText(str).setProgress(i, i2, z)));
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import com.squareup.leakcanary.RefWatcher;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public interface FragmentRefWatcher {
|
||||
|
||||
public static final class Helper {
|
||||
private static final String SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME = "com.squareup.leakcanary.internal.SupportFragmentRefWatcher";
|
||||
private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { // from class: com.squareup.leakcanary.internal.FragmentRefWatcher.Helper.1
|
||||
@Override // com.squareup.leakcanary.internal.ActivityLifecycleCallbacksAdapter, android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityCreated(Activity activity, Bundle bundle) {
|
||||
Iterator it = Helper.this.fragmentRefWatchers.iterator();
|
||||
while (it.hasNext()) {
|
||||
((FragmentRefWatcher) it.next()).watchFragments(activity);
|
||||
}
|
||||
}
|
||||
};
|
||||
private final List<FragmentRefWatcher> fragmentRefWatchers;
|
||||
|
||||
private Helper(List<FragmentRefWatcher> list) {
|
||||
this.fragmentRefWatchers = list;
|
||||
}
|
||||
|
||||
public static void install(Context context, RefWatcher refWatcher) {
|
||||
ArrayList arrayList = new ArrayList();
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
arrayList.add(new AndroidOFragmentRefWatcher(refWatcher));
|
||||
}
|
||||
try {
|
||||
arrayList.add((FragmentRefWatcher) Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME).getDeclaredConstructor(RefWatcher.class).newInstance(refWatcher));
|
||||
} catch (Exception unused) {
|
||||
}
|
||||
if (arrayList.size() == 0) {
|
||||
return;
|
||||
}
|
||||
((Application) context.getApplicationContext()).registerActivityLifecycleCallbacks(new Helper(arrayList).activityLifecycleCallbacks);
|
||||
}
|
||||
}
|
||||
|
||||
void watchFragments(Activity activity);
|
||||
}
|
31
sources/com/squareup/leakcanary/internal/FutureResult.java
Normal file
31
sources/com/squareup/leakcanary/internal/FutureResult.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class FutureResult<T> {
|
||||
private final AtomicReference<T> resultHolder = new AtomicReference<>();
|
||||
private final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
public T get() {
|
||||
if (this.latch.getCount() <= 0) {
|
||||
return this.resultHolder.get();
|
||||
}
|
||||
throw new IllegalStateException("Call wait() and check its result");
|
||||
}
|
||||
|
||||
public void set(T t) {
|
||||
this.resultHolder.set(t);
|
||||
this.latch.countDown();
|
||||
}
|
||||
|
||||
public boolean wait(long j, TimeUnit timeUnit) {
|
||||
try {
|
||||
return this.latch.await(j, timeUnit);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("Did not expect thread to be interrupted", e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import com.squareup.leakcanary.AbstractAnalysisResultService;
|
||||
import com.squareup.leakcanary.AnalyzerProgressListener;
|
||||
import com.squareup.leakcanary.CanaryLog;
|
||||
import com.squareup.leakcanary.HeapAnalyzer;
|
||||
import com.squareup.leakcanary.HeapDump;
|
||||
import com.squareup.leakcanary.R;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class HeapAnalyzerService extends ForegroundService implements AnalyzerProgressListener {
|
||||
private static final String HEAPDUMP_EXTRA = "heapdump_extra";
|
||||
private static final String LISTENER_CLASS_EXTRA = "listener_class_extra";
|
||||
|
||||
public HeapAnalyzerService() {
|
||||
super(HeapAnalyzerService.class.getSimpleName(), R.string.leak_canary_notification_analysing);
|
||||
}
|
||||
|
||||
public static void runAnalysis(Context context, HeapDump heapDump, Class<? extends AbstractAnalysisResultService> cls) {
|
||||
LeakCanaryInternals.setEnabledBlocking(context, HeapAnalyzerService.class, true);
|
||||
LeakCanaryInternals.setEnabledBlocking(context, cls, true);
|
||||
Intent intent = new Intent(context, (Class<?>) HeapAnalyzerService.class);
|
||||
intent.putExtra(LISTENER_CLASS_EXTRA, cls.getName());
|
||||
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
|
||||
ContextCompat.a(context, intent);
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.internal.ForegroundService
|
||||
protected void onHandleIntentInForeground(Intent intent) {
|
||||
if (intent == null) {
|
||||
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.", new Object[0]);
|
||||
return;
|
||||
}
|
||||
String stringExtra = intent.getStringExtra(LISTENER_CLASS_EXTRA);
|
||||
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
|
||||
AbstractAnalysisResultService.sendResultToListener(this, stringExtra, heapDump, new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses).checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey, heapDump.computeRetainedHeapSize));
|
||||
}
|
||||
|
||||
@Override // com.squareup.leakcanary.AnalyzerProgressListener
|
||||
public void onProgressUpdate(AnalyzerProgressListener.Step step) {
|
||||
int ordinal = (int) ((step.ordinal() * 100.0f) / AnalyzerProgressListener.Step.values().length);
|
||||
CanaryLog.d("Analysis in progress, working on: %s", step.name());
|
||||
String lowerCase = step.name().replace("_", " ").toLowerCase();
|
||||
showForegroundNotification(100, ordinal, false, lowerCase.substring(0, 1).toUpperCase() + lowerCase.substring(1));
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public class LeakCanaryFileProvider extends FileProvider {
|
||||
}
|
@@ -0,0 +1,140 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import com.ijm.dataencryption.de.DataDecryptTool;
|
||||
import com.squareup.leakcanary.CanaryLog;
|
||||
import com.squareup.leakcanary.DefaultLeakDirectoryProvider;
|
||||
import com.squareup.leakcanary.LeakDirectoryProvider;
|
||||
import com.squareup.leakcanary.R;
|
||||
import com.squareup.leakcanary.RefWatcher;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class LeakCanaryInternals {
|
||||
public static final String HUAWEI = "HUAWEI";
|
||||
public static final String LENOVO = "LENOVO";
|
||||
public static final String LG = "LGE";
|
||||
public static final String MEIZU = "Meizu";
|
||||
public static final String MOTOROLA = "motorola";
|
||||
private static final String NOTIFICATION_CHANNEL_ID = "leakcanary";
|
||||
public static final String NVIDIA = "NVIDIA";
|
||||
public static final String SAMSUNG = "samsung";
|
||||
public static final String VIVO = "vivo";
|
||||
public static volatile RefWatcher installedRefWatcher;
|
||||
public static volatile Boolean isInAnalyzerProcess;
|
||||
private static volatile LeakDirectoryProvider leakDirectoryProvider;
|
||||
|
||||
private LeakCanaryInternals() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static Notification buildNotification(Context context, Notification.Builder builder) {
|
||||
builder.setSmallIcon(R.drawable.leak_canary_notification).setWhen(System.currentTimeMillis()).setOnlyAlertOnce(true);
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService("notification");
|
||||
if (notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) == null) {
|
||||
notificationManager.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID, context.getString(R.string.leak_canary_notification_channel), 3));
|
||||
}
|
||||
builder.setChannelId(NOTIFICATION_CHANNEL_ID);
|
||||
}
|
||||
return Build.VERSION.SDK_INT < 16 ? builder.getNotification() : builder.build();
|
||||
}
|
||||
|
||||
public static String classSimpleName(String str) {
|
||||
int lastIndexOf = str.lastIndexOf(46);
|
||||
return lastIndexOf == -1 ? str : str.substring(lastIndexOf + 1);
|
||||
}
|
||||
|
||||
public static LeakDirectoryProvider getLeakDirectoryProvider(Context context) {
|
||||
LeakDirectoryProvider leakDirectoryProvider2 = leakDirectoryProvider;
|
||||
return leakDirectoryProvider2 == null ? new DefaultLeakDirectoryProvider(context) : leakDirectoryProvider2;
|
||||
}
|
||||
|
||||
public static boolean isInServiceProcess(Context context, Class<? extends Service> cls) {
|
||||
PackageManager packageManager = context.getPackageManager();
|
||||
try {
|
||||
String str = packageManager.getPackageInfo(context.getPackageName(), 4).applicationInfo.processName;
|
||||
try {
|
||||
ServiceInfo serviceInfo = packageManager.getServiceInfo(new ComponentName(context, cls), DataDecryptTool.DECRYPT_DB_FILE);
|
||||
if (serviceInfo.processName.equals(str)) {
|
||||
CanaryLog.d("Did not expect service %s to run in main process %s", cls, str);
|
||||
return false;
|
||||
}
|
||||
int myPid = Process.myPid();
|
||||
ActivityManager.RunningAppProcessInfo runningAppProcessInfo = null;
|
||||
try {
|
||||
List<ActivityManager.RunningAppProcessInfo> runningAppProcesses = ((ActivityManager) context.getSystemService("activity")).getRunningAppProcesses();
|
||||
if (runningAppProcesses != null) {
|
||||
Iterator<ActivityManager.RunningAppProcessInfo> it = runningAppProcesses.iterator();
|
||||
while (true) {
|
||||
if (!it.hasNext()) {
|
||||
break;
|
||||
}
|
||||
ActivityManager.RunningAppProcessInfo next = it.next();
|
||||
if (next.pid == myPid) {
|
||||
runningAppProcessInfo = next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (runningAppProcessInfo != null) {
|
||||
return runningAppProcessInfo.processName.equals(serviceInfo.processName);
|
||||
}
|
||||
CanaryLog.d("Could not find running process for %d", Integer.valueOf(myPid));
|
||||
return false;
|
||||
} catch (SecurityException e) {
|
||||
CanaryLog.d("Could not get running app processes %d", e);
|
||||
return false;
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException unused) {
|
||||
}
|
||||
} catch (Exception e2) {
|
||||
CanaryLog.d(e2, "Could not get package info for %s", context.getPackageName());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Executor newSingleThreadExecutor(String str) {
|
||||
return Executors.newSingleThreadExecutor(new LeakCanarySingleThreadFactory(str));
|
||||
}
|
||||
|
||||
public static void setEnabledAsync(Context context, final Class<?> cls, final boolean z) {
|
||||
final Context applicationContext = context.getApplicationContext();
|
||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() { // from class: com.squareup.leakcanary.internal.LeakCanaryInternals.1
|
||||
@Override // java.lang.Runnable
|
||||
public void run() {
|
||||
LeakCanaryInternals.setEnabledBlocking(applicationContext, cls, z);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void setEnabledBlocking(Context context, Class<?> cls, boolean z) {
|
||||
context.getPackageManager().setComponentEnabledSetting(new ComponentName(context, cls), z ? 1 : 2, 1);
|
||||
}
|
||||
|
||||
public static void setLeakDirectoryProvider(LeakDirectoryProvider leakDirectoryProvider2) {
|
||||
if (leakDirectoryProvider != null) {
|
||||
throw new IllegalStateException("Cannot set the LeakDirectoryProvider after it has already been set. Try setting it before installing the RefWatcher.");
|
||||
}
|
||||
leakDirectoryProvider = leakDirectoryProvider2;
|
||||
}
|
||||
|
||||
public static void showNotification(Context context, CharSequence charSequence, CharSequence charSequence2, PendingIntent pendingIntent, int i) {
|
||||
((NotificationManager) context.getSystemService("notification")).notify(i, buildNotification(context, new Notification.Builder(context).setContentText(charSequence2).setContentTitle(charSequence).setAutoCancel(true).setContentIntent(pendingIntent)));
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
final class LeakCanarySingleThreadFactory implements ThreadFactory {
|
||||
private final String threadName;
|
||||
|
||||
LeakCanarySingleThreadFactory(String str) {
|
||||
this.threadName = "LeakCanary-" + str;
|
||||
}
|
||||
|
||||
@Override // java.util.concurrent.ThreadFactory
|
||||
public Thread newThread(Runnable runnable) {
|
||||
return new Thread(runnable, this.threadName);
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import com.squareup.leakcanary.R;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public final class MoreDetailsView extends View {
|
||||
private final Paint iconPaint;
|
||||
private boolean opened;
|
||||
|
||||
public MoreDetailsView(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
Resources resources = getResources();
|
||||
this.iconPaint = new Paint(1);
|
||||
this.iconPaint.setStrokeWidth(resources.getDimensionPixelSize(R.dimen.leak_canary_more_stroke_width));
|
||||
TypedArray obtainStyledAttributes = context.obtainStyledAttributes(attributeSet, R.styleable.leak_canary_MoreDetailsView);
|
||||
int color = obtainStyledAttributes.getColor(R.styleable.leak_canary_MoreDetailsView_leak_canary_plus_color, -16777216);
|
||||
obtainStyledAttributes.recycle();
|
||||
this.iconPaint.setColor(color);
|
||||
}
|
||||
|
||||
@Override // android.view.View
|
||||
protected void onDraw(Canvas canvas) {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
int i = height / 2;
|
||||
int i2 = width / 2;
|
||||
if (this.opened) {
|
||||
float f = i;
|
||||
canvas.drawLine(0.0f, f, width, f, this.iconPaint);
|
||||
} else {
|
||||
float f2 = i;
|
||||
canvas.drawLine(0.0f, f2, width, f2, this.iconPaint);
|
||||
float f3 = i2;
|
||||
canvas.drawLine(f3, 0.0f, f3, height, this.iconPaint);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOpened(boolean z) {
|
||||
if (z != this.opened) {
|
||||
this.opened = z;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
import com.squareup.leakcanary.R;
|
||||
|
||||
@TargetApi(23)
|
||||
/* loaded from: classes.dex */
|
||||
public class RequestStoragePermissionActivity extends Activity {
|
||||
public static PendingIntent createPendingIntent(Context context) {
|
||||
LeakCanaryInternals.setEnabledBlocking(context, RequestStoragePermissionActivity.class, true);
|
||||
Intent intent = new Intent(context, (Class<?>) RequestStoragePermissionActivity.class);
|
||||
intent.setFlags(335544320);
|
||||
return PendingIntent.getActivity(context, 1, intent, 134217728);
|
||||
}
|
||||
|
||||
private boolean hasStoragePermission() {
|
||||
return checkSelfPermission("android.permission.WRITE_EXTERNAL_STORAGE") == 0;
|
||||
}
|
||||
|
||||
@Override // android.app.Activity
|
||||
public void finish() {
|
||||
overridePendingTransition(0, 0);
|
||||
super.finish();
|
||||
}
|
||||
|
||||
@Override // android.app.Activity
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
if (bundle == null) {
|
||||
if (hasStoragePermission()) {
|
||||
finish();
|
||||
} else {
|
||||
requestPermissions(new String[]{"android.permission.WRITE_EXTERNAL_STORAGE"}, 42);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override // android.app.Activity
|
||||
public void onRequestPermissionsResult(int i, String[] strArr, int[] iArr) {
|
||||
if (!hasStoragePermission()) {
|
||||
Toast.makeText(getApplication(), R.string.leak_canary_permission_not_granted, 1).show();
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import com.squareup.leakcanary.R;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
public class RowElementLayout extends ViewGroup {
|
||||
private View connector;
|
||||
private final int connectorWidth;
|
||||
private View details;
|
||||
private final int minHeight;
|
||||
private View moreButton;
|
||||
private final int moreMarginTop;
|
||||
private final int moreSize;
|
||||
private final int rowMargins;
|
||||
private View title;
|
||||
private final int titleMarginTop;
|
||||
|
||||
public RowElementLayout(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
Resources resources = getResources();
|
||||
this.connectorWidth = resources.getDimensionPixelSize(R.dimen.leak_canary_connector_width);
|
||||
this.rowMargins = resources.getDimensionPixelSize(R.dimen.leak_canary_row_margins);
|
||||
this.moreSize = resources.getDimensionPixelSize(R.dimen.leak_canary_more_size);
|
||||
this.minHeight = resources.getDimensionPixelSize(R.dimen.leak_canary_row_min);
|
||||
this.titleMarginTop = resources.getDimensionPixelSize(R.dimen.leak_canary_row_title_margin_top);
|
||||
this.moreMarginTop = resources.getDimensionPixelSize(R.dimen.leak_canary_more_margin_top);
|
||||
}
|
||||
|
||||
@Override // android.view.View
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
this.connector = findViewById(R.id.leak_canary_row_connector);
|
||||
this.moreButton = findViewById(R.id.leak_canary_row_more);
|
||||
this.title = findViewById(R.id.leak_canary_row_title);
|
||||
this.details = findViewById(R.id.leak_canary_row_details);
|
||||
}
|
||||
|
||||
@Override // android.view.ViewGroup, android.view.View
|
||||
protected void onLayout(boolean z, int i, int i2, int i3, int i4) {
|
||||
int measuredWidth = getMeasuredWidth();
|
||||
int measuredWidth2 = this.rowMargins + this.connector.getMeasuredWidth();
|
||||
View view = this.connector;
|
||||
view.layout(this.rowMargins, 0, measuredWidth2, view.getMeasuredHeight());
|
||||
View view2 = this.moreButton;
|
||||
int i5 = this.rowMargins;
|
||||
int i6 = this.moreSize;
|
||||
int i7 = this.moreMarginTop;
|
||||
view2.layout((measuredWidth - i5) - i6, i7, measuredWidth - i5, i6 + i7);
|
||||
int i8 = measuredWidth2 + this.rowMargins;
|
||||
int measuredHeight = this.titleMarginTop + this.title.getMeasuredHeight();
|
||||
View view3 = this.title;
|
||||
view3.layout(i8, this.titleMarginTop, view3.getMeasuredWidth() + i8, measuredHeight);
|
||||
if (this.details.getVisibility() != 8) {
|
||||
View view4 = this.details;
|
||||
view4.layout(i8, measuredHeight, measuredWidth - this.rowMargins, view4.getMeasuredHeight() + measuredHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // android.view.View
|
||||
protected void onMeasure(int i, int i2) {
|
||||
int size = View.MeasureSpec.getSize(i);
|
||||
this.title.measure(View.MeasureSpec.makeMeasureSpec(((size - this.connectorWidth) - this.moreSize) - (this.rowMargins * 4), Integer.MIN_VALUE), View.MeasureSpec.makeMeasureSpec(0, 0));
|
||||
int makeMeasureSpec = View.MeasureSpec.makeMeasureSpec(this.moreSize, 1073741824);
|
||||
this.moreButton.measure(makeMeasureSpec, makeMeasureSpec);
|
||||
int measuredHeight = this.titleMarginTop + this.title.getMeasuredHeight();
|
||||
this.details.measure(View.MeasureSpec.makeMeasureSpec((size - this.connectorWidth) - (this.rowMargins * 3), Integer.MIN_VALUE), View.MeasureSpec.makeMeasureSpec(0, 0));
|
||||
if (this.details.getVisibility() != 8) {
|
||||
measuredHeight += this.details.getMeasuredHeight();
|
||||
}
|
||||
int max = Math.max(measuredHeight, this.minHeight);
|
||||
this.connector.measure(View.MeasureSpec.makeMeasureSpec(this.connectorWidth, 1073741824), View.MeasureSpec.makeMeasureSpec(max, 1073741824));
|
||||
setMeasuredDimension(size, max);
|
||||
}
|
||||
}
|
69
sources/com/squareup/leakcanary/internal/SquigglySpan.java
Normal file
69
sources/com/squareup/leakcanary/internal/SquigglySpan.java
Normal file
@@ -0,0 +1,69 @@
|
||||
package com.squareup.leakcanary.internal;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.ReplacementSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import com.squareup.leakcanary.R;
|
||||
|
||||
/* loaded from: classes.dex */
|
||||
class SquigglySpan extends ReplacementSpan {
|
||||
private final float amplitude;
|
||||
private final float halfStrokeWidth;
|
||||
private final float halfWaveHeight;
|
||||
private final Path path;
|
||||
private final float periodDegrees;
|
||||
private final int referenceColor;
|
||||
private final Paint squigglyPaint = new Paint(1);
|
||||
private int width;
|
||||
|
||||
SquigglySpan(Resources resources) {
|
||||
this.squigglyPaint.setStyle(Paint.Style.STROKE);
|
||||
this.squigglyPaint.setColor(resources.getColor(R.color.leak_canary_leak));
|
||||
float dimensionPixelSize = resources.getDimensionPixelSize(R.dimen.leak_canary_squiggly_span_stroke_width);
|
||||
this.squigglyPaint.setStrokeWidth(dimensionPixelSize);
|
||||
this.halfStrokeWidth = dimensionPixelSize / 2.0f;
|
||||
this.amplitude = resources.getDimensionPixelSize(R.dimen.leak_canary_squiggly_span_amplitude);
|
||||
this.periodDegrees = resources.getDimensionPixelSize(R.dimen.leak_canary_squiggly_span_period_degrees);
|
||||
this.path = new Path();
|
||||
this.halfWaveHeight = ((this.amplitude * 2.0f) + dimensionPixelSize) / 2.0f;
|
||||
this.referenceColor = resources.getColor(R.color.leak_canary_reference);
|
||||
}
|
||||
|
||||
public static void replaceUnderlineSpans(SpannableStringBuilder spannableStringBuilder, Resources resources) {
|
||||
for (UnderlineSpan underlineSpan : (UnderlineSpan[]) spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), UnderlineSpan.class)) {
|
||||
int spanStart = spannableStringBuilder.getSpanStart(underlineSpan);
|
||||
int spanEnd = spannableStringBuilder.getSpanEnd(underlineSpan);
|
||||
spannableStringBuilder.removeSpan(underlineSpan);
|
||||
spannableStringBuilder.setSpan(new SquigglySpan(resources), spanStart, spanEnd, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static void squigglyHorizontalPath(Path path, float f, float f2, float f3, float f4, float f5) {
|
||||
path.reset();
|
||||
path.moveTo(f, f3);
|
||||
float f6 = (float) (6.283185307179586d / f5);
|
||||
for (float f7 = 0.0f; f7 <= f2 - f; f7 += 1.0f) {
|
||||
path.lineTo(f + f7, (float) ((f4 * Math.sin((f6 * f7) + 40.0f)) + f3));
|
||||
}
|
||||
}
|
||||
|
||||
@Override // android.text.style.ReplacementSpan
|
||||
public void draw(Canvas canvas, CharSequence charSequence, int i, int i2, float f, int i3, int i4, int i5, Paint paint) {
|
||||
Path path = this.path;
|
||||
float f2 = this.halfStrokeWidth;
|
||||
squigglyHorizontalPath(path, f + f2, (this.width + f) - f2, i5 - this.halfWaveHeight, this.amplitude, this.periodDegrees);
|
||||
canvas.drawPath(this.path, this.squigglyPaint);
|
||||
paint.setColor(this.referenceColor);
|
||||
canvas.drawText(charSequence, i, i2, f, i4, paint);
|
||||
}
|
||||
|
||||
@Override // android.text.style.ReplacementSpan
|
||||
public int getSize(Paint paint, CharSequence charSequence, int i, int i2, Paint.FontMetricsInt fontMetricsInt) {
|
||||
this.width = (int) paint.measureText(charSequence, i, i2);
|
||||
return this.width;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user