1package eu.siacs.conversations.utils;
2
3import android.content.Context;
4import android.content.SharedPreferences;
5import android.content.pm.PackageInfo;
6import android.content.pm.PackageManager;
7import android.content.pm.Signature;
8import android.preference.PreferenceManager;
9import android.support.v7.app.AlertDialog;
10import android.util.Log;
11
12import java.io.BufferedReader;
13import java.io.FileInputStream;
14import java.io.IOException;
15import java.io.InputStreamReader;
16import java.io.OutputStream;
17import java.text.SimpleDateFormat;
18import java.util.Date;
19import java.util.Locale;
20
21import eu.siacs.conversations.Config;
22import eu.siacs.conversations.R;
23import eu.siacs.conversations.entities.Account;
24import eu.siacs.conversations.entities.Conversation;
25import eu.siacs.conversations.entities.Message;
26import eu.siacs.conversations.services.XmppConnectionService;
27import eu.siacs.conversations.ui.XmppActivity;
28
29public class ExceptionHelper {
30
31 private static final String FILENAME = "stacktrace.txt";
32 private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
33
34 public static void init(Context context) {
35 if (Thread.getDefaultUncaughtExceptionHandler() instanceof ExceptionHandler) {
36 return;
37 }
38 Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(context));
39 }
40
41 public static boolean checkForCrash(XmppActivity activity) {
42 try {
43 final XmppConnectionService service = activity == null ? null : activity.xmppConnectionService;
44 if (service == null) {
45 return false;
46 }
47 final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
48 boolean neverSend = preferences.getBoolean("never_send", false);
49 if (neverSend || Config.BUG_REPORTS == null) {
50 return false;
51 }
52 final Account account = AccountUtils.getFirstEnabled(service);
53 if (account == null) {
54 return false;
55 }
56 FileInputStream file = activity.openFileInput(FILENAME);
57 InputStreamReader inputStreamReader = new InputStreamReader(file);
58 BufferedReader stacktrace = new BufferedReader(inputStreamReader);
59 final StringBuilder report = new StringBuilder();
60 PackageManager pm = activity.getPackageManager();
61 PackageInfo packageInfo;
62 try {
63 packageInfo = pm.getPackageInfo(activity.getPackageName(), PackageManager.GET_SIGNATURES);
64 final String versionName = packageInfo.versionName;
65 final int versionCode = packageInfo.versionCode;
66 final int version = versionCode > 10000 ? (versionCode / 100) : versionCode;
67 report.append(String.format(Locale.ROOT, "Version: %s(%d)", versionName, version)).append('\n');
68 report.append("Last Update: ").append(DATE_FORMAT.format(new Date(packageInfo.lastUpdateTime))).append('\n');
69 Signature[] signatures = packageInfo.signatures;
70 if (signatures != null && signatures.length >= 1) {
71 report.append("SHA-1: ").append(CryptoHelper.getFingerprintCert(packageInfo.signatures[0].toByteArray())).append('\n');
72 }
73 report.append('\n');
74 } catch (Exception e) {
75 e.printStackTrace();
76 return false;
77 }
78 String line;
79 while ((line = stacktrace.readLine()) != null) {
80 report.append(line);
81 report.append('\n');
82 }
83 file.close();
84 activity.deleteFile(FILENAME);
85 AlertDialog.Builder builder = new AlertDialog.Builder(activity);
86 builder.setTitle(activity.getString(R.string.crash_report_title));
87 builder.setMessage(activity.getText(R.string.crash_report_message));
88 builder.setPositiveButton(activity.getText(R.string.send_now), (dialog, which) -> {
89
90 Log.d(Config.LOGTAG, "using account=" + account.getJid().asBareJid() + " to send in stack trace");
91 Conversation conversation = service.findOrCreateConversation(account, Config.BUG_REPORTS, false, true);
92 Message message = new Message(conversation, report.toString(), Message.ENCRYPTION_NONE);
93 service.sendMessage(message);
94 });
95 builder.setNegativeButton(activity.getText(R.string.send_never), (dialog, which) -> preferences.edit().putBoolean("never_send", true).apply());
96 builder.create().show();
97 return true;
98 } catch (final IOException ignored) {
99 return false;
100 }
101 }
102
103 static void writeToStacktraceFile(Context context, String msg) {
104 try {
105 OutputStream os = context.openFileOutput(FILENAME, Context.MODE_PRIVATE);
106 os.write(msg.getBytes());
107 os.flush();
108 os.close();
109 } catch (IOException ignored) {
110 }
111 }
112}