Bug 695100 - Code cleanup for CrashReporter (r=lucasr)

Fix indentation, remove trailing spaces, expand wildcard imports and sort them,
pull out magic constant strings, scope some methods down to private, used
java's foreach loop construct.
This commit is contained in:
Kartikaya Gupta 2011-10-17 16:27:27 -04:00
parent 19de227cf0
commit 9b2f5cb4f6

View File

@ -38,299 +38,297 @@
#filter substitution
package @ANDROID_PACKAGE_NAME@;
import java.util.HashMap;
import java.util.Map;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import android.app.*;
import android.content.*;
import android.os.*;
import android.util.*;
import android.view.*;
import android.view.View.*;
import android.widget.*;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import org.mozilla.gecko.*;
import java.util.*;
import java.io.*;
import java.net.*;
import java.nio.channels.*;
import org.mozilla.gecko.R;
public class CrashReporter extends Activity
{
static final String kMiniDumpPathKey = "upload_file_minidump";
static final String kPageURLKey = "URL";
static final String kNotesKey = "Notes";
Handler mHandler = null;
ProgressDialog mProgressDialog;
File mPendingMinidumpFile;
File mPendingExtrasFile;
HashMap<String, String> mExtrasStringMap;
private static final String PASSED_MINI_DUMP_KEY = "minidumpPath";
private static final String MINI_DUMP_PATH_KEY = "upload_file_minidump";
private static final String PAGE_URL_KEY = "URL";
private static final String NOTES_KEY = "Notes";
private static final String SERVER_URL_KEY = "ServerURL";
boolean moveFile(File inFile, File outFile)
{
Log.i("GeckoCrashReporter", "moving " + inFile + " to " + outFile);
if (inFile.renameTo(outFile))
return true;
try {
outFile.createNewFile();
Log.i("GeckoCrashReporter", "couldn't rename minidump file");
// so copy it instead
FileChannel inChannel = new FileInputStream(inFile).getChannel();
FileChannel outChannel = new FileOutputStream(outFile).getChannel();
long transferred = inChannel.transferTo(0, inChannel.size(), outChannel);
inChannel.close();
outChannel.close();
private static final String CRASH_REPORT_DIR = "/data/data/@ANDROID_PACKAGE_NAME@/files/mozilla/Crash Reports/";
private static final String PENDING_DIR = CRASH_REPORT_DIR + "pending";
private static final String SUBMITTED_DIR = CRASH_REPORT_DIR + "submitted";
if (transferred > 0)
inFile.delete();
} catch (Exception e) {
Log.e("GeckoCrashReporter",
"exception while copying minidump file: ", e);
return false;
}
return true;
}
private static final String LOG_NAME = "GeckoCrashReporter";
void doFinish() {
if (mHandler != null) {
mHandler.post(new Runnable(){
public void run() {
finish();
}});
}
}
private Handler mHandler;
private ProgressDialog mProgressDialog;
private File mPendingMinidumpFile;
private File mPendingExtrasFile;
private HashMap<String, String> mExtrasStringMap;
@Override
public void finish()
{
mProgressDialog.dismiss();
super.finish();
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// mHandler is created here so runnables can be run on the main thread
mHandler = new Handler();
setContentView(R.layout.crash_reporter);
mProgressDialog = new ProgressDialog(CrashReporter.this);
mProgressDialog.setMessage(getString(R.string.sending_crash_report));
final Button restartButton = (Button) findViewById(R.id.restart);
final Button closeButton = (Button) findViewById(R.id.close);
String passedMinidumpPath = getIntent().getStringExtra("minidumpPath");
File passedMinidumpFile = new File(passedMinidumpPath);
File pendingDir =
new File("/data/data/@ANDROID_PACKAGE_NAME@/files/mozilla/Crash Reports/pending");
pendingDir.mkdirs();
mPendingMinidumpFile = new File(pendingDir, passedMinidumpFile.getName());
moveFile(passedMinidumpFile, mPendingMinidumpFile);
File extrasFile = new File(passedMinidumpPath.replaceAll(".dmp", ".extra"));
mPendingExtrasFile = new File(pendingDir, extrasFile.getName());
moveFile(extrasFile, mPendingExtrasFile);
mExtrasStringMap = new HashMap<String, String>();
readStringsFromFile(mPendingExtrasFile.getPath(), mExtrasStringMap);
}
void backgroundSendReport()
{
final CheckBox sendReportCheckbox = (CheckBox) findViewById(R.id.send_report);
if (!sendReportCheckbox.isChecked()) {
doFinish();
return;
}
mProgressDialog.show();
new Thread(new Runnable() {
public void run() {
sendReport(mPendingMinidumpFile, mExtrasStringMap, mPendingExtrasFile);
}}).start();
}
public void onCloseClick(View v)
{
backgroundSendReport();
}
public void onRestartClick(View v)
{
doRestart();
backgroundSendReport();
}
boolean readStringsFromFile(String filePath, Map<String, String> stringMap)
{
try {
BufferedReader reader = new BufferedReader(
new FileReader(filePath));
return readStringsFromReader(reader, stringMap);
} catch (Exception e) {
Log.e("GeckoCrashReporter", "exception while reading strings: ", e);
return false;
}
}
boolean readStringsFromReader(BufferedReader reader, Map<String, String> stringMap)
throws java.io.IOException
{
String line;
while ((line = reader.readLine()) != null) {
int equalsPos = -1;
if ((equalsPos = line.indexOf('=')) != -1) {
String key = line.substring(0, equalsPos);
String val = unescape(line.substring(equalsPos + 1));
stringMap.put(key, val);
}
}
reader.close();
return true;
}
String generateBoundary()
{
// Generate some random numbers to fill out the boundary
int r0 = (int)((double)Integer.MAX_VALUE * Math.random());
int r1 = (int)((double)Integer.MAX_VALUE * Math.random());
return String.format("---------------------------%08X%08X", r0, r1);
}
void sendPart(OutputStream os, String boundary, String name, String data)
{
try {
os.write(("--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"" +
name + "\"\r\n\r\n" +
data + "\r\n").getBytes());
} catch (Exception ex) {
Log.e("GeckoCrashReporter", "Exception when sending \"" + name + "\"", ex);
}
}
void sendFile(OutputStream os, String boundary, String name, File file)
throws IOException
{
os.write(("--" + boundary + "\r\n" +
"Content-Disposition: form-data; " +
"name=\"" + name + "\"; " +
"filename=\"" + file.getName() + "\"\r\n" +
"Content-Type: application/octet-stream\r\n" +
"\r\n").getBytes());
FileChannel fc =
new FileInputStream(file).getChannel();
fc.transferTo(0, fc.size(), Channels.newChannel(os));
fc.close();
}
void sendReport(File minidumpFile, Map<String, String> extras,
File extrasFile)
{
Log.i("GeckoCrashReport", "sendReport: " + minidumpFile.getPath());
final CheckBox includeURLCheckbox = (CheckBox) findViewById(R.id.include_url);
String spec = extras.get("ServerURL");
if (spec == null)
doFinish();
Log.i("GeckoCrashReport", "server url: " + spec);
try {
URL url = new URL(spec);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST");
String boundary = generateBoundary();
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
OutputStream os = conn.getOutputStream();
Iterator<String> keys = extras.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next();
if (key.equals(kPageURLKey)) {
if (includeURLCheckbox.isChecked())
sendPart(os, boundary, key, extras.get(key));
} else if (!key.equals("ServerURL") && !key.equals(kNotesKey)) {
sendPart(os, boundary, key, extras.get(key));
}
}
// Add some extra information to notes so its displayed by
// crash-stats.mozilla.org. Remove this when bug 607942 is fixed.
String notes = extras.containsKey(kNotesKey) ? extras.get(kNotesKey) +
"\n" : "";
if (@MOZ_MIN_CPU_VERSION@ < 7)
notes += "nothumb Build\n";
notes += Build.MANUFACTURER + " ";
notes += Build.MODEL + "\n";
notes += Build.FINGERPRINT;
sendPart(os, boundary, kNotesKey, notes);
sendPart(os, boundary, "Min_ARM_Version", "@MOZ_MIN_CPU_VERSION@");
sendPart(os, boundary, "Android_Manufacturer", Build.MANUFACTURER);
sendPart(os, boundary, "Android_Model", Build.MODEL);
sendPart(os, boundary, "Android_Board", Build.BOARD);
sendPart(os, boundary, "Android_Brand", Build.BRAND);
sendPart(os, boundary, "Android_Device", Build.DEVICE);
sendPart(os, boundary, "Android_Display", Build.DISPLAY);
sendPart(os, boundary, "Android_Fingerprint", Build.FINGERPRINT);
sendPart(os, boundary, "Android_CPU_ABI", Build.CPU_ABI);
if (Build.VERSION.SDK_INT >= 8) {
private boolean moveFile(File inFile, File outFile) {
Log.i(LOG_NAME, "moving " + inFile + " to " + outFile);
if (inFile.renameTo(outFile))
return true;
try {
sendPart(os, boundary, "Android_CPU_ABI2", Build.CPU_ABI2);
sendPart(os, boundary, "Android_Hardware", Build.HARDWARE);
} catch (Exception ex) {
Log.e("GeckoCrashReporter", "Exception while sending SDK version 8 keys", ex);
outFile.createNewFile();
Log.i(LOG_NAME, "couldn't rename minidump file");
// so copy it instead
FileChannel inChannel = new FileInputStream(inFile).getChannel();
FileChannel outChannel = new FileOutputStream(outFile).getChannel();
long transferred = inChannel.transferTo(0, inChannel.size(), outChannel);
inChannel.close();
outChannel.close();
if (transferred > 0)
inFile.delete();
} catch (Exception e) {
Log.e(LOG_NAME, "exception while copying minidump file: ", e);
return false;
}
}
sendPart(os, boundary, "Android_Version", Build.VERSION.SDK_INT + " (" + Build.VERSION.CODENAME + ")");
sendFile(os, boundary, kMiniDumpPathKey, minidumpFile);
os.write(("\r\n--" + boundary + "--\r\n").getBytes());
os.flush();
os.close();
BufferedReader br = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
HashMap<String, String> responseMap = new HashMap<String, String>();
readStringsFromReader(br, responseMap);
if (conn.getResponseCode() == conn.HTTP_OK) {
File submittedDir = new File(
"/data/data/@ANDROID_PACKAGE_NAME@/files/mozilla/Crash Reports/submitted");
submittedDir.mkdirs();
minidumpFile.delete();
extrasFile.delete();
String crashid = responseMap.get("CrashID");
File file = new File(submittedDir, crashid + ".txt");
FileOutputStream fos = new FileOutputStream(file);
fos.write("Crash ID: ".getBytes());
fos.write(crashid.getBytes());
fos.close();
}
} catch (IOException e) {
Log.e("GeckoCrashReporter", "exception during send: ", e);
return true;
}
doFinish();
}
void doRestart()
{
try {
String action = "android.intent.action.MAIN";
Intent intent = new Intent(action);
intent.setClassName("@ANDROID_PACKAGE_NAME@",
"@ANDROID_PACKAGE_NAME@.App");
Log.i("GeckoCrashReporter", intent.toString());
startActivity(intent);
} catch (Exception e) {
Log.e("GeckoCrashReporter", "error while trying to restart", e);
private void doFinish() {
if (mHandler != null) {
mHandler.post(new Runnable() {
public void run() {
finish();
}
});
}
}
}
public String unescape(String string)
{
return string.replaceAll("\\\\", "\\").replaceAll("\\n", "\n")
.replaceAll("\\t", "\t");
}
@Override
public void finish() {
mProgressDialog.dismiss();
super.finish();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// mHandler is created here so runnables can be run on the main thread
mHandler = new Handler();
setContentView(R.layout.crash_reporter);
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage(getString(R.string.sending_crash_report));
final Button restartButton = (Button) findViewById(R.id.restart);
final Button closeButton = (Button) findViewById(R.id.close);
String passedMinidumpPath = getIntent().getStringExtra(PASSED_MINI_DUMP_KEY);
File passedMinidumpFile = new File(passedMinidumpPath);
File pendingDir = new File(PENDING_DIR);
pendingDir.mkdirs();
mPendingMinidumpFile = new File(pendingDir, passedMinidumpFile.getName());
moveFile(passedMinidumpFile, mPendingMinidumpFile);
File extrasFile = new File(passedMinidumpPath.replaceAll(".dmp", ".extra"));
mPendingExtrasFile = new File(pendingDir, extrasFile.getName());
moveFile(extrasFile, mPendingExtrasFile);
mExtrasStringMap = new HashMap<String, String>();
readStringsFromFile(mPendingExtrasFile.getPath(), mExtrasStringMap);
}
private void backgroundSendReport() {
final CheckBox sendReportCheckbox = (CheckBox) findViewById(R.id.send_report);
if (!sendReportCheckbox.isChecked()) {
doFinish();
return;
}
mProgressDialog.show();
new Thread(new Runnable() {
public void run() {
sendReport(mPendingMinidumpFile, mExtrasStringMap, mPendingExtrasFile);
}
}).start();
}
public void onCloseClick(View v) { // bound via crash_reporter.xml
backgroundSendReport();
}
public void onRestartClick(View v) { // bound via crash_reporter.xml
doRestart();
backgroundSendReport();
}
private boolean readStringsFromFile(String filePath, Map<String, String> stringMap) {
try {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
return readStringsFromReader(reader, stringMap);
} catch (Exception e) {
Log.e(LOG_NAME, "exception while reading strings: ", e);
return false;
}
}
private boolean readStringsFromReader(BufferedReader reader, Map<String, String> stringMap) throws IOException {
String line;
while ((line = reader.readLine()) != null) {
int equalsPos = -1;
if ((equalsPos = line.indexOf('=')) != -1) {
String key = line.substring(0, equalsPos);
String val = unescape(line.substring(equalsPos + 1));
stringMap.put(key, val);
}
}
reader.close();
return true;
}
private String generateBoundary() {
// Generate some random numbers to fill out the boundary
int r0 = (int)((double)Integer.MAX_VALUE * Math.random());
int r1 = (int)((double)Integer.MAX_VALUE * Math.random());
return String.format("---------------------------%08X%08X", r0, r1);
}
private void sendPart(OutputStream os, String boundary, String name, String data) {
try {
os.write(("--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"" + name + "\"\r\n" +
"\r\n" +
data + "\r\n"
).getBytes());
} catch (Exception ex) {
Log.e(LOG_NAME, "Exception when sending \"" + name + "\"", ex);
}
}
private void sendFile(OutputStream os, String boundary, String name, File file) throws IOException {
os.write(("--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"" + name + "\"; " +
"filename=\"" + file.getName() + "\"\r\n" +
"Content-Type: application/octet-stream\r\n" +
"\r\n"
).getBytes());
FileChannel fc = new FileInputStream(file).getChannel();
fc.transferTo(0, fc.size(), Channels.newChannel(os));
fc.close();
}
private void sendReport(File minidumpFile, Map<String, String> extras, File extrasFile) {
Log.i(LOG_NAME, "sendReport: " + minidumpFile.getPath());
final CheckBox includeURLCheckbox = (CheckBox) findViewById(R.id.include_url);
String spec = extras.get(SERVER_URL_KEY);
if (spec == null)
doFinish();
Log.i(LOG_NAME, "server url: " + spec);
try {
URL url = new URL(spec);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST");
String boundary = generateBoundary();
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
OutputStream os = conn.getOutputStream();
for (String key : extras.keySet()) {
if (key.equals(PAGE_URL_KEY)) {
if (includeURLCheckbox.isChecked())
sendPart(os, boundary, key, extras.get(key));
} else if (!key.equals(SERVER_URL_KEY) && !key.equals(NOTES_KEY)) {
sendPart(os, boundary, key, extras.get(key));
}
}
// Add some extra information to notes so its displayed by
// crash-stats.mozilla.org. Remove this when bug 607942 is fixed.
StringBuffer sb = new StringBuffer();
sb.append(extras.containsKey(NOTES_KEY) ? extras.get(NOTES_KEY) + "\n" : "");
if (@MOZ_MIN_CPU_VERSION@ < 7)
sb.append("nothumb Build\n");
sb.append(Build.MANUFACTURER).append(' ')
.append(Build.MODEL).append('\n')
.append(Build.FINGERPRINT);
sendPart(os, boundary, NOTES_KEY, sb.toString());
sendPart(os, boundary, "Min_ARM_Version", "@MOZ_MIN_CPU_VERSION@");
sendPart(os, boundary, "Android_Manufacturer", Build.MANUFACTURER);
sendPart(os, boundary, "Android_Model", Build.MODEL);
sendPart(os, boundary, "Android_Board", Build.BOARD);
sendPart(os, boundary, "Android_Brand", Build.BRAND);
sendPart(os, boundary, "Android_Device", Build.DEVICE);
sendPart(os, boundary, "Android_Display", Build.DISPLAY);
sendPart(os, boundary, "Android_Fingerprint", Build.FINGERPRINT);
sendPart(os, boundary, "Android_CPU_ABI", Build.CPU_ABI);
if (Build.VERSION.SDK_INT >= 8) {
try {
sendPart(os, boundary, "Android_CPU_ABI2", Build.CPU_ABI2);
sendPart(os, boundary, "Android_Hardware", Build.HARDWARE);
} catch (Exception ex) {
Log.e(LOG_NAME, "Exception while sending SDK version 8 keys", ex);
}
}
sendPart(os, boundary, "Android_Version", Build.VERSION.SDK_INT + " (" + Build.VERSION.CODENAME + ")");
sendFile(os, boundary, MINI_DUMP_PATH_KEY, minidumpFile);
os.write(("\r\n--" + boundary + "--\r\n").getBytes());
os.flush();
os.close();
BufferedReader br = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
HashMap<String, String> responseMap = new HashMap<String, String>();
readStringsFromReader(br, responseMap);
if (conn.getResponseCode() == conn.HTTP_OK) {
File submittedDir = new File(SUBMITTED_DIR);
submittedDir.mkdirs();
minidumpFile.delete();
extrasFile.delete();
String crashid = responseMap.get("CrashID");
File file = new File(submittedDir, crashid + ".txt");
FileOutputStream fos = new FileOutputStream(file);
fos.write("Crash ID: ".getBytes());
fos.write(crashid.getBytes());
fos.close();
}
} catch (IOException e) {
Log.e(LOG_NAME, "exception during send: ", e);
}
doFinish();
}
private void doRestart() {
try {
String action = "android.intent.action.MAIN";
Intent intent = new Intent(action);
intent.setClassName("@ANDROID_PACKAGE_NAME@",
"@ANDROID_PACKAGE_NAME@.App");
Log.i(LOG_NAME, intent.toString());
startActivity(intent);
} catch (Exception e) {
Log.e(LOG_NAME, "error while trying to restart", e);
}
}
private String unescape(String string) {
return string.replaceAll("\\\\", "\\").replaceAll("\\n", "\n").replaceAll("\\t", "\t");
}
}