MD5 校验值:195005882709ac21163d7a1b97aeec73
Crashes.java 文件包含反编译后的源代码,请注意,该内容仅供学习和参考使用,不得用于非法用途。
package com.microsoft.appcenter.crashes; import android.annotation.SuppressLint; import android.content.ComponentCallbacks2; import android.content.Context; import android.content.res.Configuration; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.support.annotation.WorkerThread; import android.util.Log; import com.google.appinventor.components.runtime.util.NanoHTTPD; import com.microsoft.appcenter.AbstractAppCenterService; import com.microsoft.appcenter.Constants; import com.microsoft.appcenter.channel.Channel; import com.microsoft.appcenter.crashes.ingestion.models.ErrorAttachmentLog; import com.microsoft.appcenter.crashes.ingestion.models.Exception; import com.microsoft.appcenter.crashes.ingestion.models.HandledErrorLog; import com.microsoft.appcenter.crashes.ingestion.models.ManagedErrorLog; import com.microsoft.appcenter.crashes.ingestion.models.json.ErrorAttachmentLogFactory; import com.microsoft.appcenter.crashes.ingestion.models.json.HandledErrorLogFactory; import com.microsoft.appcenter.crashes.ingestion.models.json.ManagedErrorLogFactory; import com.microsoft.appcenter.crashes.model.ErrorReport; import com.microsoft.appcenter.crashes.model.NativeException; import com.microsoft.appcenter.crashes.model.TestCrashException; import com.microsoft.appcenter.crashes.utils.ErrorLogHelper; import com.microsoft.appcenter.ingestion.models.Device; import com.microsoft.appcenter.ingestion.models.json.DefaultLogSerializer; import com.microsoft.appcenter.ingestion.models.json.LogFactory; import com.microsoft.appcenter.ingestion.models.json.LogSerializer; import com.microsoft.appcenter.utils.AppCenterLog; import com.microsoft.appcenter.utils.DeviceInfoHelper; import com.microsoft.appcenter.utils.HandlerUtils; import com.microsoft.appcenter.utils.async.AppCenterFuture; import com.microsoft.appcenter.utils.async.DefaultAppCenterFuture; import com.microsoft.appcenter.utils.context.SessionContext; import com.microsoft.appcenter.utils.context.UserIdContext; import com.microsoft.appcenter.utils.storage.FileManager; import com.microsoft.appcenter.utils.storage.SharedPreferencesManager; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; import java.util.UUID; import org.json.JSONException; public class Crashes extends AbstractAppCenterService { public static final int ALWAYS_SEND = 2; public static final int DONT_SEND = 1; @VisibleForTesting static final String ERROR_GROUP = "groupErrors"; public static final String LOG_TAG = "AppCenterCrashes"; private static final int MAX_ATTACHMENT_SIZE = 7340032; @VisibleForTesting public static final String PREF_KEY_ALWAYS_SEND = "com.microsoft.appcenter.crashes.always.send"; @VisibleForTesting static final String PREF_KEY_MEMORY_RUNNING_LEVEL = "com.microsoft.appcenter.crashes.memory"; public static final int SEND = 0; private static final String SERVICE_NAME = "Crashes"; private Context mContext; private CrashesListener mCrashesListener; private Device mDevice; private final Map<UUID, ErrorLogReport> mErrorReportCache; private boolean mHasReceivedMemoryWarningInLastSession; private long mInitializeTimestamp; private ErrorReport mLastSessionErrorReport; private LogSerializer mLogSerializer; private ComponentCallbacks2 mMemoryWarningListener; private boolean mSavedUncaughtException; private UncaughtExceptionHandler mUncaughtExceptionHandler; private final Map<UUID, ErrorLogReport> mUnprocessedErrorReports; private static final CrashesListener DEFAULT_ERROR_REPORTING_LISTENER = new DefaultCrashesListener(); @SuppressLint({"StaticFieldLeak"}) private static Crashes sInstance = null; private boolean mAutomaticProcessing = true; private final Map<String, LogFactory> mFactories = new HashMap(); public interface CallbackProcessor { void onCallBack(ErrorReport errorReport); boolean shouldDeleteThrowable(); } private static class DefaultCrashesListener extends AbstractCrashesListener { private DefaultCrashesListener() { } } public static class ErrorLogReport { private final ManagedErrorLog log; private final ErrorReport report; private ErrorLogReport(ManagedErrorLog managedErrorLog, ErrorReport errorReport) { this.log = managedErrorLog; this.report = errorReport; } } public interface ExceptionModelBuilder { Exception buildExceptionModel(); } private Crashes() { this.mFactories.put(ManagedErrorLog.TYPE, ManagedErrorLogFactory.getInstance()); this.mFactories.put(HandledErrorLog.TYPE, HandledErrorLogFactory.getInstance()); this.mFactories.put(ErrorAttachmentLog.TYPE, ErrorAttachmentLogFactory.getInstance()); this.mLogSerializer = new DefaultLogSerializer(); this.mLogSerializer.addLogFactory(ManagedErrorLog.TYPE, ManagedErrorLogFactory.getInstance()); this.mLogSerializer.addLogFactory(ErrorAttachmentLog.TYPE, ErrorAttachmentLogFactory.getInstance()); this.mCrashesListener = DEFAULT_ERROR_REPORTING_LISTENER; this.mUnprocessedErrorReports = new LinkedHashMap(); this.mErrorReportCache = new LinkedHashMap(); } public static void generateTestCrash() { if (Constants.APPLICATION_DEBUGGABLE) { throw new TestCrashException(); } AppCenterLog.warn(LOG_TAG, "The application is not debuggable so SDK won't generate test crash"); } @NonNull public static synchronized Crashes getInstance() { Crashes crashes; synchronized (Crashes.class) { if (sInstance == null) { sInstance = new Crashes(); } crashes = sInstance; } return crashes; } private synchronized AppCenterFuture<ErrorReport> getInstanceLastSessionCrashReport() { final DefaultAppCenterFuture defaultAppCenterFuture; defaultAppCenterFuture = new DefaultAppCenterFuture(); postAsyncGetter(new Runnable() { @Override public void run() { defaultAppCenterFuture.complete(Crashes.this.mLastSessionErrorReport); } }, defaultAppCenterFuture, null); return defaultAppCenterFuture; } public static AppCenterFuture<ErrorReport> getLastSessionCrashReport() { return getInstance().getInstanceLastSessionCrashReport(); } public static AppCenterFuture<String> getMinidumpDirectory() { return getInstance().getNewMinidumpDirectoryAsync(); } private synchronized AppCenterFuture<String> getNewMinidumpDirectoryAsync() { final DefaultAppCenterFuture defaultAppCenterFuture; defaultAppCenterFuture = new DefaultAppCenterFuture(); postAsyncGetter(new Runnable() { @Override public void run() { defaultAppCenterFuture.complete(ErrorLogHelper.getNewMinidumpSubfolderWithContextData(Crashes.this.mContext).getAbsolutePath()); } }, defaultAppCenterFuture, null); return defaultAppCenterFuture; } @VisibleForTesting public synchronized void handleUserConfirmation(final int i) { post(new Runnable() { @Override public void run() { if (i == 1) { Iterator it = Crashes.this.mUnprocessedErrorReports.keySet().iterator(); while (it.hasNext()) { UUID uuid = (UUID) it.next(); it.remove(); Crashes.this.removeAllStoredErrorLogFiles(uuid); } ErrorLogHelper.cleanPendingMinidumps(); return; } if (i == 2) { SharedPreferencesManager.putBoolean(Crashes.PREF_KEY_ALWAYS_SEND, true); } Iterator it2 = Crashes.this.mUnprocessedErrorReports.entrySet().iterator(); while (it2.hasNext()) { File file = null; ErrorAttachmentLog errorAttachmentLog = null; Map.Entry entry = (Map.Entry) it2.next(); ErrorLogReport errorLogReport = (ErrorLogReport) entry.getValue(); if (errorLogReport.report.getDevice() != null && Constants.WRAPPER_SDK_NAME_NDK.equals(errorLogReport.report.getDevice().getWrapperSdkName())) { Exception exception = errorLogReport.log.getException(); String minidumpFilePath = exception.getMinidumpFilePath(); exception.setMinidumpFilePath(null); if (minidumpFilePath == null) { minidumpFilePath = exception.getStackTrace(); exception.setStackTrace(null); } if (minidumpFilePath != null) { file = new File(minidumpFilePath); errorAttachmentLog = ErrorAttachmentLog.attachmentWithBinary(FileManager.readBytes(file), "minidump.dmp", NanoHTTPD.MIME_DEFAULT_BINARY); } else { AppCenterLog.warn(Crashes.LOG_TAG, "NativeException found without minidump."); } } Crashes.this.mChannel.enqueue(errorLogReport.log, Crashes.ERROR_GROUP, 2); if (errorAttachmentLog != null) { Crashes.this.sendErrorAttachment(errorLogReport.log.getId(), Collections.singleton(errorAttachmentLog)); file.delete(); } if (Crashes.this.mAutomaticProcessing) { Crashes.this.sendErrorAttachment(errorLogReport.log.getId(), Crashes.this.mCrashesListener.getErrorAttachments(errorLogReport.report)); } it2.remove(); ErrorLogHelper.removeStoredErrorLogFile((UUID) entry.getKey()); } } }); } public static AppCenterFuture<Boolean> hasCrashedInLastSession() { return getInstance().hasInstanceCrashedInLastSession(); } private synchronized AppCenterFuture<Boolean> hasInstanceCrashedInLastSession() { final DefaultAppCenterFuture defaultAppCenterFuture; defaultAppCenterFuture = new DefaultAppCenterFuture(); postAsyncGetter(new Runnable() { @Override public void run() { defaultAppCenterFuture.complete(Boolean.valueOf(Crashes.this.mLastSessionErrorReport != null)); } }, defaultAppCenterFuture, false); return defaultAppCenterFuture; } private synchronized AppCenterFuture<Boolean> hasInstanceReceivedMemoryWarningInLastSession() { final DefaultAppCenterFuture defaultAppCenterFuture; defaultAppCenterFuture = new DefaultAppCenterFuture(); postAsyncGetter(new Runnable() { @Override public void run() { defaultAppCenterFuture.complete(Boolean.valueOf(Crashes.this.mHasReceivedMemoryWarningInLastSession)); } }, defaultAppCenterFuture, false); return defaultAppCenterFuture; } public static AppCenterFuture<Boolean> hasReceivedMemoryWarningInLastSession() { return getInstance().hasInstanceReceivedMemoryWarningInLastSession(); } private void initialize() { boolean isInstanceEnabled = isInstanceEnabled(); this.mInitializeTimestamp = isInstanceEnabled ? System.currentTimeMillis() : -1L; if (isInstanceEnabled) { this.mUncaughtExceptionHandler = new UncaughtExceptionHandler(); this.mUncaughtExceptionHandler.register(); processMinidumpFiles(); } else if (this.mUncaughtExceptionHandler != null) { this.mUncaughtExceptionHandler.unregister(); this.mUncaughtExceptionHandler = null; } } public static AppCenterFuture<Boolean> isEnabled() { return getInstance().isInstanceEnabledAsync(); } private static boolean isMemoryRunningLevelWasReceived(int i) { return i == 5 || i == 10 || i == 15 || i == 80; } public static void notifyUserConfirmation(int i) { getInstance().handleUserConfirmation(i); } private void processMinidumpFiles() { File file; for (File file2 : ErrorLogHelper.getNewMinidumpFiles()) { if (file2.isDirectory()) { File[] listFiles = file2.listFiles(new FilenameFilter() { @Override public boolean accept(File file3, String str) { return str.endsWith(ErrorLogHelper.MINIDUMP_FILE_EXTENSION); } }); if (listFiles != null && listFiles.length != 0) { for (File file3 : listFiles) { processSingleMinidump(file3, file2); } } } else { AppCenterLog.debug(LOG_TAG, "Found a minidump from a previous SDK version."); processSingleMinidump(file2, file2); } } File lastErrorLogFile = ErrorLogHelper.getLastErrorLogFile(); while (true) { file = lastErrorLogFile; if (file == null || file.length() != 0) { break; } AppCenterLog.warn(LOG_TAG, "Deleting empty error file: " + file); file.delete(); lastErrorLogFile = ErrorLogHelper.getLastErrorLogFile(); } if (file != null) { AppCenterLog.debug(LOG_TAG, "Processing crash report for the last session."); String read = FileManager.read(file); if (read == null) { AppCenterLog.error(LOG_TAG, "Error reading last session error log."); } else { try { this.mLastSessionErrorReport = buildErrorReport((ManagedErrorLog) this.mLogSerializer.deserializeLog(read, null)); AppCenterLog.debug(LOG_TAG, "Processed crash report for the last session."); } catch (JSONException e) { AppCenterLog.error(LOG_TAG, "Error parsing last session error log.", e); } } } ErrorLogHelper.removeStaleMinidumpSubfolders(); } private void processPendingErrors() { for (File file : ErrorLogHelper.getStoredErrorLogFiles()) { AppCenterLog.debug(LOG_TAG, "Process pending error file: " + file); String read = FileManager.read(file); if (read != null) { try { ManagedErrorLog managedErrorLog = (ManagedErrorLog) this.mLogSerializer.deserializeLog(read, null); UUID id = managedErrorLog.getId(); ErrorReport buildErrorReport = buildErrorReport(managedErrorLog); if (buildErrorReport == null) { removeAllStoredErrorLogFiles(id); } else if (!this.mAutomaticProcessing || this.mCrashesListener.shouldProcess(buildErrorReport)) { if (!this.mAutomaticProcessing) { AppCenterLog.debug(LOG_TAG, "CrashesListener.shouldProcess returned true, continue processing log: " + id.toString()); } this.mUnprocessedErrorReports.put(id, this.mErrorReportCache.get(id)); } else { AppCenterLog.debug(LOG_TAG, "CrashesListener.shouldProcess returned false, clean up and ignore log: " + id.toString()); removeAllStoredErrorLogFiles(id); } } catch (JSONException e) { AppCenterLog.error(LOG_TAG, "Error parsing error log. Deleting invalid file: " + file, e); file.delete(); } } } this.mHasReceivedMemoryWarningInLastSession = isMemoryRunningLevelWasReceived(SharedPreferencesManager.getInt(PREF_KEY_MEMORY_RUNNING_LEVEL, -1)); if (this.mHasReceivedMemoryWarningInLastSession) { AppCenterLog.debug(LOG_TAG, "The application received a low memory warning in the last session."); } SharedPreferencesManager.remove(PREF_KEY_MEMORY_RUNNING_LEVEL); if (this.mAutomaticProcessing) { sendCrashReportsOrAwaitUserConfirmation(); } } private void processSingleMinidump(File file, File file2) { AppCenterLog.debug(LOG_TAG, "Process pending minidump file: " + file); long lastModified = file.lastModified(); File file3 = new File(ErrorLogHelper.getPendingMinidumpDirectory(), file.getName()); Exception exception = new Exception(); exception.setType("minidump"); exception.setWrapperSdkName(Constants.WRAPPER_SDK_NAME_NDK); exception.setMinidumpFilePath(file3.getPath()); ManagedErrorLog managedErrorLog = new ManagedErrorLog(); managedErrorLog.setException(exception); managedErrorLog.setTimestamp(new Date(lastModified)); managedErrorLog.setFatal(true); managedErrorLog.setId(ErrorLogHelper.parseLogFolderUuid(file2)); SessionContext.SessionInfo sessionAt = SessionContext.getInstance().getSessionAt(lastModified); if (sessionAt == null || sessionAt.getAppLaunchTimestamp() > lastModified) { managedErrorLog.setAppLaunchTimestamp(managedErrorLog.getTimestamp()); } else { managedErrorLog.setAppLaunchTimestamp(new Date(sessionAt.getAppLaunchTimestamp())); } managedErrorLog.setProcessId(0); managedErrorLog.setProcessName(""); managedErrorLog.setUserId(UserIdContext.getInstance().getUserId()); try { Device storedDeviceInfo = ErrorLogHelper.getStoredDeviceInfo(file2); if (storedDeviceInfo == null) { storedDeviceInfo = getDeviceInfo(this.mContext); storedDeviceInfo.setWrapperSdkName(Constants.WRAPPER_SDK_NAME_NDK); } managedErrorLog.setDevice(storedDeviceInfo); saveErrorLogFiles(new NativeException(), managedErrorLog); if (file.renameTo(file3)) { } else { throw new IOException("Failed to move file"); } } catch (Exception e) { file.delete(); removeAllStoredErrorLogFiles(managedErrorLog.getId()); AppCenterLog.error(LOG_TAG, "Failed to process new minidump file: " + file, e); } } private synchronized UUID queueException(@NonNull final ExceptionModelBuilder exceptionModelBuilder, Map<String, String> map, final Iterable<ErrorAttachmentLog> iterable) { final UUID randomUUID; final String userId = UserIdContext.getInstance().getUserId(); randomUUID = UUID.randomUUID(); final Map<String, String> validateProperties = ErrorLogHelper.validateProperties(map, "HandledError"); post(new Runnable() { @Override public void run() { HandledErrorLog handledErrorLog = new HandledErrorLog(); handledErrorLog.setId(randomUUID); handledErrorLog.setUserId(userId); handledErrorLog.setException(exceptionModelBuilder.buildExceptionModel()); handledErrorLog.setProperties(validateProperties); Crashes.this.mChannel.enqueue(handledErrorLog, Crashes.ERROR_GROUP, 1); Crashes.this.sendErrorAttachment(randomUUID, iterable); } }); return randomUUID; } private synchronized void queueException(@NonNull final Throwable th, Map<String, String> map, Iterable<ErrorAttachmentLog> iterable) { queueException(new ExceptionModelBuilder() { @Override public Exception buildExceptionModel() { return ErrorLogHelper.getModelExceptionFromThrowable(th); } }, map, iterable); } public void removeAllStoredErrorLogFiles(UUID uuid) { ErrorLogHelper.removeStoredErrorLogFile(uuid); removeStoredThrowable(uuid); } public void removeStoredThrowable(UUID uuid) { this.mErrorReportCache.remove(uuid); WrapperSdkExceptionManager.deleteWrapperExceptionData(uuid); ErrorLogHelper.removeStoredThrowableFile(uuid); } @NonNull private UUID saveErrorLogFiles(Throwable th, ManagedErrorLog managedErrorLog) throws JSONException, IOException { Throwable th2 = th; File errorStorageDirectory = ErrorLogHelper.getErrorStorageDirectory(); UUID id = managedErrorLog.getId(); String uuid = id.toString(); AppCenterLog.debug(LOG_TAG, "Saving uncaught exception."); File file = new File(errorStorageDirectory, uuid + ErrorLogHelper.ERROR_LOG_FILE_EXTENSION); FileManager.write(file, this.mLogSerializer.serializeLog(managedErrorLog)); AppCenterLog.debug(LOG_TAG, "Saved JSON content for ingestion into " + file); File file2 = new File(errorStorageDirectory, uuid + ErrorLogHelper.THROWABLE_FILE_EXTENSION); if (th2 != null) { try { String stackTraceString = Log.getStackTraceString(th2); FileManager.write(file2, stackTraceString); AppCenterLog.debug(LOG_TAG, "Saved stack trace as is for client side inspection in " + file2 + " stack trace:" + stackTraceString); } catch (StackOverflowError e) { AppCenterLog.error(LOG_TAG, "Failed to store stack trace.", e); th2 = null; file2.delete(); } } if (th2 == null) { if (!file2.createNewFile()) { throw new IOException(file2.getName()); } AppCenterLog.debug(LOG_TAG, "Saved empty Throwable file in " + file2); } return id; } @WorkerThread public static void saveMemoryRunningLevel(int i) { SharedPreferencesManager.putInt(PREF_KEY_MEMORY_RUNNING_LEVEL, i); AppCenterLog.debug(LOG_TAG, String.format("The memory running level (%s) was saved.", Integer.valueOf(i))); } public boolean sendCrashReportsOrAwaitUserConfirmation() { final boolean z = SharedPreferencesManager.getBoolean(PREF_KEY_ALWAYS_SEND, false); HandlerUtils.runOnUiThread(new Runnable() { @Override public void run() { if (Crashes.this.mUnprocessedErrorReports.size() > 0) { if (z) { AppCenterLog.debug(Crashes.LOG_TAG, "The flag for user confirmation is set to ALWAYS_SEND, will send logs."); Crashes.this.handleUserConfirmation(0); } else if (!Crashes.this.mAutomaticProcessing) { AppCenterLog.debug(Crashes.LOG_TAG, "Automatic processing disabled, will wait for explicit user confirmation."); } else if (Crashes.this.mCrashesListener.shouldAwaitUserConfirmation()) { AppCenterLog.debug(Crashes.LOG_TAG, "CrashesListener.shouldAwaitUserConfirmation returned true, wait sending logs."); } else { AppCenterLog.debug(Crashes.LOG_TAG, "CrashesListener.shouldAwaitUserConfirmation returned false, will send logs."); Crashes.this.handleUserConfirmation(0); } } } }); return z; } @WorkerThread public void sendErrorAttachment(UUID uuid, Iterable<ErrorAttachmentLog> iterable) { if (iterable == null) { AppCenterLog.debug(LOG_TAG, "Error report: " + uuid.toString() + " does not have any attachment."); return; } for (ErrorAttachmentLog errorAttachmentLog : iterable) { if (errorAttachmentLog != null) { errorAttachmentLog.setId(UUID.randomUUID()); errorAttachmentLog.setErrorId(uuid); if (!errorAttachmentLog.isValid()) { AppCenterLog.error(LOG_TAG, "Not all required fields are present in ErrorAttachmentLog."); } else if (errorAttachmentLog.getData().length > MAX_ATTACHMENT_SIZE) { AppCenterLog.error(LOG_TAG, String.format(Locale.ENGLISH, "Discarding attachment with size above %d bytes: size=%d, fileName=%s.", Integer.valueOf(MAX_ATTACHMENT_SIZE), Integer.valueOf(errorAttachmentLog.getData().length), errorAttachmentLog.getFileName())); } else { this.mChannel.enqueue(errorAttachmentLog, ERROR_GROUP, 1); } } else { AppCenterLog.warn(LOG_TAG, "Skipping null ErrorAttachmentLog."); } } } public static AppCenterFuture<Void> setEnabled(boolean z) { return getInstance().setInstanceEnabledAsync(z); } public static void setListener(CrashesListener crashesListener) { getInstance().setInstanceListener(crashesListener); } public static void trackError(Throwable th) { trackError(th, null, null); } public static void trackError(Throwable th, Map<String, String> map, Iterable<ErrorAttachmentLog> iterable) { getInstance().queueException(th, map, iterable); } @VisibleForTesting static synchronized void unsetInstance() { synchronized (Crashes.class) { sInstance = null; } } @Override protected synchronized void applyEnabledState(boolean z) { initialize(); if (z) { this.mMemoryWarningListener = new ComponentCallbacks2() { @Override public void onConfigurationChanged(@NonNull Configuration configuration) { } @Override public void onLowMemory() { Crashes.saveMemoryRunningLevel(80); } @Override public void onTrimMemory(int i) { Crashes.saveMemoryRunningLevel(i); } }; this.mContext.registerComponentCallbacks(this.mMemoryWarningListener); } else { File[] listFiles = ErrorLogHelper.getErrorStorageDirectory().listFiles(); if (listFiles != null) { for (File file : listFiles) { AppCenterLog.debug(LOG_TAG, "Deleting file " + file); if (!file.delete()) { AppCenterLog.warn(LOG_TAG, "Failed to delete file " + file); } } } AppCenterLog.info(LOG_TAG, "Deleted crashes local files"); this.mErrorReportCache.clear(); this.mLastSessionErrorReport = null; this.mContext.unregisterComponentCallbacks(this.mMemoryWarningListener); this.mMemoryWarningListener = null; SharedPreferencesManager.remove(PREF_KEY_MEMORY_RUNNING_LEVEL); } } @VisibleForTesting @Nullable ErrorReport buildErrorReport(ManagedErrorLog managedErrorLog) { UUID id = managedErrorLog.getId(); if (this.mErrorReportCache.containsKey(id)) { ErrorReport errorReport = this.mErrorReportCache.get(id).report; errorReport.setDevice(managedErrorLog.getDevice()); return errorReport; } File storedThrowableFile = ErrorLogHelper.getStoredThrowableFile(id); if (storedThrowableFile == null) { return null; } String str = null; if (storedThrowableFile.length() > 0) { str = FileManager.read(storedThrowableFile); } ErrorReport errorReportFromErrorLog = ErrorLogHelper.getErrorReportFromErrorLog(managedErrorLog, str); this.mErrorReportCache.put(id, new ErrorLogReport(managedErrorLog, errorReportFromErrorLog)); return errorReportFromErrorLog; } @Override protected Channel.GroupListener getChannelListener() { return new Channel.GroupListener() { private void processCallback(final com.microsoft.appcenter.ingestion.models.Log log, final CallbackProcessor callbackProcessor) { Crashes.this.post(new Runnable() { @Override public void run() { if (!(log instanceof ManagedErrorLog)) { if ((log instanceof ErrorAttachmentLog) || (log instanceof HandledErrorLog)) { return; } AppCenterLog.warn(Crashes.LOG_TAG, "A different type of log comes to crashes: " + log.getClass().getName()); return; } ManagedErrorLog managedErrorLog = (ManagedErrorLog) log; final ErrorReport buildErrorReport = Crashes.this.buildErrorReport(managedErrorLog); UUID id = managedErrorLog.getId(); if (buildErrorReport == null) { AppCenterLog.warn(Crashes.LOG_TAG, "Cannot find crash report for the error log: " + id); return; } if (callbackProcessor.shouldDeleteThrowable()) { Crashes.this.removeStoredThrowable(id); } HandlerUtils.runOnUiThread(new Runnable() { @Override public void run() { callbackProcessor.onCallBack(buildErrorReport); } }); } }); } @Override public void onBeforeSending(com.microsoft.appcenter.ingestion.models.Log log) { processCallback(log, new CallbackProcessor() { @Override public void onCallBack(ErrorReport errorReport) { Crashes.this.mCrashesListener.onBeforeSending(errorReport); } @Override public boolean shouldDeleteThrowable() { return false; } }); } @Override public void onFailure(com.microsoft.appcenter.ingestion.models.Log log, final Exception exc) { processCallback(log, new CallbackProcessor() { @Override public void onCallBack(ErrorReport errorReport) { Crashes.this.mCrashesListener.onSendingFailed(errorReport, exc); } @Override public boolean shouldDeleteThrowable() { return true; } }); } @Override public void onSuccess(com.microsoft.appcenter.ingestion.models.Log log) { processCallback(log, new CallbackProcessor() { @Override public void onCallBack(ErrorReport errorReport) { Crashes.this.mCrashesListener.onSendingSucceeded(errorReport); } @Override public boolean shouldDeleteThrowable() { return true; } }); } }; } public synchronized Device getDeviceInfo(Context context) throws DeviceInfoHelper.DeviceInfoException { if (this.mDevice == null) { this.mDevice = DeviceInfoHelper.getDeviceInfo(context); } return this.mDevice; } @Override protected String getGroupName() { return ERROR_GROUP; } public synchronized long getInitializeTimestamp() { return this.mInitializeTimestamp; } @VisibleForTesting CrashesListener getInstanceListener() { return this.mCrashesListener; } @Override public Map<String, LogFactory> getLogFactories() { return this.mFactories; } @Override protected String getLoggerTag() { return LOG_TAG; } @Override public String getServiceName() { return SERVICE_NAME; } @Override public int getTriggerCount() { return 1; } @VisibleForTesting UncaughtExceptionHandler getUncaughtExceptionHandler() { return this.mUncaughtExceptionHandler; } public AppCenterFuture<Collection<ErrorReport>> getUnprocessedErrorReports() { final DefaultAppCenterFuture defaultAppCenterFuture = new DefaultAppCenterFuture(); postAsyncGetter(new Runnable() { @Override public void run() { ArrayList arrayList = new ArrayList(Crashes.this.mUnprocessedErrorReports.size()); Iterator it = Crashes.this.mUnprocessedErrorReports.values().iterator(); while (it.hasNext()) { arrayList.add(((ErrorLogReport) it.next()).report); } defaultAppCenterFuture.complete(arrayList); } }, defaultAppCenterFuture, Collections.emptyList()); return defaultAppCenterFuture; } @Override public synchronized void onStarted(@NonNull Context context, @NonNull Channel channel, String str, String str2, boolean z) { this.mContext = context; if (!isInstanceEnabled()) { ErrorLogHelper.removeMinidumpFolder(); AppCenterLog.debug(LOG_TAG, "Clean up minidump folder."); } super.onStarted(context, channel, str, str2, z); if (isInstanceEnabled()) { processPendingErrors(); } } public synchronized UUID queueException(@NonNull final Exception exception, Map<String, String> map, Iterable<ErrorAttachmentLog> iterable) { return queueException(new ExceptionModelBuilder() { @Override public Exception buildExceptionModel() { return exception; } }, map, iterable); } public UUID saveUncaughtException(Thread thread, Throwable th, Exception exception) throws JSONException, IOException { if (isEnabled().get().booleanValue() && !this.mSavedUncaughtException) { this.mSavedUncaughtException = true; return saveErrorLogFiles(th, ErrorLogHelper.createErrorLog(this.mContext, thread, exception, Thread.getAllStackTraces(), this.mInitializeTimestamp, true)); } return null; } public void saveUncaughtException(Thread thread, Throwable th) { try { saveUncaughtException(thread, th, ErrorLogHelper.getModelExceptionFromThrowable(th)); } catch (IOException e) { AppCenterLog.error(LOG_TAG, "Error writing error log to file", e); } catch (JSONException e2) { AppCenterLog.error(LOG_TAG, "Error serializing error log to JSON", e2); } } public AppCenterFuture<Boolean> sendCrashReportsOrAwaitUserConfirmation(final Collection<String> collection) { final DefaultAppCenterFuture defaultAppCenterFuture = new DefaultAppCenterFuture(); postAsyncGetter(new Runnable() { @Override public void run() { Iterator it = Crashes.this.mUnprocessedErrorReports.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); UUID uuid = (UUID) entry.getKey(); String id = ((ErrorLogReport) entry.getValue()).report.getId(); if (collection == null || !collection.contains(id)) { AppCenterLog.debug(Crashes.LOG_TAG, "CrashesListener.shouldProcess returned false, clean up and ignore log: " + id); Crashes.this.removeAllStoredErrorLogFiles(uuid); it.remove(); } else { AppCenterLog.debug(Crashes.LOG_TAG, "CrashesListener.shouldProcess returned true, continue processing log: " + id); } } defaultAppCenterFuture.complete(Boolean.valueOf(Crashes.this.sendCrashReportsOrAwaitUserConfirmation())); } }, defaultAppCenterFuture, false); return defaultAppCenterFuture; } @WorkerThread public void sendErrorAttachments(final String str, final Iterable<ErrorAttachmentLog> iterable) { post(new Runnable() { @Override public void run() { try { Crashes.this.sendErrorAttachment(UUID.fromString(str), iterable); } catch (RuntimeException e) { AppCenterLog.error(Crashes.LOG_TAG, "Error report identifier has an invalid format for sending attachments."); } } }); } public void setAutomaticProcessing(boolean z) { this.mAutomaticProcessing = z; } @VisibleForTesting synchronized void setInstanceListener(CrashesListener crashesListener) { CrashesListener crashesListener2 = crashesListener; synchronized (this) { if (crashesListener2 == null) { crashesListener2 = DEFAULT_ERROR_REPORTING_LISTENER; } this.mCrashesListener = crashesListener2; } } @VisibleForTesting void setLogSerializer(LogSerializer logSerializer) { this.mLogSerializer = logSerializer; } @VisibleForTesting void setUncaughtExceptionHandler(UncaughtExceptionHandler uncaughtExceptionHandler) { this.mUncaughtExceptionHandler = uncaughtExceptionHandler; } }