@sentry/react-native 6.0.0-rc.1 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +8 -0
  2. package/RNSentry.podspec +1 -1
  3. package/android/build.gradle +1 -1
  4. package/android/src/main/java/io/sentry/react/RNSentryBreadcrumb.java +67 -66
  5. package/android/src/main/java/io/sentry/react/RNSentryMapConverter.java +110 -110
  6. package/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java +868 -813
  7. package/android/src/main/java/io/sentry/react/RNSentryOnDrawReporterManager.java +128 -121
  8. package/android/src/main/java/io/sentry/react/RNSentryPackage.java +8 -14
  9. package/android/src/main/java/io/sentry/react/RNSentryReactFragmentLifecycleTracer.java +76 -70
  10. package/android/src/main/java/io/sentry/react/RNSentryReplayBreadcrumbConverter.java +29 -38
  11. package/android/src/newarch/java/io/sentry/react/RNSentryModule.java +164 -165
  12. package/android/src/oldarch/java/io/sentry/react/RNSentryModule.java +164 -165
  13. package/dist/js/integrations/nativelinkederrors.js +7 -2
  14. package/dist/js/integrations/nativelinkederrors.js.map +1 -1
  15. package/dist/js/tracing/timetodisplay.d.ts.map +1 -1
  16. package/dist/js/tracing/timetodisplay.js +3 -1
  17. package/dist/js/tracing/timetodisplay.js.map +1 -1
  18. package/dist/js/version.d.ts +1 -1
  19. package/dist/js/version.d.ts.map +1 -1
  20. package/dist/js/version.js +1 -1
  21. package/dist/js/version.js.map +1 -1
  22. package/dist/js/wrapper.d.ts +1 -1
  23. package/dist/js/wrapper.d.ts.map +1 -1
  24. package/dist/js/wrapper.js +37 -5
  25. package/dist/js/wrapper.js.map +1 -1
  26. package/ios/RNSentry.mm +9 -1
  27. package/package.json +9 -9
  28. package/plugin/build/utils.js +6 -6
  29. package/plugin/build/withSentry.js +2 -2
  30. package/plugin/build/withSentryAndroid.js +2 -2
  31. package/plugin/build/withSentryIOS.js +3 -3
  32. package/ts3.8/dist/js/version.d.ts +1 -1
  33. package/ts3.8/dist/js/wrapper.d.ts +1 -1
@@ -1,9 +1,9 @@
1
1
  package io.sentry.react;
2
2
 
3
- import static java.util.concurrent.TimeUnit.SECONDS;
4
3
  import static io.sentry.android.core.internal.util.ScreenshotUtils.takeScreenshot;
5
4
  import static io.sentry.vendor.Base64.NO_PADDING;
6
5
  import static io.sentry.vendor.Base64.NO_WRAP;
6
+ import static java.util.concurrent.TimeUnit.SECONDS;
7
7
 
8
8
  import android.app.Activity;
9
9
  import android.content.Context;
@@ -11,11 +11,9 @@ import android.content.pm.PackageInfo;
11
11
  import android.content.pm.PackageManager;
12
12
  import android.content.res.AssetManager;
13
13
  import android.util.SparseIntArray;
14
-
15
14
  import androidx.core.app.FrameMetricsAggregator;
16
15
  import androidx.fragment.app.FragmentActivity;
17
16
  import androidx.fragment.app.FragmentManager;
18
-
19
17
  import com.facebook.hermes.instrumentation.HermesSamplingProfiler;
20
18
  import com.facebook.react.bridge.Arguments;
21
19
  import com.facebook.react.bridge.Promise;
@@ -28,32 +26,10 @@ import com.facebook.react.bridge.WritableMap;
28
26
  import com.facebook.react.bridge.WritableNativeArray;
29
27
  import com.facebook.react.bridge.WritableNativeMap;
30
28
  import com.facebook.react.modules.core.DeviceEventManagerModule;
31
-
32
- import org.jetbrains.annotations.NotNull;
33
- import org.jetbrains.annotations.Nullable;
34
-
35
- import java.io.BufferedInputStream;
36
- import java.io.BufferedReader;
37
- import java.io.File;
38
- import java.io.FileNotFoundException;
39
- import java.io.FileReader;
40
- import java.io.IOException;
41
- import java.io.InputStream;
42
- import java.nio.charset.Charset;
43
- import java.util.HashMap;
44
- import java.util.HashSet;
45
- import java.util.List;
46
- import java.util.Map;
47
- import java.util.Properties;
48
- import java.util.Set;
49
- import java.util.concurrent.CountDownLatch;
50
-
51
- import io.sentry.Breadcrumb;
52
- import io.sentry.DateUtils;
53
29
  import io.sentry.HubAdapter;
54
30
  import io.sentry.ILogger;
55
- import io.sentry.ISentryExecutorService;
56
31
  import io.sentry.IScope;
32
+ import io.sentry.ISentryExecutorService;
57
33
  import io.sentry.ISerializer;
58
34
  import io.sentry.Integration;
59
35
  import io.sentry.Sentry;
@@ -87,837 +63,916 @@ import io.sentry.protocol.SentryPackage;
87
63
  import io.sentry.protocol.User;
88
64
  import io.sentry.protocol.ViewHierarchy;
89
65
  import io.sentry.util.DebugMetaPropertiesApplier;
66
+ import io.sentry.util.FileUtils;
90
67
  import io.sentry.util.JsonSerializationUtils;
91
68
  import io.sentry.vendor.Base64;
92
- import io.sentry.util.FileUtils;
69
+ import java.io.BufferedInputStream;
70
+ import java.io.BufferedReader;
71
+ import java.io.File;
72
+ import java.io.FileNotFoundException;
73
+ import java.io.FileReader;
74
+ import java.io.IOException;
75
+ import java.io.InputStream;
76
+ import java.nio.charset.Charset;
77
+ import java.util.HashMap;
78
+ import java.util.List;
79
+ import java.util.Map;
80
+ import java.util.Properties;
81
+ import java.util.concurrent.CountDownLatch;
82
+ import org.jetbrains.annotations.NotNull;
83
+ import org.jetbrains.annotations.Nullable;
93
84
 
