Add crash handler and debugging options
This commit is contained in:
parent
461b4fc013
commit
2093fa1dee
|
@ -10,6 +10,7 @@
|
|||
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.READ_LOG" android:maxSdkVersion="15" />
|
||||
|
||||
<!-- may be needed for call blocking on Android 9, also requires to be installed as a system app -->
|
||||
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE"
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.content.Context;
|
|||
import android.os.Build;
|
||||
|
||||
import dummydomain.yetanothercallblocker.data.Config;
|
||||
import dummydomain.yetanothercallblocker.utils.DebuggingUtils;
|
||||
|
||||
public class App extends Application {
|
||||
|
||||
|
@ -28,6 +29,8 @@ public class App extends Application {
|
|||
|
||||
instance = this;
|
||||
|
||||
DebuggingUtils.setUpCrashHandler();
|
||||
|
||||
new DeviceProtectedStorageMigrator().migrate(this);
|
||||
|
||||
settings = new Settings(getDeviceProtectedStorageContext());
|
||||
|
|
|
@ -19,6 +19,8 @@ public class Settings extends GenericSettings {
|
|||
public static final String PREF_LAST_UPDATE_CHECK_TIME = "lastUpdateCheckTime";
|
||||
public static final String PREF_COUNTRY_CODE_OVERRIDE = "countryCodeOverride";
|
||||
public static final String PREF_COUNTRY_CODE_FOR_REVIEWS_OVERRIDE = "countryCodeForReviewsOverride";
|
||||
public static final String PREF_SAVE_CRASHES_TO_EXTERNAL_STORAGE = "saveCrashesToExternalStorage";
|
||||
public static final String PREF_SAVE_LOGCAT_ON_CRASH = "saveLogcatOnCrash";
|
||||
|
||||
static final String SYS_PREFERENCES_VERSION = "__preferencesVersion";
|
||||
|
||||
|
@ -166,4 +168,20 @@ public class Settings extends GenericSettings {
|
|||
return code;
|
||||
}
|
||||
|
||||
public boolean getSaveCrashesToExternalStorage() {
|
||||
return getBoolean(PREF_SAVE_CRASHES_TO_EXTERNAL_STORAGE);
|
||||
}
|
||||
|
||||
public void setSaveCrashesToExternalStorage(boolean flag) {
|
||||
setBoolean(PREF_SAVE_CRASHES_TO_EXTERNAL_STORAGE, flag);
|
||||
}
|
||||
|
||||
public boolean getSaveLogcatOnCrash() {
|
||||
return getBoolean(PREF_SAVE_LOGCAT_ON_CRASH);
|
||||
}
|
||||
|
||||
public void setSaveLogcatOnCrash(boolean flag) {
|
||||
setBoolean(PREF_SAVE_LOGCAT_ON_CRASH, flag);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,8 +20,13 @@ import androidx.preference.PreferenceGroup;
|
|||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import dummydomain.yetanothercallblocker.utils.DebuggingUtils;
|
||||
import dummydomain.yetanothercallblocker.work.UpdateScheduler;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
@ -94,6 +99,9 @@ public class SettingsActivity extends AppCompatActivity
|
|||
private static final String PREF_CATEGORY_NOTIFICATIONS = "categoryNotifications";
|
||||
private static final String PREF_SCREEN_ADVANCED = "screenAdvanced";
|
||||
private static final String PREF_COUNTRY_CODES_INFO = "countryCodesInfo";
|
||||
private static final String PREF_SAVE_LOGCAT_TO_FILE = "saveLogcatToFile";
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SettingsFragment.class);
|
||||
|
||||
private final UpdateScheduler updateScheduler = UpdateScheduler.get(App.getInstance());
|
||||
|
||||
|
@ -226,6 +234,12 @@ public class SettingsActivity extends AppCompatActivity
|
|||
EditTextPreference countryCodeForReviewsPreference
|
||||
= requireNonNull(findPreference(Settings.PREF_COUNTRY_CODE_FOR_REVIEWS_OVERRIDE));
|
||||
countryCodeForReviewsPreference.setOnPreferenceChangeListener(countryCodeChangeListener);
|
||||
|
||||
requireNonNull((Preference) findPreference(PREF_SAVE_LOGCAT_TO_FILE))
|
||||
.setOnPreferenceClickListener(preference -> {
|
||||
saveLogcatToFile();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void updateCallScreeningPreference() {
|
||||
|
@ -233,5 +247,20 @@ public class SettingsActivity extends AppCompatActivity
|
|||
requireNonNull(findPreference(PREF_USE_CALL_SCREENING_SERVICE));
|
||||
callScreeningPref.setChecked(PermissionHelper.isCallScreeningHeld(getActivity()));
|
||||
}
|
||||
|
||||
private void saveLogcatToFile() {
|
||||
boolean success = false;
|
||||
try {
|
||||
DebuggingUtils.saveLogcatToFile(getActivity(), true);
|
||||
success = true;
|
||||
} catch (IOException e) {
|
||||
LOG.warn("saveLogcatToFile()", e);
|
||||
}
|
||||
|
||||
Toast.makeText(getActivity(), success
|
||||
? R.string.save_logcat_to_file_done
|
||||
: R.string.save_logcat_to_file_error,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
package dummydomain.yetanothercallblocker.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import dummydomain.yetanothercallblocker.App;
|
||||
|
||||
public class DebuggingUtils {
|
||||
|
||||
private static final String TAG = DebuggingUtils.class.getSimpleName();
|
||||
|
||||
public static void setUpCrashHandler() {
|
||||
Thread.UncaughtExceptionHandler defaultHandler
|
||||
= Thread.getDefaultUncaughtExceptionHandler();
|
||||
|
||||
Thread.UncaughtExceptionHandler customHandler = (t, e) -> {
|
||||
try {
|
||||
handleCrash(e);
|
||||
} finally {
|
||||
if (defaultHandler != null) {
|
||||
defaultHandler.uncaughtException(t, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Thread.setDefaultUncaughtExceptionHandler(customHandler);
|
||||
}
|
||||
|
||||
private static void handleCrash(Throwable e) {
|
||||
boolean logToExternal;
|
||||
try {
|
||||
logToExternal = App.getSettings().getSaveCrashesToExternalStorage();
|
||||
} catch (Exception ignored) {
|
||||
logToExternal = false;
|
||||
}
|
||||
|
||||
try {
|
||||
saveCrashToFile(App.getInstance(), e, logToExternal);
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
boolean saveLogcat;
|
||||
try {
|
||||
saveLogcat = App.getSettings().getSaveLogcatOnCrash();
|
||||
} catch (Exception ignored) {
|
||||
saveLogcat = false;
|
||||
}
|
||||
|
||||
if (saveLogcat) {
|
||||
try {
|
||||
saveLogcatToFile(App.getInstance(), logToExternal);
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveCrashToFile(Context context, Throwable th, boolean external)
|
||||
throws IOException {
|
||||
saveCrashToFile(context, th, external, getDateString());
|
||||
}
|
||||
|
||||
public static void saveLogcatToFile(Context context, boolean external) throws IOException {
|
||||
saveLogcatToFile(context, external, getDateString());
|
||||
}
|
||||
|
||||
private static void saveCrashToFile(Context context, Throwable th,
|
||||
boolean external, String name)
|
||||
throws IOException {
|
||||
String fileName = getFilesDir(context, external).getAbsolutePath()
|
||||
+ "/crash_" + name + ".txt";
|
||||
|
||||
try (PrintWriter writer = new PrintWriter(new FileWriter(fileName))) {
|
||||
th.printStackTrace(writer);
|
||||
}
|
||||
}
|
||||
|
||||
private static void saveLogcatToFile(Context context, boolean external, String name)
|
||||
throws IOException {
|
||||
String path = getFilesDir(context, external).getAbsolutePath()
|
||||
+ "/logcat_" + name + ".txt";
|
||||
|
||||
Log.d(TAG, "Saving logcat to " + path);
|
||||
Runtime.getRuntime().exec("logcat -d -f " + path);
|
||||
}
|
||||
|
||||
private static String getDateString() {
|
||||
return new SimpleDateFormat("yyyyMMdd_HHmmssS", Locale.US).format(new Date());
|
||||
}
|
||||
|
||||
private static File getFilesDir(Context context, boolean external) {
|
||||
if (external) {
|
||||
File[] dirs = ContextCompat.getExternalFilesDirs(context, null);
|
||||
for (File dir : dirs) {
|
||||
if (dir != null) return dir;
|
||||
}
|
||||
|
||||
Log.d(TAG, "getFilesDir() no external dirs available");
|
||||
}
|
||||
|
||||
/*
|
||||
not secure enough
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
if (!context.isDeviceProtectedStorage()) {
|
||||
context = context.createDeviceProtectedStorageContext();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return context.getFilesDir();
|
||||
}
|
||||
|
||||
}
|
|
@ -99,4 +99,13 @@
|
|||
<string name="country_code_for_reviews_override">Код страны для отзывов</string>
|
||||
<string name="country_code_for_reviews_override_summary">Код страны, используемый для запроса онлайн-отзывов. Предназначен для представления страны звонящего. Оставьте пустым для автоопределения</string>
|
||||
<string name="country_code_incorrect_format">Некорректный формат кода страны. Значение не обновлено</string>
|
||||
<string name="settings_category_debugging">Отладка</string>
|
||||
<string name="save_crashes_to_external_storage">Сохранять отчёты в общее хранилище</string>
|
||||
<string name="save_crashes_to_external_storage_summary">Сохранять отчёты об ошибках и логи в общее хранилище. В противном случае отчёты сохраняются в личную папку приложения. Отчёты могут содержать конфиденциальные данные (номера телефонов, имена контактов). Другие приложения с разрешением на доступ к хранилищу могут иметь доступ к этим данным в общем хранилище</string>
|
||||
<string name="save_logcat_on_crash">Сохранять logcat при ошибках</string>
|
||||
<string name="save_logcat_on_crash_summary">Сохранять содержимое logcat при ошибках (в дополнение к обычныму stacktrace)</string>
|
||||
<string name="save_logcat_to_file">Сохранить logcat</string>
|
||||
<string name="save_logcat_to_file_summary">Сохранить содержимое logcat в файл в общем хранилище (/sdcard/Android/data/dummydomain.yetanothercallblocker/files/). Отчёты могут содержать конфиденциальные данные (номера телефонов, имена контактов). Другие приложения с разрешением на доступ к хранилищу могут иметь доступ к этим данным в общем хранилище</string>
|
||||
<string name="save_logcat_to_file_done">Выполнено</string>
|
||||
<string name="save_logcat_to_file_error">Ошибка</string>
|
||||
</resources>
|
|
@ -111,6 +111,15 @@
|
|||
<string name="country_code_for_reviews_override">Country code for reviews</string>
|
||||
<string name="country_code_for_reviews_override_summary">Country code used in requests for online reviews. Meant to represent the country of the caller. Leave empty for auto-detection</string>
|
||||
<string name="country_code_incorrect_format">Incorrect country code format. Value is not updated</string>
|
||||
<string name="settings_category_debugging">Debugging</string>
|
||||
<string name="save_crashes_to_external_storage">Save reports to public storage</string>
|
||||
<string name="save_crashes_to_external_storage_summary">Save crash reports and logs to public storage, otherwise crash reports are saved to a private app folder. The reports may contain sensitive data (phone numbers, contact names). Other apps with storage permission may have access to this data in public storage</string>
|
||||
<string name="save_logcat_on_crash">Save logcat on crash</string>
|
||||
<string name="save_logcat_on_crash_summary">Save logcat output on crash (in addition to a basic stacktrace)</string>
|
||||
<string name="save_logcat_to_file">Save logcat</string>
|
||||
<string name="save_logcat_to_file_summary">Save logcat output to a file in public storage (/sdcard/Android/data/dummydomain.yetanothercallblocker/files/). The reports may contain sensitive data (phone numbers, contact names). Other apps with storage permission may have access to this data in public storage</string>
|
||||
<string name="save_logcat_to_file_done">Done</string>
|
||||
<string name="save_logcat_to_file_error">Error</string>
|
||||
|
||||
<string name="open_debug_activity">Open debug screen</string>
|
||||
<string name="debug_activity_label">Debug</string>
|
||||
|
|
|
@ -68,6 +68,23 @@
|
|||
app:summary="@string/country_code_for_reviews_override_summary"
|
||||
app:title="@string/country_code_for_reviews_override" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
app:key="categoryDebugging"
|
||||
app:title="@string/settings_category_debugging">
|
||||
<SwitchPreferenceCompat
|
||||
app:key="saveCrashesToExternalStorage"
|
||||
app:summary="@string/save_crashes_to_external_storage_summary"
|
||||
app:title="@string/save_crashes_to_external_storage" />
|
||||
<SwitchPreferenceCompat
|
||||
app:key="saveLogcatOnCrash"
|
||||
app:summary="@string/save_logcat_on_crash_summary"
|
||||
app:title="@string/save_logcat_on_crash" />
|
||||
<Preference
|
||||
app:key="saveLogcatToFile"
|
||||
app:persistent="false"
|
||||
app:summary="@string/save_logcat_to_file_summary"
|
||||
app:title="@string/save_logcat_to_file" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
</PreferenceCategory>
|
||||
|
||||
|
|
Loading…
Reference in New Issue