/* * Copyright 2016 * * 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 org.acra.collector; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; import org.acra.ReportField; import org.acra.builder.ReportBuilder; import org.acra.model.Element; import org.acra.model.StringElement; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.util.Set; /** * Collects the holy stacktrace * * @author F43nd1r * @since 4.9.1 */ final class StacktraceCollector extends Collector { StacktraceCollector() { super(ReportField.STACK_TRACE, ReportField.STACK_TRACE_HASH); } @Override boolean shouldCollect(Set<ReportField> crashReportFields, ReportField collect, ReportBuilder reportBuilder) { return collect == ReportField.STACK_TRACE || super.shouldCollect(crashReportFields, collect, reportBuilder); } @NonNull @Override Element collect(ReportField reportField, ReportBuilder reportBuilder) { switch (reportField) { case STACK_TRACE: return new StringElement( getStackTrace(reportBuilder.getMessage(), reportBuilder.getException())); case STACK_TRACE_HASH: return new StringElement(getStackTraceHash(reportBuilder.getException())); default: //will not happen if used correctly throw new IllegalArgumentException(); } } @NonNull private String getStackTrace(@Nullable String msg, @Nullable Throwable th) { final Writer result = new StringWriter(); final PrintWriter printWriter = new PrintWriter(result); if (msg != null && !TextUtils.isEmpty(msg)) { printWriter.println(msg); } // If the exception was thrown in a background thread inside // AsyncTask, then the actual exception can be found with getCause Throwable cause = th; while (cause != null) { cause.printStackTrace(printWriter); cause = cause.getCause(); } final String stacktraceAsString = result.toString(); printWriter.close(); return stacktraceAsString; } @NonNull private String getStackTraceHash(@Nullable Throwable th) { final StringBuilder res = new StringBuilder(); Throwable cause = th; while (cause != null) { final StackTraceElement[] stackTraceElements = cause.getStackTrace(); for (final StackTraceElement e : stackTraceElements) { res.append(e.getClassName()); res.append(e.getMethodName()); } cause = cause.getCause(); } return Integer.toHexString(res.toString().hashCode()); } }