94
85
  public class RNSentryModuleImpl {
95
86
 
96
- public static final String NAME = "RNSentry";
97
-
98
- private static final String NATIVE_SDK_NAME = "sentry.native.android.react-native";
99
- private static final String ANDROID_SDK_NAME = "sentry.java.android.react-native";
100
- private static final ILogger logger = new AndroidLogger(NAME);
101
- private static final BuildInfoProvider buildInfo = new BuildInfoProvider(logger);
102
- private static final String modulesPath = "modules.json";
103
- private static final Charset UTF_8 = Charset.forName("UTF-8");
104
-
105
- private final ReactApplicationContext reactApplicationContext;
106
- private final PackageInfo packageInfo;
107
- private FrameMetricsAggregator frameMetricsAggregator = null;
108
- private boolean androidXAvailable;
109
-
110
- private static boolean hasFetchedAppStart;
111
-
112
- // 700ms to constitute frozen frames.
113
- private static final int FROZEN_FRAME_THRESHOLD = 700;
114
- // 16ms (slower than 60fps) to constitute slow frames.
115
- private static final int SLOW_FRAME_THRESHOLD = 16;
116
-
117
- private static final int SCREENSHOT_TIMEOUT_SECONDS = 2;
118
-
119
- /**
120
- * Profiling traces rate. 101 hz means 101 traces in 1 second. Defaults to 101 to avoid possible
121
- * lockstep sampling. More on
122
- * https://stackoverflow.com/questions/45470758/what-is-lockstep-sampling
123
- */
124
- private int profilingTracesHz = 101;
125
-
126
- private AndroidProfiler androidProfiler = null;
127
-
128
- private boolean isProguardDebugMetaLoaded = false;
129
- private @Nullable String proguardUuid = null;
130
- private String cacheDirPath = null;
131
- private ISentryExecutorService executorService = null;
132
-
133
- private final @NotNull Runnable emitNewFrameEvent;
134
-
135
- /** Max trace file size in bytes. */
136
- private long maxTraceFileSize = 5 * 1024 * 1024;
137
-
138
- public RNSentryModuleImpl(ReactApplicationContext reactApplicationContext) {
139
- packageInfo = getPackageInfo(reactApplicationContext);
140
- this.reactApplicationContext = reactApplicationContext;
141
- this.emitNewFrameEvent = createEmitNewFrameEvent();
142
- }
143
-
144
- private ReactApplicationContext getReactApplicationContext() {
145
- return this.reactApplicationContext;
146
- }
147
-
148
- private @Nullable Activity getCurrentActivity() {
149
- return this.reactApplicationContext.getCurrentActivity();
150
- }
151
-
152
- private @NotNull Runnable createEmitNewFrameEvent() {
153
- final @NotNull SentryDateProvider dateProvider = new SentryAndroidDateProvider();
154
-
155
- return () -> {
156
- final SentryDate endDate = dateProvider.now();
157
- WritableMap event = Arguments.createMap();
158
- event.putDouble("newFrameTimestampInSeconds", endDate.nanoTimestamp() / 1e9);
159
- getReactApplicationContext()
160
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
161
- .emit("rn_sentry_new_frame", event);
162
- };
163
- }
164
-
165
- private void initFragmentInitialFrameTracking() {
166
- final RNSentryReactFragmentLifecycleTracer fragmentLifecycleTracer =
167
- new RNSentryReactFragmentLifecycleTracer(buildInfo, emitNewFrameEvent, logger);
168
-
169
- final @Nullable FragmentActivity fragmentActivity = (FragmentActivity) getCurrentActivity();
170
- if (fragmentActivity != null) {
171
- final @Nullable FragmentManager supportFragmentManager = fragmentActivity.getSupportFragmentManager();
172
- if (supportFragmentManager != null) {
173
- supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleTracer, true);
174
- }
175
- }
176
- }
177
-
178
- public void initNativeReactNavigationNewFrameTracking(Promise promise) {
179
- this.initFragmentInitialFrameTracking();
180
- }
181
-
182
- public void initNativeSdk(final ReadableMap rnOptions, Promise promise) {
183
- SentryAndroid.init(this.getReactApplicationContext(), options -> {
184
- @Nullable SdkVersion sdkVersion = options.getSdkVersion();
185
- if (sdkVersion == null) {
186
- sdkVersion = new SdkVersion(ANDROID_SDK_NAME, BuildConfig.VERSION_NAME);
187
- } else {
188
- sdkVersion.setName(ANDROID_SDK_NAME);
189
- }
190
-
191
- options.setSentryClientName(sdkVersion.getName() + "/" + sdkVersion.getVersion());
192
- options.setNativeSdkName(NATIVE_SDK_NAME);
193
- options.setSdkVersion(sdkVersion);
194
-
195
- if (rnOptions.hasKey("debug") && rnOptions.getBoolean("debug")) {
196
- options.setDebug(true);
197
- }
198
- if (rnOptions.hasKey("dsn") && rnOptions.getString("dsn") != null) {
199
- String dsn = rnOptions.getString("dsn");
200
- logger.log(SentryLevel.INFO, String.format("Starting with DSN: '%s'", dsn));
201
- options.setDsn(dsn);
202
- } else {
203
- // SentryAndroid needs an empty string fallback for the dsn.
204
- options.setDsn("");
205
- }
206
- if (rnOptions.hasKey("sampleRate")) {
207
- options.setSampleRate(rnOptions.getDouble("sampleRate"));
208
- }
209
- if (rnOptions.hasKey("sendClientReports")) {
210
- options.setSendClientReports(rnOptions.getBoolean("sendClientReports"));
211
- }
212
- if (rnOptions.hasKey("maxBreadcrumbs")) {
213
- options.setMaxBreadcrumbs(rnOptions.getInt("maxBreadcrumbs"));
214
- }
215
- if (rnOptions.hasKey("maxCacheItems")) {
216
- options.setMaxCacheItems(rnOptions.getInt("maxCacheItems"));
217
- }
218
- if (rnOptions.hasKey("environment") && rnOptions.getString("environment") != null) {
219
- options.setEnvironment(rnOptions.getString("environment"));
220
- }
221
- if (rnOptions.hasKey("release") && rnOptions.getString("release") != null) {
222
- options.setRelease(rnOptions.getString("release"));
223
- }
224
- if (rnOptions.hasKey("dist") && rnOptions.getString("dist") != null) {
225
- options.setDist(rnOptions.getString("dist"));
226
- }
227
- if (rnOptions.hasKey("enableAutoSessionTracking")) {
228
- options.setEnableAutoSessionTracking(rnOptions.getBoolean("enableAutoSessionTracking"));
229
- }
230
- if (rnOptions.hasKey("sessionTrackingIntervalMillis")) {
231
- options.setSessionTrackingIntervalMillis(rnOptions.getInt("sessionTrackingIntervalMillis"));
232
- }
233
- if (rnOptions.hasKey("shutdownTimeout")) {
234
- options.setShutdownTimeoutMillis(rnOptions.getInt("shutdownTimeout"));
235
- }
236
- if (rnOptions.hasKey("enableNdkScopeSync")) {
237
- options.setEnableScopeSync(rnOptions.getBoolean("enableNdkScopeSync"));
238
- }
239
- if (rnOptions.hasKey("attachStacktrace")) {
240
- options.setAttachStacktrace(rnOptions.getBoolean("attachStacktrace"));
241
- }
242
- if (rnOptions.hasKey("attachThreads")) {
243
- // JS use top level stacktrace and android attaches Threads which hides them so
244
- // by default we hide.
245
- options.setAttachThreads(rnOptions.getBoolean("attachThreads"));
246
- }
247
- if (rnOptions.hasKey("attachScreenshot")) {
248
- options.setAttachScreenshot(rnOptions.getBoolean("attachScreenshot"));
249
- }
250
- if (rnOptions.hasKey("attachViewHierarchy")) {
251
- options.setAttachViewHierarchy(rnOptions.getBoolean("attachViewHierarchy"));
252
- }
253
- if (rnOptions.hasKey("sendDefaultPii")) {
254
- options.setSendDefaultPii(rnOptions.getBoolean("sendDefaultPii"));
255
- }
256
- if (rnOptions.hasKey("maxQueueSize")) {
257
- options.setMaxQueueSize(rnOptions.getInt("maxQueueSize"));
258
- }
259
- if (rnOptions.hasKey("enableNdk")) {
260
- options.setEnableNdk(rnOptions.getBoolean("enableNdk"));
261
- }
262
- if (rnOptions.hasKey("_experiments")) {
263
- options.getExperimental().setSessionReplay(getReplayOptions(rnOptions));
264
- options.getReplayController().setBreadcrumbConverter(new RNSentryReplayBreadcrumbConverter());
265
- }
266
- options.setBeforeSend((event, hint) -> {
87
+ public static final String NAME = "RNSentry";
88
+
89
+ private static final String NATIVE_SDK_NAME = "sentry.native.android.react-native";
90
+ private static final String ANDROID_SDK_NAME = "sentry.java.android.react-native";
91
+ private static final ILogger logger = new AndroidLogger(NAME);
92
+ private static final BuildInfoProvider buildInfo = new BuildInfoProvider(logger);
93
+ private static final String modulesPath = "modules.json";
94
+ private static final Charset UTF_8 = Charset.forName("UTF-8"); // NOPMD - Allow using UTF-8
95
+
96
+ private final ReactApplicationContext reactApplicationContext;
97
+ private final PackageInfo packageInfo;
98
+ private FrameMetricsAggregator frameMetricsAggregator = null;
99
+ private boolean androidXAvailable;
100
+
101
+ private static boolean hasFetchedAppStart;
102
+
103
+ // 700ms to constitute frozen frames.
104
+ private static final int FROZEN_FRAME_THRESHOLD = 700;
105
+ // 16ms (slower than 60fps) to constitute slow frames.
106
+ private static final int SLOW_FRAME_THRESHOLD = 16;
107
+
108
+ private static final int SCREENSHOT_TIMEOUT_SECONDS = 2;
109
+
110
+ /**
111
+ * Profiling traces rate. 101 hz means 101 traces in 1 second. Defaults to 101 to avoid possible
112
+ * lockstep sampling. More on
113
+ * https://stackoverflow.com/questions/45470758/what-is-lockstep-sampling
114
+ */
115
+ private int profilingTracesHz = 101;
116
+
117
+ private AndroidProfiler androidProfiler = null;
118
+
119
+ private boolean isProguardDebugMetaLoaded = false;
120
+ private @Nullable String proguardUuid = null;
121
+ private String cacheDirPath = null;
122
+ private ISentryExecutorService executorService = null;
123
+
124
+ private final @NotNull Runnable emitNewFrameEvent;
125
+
126
+ /** Max trace file size in bytes. */
127
+ private long maxTraceFileSize = 5 * 1024 * 1024;
128
+
129
+ public RNSentryModuleImpl(ReactApplicationContext reactApplicationContext) {
130
+ packageInfo = getPackageInfo(reactApplicationContext);
131
+ this.reactApplicationContext = reactApplicationContext;
132
+ this.emitNewFrameEvent = createEmitNewFrameEvent();
133
+ }
134
+
135
+ private ReactApplicationContext getReactApplicationContext() {
136
+ return this.reactApplicationContext;
137
+ }
138
+
139
+ private @Nullable Activity getCurrentActivity() {
140
+ return this.reactApplicationContext.getCurrentActivity();
141
+ }
142
+
143
+ private @NotNull Runnable createEmitNewFrameEvent() {
144
+ final @NotNull SentryDateProvider dateProvider = new SentryAndroidDateProvider();
145
+
146
+ return () -> {
147
+ final SentryDate endDate = dateProvider.now();
148
+ WritableMap event = Arguments.createMap();
149
+ event.putDouble("newFrameTimestampInSeconds", endDate.nanoTimestamp() / 1e9);
150
+ getReactApplicationContext()
151
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
152
+ .emit("rn_sentry_new_frame", event);
153
+ };
154
+ }
155
+
156
+ private void initFragmentInitialFrameTracking() {
157
+ final RNSentryReactFragmentLifecycleTracer fragmentLifecycleTracer =
158
+ new RNSentryReactFragmentLifecycleTracer(buildInfo, emitNewFrameEvent, logger);
159
+
160
+ final @Nullable FragmentActivity fragmentActivity = (FragmentActivity) getCurrentActivity();
161
+ if (fragmentActivity != null) {
162
+ final @Nullable FragmentManager supportFragmentManager =
163
+ fragmentActivity.getSupportFragmentManager();
164
+ if (supportFragmentManager != null) {
165
+ supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleTracer, true);
166
+ }
167
+ }
168
+ }
169
+
170
+ public void initNativeReactNavigationNewFrameTracking(Promise promise) {
171
+ this.initFragmentInitialFrameTracking();
172
+ }
173
+
174
+ public void initNativeSdk(final ReadableMap rnOptions, Promise promise) {
175
+ SentryAndroid.init(
176
+ this.getReactApplicationContext(),
177
+ options -> {
178
+ @Nullable SdkVersion sdkVersion = options.getSdkVersion();
179
+ if (sdkVersion == null) {
180
+ sdkVersion = new SdkVersion(ANDROID_SDK_NAME, BuildConfig.VERSION_NAME);
181
+ } else {
182
+ sdkVersion.setName(ANDROID_SDK_NAME);
183
+ }
184
+
185
+ options.setSentryClientName(sdkVersion.getName() + "/" + sdkVersion.getVersion());
186
+ options.setNativeSdkName(NATIVE_SDK_NAME);
187
+ options.setSdkVersion(sdkVersion);
188
+
189
+ if (rnOptions.hasKey("debug") && rnOptions.getBoolean("debug")) {
190
+ options.setDebug(true);
191
+ }
192
+ if (rnOptions.hasKey("dsn") && rnOptions.getString("dsn") != null) {
193
+ String dsn = rnOptions.getString("dsn");
194
+ logger.log(SentryLevel.INFO, String.format("Starting with DSN: '%s'", dsn));
195
+ options.setDsn(dsn);
196
+ } else {
197
+ // SentryAndroid needs an empty string fallback for the dsn.
198
+ options.setDsn("");
199
+ }
200
+ if (rnOptions.hasKey("sampleRate")) {
201
+ options.setSampleRate(rnOptions.getDouble("sampleRate"));
202
+ }
203
+ if (rnOptions.hasKey("sendClientReports")) {
204
+ options.setSendClientReports(rnOptions.getBoolean("sendClientReports"));
205
+ }
206
+ if (rnOptions.hasKey("maxBreadcrumbs")) {
207
+ options.setMaxBreadcrumbs(rnOptions.getInt("maxBreadcrumbs"));
208
+ }
209
+ if (rnOptions.hasKey("maxCacheItems")) {
210
+ options.setMaxCacheItems(rnOptions.getInt("maxCacheItems"));
211
+ }
212
+ if (rnOptions.hasKey("environment") && rnOptions.getString("environment") != null) {
213
+ options.setEnvironment(rnOptions.getString("environment"));
214
+ }
215
+ if (rnOptions.hasKey("release") && rnOptions.getString("release") != null) {
216
+ options.setRelease(rnOptions.getString("release"));
217
+ }
218
+ if (rnOptions.hasKey("dist") && rnOptions.getString("dist") != null) {
219
+ options.setDist(rnOptions.getString("dist"));
220
+ }
221
+ if (rnOptions.hasKey("enableAutoSessionTracking")) {
222
+ options.setEnableAutoSessionTracking(rnOptions.getBoolean("enableAutoSessionTracking"));
223
+ }
224
+ if (rnOptions.hasKey("sessionTrackingIntervalMillis")) {
225
+ options.setSessionTrackingIntervalMillis(
226
+ rnOptions.getInt("sessionTrackingIntervalMillis"));
227
+ }
228
+ if (rnOptions.hasKey("shutdownTimeout")) {
229
+ options.setShutdownTimeoutMillis(rnOptions.getInt("shutdownTimeout"));
230
+ }
231
+ if (rnOptions.hasKey("enableNdkScopeSync")) {
232
+ options.setEnableScopeSync(rnOptions.getBoolean("enableNdkScopeSync"));
233
+ }
234
+ if (rnOptions.hasKey("attachStacktrace")) {
235
+ options.setAttachStacktrace(rnOptions.getBoolean("attachStacktrace"));
236
+ }
237
+ if (rnOptions.hasKey("attachThreads")) {
238
+ // JS use top level stacktrace and android attaches Threads which hides them so
239
+ // by default we hide.
240
+ options.setAttachThreads(rnOptions.getBoolean("attachThreads"));
241
+ }
242
+ if (rnOptions.hasKey("attachScreenshot")) {
243
+ options.setAttachScreenshot(rnOptions.getBoolean("attachScreenshot"));
244
+ }
245
+ if (rnOptions.hasKey("attachViewHierarchy")) {
246
+ options.setAttachViewHierarchy(rnOptions.getBoolean("attachViewHierarchy"));
247
+ }
248
+ if (rnOptions.hasKey("sendDefaultPii")) {
249
+ options.setSendDefaultPii(rnOptions.getBoolean("sendDefaultPii"));
250
+ }
251
+ if (rnOptions.hasKey("maxQueueSize")) {
252
+ options.setMaxQueueSize(rnOptions.getInt("maxQueueSize"));
253
+ }
254
+ if (rnOptions.hasKey("enableNdk")) {
255
+ options.setEnableNdk(rnOptions.getBoolean("enableNdk"));
256
+ }
257
+ if (rnOptions.hasKey("_experiments")) {
258
+ options.getExperimental().setSessionReplay(getReplayOptions(rnOptions));
259
+ options
260
+ .getReplayController()
261
+ .setBreadcrumbConverter(new RNSentryReplayBreadcrumbConverter());
262
+ }
263
+ options.setBeforeSend(
264
+ (event, hint) -> {
267
265
  // React native internally throws a JavascriptException
268
266
  // Since we catch it before that, we don't want to send this one
269
267
  // because we would send it twice
270
268
  try {
271
- SentryException ex = event.getExceptions().get(0);
272
- if (null != ex && ex.getType().contains("JavascriptException")) {
273
- return null;
274
- }
275
- } catch (Throwable ignored) {
276
- // We do nothing
269
+ SentryException ex = event.getExceptions().get(0);
270
+ if (null != ex && ex.getType().contains("JavascriptException")) {
271
+ return null;
272
+ }
273
+ } catch (Throwable ignored) { // NOPMD - We don't want to crash in any case
274
+ // We do nothing
277
275
  }
278
276
 
279
277
  setEventOriginTag(event);
280
278
  addPackages(event, options.getSdkVersion());
281
279
 
282
280
  return event;
283
- });
284
-
285
- if (rnOptions.hasKey("enableNativeCrashHandling") && !rnOptions.getBoolean("enableNativeCrashHandling")) {
286
- final List<Integration> integrations = options.getIntegrations();
287
- for (final Integration integration : integrations) {
288
- if (integration instanceof UncaughtExceptionHandlerIntegration
289
- || integration instanceof AnrIntegration || integration instanceof NdkIntegration) {
290
- integrations.remove(integration);
291
- }
292
- }
293
- }
294
- logger.log(SentryLevel.INFO, String.format("Native Integrations '%s'", options.getIntegrations()));
295
-
296
- final CurrentActivityHolder currentActivityHolder = CurrentActivityHolder.getInstance();
297
- final Activity currentActivity = getCurrentActivity();
298
- if (currentActivity != null) {
299
- currentActivityHolder.setActivity(currentActivity);
300
- }
281
+ });
282
+
283
+ if (rnOptions.hasKey("enableNativeCrashHandling")
284
+ && !rnOptions.getBoolean("enableNativeCrashHandling")) {
285
+ final List<Integration> integrations = options.getIntegrations();
286
+ for (final Integration integration : integrations) {
287
+ if (integration instanceof UncaughtExceptionHandlerIntegration
288
+ || integration instanceof AnrIntegration
289
+ || integration instanceof NdkIntegration) {
290
+ integrations.remove(integration);
291
+ }
292
+ }
293
+ }
294
+ logger.log(
295
+ SentryLevel.INFO,
296
+ String.format("Native Integrations '%s'", options.getIntegrations()));
297
+
298
+ final CurrentActivityHolder currentActivityHolder = CurrentActivityHolder.getInstance();
299
+ final Activity currentActivity = getCurrentActivity();
300
+ if (currentActivity != null) {
301
+ currentActivityHolder.setActivity(currentActivity);
302
+ }
301
303
  });
302
304
 
303
- promise.resolve(true);
304
- }
305
-
306
- private SentryReplayOptions getReplayOptions(@NotNull ReadableMap rnOptions) {
307
- @NotNull final SentryReplayOptions androidReplayOptions = new SentryReplayOptions();
308
-
309
- @Nullable final ReadableMap rnExperimentsOptions = rnOptions.getMap("_experiments");
310
- if (rnExperimentsOptions == null) {
311
- return androidReplayOptions;
312
- }
313
-
314
- if (!(rnExperimentsOptions.hasKey("replaysSessionSampleRate") || rnExperimentsOptions.hasKey("replaysOnErrorSampleRate"))) {
315
- return androidReplayOptions;
316
- }
317
-
318
- androidReplayOptions.setSessionSampleRate(rnExperimentsOptions.hasKey("replaysSessionSampleRate")
319
- ? rnExperimentsOptions.getDouble("replaysSessionSampleRate") : null);
320
- androidReplayOptions.setErrorSampleRate(rnExperimentsOptions.hasKey("replaysOnErrorSampleRate")
321
- ? rnExperimentsOptions.getDouble("replaysOnErrorSampleRate") : null);
322
-
323
- if (!rnOptions.hasKey("mobileReplayOptions")) {
324
- return androidReplayOptions;
325
- }
326
- @Nullable final ReadableMap rnMobileReplayOptions = rnOptions.getMap("mobileReplayOptions");
327
- if (rnMobileReplayOptions == null) {
328
- return androidReplayOptions;
329
- }
330
-
331
- androidReplayOptions.setRedactAllText(!rnMobileReplayOptions.hasKey("maskAllText") || rnMobileReplayOptions.getBoolean("maskAllText"));
332
- androidReplayOptions.setRedactAllImages(!rnMobileReplayOptions.hasKey("maskAllImages") || rnMobileReplayOptions.getBoolean("maskAllImages"));
333
-
334
- final boolean redactVectors = !rnMobileReplayOptions.hasKey("maskAllVectors") || rnMobileReplayOptions.getBoolean("maskAllVectors");
335
- if (redactVectors) {
336
- androidReplayOptions.addClassToRedact("com.horcrux.svg.SvgView"); // react-native-svg
337
- }
338
-
339
- return androidReplayOptions;
340
- }
341
-
342
- public void crash() {
343
- throw new RuntimeException("TEST - Sentry Client Crash (only works in release mode)");
344
- }
345
-
346
- public void addListener(String _eventType) {
347
- // Is must be defined otherwise the generated interface from TS won't be fulfilled
348
- logger.log(SentryLevel.ERROR, "addListener of NativeEventEmitter can't be used on Android!");
349
- }
350
-
351
- public void removeListeners(double _id) {
352
- // Is must be defined otherwise the generated interface from TS won't be fulfilled
353
- logger.log(SentryLevel.ERROR, "removeListeners of NativeEventEmitter can't be used on Android!");
354
- }
355
-
356
- public void fetchModules(Promise promise) {
357
- final AssetManager assets = this.getReactApplicationContext().getResources().getAssets();
358
- try (final InputStream stream =
359
- new BufferedInputStream(assets.open(RNSentryModuleImpl.modulesPath))) {
360
- int size = stream.available();
361
- byte[] buffer = new byte[size];
362
- stream.read(buffer);
363
- stream.close();
364
- String modulesJson = new String(buffer, RNSentryModuleImpl.UTF_8);
365
- promise.resolve(modulesJson);
366
- } catch (FileNotFoundException e) {
367
- promise.resolve(null);
368
- } catch (Throwable e) {
369
- logger.log(SentryLevel.WARNING, "Fetching JS Modules failed.");
370
- promise.resolve(null);
371
- }
372
- }
373
-
374
- public void fetchNativeRelease(Promise promise) {
375
- WritableMap release = Arguments.createMap();
376
- release.putString("id", packageInfo.packageName);
377
- release.putString("version", packageInfo.versionName);
378
- release.putString("build", String.valueOf(packageInfo.versionCode));
379
- promise.resolve(release);
380
- }
381
-
382
- public void fetchNativeAppStart(Promise promise) {
383
- final Map<String, Object> measurement = InternalSentrySdk.getAppStartMeasurement();
384
-
385
- WritableMap mutableMeasurement = (WritableMap) RNSentryMapConverter.convertToWritable(measurement);
386
- mutableMeasurement.putBoolean("has_fetched", hasFetchedAppStart);
387
-
388
- // This is always set to true, as we would only allow an app start fetch to only
389
- // happen once in the case of a JS bundle reload, we do not want it to be
390
- // instrumented again.
391
- hasFetchedAppStart = true;
392
-
393
- promise.resolve(mutableMeasurement);
394
- }
395
-
396
- /**
397
- * Returns frames metrics at the current point in time.
398
- */
399
- public void fetchNativeFrames(Promise promise) {
400
- if (!isFrameMetricsAggregatorAvailable()) {
401
- promise.resolve(null);
402
- } else {
403
- try {
404
- int totalFrames = 0;
405
- int slowFrames = 0;
406
- int frozenFrames = 0;
407
-
408
- final SparseIntArray[] framesRates = frameMetricsAggregator.getMetrics();
409
-
410
- if (framesRates != null) {
411
- final SparseIntArray totalIndexArray = framesRates[FrameMetricsAggregator.TOTAL_INDEX];
412
- if (totalIndexArray != null) {
413
- for (int i = 0; i < totalIndexArray.size(); i++) {
414
- int frameTime = totalIndexArray.keyAt(i);
415
- int numFrames = totalIndexArray.valueAt(i);
416
- totalFrames += numFrames;
417
- // hard coded values, its also in the official android docs and frame metrics
418
- // API
419
- if (frameTime > FROZEN_FRAME_THRESHOLD) {
420
- // frozen frames, threshold is 700ms
421
- frozenFrames += numFrames;
422
- } else if (frameTime > SLOW_FRAME_THRESHOLD) {
423
- // slow frames, above 16ms, 60 frames/second
424
- slowFrames += numFrames;
425
- }
426
- }
427
- }
428
- }
429
-
430
- WritableMap map = Arguments.createMap();
431
- map.putInt("totalFrames", totalFrames);
432
- map.putInt("slowFrames", slowFrames);
433
- map.putInt("frozenFrames", frozenFrames);
434
-
435
- promise.resolve(map);
436
- } catch (Throwable ignored) {
437
- logger.log(SentryLevel.WARNING, "Error fetching native frames.");
438
- promise.resolve(null);
439
- }
440
- }
441
- }
442
-
443
- public void captureReplay(boolean isHardCrash, Promise promise) {
444
- Sentry.getCurrentHub().getOptions().getReplayController().captureReplay(isHardCrash);
445
- promise.resolve(getCurrentReplayId());
446
- }
447
-
448
- public @Nullable String getCurrentReplayId() {
449
- final @Nullable IScope scope = InternalSentrySdk.getCurrentScope();
450
- if (scope == null) {
451
- return null;
452
- }
453
-
454
- final @NotNull SentryId id = scope.getReplayId();
455
- if (id == SentryId.EMPTY_ID) {
456
- return null;
457
- }
458
- return id.toString();
459
- }
460
-
461
- public void captureEnvelope(String rawBytes, ReadableMap options, Promise promise) {
462
- byte[] bytes = Base64.decode(rawBytes, Base64.DEFAULT);
463
-
464
- try {
465
- InternalSentrySdk.captureEnvelope(bytes, !options.hasKey("hardCrashed") || !options.getBoolean("hardCrashed"));
466
- } catch (Throwable e) {
467
- logger.log(SentryLevel.ERROR, "Error while capturing envelope");
468
- promise.resolve(false);
469
- }
470
- promise.resolve(true);
471
- }
472
-
473
- public void captureScreenshot(Promise promise) {
474
-
475
- final Activity activity = getCurrentActivity();
476
- if (activity == null) {
477
- logger.log(SentryLevel.WARNING, "CurrentActivity is null, can't capture screenshot.");
478
- promise.resolve(null);
479
- return;
480
- }
481
-
482
- final byte[] raw = takeScreenshotOnUiThread(activity);
483
-
484
- if (raw == null) {
485
- logger.log(SentryLevel.WARNING, "Screenshot is null, screen was not captured.");
486
- promise.resolve(null);
487
- return;
488
- }
489
-
490
- final WritableNativeArray data = new WritableNativeArray();
491
- for (final byte b : raw) {
492
- data.pushInt(b);
493
- }
494
- final WritableMap screenshot = new WritableNativeMap();
495
- screenshot.putString("contentType", "image/png");
496
- screenshot.putArray("data", data);
497
- screenshot.putString("filename", "screenshot.png");
498
-
499
- final WritableArray screenshotsArray = new WritableNativeArray();
500
- screenshotsArray.pushMap(screenshot);
501
- promise.resolve(screenshotsArray);
502
- }
503
-
504
- private static byte[] takeScreenshotOnUiThread(Activity activity) {
505
- CountDownLatch doneSignal = new CountDownLatch(1);
506
- final byte[][] bytesWrapper = {{}}; // wrapper to be able to set the value in the runnable
507
- final Runnable runTakeScreenshot = () -> {
508
- bytesWrapper[0] = takeScreenshot(activity, logger, buildInfo);
509
- doneSignal.countDown();
305
+ promise.resolve(true);
306
+ }
307
+
308
+ private SentryReplayOptions getReplayOptions(@NotNull ReadableMap rnOptions) {
309
+ @NotNull final SentryReplayOptions androidReplayOptions = new SentryReplayOptions(false);
310
+
311
+ @Nullable final ReadableMap rnExperimentsOptions = rnOptions.getMap("_experiments");
312
+ if (rnExperimentsOptions == null) {
313
+ return androidReplayOptions;
314
+ }
315
+
316
+ if (!(rnExperimentsOptions.hasKey("replaysSessionSampleRate")
317
+ || rnExperimentsOptions.hasKey("replaysOnErrorSampleRate"))) {
318
+ return androidReplayOptions;
319
+ }
320
+
321
+ androidReplayOptions.setSessionSampleRate(
322
+ rnExperimentsOptions.hasKey("replaysSessionSampleRate")
323
+ ? rnExperimentsOptions.getDouble("replaysSessionSampleRate")
324
+ : null);
325
+ androidReplayOptions.setOnErrorSampleRate(
326
+ rnExperimentsOptions.hasKey("replaysOnErrorSampleRate")
327
+ ? rnExperimentsOptions.getDouble("replaysOnErrorSampleRate")
328
+ : null);
329
+
330
+ if (!rnOptions.hasKey("mobileReplayOptions")) {
331
+ return androidReplayOptions;
332
+ }
333
+ @Nullable final ReadableMap rnMobileReplayOptions = rnOptions.getMap("mobileReplayOptions");
334
+ if (rnMobileReplayOptions == null) {
335
+ return androidReplayOptions;
336
+ }
337
+
338
+ androidReplayOptions.setMaskAllText(
339
+ !rnMobileReplayOptions.hasKey("maskAllText")
340
+ || rnMobileReplayOptions.getBoolean("maskAllText"));
341
+ androidReplayOptions.setMaskAllImages(
342
+ !rnMobileReplayOptions.hasKey("maskAllImages")
343
+ || rnMobileReplayOptions.getBoolean("maskAllImages"));
344
+
345
+ final boolean redactVectors =
346
+ !rnMobileReplayOptions.hasKey("maskAllVectors")
347
+ || rnMobileReplayOptions.getBoolean("maskAllVectors");
348
+ if (redactVectors) {
349
+ androidReplayOptions.addMaskViewClass("com.horcrux.svg.SvgView"); // react-native-svg
350
+ }
351
+
352
+ return androidReplayOptions;
353
+ }
354
+
355
+ public void crash() {
356
+ throw new RuntimeException("TEST - Sentry Client Crash (only works in release mode)");
357
+ }
358
+
359
+ public void addListener(String eventType) {
360
+ // Is must be defined otherwise the generated interface from TS won't be fulfilled
361
+ logger.log(SentryLevel.ERROR, "addListener of NativeEventEmitter can't be used on Android!");
362
+ }
363
+
364
+ public void removeListeners(double id) {
365
+ // Is must be defined otherwise the generated interface from TS won't be fulfilled
366
+ logger.log(
367
+ SentryLevel.ERROR, "removeListeners of NativeEventEmitter can't be used on Android!");
368
+ }
369
+
370
+ public void fetchModules(Promise promise) {
371
+ final AssetManager assets = this.getReactApplicationContext().getResources().getAssets();
372
+ try (InputStream stream = new BufferedInputStream(assets.open(modulesPath))) {
373
+ int size = stream.available();
374
+ byte[] buffer = new byte[size];
375
+ stream.read(buffer);
376
+ stream.close();
377
+ String modulesJson = new String(buffer, UTF_8);
378
+ promise.resolve(modulesJson);
379
+ } catch (FileNotFoundException e) {
380
+ promise.resolve(null);
381
+ } catch (Throwable e) { // NOPMD - We don't want to crash in any case
382
+ logger.log(SentryLevel.WARNING, "Fetching JS Modules failed.");
383
+ promise.resolve(null);
384
+ }
385
+ }
386
+
387
+ public void fetchNativeRelease(Promise promise) {
388
+ WritableMap release = Arguments.createMap();
389
+ release.putString("id", packageInfo.packageName);
390
+ release.putString("version", packageInfo.versionName);
391
+ release.putString("build", String.valueOf(packageInfo.versionCode));
392
+ promise.resolve(release);
393
+ }
394
+
395
+ public void fetchNativeAppStart(Promise promise) {
396
+ fetchNativeAppStart(
397
+ promise,
398
+ InternalSentrySdk.getAppStartMeasurement(),
399
+ logger,
400
+ AppStartMetrics.getInstance().isAppLaunchedInForeground());
401
+ }
402
+
403
+ protected void fetchNativeAppStart(
404
+ Promise promise,
405
+ final Map<String, Object> appStartMeasurement,
406
+ ILogger logger,
407
+ boolean isAppLaunchedInForeground) {
408
+ if (!isAppLaunchedInForeground) {
409
+ logger.log(SentryLevel.WARNING, "Invalid app start data: app not launched in foreground.");
410
+ promise.resolve(null);
411
+ return;
412
+ }
413
+
414
+ WritableMap mutableMeasurement =
415
+ (WritableMap) RNSentryMapConverter.convertToWritable(appStartMeasurement);
416
+ mutableMeasurement.putBoolean("has_fetched", hasFetchedAppStart);
417
+
418
+ // This is always set to true, as we would only allow an app start fetch to only
419
+ // happen once in the case of a JS bundle reload, we do not want it to be
420
+ // instrumented again.
421
+ hasFetchedAppStart = true;
422
+
423
+ promise.resolve(mutableMeasurement);
424
+ }
425
+
426
+ /** Returns frames metrics at the current point in time. */
427
+ public void fetchNativeFrames(Promise promise) {
428
+ if (!isFrameMetricsAggregatorAvailable()) {
429
+ promise.resolve(null);
430
+ } else {
431
+ try {
432
+ int totalFrames = 0;
433
+ int slowFrames = 0;
434
+ int frozenFrames = 0;
435
+
436
+ final SparseIntArray[] framesRates = frameMetricsAggregator.getMetrics();
437
+
438
+ if (framesRates != null) {
439
+ final SparseIntArray totalIndexArray = framesRates[FrameMetricsAggregator.TOTAL_INDEX];
440
+ if (totalIndexArray != null) {
441
+ for (int i = 0; i < totalIndexArray.size(); i++) {
442
+ int frameTime = totalIndexArray.keyAt(i);
443
+ int numFrames = totalIndexArray.valueAt(i);
444
+ totalFrames += numFrames;
445
+ // hard coded values, its also in the official android docs and frame metrics
446
+ // API
447
+ if (frameTime > FROZEN_FRAME_THRESHOLD) {
448
+ // frozen frames, threshold is 700ms
449
+ frozenFrames += numFrames;
450
+ } else if (frameTime > SLOW_FRAME_THRESHOLD) {
451
+ // slow frames, above 16ms, 60 frames/second
452
+ slowFrames += numFrames;
453
+ }
454
+ }
455
+ }
456
+ }
457
+
458
+ WritableMap map = Arguments.createMap();
459
+ map.putInt("totalFrames", totalFrames);
460
+ map.putInt("slowFrames", slowFrames);
461
+ map.putInt("frozenFrames", frozenFrames);
462
+
463
+ promise.resolve(map);
464
+ } catch (Throwable ignored) { // NOPMD - We don't want to crash in any case
465
+ logger.log(SentryLevel.WARNING, "Error fetching native frames.");
466
+ promise.resolve(null);
467
+ }
468
+ }
469
+ }
470
+
471
+ public void captureReplay(boolean isHardCrash, Promise promise) {
472
+ Sentry.getCurrentHub().getOptions().getReplayController().captureReplay(isHardCrash);
473
+ promise.resolve(getCurrentReplayId());
474
+ }
475
+
476
+ public @Nullable String getCurrentReplayId() {
477
+ final @Nullable IScope scope = InternalSentrySdk.getCurrentScope();
478
+ if (scope == null) {
479
+ return null;
480
+ }
481
+
482
+ final @NotNull SentryId id = scope.getReplayId();
483
+ if (id == SentryId.EMPTY_ID) {
484
+ return null;
485
+ }
486
+ return id.toString();
487
+ }
488
+
489
+ public void captureEnvelope(String rawBytes, ReadableMap options, Promise promise) {
490
+ byte[] bytes = Base64.decode(rawBytes, Base64.DEFAULT);
491
+
492
+ try {
493
+ InternalSentrySdk.captureEnvelope(
494
+ bytes, !options.hasKey("hardCrashed") || !options.getBoolean("hardCrashed"));
495
+ } catch (Throwable e) { // NOPMD - We don't want to crash in any case
496
+ logger.log(SentryLevel.ERROR, "Error while capturing envelope");
497
+ promise.resolve(false);
498
+ }
499
+ promise.resolve(true);
500
+ }
501
+
502
+ public void captureScreenshot(Promise promise) {
503
+
504
+ final Activity activity = getCurrentActivity();
505
+ if (activity == null) {
506
+ logger.log(SentryLevel.WARNING, "CurrentActivity is null, can't capture screenshot.");
507
+ promise.resolve(null);
508
+ return;
509
+ }
510
+
511
+ final byte[] raw = takeScreenshotOnUiThread(activity);
512
+
513
+ if (raw == null || raw.length == 0) {
514
+ logger.log(SentryLevel.WARNING, "Screenshot is null, screen was not captured.");
515
+ promise.resolve(null);
516
+ return;
517
+ }
518
+
519
+ final WritableNativeArray data = new WritableNativeArray();
520
+ for (final byte b : raw) {
521
+ data.pushInt(b);
522
+ }
523
+ final WritableMap screenshot = new WritableNativeMap();
524
+ screenshot.putString("contentType", "image/png");
525
+ screenshot.putArray("data", data);
526
+ screenshot.putString("filename", "screenshot.png");
527
+
528
+ final WritableArray screenshotsArray = new WritableNativeArray();
529
+ screenshotsArray.pushMap(screenshot);
530
+ promise.resolve(screenshotsArray);
531
+ }
532
+
533
+ private static byte[] takeScreenshotOnUiThread(Activity activity) {
534
+ CountDownLatch doneSignal = new CountDownLatch(1);
535
+ final byte[][] bytesWrapper = {{}}; // wrapper to be able to set the value in the runnable
536
+ final Runnable runTakeScreenshot =
537
+ () -> {
538
+ bytesWrapper[0] = takeScreenshot(activity, logger, buildInfo);
539
+ doneSignal.countDown();
510
540
  };
511
541
 
512
- if (UiThreadUtil.isOnUiThread()) {
513
- runTakeScreenshot.run();
514
- } else {
515
- UiThreadUtil.runOnUiThread(runTakeScreenshot);
516
- }
517
-
518
- try {
519
- doneSignal.await(SCREENSHOT_TIMEOUT_SECONDS, SECONDS);
520
- } catch (InterruptedException e) {
521
- logger.log(SentryLevel.ERROR, "Screenshot process was interrupted.");
522
- return null;
523
- }
524
-
525
- return bytesWrapper[0];
526
- }
527
-
528
- public void fetchViewHierarchy(Promise promise) {
529
- final @Nullable Activity activity = getCurrentActivity();
530
- final @Nullable ViewHierarchy viewHierarchy = ViewHierarchyEventProcessor.snapshotViewHierarchy(activity, logger);
531
- if (viewHierarchy == null) {
532
- logger.log(SentryLevel.ERROR, "Could not get ViewHierarchy.");
533
- promise.resolve(null);
534
- return;
535
- }
536
-
537
- ISerializer serializer = HubAdapter.getInstance().getOptions().getSerializer();
538
- final @Nullable byte[] bytes = JsonSerializationUtils.bytesFrom(serializer, logger, viewHierarchy);
539
- if (bytes == null) {
540
- logger.log(SentryLevel.ERROR, "Could not serialize ViewHierarchy.");
541
- promise.resolve(null);
542
- return;
543
- }
544
- if (bytes.length < 1) {
545
- logger.log(SentryLevel.ERROR, "Got empty bytes array after serializing ViewHierarchy.");
546
- promise.resolve(null);
547
- return;
548
- }
549
-
550
- final WritableNativeArray data = new WritableNativeArray();
551
- for (final byte b : bytes) {
552
- data.pushInt(b);
553
- }
554
- promise.resolve(data);
555
- }
556
-
557
- private static PackageInfo getPackageInfo(Context ctx) {
558
- try {
559
- return ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), 0);
560
- } catch (PackageManager.NameNotFoundException e) {
561
- logger.log(SentryLevel.WARNING, "Error getting package info.");
562
- return null;
563
- }
564
- }
565
-
566
- public void setUser(final ReadableMap userKeys, final ReadableMap userDataKeys) {
567
- Sentry.configureScope(scope -> {
568
- if (userKeys == null && userDataKeys == null) {
569
- scope.setUser(null);
570
- } else {
571
- User userInstance = new User();
572
-
573
- if (userKeys != null) {
574
- if (userKeys.hasKey("email")) {
575
- userInstance.setEmail(userKeys.getString("email"));
576
- }
577
-
578
- if (userKeys.hasKey("id")) {
579
- userInstance.setId(userKeys.getString("id"));
580
- }
581
-
582
- if (userKeys.hasKey("username")) {
583
- userInstance.setUsername(userKeys.getString("username"));
584
- }
585
-
586
- if (userKeys.hasKey("ip_address")) {
587
- userInstance.setIpAddress(userKeys.getString("ip_address"));
588
- }
589
-
590
- if (userKeys.hasKey("segment")) {
591
- userInstance.setSegment(userKeys.getString("segment"));
592
- }
542
+ if (UiThreadUtil.isOnUiThread()) {
543
+ runTakeScreenshot.run();
544
+ } else {
545
+ UiThreadUtil.runOnUiThread(runTakeScreenshot);
546
+ }
547
+
548
+ try {
549
+ doneSignal.await(SCREENSHOT_TIMEOUT_SECONDS, SECONDS);
550
+ } catch (InterruptedException e) {
551
+ logger.log(SentryLevel.ERROR, "Screenshot process was interrupted.");
552
+ return new byte[0];
553
+ }
554
+
555
+ return bytesWrapper[0];
556
+ }
557
+
558
+ public void fetchViewHierarchy(Promise promise) {
559
+ final @Nullable Activity activity = getCurrentActivity();
560
+ final @Nullable ViewHierarchy viewHierarchy =
561
+ ViewHierarchyEventProcessor.snapshotViewHierarchy(activity, logger);
562
+ if (viewHierarchy == null) {
563
+ logger.log(SentryLevel.ERROR, "Could not get ViewHierarchy.");
564
+ promise.resolve(null);
565
+ return;
566
+ }
567
+
568
+ ISerializer serializer = HubAdapter.getInstance().getOptions().getSerializer();
569
+ final @Nullable byte[] bytes =
570
+ JsonSerializationUtils.bytesFrom(serializer, logger, viewHierarchy);
571
+ if (bytes == null) {
572
+ logger.log(SentryLevel.ERROR, "Could not serialize ViewHierarchy.");
573
+ promise.resolve(null);
574
+ return;
575
+ }
576
+ if (bytes.length < 1) {
577
+ logger.log(SentryLevel.ERROR, "Got empty bytes array after serializing ViewHierarchy.");
578
+ promise.resolve(null);
579
+ return;
580
+ }
581
+
582
+ final WritableNativeArray data = new WritableNativeArray();
583
+ for (final byte b : bytes) {
584
+ data.pushInt(b);
585
+ }
586
+ promise.resolve(data);
587
+ }
588
+
589
+ private static PackageInfo getPackageInfo(Context ctx) {
590
+ try {
591
+ return ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), 0);
592
+ } catch (PackageManager.NameNotFoundException e) {
593
+ logger.log(SentryLevel.WARNING, "Error getting package info.");
594
+ return null;
595
+ }
596
+ }
597
+
598
+ public void setUser(final ReadableMap userKeys, final ReadableMap userDataKeys) {
599
+ Sentry.configureScope(
600
+ scope -> {
601
+ if (userKeys == null && userDataKeys == null) {
602
+ scope.setUser(null);
603
+ } else {
604
+ User userInstance = new User();
605
+
606
+ if (userKeys != null) {
607
+ if (userKeys.hasKey("email")) {
608
+ userInstance.setEmail(userKeys.getString("email"));
609
+ }
610
+
611
+ if (userKeys.hasKey("id")) {
612
+ userInstance.setId(userKeys.getString("id"));
613
+ }
614
+
615
+ if (userKeys.hasKey("username")) {
616
+ userInstance.setUsername(userKeys.getString("username"));
617
+ }
618
+
619
+ if (userKeys.hasKey("ip_address")) {
620
+ userInstance.setIpAddress(userKeys.getString("ip_address"));
621
+ }
622
+
623
+ if (userKeys.hasKey("segment")) {
624
+ userInstance.setSegment(userKeys.getString("segment"));
625
+ }
626
+ }
627
+
628
+ if (userDataKeys != null) {
629
+ Map<String, String> userDataMap = new HashMap<>();
630
+ ReadableMapKeySetIterator it = userDataKeys.keySetIterator();
631
+ while (it.hasNextKey()) {
632
+ String key = it.nextKey();
633
+ String value = userDataKeys.getString(key);
634
+
635
+ // other is ConcurrentHashMap and can't have null values
636
+ if (value != null) {
637
+ userDataMap.put(key, value);
593
638
  }
639
+ }
594
640
 
595
- if (userDataKeys != null) {
596
- HashMap<String, String> userDataMap = new HashMap<>();
597
- ReadableMapKeySetIterator it = userDataKeys.keySetIterator();
598
- while (it.hasNextKey()) {
599
- String key = it.nextKey();
600
- String value = userDataKeys.getString(key);
601
-
602
- // other is ConcurrentHashMap and can't have null values
603
- if (value != null) {
604
- userDataMap.put(key, value);
605
- }
606
- }
607
-
608
- userInstance.setData(userDataMap);
609
- }
610
-
611
- scope.setUser(userInstance);
641
+ userInstance.setData(userDataMap);
612
642
  }
643
+
644
+ scope.setUser(userInstance);
645
+ }
613
646
  });
614
- }
647
+ }
615
648
 
616
- public void addBreadcrumb(final ReadableMap breadcrumb) {
617
- Sentry.configureScope(scope -> {
618
- scope.addBreadcrumb(RNSentryBreadcrumb.fromMap(breadcrumb));
649
+ public void addBreadcrumb(final ReadableMap breadcrumb) {
650
+ Sentry.configureScope(
651
+ scope -> {
652
+ scope.addBreadcrumb(RNSentryBreadcrumb.fromMap(breadcrumb));
619
653
 
620
- final @Nullable String screen = RNSentryBreadcrumb.getCurrentScreenFrom(breadcrumb);
621
- if (screen != null) {
622
- scope.setScreen(screen);
623
- }
654
+ final @Nullable String screen = RNSentryBreadcrumb.getCurrentScreenFrom(breadcrumb);
655
+ if (screen != null) {
656
+ scope.setScreen(screen);
657
+ }
624
658
  });
625
- }
659
+ }
626
660
 
627
- public void clearBreadcrumbs() {
628
- Sentry.configureScope(scope -> {
629
- scope.clearBreadcrumbs();
661
+ public void clearBreadcrumbs() {
662
+ Sentry.configureScope(
663
+ scope -> {
664
+ scope.clearBreadcrumbs();
630
665
  });
666
+ }
667
+
668
+ public void setExtra(String key, String extra) {
669
+ if (key == null || extra == null) {
670
+ logger.log(
671
+ SentryLevel.ERROR,
672
+ "RNSentry.setExtra called with null key or value, can't change extra.");
673
+ return;
631
674
  }
632
675
 
633
- public void setExtra(String key, String extra) {
634
- Sentry.configureScope(scope -> {
635
- scope.setExtra(key, extra);
676
+ Sentry.configureScope(
677
+ scope -> {
678
+ scope.setExtra(key, extra);
636
679
  });
680
+ }
681
+
682
+ public void setContext(final String key, final ReadableMap context) {
683
+ if (key == null) {
684
+ logger.log(
685
+ SentryLevel.ERROR, "RNSentry.setContext called with null key, can't change context.");
686
+ return;
637
687
  }
638
688
 
639
- public void setContext(final String key, final ReadableMap context) {
640
- if (key == null || context == null) {
689
+ Sentry.configureScope(
690
+ scope -> {
691
+ if (context == null) {
692
+ scope.removeContexts(key);
641
693
  return;
642
- }
643
- Sentry.configureScope(scope -> {
644
- final HashMap<String, Object> contextHashMap = context.toHashMap();
694
+ }
645
695
 
646
- scope.setContexts(key, contextHashMap);
696
+ final Map<String, Object> contextHashMap = context.toHashMap();
697
+ scope.setContexts(key, contextHashMap);
647
698
  });
648
- }
699
+ }
649
700
 
650
- public void setTag(String key, String value) {
651
- Sentry.configureScope(scope -> {
652
- scope.setTag(key, value);
701
+ public void setTag(String key, String value) {
702
+ Sentry.configureScope(
703
+ scope -> {
704
+ scope.setTag(key, value);
653
705
  });
654
- }
706
+ }
655
707
 
656
- public void closeNativeSdk(Promise promise) {
657
- Sentry.close();
708
+ public void closeNativeSdk(Promise promise) {
709
+ Sentry.close();
658
710
 
659
- disableNativeFramesTracking();
660
-
661
- promise.resolve(true);
662
- }
711
+ disableNativeFramesTracking();
663
712
 
664
- public void enableNativeFramesTracking() {
665
- androidXAvailable = checkAndroidXAvailability();
713
+ promise.resolve(true);
714
+ }
666
715
 
667
- if (androidXAvailable) {
668
- frameMetricsAggregator = new FrameMetricsAggregator();
669
- final Activity currentActivity = getCurrentActivity();
716
+ public void enableNativeFramesTracking() {
717
+ androidXAvailable = checkAndroidXAvailability();
670
718
 
671
- if (frameMetricsAggregator != null && currentActivity != null) {
672
- try {
673
- frameMetricsAggregator.add(currentActivity);
674
-
675
- logger.log(SentryLevel.INFO, "FrameMetricsAggregator installed.");
676
- } catch (Throwable ignored) {
677
- // throws ConcurrentModification when calling addOnFrameMetricsAvailableListener
678
- // this is a best effort since we can't reproduce it
679
- logger.log(SentryLevel.ERROR, "Error adding Activity to frameMetricsAggregator.");
680
- }
681
- } else {
682
- logger.log(SentryLevel.INFO, "currentActivity isn't available.");
683
- }
684
- } else {
685
- logger.log(SentryLevel.WARNING, "androidx.core' isn't available as a dependency.");
686
- }
687
- }
688
-
689
- public void disableNativeFramesTracking() {
690
- if (isFrameMetricsAggregatorAvailable()) {
691
- frameMetricsAggregator.stop();
692
- frameMetricsAggregator = null;
693
- }
694
- }
695
-
696
- private String getProfilingTracesDirPath() {
697
- if (cacheDirPath == null) {
698
- cacheDirPath = new File(getReactApplicationContext().getCacheDir(), "sentry/react").getAbsolutePath();
699
- }
700
- File profilingTraceDir = new File(cacheDirPath, "profiling_trace");
701
- profilingTraceDir.mkdirs();
702
- return profilingTraceDir.getAbsolutePath();
703
- }
704
-
705
- private void initializeAndroidProfiler() {
706
- if (executorService == null) {
707
- executorService = new SentryExecutorService();
708
- }
709
- final String tracesFilesDirPath = getProfilingTracesDirPath();
710
-
711
- androidProfiler = new AndroidProfiler(
712
- tracesFilesDirPath,
713
- (int) SECONDS.toMicros(1) / profilingTracesHz,
714
- new SentryFrameMetricsCollector(reactApplicationContext, logger, buildInfo),
715
- executorService,
716
- logger,
717
- buildInfo
718
- );
719
- }
720
-
721
- public WritableMap startProfiling(boolean platformProfilers) {
722
- final WritableMap result = new WritableNativeMap();
723
- if (androidProfiler == null && platformProfilers) {
724
- initializeAndroidProfiler();
725
- }
719
+ if (androidXAvailable) {
720
+ frameMetricsAggregator = new FrameMetricsAggregator();
721
+ final Activity currentActivity = getCurrentActivity();
726
722
 
723
+ if (frameMetricsAggregator != null && currentActivity != null) {
727
724
  try {
728
- HermesSamplingProfiler.enable();
729
- if (androidProfiler != null) {
730
- androidProfiler.start();
731
- }
732
-
733
- result.putBoolean("started", true);
734
- } catch (Throwable e) {
735
- result.putBoolean("started", false);
736
- result.putString("error", e.toString());
737
- }
738
- return result;
739
- }
740
-
741
- public WritableMap stopProfiling() {
742
- final boolean isDebug = HubAdapter.getInstance().getOptions().isDebug();
743
- final WritableMap result = new WritableNativeMap();
744
- File output = null;
725
+ frameMetricsAggregator.add(currentActivity);
726
+
727
+ logger.log(SentryLevel.INFO, "FrameMetricsAggregator installed.");
728
+ } catch (Throwable ignored) { // NOPMD - We don't want to crash in any case
729
+ // throws ConcurrentModification when calling addOnFrameMetricsAvailableListener
730
+ // this is a best effort since we can't reproduce it
731
+ logger.log(SentryLevel.ERROR, "Error adding Activity to frameMetricsAggregator.");
732
+ }
733
+ } else {
734
+ logger.log(SentryLevel.INFO, "currentActivity isn't available.");
735
+ }
736
+ } else {
737
+ logger.log(SentryLevel.WARNING, "androidx.core' isn't available as a dependency.");
738
+ }
739
+ }
740
+
741
+ public void disableNativeFramesTracking() {
742
+ if (isFrameMetricsAggregatorAvailable()) {
743
+ frameMetricsAggregator.stop();
744
+ frameMetricsAggregator = null;
745
+ }
746
+ }
747
+
748
+ private String getProfilingTracesDirPath() {
749
+ if (cacheDirPath == null) {
750
+ cacheDirPath =
751
+ new File(getReactApplicationContext().getCacheDir(), "sentry/react").getAbsolutePath();
752
+ }
753
+ File profilingTraceDir = new File(cacheDirPath, "profiling_trace");
754
+ profilingTraceDir.mkdirs();
755
+ return profilingTraceDir.getAbsolutePath();
756
+ }
757
+
758
+ private void initializeAndroidProfiler() {
759
+ if (executorService == null) {
760
+ executorService = new SentryExecutorService();
761
+ }
762
+ final String tracesFilesDirPath = getProfilingTracesDirPath();
763
+
764
+ androidProfiler =
765
+ new AndroidProfiler(
766
+ tracesFilesDirPath,
767
+ (int) SECONDS.toMicros(1) / profilingTracesHz,
768
+ new SentryFrameMetricsCollector(reactApplicationContext, logger, buildInfo),
769
+ executorService,
770
+ logger,
771
+ buildInfo);
772
+ }
773
+
774
+ public WritableMap startProfiling(boolean platformProfilers) {
775
+ final WritableMap result = new WritableNativeMap();
776
+ if (androidProfiler == null && platformProfilers) {
777
+ initializeAndroidProfiler();
778
+ }
779
+
780
+ try {
781
+ HermesSamplingProfiler.enable();
782
+ if (androidProfiler != null) {
783
+ androidProfiler.start();
784
+ }
785
+
786
+ result.putBoolean("started", true);
787
+ } catch (Throwable e) { // NOPMD - We don't want to crash in any case
788
+ result.putBoolean("started", false);
789
+ result.putString("error", e.toString());
790
+ }
791
+ return result;
792
+ }
793
+
794
+ public WritableMap stopProfiling() {
795
+ final boolean isDebug = HubAdapter.getInstance().getOptions().isDebug();
796
+ final WritableMap result = new WritableNativeMap();
797
+ File output = null;
798
+ try {
799
+ AndroidProfiler.ProfileEndData end = null;
800
+ if (androidProfiler != null) {
801
+ end = androidProfiler.endAndCollect(false, null);
802
+ }
803
+ HermesSamplingProfiler.disable();
804
+
805
+ output =
806
+ File.createTempFile(
807
+ "sampling-profiler-trace", ".cpuprofile", reactApplicationContext.getCacheDir());
808
+ if (isDebug) {
809
+ logger.log(SentryLevel.INFO, "Profile saved to: " + output.getAbsolutePath());
810
+ }
811
+
812
+ HermesSamplingProfiler.dumpSampledTraceToFile(output.getPath());
813
+ result.putString("profile", readStringFromFile(output));
814
+
815
+ if (end != null) {
816
+ WritableMap androidProfile = new WritableNativeMap();
817
+ byte[] androidProfileBytes =
818
+ FileUtils.readBytesFromFile(end.traceFile.getPath(), maxTraceFileSize);
819
+ String base64AndroidProfile =
820
+ Base64.encodeToString(androidProfileBytes, NO_WRAP | NO_PADDING);
821
+
822
+ androidProfile.putString("sampled_profile", base64AndroidProfile);
823
+ androidProfile.putInt("android_api_level", buildInfo.getSdkInfoVersion());
824
+ androidProfile.putString("build_id", getProguardUuid());
825
+ result.putMap("androidProfile", androidProfile);
826
+ }
827
+ } catch (Throwable e) { // NOPMD - We don't want to crash in any case
828
+ result.putString("error", e.toString());
829
+ } finally {
830
+ if (output != null) {
745
831
  try {
746
- AndroidProfiler.ProfileEndData end = null;
747
- if (androidProfiler != null) {
748
- end = androidProfiler.endAndCollect(false, null);
749
- }
750
- HermesSamplingProfiler.disable();
751
-
752
- output = File.createTempFile(
753
- "sampling-profiler-trace", ".cpuprofile", reactApplicationContext.getCacheDir());
754
- if (isDebug) {
755
- logger.log(SentryLevel.INFO, "Profile saved to: " + output.getAbsolutePath());
756
- }
757
-
758
- HermesSamplingProfiler.dumpSampledTraceToFile(output.getPath());
759
- result.putString("profile", readStringFromFile(output));
760
-
761
- if (end != null) {
762
- WritableMap androidProfile = new WritableNativeMap();
763
- byte[] androidProfileBytes = FileUtils.readBytesFromFile(end.traceFile.getPath(), maxTraceFileSize);
764
- String base64AndroidProfile = Base64.encodeToString(androidProfileBytes, NO_WRAP | NO_PADDING);
765
-
766
- androidProfile.putString("sampled_profile", base64AndroidProfile);
767
- androidProfile.putInt("android_api_level", buildInfo.getSdkInfoVersion());
768
- androidProfile.putString("build_id", getProguardUuid());
769
- result.putMap("androidProfile", androidProfile);
770
- }
771
- } catch (Throwable e) {
772
- result.putString("error", e.toString());
773
- } finally {
774
- if (output != null) {
775
- try {
776
- final boolean wasProfileSuccessfullyDeleted = output.delete();
777
- if (!wasProfileSuccessfullyDeleted) {
778
- logger.log(SentryLevel.WARNING, "Profile not deleted from:" + output.getAbsolutePath());
779
- }
780
- } catch (Throwable e) {
781
- logger.log(SentryLevel.WARNING, "Profile not deleted from:" + output.getAbsolutePath());
782
- }
783
- }
784
- }
785
- return result;
786
- }
787
-
788
- private @Nullable String getProguardUuid() {
789
- if (isProguardDebugMetaLoaded) {
790
- return proguardUuid;
791
- }
792
- isProguardDebugMetaLoaded = true;
793
- final @Nullable List<Properties> debugMetaList = (new AssetsDebugMetaLoader(this.getReactApplicationContext(),
794
- logger)).loadDebugMeta();
795
- if (debugMetaList == null) {
796
- return null;
797
- }
798
-
799
- for (Properties debugMeta : debugMetaList) {
800
- proguardUuid = DebugMetaPropertiesApplier.getProguardUuid(debugMeta);
801
- if (proguardUuid != null) {
802
- logger.log(SentryLevel.INFO, "Proguard uuid found: " + proguardUuid);
803
- return proguardUuid;
804
- }
805
- }
806
-
807
- logger.log(SentryLevel.WARNING, "No proguard uuid found in debug meta properties file!");
808
- return null;
809
- }
810
-
811
- private String readStringFromFile(File path) throws IOException {
812
- try (final BufferedReader br = new BufferedReader(new FileReader(path));) {
813
-
814
- final StringBuilder text = new StringBuilder();
815
- String line;
816
- while ((line = br.readLine()) != null) {
817
- text.append(line);
818
- text.append('\n');
819
- }
820
- return text.toString();
821
- }
822
- }
823
-
824
- public void fetchNativeDeviceContexts(Promise promise) {
825
- final @NotNull SentryOptions options = HubAdapter.getInstance().getOptions();
826
- if (!(options instanceof SentryAndroidOptions)) {
827
- promise.resolve(null);
828
- return;
829
- }
830
-
831
- final @Nullable Context context = this.getReactApplicationContext().getApplicationContext();
832
- if (context == null) {
833
- promise.resolve(null);
834
- return;
835
- }
836
-
837
- final @Nullable IScope currentScope = InternalSentrySdk.getCurrentScope();
838
- final @NotNull Map<String, Object> serialized = InternalSentrySdk.serializeScope(
839
- context,
840
- (SentryAndroidOptions) options,
841
- currentScope);
842
- final @Nullable Object deviceContext = RNSentryMapConverter.convertToWritable(serialized);
843
- promise.resolve(deviceContext);
844
- }
845
-
846
- public void fetchNativeSdkInfo(Promise promise) {
847
- final @Nullable SdkVersion sdkVersion = HubAdapter.getInstance().getOptions().getSdkVersion();
848
- if (sdkVersion == null) {
849
- promise.resolve(null);
850
- } else {
851
- final WritableMap sdkInfo = new WritableNativeMap();
852
- sdkInfo.putString("name", sdkVersion.getName());
853
- sdkInfo.putString("version", sdkVersion.getVersion());
854
- promise.resolve(sdkInfo);
855
- }
856
- }
857
-
858
- public String fetchNativePackageName() {
859
- return packageInfo.packageName;
860
- }
861
-
862
- public void crashedLastRun(Promise promise) {
863
- promise.resolve(Sentry.isCrashedLastRun());
864
- }
865
-
866
- private void setEventOriginTag(SentryEvent event) {
867
- SdkVersion sdk = event.getSdk();
868
- if (sdk != null) {
869
- switch (sdk.getName()) {
870
- // If the event is from capacitor js, it gets set there and we do not handle it
871
- // here.
872
- case NATIVE_SDK_NAME:
873
- setEventEnvironmentTag(event, "native");
874
- break;
875
- case ANDROID_SDK_NAME:
876
- setEventEnvironmentTag(event, "java");
877
- break;
878
- default:
879
- break;
880
- }
881
- }
882
- }
883
-
884
- private void setEventEnvironmentTag(SentryEvent event, String environment) {
885
- event.setTag("event.origin", "android");
886
- event.setTag("event.environment", environment);
887
- }
888
-
889
- private void addPackages(SentryEvent event, SdkVersion sdk) {
890
- SdkVersion eventSdk = event.getSdk();
891
- if (eventSdk != null && eventSdk.getName().equals("sentry.javascript.react-native") && sdk != null) {
892
- List<SentryPackage> sentryPackages = sdk.getPackages();
893
- if (sentryPackages != null) {
894
- for (SentryPackage sentryPackage : sentryPackages) {
895
- eventSdk.addPackage(sentryPackage.getName(), sentryPackage.getVersion());
896
- }
897
- }
898
-
899
- List<String> integrations = sdk.getIntegrations();
900
- if (integrations != null) {
901
- for (String integration : integrations) {
902
- eventSdk.addIntegration(integration);
903
- }
904
- }
905
-
906
- event.setSdk(eventSdk);
907
- }
908
- }
909
-
910
- private boolean checkAndroidXAvailability() {
911
- try {
912
- Class.forName("androidx.core.app.FrameMetricsAggregator");
913
- return true;
914
- } catch (ClassNotFoundException ignored) {
915
- // androidx.core isn't available.
916
- return false;
917
- }
918
- }
919
-
920
- private boolean isFrameMetricsAggregatorAvailable() {
921
- return androidXAvailable && frameMetricsAggregator != null;
922
- }
832
+ final boolean wasProfileSuccessfullyDeleted = output.delete();
833
+ if (!wasProfileSuccessfullyDeleted) {
834
+ logger.log(SentryLevel.WARNING, "Profile not deleted from:" + output.getAbsolutePath());
835
+ }
836
+ } catch (Throwable e) { // NOPMD - We don't want to crash in any case
837
+ logger.log(SentryLevel.WARNING, "Profile not deleted from:" + output.getAbsolutePath());
838
+ }
839
+ }
840
+ }
841
+ return result;
842
+ }
843
+
844
+ private @Nullable String getProguardUuid() {
845
+ if (isProguardDebugMetaLoaded) {
846
+ return proguardUuid;
847
+ }
848
+ isProguardDebugMetaLoaded = true;
849
+ final @Nullable List<Properties> debugMetaList =
850
+ new AssetsDebugMetaLoader(this.getReactApplicationContext(), logger).loadDebugMeta();
851
+ if (debugMetaList == null) {
852
+ return null;
853
+ }
854
+
855
+ for (Properties debugMeta : debugMetaList) {
856
+ proguardUuid = DebugMetaPropertiesApplier.getProguardUuid(debugMeta);
857
+ if (proguardUuid != null) {
858
+ logger.log(SentryLevel.INFO, "Proguard uuid found: " + proguardUuid);
859
+ return proguardUuid;
860
+ }
861
+ }
862
+
863
+ logger.log(SentryLevel.WARNING, "No proguard uuid found in debug meta properties file!");
864
+ return null;
865
+ }
866
+
867
+ private String readStringFromFile(File path) throws IOException {
868
+ try (BufferedReader br = new BufferedReader(new FileReader(path)); ) {
869
+
870
+ final StringBuilder text = new StringBuilder();
871
+ String line;
872
+ while ((line = br.readLine()) != null) {
873
+ text.append(line);
874
+ text.append('\n');
875
+ }
876
+ return text.toString();
877
+ }
878
+ }
879
+
880
+ public void fetchNativeDeviceContexts(Promise promise) {
881
+ final @NotNull SentryOptions options = HubAdapter.getInstance().getOptions();
882
+ if (!(options instanceof SentryAndroidOptions)) {
883
+ promise.resolve(null);
884
+ return;
885
+ }
886
+
887
+ final @Nullable Context context = this.getReactApplicationContext().getApplicationContext();
888
+ if (context == null) {
889
+ promise.resolve(null);
890
+ return;
891
+ }
892
+
893
+ final @Nullable IScope currentScope = InternalSentrySdk.getCurrentScope();
894
+ final @NotNull Map<String, Object> serialized =
895
+ InternalSentrySdk.serializeScope(context, (SentryAndroidOptions) options, currentScope);
896
+ final @Nullable Object deviceContext = RNSentryMapConverter.convertToWritable(serialized);
897
+ promise.resolve(deviceContext);
898
+ }
899
+
900
+ public void fetchNativeSdkInfo(Promise promise) {
901
+ final @Nullable SdkVersion sdkVersion = HubAdapter.getInstance().getOptions().getSdkVersion();
902
+ if (sdkVersion == null) {
903
+ promise.resolve(null);
904
+ } else {
905
+ final WritableMap sdkInfo = new WritableNativeMap();
906
+ sdkInfo.putString("name", sdkVersion.getName());
907
+ sdkInfo.putString("version", sdkVersion.getVersion());
908
+ promise.resolve(sdkInfo);
909
+ }
910
+ }
911
+
912
+ public String fetchNativePackageName() {
913
+ return packageInfo.packageName;
914
+ }
915
+
916
+ public void crashedLastRun(Promise promise) {
917
+ promise.resolve(Sentry.isCrashedLastRun());
918
+ }
919
+
920
+ private void setEventOriginTag(SentryEvent event) {
921
+ // We hardcode native-java as only java events are processed by the Android SDK.
922
+ SdkVersion sdk = event.getSdk();
923
+ if (sdk != null) {
924
+ switch (sdk.getName()) {
925
+ case NATIVE_SDK_NAME:
926
+ setEventEnvironmentTag(event, "native");
927
+ break;
928
+ case ANDROID_SDK_NAME:
929
+ setEventEnvironmentTag(event, "java");
930
+ break;
931
+ default:
932
+ break;
933
+ }
934
+ }
935
+ }
936
+
937
+ private void setEventEnvironmentTag(SentryEvent event, String environment) {
938
+ event.setTag("event.origin", "android");
939
+ event.setTag("event.environment", environment);
940
+ }
941
+
942
+ private void addPackages(SentryEvent event, SdkVersion sdk) {
943
+ SdkVersion eventSdk = event.getSdk();
944
+ if (eventSdk != null
945
+ && "sentry.javascript.react-native".equals(eventSdk.getName())
946
+ && sdk != null) {
947
+ List<SentryPackage> sentryPackages = sdk.getPackages();
948
+ if (sentryPackages != null) {
949
+ for (SentryPackage sentryPackage : sentryPackages) {
950
+ eventSdk.addPackage(sentryPackage.getName(), sentryPackage.getVersion());
951
+ }
952
+ }
953
+
954
+ List<String> integrations = sdk.getIntegrations();
955
+ if (integrations != null) {
956
+ for (String integration : integrations) {
957
+ eventSdk.addIntegration(integration);
958
+ }
959
+ }
960
+
961
+ event.setSdk(eventSdk);
962
+ }
963
+ }
964
+
965
+ private boolean checkAndroidXAvailability() {
966
+ try {
967
+ Class.forName("androidx.core.app.FrameMetricsAggregator");
968
+ return true;
969
+ } catch (ClassNotFoundException ignored) {
970
+ // androidx.core isn't available.
971
+ return false;
972
+ }
973
+ }
974
+
975
+ private boolean isFrameMetricsAggregatorAvailable() {
976
+ return androidXAvailable && frameMetricsAggregator != null;
977
+ }
923
978
  }