diff --git a/features/strict_mode_disc.feature b/features/strict_mode_disc.feature new file mode 100644 index 0000000000..d254672b83 --- /dev/null +++ b/features/strict_mode_disc.feature @@ -0,0 +1,9 @@ +Feature: Android support + +Scenario: Test handled Android Exception + When I run "StrictModeDiscScenario" with the defaults + Then I should receive a request + And the request is a valid for the error reporting API + And the exception "errorClass" equals "android.os.StrictMode$StrictModeViolation" + And the exception "message" equals "policy=262145 violation=1" + And the event "metaData.StrictMode.Violation" equals "DiskWrite" diff --git a/features/strict_mode_network.feature b/features/strict_mode_network.feature new file mode 100644 index 0000000000..409d1a5fe1 --- /dev/null +++ b/features/strict_mode_network.feature @@ -0,0 +1,9 @@ +Feature: Android support + +Scenario: Test handled Android Exception + When I run "StrictModeNetworkScenario" with the defaults + Then I should receive a request + And the request is a valid for the error reporting API + And the exception "errorClass" equals "android.os.StrictMode$StrictModeViolation" + And the exception "message" equals "policy=262148 violation=4" + And the event "metaData.StrictMode.Violation" equals "NetworkOperation" diff --git a/mazerunner/src/main/java/com/bugsnag/android/mazerunner/MainActivity.kt b/mazerunner/src/main/java/com/bugsnag/android/mazerunner/MainActivity.kt index d9517be52e..87af744626 100644 --- a/mazerunner/src/main/java/com/bugsnag/android/mazerunner/MainActivity.kt +++ b/mazerunner/src/main/java/com/bugsnag/android/mazerunner/MainActivity.kt @@ -35,7 +35,7 @@ class MainActivity : Activity() { private fun executeTestCase() { val eventType = intent.getStringExtra("EVENT_TYPE") Log.d("Bugsnag", "Received test case, executing " + eventType) - val testCase = factory.testCaseForName(eventType) + val testCase = factory.testCaseForName(eventType, this) testCase.run() } diff --git a/mazerunner/src/main/java/com/bugsnag/android/mazerunner/TestCaseFactory.kt b/mazerunner/src/main/java/com/bugsnag/android/mazerunner/TestCaseFactory.kt index 403372f90e..57267d314b 100644 --- a/mazerunner/src/main/java/com/bugsnag/android/mazerunner/TestCaseFactory.kt +++ b/mazerunner/src/main/java/com/bugsnag/android/mazerunner/TestCaseFactory.kt @@ -1,13 +1,16 @@ package com.bugsnag.android.mazerunner +import android.content.Context import com.bugsnag.android.mazerunner.scenarios.Scenario internal class TestCaseFactory { - fun testCaseForName(eventType: String?): Scenario { + fun testCaseForName(eventType: String?, context: Context?): Scenario { try { val clz = Class.forName("com.bugsnag.android.mazerunner.scenarios.$eventType") - return clz.newInstance() as Scenario + val scenario = clz.newInstance() as Scenario + scenario.context = context + return scenario } catch (e: Exception) { throw IllegalArgumentException("Failed to find class for $eventType") } diff --git a/mazerunner/src/main/java/com/bugsnag/android/mazerunner/scenarios/Scenario.kt b/mazerunner/src/main/java/com/bugsnag/android/mazerunner/scenarios/Scenario.kt index 46af24c887..720982f030 100644 --- a/mazerunner/src/main/java/com/bugsnag/android/mazerunner/scenarios/Scenario.kt +++ b/mazerunner/src/main/java/com/bugsnag/android/mazerunner/scenarios/Scenario.kt @@ -1,10 +1,13 @@ package com.bugsnag.android.mazerunner.scenarios +import android.content.Context import com.bugsnag.android.Bugsnag import com.bugsnag.android.NetworkException abstract internal class Scenario { + var context: Context? = null + abstract fun run() /** diff --git a/mazerunner/src/main/java/com/bugsnag/android/mazerunner/scenarios/StrictModeDiscScenario.kt b/mazerunner/src/main/java/com/bugsnag/android/mazerunner/scenarios/StrictModeDiscScenario.kt new file mode 100644 index 0000000000..b6af1fce3d --- /dev/null +++ b/mazerunner/src/main/java/com/bugsnag/android/mazerunner/scenarios/StrictModeDiscScenario.kt @@ -0,0 +1,20 @@ +package com.bugsnag.android.mazerunner.scenarios + +import android.os.StrictMode +import java.io.File + +/** + * Generates a strictmode exception caused by writing to disc on main thread + */ +internal class StrictModeDiscScenario : Scenario() { + + override fun run() { + StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder() + .detectDiskWrites() + .penaltyDeath() + .build()) + val file = File(context?.cacheDir, "fake") + file.writeBytes("test".toByteArray()) + } + +} diff --git a/mazerunner/src/main/java/com/bugsnag/android/mazerunner/scenarios/StrictModeNetworkScenario.kt b/mazerunner/src/main/java/com/bugsnag/android/mazerunner/scenarios/StrictModeNetworkScenario.kt new file mode 100644 index 0000000000..bb30d95e9d --- /dev/null +++ b/mazerunner/src/main/java/com/bugsnag/android/mazerunner/scenarios/StrictModeNetworkScenario.kt @@ -0,0 +1,23 @@ +package com.bugsnag.android.mazerunner.scenarios + +import android.os.StrictMode +import java.net.HttpURLConnection +import java.net.URL + +/** + * Generates a strictmode exception caused by performing a network request on the main thread + */ +internal class StrictModeNetworkScenario : Scenario() { + + override fun run() { + StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder() + .detectNetwork() + .penaltyDeath() + .build()) + + val urlConnection = URL("http://example.com").openConnection() as HttpURLConnection + urlConnection.doOutput = true + urlConnection.responseMessage + } + +} diff --git a/sdk/src/main/java/com/bugsnag/android/ExceptionHandler.java b/sdk/src/main/java/com/bugsnag/android/ExceptionHandler.java index e3ce29721e..80be2a81ec 100644 --- a/sdk/src/main/java/com/bugsnag/android/ExceptionHandler.java +++ b/sdk/src/main/java/com/bugsnag/android/ExceptionHandler.java @@ -1,5 +1,6 @@ package com.bugsnag.android; +import android.os.StrictMode; import android.support.annotation.NonNull; import java.lang.Thread.UncaughtExceptionHandler; @@ -70,8 +71,16 @@ public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwab String severityReason = strictModeThrowable ? HandledState.REASON_STRICT_MODE : HandledState.REASON_UNHANDLED_EXCEPTION; - client.cacheAndNotify(throwable, Severity.ERROR, - metaData, severityReason, violationDesc); + + if (strictModeThrowable) { // writes to disk on main thread + StrictMode.ThreadPolicy originalThreadPolicy = StrictMode.getThreadPolicy(); + StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.LAX); + + client.cacheAndNotify(throwable, Severity.ERROR, + metaData, severityReason, violationDesc); + + StrictMode.setThreadPolicy(originalThreadPolicy); + } } // Pass exception on to original exception handler