-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Block reports sent to ACRA #17402
Block reports sent to ACRA #17402
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Architecture:
- This doesn't process logcat
- I believe this changes the type of the exception to
Throwable
AnkiDroid/src/test/java/com/ichi2/anki/CrashReportServiceTest.kt
Outdated
Show resolved
Hide resolved
AnkiDroid/src/test/java/com/ichi2/anki/CrashReportServiceTest.kt
Outdated
Show resolved
Hide resolved
with regard to "This doesn't process logcat" from David -> I noticed while verifying my acrarium purge that there were some instances where the stack prompting a current error did not contain an email but the included logcat did. So it is a valid concern. I altered my purge matcher to also match emails deeper into the report (logcat also, not just original stack) so I'm cleaning them now, but better if they never hit logcat. Whole thing is messy, for sure |
Work's left to be done. Still unsure about certain things. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks SO much better, thanks so much!!!
One suggestion, take it or leave it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approval still stands.
Important
squash-merge
Given discussion on Discord - pushing this back to also be implemented for unhandled exceptions
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as outdated.
This comment was marked as outdated.
No idea what's taking so long (about anki upstream release, that is), but anyway 24.11rc2 just released and I'm going to publish it soon on backend |
8a2f840
to
e68c8fc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I implemented the handling of uncaught exceptions as well, what do you guys think?
I thought the isPII filter and initial test were great, so I'm approving those
Happy to bend the last commit around to whatever seems best to get it in
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Two questions
I had a play with Default interface implementations. I didn't like this and wouldn't recommend it, as it makes more methods public than there should be, but it is less code
Patch
Subject: [PATCH] refactor: extract dialog fragments from Anki[Activity/Fragment]
* dismissAllDialogFragments
* showDialogFragment
**AnkiFragment**
* showDialogFragment
This affects all callers, as we now use extension methods
---
Index: AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/ThrowableFilterService.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/ThrowableFilterService.kt b/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/ThrowableFilterService.kt
--- a/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/ThrowableFilterService.kt (revision e68c8fc839fbc5a07dbce6343bc982094a928d15)
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/ThrowableFilterService.kt (date 1733018370799)
@@ -16,71 +16,38 @@
****************************************************************************************/
package com.ichi2.anki.servicelayer
-import androidx.annotation.VisibleForTesting
+import com.ichi2.anki.exception.ChainedUncaughtExceptionHandler
+import com.ichi2.anki.exception.Handled
import net.ankiweb.rsdroid.exceptions.BackendSyncException.BackendSyncServerMessageException
import timber.log.Timber
-object ThrowableFilterService {
-
- @FunctionalInterface
- fun interface FilteringExceptionHandler : Thread.UncaughtExceptionHandler
+object ThrowableFilterService : ChainedUncaughtExceptionHandler {
+ override var originalUncaughtExceptionHandler: Thread.UncaughtExceptionHandler? = null
- @VisibleForTesting
- var originalUncaughtExceptionHandler: Thread.UncaughtExceptionHandler? = null
-
- var uncaughtExceptionHandler = FilteringExceptionHandler {
- thread: Thread?, throwable: Throwable ->
+ override fun handleUncaughtException(thread: Thread?, throwable: Throwable): Handled {
if (thread == null) {
Timber.w("unexpected: thread was null")
- return@FilteringExceptionHandler
+ return Handled.NO_CHAIN
}
if (shouldDiscardThrowable(throwable)) {
Timber.i("discarding throwable")
- return@FilteringExceptionHandler
+ Handled.NO_CHAIN
}
- originalUncaughtExceptionHandler?.uncaughtException(thread, throwable)
+ return Handled.CHAIN
}
fun initialize() {
- Timber.i("initialize()")
installDefaultExceptionHandler()
}
- /**
- * We want to filter any exceptions with PII, then chain to other handlers
- */
- @Synchronized
- @VisibleForTesting
- fun installDefaultExceptionHandler() {
- originalUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
- Timber.d("Chaining to uncaughtExceptionHandler (%s)", originalUncaughtExceptionHandler)
- Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler)
- }
-
- /**
- * Reset the default exception handler
- */
- @Synchronized
- @VisibleForTesting
- fun unInstallDefaultExceptionHandler() {
- Thread.setDefaultUncaughtExceptionHandler(originalUncaughtExceptionHandler)
- originalUncaughtExceptionHandler = null
- }
-
/**
* Re-Initialize the throwable filter
*/
- @Synchronized
fun reInitialize() {
- // send any pending async hits, re-chain default exception handlers and re-init
- Timber.i("reInitialize()")
- unInstallDefaultExceptionHandler()
- initialize()
+ reInitializeDefaultExceptionHandler()
}
- fun shouldDiscardThrowable(t: Throwable): Boolean {
- return !t.safeFromPII()
- }
+ fun shouldDiscardThrowable(t: Throwable) = !t.safeFromPII()
/**
* Checks if the [Throwable] is safe from Personally Identifiable Information (PII)
Index: AnkiDroid/src/main/java/com/ichi2/anki/analytics/UsageAnalytics.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/analytics/UsageAnalytics.kt b/AnkiDroid/src/main/java/com/ichi2/anki/analytics/UsageAnalytics.kt
--- a/AnkiDroid/src/main/java/com/ichi2/anki/analytics/UsageAnalytics.kt (revision e68c8fc839fbc5a07dbce6343bc982094a928d15)
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/analytics/UsageAnalytics.kt (date 1733018083962)
@@ -29,6 +29,8 @@
import com.ichi2.anki.AnkiDroidApp
import com.ichi2.anki.BuildConfig
import com.ichi2.anki.R
+import com.ichi2.anki.exception.ChainedUncaughtExceptionHandler
+import com.ichi2.anki.exception.Handled
import com.ichi2.anki.preferences.sharedPrefs
import com.ichi2.utils.DisplayUtils
import com.ichi2.utils.KotlinCleanup
@@ -38,12 +40,12 @@
import timber.log.Timber
@KotlinCleanup("see if we can make variables lazy, or properties without the `s` prefix")
-object UsageAnalytics {
+object UsageAnalytics : ChainedUncaughtExceptionHandler {
const val ANALYTICS_OPTIN_KEY = "analytics_opt_in"
@KotlinCleanup("lateinit")
private var sAnalytics: GoogleAnalytics? = null
- private var sOriginalUncaughtExceptionHandler: Thread.UncaughtExceptionHandler? = null
+ override var originalUncaughtExceptionHandler: Thread.UncaughtExceptionHandler? = null
private var sOptIn = false
private var sAnalyticsTrackingId: String? = null
private var sAnalyticsSamplePercentage = -1
@@ -51,14 +53,13 @@
@FunctionalInterface
fun interface AnalyticsLoggingExceptionHandler : Thread.UncaughtExceptionHandler
- var uncaughtExceptionHandler = AnalyticsLoggingExceptionHandler {
- thread: Thread?, throwable: Throwable ->
+ override fun handleUncaughtException(thread: Thread?, throwable: Throwable): Handled {
sendAnalyticsException(throwable, true)
if (thread == null) {
Timber.w("unexpected: thread was null")
- return@AnalyticsLoggingExceptionHandler
+ return Handled.NO_CHAIN
}
- sOriginalUncaughtExceptionHandler!!.uncaughtException(thread, throwable)
+ return Handled.CHAIN
}
/**
@@ -127,27 +128,6 @@
reInitialize()
}
- /**
- * We want to send an analytics hit on any exception, then chain to other handlers (e.g., ACRA)
- */
- @Synchronized
- @VisibleForTesting
- fun installDefaultExceptionHandler() {
- sOriginalUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
- Timber.d("Chaining to uncaughtExceptionHandler (%s)", sOriginalUncaughtExceptionHandler)
- Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler)
- }
-
- /**
- * Reset the default exception handler
- */
- @Synchronized
- @VisibleForTesting
- fun unInstallDefaultExceptionHandler() {
- Thread.setDefaultUncaughtExceptionHandler(sOriginalUncaughtExceptionHandler)
- sOriginalUncaughtExceptionHandler = null
- }
-
/**
* Allow users to enable or disable analytics
*/
Index: AnkiDroid/src/main/java/com/ichi2/anki/exception/ChainedUncaughtExceptionHandler.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/exception/ChainedUncaughtExceptionHandler.kt b/AnkiDroid/src/main/java/com/ichi2/anki/exception/ChainedUncaughtExceptionHandler.kt
new file mode 100644
--- /dev/null (date 1733018290159)
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/exception/ChainedUncaughtExceptionHandler.kt (date 1733018290159)
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2024 David Allison <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.ichi2.anki.exception
+
+import androidx.annotation.CheckResult
+import androidx.annotation.VisibleForTesting
+import timber.log.Timber
+
+enum class Handled {
+ NO_CHAIN,
+ CHAIN
+}
+
+interface ChainedUncaughtExceptionHandler {
+ @CheckResult
+ fun handleUncaughtException(thread: Thread?, throwable: Throwable) : Handled
+
+ /* Implementation */
+
+ var originalUncaughtExceptionHandler: Thread.UncaughtExceptionHandler?
+
+ /**
+ * We want to send an analytics hit on any exception, then chain to other handlers (e.g., ACRA)
+ */
+ @VisibleForTesting
+ fun installDefaultExceptionHandler() {
+ Timber.i("installDefaultExceptionHandler")
+ synchronized(this) {
+ originalUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
+ Timber.d("Chaining to uncaughtExceptionHandler (%s)", originalUncaughtExceptionHandler)
+ Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler)
+ }
+ }
+
+ /**
+ * Reset the default exception handler
+ */
+ @VisibleForTesting
+ fun unInstallDefaultExceptionHandler() {
+ synchronized(this) {
+ Thread.setDefaultUncaughtExceptionHandler(originalUncaughtExceptionHandler)
+ originalUncaughtExceptionHandler = null
+ }
+ }
+
+ fun reInitializeDefaultExceptionHandler() {
+ Timber.i("reInitializeDefaultExceptionHandler()")
+ synchronized(this) {
+ unInstallDefaultExceptionHandler()
+ installDefaultExceptionHandler()
+ }
+ }
+
+ val uncaughtExceptionHandler: Thread.UncaughtExceptionHandler
+ get() = Thread.UncaughtExceptionHandler { thread: Thread?, throwable: Throwable ->
+ when (handleUncaughtException(thread, throwable)) {
+ Handled.NO_CHAIN -> return@UncaughtExceptionHandler
+ Handled.CHAIN -> { }
+ }
+ if (thread == null) {
+ Timber.w("unexpected: thread was null")
+ return@UncaughtExceptionHandler
+ }
+ originalUncaughtExceptionHandler!!.uncaughtException(thread, throwable)
+ }
+}
\ No newline at end of file
AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/ThrowableFilterService.kt
Outdated
Show resolved
Hide resolved
If macOS unit test fails, and it seems like it might with a current unrelated heapspace problem, contemplating testing one of these things - 1- changing these params on test forking behavior: Lines 51 to 54 in 0c5ea9d
2- bumping up heapspace 🤷 |
Co-authored-by: David Allison <[email protected]>
We want to filter all exception - that is ones that are uncaught as well as ones that are imperatively sent through CrashReportService That requires attaching filter to out Thread.defaultExceptionHandler chain - extract filter into service, use it from crash report service - implement default exception handler handling, init/reinit as needed - concrete types for the handlers and public uninstall/install for testability
05d76e8
to
f00ceca
Compare
be47375
to
eeef1d5
Compare
Fixed the macOS runner issue as well (test executors needed more heap) |
See title. Related:
#17392
ankidroid/Anki-Android-Backend#439
ankitects/anki@ba1f5f4
Preemptive "may lord have mercy".