Skip to content

Commit e0925a4

Browse files
authored
Merge branch 'main' into dependabot/github_actions/reactivecircus/android-emulator-runner-2.33.0
2 parents 3aae3fc + 274c295 commit e0925a4

File tree

25 files changed

+744
-69
lines changed

25 files changed

+744
-69
lines changed

.github/file-filters.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# This is used by the action https://github.com/dorny/paths-filter
2+
3+
high_risk_code: &high_risk_code
4+
# Transport classes
5+
- "sentry/src/main/java/io/sentry/transport/AsyncHttpTransport.java"
6+
- "sentry/src/main/java/io/sentry/transport/HttpConnection.java"
7+
- "sentry/src/main/java/io/sentry/transport/QueuedThreadPoolExecutor.java"
8+
- "sentry/src/main/java/io/sentry/transport/RateLimiter.java"
9+
- "sentry-apache-http-client-5/src/main/java/io/sentry/transport/apache/ApacheHttpClientTransport.java"
10+
11+
# Class used by hybrid SDKs
12+
- "sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java"
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Changes In High Risk Code
2+
on:
3+
pull_request:
4+
5+
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
6+
concurrency:
7+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
8+
cancel-in-progress: true
9+
10+
jobs:
11+
files-changed:
12+
name: Detect changed files
13+
runs-on: ubuntu-latest
14+
# Map a step output to a job output
15+
outputs:
16+
high_risk_code: ${{ steps.changes.outputs.high_risk_code }}
17+
high_risk_code_files: ${{ steps.changes.outputs.high_risk_code_files }}
18+
steps:
19+
- uses: actions/checkout@v4
20+
- name: Get changed files
21+
id: changes
22+
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
23+
with:
24+
token: ${{ github.token }}
25+
filters: .github/file-filters.yml
26+
27+
# Enable listing of files matching each filter.
28+
# Paths to files will be available in `${FILTER_NAME}_files` output variable.
29+
list-files: csv
30+
31+
validate-high-risk-code:
32+
if: needs.files-changed.outputs.high_risk_code == 'true'
33+
needs: files-changed
34+
runs-on: ubuntu-latest
35+
steps:
36+
- name: Comment on PR to notify of changes in high risk files
37+
uses: actions/github-script@v7
38+
env:
39+
high_risk_code: ${{ needs.files-changed.outputs.high_risk_code_files }}
40+
with:
41+
script: |
42+
const highRiskFiles = process.env.high_risk_code;
43+
const fileList = highRiskFiles.split(',').map(file => `- [ ] ${file}`).join('\n');
44+
github.rest.issues.createComment({
45+
issue_number: context.issue.number,
46+
owner: context.repo.owner,
47+
repo: context.repo.repo,
48+
body: `### 🚨 Detected changes in high risk code 🚨 \n High-risk code has higher potential to break the SDK and may be hard to test. To prevent severe bugs, apply the rollout process for releasing such changes and be extra careful when changing and reviewing these files:\n ${fileList}`
49+
})

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@
22

33
## Unreleased
44

