termux-app

Форк
0
158 строк · 7.2 Кб
1
package com.termux.shared.crash;
2

3
import android.content.Context;
4

5
import androidx.annotation.NonNull;
6

7
import com.termux.shared.file.FileUtils;
8
import com.termux.shared.logger.Logger;
9
import com.termux.shared.markdown.MarkdownUtils;
10
import com.termux.shared.errors.Error;
11
import com.termux.shared.android.AndroidUtils;
12

13
import java.nio.charset.Charset;
14

15
/**
16
 * Catches uncaught exceptions and logs them.
17
 */
18
public class CrashHandler implements Thread.UncaughtExceptionHandler {
19

20
    private final Context mContext;
21
    private final CrashHandlerClient mCrashHandlerClient;
22
    private final Thread.UncaughtExceptionHandler mDefaultUEH;
23
    private final boolean mIsDefaultHandler;
24

25
    private static final String LOG_TAG = "CrashUtils";
26

27
    private CrashHandler(@NonNull final Context context, @NonNull final CrashHandlerClient crashHandlerClient,
28
                         boolean isDefaultHandler) {
29
        mContext = context;
30
        mCrashHandlerClient = crashHandlerClient;
31
        mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
32
        mIsDefaultHandler = isDefaultHandler;
33
    }
34

35
    public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) {
36
        Logger.logInfo(LOG_TAG, "uncaughtException() for " + thread +  ": " + throwable.getMessage());
37
        logCrash(thread, throwable);
38

39
        // Don't stop the app if not on the main thread
40
        if (mIsDefaultHandler)
41
            mDefaultUEH.uncaughtException(thread, throwable);
42
    }
43

44
    /**
45
     * Set default uncaught crash handler for the app to {@link CrashHandler}.
46
     */
47
    public static void setDefaultCrashHandler(@NonNull final Context context, @NonNull final CrashHandlerClient crashHandlerClient) {
48
        if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof CrashHandler)) {
49
            Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(context, crashHandlerClient, true));
50
        }
51
    }
52

53
    /**
54
     * Set uncaught crash handler of current non-main thread to {@link CrashHandler}.
55
     */
56
    public static void setCrashHandler(@NonNull final Context context, @NonNull final CrashHandlerClient crashHandlerClient) {
57
        Thread.currentThread().setUncaughtExceptionHandler(new CrashHandler(context, crashHandlerClient, false));
58
    }
59

60
    /**
61
     * Get {@link CrashHandler} instance that can be set as uncaught crash handler of a non-main thread.
62
     */
63
    public static CrashHandler getCrashHandler(@NonNull final Context context, @NonNull final CrashHandlerClient crashHandlerClient) {
64
        return new CrashHandler(context, crashHandlerClient, false);
65
    }
66

67
    /**
68
     * Log a crash in the crash log file at path returned by {@link CrashHandlerClient#getCrashLogFilePath(Context)}.
69
     *
70
     * @param context The {@link Context} for operations.
71
     * @param crashHandlerClient The {@link CrashHandlerClient} implementation.
72
     * @param thread The {@link Thread} in which the crash happened.
73
     * @param throwable The {@link Throwable} thrown for the crash.
74
     */
75
    public static void logCrash(@NonNull Context context,
76
                                @NonNull CrashHandlerClient crashHandlerClient,
77
                                @NonNull Thread thread,  @NonNull Throwable throwable) {
78
        Logger.logInfo(LOG_TAG, "logCrash() for " + thread +  ": " + throwable.getMessage());
79
        new CrashHandler(context, crashHandlerClient, false).logCrash(thread, throwable);
80
    }
81

82
    public void logCrash(@NonNull Thread thread, @NonNull Throwable throwable) {
83
        if (!mCrashHandlerClient.onPreLogCrash(mContext, thread, throwable)) {
84
            logCrashToFile(mContext, mCrashHandlerClient, thread, throwable);
85
            mCrashHandlerClient.onPostLogCrash(mContext, thread, throwable);
86
        }
87
    }
88

89
    public void logCrashToFile(@NonNull Context context,
90
                               @NonNull CrashHandlerClient crashHandlerClient,
91
                               @NonNull Thread thread, @NonNull Throwable throwable) {
92
        StringBuilder reportString = new StringBuilder();
93

94
        reportString.append("## Crash Details\n");
95
        reportString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Crash Thread", thread.toString(), "-"));
96
        reportString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Crash Timestamp", AndroidUtils.getCurrentMilliSecondUTCTimeStamp(), "-"));
97
        reportString.append("\n\n").append(MarkdownUtils.getMultiLineMarkdownStringEntry("Crash Message", throwable.getMessage(), "-"));
98
        reportString.append("\n\n").append(Logger.getStackTracesMarkdownString("Stacktrace", Logger.getStackTracesStringArray(throwable)));
99

100
        String appInfoMarkdownString = crashHandlerClient.getAppInfoMarkdownString(context);
101
        if (appInfoMarkdownString != null && !appInfoMarkdownString.isEmpty())
102
            reportString.append("\n\n").append(appInfoMarkdownString);
103

104
        reportString.append("\n\n").append(AndroidUtils.getDeviceInfoMarkdownString(context));
105

106
        // Log report string to logcat
107
        Logger.logError(reportString.toString());
108

109
        // Write report string to crash log file
110
        Error error = FileUtils.writeTextToFile("crash log", crashHandlerClient.getCrashLogFilePath(context),
111
                        Charset.defaultCharset(), reportString.toString(), false);
112
        if (error != null) {
113
            Logger.logErrorExtended(LOG_TAG, error.toString());
114
        }
115
    }
116

117
    public interface CrashHandlerClient {
118

119
        /**
120
         * Called before {@link #logCrashToFile(Context, CrashHandlerClient, Thread, Throwable)} is called.
121
         *
122
         * @param context The {@link Context} passed to {@link CrashHandler#CrashHandler(Context, CrashHandlerClient, boolean)}.
123
         * @param thread The {@link Thread} in which the crash happened.
124
         * @param throwable The {@link Throwable} thrown for the crash.
125
         * @return Should return {@code true} if crash has been handled and should not be logged,
126
         * otherwise {@code false}.
127
         */
128
        boolean onPreLogCrash(Context context, Thread thread, Throwable throwable);
129

130
        /**
131
         * Called after {@link #logCrashToFile(Context, CrashHandlerClient, Thread, Throwable)} is called.
132
         *
133
         * @param context The {@link Context} passed to {@link CrashHandler#CrashHandler(Context, CrashHandlerClient, boolean)}.
134
         * @param thread The {@link Thread} in which the crash happened.
135
         * @param throwable The {@link Throwable} thrown for the crash.
136
         */
137
        void onPostLogCrash(Context context, Thread thread, Throwable throwable);
138

139
        /**
140
         * Get crash log file path.
141
         *
142
         * @param context The {@link Context} passed to {@link CrashHandler#CrashHandler(Context, CrashHandlerClient, boolean)}.
143
         * @return Should return the crash log file path.
144
         */
145
        @NonNull
146
        String getCrashLogFilePath(Context context);
147

148
        /**
149
         * Get app info markdown string to add to crash log.
150
         *
151
         * @param context The {@link Context} passed to {@link CrashHandler#CrashHandler(Context, CrashHandlerClient, boolean)}.
152
         * @return Should return app info markdown string.
153
         */
154
        String getAppInfoMarkdownString(Context context);
155

156
    }
157

158
}
159

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.