From 44aff485c9f0415bba3f3e87464f401480cc1ea3 Mon Sep 17 00:00:00 2001 From: Steve Kirkland-Walton Date: Thu, 4 Jan 2024 16:59:52 +0000 Subject: [PATCH 01/14] Set appActivity and appPackage [full ci] --- .buildkite/pipeline.full.yml | 28 ++++++++++++++++++++++++++++ .buildkite/pipeline.yml | 16 ++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/.buildkite/pipeline.full.yml b/.buildkite/pipeline.full.yml index ae6121d898..998dd980ae 100644 --- a/.buildkite/pipeline.full.yml +++ b/.buildkite/pipeline.full.yml @@ -47,6 +47,8 @@ steps: command: - "features/minimal" - "--app=/app/build/fixture-minimal.apk" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_9" - "--no-tunnel" @@ -73,6 +75,8 @@ steps: - "features/smoke_tests" - "--tags=debug-safe" - "--app=/app/build/fixture-debug.apk" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_9" - "--no-tunnel" @@ -102,6 +106,8 @@ steps: command: - "features/full_tests" - "--app=@build/fixture-r19-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_7" - "--no-tunnel" @@ -133,6 +139,8 @@ steps: - "features/full_tests" - "--exclude=features/full_tests/[^l-z].*.feature" - "--app=@build/fixture-r19-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_7" - "--no-tunnel" @@ -160,6 +168,8 @@ steps: command: - "features/full_tests" - "--app=@build/fixture-r19-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_8" - "--no-tunnel" @@ -189,6 +199,8 @@ steps: - "features/full_tests" - "--exclude=features/full_tests/[^l-z].*.feature" - "--app=@build/fixture-r19-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_8" - "--no-tunnel" @@ -218,6 +230,8 @@ steps: command: - "features/full_tests" - "--app=@build/fixture-r21-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_9" - "--no-tunnel" @@ -249,6 +263,8 @@ steps: - "features/full_tests" - "--exclude=features/full_tests/[^l-z].*.feature" - "--app=@build/fixture-r21-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_9" - "--no-tunnel" @@ -279,6 +295,8 @@ steps: - "features/full_tests" - "--exclude=features/full_tests/[^a-k].*.feature" - "--app=@build/fixture-r21-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_10" - "--no-tunnel" @@ -309,6 +327,8 @@ steps: - "features/full_tests" - "--exclude=features/full_tests/[^l-z].*.feature" - "--app=@build/fixture-r21-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_10" - "--no-tunnel" @@ -343,6 +363,8 @@ steps: - "features/full_tests" - "--exclude=features/full_tests/[^a-k].*.feature" - "--app=@build/fixture-r21-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_11" - "--no-tunnel" @@ -373,6 +395,8 @@ steps: - "features/full_tests" - "--exclude=features/full_tests/[^l-z].*.feature" - "--app=@build/fixture-r21-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_11" - "--no-tunnel" @@ -403,6 +427,8 @@ steps: - "features/full_tests" - "--exclude=features/full_tests/[^a-k].*.feature" - "--app=@build/fixture-r21-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_13" - "--no-tunnel" @@ -433,6 +459,8 @@ steps: - "features/full_tests" - "--exclude=features/full_tests/[^l-z].*.feature" - "--app=@build/fixture-r21-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_13" - "--no-tunnel" diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index af44369385..cfb6d2f52b 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -99,6 +99,8 @@ steps: command: - "features/smoke_tests" - "--app=@build/fixture-r19-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_7" - "--no-tunnel" @@ -126,6 +128,8 @@ steps: command: - "features/smoke_tests" - "--app=@build/fixture-r19-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_8" - "--no-tunnel" @@ -155,6 +159,8 @@ steps: command: - "features/smoke_tests" - "--app=@build/fixture-r21-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_9" - "--no-tunnel" @@ -184,6 +190,8 @@ steps: command: - "features/smoke_tests" - "--app=@build/fixture-r21-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_10" - "--no-tunnel" @@ -214,6 +222,8 @@ steps: command: - "features/smoke_tests" - "--app=@build/fixture-r21-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_11" - "--no-tunnel" @@ -249,6 +259,8 @@ steps: - "features/full_tests" - "--exclude=features/full_tests/[^a-k].*.feature" - "--app=@build/fixture-r21-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_12" - "--no-tunnel" @@ -280,6 +292,8 @@ steps: - "features/full_tests" - "--exclude=features/full_tests/[^l-z].*.feature" - "--app=@build/fixture-r21-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_12" - "--no-tunnel" @@ -309,6 +323,8 @@ steps: command: - "features/smoke_tests" - "--app=@build/fixture-r21-url.txt" + - "--app-activity=com.bugsnag.android.mazerunner.MainActivity" + - "--app-package=com.bugsnag.android.mazerunner" - "--farm=bb" - "--device=ANDROID_13" - "--no-tunnel" From 4e951fc7821f926febeb5c035e316d2628e7292f Mon Sep 17 00:00:00 2001 From: jason Date: Mon, 8 Jan 2024 10:18:14 +0000 Subject: [PATCH 02/14] fix(ndk): remove possibility the the `bsg_global_env->handling_crash` or `unwinding_crash_stack` CAS succeed spuriously --- CHANGELOG.md | 7 +++++++ bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c | 2 +- .../src/main/jni/utils/stack_unwinder.cpp | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 932d455415..f06b6cb6a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## TBD + +### Bug fixes + +* Avoid any possibility of multiple conflicting native crash handlers or stack-unwinders running concurrently + [#1960](https://github.com/bugsnag/bugsnag-android/pull/1960) + ## 6.1.0 (2023-12-05) ### Enhancements diff --git a/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c b/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c index e647a3b88c..e2fbd419b4 100644 --- a/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c +++ b/bugsnag-plugin-android-ndk/src/main/jni/bugsnag_ndk.c @@ -68,7 +68,7 @@ bool bsg_run_on_error() { } bool bsg_begin_handling_crash() { - static bool expected = false; + bool expected = false; return atomic_compare_exchange_strong(&bsg_global_env->handling_crash, &expected, true); } diff --git a/bugsnag-plugin-android-ndk/src/main/jni/utils/stack_unwinder.cpp b/bugsnag-plugin-android-ndk/src/main/jni/utils/stack_unwinder.cpp index 3df2edb00d..f0299e894b 100644 --- a/bugsnag-plugin-android-ndk/src/main/jni/utils/stack_unwinder.cpp +++ b/bugsnag-plugin-android-ndk/src/main/jni/utils/stack_unwinder.cpp @@ -142,7 +142,7 @@ ssize_t bsg_unwind_crash_stack(bugsnag_stackframe stack[BUGSNAG_FRAMES_MAX], // we always check unwinding_crash_stack and set *before* attempting to // retrieve the crash unwinder to avoid picking up an unwinder that is about // to be destroyed by bsg_unwinder_refresh - static bool expected = false; + bool expected = false; if (!std::atomic_compare_exchange_strong(&unwinding_crash_stack, &expected, true)) { return 0; From 069f35545e08a70e10fdec0d6d7cfd2d7d4a8350 Mon Sep 17 00:00:00 2001 From: jason Date: Mon, 8 Jan 2024 14:35:07 +0000 Subject: [PATCH 03/14] fix(ndk): remove the use of `substr` from `bsg_check_invalid_libname` --- .../src/main/jni/utils/stack_unwinder.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/bugsnag-plugin-android-ndk/src/main/jni/utils/stack_unwinder.cpp b/bugsnag-plugin-android-ndk/src/main/jni/utils/stack_unwinder.cpp index f0299e894b..5ff5a36d02 100644 --- a/bugsnag-plugin-android-ndk/src/main/jni/utils/stack_unwinder.cpp +++ b/bugsnag-plugin-android-ndk/src/main/jni/utils/stack_unwinder.cpp @@ -39,12 +39,20 @@ static void bsg_fallback_symbols(const uint64_t addr, } static bool bsg_check_invalid_libname(const std::string &filename) { + const auto length = filename.length(); + // turn clang-format off - for clarity in the string compare: + // clang-format off return filename.empty() || // if the filename ends-in ".apk" then the lib was loaded without // extracting it from the apk we consider these as "invalid" to trigger // the use of a fallback filename - (filename.length() >= 4 && - filename.substr(filename.length() - 4, 4) == ".apk"); + (length >= 4 && + // compare char-by-char to avoid the allocation is substr + filename[length - 4] == '.' && + filename[length - 3] == 'a' && + filename[length - 2] == 'p' && + filename[length - 1] == 'k'); + // clang-format on } void bsg_unwinder_init() { From 26fcfbc70792225dc55d8b7baa20a18d7453be50 Mon Sep 17 00:00:00 2001 From: Yousif Ahmed Date: Mon, 15 Jan 2024 14:56:28 +0000 Subject: [PATCH 04/14] fix(plugin-react-native): Keep BugsnagReactNativePlugin class for reflection --- bugsnag-plugin-react-native/proguard-rules.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bugsnag-plugin-react-native/proguard-rules.pro b/bugsnag-plugin-react-native/proguard-rules.pro index f9048d25cb..14ac470976 100644 --- a/bugsnag-plugin-react-native/proguard-rules.pro +++ b/bugsnag-plugin-react-native/proguard-rules.pro @@ -1,3 +1,3 @@ -keepattributes LineNumberTable,SourceFile -keepnames class com.facebook.react.common.JavascriptException { *; } --keepnames class com.bugsnag.android.BugsnagReactNativePlugin { *; } +-keep class com.bugsnag.android.BugsnagReactNativePlugin { *; } From 8d26b58ede9af5f01d5b6dcb233043596dadc933 Mon Sep 17 00:00:00 2001 From: jason Date: Thu, 14 Dec 2023 13:35:29 +0000 Subject: [PATCH 05/14] fix(json): NaN & Infinity values omitted instead of causing invalid error reports --- CHANGELOG.md | 4 +- .../java/com/bugsnag/android/JsonWriter.java | 43 +-- .../com/bugsnag/android/JsonWriterTest.java | 276 +++++++++--------- 3 files changed, 174 insertions(+), 149 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f06b6cb6a1..0b358545a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ * Avoid any possibility of multiple conflicting native crash handlers or stack-unwinders running concurrently [#1960](https://github.com/bugsnag/bugsnag-android/pull/1960) +* Metadata that includes non-finite doubles (NaN, +Infinity, -Infinity) are omitted instead of breaking serialization + [#1958](https://github.com/bugsnag/bugsnag-android/pull/1958) ## 6.1.0 (2023-12-05) @@ -16,7 +18,7 @@ ### Bug fixes -* Updating existing feature flags no longer causes them to change location. +* Updating existing feature-flags flags no longer causes them to change location. [#1940](https://github.com/bugsnag/bugsnag-android/pull/1940) * Fixed possible NDK crash when constructing several concurrent `Client` instances [#1950](https://github.com/bugsnag/bugsnag-android/pull/1950) diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/JsonWriter.java b/bugsnag-android-core/src/main/java/com/bugsnag/android/JsonWriter.java index 3e352b713d..cb4fdddb43 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/JsonWriter.java +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/JsonWriter.java @@ -145,6 +145,7 @@ class JsonWriter implements Closeable, Flushable { */ private static final String[] REPLACEMENT_CHARS; private static final String[] HTML_SAFE_REPLACEMENT_CHARS; + static { REPLACEMENT_CHARS = new String[128]; for (int i = 0; i <= 0x1f; i++) { @@ -165,11 +166,14 @@ class JsonWriter implements Closeable, Flushable { HTML_SAFE_REPLACEMENT_CHARS['\''] = "\\u0027"; } - /** The output data, containing at most one top-level array or object. */ + /** + * The output data, containing at most one top-level array or object. + */ private final Writer out; private int[] stack = new int[32]; private int stackSize = 0; + { push(EMPTY_DOCUMENT); } @@ -337,7 +341,7 @@ private JsonWriter open(int empty, String openBracket) throws IOException { * given bracket. */ private JsonWriter close(int empty, int nonempty, String closeBracket) - throws IOException { + throws IOException { int context = peek(); if (context != nonempty && context != empty) { throw new IllegalStateException("Nesting problem."); @@ -437,7 +441,7 @@ public JsonWriter jsonValue(String value) throws IOException { } writeDeferredName(); beforeValue(); - out.append(value); + out.write(value); return this; } @@ -490,17 +494,18 @@ public JsonWriter value(Boolean value) throws IOException { /** * Encodes {@code value}. * - * @param value a finite value. May not be {@link Double#isNaN() NaNs} or - * {@link Double#isInfinite() infinities}. + * @param value a finite value. * @return this writer. */ public JsonWriter value(double value) throws IOException { - writeDeferredName(); if (!lenient && (Double.isNaN(value) || Double.isInfinite(value))) { - throw new IllegalArgumentException("Numeric values must be finite, but was " + value); + // omit these values instead of attempting to write them + deferredName = null; + } else { + writeDeferredName(); + beforeValue(); + out.write(Double.toString(value)); } - beforeValue(); - out.append(Double.toString(value)); return this; } @@ -520,7 +525,7 @@ public JsonWriter value(long value) throws IOException { * Encodes {@code value}. * * @param value a finite value. May not be {@link Double#isNaN() NaNs} or - * {@link Double#isInfinite() infinities}. + * {@link Double#isInfinite() infinities}. * @return this writer. */ public JsonWriter value(Number value) throws IOException { @@ -528,14 +533,16 @@ public JsonWriter value(Number value) throws IOException { return nullValue(); } - writeDeferredName(); String string = value.toString(); if (!lenient - && (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) { - throw new IllegalArgumentException("Numeric values must be finite, but was " + value); + && (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) { + // omit this value + deferredName = null; + } else { + writeDeferredName(); + beforeValue(); + out.write(string); } - beforeValue(); - out.append(string); return this; } @@ -634,7 +641,7 @@ void beforeValue() throws IOException { case NONEMPTY_DOCUMENT: if (!lenient) { throw new IllegalStateException( - "JSON must have only one top-level value."); + "JSON must have only one top-level value."); } // fall-through case EMPTY_DOCUMENT: // first in document @@ -647,12 +654,12 @@ void beforeValue() throws IOException { break; case NONEMPTY_ARRAY: // another in array - out.append(','); + out.write(','); newline(); break; case DANGLING_NAME: // value for name - out.append(separator); + out.write(separator); replaceTop(NONEMPTY_OBJECT); break; diff --git a/bugsnag-android-core/src/test/java/com/bugsnag/android/JsonWriterTest.java b/bugsnag-android-core/src/test/java/com/bugsnag/android/JsonWriterTest.java index 65239da116..d0db16b614 100644 --- a/bugsnag-android-core/src/test/java/com/bugsnag/android/JsonWriterTest.java +++ b/bugsnag-android-core/src/test/java/com/bugsnag/android/JsonWriterTest.java @@ -76,7 +76,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch assertEquals("{\"a\":\"a\"}", string5.toString()); } - @Test public void testInvalidTopLevelTypes() throws IOException { + @Test + public void testInvalidTopLevelTypes() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.name("hello"); @@ -87,7 +88,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch } } - @Test public void testTwoNames() throws IOException { + @Test + public void testTwoNames() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginObject(); @@ -99,7 +101,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch } } - @Test public void testNameWithoutValue() throws IOException { + @Test + public void testNameWithoutValue() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginObject(); @@ -111,7 +114,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch } } - @Test public void testValueWithoutName() throws IOException { + @Test + public void testValueWithoutName() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginObject(); @@ -122,7 +126,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch } } - @Test public void testMultipleTopLevelValues() throws IOException { + @Test + public void testMultipleTopLevelValues() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginArray().endArray(); @@ -133,7 +138,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch } } - @Test public void testBadNestingObject() throws IOException { + @Test + public void testBadNestingObject() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginArray(); @@ -145,7 +151,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch } } - @Test public void testBadNestingArray() throws IOException { + @Test + public void testBadNestingArray() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginArray(); @@ -157,7 +164,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch } } - @Test public void testNullName() throws IOException { + @Test + public void testNullName() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginObject(); @@ -168,7 +176,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch } } - @Test public void testNullStringValue() throws IOException { + @Test + public void testNullStringValue() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginObject(); @@ -178,49 +187,32 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch assertEquals("{\"a\":null}", stringWriter.toString()); } - @Test public void testNonFiniteDoubles() throws IOException { + @Test + public void testNonFiniteDoubles() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginArray(); - try { - jsonWriter.value(Double.NaN); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - jsonWriter.value(Double.NEGATIVE_INFINITY); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - jsonWriter.value(Double.POSITIVE_INFINITY); - fail(); - } catch (IllegalArgumentException expected) { - } + jsonWriter.value(Double.NaN); + jsonWriter.value(Double.NEGATIVE_INFINITY); + jsonWriter.value(Double.POSITIVE_INFINITY); + jsonWriter.endArray(); + assertEquals("[]", stringWriter.toString()); } - @Test public void testNonFiniteBoxedDoubles() throws IOException { + @Test + public void testNonFiniteBoxedDoubles() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginArray(); - try { - jsonWriter.value(Double.valueOf(Double.NaN)); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - jsonWriter.value(Double.valueOf(Double.NEGATIVE_INFINITY)); - fail(); - } catch (IllegalArgumentException expected) { - } - try { - jsonWriter.value(Double.valueOf(Double.POSITIVE_INFINITY)); - fail(); - } catch (IllegalArgumentException expected) { - } + jsonWriter.value(Double.valueOf(Double.NaN)); + jsonWriter.value(Double.valueOf(Double.NEGATIVE_INFINITY)); + jsonWriter.value(Double.valueOf(Double.POSITIVE_INFINITY)); + jsonWriter.endArray(); + assertEquals("[]", stringWriter.toString()); } - @Test public void testNonFiniteBoxedDoublesWhenLenient() throws IOException { + @Test + public void testNonFiniteBoxedDoublesWhenLenient() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.setLenient(true); @@ -232,7 +224,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch assertEquals("[NaN,-Infinity,Infinity]", stringWriter.toString()); } - @Test public void testDoubles() throws IOException { + @Test + public void testDoubles() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginArray(); @@ -248,17 +241,18 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch jsonWriter.endArray(); jsonWriter.close(); assertEquals("[-0.0," - + "1.0," - + "1.7976931348623157E308," - + "4.9E-324," - + "0.0," - + "-0.5," - + "2.2250738585072014E-308," - + "3.141592653589793," - + "2.718281828459045]", stringWriter.toString()); + + "1.0," + + "1.7976931348623157E308," + + "4.9E-324," + + "0.0," + + "-0.5," + + "2.2250738585072014E-308," + + "3.141592653589793," + + "2.718281828459045]", stringWriter.toString()); } - @Test public void testLongs() throws IOException { + @Test + public void testLongs() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginArray(); @@ -270,13 +264,14 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch jsonWriter.endArray(); jsonWriter.close(); assertEquals("[0," - + "1," - + "-1," - + "-9223372036854775808," - + "9223372036854775807]", stringWriter.toString()); + + "1," + + "-1," + + "-9223372036854775808," + + "9223372036854775807]", stringWriter.toString()); } - @Test public void testNumbers() throws IOException { + @Test + public void testNumbers() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginArray(); @@ -287,12 +282,13 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch jsonWriter.endArray(); jsonWriter.close(); assertEquals("[0," - + "9223372036854775808," - + "-9223372036854775809," - + "3.141592653589793238462643383]", stringWriter.toString()); + + "9223372036854775808," + + "-9223372036854775809," + + "3.141592653589793238462643383]", stringWriter.toString()); } - @Test public void testBooleans() throws IOException { + @Test + public void testBooleans() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginArray(); @@ -302,7 +298,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch assertEquals("[true,false]", stringWriter.toString()); } - @Test public void testBoxedBooleans() throws IOException { + @Test + public void testBoxedBooleans() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginArray(); @@ -313,7 +310,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch assertEquals("[true,false,null]", stringWriter.toString()); } - @Test public void testNulls() throws IOException { + @Test + public void testNulls() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginArray(); @@ -323,7 +321,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch } @SuppressWarnings("AvoidEscapedUnicodeCharacters") - @Test public void testStrings() throws IOException { + @Test + public void testStrings() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginArray(); @@ -347,26 +346,27 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch jsonWriter.value("\u0019"); jsonWriter.endArray(); assertEquals("[\"a\"," - + "\"a\\\"\"," - + "\"\\\"\"," - + "\":\"," - + "\",\"," - + "\"\\b\"," - + "\"\\f\"," - + "\"\\n\"," - + "\"\\r\"," - + "\"\\t\"," - + "\" \"," - + "\"\\\\\"," - + "\"{\"," - + "\"}\"," - + "\"[\"," - + "\"]\"," - + "\"\\u0000\"," - + "\"\\u0019\"]", stringWriter.toString()); - } - - @Test public void testUnicodeLineBreaksEscaped() throws IOException { + + "\"a\\\"\"," + + "\"\\\"\"," + + "\":\"," + + "\",\"," + + "\"\\b\"," + + "\"\\f\"," + + "\"\\n\"," + + "\"\\r\"," + + "\"\\t\"," + + "\" \"," + + "\"\\\\\"," + + "\"{\"," + + "\"}\"," + + "\"[\"," + + "\"]\"," + + "\"\\u0000\"," + + "\"\\u0019\"]", stringWriter.toString()); + } + + @Test + public void testUnicodeLineBreaksEscaped() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginArray(); @@ -375,7 +375,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch assertEquals("[\"\\u2028 \\u2029\"]", stringWriter.toString()); } - @Test public void testEmptyArray() throws IOException { + @Test + public void testEmptyArray() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginArray(); @@ -383,7 +384,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch assertEquals("[]", stringWriter.toString()); } - @Test public void testEmptyObject() throws IOException { + @Test + public void testEmptyObject() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginObject(); @@ -391,7 +393,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch assertEquals("{}", stringWriter.toString()); } - @Test public void testObjectsInArrays() throws IOException { + @Test + public void testObjectsInArrays() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginArray(); @@ -405,10 +408,11 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch jsonWriter.endObject(); jsonWriter.endArray(); assertEquals("[{\"a\":5,\"b\":false}," - + "{\"c\":6,\"d\":true}]", stringWriter.toString()); + + "{\"c\":6,\"d\":true}]", stringWriter.toString()); } - @Test public void testArraysInObjects() throws IOException { + @Test + public void testArraysInObjects() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginObject(); @@ -424,10 +428,11 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch jsonWriter.endArray(); jsonWriter.endObject(); assertEquals("{\"a\":[5,false]," - + "\"b\":[6,true]}", stringWriter.toString()); + + "\"b\":[6,true]}", stringWriter.toString()); } - @Test public void testDeepNestingArrays() throws IOException { + @Test + public void testDeepNestingArrays() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); for (int i = 0; i < 20; i++) { @@ -439,7 +444,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch assertEquals("[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]", stringWriter.toString()); } - @Test public void testDeepNestingObjects() throws IOException { + @Test + public void testDeepNestingObjects() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginObject(); @@ -452,11 +458,12 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch } jsonWriter.endObject(); assertEquals("{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":" - + "{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{" - + "}}}}}}}}}}}}}}}}}}}}}", stringWriter.toString()); + + "{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{" + + "}}}}}}}}}}}}}}}}}}}}}", stringWriter.toString()); } - @Test public void testRepeatedName() throws IOException { + @Test + public void testRepeatedName() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.beginObject(); @@ -467,7 +474,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch assertEquals("{\"a\":true,\"a\":false}", stringWriter.toString()); } - @Test public void testPrettyPrintObject() throws IOException { + @Test + public void testPrettyPrintObject() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.setIndent(" "); @@ -488,23 +496,24 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch jsonWriter.endObject(); String expected = "{\n" - + " \"a\": true,\n" - + " \"b\": false,\n" - + " \"c\": 5.0,\n" - + " \"e\": null,\n" - + " \"f\": [\n" - + " 6.0,\n" - + " 7.0\n" - + " ],\n" - + " \"g\": {\n" - + " \"h\": 8.0,\n" - + " \"i\": 9.0\n" - + " }\n" - + "}"; + + " \"a\": true,\n" + + " \"b\": false,\n" + + " \"c\": 5.0,\n" + + " \"e\": null,\n" + + " \"f\": [\n" + + " 6.0,\n" + + " 7.0\n" + + " ],\n" + + " \"g\": {\n" + + " \"h\": 8.0,\n" + + " \"i\": 9.0\n" + + " }\n" + + "}"; assertEquals(expected, stringWriter.toString()); } - @Test public void testPrettyPrintArray() throws IOException { + @Test + public void testPrettyPrintArray() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter jsonWriter = new JsonWriter(stringWriter); jsonWriter.setIndent(" "); @@ -525,23 +534,24 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch jsonWriter.endArray(); String expected = "[\n" - + " true,\n" - + " false,\n" - + " 5.0,\n" - + " null,\n" - + " {\n" - + " \"a\": 6.0,\n" - + " \"b\": 7.0\n" - + " },\n" - + " [\n" - + " 8.0,\n" - + " 9.0\n" - + " ]\n" - + "]"; + + " true,\n" + + " false,\n" + + " 5.0,\n" + + " null,\n" + + " {\n" + + " \"a\": 6.0,\n" + + " \"b\": 7.0\n" + + " },\n" + + " [\n" + + " 8.0,\n" + + " 9.0\n" + + " ]\n" + + "]"; assertEquals(expected, stringWriter.toString()); } - @Test public void testLenientWriterPermitsMultipleTopLevelValues() throws IOException { + @Test + public void testLenientWriterPermitsMultipleTopLevelValues() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter writer = new JsonWriter(stringWriter); writer.setLenient(true); @@ -553,7 +563,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch assertEquals("[][]", stringWriter.toString()); } - @Test public void testStrictWriterDoesNotPermitMultipleTopLevelValues() throws IOException { + @Test + public void testStrictWriterDoesNotPermitMultipleTopLevelValues() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter writer = new JsonWriter(stringWriter); writer.beginArray(); @@ -565,7 +576,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch } } - @Test public void testClosedWriterThrowsOnStructure() throws IOException { + @Test + public void testClosedWriterThrowsOnStructure() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter writer = new JsonWriter(stringWriter); writer.beginArray(); @@ -593,7 +605,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch } } - @Test public void testClosedWriterThrowsOnName() throws IOException { + @Test + public void testClosedWriterThrowsOnName() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter writer = new JsonWriter(stringWriter); writer.beginArray(); @@ -606,7 +619,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch } } - @Test public void testClosedWriterThrowsOnValue() throws IOException { + @Test + public void testClosedWriterThrowsOnValue() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter writer = new JsonWriter(stringWriter); writer.beginArray(); @@ -619,7 +633,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch } } - @Test public void testClosedWriterThrowsOnFlush() throws IOException { + @Test + public void testClosedWriterThrowsOnFlush() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter writer = new JsonWriter(stringWriter); writer.beginArray(); @@ -632,7 +647,8 @@ public void testTopLevelValueTypes() throws IOException { // method monkey-patch } } - @Test public void testWriterCloseIsIdempotent() throws IOException { + @Test + public void testWriterCloseIsIdempotent() throws IOException { StringWriter stringWriter = new StringWriter(); JsonWriter writer = new JsonWriter(stringWriter); writer.beginArray(); From dcb3b12f051e5811cd673f6db1309ad90630bea8 Mon Sep 17 00:00:00 2001 From: jason Date: Fri, 26 Jan 2024 09:13:37 +0000 Subject: [PATCH 06/14] fix(json): improve robustness of BugsnagEventMapper allowing `metaData`, `breadcrumbs`, and `featureFlags` to be missing from the marshalled JSON without causing a deserialization failure --- .../java/com/bugsnag/android/BugsnagEventMapper.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/BugsnagEventMapper.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/BugsnagEventMapper.kt index c8a7763932..c3af9864b5 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/BugsnagEventMapper.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/BugsnagEventMapper.kt @@ -29,12 +29,14 @@ internal class BugsnagEventMapper( event.userImpl = convertUser(map.readEntry("user")) // populate metadata - val metadataMap: Map> = map.readEntry("metaData") + val metadataMap: Map> = + (map["metaData"] as? Map>).orEmpty() metadataMap.forEach { (key, value) -> event.addMetadata(key, value) } - val featureFlagsList: List> = map.readEntry("featureFlags") + val featureFlagsList: List> = + (map["featureFlags"] as? List>).orEmpty() featureFlagsList.forEach { featureFlagMap -> event.addFeatureFlag( featureFlagMap.readEntry("featureFlag"), @@ -43,7 +45,8 @@ internal class BugsnagEventMapper( } // populate breadcrumbs - val breadcrumbList: List> = map.readEntry("breadcrumbs") + val breadcrumbList: List> = + (map["breadcrumbs"] as? List>).orEmpty() breadcrumbList.mapTo(event.breadcrumbs) { Breadcrumb( convertBreadcrumbInternal(it), @@ -226,8 +229,7 @@ internal class BugsnagEventMapper( is T -> return value null -> throw IllegalStateException("cannot find json property '$key'") else -> throw IllegalArgumentException( - "json property '$key' not " + - "of expected type, found ${value.javaClass.name}" + "json property '$key' not of expected type, found ${value.javaClass.name}" ) } } From e83ab5997ba23347b5bff1a084886fad4b7ff7f8 Mon Sep 17 00:00:00 2001 From: SmartbearYing Date: Fri, 26 Jan 2024 10:31:05 +0000 Subject: [PATCH 07/14] feat(metadata):included exit reason and process importance in APP --- .../detekt-baseline.xml | 1 + .../com/bugsnag/android/ExitInfoCallback.kt | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/bugsnag-plugin-android-exitinfo/detekt-baseline.xml b/bugsnag-plugin-android-exitinfo/detekt-baseline.xml index a022fb6b72..29c898e414 100644 --- a/bugsnag-plugin-android-exitinfo/detekt-baseline.xml +++ b/bugsnag-plugin-android-exitinfo/detekt-baseline.xml @@ -2,6 +2,7 @@ + CyclomaticComplexMethod:ExitInfoCallback.kt$ExitInfoCallback$override fun onSend(event: Event): Boolean LongParameterList:TombstoneParser.kt$TombstoneParser$( exitInfo: ApplicationExitInfo, listOpenFds: Boolean, includeLogcat: Boolean, threadConsumer: (BugsnagThread) -> Unit, fileDescriptorConsumer: (Int, String, String) -> Unit, logcatConsumer: (String) -> Unit ) MagicNumber:TraceParser.kt$TraceParser$16 MagicNumber:TraceParser.kt$TraceParser$3 diff --git a/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt b/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt index 814cb6eb3c..82bba76f2f 100644 --- a/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt +++ b/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt @@ -22,6 +22,42 @@ internal class ExitInfoCallback( ?: findExitInfoByPid(allExitInfo) ?: return true try { + val reason = when (exitInfo.reason) { + ApplicationExitInfo.REASON_UNKNOWN -> "${exitInfo.reason}" + ApplicationExitInfo.REASON_EXIT_SELF -> "exit self" + ApplicationExitInfo.REASON_SIGNALED -> "signaled" + ApplicationExitInfo.REASON_LOW_MEMORY -> "low memory" + ApplicationExitInfo.REASON_CRASH -> "crash" + ApplicationExitInfo.REASON_CRASH_NATIVE -> "crash native" + ApplicationExitInfo.REASON_ANR -> "ANR" + ApplicationExitInfo.REASON_INITIALIZATION_FAILURE -> "initialization failure" + ApplicationExitInfo.REASON_PERMISSION_CHANGE -> "permission change" + ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE -> "excessive resource usage" + ApplicationExitInfo.REASON_USER_REQUESTED -> "user requested" + ApplicationExitInfo.REASON_USER_STOPPED -> "user stopped" + ApplicationExitInfo.REASON_DEPENDENCY_DIED -> "dependency died" + ApplicationExitInfo.REASON_OTHER -> "other" + ApplicationExitInfo.REASON_FREEZER -> "freezer" + ApplicationExitInfo.REASON_PACKAGE_STATE_CHANGE -> "package state change" + ApplicationExitInfo.REASON_PACKAGE_UPDATED -> "package updated" + else -> "${exitInfo.reason}" + } + event.addMetadata("App", "exit reason", reason) + + val importance = when (exitInfo.importance) { + ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND -> "foreground" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE -> "foreground service" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING -> "top sleeping" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE -> "visible" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE -> "perceptible" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE -> "can not save state" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE -> "service" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED -> "cached" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE -> "gone" + else -> "${exitInfo.importance}" + } + event.addMetadata("App", "process importance", importance) + if (exitInfo.reason == ApplicationExitInfo.REASON_CRASH_NATIVE || exitInfo.reason == ApplicationExitInfo.REASON_SIGNALED ) { From c0bc28121dfe4d87b31ba9bc00e80e2d3801549d Mon Sep 17 00:00:00 2001 From: SmartbearYing Date: Fri, 26 Jan 2024 10:31:05 +0000 Subject: [PATCH 08/14] feat(metadata):included exit reason and process importance in APP --- .../detekt-baseline.xml | 1 + .../com/bugsnag/android/ExitInfoCallback.kt | 36 +++++++++++++++++++ .../bugsnag/android/ExitInfoCallbackTest.kt | 12 +++++++ 3 files changed, 49 insertions(+) diff --git a/bugsnag-plugin-android-exitinfo/detekt-baseline.xml b/bugsnag-plugin-android-exitinfo/detekt-baseline.xml index a022fb6b72..29c898e414 100644 --- a/bugsnag-plugin-android-exitinfo/detekt-baseline.xml +++ b/bugsnag-plugin-android-exitinfo/detekt-baseline.xml @@ -2,6 +2,7 @@ + CyclomaticComplexMethod:ExitInfoCallback.kt$ExitInfoCallback$override fun onSend(event: Event): Boolean LongParameterList:TombstoneParser.kt$TombstoneParser$( exitInfo: ApplicationExitInfo, listOpenFds: Boolean, includeLogcat: Boolean, threadConsumer: (BugsnagThread) -> Unit, fileDescriptorConsumer: (Int, String, String) -> Unit, logcatConsumer: (String) -> Unit ) MagicNumber:TraceParser.kt$TraceParser$16 MagicNumber:TraceParser.kt$TraceParser$3 diff --git a/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt b/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt index 814cb6eb3c..d987203bab 100644 --- a/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt +++ b/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt @@ -22,6 +22,42 @@ internal class ExitInfoCallback( ?: findExitInfoByPid(allExitInfo) ?: return true try { + val reason = when (exitInfo.reason) { + ApplicationExitInfo.REASON_UNKNOWN -> "unknown reason (${exitInfo.reason})" + ApplicationExitInfo.REASON_EXIT_SELF -> "exit self" + ApplicationExitInfo.REASON_SIGNALED -> "signaled" + ApplicationExitInfo.REASON_LOW_MEMORY -> "low memory" + ApplicationExitInfo.REASON_CRASH -> "crash" + ApplicationExitInfo.REASON_CRASH_NATIVE -> "crash native" + ApplicationExitInfo.REASON_ANR -> "ANR" + ApplicationExitInfo.REASON_INITIALIZATION_FAILURE -> "initialization failure" + ApplicationExitInfo.REASON_PERMISSION_CHANGE -> "permission change" + ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE -> "excessive resource usage" + ApplicationExitInfo.REASON_USER_REQUESTED -> "user requested" + ApplicationExitInfo.REASON_USER_STOPPED -> "user stopped" + ApplicationExitInfo.REASON_DEPENDENCY_DIED -> "dependency died" + ApplicationExitInfo.REASON_OTHER -> "other" + ApplicationExitInfo.REASON_FREEZER -> "freezer" + ApplicationExitInfo.REASON_PACKAGE_STATE_CHANGE -> "package state change" + ApplicationExitInfo.REASON_PACKAGE_UPDATED -> "package updated" + else -> "unknown reason (${exitInfo.reason})" + } + event.addMetadata("App", "exit reason", reason) + + val importance = when (exitInfo.importance) { + ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND -> "foreground" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE -> "foreground service" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING -> "top sleeping" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE -> "visible" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE -> "perceptible" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE -> "can not save state" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE -> "service" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED -> "cached" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE -> "gone" + else -> "unknown importance ${exitInfo.importance}" + } + event.addMetadata("App", "process importance", importance) + if (exitInfo.reason == ApplicationExitInfo.REASON_CRASH_NATIVE || exitInfo.reason == ApplicationExitInfo.REASON_SIGNALED ) { diff --git a/bugsnag-plugin-android-exitinfo/src/test/java/com/bugsnag/android/ExitInfoCallbackTest.kt b/bugsnag-plugin-android-exitinfo/src/test/java/com/bugsnag/android/ExitInfoCallbackTest.kt index c0cb1a243a..98f11d9264 100644 --- a/bugsnag-plugin-android-exitinfo/src/test/java/com/bugsnag/android/ExitInfoCallbackTest.kt +++ b/bugsnag-plugin-android-exitinfo/src/test/java/com/bugsnag/android/ExitInfoCallbackTest.kt @@ -3,6 +3,7 @@ package com.bugsnag.android import android.app.ActivityManager import android.app.ApplicationExitInfo import android.content.Context +import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test @@ -108,4 +109,15 @@ internal class ExitInfoCallbackTest { verify(nativeEnhancer, times(0)).invoke(event, exitInfo1) verify(anrEventEnhancer, times(0)).invoke(event, exitInfo1) } + + @Test + fun testUnknownExitReasonAndImportance() { + `when`(exitInfos.first().processStateSummary).thenReturn("1".toByteArray()) + `when`(event.session?.id).thenReturn("1") + `when`(exitInfo1.reason).thenReturn(ApplicationExitInfo.REASON_UNKNOWN) + `when`(exitInfo1.importance).thenReturn(ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN) + assertTrue(exitInfoCallback.onSend(event)) + assertEquals("0", exitInfo1.reason.toString()) + assertEquals("0", exitInfo1.importance.toString()) + } } From 4ea7035111f61ba8101e28afa561f8171dd8f456 Mon Sep 17 00:00:00 2001 From: SmartbearYing Date: Tue, 30 Jan 2024 08:52:48 +0000 Subject: [PATCH 09/14] feat(metadata):tests --- .../detekt-baseline.xml | 2 +- .../com/bugsnag/android/ExitInfoCallback.kt | 76 ++++++++++--------- .../mazerunner/bugsnag-dependency.gradle | 1 + .../mazerunner/scenarios/ExitInfoScenario.kt | 26 +++++++ .../android/mazerunner/BugsnagConfig.kt | 2 + features/full_tests/exit_information.feature | 15 ++++ 6 files changed, 87 insertions(+), 35 deletions(-) create mode 100644 features/fixtures/mazerunner/cxx-scenarios-bugsnag/src/main/java/com/bugsnag/android/mazerunner/scenarios/ExitInfoScenario.kt create mode 100644 features/full_tests/exit_information.feature diff --git a/bugsnag-plugin-android-exitinfo/detekt-baseline.xml b/bugsnag-plugin-android-exitinfo/detekt-baseline.xml index 29c898e414..4597fca69f 100644 --- a/bugsnag-plugin-android-exitinfo/detekt-baseline.xml +++ b/bugsnag-plugin-android-exitinfo/detekt-baseline.xml @@ -2,7 +2,7 @@ - CyclomaticComplexMethod:ExitInfoCallback.kt$ExitInfoCallback$override fun onSend(event: Event): Boolean + CyclomaticComplexMethod:ExitInfoCallback.kt$ExitInfoCallback$private fun exitReasonOf(exitInfo: ApplicationExitInfo) LongParameterList:TombstoneParser.kt$TombstoneParser$( exitInfo: ApplicationExitInfo, listOpenFds: Boolean, includeLogcat: Boolean, threadConsumer: (BugsnagThread) -> Unit, fileDescriptorConsumer: (Int, String, String) -> Unit, logcatConsumer: (String) -> Unit ) MagicNumber:TraceParser.kt$TraceParser$16 MagicNumber:TraceParser.kt$TraceParser$3 diff --git a/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt b/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt index d987203bab..807ffd6bfe 100644 --- a/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt +++ b/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt @@ -4,6 +4,7 @@ import android.app.ActivityManager import android.app.ApplicationExitInfo import android.content.Context import android.os.Build +import android.util.Log import androidx.annotation.RequiresApi @RequiresApi(Build.VERSION_CODES.R) @@ -17,46 +18,19 @@ internal class ExitInfoCallback( override fun onSend(event: Event): Boolean { val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager val allExitInfo = am.getHistoricalProcessExitReasons(context.packageName, 0, MAX_EXIT_INFO) + Log.e("Bugsnag", "allExitInfo: $allExitInfo") val sessionIdBytes = event.session?.id?.toByteArray() ?: return true + Log.e("Bugsnag", "sessionIdBytes: $sessionIdBytes") val exitInfo = findExitInfoBySessionId(allExitInfo, sessionIdBytes) ?: findExitInfoByPid(allExitInfo) ?: return true + Log.e("Bugsnag", "exitInfo: $exitInfo") try { - val reason = when (exitInfo.reason) { - ApplicationExitInfo.REASON_UNKNOWN -> "unknown reason (${exitInfo.reason})" - ApplicationExitInfo.REASON_EXIT_SELF -> "exit self" - ApplicationExitInfo.REASON_SIGNALED -> "signaled" - ApplicationExitInfo.REASON_LOW_MEMORY -> "low memory" - ApplicationExitInfo.REASON_CRASH -> "crash" - ApplicationExitInfo.REASON_CRASH_NATIVE -> "crash native" - ApplicationExitInfo.REASON_ANR -> "ANR" - ApplicationExitInfo.REASON_INITIALIZATION_FAILURE -> "initialization failure" - ApplicationExitInfo.REASON_PERMISSION_CHANGE -> "permission change" - ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE -> "excessive resource usage" - ApplicationExitInfo.REASON_USER_REQUESTED -> "user requested" - ApplicationExitInfo.REASON_USER_STOPPED -> "user stopped" - ApplicationExitInfo.REASON_DEPENDENCY_DIED -> "dependency died" - ApplicationExitInfo.REASON_OTHER -> "other" - ApplicationExitInfo.REASON_FREEZER -> "freezer" - ApplicationExitInfo.REASON_PACKAGE_STATE_CHANGE -> "package state change" - ApplicationExitInfo.REASON_PACKAGE_UPDATED -> "package updated" - else -> "unknown reason (${exitInfo.reason})" - } - event.addMetadata("App", "exit reason", reason) + val reason = exitReasonOf(exitInfo) + event.addMetadata("app", "exitReason", reason) - val importance = when (exitInfo.importance) { - ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND -> "foreground" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE -> "foreground service" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING -> "top sleeping" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE -> "visible" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE -> "perceptible" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE -> "can not save state" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE -> "service" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED -> "cached" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE -> "gone" - else -> "unknown importance ${exitInfo.importance}" - } - event.addMetadata("App", "process importance", importance) + val importance = importanceDescriptionOf(exitInfo) + event.addMetadata("app", "processImportance", importance) if (exitInfo.reason == ApplicationExitInfo.REASON_CRASH_NATIVE || exitInfo.reason == ApplicationExitInfo.REASON_SIGNALED @@ -71,6 +45,40 @@ internal class ExitInfoCallback( return true } + private fun exitReasonOf(exitInfo: ApplicationExitInfo) = when (exitInfo.reason) { + ApplicationExitInfo.REASON_UNKNOWN -> "unknown reason (${exitInfo.reason})" + ApplicationExitInfo.REASON_EXIT_SELF -> "exit self" + ApplicationExitInfo.REASON_SIGNALED -> "signaled" + ApplicationExitInfo.REASON_LOW_MEMORY -> "low memory" + ApplicationExitInfo.REASON_CRASH -> "crash" + ApplicationExitInfo.REASON_CRASH_NATIVE -> "crash native" + ApplicationExitInfo.REASON_ANR -> "ANR" + ApplicationExitInfo.REASON_INITIALIZATION_FAILURE -> "initialization failure" + ApplicationExitInfo.REASON_PERMISSION_CHANGE -> "permission change" + ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE -> "excessive resource usage" + ApplicationExitInfo.REASON_USER_REQUESTED -> "user requested" + ApplicationExitInfo.REASON_USER_STOPPED -> "user stopped" + ApplicationExitInfo.REASON_DEPENDENCY_DIED -> "dependency died" + ApplicationExitInfo.REASON_OTHER -> "other" + ApplicationExitInfo.REASON_FREEZER -> "freezer" + ApplicationExitInfo.REASON_PACKAGE_STATE_CHANGE -> "package state change" + ApplicationExitInfo.REASON_PACKAGE_UPDATED -> "package updated" + else -> "unknown reason (${exitInfo.reason})" + } + + private fun importanceDescriptionOf(exitInfo: ApplicationExitInfo) = when (exitInfo.importance) { + ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND -> "foreground" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE -> "foreground service" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING -> "top sleeping" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE -> "visible" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE -> "perceptible" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE -> "can not save state" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE -> "service" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED -> "cached" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE -> "gone" + else -> "unknown importance ${exitInfo.importance}" + } + private fun findExitInfoBySessionId( allExitInfo: List, sessionIdBytes: ByteArray diff --git a/features/fixtures/mazerunner/bugsnag-dependency.gradle b/features/fixtures/mazerunner/bugsnag-dependency.gradle index 081d0fc48e..99680b4ddf 100644 --- a/features/fixtures/mazerunner/bugsnag-dependency.gradle +++ b/features/fixtures/mazerunner/bugsnag-dependency.gradle @@ -16,5 +16,6 @@ dependencies { project.logger.lifecycle("Compiling full mazerunner fixture with ANR/NDK scenarios") implementation "com.bugsnag:bugsnag-android:9.9.9" implementation "com.bugsnag:bugsnag-plugin-android-okhttp:9.9.9" + implementation "com.bugsnag:bugsnag-plugin-android-exitinfo:9.9.9" } } diff --git a/features/fixtures/mazerunner/cxx-scenarios-bugsnag/src/main/java/com/bugsnag/android/mazerunner/scenarios/ExitInfoScenario.kt b/features/fixtures/mazerunner/cxx-scenarios-bugsnag/src/main/java/com/bugsnag/android/mazerunner/scenarios/ExitInfoScenario.kt new file mode 100644 index 0000000000..61126dae56 --- /dev/null +++ b/features/fixtures/mazerunner/cxx-scenarios-bugsnag/src/main/java/com/bugsnag/android/mazerunner/scenarios/ExitInfoScenario.kt @@ -0,0 +1,26 @@ +package com.bugsnag.android.mazerunner.scenarios + +import android.content.Context +import android.os.Handler +import android.os.Looper +import android.util.Log +import com.bugsnag.android.Bugsnag +import com.bugsnag.android.Configuration + +class ExitInfoScenario( + config: com.bugsnag.android.Configuration, + context: android.content.Context, + eventMetadata: String? +) : Scenario(config, context, eventMetadata) { + external fun crash(value: Int): Int + override fun startScenario() { + super.startScenario() + Bugsnag.startSession() + val main: android.os.Handler = android.os.Handler(android.os.Looper.getMainLooper()) + main.postDelayed(object : java.lang.Runnable { + override fun run() { + crash(2726) + } + }, 500) + } +} \ No newline at end of file diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/BugsnagConfig.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/BugsnagConfig.kt index 63048e09cd..116d236d9c 100644 --- a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/BugsnagConfig.kt +++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/BugsnagConfig.kt @@ -1,6 +1,7 @@ package com.bugsnag.android.mazerunner import android.util.Log +import com.bugsnag.android.BugsnagExitInfoPlugin import com.bugsnag.android.Configuration import com.bugsnag.android.Delivery import com.bugsnag.android.DeliveryParams @@ -19,6 +20,7 @@ fun prepareConfig( logFilter: (msg: String) -> Boolean ): Configuration { val config = Configuration(apiKey) + config.addPlugin(BugsnagExitInfoPlugin()) if (notify.isNotEmpty() && sessions.isNotEmpty()) { config.endpoints = EndpointConfiguration(notify, sessions) diff --git a/features/full_tests/exit_information.feature b/features/full_tests/exit_information.feature new file mode 100644 index 0000000000..4eb4b5e5b4 --- /dev/null +++ b/features/full_tests/exit_information.feature @@ -0,0 +1,15 @@ +Feature: Application exitInfo is reported in crashes + + Background: + Given I clear all persistent data + + @skip_below_android_11 + Scenario: Application exitInfo is reported in crashes + When I set the screen orientation to portrait + And I run "ExitInfoScenario" and relaunch the crashed app + And I configure Bugsnag for "ExitInfoScenario" + And I wait to receive an error + Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier + And the error payload field "events.0.metaData.app.processImportance" equals "foreground" + And the error payload field "events.0.metaData.app.exitReason" equals "crash" + From 4fa06110f3745338895e09a6ae3c27968516b70b Mon Sep 17 00:00:00 2001 From: SmartbearYing Date: Wed, 31 Jan 2024 09:20:01 +0000 Subject: [PATCH 10/14] feat(metadata):tests --- CHANGELOG.md | 5 +++++ .../src/main/java/com/bugsnag/android/ExitInfoCallback.kt | 2 +- .../test/java/com/bugsnag/android/ExitInfoCallbackTest.kt | 5 ++--- features/full_tests/usage.feature | 6 +++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b358545a9..b00ae58686 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## TBD +### Enhancements + +* `bugsnag-plugin-android-exitinfo` now includes `exitReason` and `processImportance` in the `APP` tab on the dashboard + [#1968](https://github.com/bugsnag/bugsnag-android/pull/1968) + ### Bug fixes * Avoid any possibility of multiple conflicting native crash handlers or stack-unwinders running concurrently diff --git a/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt b/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt index 42c187b8ac..21053f6a41 100644 --- a/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt +++ b/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt @@ -68,7 +68,7 @@ internal class ExitInfoCallback( ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING -> "top sleeping" ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE -> "visible" ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE -> "perceptible" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE -> "can not save state" + ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE -> "can't save state" ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE -> "service" ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED -> "cached" ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE -> "gone" diff --git a/bugsnag-plugin-android-exitinfo/src/test/java/com/bugsnag/android/ExitInfoCallbackTest.kt b/bugsnag-plugin-android-exitinfo/src/test/java/com/bugsnag/android/ExitInfoCallbackTest.kt index 98f11d9264..f6ffe3ef34 100644 --- a/bugsnag-plugin-android-exitinfo/src/test/java/com/bugsnag/android/ExitInfoCallbackTest.kt +++ b/bugsnag-plugin-android-exitinfo/src/test/java/com/bugsnag/android/ExitInfoCallbackTest.kt @@ -3,7 +3,6 @@ package com.bugsnag.android import android.app.ActivityManager import android.app.ApplicationExitInfo import android.content.Context -import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test @@ -117,7 +116,7 @@ internal class ExitInfoCallbackTest { `when`(exitInfo1.reason).thenReturn(ApplicationExitInfo.REASON_UNKNOWN) `when`(exitInfo1.importance).thenReturn(ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN) assertTrue(exitInfoCallback.onSend(event)) - assertEquals("0", exitInfo1.reason.toString()) - assertEquals("0", exitInfo1.importance.toString()) + verify(event, times(1)).addMetadata("app", "exitReason", "unknown reason (0)") + verify(event, times(1)).addMetadata("app", "processImportance", "unknown importance (0)") } } diff --git a/features/full_tests/usage.feature b/features/full_tests/usage.feature index 4354427ab1..58cf7622b8 100644 --- a/features/full_tests/usage.feature +++ b/features/full_tests/usage.feature @@ -53,7 +53,7 @@ Feature: Reporting Errors with usage info And the event "usage.callbacks.ndkOnError" equals 1 And the event "usage.callbacks.onBreadcrumb" equals 3 And the event "usage.callbacks.onError" equals 2 - And the event "usage.callbacks.onSession" equals 2 + And the event "usage.callbacks.onSession" equals 3 Scenario: Report an unhandled exception with custom configuration and set callbacks, usage disabled When I configure the app to run in the "disable-usage" state @@ -87,7 +87,7 @@ Feature: Reporting Errors with usage info And the event "usage.config.maxPersistedSessions" equals 1000 And the event "usage.callbacks.onBreadcrumb" equals 1 And the event "usage.callbacks.onError" equals 2 - And the event "usage.callbacks.onSession" equals 4 + And the event "usage.callbacks.onSession" equals 5 Scenario: Report a native exception with custom configuration and set callbacks, usage disabled When I configure the app to run in the "disable-usage" state @@ -121,7 +121,7 @@ Feature: Reporting Errors with usage info And the event "usage.callbacks.ndkOnError" equals 1 And the event "usage.callbacks.onBreadcrumb" equals 1 And the event "usage.callbacks.onError" equals 2 - And the event "usage.callbacks.onSession" equals 4 + And the event "usage.callbacks.onSession" equals 5 And the event "usage.callbacks.event_set_user" is true And the event "usage.callbacks.app_set_binary_arch" is true And the event "usage.callbacks.device_get_model" is true From 53f76a00cc77e76bea0f39e9891810e97bbd5abc Mon Sep 17 00:00:00 2001 From: SmartbearYing Date: Wed, 31 Jan 2024 14:23:25 +0000 Subject: [PATCH 11/14] feat(mazerunner):flush --- .../main/java/com/bugsnag/android/BugsnagInternals.java | 9 +++++++++ .../java/com/bugsnag/android/mazerunner/MainActivity.kt | 2 ++ 2 files changed, 11 insertions(+) create mode 100644 features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/BugsnagInternals.java diff --git a/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/BugsnagInternals.java b/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/BugsnagInternals.java new file mode 100644 index 0000000000..8a01fe02bc --- /dev/null +++ b/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/BugsnagInternals.java @@ -0,0 +1,9 @@ +package com.bugsnag.android; + +public class BugsnagInternals { + private BugsnagInternals() {} + + public static void flush() { + Bugsnag.client.eventStore.flushAsync(); + } +} diff --git a/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MainActivity.kt b/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MainActivity.kt index e7d631ac25..8ae8c3a391 100644 --- a/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MainActivity.kt +++ b/features/fixtures/mazerunner/app/src/main/java/com/bugsnag/android/mazerunner/MainActivity.kt @@ -11,6 +11,7 @@ import android.os.Looper import android.view.Window import android.widget.Button import android.widget.EditText +import com.bugsnag.android.BugsnagInternals import com.bugsnag.android.mazerunner.scenarios.Scenario import org.json.JSONObject import java.io.File @@ -177,6 +178,7 @@ class MainActivity : Activity() { runScenario(scenarioName, scenarioMode, sessionsUrl, notifyUrl) } "clear_persistent_data" -> clearPersistentData() + "flush" -> BugsnagInternals.flush() else -> throw IllegalArgumentException("Unknown action: $action") } } From 11213b1bfc0486eba9f9dd09bbe0d7b4610c55e4 Mon Sep 17 00:00:00 2001 From: YingYing Chen <129767130+SmartbearYing@users.noreply.github.com> Date: Tue, 6 Feb 2024 09:30:41 +0000 Subject: [PATCH 12/14] =?UTF-8?q?Added=C2=A0BugsnagExitInfoPlugin=20=20in?= =?UTF-8?q?=20to=20minimal=20test=20fixture=20=20(#1972)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(metadata):tests * feat(metadata):tests --- .../fixtures/mazerunner/bugsnag-dependency.gradle | 1 + .../com/bugsnag/android/mazerunner/BugsnagConfig.kt | 5 ++++- features/full_tests/exit_information.feature | 5 ++--- features/full_tests/usage.feature | 12 ++++++++---- features/support/env.rb | 8 ++++++++ 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/features/fixtures/mazerunner/bugsnag-dependency.gradle b/features/fixtures/mazerunner/bugsnag-dependency.gradle index 99680b4ddf..0a29b93459 100644 --- a/features/fixtures/mazerunner/bugsnag-dependency.gradle +++ b/features/fixtures/mazerunner/bugsnag-dependency.gradle @@ -11,6 +11,7 @@ dependencies { exclude group: "com.bugsnag", module: "bugsnag-plugin-android-ndk" } implementation "com.bugsnag:bugsnag-plugin-android-okhttp:9.9.9" + implementation "com.bugsnag:bugsnag-plugin-android-exitinfo:9.9.9" } else { // compile with the NDK scenarios by default project.logger.lifecycle("Compiling full mazerunner fixture with ANR/NDK scenarios") diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/BugsnagConfig.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/BugsnagConfig.kt index 116d236d9c..7a594a9f78 100644 --- a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/BugsnagConfig.kt +++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/BugsnagConfig.kt @@ -1,5 +1,6 @@ package com.bugsnag.android.mazerunner +import android.os.Build import android.util.Log import com.bugsnag.android.BugsnagExitInfoPlugin import com.bugsnag.android.Configuration @@ -20,7 +21,9 @@ fun prepareConfig( logFilter: (msg: String) -> Boolean ): Configuration { val config = Configuration(apiKey) - config.addPlugin(BugsnagExitInfoPlugin()) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + config.addPlugin(BugsnagExitInfoPlugin()) + } if (notify.isNotEmpty() && sessions.isNotEmpty()) { config.endpoints = EndpointConfiguration(notify, sessions) diff --git a/features/full_tests/exit_information.feature b/features/full_tests/exit_information.feature index 4eb4b5e5b4..de56e27355 100644 --- a/features/full_tests/exit_information.feature +++ b/features/full_tests/exit_information.feature @@ -10,6 +10,5 @@ Feature: Application exitInfo is reported in crashes And I configure Bugsnag for "ExitInfoScenario" And I wait to receive an error Then the error is valid for the error reporting API version "4.0" for the "Android Bugsnag Notifier" notifier - And the error payload field "events.0.metaData.app.processImportance" equals "foreground" - And the error payload field "events.0.metaData.app.exitReason" equals "crash" - + And the event "metaData.app.processImportance" equals "foreground" + And the event "metaData.app.exitReason" is not null diff --git a/features/full_tests/usage.feature b/features/full_tests/usage.feature index 58cf7622b8..bb6fb1c7b2 100644 --- a/features/full_tests/usage.feature +++ b/features/full_tests/usage.feature @@ -3,6 +3,7 @@ Feature: Reporting Errors with usage info Background: Given I clear all persistent data + @skip_above_android_11 Scenario: Report a handled exception with custom configuration and set callbacks When I configure the app to run in the "USAGE" state And I run "HandledExceptionWithUsageScenario" @@ -18,7 +19,7 @@ Feature: Reporting Errors with usage info And the event "usage.callbacks.ndkOnError" equals 1 And the event "usage.callbacks.onBreadcrumb" equals 1 And the event "usage.callbacks.onError" equals 3 - And the event "usage.callbacks.onSession" equals 4 + And the event "usage.callbacks.onSession" equals 3 Scenario: Report a handled exception with custom configuration and set callbacks, usage disabled When I configure the app to run in the "disable-usage" state @@ -37,6 +38,7 @@ Feature: Reporting Errors with usage info And the event "usage.callbacks.onError" is null And the event "usage.callbacks.onSession" is null + @skip_above_android_11 Scenario: Report an unhandled exception with custom configuration and set callbacks When I configure the app to run in the "USAGE" state And I run "UnhandledExceptionWithUsageScenario" and relaunch the crashed app @@ -53,7 +55,7 @@ Feature: Reporting Errors with usage info And the event "usage.callbacks.ndkOnError" equals 1 And the event "usage.callbacks.onBreadcrumb" equals 3 And the event "usage.callbacks.onError" equals 2 - And the event "usage.callbacks.onSession" equals 3 + And the event "usage.callbacks.onSession" equals 2 Scenario: Report an unhandled exception with custom configuration and set callbacks, usage disabled When I configure the app to run in the "disable-usage" state @@ -73,6 +75,7 @@ Feature: Reporting Errors with usage info And the event "usage.callbacks.onError" is null And the event "usage.callbacks.onSession" is null + @skip_above_android_11 Scenario: Report a native exception with custom configuration and set callbacks When I configure the app to run in the "USAGE" state And I run "CXXExceptionWithUsageScenario" and relaunch the crashed app @@ -87,7 +90,7 @@ Feature: Reporting Errors with usage info And the event "usage.config.maxPersistedSessions" equals 1000 And the event "usage.callbacks.onBreadcrumb" equals 1 And the event "usage.callbacks.onError" equals 2 - And the event "usage.callbacks.onSession" equals 5 + And the event "usage.callbacks.onSession" equals 4 Scenario: Report a native exception with custom configuration and set callbacks, usage disabled When I configure the app to run in the "disable-usage" state @@ -105,6 +108,7 @@ Feature: Reporting Errors with usage info And the event "usage.callbacks.onError" is null And the event "usage.callbacks.onSession" is null + @skip_above_android_11 Scenario: Report a native exception with custom configuration and set callbacks When I configure the app to run in the "USAGE" state And I run "CXXSigsegvWithUsageScenario" and relaunch the crashed app @@ -121,7 +125,7 @@ Feature: Reporting Errors with usage info And the event "usage.callbacks.ndkOnError" equals 1 And the event "usage.callbacks.onBreadcrumb" equals 1 And the event "usage.callbacks.onError" equals 2 - And the event "usage.callbacks.onSession" equals 5 + And the event "usage.callbacks.onSession" equals 4 And the event "usage.callbacks.event_set_user" is true And the event "usage.callbacks.app_set_binary_arch" is true And the event "usage.callbacks.device_get_model" is true diff --git a/features/support/env.rb b/features/support/env.rb index 58adf1e596..d2eff5d3a0 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -24,6 +24,10 @@ skip_this_scenario("Skipping scenario") end +Before('@skip_above_android_11') do |scenario| + skip_this_scenario("Skipping scenario") if Maze.config.os_version >= 11 +end + Before('@skip_above_android_8') do |scenario| skip_this_scenario("Skipping scenario") if Maze.config.os_version >= 9 end @@ -32,6 +36,10 @@ skip_this_scenario("Skipping scenario") if Maze.config.os_version >= 8 end +Before('@skip_below_android_11') do |scenario| + skip_this_scenario("Skipping scenario") if Maze.config.os_version < 11 +end + Before('@skip_below_android_9') do |scenario| skip_this_scenario("Skipping scenario") if Maze.config.os_version < 9 end From c833eb7be69be514630097a33e5dba7d183be7f8 Mon Sep 17 00:00:00 2001 From: YingYing Chen <129767130+SmartbearYing@users.noreply.github.com> Date: Thu, 8 Feb 2024 08:54:25 +0000 Subject: [PATCH 13/14] feat(metadata): process importance (#1973) --- bugsnag-android-core/detekt-baseline.xml | 1 + .../bugsnag/android/AppDataCollectorTest.kt | 81 +++++++++++++++++++ .../java/com/bugsnag/android/ClientTest.java | 2 +- .../com/bugsnag/android/AppDataCollector.kt | 64 +++++++++++++++ .../detekt-baseline.xml | 1 + .../com/bugsnag/android/ExitInfoCallback.kt | 44 +++++++--- 6 files changed, 182 insertions(+), 11 deletions(-) diff --git a/bugsnag-android-core/detekt-baseline.xml b/bugsnag-android-core/detekt-baseline.xml index bccf015f28..1435111433 100644 --- a/bugsnag-android-core/detekt-baseline.xml +++ b/bugsnag-android-core/detekt-baseline.xml @@ -2,6 +2,7 @@ + CyclomaticComplexMethod:AppDataCollector.kt$AppDataCollector$@SuppressLint("SwitchIntDef") @Suppress("DEPRECATION") private fun getProcessImportance(): String? CyclomaticComplexMethod:ConfigInternal.kt$ConfigInternal$fun getConfigDifferences(): Map<String, Any> ImplicitDefaultLocale:DeliveryHeaders.kt$String.format("%02x", byte) LongParameterList:App.kt$App$( /** * The architecture of the running application binary */ var binaryArch: String?, /** * The package name of the application */ var id: String?, /** * The release stage set in [Configuration.releaseStage] */ var releaseStage: String?, /** * The version of the application set in [Configuration.version] */ var version: String?, /** The revision ID from the manifest (React Native apps only) */ var codeBundleId: String?, /** * The unique identifier for the build of the application set in [Configuration.buildUuid] */ var buildUuid: String?, /** * The application type set in [Configuration#version] */ var type: String?, /** * The version code of the application set in [Configuration.versionCode] */ var versionCode: Number? ) diff --git a/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/AppDataCollectorTest.kt b/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/AppDataCollectorTest.kt index 5847d1fe4e..09da647a6a 100644 --- a/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/AppDataCollectorTest.kt +++ b/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/AppDataCollectorTest.kt @@ -4,6 +4,7 @@ import android.app.ActivityManager import android.content.Context import android.content.pm.PackageManager import android.os.Build +import android.os.Process import androidx.test.core.app.ApplicationProvider import org.junit.Assert.assertEquals import org.junit.Assert.assertNull @@ -126,4 +127,84 @@ class AppDataCollectorTest { val result = collector.getInstallerPackageName() assertEquals("Test Installer name", result) } + + @Test + fun testGetProcessImportanceWithVersion29() = withBuildSdkInt(Build.VERSION_CODES.Q) { + val packageManager = mock(PackageManager::class.java) + `when`(packageManager.getApplicationLabel(any())).thenReturn("Test App name") + `when`(am.runningAppProcesses).thenReturn( + listOf( + ActivityManager.RunningAppProcessInfo().apply { + importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND + } + ) + ) + + val collector = AppDataCollector( + context, + packageManager, + client.immutableConfig, + client.sessionTracker, + am, + client.launchCrashTracker, + client.memoryTrimState + ) + + val result = collector.getAppDataMetadata()["processImportance"] + assertEquals("foreground service", result) + } + + @Test + fun testGetProcessImportanceWithVersion14() = withBuildSdkInt(Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + val packageManager = mock(PackageManager::class.java) + `when`(packageManager.getApplicationLabel(any())).thenReturn("Test App name") + `when`(am.runningAppProcesses).thenReturn( + listOf( + ActivityManager.RunningAppProcessInfo().apply { + importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND + pid = Process.myPid() + } + ) + ) + + val collector = AppDataCollector( + context, + packageManager, + client.immutableConfig, + client.sessionTracker, + am, + client.launchCrashTracker, + client.memoryTrimState + ) + + val result = collector.getAppDataMetadata()["processImportance"] + assertEquals("foreground", result) + } + + @Test + fun testGetProcessImportanceWithPid0() = withBuildSdkInt(Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + val packageManager = mock(PackageManager::class.java) + `when`(packageManager.getApplicationLabel(any())).thenReturn("Test App name") + `when`(am.runningAppProcesses).thenReturn( + listOf( + ActivityManager.RunningAppProcessInfo().apply { + importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND + pid = 0 + } + ) + ) + + val collector = AppDataCollector( + context, + packageManager, + client.immutableConfig, + client.sessionTracker, + am, + client.launchCrashTracker, + client.memoryTrimState + ) + + val result = collector.getAppDataMetadata()["processImportance"] + assertEquals(null, result) + } } diff --git a/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/ClientTest.java b/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/ClientTest.java index 8f5b84dc20..96cccb0bd7 100644 --- a/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/ClientTest.java +++ b/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/ClientTest.java @@ -171,7 +171,7 @@ public void testAppDataCollection() { public void testAppDataMetadata() { client = generateClient(); Map app = client.getAppDataCollector().getAppDataMetadata(); - assertEquals(10, app.size()); + assertEquals(11, app.size()); assertEquals("Bugsnag Android Tests", app.get("name")); assertEquals("com.bugsnag.android.core.test", app.get("processName")); assertNotNull(app.get("memoryUsage")); diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/AppDataCollector.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/AppDataCollector.kt index ee23a07a72..a74579eea9 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/AppDataCollector.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/AppDataCollector.kt @@ -2,11 +2,26 @@ package com.bugsnag.android import android.annotation.SuppressLint import android.app.ActivityManager +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE_PRE_26 +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING_PRE_28 +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE +import android.app.ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE +import android.app.ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE import android.app.Application import android.content.Context import android.content.pm.PackageManager import android.os.Build.VERSION import android.os.Build.VERSION_CODES +import android.os.Process import android.os.SystemClock import com.bugsnag.android.internal.ImmutableConfig @@ -49,12 +64,57 @@ internal class AppDataCollector( ) } + @SuppressLint("SwitchIntDef") + @Suppress("DEPRECATION") + private fun getProcessImportance(): String? { + try { + val appInfo = ActivityManager.RunningAppProcessInfo() + if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { + ActivityManager.getMyMemoryState(appInfo) + } else { + val expectedPid = Process.myPid() + activityManager?.runningAppProcesses + ?.find { it.pid == expectedPid } + ?.let { + appInfo.importance = it.importance + appInfo.pid = expectedPid + } + } + + if (appInfo.pid == 0) { + return null + } + + return when (appInfo.importance) { + IMPORTANCE_FOREGROUND -> "foreground" + IMPORTANCE_FOREGROUND_SERVICE -> "foreground service" + IMPORTANCE_TOP_SLEEPING -> "top sleeping" + IMPORTANCE_TOP_SLEEPING_PRE_28 -> "top sleeping" + IMPORTANCE_VISIBLE -> "visible" + IMPORTANCE_PERCEPTIBLE -> "perceptible" + IMPORTANCE_PERCEPTIBLE_PRE_26 -> "perceptible" + IMPORTANCE_CANT_SAVE_STATE -> "can't save state" + IMPORTANCE_CANT_SAVE_STATE_PRE_26 -> "can't save state" + IMPORTANCE_SERVICE -> "service" + IMPORTANCE_CACHED -> "cached/background" + IMPORTANCE_GONE -> "gone" + IMPORTANCE_EMPTY -> "empty" + REASON_PROVIDER_IN_USE -> "provider in use" + REASON_SERVICE_IN_USE -> "service in use" + else -> "unknown importance (${appInfo.importance})" + } + } catch (e: Exception) { + return null + } + } + fun getAppDataMetadata(): MutableMap { val map = HashMap() map["name"] = appName map["activeScreen"] = sessionTracker.contextActivity map["lowMemory"] = memoryTrimState.isLowMemory map["memoryTrimLevel"] = memoryTrimState.trimLevelDescription + map["processImportance"] = getProcessImportance() populateRuntimeMemoryMetadata(map) @@ -128,6 +188,7 @@ internal class AppDataCollector( packageManager != null && copy != null -> { packageManager.getApplicationLabel(copy).toString() } + else -> null } } @@ -156,6 +217,7 @@ internal class AppDataCollector( VERSION.SDK_INT >= VERSION_CODES.P -> { Application.getProcessName() } + else -> { // see https://stackoverflow.com/questions/19631894 val clz = Class.forName("android.app.ActivityThread") @@ -179,5 +241,7 @@ internal class AppDataCollector( * good approximation for how long the app has been running. */ fun getDurationMs(): Long = SystemClock.elapsedRealtime() - startTimeMs + + private const val IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170 } } diff --git a/bugsnag-plugin-android-exitinfo/detekt-baseline.xml b/bugsnag-plugin-android-exitinfo/detekt-baseline.xml index 4597fca69f..09e64143ef 100644 --- a/bugsnag-plugin-android-exitinfo/detekt-baseline.xml +++ b/bugsnag-plugin-android-exitinfo/detekt-baseline.xml @@ -2,6 +2,7 @@ + CyclomaticComplexMethod:ExitInfoCallback.kt$ExitInfoCallback$@SuppressLint("SwitchIntDef") @Suppress("DEPRECATION") private fun importanceDescriptionOf(exitInfo: ApplicationExitInfo) CyclomaticComplexMethod:ExitInfoCallback.kt$ExitInfoCallback$private fun exitReasonOf(exitInfo: ApplicationExitInfo) LongParameterList:TombstoneParser.kt$TombstoneParser$( exitInfo: ApplicationExitInfo, listOpenFds: Boolean, includeLogcat: Boolean, threadConsumer: (BugsnagThread) -> Unit, fileDescriptorConsumer: (Int, String, String) -> Unit, logcatConsumer: (String) -> Unit ) MagicNumber:TraceParser.kt$TraceParser$16 diff --git a/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt b/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt index 21053f6a41..0feefbf78c 100644 --- a/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt +++ b/bugsnag-plugin-android-exitinfo/src/main/java/com/bugsnag/android/ExitInfoCallback.kt @@ -1,6 +1,21 @@ package com.bugsnag.android +import android.annotation.SuppressLint import android.app.ActivityManager +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE_PRE_26 +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING_PRE_28 +import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE +import android.app.ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE +import android.app.ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE import android.app.ApplicationExitInfo import android.content.Context import android.os.Build @@ -62,16 +77,24 @@ internal class ExitInfoCallback( else -> "unknown reason (${exitInfo.reason})" } + @SuppressLint("SwitchIntDef") + @Suppress("DEPRECATION") private fun importanceDescriptionOf(exitInfo: ApplicationExitInfo) = when (exitInfo.importance) { - ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND -> "foreground" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE -> "foreground service" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING -> "top sleeping" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE -> "visible" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE -> "perceptible" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE -> "can't save state" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE -> "service" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED -> "cached" - ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE -> "gone" + IMPORTANCE_FOREGROUND -> "foreground" + IMPORTANCE_FOREGROUND_SERVICE -> "foreground service" + IMPORTANCE_TOP_SLEEPING -> "top sleeping" + IMPORTANCE_TOP_SLEEPING_PRE_28 -> "top sleeping" + IMPORTANCE_VISIBLE -> "visible" + IMPORTANCE_PERCEPTIBLE -> "perceptible" + IMPORTANCE_PERCEPTIBLE_PRE_26 -> "perceptible" + IMPORTANCE_CANT_SAVE_STATE -> "can't save state" + IMPORTANCE_CANT_SAVE_STATE_PRE_26 -> "can't save state" + IMPORTANCE_SERVICE -> "service" + IMPORTANCE_CACHED -> "cached/background" + IMPORTANCE_GONE -> "gone" + IMPORTANCE_EMPTY -> "empty" + REASON_PROVIDER_IN_USE -> "provider in use" + REASON_SERVICE_IN_USE -> "service in use" else -> "unknown importance (${exitInfo.importance})" } @@ -86,6 +109,7 @@ internal class ExitInfoCallback( allExitInfo.find { it.pid == pid } internal companion object { - const val MAX_EXIT_INFO = 100 + private const val MAX_EXIT_INFO = 100 + private const val IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170 } } From 647544643ee77722625e4aaba5c07d1741497996 Mon Sep 17 00:00:00 2001 From: SmartbearYing Date: Thu, 8 Feb 2024 09:27:03 +0000 Subject: [PATCH 14/14] release/v6.2.0 --- CHANGELOG.md | 5 ++++- .../src/main/java/com/bugsnag/android/Notifier.kt | 2 +- examples/sdk-app-example/app/build.gradle | 4 ++-- gradle.properties | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b00ae58686..8507a1a351 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,12 @@ # Changelog -## TBD +## 6.2.0 (2024-02-08) ### Enhancements +* `processImportance` is now included in the 'app' tab on the dashboard + [#1973](https://github.com/bugsnag/bugsnag-android/pull/1973) + * `bugsnag-plugin-android-exitinfo` now includes `exitReason` and `processImportance` in the `APP` tab on the dashboard [#1968](https://github.com/bugsnag/bugsnag-android/pull/1968) diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/Notifier.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/Notifier.kt index f63f29d49d..b591d4196c 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/Notifier.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/Notifier.kt @@ -7,7 +7,7 @@ import java.io.IOException */ class Notifier @JvmOverloads constructor( var name: String = "Android Bugsnag Notifier", - var version: String = "6.1.0", + var version: String = "6.2.0", var url: String = "https://bugsnag.com" ) : JsonStream.Streamable { diff --git a/examples/sdk-app-example/app/build.gradle b/examples/sdk-app-example/app/build.gradle index fd3e161c97..542c6aa9b7 100644 --- a/examples/sdk-app-example/app/build.gradle +++ b/examples/sdk-app-example/app/build.gradle @@ -41,8 +41,8 @@ android { } dependencies { - implementation "com.bugsnag:bugsnag-android:6.1.0" - implementation "com.bugsnag:bugsnag-plugin-android-okhttp:6.1.0" + implementation "com.bugsnag:bugsnag-android:6.2.0" + implementation "com.bugsnag:bugsnag-plugin-android-okhttp:6.2.0" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "androidx.appcompat:appcompat:1.4.0" implementation "com.google.android.material:material:1.4.0" diff --git a/gradle.properties b/gradle.properties index be74ff989e..f0cd27ca84 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ org.gradle.jvmargs=-Xmx4096m # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects org.gradle.parallel=true -VERSION_NAME=6.1.0 +VERSION_NAME=6.2.0 GROUP=com.bugsnag POM_SCM_URL=https://github.com/bugsnag/bugsnag-android POM_SCM_CONNECTION=scm:git@github.com:bugsnag/bugsnag-android.git