5+
### Features
6+
7+
- Add meta option to attach ANR thread dumps ([#3791](https://github.com/getsentry/sentry-java/pull/3791))
8+
59
### Fixes
610

11+
- fix invalid profiles when the transaction name is empty ([#3747](https://github.com/getsentry/sentry-java/pull/3747))
712
- Deprecate `enableTracing` option ([#3777](https://github.com/getsentry/sentry-java/pull/3777))
13+
- Vendor `java.util.Random` and replace `java.security.SecureRandom` usages ([#3783](https://github.com/getsentry/sentry-java/pull/3783))
14+
- Fix potential ANRs due to NDK scope sync ([#3754](https://github.com/getsentry/sentry-java/pull/3754))
15+
- Fix potential ANRs due to NDK System.loadLibrary calls ([#3670](https://github.com/getsentry/sentry-java/pull/3670))
816

917
## 7.15.0
1018

sentry-android-core/src/main/java/io/sentry/android/core/AnrV2EventProcessor.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@
5454
import io.sentry.protocol.SentryTransaction;
5555
import io.sentry.protocol.User;
5656
import io.sentry.util.HintUtils;
57+
import io.sentry.util.Random;
5758
import java.io.File;
58-
import java.security.SecureRandom;
5959
import java.util.ArrayList;
6060
import java.util.Arrays;
6161
import java.util.Collections;
@@ -83,7 +83,7 @@ public final class AnrV2EventProcessor implements BackfillingEventProcessor {
8383

8484
private final @NotNull SentryExceptionFactory sentryExceptionFactory;
8585

86-
private final @Nullable SecureRandom random;
86+
private final @Nullable Random random;
8787

8888
public AnrV2EventProcessor(
8989
final @NotNull Context context,
@@ -96,7 +96,7 @@ public AnrV2EventProcessor(
9696
final @NotNull Context context,
9797
final @NotNull SentryAndroidOptions options,
9898
final @NotNull BuildInfoProvider buildInfoProvider,
99-
final @Nullable SecureRandom random) {
99+
final @Nullable Random random) {
100100
this.context = ContextUtils.getApplicationContext(context);
101101
this.options = options;
102102
this.buildInfoProvider = buildInfoProvider;
@@ -180,7 +180,7 @@ private boolean sampleReplay(final @NotNull SentryEvent event) {
180180

181181
try {
182182
// we have to sample here with the old sample rate, because it may change between app launches
183-
final @NotNull SecureRandom random = this.random != null ? this.random : new SecureRandom();
183+
final @NotNull Random random = this.random != null ? this.random : new Random();
184184
final double replayErrorSampleRateDouble = Double.parseDouble(replayErrorSampleRate);
185185
if (replayErrorSampleRateDouble < random.nextDouble()) {
186186
options

sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ final class ManifestMetadataReader {
2626
static final String SAMPLE_RATE = "io.sentry.sample-rate";
2727
static final String ANR_ENABLE = "io.sentry.anr.enable";
2828
static final String ANR_REPORT_DEBUG = "io.sentry.anr.report-debug";
29-
3029
static final String ANR_TIMEOUT_INTERVAL_MILLIS = "io.sentry.anr.timeout-interval-millis";
30+
static final String ANR_ATTACH_THREAD_DUMPS = "io.sentry.anr.attach-thread-dumps";
3131

3232
static final String AUTO_INIT = "io.sentry.auto-init";
3333
static final String NDK_ENABLE = "io.sentry.ndk.enable";
@@ -176,6 +176,9 @@ static void applyMetadata(
176176
ANR_TIMEOUT_INTERVAL_MILLIS,
177177
options.getAnrTimeoutIntervalMillis()));
178178

179+
options.setAttachAnrThreadDump(
180+
readBool(metadata, logger, ANR_ATTACH_THREAD_DUMPS, options.isAttachAnrThreadDump()));
181+
179182
final String dsn = readString(metadata, logger, DSN, options.getDsn());
180183
final boolean enabled = readBool(metadata, logger, ENABLE_SENTRY, options.isEnabled());
181184

sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransactionProfilerTest.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,17 @@ class AndroidTransactionProfilerTest {
360360
verify(mockExecutorService, never()).submit(any<Callable<*>>())
361361
}
362362

363+
@Test
364+
fun `profiling transaction with empty name fallbacks to unknown`() {
365+
val profiler = fixture.getSut(context)
366+
profiler.start()
367+
profiler.bindTransaction(fixture.transaction1)
368+
val profilingTraceData = profiler.onTransactionFinish(fixture.transaction1, null, fixture.options)
369+
assertNotNull(profilingTraceData)
370+
assertEquals("unknown", profilingTraceData.transactionName)
371+
assertEquals("unknown", profilingTraceData.transactions.first().name)
372+
}
373+
363374
@Test
364375
fun `profiler does not throw if traces cannot be written to disk`() {
365376
File(fixture.options.profilingTracesDirPath!!).setWritable(false)

sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,31 @@ class ManifestMetadataReaderTest {
246246
assertEquals(5000.toLong(), fixture.options.anrTimeoutIntervalMillis)
247247
}
248248

249+
@Test
250+
fun `applyMetadata reads anr attach thread dump to options`() {
251+
// Arrange
252+
val bundle = bundleOf(ManifestMetadataReader.ANR_ATTACH_THREAD_DUMPS to true)
253+
val context = fixture.getContext(metaData = bundle)
254+
255+
// Act
256+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
257+
258+
// Assert
259+
assertEquals(true, fixture.options.isAttachAnrThreadDump)
260+
}
261+
262+
@Test
263+
fun `applyMetadata reads anr attach thread dump to options and keeps default`() {
264+
// Arrange
265+
val context = fixture.getContext()
266+
267+
// Act
268+
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)
269+
270+
// Assert
271+
assertEquals(false, fixture.options.isAttachAnrThreadDump)
272+
}
273+
249274
@Test
250275
fun `applyMetadata reads activity breadcrumbs to options`() {
251276
// Arrange

sentry-android-ndk/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,6 @@ dependencies {
105105

106106
testImplementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION))
107107
testImplementation(Config.TestLibs.kotlinTestJunit)
108-
109108
testImplementation(Config.TestLibs.mockitoKotlin)
109+
testImplementation(projects.sentryTestSupport)
110110
}

sentry-android-ndk/src/main/java/io/sentry/android/ndk/NdkScopeObserver.java

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,18 @@ public NdkScopeObserver(final @NotNull SentryOptions options) {
3131
@Override
3232
public void setUser(final @Nullable User user) {
3333
try {
34-
if (user == null) {
35-
// remove user if its null
36-
nativeScope.removeUser();
37-
} else {
38-
nativeScope.setUser(user.getId(), user.getEmail(), user.getIpAddress(), user.getUsername());
39-
}
34+
options
35+
.getExecutorService()
36+
.submit(
37+
() -> {
38+
if (user == null) {
39+
// remove user if its null
40+
nativeScope.removeUser();
41+
} else {
42+
nativeScope.setUser(
43+
user.getId(), user.getEmail(), user.getIpAddress(), user.getUsername());
44+
}
45+
});
4046
} catch (Throwable e) {
4147
options.getLogger().log(SentryLevel.ERROR, e, "Scope sync setUser has an error.");
4248
}
@@ -45,24 +51,36 @@ public void setUser(final @Nullable User user) {
4551
@Override
4652
public void addBreadcrumb(final @NotNull Breadcrumb crumb) {
4753
try {
48-
String level = null;
49-
if (crumb.getLevel() != null) {
50-
level = crumb.getLevel().name().toLowerCase(Locale.ROOT);
51-
}
52-
final String timestamp = DateUtils.getTimestamp(crumb.getTimestamp());
54+
options
55+
.getExecutorService()
56+
.submit(
57+
() -> {
58+
String level = null;
59+
if (crumb.getLevel() != null) {
60+
level = crumb.getLevel().name().toLowerCase(Locale.ROOT);
61+
}
62+
final String timestamp = DateUtils.getTimestamp(crumb.getTimestamp());
5363

54-
String data = null;
55-
try {
56-
final Map<String, Object> dataRef = crumb.getData();
57-
if (!dataRef.isEmpty()) {
58-
data = options.getSerializer().serialize(dataRef);
59-
}
60-
} catch (Throwable e) {
61-
options.getLogger().log(SentryLevel.ERROR, e, "Breadcrumb data is not serializable.");
62-
}
64+
String data = null;
65+
try {
66+
final Map<String, Object> dataRef = crumb.getData();
67+
if (!dataRef.isEmpty()) {
68+
data = options.getSerializer().serialize(dataRef);
69+
}
70+
} catch (Throwable e) {
71+
options
72+
.getLogger()
73+
.log(SentryLevel.ERROR, e, "Breadcrumb data is not serializable.");
74+
}
6375

64-
nativeScope.addBreadcrumb(
65-
level, crumb.getMessage(), crumb.getCategory(), crumb.getType(), timestamp, data);
76+
nativeScope.addBreadcrumb(
77+
level,
78+
crumb.getMessage(),
79+
crumb.getCategory(),
80+
crumb.getType(),
81+
timestamp,
82+
data);
83+
});
6684
} catch (Throwable e) {
6785
options.getLogger().log(SentryLevel.ERROR, e, "Scope sync addBreadcrumb has an error.");
6886
}
@@ -71,7 +89,7 @@ public void addBreadcrumb(final @NotNull Breadcrumb crumb) {
7189
@Override
7290
public void setTag(final @NotNull String key, final @NotNull String value) {
7391
try {
74-
nativeScope.setTag(key, value);
92+
options.getExecutorService().submit(() -> nativeScope.setTag(key, value));
7593
} catch (Throwable e) {
7694
options.getLogger().log(SentryLevel.ERROR, e, "Scope sync setTag(%s) has an error.", key);
7795
}
@@ -80,7 +98,7 @@ public void setTag(final @NotNull String key, final @NotNull String value) {
8098
@Override
8199
public void removeTag(final @NotNull String key) {
82100
try {
83-
nativeScope.removeTag(key);
101+
options.getExecutorService().submit(() -> nativeScope.removeTag(key));
84102
} catch (Throwable e) {
85103
options.getLogger().log(SentryLevel.ERROR, e, "Scope sync removeTag(%s) has an error.", key);
86104
}
@@ -89,7 +107,7 @@ public void removeTag(final @NotNull String key) {
89107
@Override
90108
public void setExtra(final @NotNull String key, final @NotNull String value) {
91109
try {
92-
nativeScope.setExtra(key, value);
110+
options.getExecutorService().submit(() -> nativeScope.setExtra(key, value));
93111
} catch (Throwable e) {
94112
options.getLogger().log(SentryLevel.ERROR, e, "Scope sync setExtra(%s) has an error.", key);
95113
}
@@ -98,7 +116,7 @@ public void setExtra(final @NotNull String key, final @NotNull String value) {
98116
@Override
99117
public void removeExtra(final @NotNull String key) {
100118
try {
101-
nativeScope.removeExtra(key);
119+
options.getExecutorService().submit(() -> nativeScope.removeExtra(key));
102120
} catch (Throwable e) {
103121
options
104122
.getLogger()

sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdk.java

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,40 @@
11
package io.sentry.android.ndk;
22

33
import io.sentry.android.core.SentryAndroidOptions;
4+
import java.util.concurrent.CountDownLatch;
5+
import java.util.concurrent.TimeUnit;
46
import org.jetbrains.annotations.ApiStatus;
57
import org.jetbrains.annotations.NotNull;
68

79
@ApiStatus.Internal
810
public final class SentryNdk {
911

12+
private static final @NotNull CountDownLatch loadLibraryLatch = new CountDownLatch(1);
13+
1014
private SentryNdk() {}
1115

1216
static {
13-
// On older Android versions, it was necessary to manually call "`System.loadLibrary` on all
14-
// transitive dependencies before loading [the] main library."
15-
// The dependencies of `libsentry.so` are currently `lib{c,m,dl,log}.so`.
16-
// See
17-
// https://android.googlesource.com/platform/bionic/+/master/android-changes-for-ndk-developers.md#changes-to-library-dependency-resolution
18-
System.loadLibrary("log");
19-
System.loadLibrary("sentry");
20-
System.loadLibrary("sentry-android");
17+
new Thread(
18+
() -> {
19+
// On older Android versions, it was necessary to manually call "`System.loadLibrary`
20+
// on all
21+
// transitive dependencies before loading [the] main library."
22+
// The dependencies of `libsentry.so` are currently `lib{c,m,dl,log}.so`.
23+
// See
24+
// https://android.googlesource.com/platform/bionic/+/master/android-changes-for-ndk-developers.md#changes-to-library-dependency-resolution
25+
try {
26+
System.loadLibrary("log");
27+
System.loadLibrary("sentry");
28+
System.loadLibrary("sentry-android");
29+
} catch (Throwable t) {
30+
// ignored
31+
// if loadLibrary() fails, the later init() will throw an exception anyway
32+
} finally {
33+
loadLibraryLatch.countDown();
34+
}
35+
},
36+
"SentryNdkLoadLibs")
37+
.start();
2138
}
2239

2340
private static native void initSentryNative(@NotNull final SentryAndroidOptions options);
@@ -31,14 +48,23 @@ private SentryNdk() {}
3148
*/
3249
public static void init(@NotNull final SentryAndroidOptions options) {
3350
SentryNdkUtil.addPackage(options.getSdkVersion());
34-
initSentryNative(options);
51+
try {
52+
if (loadLibraryLatch.await(2000, TimeUnit.MILLISECONDS)) {
53+
initSentryNative(options);
3554

36-
// only add scope sync observer if the scope sync is enabled.
37-
if (options.isEnableScopeSync()) {
38-
options.addScopeObserver(new NdkScopeObserver(options));
39-
}
55+
// only add scope sync observer if the scope sync is enabled.
56+
if (options.isEnableScopeSync()) {
57+
options.addScopeObserver(new NdkScopeObserver(options));
58+
}
4059

41-
options.setDebugImagesLoader(new DebugImagesLoader(options, new NativeModuleListLoader()));
60+
options.setDebugImagesLoader(new DebugImagesLoader(options, new NativeModuleListLoader()));
61+
} else {
62+
throw new IllegalStateException("Timeout waiting for Sentry NDK library to load");
63+
}
64+
} catch (InterruptedException e) {
65+
throw new IllegalStateException(
66+
"Thread interrupted while waiting for NDK libs to be loaded", e);
67+
}
4268
}
4369

4470
/** Closes the NDK integration */

0 commit comments

Comments
 (0)