blob: bd1828592c06c92abcbbad8160b6af2f8e6a569b [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;
33import android.content.Context;
34import android.os.Build;
35import android.os.Build.VERSION;
36import android.os.SystemClock;
37import android.net.http.AndroidHttpClient;
38import android.util.Log;
39
40import org.codeaurora.swe.BrowserCommandLine;
41
42import org.apache.http.HttpEntity;
43import org.apache.http.HttpResponse;
44import org.apache.http.client.methods.HttpPost;
45import org.apache.http.entity.StringEntity;
46import org.apache.http.client.ClientProtocolException;
47
48import org.json.JSONArray;
49import org.json.JSONObject;
50import org.json.JSONException;
51
52import java.io.File;
53import java.io.FileOutputStream;
54import java.io.FileInputStream;
55import java.io.FileNotFoundException;
56import java.io.IOException;
57import java.io.BufferedReader;
58import java.io.InputStreamReader;
59import java.lang.Integer;
60import java.lang.StringBuilder;
61import java.lang.System;
62import java.lang.Thread.UncaughtExceptionHandler;
63import java.util.Calendar;
64
65public class CrashLogExceptionHandler implements Thread.UncaughtExceptionHandler {
66
67 private static final String CRASH_LOG_FILE = "crash.log";
68 private static final String CRASH_LOG_SERVER_CMD = "crash-log-server";
69 private static final String CRASH_LOG_MAX_FILE_SIZE_CMD = "crash-log-max-file-size";
70
71 private final static String LOGTAG = "CrashLog";
72
73 private Context mAppContext = null;
74
75 private UncaughtExceptionHandler mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
76
77 private String mLogServer = new String();
78
79 private boolean mOverrideHandler = false;
80
81 private int mMaxLogFileSize = 1024 * 1024;
82
83 public CrashLogExceptionHandler(Context ctx) {
84 mAppContext = ctx;
Vivek Sekhardcf9d6b2014-12-01 15:08:37 -080085 if (BrowserCommandLine.hasSwitch(CRASH_LOG_SERVER_CMD)) {
86 mLogServer = BrowserCommandLine.getSwitchValue(CRASH_LOG_SERVER_CMD);
Pankaj Gargf04dbda2014-10-02 13:52:46 -070087 if (mLogServer != null) {
88 uploadPastCrashLog();
89 mOverrideHandler = true;
90 }
91 }
92
93 try {
Vivek Sekhardcf9d6b2014-12-01 15:08:37 -080094 int size = Integer.parseInt(
95 BrowserCommandLine.getSwitchValue(CRASH_LOG_MAX_FILE_SIZE_CMD,
96 Integer.toString(mMaxLogFileSize)));
Pankaj Gargf04dbda2014-10-02 13:52:46 -070097 mMaxLogFileSize = size;
98 } catch (NumberFormatException nfe) {
99 Log.e(LOGTAG,"Max log file size is not configured properly. Using default: "
100 + mMaxLogFileSize);
101 }
102
103 }
104
105 private void saveCrashLog(String crashLog) {
106 // Check if log file exists and it's current size
107 try {
108 File file = new File(mAppContext.getFilesDir(), CRASH_LOG_FILE);
109 if (file.exists()) {
110 if (file.length() > mMaxLogFileSize) {
111 Log.e(LOGTAG,"CRASH Log file size(" + file.length()
112 + ") exceeded max log file size("
113 + mMaxLogFileSize + ")");
114 return;
115 }
116 }
117 } catch (NullPointerException npe) {
118 Log.e(LOGTAG,"Exception while checking file size: " + npe);
119 }
120
121 FileOutputStream crashLogFile = null;
122 try {
123 crashLogFile = mAppContext.openFileOutput(CRASH_LOG_FILE, Context.MODE_APPEND);
124 crashLogFile.write(crashLog.getBytes());
125 } catch(IOException ioe) {
126 Log.e(LOGTAG,"Exception while writing file: " + ioe);
127 } finally {
128 if (crashLogFile != null) {
129 try {
130 crashLogFile.close();
131 } catch (IOException ignore) {
132 }
133 }
134 }
135 }
136
137 private void uploadPastCrashLog() {
138 FileInputStream crashLogFile = null;
139 BufferedReader reader = null;
140 try {
141 crashLogFile = mAppContext.openFileInput(CRASH_LOG_FILE);
142
143 reader = new BufferedReader(new InputStreamReader(crashLogFile));
144 StringBuilder crashLog = new StringBuilder();
145 String line = reader.readLine();
146 if (line != null) {
147 crashLog.append(line);
148 }
149
150 // Typically there's only one line (JSON string) in the crash
151 // log file. This loop would not be executed.
152 while ((line = reader.readLine()) != null) {
153 crashLog.append("\n").append(line);
154 }
155
156 uploadCrashLog(crashLog.toString(), 3000);
157 } catch(FileNotFoundException fnfe) {
158 Log.v(LOGTAG,"No previous crash found");
159 } catch(IOException ioe) {
160 Log.e(LOGTAG,"Exception while reading crash file: " + ioe);
161 } finally {
162 if (crashLogFile != null) {
163 try {
164 crashLogFile.close();
165 } catch (IOException ignore) {
166 }
167 }
168 if (reader != null) {
169 try {
170 reader.close();
171 } catch (IOException ignore) {
172 }
173 }
174 }
175 }
176
177 private void uploadCrashLog(String data, int after) {
178 final String crashLog = data;
179 final int waitFor = after;
180 new Thread(new Runnable() {
181 public void run(){
182 try {
183 SystemClock.sleep(waitFor);
184 AndroidHttpClient httpClient = AndroidHttpClient.newInstance("Android");;
185 HttpPost httpPost = new HttpPost(mLogServer);
186 HttpEntity se = new StringEntity(crashLog);
187 httpPost.setEntity(se);
188 HttpResponse response = httpClient.execute(httpPost);
189
190 File crashLogFile = new File(mAppContext.getFilesDir(),
191 CRASH_LOG_FILE);
192 if (crashLogFile != null) {
193 crashLogFile.delete();
194 } else {
195 Log.e(LOGTAG,"crash log file could not be opened for deletion");
196 }
197 } catch (ClientProtocolException pe) {
198 Log.e(LOGTAG,"Exception while sending http post: " + pe);
199 } catch (IOException ioe1) {
200 Log.e(LOGTAG,"Exception while sending http post: " + ioe1);
201 }
202 }
203 }).start();
204 }
205
206 public void uncaughtException(Thread t, Throwable e) {
207 if (!mOverrideHandler) {
208 mDefaultHandler.uncaughtException(t, e);
209 return;
210 }
211
212 String crashLog = new String();
213
214 try {
215 Calendar calendar = Calendar.getInstance();
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800216 JSONObject jsonBackTraceObj = new JSONObject();
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700217 String date = calendar.getTime().toString();
218 String aboutSWE = mAppContext.getResources().getString(R.string.about_text);
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800219 String sweVer = findValueFromAboutText(aboutSWE, "Version:");
220 String sweHash = findValueFromAboutText(aboutSWE, "Hash:");
221 String sweBuildDate = findValueFromAboutText(aboutSWE, "Built:");
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700222
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800223 jsonBackTraceObj.put("date", date);
224 jsonBackTraceObj.put("device", android.os.Build.MODEL);
225 jsonBackTraceObj.put("android-ver", android.os.Build.VERSION.RELEASE);
226 jsonBackTraceObj.put("browser-ver", sweVer);
227 jsonBackTraceObj.put("browser-hash", sweHash);
228 jsonBackTraceObj.put("browser-build-date", sweBuildDate);
229 jsonBackTraceObj.put("thread", t.toString());
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700230
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800231 JSONArray jsonStackArray = new JSONArray();
232
233 Throwable throwable = e;
234 String stackTag = "Exception thrown while running";
235 while (throwable != null) {
236 JSONObject jsonStackObj = new JSONObject();
237 StackTraceElement[] arr = throwable.getStackTrace();
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700238 JSONArray jsonStack = new JSONArray(arr);
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800239
240 jsonStackObj.put("cause", throwable.getCause());
241 jsonStackObj.put("message", throwable.getMessage());
242 jsonStackObj.put(stackTag, jsonStack);
243
244 jsonStackArray.put(jsonStackObj);
245
246 stackTag = "stack";
247 throwable = throwable.getCause();
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700248 }
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800249 jsonBackTraceObj.put("exceptions", jsonStackArray);
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700250
251 JSONObject jsonMainObj = new JSONObject();
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800252 jsonMainObj.put("backtraces", jsonBackTraceObj);
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700253
254 Log.e(LOGTAG, "Exception: " + jsonMainObj.toString(4));
255 crashLog = jsonMainObj.toString();
256
257 } catch (JSONException je) {
258 Log.e(LOGTAG, "Failed in JSON encoding: " + je);
259 }
260
261 saveCrashLog(crashLog);
262
263 uploadCrashLog(crashLog, 0);
264
265 mDefaultHandler.uncaughtException(t, e);
266 }
Pankaj Garg5fb557e2014-12-22 14:59:18 -0800267
268 private String findValueFromAboutText(String aboutText, String aboutKey) {
269 int start = aboutText.indexOf(aboutKey);
270 int end = aboutText.indexOf("\n", start);
271 String value = "";
272
273 if (start != -1 && end != -1) {
274 start += aboutKey.length();
275 value = aboutText.substring(start, end);
276 }
277 return value;
278 }
279
Pankaj Gargf04dbda2014-10-02 13:52:46 -0700280}