blob: c27a7fd5b523a8b797707e725390ccbf207182a8 [file] [log] [blame]
Pankaj Gargf04dbda2014-10-02 13:52:46 -07001/*
2 * Copyright (c) 2014, The Linux Foundation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of The Linux Foundation nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*/
29
30package com.android.browser;
31
32import android.app.Activity;
Pankaj Gargd106f372015-01-20 18:46:09 -080033import android.app.ActivityManager;
Pankaj Gargf04dbda2014-10-02 13:52:46 -070034import android.content.Context;
35import android.os.Build;
36import android.os.Build.VERSION;
37import android.os.SystemClock;
38import android.net.http.AndroidHttpClient;
39import android.util.Log;
40
41import org.codeaurora.swe.BrowserCommandLine;
42
43import org.apache.http.HttpEntity;
44import org.apache.http.HttpResponse;
45import org.apache.http.client.methods.HttpPost;
46import org.apache.http.entity.StringEntity;
47import org.apache.http.client.ClientProtocolException;
48
49import org.json.JSONArray;
50import org.json.JSONObject;
51import org.json.JSONException;
52
53import java.io.File;
54import java.io.FileOutputStream;
55import java.io.FileInputStream;
56import java.io.FileNotFoundException;
57import java.io.IOException;
58import java.io.BufferedReader;
59import java.io.InputStreamReader;
60import java.lang.Integer;
61import java.lang.StringBuilder;
62import java.lang.System;
63import java.lang.Thread.UncaughtExceptionHandler;
64import java.util.Calendar;
65
66public class CrashLogExceptionHandler implements Thread.UncaughtExceptionHandler {
67
68 private static final String CRASH_LOG_FILE = "crash.log";
69 private static final String CRASH_LOG_SERVER_CMD = "crash-log-server";
70 private static final String CRASH_LOG_MAX_FILE_SIZE_CMD = "crash-log-max-file-size";
71
72 private final static String LOGTAG = "CrashLog";
73
74 private Context mAppContext = null;
75
76 private UncaughtExceptionHandler mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
77
78 private String mLogServer = new String();
79
80 private boolean mOverrideHandler = false;
81
82 private int mMaxLogFileSize = 1024 * 1024;
83
84 public CrashLogExceptionHandler(Context ctx) {
85 mAppContext = ctx;
Vivek Sekhardcf9d6b2014-12-01 15:08:37 -080086 if (BrowserCommandLine.hasSwitch(CRASH_LOG_SERVER_CMD)) {
87 mLogServer = BrowserCommandLine.getSwitchValue(CRASH_LOG_SERVER_CMD);
Pankaj Gargf04dbda2014-10-02 13:52:46 -070088 if (mLogServer != null) {
89 uploadPastCrashLog();
90 mOverrideHandler = true;
91 }
92 }
93
94 try {
Vivek Sekhardcf9d6b2014-12-01 15:08:37 -080095 int size = Integer.parseInt(
96 BrowserCommandLine.getSwitchValue(CRASH_LOG_MAX_FILE_SIZE_CMD,
97 Integer.toString(mMaxLogFileSize)));
Pankaj Gargf04dbda2014-10-02 13:52:46 -070098 mMaxLogFileSize = size;
99 } catch (NumberFormatException nfe) {
100 Log.e(LOGTAG,"Max log file size is not configured properly. Using default: "
101 + mMaxLogFileSize);
102 }
103
104 }
105
106 private void saveCrashLog(String crashLog) {
107 // Check if log file exists and it's current size
108 try {
109 File file = new File(mAppContext.getFilesDir(), CRASH_LOG_FILE);
110 if (file.exists()) {
111 if (file.length() > mMaxLogFileSize) {
112 Log.e(LOGTAG,"CRASH Log file size(" + file.length()
113 + ") exceeded max log file size("
114 + mMaxLogFileSize + ")");
115 return;
116 }
117 }
118 } catch (NullPointerException npe) {
119 Log.e(LOGTAG,"Exception while checking file size: " + npe);
120 }
121
122 FileOutputStream crashLogFile = null;
123 try {
124 crashLogFile = mAppContext.openFileOutput(CRASH_LOG_FILE, Context.MODE_APPEND);
125 crashLogFile.write(crashLog.getBytes());
126 } catch(IOException ioe) {
127 Log.e(LOGTAG,"Exception while writing file: " + ioe);
128 } finally {
129 if (crashLogFile != null) {
130 try {
131 crashLogFile.close();
132 } catch (IOException ignore) {
133 }
134 }
135 }
136 }
137
138 private void uploadPastCrashLog() {
139 FileInputStream crashLogFile = null;
140 BufferedReader reader = null;
141 try {
142 crashLogFile = mAppContext.openFileInput(CRASH_LOG_FILE);
143
144 reader = new BufferedReader(new InputStreamReader(crashLogFile));
145 StringBuilder crashLog = new StringBuilder();
146 String line = reader.readLine();
147 if (line != null) {
148 crashLog.append(line);
149 }
150
151 // Typically there's only one line (JSON string) in the crash
152 // log file. This loop would not be executed.
153 while ((line = reader.readLine()) != null) {
154 crashLog.append("\n").append(line);
155 }
156
157 uploadCrashLog(crashLog.toString(), 3000);
158 } catch(FileNotFoundException fnfe) {
159 Log.v(LOGTAG,"No previous crash found");
160 } catch(IOException ioe) {
161 Log.e(LOGTAG,"Exception while reading crash file: " + ioe);
162 } finally {
163 if (crashLogFile != null) {
164 try {
165 crashLogFile.close();
166 } catch (IOException ignore) {
167 }
168 }
169 if (reader != null) {
170 try {
171 reader.close();
172 } catch (IOException ignore) {
173 }
174 }
175 }
176 }
177
178 private void uploadCrashLog(String data, int after) {
179 final String crashLog = data;
180 final int waitFor = after;
181 new Thread(new Runnable() {
182 public void run(){
183 try {
184 SystemClock.sleep(waitFor);
185 AndroidHttpClient httpClient = AndroidHttpClient.newInstance("Android");;
186 HttpPost httpPost = new HttpPost(mLogServer);
187 HttpEntity se = new StringEntity(crashLog);
188 httpPost.setEntity(se);
189 HttpResponse response = httpClient.execute(httpPost);
190
191 File crashLogFile = new File(mAppContext.getFilesDir(),
192 CRASH_LOG_FILE);
193 if (crashLogFile != null) {
194 crashLogFile.delete();
195 } else {
196 Log.e(LOGTAG,"crash log file could not be opened for deletion");
197 }
198 } catch (ClientProtocolException pe) {
199 Log.e(LOGTAG,"Exception while sending http post: " + pe);
200 } catch (IOException ioe1) {
201 Log.e(LOGTAG,"Exception while sending http post: " + ioe1);
202 }
203 }
204 }).start();
205 }
206
207 public void uncaughtException(Thread t, Throwable e) {
208 if (!mOverrideHandler) {
209 mDefaultHandler.uncaughtException(t, e);
210 return;
211 }
212
213 String crashLog = new String();
214
215 try {
216 Calendar calendar = Calendar.getInstance();
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800217 JSONObject jsonBackTraceObj = new JSONObject();
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700218 String date = calendar.getTime().toString();
219 String aboutSWE = mAppContext.getResources().getString(R.string.about_text);
Pankaj Gargd106f372015-01-20 18:46:09 -0800220 String sweVer = findValueFromAboutText(aboutSWE, "Version: ");
221 String sweHash = findValueFromAboutText(aboutSWE, "Hash: ");
222 String sweBuildDate = findValueFromAboutText(aboutSWE, "Built: ");
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700223
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800224 jsonBackTraceObj.put("date", date);
Pankaj Gargd106f372015-01-20 18:46:09 -0800225 jsonBackTraceObj.put("android-model", android.os.Build.MODEL);
226 jsonBackTraceObj.put("android-device", android.os.Build.DEVICE);
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800227 jsonBackTraceObj.put("android-ver", android.os.Build.VERSION.RELEASE);
228 jsonBackTraceObj.put("browser-ver", sweVer);
229 jsonBackTraceObj.put("browser-hash", sweHash);
230 jsonBackTraceObj.put("browser-build-date", sweBuildDate);
231 jsonBackTraceObj.put("thread", t.toString());
Pankaj Gargd106f372015-01-20 18:46:09 -0800232 jsonBackTraceObj.put("format", "crashmon-1");
233 jsonBackTraceObj.put("monkey-test", ActivityManager.isUserAMonkey());
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700234
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800235 JSONArray jsonStackArray = new JSONArray();
236
237 Throwable throwable = e;
238 String stackTag = "Exception thrown while running";
239 while (throwable != null) {
240 JSONObject jsonStackObj = new JSONObject();
241 StackTraceElement[] arr = throwable.getStackTrace();
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700242 JSONArray jsonStack = new JSONArray(arr);
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800243
244 jsonStackObj.put("cause", throwable.getCause());
245 jsonStackObj.put("message", throwable.getMessage());
246 jsonStackObj.put(stackTag, jsonStack);
247
248 jsonStackArray.put(jsonStackObj);
249
250 stackTag = "stack";
251 throwable = throwable.getCause();
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700252 }
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800253 jsonBackTraceObj.put("exceptions", jsonStackArray);
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700254
255 JSONObject jsonMainObj = new JSONObject();
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800256 jsonMainObj.put("backtraces", jsonBackTraceObj);
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700257
258 Log.e(LOGTAG, "Exception: " + jsonMainObj.toString(4));
259 crashLog = jsonMainObj.toString();
260
261 } catch (JSONException je) {
262 Log.e(LOGTAG, "Failed in JSON encoding: " + je);
263 }
264
265 saveCrashLog(crashLog);
266
267 uploadCrashLog(crashLog, 0);
268
269 mDefaultHandler.uncaughtException(t, e);
270 }
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800271
272 private String findValueFromAboutText(String aboutText, String aboutKey) {
273 int start = aboutText.indexOf(aboutKey);
274 int end = aboutText.indexOf("\n", start);
275 String value = "";
276
277 if (start != -1 && end != -1) {
278 start += aboutKey.length();
279 value = aboutText.substring(start, end);
280 }
281 return value;
282 }
283
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700284}