diff options
author | Pankaj Garg <pgarg@codeaurora.org> | 2014-10-02 13:52:46 -0700 |
---|---|---|
committer | WebTech Code Review <code-review@localhost> | 2014-10-20 17:45:27 -0700 |
commit | f04dbda03764a4c69694a6a513e41c56fa4a1b52 (patch) | |
tree | 2fc8465381971b502bda2efb523a626fb71806d8 /src/com/android/browser/CrashLogExceptionHandler.java | |
parent | 1c4d6b80bf174cf4785d28288c68dd5447a178c5 (diff) | |
download | android_packages_apps_Gello-f04dbda03764a4c69694a6a513e41c56fa4a1b52.tar.gz android_packages_apps_Gello-f04dbda03764a4c69694a6a513e41c56fa4a1b52.tar.bz2 android_packages_apps_Gello-f04dbda03764a4c69694a6a513e41c56fa4a1b52.zip |
Save crash logs on a server
If the browser crashes, save the crash logs on a remote server.
Change-Id: I2758c11909d6d85d3472f5254e54dd9cbece728c
Diffstat (limited to 'src/com/android/browser/CrashLogExceptionHandler.java')
-rw-r--r-- | src/com/android/browser/CrashLogExceptionHandler.java | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/src/com/android/browser/CrashLogExceptionHandler.java b/src/com/android/browser/CrashLogExceptionHandler.java new file mode 100644 index 00000000..92075707 --- /dev/null +++ b/src/com/android/browser/CrashLogExceptionHandler.java @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package com.android.browser; + +import android.app.Activity; +import android.content.Context; +import android.os.Build; +import android.os.Build.VERSION; +import android.os.SystemClock; +import android.net.http.AndroidHttpClient; +import android.util.Log; + +import org.codeaurora.swe.BrowserCommandLine; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.client.ClientProtocolException; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.json.JSONException; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.lang.Integer; +import java.lang.StringBuilder; +import java.lang.System; +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.Calendar; + +public class CrashLogExceptionHandler implements Thread.UncaughtExceptionHandler { + + private static final String CRASH_LOG_FILE = "crash.log"; + private static final String CRASH_LOG_SERVER_CMD = "crash-log-server"; + private static final String CRASH_LOG_MAX_FILE_SIZE_CMD = "crash-log-max-file-size"; + + private final static String LOGTAG = "CrashLog"; + + private Context mAppContext = null; + + private UncaughtExceptionHandler mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); + + private String mLogServer = new String(); + + private boolean mOverrideHandler = false; + + private int mMaxLogFileSize = 1024 * 1024; + + public CrashLogExceptionHandler(Context ctx) { + mAppContext = ctx; + BrowserCommandLine cl = new BrowserCommandLine(); + if (cl.hasSwitch(CRASH_LOG_SERVER_CMD)) { + mLogServer = cl.getSwitchValue(CRASH_LOG_SERVER_CMD); + if (mLogServer != null) { + uploadPastCrashLog(); + mOverrideHandler = true; + } + } + + try { + int size = Integer.parseInt(cl.getSwitchValue(CRASH_LOG_MAX_FILE_SIZE_CMD, + Integer.toString(mMaxLogFileSize))); + mMaxLogFileSize = size; + } catch (NumberFormatException nfe) { + Log.e(LOGTAG,"Max log file size is not configured properly. Using default: " + + mMaxLogFileSize); + } + + } + + private void saveCrashLog(String crashLog) { + // Check if log file exists and it's current size + try { + File file = new File(mAppContext.getFilesDir(), CRASH_LOG_FILE); + if (file.exists()) { + if (file.length() > mMaxLogFileSize) { + Log.e(LOGTAG,"CRASH Log file size(" + file.length() + + ") exceeded max log file size(" + + mMaxLogFileSize + ")"); + return; + } + } + } catch (NullPointerException npe) { + Log.e(LOGTAG,"Exception while checking file size: " + npe); + } + + FileOutputStream crashLogFile = null; + try { + crashLogFile = mAppContext.openFileOutput(CRASH_LOG_FILE, Context.MODE_APPEND); + crashLogFile.write(crashLog.getBytes()); + } catch(IOException ioe) { + Log.e(LOGTAG,"Exception while writing file: " + ioe); + } finally { + if (crashLogFile != null) { + try { + crashLogFile.close(); + } catch (IOException ignore) { + } + } + } + } + + private void uploadPastCrashLog() { + FileInputStream crashLogFile = null; + BufferedReader reader = null; + try { + crashLogFile = mAppContext.openFileInput(CRASH_LOG_FILE); + + reader = new BufferedReader(new InputStreamReader(crashLogFile)); + StringBuilder crashLog = new StringBuilder(); + String line = reader.readLine(); + if (line != null) { + crashLog.append(line); + } + + // Typically there's only one line (JSON string) in the crash + // log file. This loop would not be executed. + while ((line = reader.readLine()) != null) { + crashLog.append("\n").append(line); + } + + uploadCrashLog(crashLog.toString(), 3000); + } catch(FileNotFoundException fnfe) { + Log.v(LOGTAG,"No previous crash found"); + } catch(IOException ioe) { + Log.e(LOGTAG,"Exception while reading crash file: " + ioe); + } finally { + if (crashLogFile != null) { + try { + crashLogFile.close(); + } catch (IOException ignore) { + } + } + if (reader != null) { + try { + reader.close(); + } catch (IOException ignore) { + } + } + } + } + + private void uploadCrashLog(String data, int after) { + final String crashLog = data; + final int waitFor = after; + new Thread(new Runnable() { + public void run(){ + try { + SystemClock.sleep(waitFor); + AndroidHttpClient httpClient = AndroidHttpClient.newInstance("Android");; + HttpPost httpPost = new HttpPost(mLogServer); + HttpEntity se = new StringEntity(crashLog); + httpPost.setEntity(se); + HttpResponse response = httpClient.execute(httpPost); + + File crashLogFile = new File(mAppContext.getFilesDir(), + CRASH_LOG_FILE); + if (crashLogFile != null) { + crashLogFile.delete(); + } else { + Log.e(LOGTAG,"crash log file could not be opened for deletion"); + } + } catch (ClientProtocolException pe) { + Log.e(LOGTAG,"Exception while sending http post: " + pe); + } catch (IOException ioe1) { + Log.e(LOGTAG,"Exception while sending http post: " + ioe1); + } + } + }).start(); + } + + public void uncaughtException(Thread t, Throwable e) { + if (!mOverrideHandler) { + mDefaultHandler.uncaughtException(t, e); + return; + } + + String crashLog = new String(); + + try { + Calendar calendar = Calendar.getInstance(); + JSONObject jsonStackObj = new JSONObject(); + String date = calendar.getTime().toString(); + String aboutSWE = mAppContext.getResources().getString(R.string.about_text); + String sweVer = aboutSWE.substring(aboutSWE.indexOf("Hash"), + aboutSWE.length()); + + jsonStackObj.put("date", date); + jsonStackObj.put("device", android.os.Build.MODEL); + jsonStackObj.put("android-ver", android.os.Build.VERSION.RELEASE); + jsonStackObj.put("browser-ver", sweVer); + jsonStackObj.put("thread", t.toString()); + jsonStackObj.put("cause", e.getCause()); + + Throwable cause = e.getCause(); + if(cause != null) { + StackTraceElement[] arr = cause.getStackTrace(); + JSONArray jsonStack = new JSONArray(arr); + jsonStackObj.put("stack", jsonStack); + } + + JSONObject jsonMainObj = new JSONObject(); + jsonMainObj.put("backtraces", jsonStackObj); + + Log.e(LOGTAG, "Exception: " + jsonMainObj.toString(4)); + crashLog = jsonMainObj.toString(); + + } catch (JSONException je) { + Log.e(LOGTAG, "Failed in JSON encoding: " + je); + } + + saveCrashLog(crashLog); + + uploadCrashLog(crashLog, 0); + + mDefaultHandler.uncaughtException(t, e); + } +} |