/* * Copyright (C) 2015 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 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.util.Log; import com.squareup.leakcanary.internal.DisplayLeakActivity; import com.squareup.leakcanary.internal.HeapAnalyzerService; import static com.squareup.leakcanary.internal.LeakCanaryInternals.isInServiceProcess; import static com.squareup.leakcanary.internal.LeakCanaryInternals.setEnabled; public final class LeakCanary { /** * Creates a {@link RefWatcher} that works out of the box, and starts watching activity * references (on ICS+). */ public static RefWatcher install(Application application) { return install(application, DisplayLeakService.class); } /** * Creates a {@link RefWatcher} that reports results to the provided service, and starts watching * activity references (on ICS+). */ public static RefWatcher install(Application application, Class<? extends AbstractAnalysisResultService> listenerServiceClass) { if (isInAnalyzerProcess(application)) { return RefWatcher.DISABLED; } enableDisplayLeakActivity(application); HeapDump.Listener heapDumpListener = new ServiceHeapDumpListener(application, listenerServiceClass); RefWatcher refWatcher = androidWatcher(heapDumpListener); ActivityRefWatcher.installOnIcsPlus(application, refWatcher); return refWatcher; } /** * Creates a {@link RefWatcher} with a default configuration suitable for Android. */ public static RefWatcher androidWatcher(HeapDump.Listener heapDumpListener) { DebuggerControl debuggerControl = new AndroidDebuggerControl(); AndroidHeapDumper heapDumper = new AndroidHeapDumper(); heapDumper.cleanup(); ExcludedRefs excludedRefs = AndroidExcludedRefs.createAndroidDefaults().build(); return new RefWatcher(new AndroidWatchExecutor(), debuggerControl, GcTrigger.DEFAULT, heapDumper, heapDumpListener, excludedRefs); } public static void enableDisplayLeakActivity(Context context) { setEnabled(context, DisplayLeakActivity.class, true); } /** Returns a string representation of the result of a heap analysis. */ public static String leakInfo(Context context, HeapDump heapDump, AnalysisResult result) { PackageManager packageManager = context.getPackageManager(); String packageName = context.getPackageName(); PackageInfo packageInfo; try { packageInfo = packageManager.getPackageInfo(packageName, 0); } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException(e); } String versionName = packageInfo.versionName; int versionCode = packageInfo.versionCode; String info = "In " + packageName + ":" + versionName + ":" + versionCode + ".\n"; if (result.leakFound) { if (result.excludedLeak) { info += "* LEAK CAN BE IGNORED.\n"; } info += "* " + result.className; if (!heapDump.referenceName.equals("")) { info += " (" + heapDump.referenceName + ")"; } info += " has leaked:\n" + result.leakTrace.toString() + "\n"; } else if (result.failure != null) { info += "* FAILURE:\n" + Log.getStackTraceString(result.failure) + "\n"; } else { info += "* NO LEAK FOUND.\n\n"; } info += "* 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 + "\n" + "* Durations: watch=" + heapDump.watchDurationMs + "ms, gc=" + heapDump.gcDurationMs + "ms, heap dump=" + heapDump.heapDumpDurationMs + "ms, analysis=" + result.analysisDurationMs + "ms" + "\n"; return info; } /** * Whether the current process is the process running the {@link HeapAnalyzerService}, which is * a different process than the normal app process. */ public static boolean isInAnalyzerProcess(Context context) { return isInServiceProcess(context, HeapAnalyzerService.class); } private LeakCanary() { throw new AssertionError(); } }