@sentry/react-native 6.0.0-rc.1 → 6.0.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.
- package/README.md +8 -0
- package/RNSentry.podspec +1 -1
- package/android/build.gradle +1 -1
- package/android/src/main/java/io/sentry/react/RNSentryBreadcrumb.java +63 -66
- package/android/src/main/java/io/sentry/react/RNSentryMapConverter.java +106 -110
- package/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java +867 -811
- package/android/src/main/java/io/sentry/react/RNSentryOnDrawReporterManager.java +128 -121
- package/android/src/main/java/io/sentry/react/RNSentryPackage.java +8 -14
- package/android/src/main/java/io/sentry/react/RNSentryReactFragmentLifecycleTracer.java +76 -70
- package/android/src/main/java/io/sentry/react/RNSentryReplayBreadcrumbConverter.java +26 -31
- package/android/src/newarch/java/io/sentry/react/RNSentryModule.java +164 -165
- package/android/src/oldarch/java/io/sentry/react/RNSentryModule.java +164 -165
- package/dist/js/integrations/nativelinkederrors.js +7 -2
- package/dist/js/integrations/nativelinkederrors.js.map +1 -1
- package/dist/js/tracing/timetodisplay.d.ts.map +1 -1
- package/dist/js/tracing/timetodisplay.js +3 -1
- package/dist/js/tracing/timetodisplay.js.map +1 -1
- package/dist/js/version.d.ts +1 -1
- package/dist/js/version.d.ts.map +1 -1
- package/dist/js/version.js +1 -1
- package/dist/js/version.js.map +1 -1
- package/dist/js/wrapper.d.ts +1 -1
- package/dist/js/wrapper.d.ts.map +1 -1
- package/dist/js/wrapper.js +37 -5
- package/dist/js/wrapper.js.map +1 -1
- package/ios/RNSentry.mm +9 -1
- package/package.json +1 -1
- package/ts3.8/dist/js/version.d.ts +1 -1
- 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,917 @@ 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.
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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");
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
269
|
+
SentryException ex = event.getExceptions().get(0);
|
|
270
|
+
if (null != ex && ex.getType().contains("JavascriptException")) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
275
273
|
} catch (Throwable ignored) {
|
|
276
|
-
|
|
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
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
//
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
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 (final InputStream stream =
|
|
373
|
+
new BufferedInputStream(assets.open(RNSentryModuleImpl.modulesPath))) {
|
|
374
|
+
int size = stream.available();
|
|
375
|
+
byte[] buffer = new byte[size];
|
|
376
|
+
stream.read(buffer);
|
|
377
|
+
stream.close();
|
|
378
|
+
String modulesJson = new String(buffer, RNSentryModuleImpl.UTF_8);
|
|
379
|
+
promise.resolve(modulesJson);
|
|
380
|
+
} catch (FileNotFoundException e) {
|
|
381
|
+
promise.resolve(null);
|
|
382
|
+
} catch (Throwable e) {
|
|
383
|
+
logger.log(SentryLevel.WARNING, "Fetching JS Modules failed.");
|
|
384
|
+
promise.resolve(null);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
public void fetchNativeRelease(Promise promise) {
|
|
389
|
+
WritableMap release = Arguments.createMap();
|
|
390
|
+
release.putString("id", packageInfo.packageName);
|
|
391
|
+
release.putString("version", packageInfo.versionName);
|
|
392
|
+
release.putString("build", String.valueOf(packageInfo.versionCode));
|
|
393
|
+
promise.resolve(release);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
public void fetchNativeAppStart(Promise promise) {
|
|
397
|
+
fetchNativeAppStart(
|
|
398
|
+
promise,
|
|
399
|
+
InternalSentrySdk.getAppStartMeasurement(),
|
|
400
|
+
logger,
|
|
401
|
+
AppStartMetrics.getInstance().isAppLaunchedInForeground());
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
protected void fetchNativeAppStart(
|
|
405
|
+
Promise promise,
|
|
406
|
+
final Map<String, Object> appStartMeasurement,
|
|
407
|
+
ILogger logger,
|
|
408
|
+
boolean isAppLaunchedInForeground) {
|
|
409
|
+
if (!isAppLaunchedInForeground) {
|
|
410
|
+
logger.log(SentryLevel.WARNING, "Invalid app start data: app not launched in foreground.");
|
|
411
|
+
promise.resolve(null);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
WritableMap mutableMeasurement =
|
|
416
|
+
(WritableMap) RNSentryMapConverter.convertToWritable(appStartMeasurement);
|
|
417
|
+
mutableMeasurement.putBoolean("has_fetched", hasFetchedAppStart);
|
|
418
|
+
|
|
419
|
+
// This is always set to true, as we would only allow an app start fetch to only
|
|
420
|
+
// happen once in the case of a JS bundle reload, we do not want it to be
|
|
421
|
+
// instrumented again.
|
|
422
|
+
hasFetchedAppStart = true;
|
|
423
|
+
|
|
424
|
+
promise.resolve(mutableMeasurement);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/** Returns frames metrics at the current point in time. */
|
|
428
|
+
public void fetchNativeFrames(Promise promise) {
|
|
429
|
+
if (!isFrameMetricsAggregatorAvailable()) {
|
|
430
|
+
promise.resolve(null);
|
|
431
|
+
} else {
|
|
432
|
+
try {
|
|
433
|
+
int totalFrames = 0;
|
|
434
|
+
int slowFrames = 0;
|
|
435
|
+
int frozenFrames = 0;
|
|
436
|
+
|
|
437
|
+
final SparseIntArray[] framesRates = frameMetricsAggregator.getMetrics();
|
|
438
|
+
|
|
439
|
+
if (framesRates != null) {
|
|
440
|
+
final SparseIntArray totalIndexArray = framesRates[FrameMetricsAggregator.TOTAL_INDEX];
|
|
441
|
+
if (totalIndexArray != null) {
|
|
442
|
+
for (int i = 0; i < totalIndexArray.size(); i++) {
|
|
443
|
+
int frameTime = totalIndexArray.keyAt(i);
|
|
444
|
+
int numFrames = totalIndexArray.valueAt(i);
|
|
445
|
+
totalFrames += numFrames;
|
|
446
|
+
// hard coded values, its also in the official android docs and frame metrics
|
|
447
|
+
// API
|
|
448
|
+
if (frameTime > FROZEN_FRAME_THRESHOLD) {
|
|
449
|
+
// frozen frames, threshold is 700ms
|
|
450
|
+
frozenFrames += numFrames;
|
|
451
|
+
} else if (frameTime > SLOW_FRAME_THRESHOLD) {
|
|
452
|
+
// slow frames, above 16ms, 60 frames/second
|
|
453
|
+
slowFrames += numFrames;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
WritableMap map = Arguments.createMap();
|
|
460
|
+
map.putInt("totalFrames", totalFrames);
|
|
461
|
+
map.putInt("slowFrames", slowFrames);
|
|
462
|
+
map.putInt("frozenFrames", frozenFrames);
|
|
463
|
+
|
|
464
|
+
promise.resolve(map);
|
|
465
|
+
} catch (Throwable ignored) {
|
|
466
|
+
logger.log(SentryLevel.WARNING, "Error fetching native frames.");
|
|
467
|
+
promise.resolve(null);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
public void captureReplay(boolean isHardCrash, Promise promise) {
|
|
473
|
+
Sentry.getCurrentHub().getOptions().getReplayController().captureReplay(isHardCrash);
|
|
474
|
+
promise.resolve(getCurrentReplayId());
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
public @Nullable String getCurrentReplayId() {
|
|
478
|
+
final @Nullable IScope scope = InternalSentrySdk.getCurrentScope();
|
|
479
|
+
if (scope == null) {
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
final @NotNull SentryId id = scope.getReplayId();
|
|
484
|
+
if (id == SentryId.EMPTY_ID) {
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
return id.toString();
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
public void captureEnvelope(String rawBytes, ReadableMap options, Promise promise) {
|
|
491
|
+
byte[] bytes = Base64.decode(rawBytes, Base64.DEFAULT);
|
|
492
|
+
|
|
493
|
+
try {
|
|
494
|
+
InternalSentrySdk.captureEnvelope(
|
|
495
|
+
bytes, !options.hasKey("hardCrashed") || !options.getBoolean("hardCrashed"));
|
|
496
|
+
} catch (Throwable e) {
|
|
497
|
+
logger.log(SentryLevel.ERROR, "Error while capturing envelope");
|
|
498
|
+
promise.resolve(false);
|
|
499
|
+
}
|
|
500
|
+
promise.resolve(true);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
public void captureScreenshot(Promise promise) {
|
|
504
|
+
|
|
505
|
+
final Activity activity = getCurrentActivity();
|
|
506
|
+
if (activity == null) {
|
|
507
|
+
logger.log(SentryLevel.WARNING, "CurrentActivity is null, can't capture screenshot.");
|
|
508
|
+
promise.resolve(null);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
final byte[] raw = takeScreenshotOnUiThread(activity);
|
|
513
|
+
|
|
514
|
+
if (raw == null) {
|
|
515
|
+
logger.log(SentryLevel.WARNING, "Screenshot is null, screen was not captured.");
|
|
516
|
+
promise.resolve(null);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
final WritableNativeArray data = new WritableNativeArray();
|
|
521
|
+
for (final byte b : raw) {
|
|
522
|
+
data.pushInt(b);
|
|
523
|
+
}
|
|
524
|
+
final WritableMap screenshot = new WritableNativeMap();
|
|
525
|
+
screenshot.putString("contentType", "image/png");
|
|
526
|
+
screenshot.putArray("data", data);
|
|
527
|
+
screenshot.putString("filename", "screenshot.png");
|
|
528
|
+
|
|
529
|
+
final WritableArray screenshotsArray = new WritableNativeArray();
|
|
530
|
+
screenshotsArray.pushMap(screenshot);
|
|
531
|
+
promise.resolve(screenshotsArray);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
private static byte[] takeScreenshotOnUiThread(Activity activity) {
|
|
535
|
+
CountDownLatch doneSignal = new CountDownLatch(1);
|
|
536
|
+
final byte[][] bytesWrapper = {{}}; // wrapper to be able to set the value in the runnable
|
|
537
|
+
final Runnable runTakeScreenshot =
|
|
538
|
+
() -> {
|
|
539
|
+
bytesWrapper[0] = takeScreenshot(activity, logger, buildInfo);
|
|
540
|
+
doneSignal.countDown();
|
|
510
541
|
};
|
|
511
542
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
543
|
+
if (UiThreadUtil.isOnUiThread()) {
|
|
544
|
+
runTakeScreenshot.run();
|
|
545
|
+
} else {
|
|
546
|
+
UiThreadUtil.runOnUiThread(runTakeScreenshot);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
try {
|
|
550
|
+
doneSignal.await(SCREENSHOT_TIMEOUT_SECONDS, SECONDS);
|
|
551
|
+
} catch (InterruptedException e) {
|
|
552
|
+
logger.log(SentryLevel.ERROR, "Screenshot process was interrupted.");
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return bytesWrapper[0];
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
public void fetchViewHierarchy(Promise promise) {
|
|
560
|
+
final @Nullable Activity activity = getCurrentActivity();
|
|
561
|
+
final @Nullable ViewHierarchy viewHierarchy =
|
|
562
|
+
ViewHierarchyEventProcessor.snapshotViewHierarchy(activity, logger);
|
|
563
|
+
if (viewHierarchy == null) {
|
|
564
|
+
logger.log(SentryLevel.ERROR, "Could not get ViewHierarchy.");
|
|
565
|
+
promise.resolve(null);
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
ISerializer serializer = HubAdapter.getInstance().getOptions().getSerializer();
|
|
570
|
+
final @Nullable byte[] bytes =
|
|
571
|
+
JsonSerializationUtils.bytesFrom(serializer, logger, viewHierarchy);
|
|
572
|
+
if (bytes == null) {
|
|
573
|
+
logger.log(SentryLevel.ERROR, "Could not serialize ViewHierarchy.");
|
|
574
|
+
promise.resolve(null);
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
if (bytes.length < 1) {
|
|
578
|
+
logger.log(SentryLevel.ERROR, "Got empty bytes array after serializing ViewHierarchy.");
|
|
579
|
+
promise.resolve(null);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
final WritableNativeArray data = new WritableNativeArray();
|
|
584
|
+
for (final byte b : bytes) {
|
|
585
|
+
data.pushInt(b);
|
|
586
|
+
}
|
|
587
|
+
promise.resolve(data);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
private static PackageInfo getPackageInfo(Context ctx) {
|
|
591
|
+
try {
|
|
592
|
+
return ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), 0);
|
|
593
|
+
} catch (PackageManager.NameNotFoundException e) {
|
|
594
|
+
logger.log(SentryLevel.WARNING, "Error getting package info.");
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
public void setUser(final ReadableMap userKeys, final ReadableMap userDataKeys) {
|
|
600
|
+
Sentry.configureScope(
|
|
601
|
+
scope -> {
|
|
602
|
+
if (userKeys == null && userDataKeys == null) {
|
|
603
|
+
scope.setUser(null);
|
|
604
|
+
} else {
|
|
605
|
+
User userInstance = new User();
|
|
606
|
+
|
|
607
|
+
if (userKeys != null) {
|
|
608
|
+
if (userKeys.hasKey("email")) {
|
|
609
|
+
userInstance.setEmail(userKeys.getString("email"));
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
if (userKeys.hasKey("id")) {
|
|
613
|
+
userInstance.setId(userKeys.getString("id"));
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (userKeys.hasKey("username")) {
|
|
617
|
+
userInstance.setUsername(userKeys.getString("username"));
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (userKeys.hasKey("ip_address")) {
|
|
621
|
+
userInstance.setIpAddress(userKeys.getString("ip_address"));
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
if (userKeys.hasKey("segment")) {
|
|
625
|
+
userInstance.setSegment(userKeys.getString("segment"));
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (userDataKeys != null) {
|
|
630
|
+
HashMap<String, String> userDataMap = new HashMap<>();
|
|
631
|
+
ReadableMapKeySetIterator it = userDataKeys.keySetIterator();
|
|
632
|
+
while (it.hasNextKey()) {
|
|
633
|
+
String key = it.nextKey();
|
|
634
|
+
String value = userDataKeys.getString(key);
|
|
635
|
+
|
|
636
|
+
// other is ConcurrentHashMap and can't have null values
|
|
637
|
+
if (value != null) {
|
|
638
|
+
userDataMap.put(key, value);
|
|
593
639
|
}
|
|
640
|
+
}
|
|
594
641
|
|
|
595
|
-
|
|
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);
|
|
642
|
+
userInstance.setData(userDataMap);
|
|
612
643
|
}
|
|
644
|
+
|
|
645
|
+
scope.setUser(userInstance);
|
|
646
|
+
}
|
|
613
647
|
});
|
|
614
|
-
|
|
648
|
+
}
|
|
615
649
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
650
|
+
public void addBreadcrumb(final ReadableMap breadcrumb) {
|
|
651
|
+
Sentry.configureScope(
|
|
652
|
+
scope -> {
|
|
653
|
+
scope.addBreadcrumb(RNSentryBreadcrumb.fromMap(breadcrumb));
|
|
619
654
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
655
|
+
final @Nullable String screen = RNSentryBreadcrumb.getCurrentScreenFrom(breadcrumb);
|
|
656
|
+
if (screen != null) {
|
|
657
|
+
scope.setScreen(screen);
|
|
658
|
+
}
|
|
624
659
|
});
|
|
625
|
-
|
|
660
|
+
}
|
|
626
661
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
662
|
+
public void clearBreadcrumbs() {
|
|
663
|
+
Sentry.configureScope(
|
|
664
|
+
scope -> {
|
|
665
|
+
scope.clearBreadcrumbs();
|
|
630
666
|
});
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
public void setExtra(String key, String extra) {
|
|
670
|
+
if (key == null || extra == null) {
|
|
671
|
+
logger.log(
|
|
672
|
+
SentryLevel.ERROR,
|
|
673
|
+
"RNSentry.setExtra called with null key or value, can't change extra.");
|
|
674
|
+
return;
|
|
631
675
|
}
|
|
632
676
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
677
|
+
Sentry.configureScope(
|
|
678
|
+
scope -> {
|
|
679
|
+
scope.setExtra(key, extra);
|
|
636
680
|
});
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
public void setContext(final String key, final ReadableMap context) {
|
|
684
|
+
if (key == null) {
|
|
685
|
+
logger.log(
|
|
686
|
+
SentryLevel.ERROR, "RNSentry.setContext called with null key, can't change context.");
|
|
687
|
+
return;
|
|
637
688
|
}
|
|
638
689
|
|
|
639
|
-
|
|
640
|
-
|
|
690
|
+
Sentry.configureScope(
|
|
691
|
+
scope -> {
|
|
692
|
+
if (context == null) {
|
|
693
|
+
scope.removeContexts(key);
|
|
641
694
|
return;
|
|
642
|
-
|
|
643
|
-
Sentry.configureScope(scope -> {
|
|
644
|
-
final HashMap<String, Object> contextHashMap = context.toHashMap();
|
|
695
|
+
}
|
|
645
696
|
|
|
646
|
-
|
|
697
|
+
final HashMap<String, Object> contextHashMap = context.toHashMap();
|
|
698
|
+
scope.setContexts(key, contextHashMap);
|
|
647
699
|
});
|
|
648
|
-
|
|
700
|
+
}
|
|
649
701
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
702
|
+
public void setTag(String key, String value) {
|
|
703
|
+
Sentry.configureScope(
|
|
704
|
+
scope -> {
|
|
705
|
+
scope.setTag(key, value);
|
|
653
706
|
});
|
|
654
|
-
|
|
707
|
+
}
|
|
655
708
|
|
|
656
|
-
|
|
657
|
-
|
|
709
|
+
public void closeNativeSdk(Promise promise) {
|
|
710
|
+
Sentry.close();
|
|
658
711
|
|
|
659
|
-
|
|
712
|
+
disableNativeFramesTracking();
|
|
660
713
|
|
|
661
|
-
|
|
662
|
-
|
|
714
|
+
promise.resolve(true);
|
|
715
|
+
}
|
|
663
716
|
|
|
664
|
-
|
|
665
|
-
|
|
717
|
+
public void enableNativeFramesTracking() {
|
|
718
|
+
androidXAvailable = checkAndroidXAvailability();
|
|
666
719
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
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
|
-
}
|
|
720
|
+
if (androidXAvailable) {
|
|
721
|
+
frameMetricsAggregator = new FrameMetricsAggregator();
|
|
722
|
+
final Activity currentActivity = getCurrentActivity();
|
|
726
723
|
|
|
724
|
+
if (frameMetricsAggregator != null && currentActivity != null) {
|
|
727
725
|
try {
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
726
|
+
frameMetricsAggregator.add(currentActivity);
|
|
727
|
+
|
|
728
|
+
logger.log(SentryLevel.INFO, "FrameMetricsAggregator installed.");
|
|
729
|
+
} catch (Throwable ignored) {
|
|
730
|
+
// throws ConcurrentModification when calling addOnFrameMetricsAvailableListener
|
|
731
|
+
// this is a best effort since we can't reproduce it
|
|
732
|
+
logger.log(SentryLevel.ERROR, "Error adding Activity to frameMetricsAggregator.");
|
|
733
|
+
}
|
|
734
|
+
} else {
|
|
735
|
+
logger.log(SentryLevel.INFO, "currentActivity isn't available.");
|
|
736
|
+
}
|
|
737
|
+
} else {
|
|
738
|
+
logger.log(SentryLevel.WARNING, "androidx.core' isn't available as a dependency.");
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
public void disableNativeFramesTracking() {
|
|
743
|
+
if (isFrameMetricsAggregatorAvailable()) {
|
|
744
|
+
frameMetricsAggregator.stop();
|
|
745
|
+
frameMetricsAggregator = null;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
private String getProfilingTracesDirPath() {
|
|
750
|
+
if (cacheDirPath == null) {
|
|
751
|
+
cacheDirPath =
|
|
752
|
+
new File(getReactApplicationContext().getCacheDir(), "sentry/react").getAbsolutePath();
|
|
753
|
+
}
|
|
754
|
+
File profilingTraceDir = new File(cacheDirPath, "profiling_trace");
|
|
755
|
+
profilingTraceDir.mkdirs();
|
|
756
|
+
return profilingTraceDir.getAbsolutePath();
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
private void initializeAndroidProfiler() {
|
|
760
|
+
if (executorService == null) {
|
|
761
|
+
executorService = new SentryExecutorService();
|
|
762
|
+
}
|
|
763
|
+
final String tracesFilesDirPath = getProfilingTracesDirPath();
|
|
764
|
+
|
|
765
|
+
androidProfiler =
|
|
766
|
+
new AndroidProfiler(
|
|
767
|
+
tracesFilesDirPath,
|
|
768
|
+
(int) SECONDS.toMicros(1) / profilingTracesHz,
|
|
769
|
+
new SentryFrameMetricsCollector(reactApplicationContext, logger, buildInfo),
|
|
770
|
+
executorService,
|
|
771
|
+
logger,
|
|
772
|
+
buildInfo);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
public WritableMap startProfiling(boolean platformProfilers) {
|
|
776
|
+
final WritableMap result = new WritableNativeMap();
|
|
777
|
+
if (androidProfiler == null && platformProfilers) {
|
|
778
|
+
initializeAndroidProfiler();
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
try {
|
|
782
|
+
HermesSamplingProfiler.enable();
|
|
783
|
+
if (androidProfiler != null) {
|
|
784
|
+
androidProfiler.start();
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
result.putBoolean("started", true);
|
|
788
|
+
} catch (Throwable e) {
|
|
789
|
+
result.putBoolean("started", false);
|
|
790
|
+
result.putString("error", e.toString());
|
|
791
|
+
}
|
|
792
|
+
return result;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
public WritableMap stopProfiling() {
|
|
796
|
+
final boolean isDebug = HubAdapter.getInstance().getOptions().isDebug();
|
|
797
|
+
final WritableMap result = new WritableNativeMap();
|
|
798
|
+
File output = null;
|
|
799
|
+
try {
|
|
800
|
+
AndroidProfiler.ProfileEndData end = null;
|
|
801
|
+
if (androidProfiler != null) {
|
|
802
|
+
end = androidProfiler.endAndCollect(false, null);
|
|
803
|
+
}
|
|
804
|
+
HermesSamplingProfiler.disable();
|
|
805
|
+
|
|
806
|
+
output =
|
|
807
|
+
File.createTempFile(
|
|
808
|
+
"sampling-profiler-trace", ".cpuprofile", reactApplicationContext.getCacheDir());
|
|
809
|
+
if (isDebug) {
|
|
810
|
+
logger.log(SentryLevel.INFO, "Profile saved to: " + output.getAbsolutePath());
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
HermesSamplingProfiler.dumpSampledTraceToFile(output.getPath());
|
|
814
|
+
result.putString("profile", readStringFromFile(output));
|
|
815
|
+
|
|
816
|
+
if (end != null) {
|
|
817
|
+
WritableMap androidProfile = new WritableNativeMap();
|
|
818
|
+
byte[] androidProfileBytes =
|
|
819
|
+
FileUtils.readBytesFromFile(end.traceFile.getPath(), maxTraceFileSize);
|
|
820
|
+
String base64AndroidProfile =
|
|
821
|
+
Base64.encodeToString(androidProfileBytes, NO_WRAP | NO_PADDING);
|
|
822
|
+
|
|
823
|
+
androidProfile.putString("sampled_profile", base64AndroidProfile);
|
|
824
|
+
androidProfile.putInt("android_api_level", buildInfo.getSdkInfoVersion());
|
|
825
|
+
androidProfile.putString("build_id", getProguardUuid());
|
|
826
|
+
result.putMap("androidProfile", androidProfile);
|
|
827
|
+
}
|
|
828
|
+
} catch (Throwable e) {
|
|
829
|
+
result.putString("error", e.toString());
|
|
830
|
+
} finally {
|
|
831
|
+
if (output != null) {
|
|
745
832
|
try {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
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
|
-
}
|
|
833
|
+
final boolean wasProfileSuccessfullyDeleted = output.delete();
|
|
834
|
+
if (!wasProfileSuccessfullyDeleted) {
|
|
835
|
+
logger.log(SentryLevel.WARNING, "Profile not deleted from:" + output.getAbsolutePath());
|
|
836
|
+
}
|
|
771
837
|
} catch (Throwable e) {
|
|
772
|
-
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
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
|
-
}
|
|
838
|
+
logger.log(SentryLevel.WARNING, "Profile not deleted from:" + output.getAbsolutePath());
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
return result;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
private @Nullable String getProguardUuid() {
|
|
846
|
+
if (isProguardDebugMetaLoaded) {
|
|
847
|
+
return proguardUuid;
|
|
848
|
+
}
|
|
849
|
+
isProguardDebugMetaLoaded = true;
|
|
850
|
+
final @Nullable List<Properties> debugMetaList =
|
|
851
|
+
(new AssetsDebugMetaLoader(this.getReactApplicationContext(), logger)).loadDebugMeta();
|
|
852
|
+
if (debugMetaList == null) {
|
|
853
|
+
return null;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
for (Properties debugMeta : debugMetaList) {
|
|
857
|
+
proguardUuid = DebugMetaPropertiesApplier.getProguardUuid(debugMeta);
|
|
858
|
+
if (proguardUuid != null) {
|
|
859
|
+
logger.log(SentryLevel.INFO, "Proguard uuid found: " + proguardUuid);
|
|
860
|
+
return proguardUuid;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
logger.log(SentryLevel.WARNING, "No proguard uuid found in debug meta properties file!");
|
|
865
|
+
return null;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
private String readStringFromFile(File path) throws IOException {
|
|
869
|
+
try (final BufferedReader br = new BufferedReader(new FileReader(path)); ) {
|
|
870
|
+
|
|
871
|
+
final StringBuilder text = new StringBuilder();
|
|
872
|
+
String line;
|
|
873
|
+
while ((line = br.readLine()) != null) {
|
|
874
|
+
text.append(line);
|
|
875
|
+
text.append('\n');
|
|
876
|
+
}
|
|
877
|
+
return text.toString();
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
public void fetchNativeDeviceContexts(Promise promise) {
|
|
882
|
+
final @NotNull SentryOptions options = HubAdapter.getInstance().getOptions();
|
|
883
|
+
if (!(options instanceof SentryAndroidOptions)) {
|
|
884
|
+
promise.resolve(null);
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
final @Nullable Context context = this.getReactApplicationContext().getApplicationContext();
|
|
889
|
+
if (context == null) {
|
|
890
|
+
promise.resolve(null);
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
final @Nullable IScope currentScope = InternalSentrySdk.getCurrentScope();
|
|
895
|
+
final @NotNull Map<String, Object> serialized =
|
|
896
|
+
InternalSentrySdk.serializeScope(context, (SentryAndroidOptions) options, currentScope);
|
|
897
|
+
final @Nullable Object deviceContext = RNSentryMapConverter.convertToWritable(serialized);
|
|
898
|
+
promise.resolve(deviceContext);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
public void fetchNativeSdkInfo(Promise promise) {
|
|
902
|
+
final @Nullable SdkVersion sdkVersion = HubAdapter.getInstance().getOptions().getSdkVersion();
|
|
903
|
+
if (sdkVersion == null) {
|
|
904
|
+
promise.resolve(null);
|
|
905
|
+
} else {
|
|
906
|
+
final WritableMap sdkInfo = new WritableNativeMap();
|
|
907
|
+
sdkInfo.putString("name", sdkVersion.getName());
|
|
908
|
+
sdkInfo.putString("version", sdkVersion.getVersion());
|
|
909
|
+
promise.resolve(sdkInfo);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
public String fetchNativePackageName() {
|
|
914
|
+
return packageInfo.packageName;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
public void crashedLastRun(Promise promise) {
|
|
918
|
+
promise.resolve(Sentry.isCrashedLastRun());
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
private void setEventOriginTag(SentryEvent event) {
|
|
922
|
+
// We hardcode native-java as only java events are processed by the Android SDK.
|
|
923
|
+
SdkVersion sdk = event.getSdk();
|
|
924
|
+
if (sdk != null) {
|
|
925
|
+
switch (sdk.getName()) {
|
|
926
|
+
case NATIVE_SDK_NAME:
|
|
927
|
+
setEventEnvironmentTag(event, "native");
|
|
928
|
+
break;
|
|
929
|
+
case ANDROID_SDK_NAME:
|
|
930
|
+
setEventEnvironmentTag(event, "java");
|
|
931
|
+
break;
|
|
932
|
+
default:
|
|
933
|
+
break;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
private void setEventEnvironmentTag(SentryEvent event, String environment) {
|
|
939
|
+
event.setTag("event.origin", "android");
|
|
940
|
+
event.setTag("event.environment", environment);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
private void addPackages(SentryEvent event, SdkVersion sdk) {
|
|
944
|
+
SdkVersion eventSdk = event.getSdk();
|
|
945
|
+
if (eventSdk != null
|
|
946
|
+
&& eventSdk.getName().equals("sentry.javascript.react-native")
|
|
947
|
+
&& sdk != null) {
|
|
948
|
+
List<SentryPackage> sentryPackages = sdk.getPackages();
|
|
949
|
+
if (sentryPackages != null) {
|
|
950
|
+
for (SentryPackage sentryPackage : sentryPackages) {
|
|
951
|
+
eventSdk.addPackage(sentryPackage.getName(), sentryPackage.getVersion());
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
List<String> integrations = sdk.getIntegrations();
|
|
956
|
+
if (integrations != null) {
|
|
957
|
+
for (String integration : integrations) {
|
|
958
|
+
eventSdk.addIntegration(integration);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
event.setSdk(eventSdk);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
private boolean checkAndroidXAvailability() {
|
|
967
|
+
try {
|
|
968
|
+
Class.forName("androidx.core.app.FrameMetricsAggregator");
|
|
969
|
+
return true;
|
|
970
|
+
} catch (ClassNotFoundException ignored) {
|
|
971
|
+
// androidx.core isn't available.
|
|
972
|
+
return false;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
private boolean isFrameMetricsAggregatorAvailable() {
|
|
977
|
+
return androidXAvailable && frameMetricsAggregator != null;
|
|
978
|
+
}
|
|
923
979
|
}
|