package com.demondevelopers.example.crashreporting; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.lang.Thread.UncaughtExceptionHandler; import android.annotation.TargetApi; import android.app.Activity; import android.app.ApplicationErrorReport; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.os.Build; import android.os.Debug; import android.util.Log; import android.view.View; import android.view.Window; public class ReportHandler { private static final String TAG = ReportHandler.class.getSimpleName(); private static final String[] EVENT_LOG_CMD = { "logcat", "-d", "-b", "events", "-v", "time" }; private static final String[] SYSTEM_LOG_CMD = { "logcat", "-d", "-v", "time" }; private static Context sAppContext; private static String sEmailAddress; private static volatile boolean mCrashing = false; public static void install(Context context, String emailAddress) { sAppContext = context.getApplicationContext(); sEmailAddress = emailAddress; // NOTE: It does not generate crash reports when you are debugging your app. if(!Debug.waitingForDebugger() && !Debug.isDebuggerConnected()){ Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler()); } } public static String getEmailAddress() { return sEmailAddress; } private static class UncaughtHandler implements UncaughtExceptionHandler { public void uncaughtException(Thread thread, Throwable ex) { try{ // Don't re-enter -- avoid infinite loops if crash-reporting crashes. if(mCrashing){ return; } mCrashing = true; Log.e(TAG, "FATAL EXCEPTION: " + thread.getName(), ex); // Attempt to save a screenshot (no permissions required!) String screenshot = null; Bitmap bm = ReportHandler.getScreenshot(); if(bm != null){ screenshot = ReportHandler.saveScreenShot(bm); bm.recycle(); } // Bring up crash dialog sAppContext.startActivity(ReportActivity .createIntent(sAppContext, ex, screenshot)); } catch(Throwable t2){ try{ Log.e(TAG, "Error reporting crash", t2); } catch(Throwable t3){ // Even Log.e() fails! Oh well. } } finally{ // Try everything to make sure this process goes away. android.os.Process.killProcess(android.os.Process.myPid()); System.exit(10); // magic numbers! } } } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public static void showDefaultReportActivity(Context context, Throwable th) { String packageName = context.getPackageName(); PackageManager pm = context.getPackageManager(); // Not perfect.. but it'll have to do. ApplicationErrorReport report = new ApplicationErrorReport(); report.installerPackageName = pm.getInstallerPackageName(packageName); report.packageName = packageName; report.processName = packageName; report.time = System.currentTimeMillis(); report.systemApp = false; report.type = ApplicationErrorReport.TYPE_CRASH; report.crashInfo = new ApplicationErrorReport.CrashInfo(th); sAppContext.startActivity(new Intent(Intent.ACTION_APP_ERROR) .setPackage("com.google.android.feedback") .putExtra(Intent.EXTRA_BUG_REPORT, report) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } public static Bitmap getScreenshot() { Activity activity = ReportingActivity.getForegroundInstance(); if(activity == null){ return null; } Window window = activity.getWindow(); if(window == null){ return null; } View view = window.getDecorView(); if(view == null){ return null; } view.buildDrawingCache(); Bitmap cache = view.getDrawingCache(); Bitmap screenshot = cache.copy(cache.getConfig(), false); cache = null; if(!view.isDrawingCacheEnabled()){ view.destroyDrawingCache(); } return screenshot; } public static String saveScreenShot(Bitmap bitmap) { FileOutputStream stream = null; try{ File temp = File.createTempFile("crash-report", ".jpg"); stream = new FileOutputStream(temp); bitmap.compress(CompressFormat.JPEG, 80, stream); return temp.getAbsolutePath(); } catch(IOException e){ Log.e(TAG, e.getMessage(), e); } finally{ if(stream != null){ try{ stream.close(); } catch(IOException e){ Log.e(TAG, e.getMessage(), e); } } } return null; } public static String saveEventLog() { return captureCommand("event-log", EVENT_LOG_CMD); } public static String saveSystemLog() { return captureCommand("system-log", SYSTEM_LOG_CMD); } private static String captureCommand(String filePrefix, String[] command) { Process process = null; InputStream is = null; FileOutputStream fos = null; try{ File temp = File.createTempFile(filePrefix, ".txt"); fos = new FileOutputStream(temp); process = new ProcessBuilder(command) .redirectErrorStream(true) .start(); is = process.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos)); String line; while((line = reader.readLine()) != null){ writer.append(line).append('\n'); } writer.flush(); return temp.getAbsolutePath(); } catch(IOException e){ Log.e(TAG, e.getMessage(), e); } finally{ if(process != null){ process.destroy(); } if(fos != null){ try{ fos.close(); } catch(IOException e){ Log.e(TAG, e.getMessage(), e); } } if(is != null){ try{ is.close(); } catch(IOException e){ Log.e(TAG, e.getMessage(), e); } } } return null; } }