@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.
- 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 +67 -66
- package/android/src/main/java/io/sentry/react/RNSentryMapConverter.java +110 -110
- package/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java +868 -813
- 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 +29 -38
- 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 +9 -9
- package/plugin/build/utils.js +6 -6
- package/plugin/build/withSentry.js +2 -2
- package/plugin/build/withSentryAndroid.js +2 -2
- package/plugin/build/withSentryIOS.js +3 -3
- 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,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.
|
|
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"); // 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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
} catch (Throwable ignored) {
|
|
276
|
-
|
|
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
|
-
|
|
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 (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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
617
|
-
|
|
618
|
-
|
|
649
|
+
public void addBreadcrumb(final ReadableMap breadcrumb) {
|
|
650
|
+
Sentry.configureScope(
|
|
651
|
+
scope -> {
|
|
652
|
+
scope.addBreadcrumb(RNSentryBreadcrumb.fromMap(breadcrumb));
|
|
619
653
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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
|
-
|
|
628
|
-
|
|
629
|
-
|
|
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
|
-
|
|
634
|
-
|
|
635
|
-
|
|
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
|
-
|
|
640
|
-
|
|
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
|
-
|
|
696
|
+
final Map<String, Object> contextHashMap = context.toHashMap();
|
|
697
|
+
scope.setContexts(key, contextHashMap);
|
|
647
698
|
});
|
|
648
|
-
|
|
699
|
+
}
|
|
649
700
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
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
|
-
|
|
657
|
-
|
|
708
|
+
public void closeNativeSdk(Promise promise) {
|
|
709
|
+
Sentry.close();
|
|
658
710
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
promise.resolve(true);
|
|
662
|
-
}
|
|
711
|
+
disableNativeFramesTracking();
|
|
663
712
|
|
|
664
|
-
|
|
665
|
-
|
|
713
|
+
promise.resolve(true);
|
|
714
|
+
}
|
|
666
715
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
final Activity currentActivity = getCurrentActivity();
|
|
716
|
+
public void enableNativeFramesTracking() {
|
|
717
|
+
androidXAvailable = checkAndroidXAvailability();
|
|
670
718
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
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
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
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
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
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
|
-
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
|
}
|