diff --git a/CHANGELOG.md b/CHANGELOG.md index e05a7a1af38..9bca5afb254 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ ### Fixes +- Fix `NoSuchMethodError` when using `Math.floorDiv`/`Math.floorMod` on Android < 24 ([#5743](https://github.com/getsentry/sentry-java/pull/5743)) - Fix main thread identification parsing for ApplicationExitInfo ANRs ([#5733](https://github.com/getsentry/sentry-java/pull/5733)) - Do not send threads without stacktraces for ApplicationExitInfo ANRs ([#5733](https://github.com/getsentry/sentry-java/pull/5733)) - Record byte-level client reports when event processors discard logs or trace metrics ([#5718](https://github.com/getsentry/sentry-java/pull/5718)) diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 7b87b92dcb3..381c39cfd68 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -34,6 +34,34 @@ limitations under the License. --- +## Google Guava — LongMath (Apache 2.0) + +**Source:** https://github.com/google/guava/blob/v33.0.0/guava/src/com/google/common/math/LongMath.java
+**License:** Apache License 2.0
+**Copyright:** Copyright (C) 2011 The Guava Authors + +### Scope + +The Sentry Java SDK includes adapted floor division logic from Guava's `LongMath` class to support older Android API levels. The code resides in `io.sentry.vendor.SentryMath`. + +``` +Copyright (C) 2011 The Guava Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` + +--- + ## FasterXML Jackson — ISO8601Utils (Apache 2.0) **Source:** https://github.com/FasterXML/jackson-databind
diff --git a/sentry/src/main/java/io/sentry/vendor/SentryIso8601Utils.java b/sentry/src/main/java/io/sentry/vendor/SentryIso8601Utils.java index b5cb1811aa9..134a13e72a5 100644 --- a/sentry/src/main/java/io/sentry/vendor/SentryIso8601Utils.java +++ b/sentry/src/main/java/io/sentry/vendor/SentryIso8601Utils.java @@ -142,8 +142,8 @@ public static long parseTimestamp(final @NotNull String timestamp) { return formatTimestampWithCalendar(millis); } - final long epochDay = Math.floorDiv(millis, MILLIS_PER_DAY); - int millisOfDay = (int) Math.floorMod(millis, MILLIS_PER_DAY); + final long epochDay = SentryMath.floorDiv(millis, MILLIS_PER_DAY); + int millisOfDay = (int) SentryMath.floorMod(millis, MILLIS_PER_DAY); final int[] yearMonthDay = epochDayToYearMonthDay(epochDay); final int hour = millisOfDay / (int) MILLIS_PER_HOUR; @@ -281,7 +281,7 @@ private static long epochMillis( private static long daysFromYearMonthDay(int year, final int month, final int day) { year -= month <= 2 ? 1 : 0; - final long era = Math.floorDiv(year, 400); + final long era = SentryMath.floorDiv(year, 400L); final int yearOfEra = (int) (year - era * 400); final int dayOfYear = (153 * (month + (month > 2 ? -3 : 9)) + 2) / 5 + day - 1; final int dayOfEra = yearOfEra * 365 + yearOfEra / 4 - yearOfEra / 100 + dayOfYear; @@ -290,7 +290,7 @@ private static long daysFromYearMonthDay(int year, final int month, final int da private static int[] epochDayToYearMonthDay(long epochDay) { epochDay += DAYS_0000_TO_1970; - final long era = Math.floorDiv(epochDay, 146097); + final long era = SentryMath.floorDiv(epochDay, 146097L); final int dayOfEra = (int) (epochDay - era * 146097); final int yearOfEra = (dayOfEra - dayOfEra / 1460 + dayOfEra / 36524 - dayOfEra / 146096) / 365; final int year = (int) (yearOfEra + era * 400); diff --git a/sentry/src/main/java/io/sentry/vendor/SentryMath.java b/sentry/src/main/java/io/sentry/vendor/SentryMath.java new file mode 100644 index 00000000000..50a8118d462 --- /dev/null +++ b/sentry/src/main/java/io/sentry/vendor/SentryMath.java @@ -0,0 +1,40 @@ +/* + * Adapted from https://github.com/google/guava/blob/v33.0.0/guava/src/com/google/common/math/LongMath.java + * + * Copyright (C) 2011 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.sentry.vendor; + +final class SentryMath { + + private SentryMath() {} + + static long floorDiv(final long x, final long y) { + final long quotient = x / y; + final long remainder = x - y * quotient; + + if (remainder == 0) { + return quotient; + } + + final int signum = 1 | (int) ((x ^ y) >> (Long.SIZE - 1)); + return signum < 0 ? quotient + signum : quotient; + } + + static long floorMod(final long x, final long y) { + return x - floorDiv(x, y) * y; + } +} diff --git a/sentry/src/test/java/io/sentry/vendor/SentryMathTest.kt b/sentry/src/test/java/io/sentry/vendor/SentryMathTest.kt new file mode 100644 index 00000000000..32b9f8621d0 --- /dev/null +++ b/sentry/src/test/java/io/sentry/vendor/SentryMathTest.kt @@ -0,0 +1,58 @@ +package io.sentry.vendor + +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlin.test.assertFailsWith + +class SentryMathTest { + @Test + fun `floorDiv matches Java Math floorDiv`() { + values.forEach { x -> + divisors.forEach { y -> assertThat(SentryMath.floorDiv(x, y)).isEqualTo(Math.floorDiv(x, y)) } + } + } + + @Test + fun `floorMod matches Java Math floorMod`() { + values.forEach { x -> + divisors.forEach { y -> assertThat(SentryMath.floorMod(x, y)).isEqualTo(Math.floorMod(x, y)) } + } + } + + @Test + fun `floorDiv throws when dividing by zero`() { + assertFailsWith { SentryMath.floorDiv(1L, 0L) } + } + + @Test + fun `floorMod throws when dividing by zero`() { + assertFailsWith { SentryMath.floorMod(1L, 0L) } + } + + private companion object { + val values = + listOf( + Long.MIN_VALUE, + -86_400_001L, + -86_400_000L, + -86_399_999L, + -401L, + -400L, + -399L, + -2L, + -1L, + 0L, + 1L, + 2L, + 399L, + 400L, + 401L, + 86_399_999L, + 86_400_000L, + 86_400_001L, + Long.MAX_VALUE, + ) + + val divisors = listOf(Long.MIN_VALUE, -146_097L, -400L, -1L, 1L, 400L, 146_097L, Long.MAX_VALUE) + } +}