Skip to content

Commit c40ff0c

Browse files
committed
Update
1 parent a40112f commit c40ff0c

File tree

9 files changed

+179
-50
lines changed

9 files changed

+179
-50
lines changed

packages/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ class SentryFlutterPlugin :
7171
"removeExtra" -> removeExtra(call.argument("key"), result)
7272
"setTag" -> setTag(call.argument("key"), call.argument("value"), result)
7373
"removeTag" -> removeTag(call.argument("key"), result)
74-
"nativeCrash" -> crash()
7574
"setReplayConfig" -> setReplayConfig(call, result)
7675
"captureReplay" -> captureReplay(result)
7776
else -> result.notImplemented()
@@ -289,6 +288,15 @@ class SentryFlutterPlugin :
289288
@JvmStatic
290289
fun privateSentryGetReplayIntegration(): ReplayIntegration? = replay
291290

291+
@Suppress("unused") // Used by native/jni bindings
292+
@JvmStatic
293+
fun nativeCrash() {
294+
val exception = RuntimeException("FlutterSentry Native Integration: Sample RuntimeException")
295+
val mainThread = Looper.getMainLooper().thread
296+
mainThread.uncaughtExceptionHandler?.uncaughtException(mainThread, exception)
297+
mainThread.join(NATIVE_CRASH_WAIT_TIME)
298+
}
299+
292300
@Suppress("unused") // Used by native/jni bindings
293301
@JvmStatic
294302
fun getDisplayRefreshRate(): Int? {
@@ -457,13 +465,6 @@ class SentryFlutterPlugin :
457465
"debug_file" to debugFile,
458466
)
459467

460-
private fun crash() {
461-
val exception = RuntimeException("FlutterSentry Native Integration: Sample RuntimeException")
462-
val mainThread = Looper.getMainLooper().thread
463-
mainThread.uncaughtExceptionHandler?.uncaughtException(mainThread, exception)
464-
mainThread.join(NATIVE_CRASH_WAIT_TIME)
465-
}
466-
467468
private fun Double.adjustReplaySizeToBlockSize(): Double {
468469
val remainder = this % VIDEO_BLOCK_SIZE
469470
return if (remainder <= VIDEO_BLOCK_SIZE / 2) {

packages/flutter/ios/sentry_flutter/Sources/sentry_flutter/SentryFlutterPlugin.swift

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,6 @@ public class SentryFlutterPlugin: NSObject, FlutterPlugin {
130130
collectProfile(call, result)
131131
#endif
132132

133-
case "pauseAppHangTracking":
134-
pauseAppHangTracking(result)
135-
136-
case "resumeAppHangTracking":
137-
resumeAppHangTracking(result)
138-
139-
case "nativeCrash":
140-
crash()
141-
142133
case "captureReplay":
143134
#if canImport(UIKit) && !SENTRY_NO_UIKIT && (os(iOS) || os(tvOS))
144135
PrivateSentrySDKOnly.captureReplay()
@@ -431,20 +422,6 @@ public class SentryFlutterPlugin: NSObject, FlutterPlugin {
431422
result(nil)
432423
}
433424

434-
private func pauseAppHangTracking(_ result: @escaping FlutterResult) {
435-
SentrySDK.pauseAppHangTracking()
436-
result("")
437-
}
438-
439-
private func resumeAppHangTracking(_ result: @escaping FlutterResult) {
440-
SentrySDK.resumeAppHangTracking()
441-
result("")
442-
}
443-
444-
private func crash() {
445-
SentrySDK.crash()
446-
}
447-
448425
// MARK: - Objective-C interoperability
449426
//
450427
// Group of methods exposed to the Objective-C runtime via `@objc`.
@@ -541,6 +518,18 @@ public class SentryFlutterPlugin: NSObject, FlutterPlugin {
541518
#endif
542519
}
543520

521+
@objc public class func nativeCrash() {
522+
SentrySDK.crash()
523+
}
524+
525+
@objc public class func pauseAppHangTracking() {
526+
SentrySDK.pauseAppHangTracking()
527+
}
528+
529+
@objc public class func resumeAppHangTracking() {
530+
SentrySDK.resumeAppHangTracking()
531+
}
532+
544533
@objc(loadDebugImagesAsBytes:)
545534
public class func loadDebugImagesAsBytes(instructionAddresses: Set<String>) -> NSData? {
546535
var debugImages: [DebugMeta] = []

packages/flutter/ios/sentry_flutter/Sources/sentry_flutter_objc/SentryFlutterPlugin.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@
88
+ (nullable NSData *)fetchNativeAppStartAsBytes;
99
+ (nullable NSData *)loadContextsAsBytes;
1010
+ (nullable NSData *)loadDebugImagesAsBytes:(NSSet<NSString *> *)instructionAddresses;
11+
+ (void)nativeCrash;
12+
+ (void)pauseAppHangTracking;
13+
+ (void)resumeAppHangTracking;
1114
@end
1215
#endif

packages/flutter/lib/src/native/cocoa/binding.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,19 @@ late final _sel_fetchNativeAppStartAsBytes =
11281128
late final _sel_loadContextsAsBytes = objc.registerName("loadContextsAsBytes");
11291129
late final _sel_loadDebugImagesAsBytes_ =
11301130
objc.registerName("loadDebugImagesAsBytes:");
1131+
late final _sel_nativeCrash = objc.registerName("nativeCrash");
1132+
final _objc_msgSend_1pl9qdv = objc.msgSendPointer
1133+
.cast<
1134+
ffi.NativeFunction<
1135+
ffi.Void Function(ffi.Pointer<objc.ObjCObject>,
1136+
ffi.Pointer<objc.ObjCSelector>)>>()
1137+
.asFunction<
1138+
void Function(
1139+
ffi.Pointer<objc.ObjCObject>, ffi.Pointer<objc.ObjCSelector>)>();
1140+
late final _sel_pauseAppHangTracking =
1141+
objc.registerName("pauseAppHangTracking");
1142+
late final _sel_resumeAppHangTracking =
1143+
objc.registerName("resumeAppHangTracking");
11311144

11321145
/// SentryFlutterPlugin
11331146
class SentryFlutterPlugin extends objc.NSObject {
@@ -1186,6 +1199,23 @@ class SentryFlutterPlugin extends objc.NSObject {
11861199
: objc.NSData.castFromPointer(_ret, retain: true, release: true);
11871200
}
11881201

1202+
/// nativeCrash
1203+
static void nativeCrash() {
1204+
_objc_msgSend_1pl9qdv(_class_SentryFlutterPlugin, _sel_nativeCrash);
1205+
}
1206+
1207+
/// pauseAppHangTracking
1208+
static void pauseAppHangTracking() {
1209+
_objc_msgSend_1pl9qdv(
1210+
_class_SentryFlutterPlugin, _sel_pauseAppHangTracking);
1211+
}
1212+
1213+
/// resumeAppHangTracking
1214+
static void resumeAppHangTracking() {
1215+
_objc_msgSend_1pl9qdv(
1216+
_class_SentryFlutterPlugin, _sel_resumeAppHangTracking);
1217+
}
1218+
11891219
/// init
11901220
SentryFlutterPlugin init() {
11911221
objc.checkOsVersionInternal('SentryFlutterPlugin.init',

packages/flutter/lib/src/native/cocoa/sentry_native_cocoa.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,23 @@ class SentryNativeCocoa extends SentryNativeChannel {
178178
return NativeAppStart.fromJson(json);
179179
},
180180
);
181+
182+
@override
183+
void nativeCrash() {
184+
cocoa.SentryFlutterPlugin.nativeCrash();
185+
}
186+
187+
@override
188+
void pauseAppHangTracking() {
189+
tryCatchSync('pauseAppHangTracking', () {
190+
cocoa.SentryFlutterPlugin.pauseAppHangTracking();
191+
});
192+
}
193+
194+
@override
195+
void resumeAppHangTracking() {
196+
tryCatchSync('resumeAppHangTracking', () {
197+
cocoa.SentryFlutterPlugin.resumeAppHangTracking();
198+
});
199+
}
181200
}

packages/flutter/lib/src/native/java/binding.dart

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,29 @@ class SentryFlutterPlugin$Companion extends jni$_.JObject {
13051305
.object<ReplayIntegration?>(const $ReplayIntegration$NullableType());
13061306
}
13071307

1308+
static final _id_nativeCrash = _class.instanceMethodId(
1309+
r'nativeCrash',
1310+
r'()V',
1311+
);
1312+
1313+
static final _nativeCrash = jni$_.ProtectedJniExtensions.lookup<
1314+
jni$_.NativeFunction<
1315+
jni$_.JThrowablePtr Function(
1316+
jni$_.Pointer<jni$_.Void>,
1317+
jni$_.JMethodIDPtr,
1318+
)>>('globalEnv_CallVoidMethod')
1319+
.asFunction<
1320+
jni$_.JThrowablePtr Function(
1321+
jni$_.Pointer<jni$_.Void>,
1322+
jni$_.JMethodIDPtr,
1323+
)>();
1324+
1325+
/// from: `public final void nativeCrash()`
1326+
void nativeCrash() {
1327+
_nativeCrash(reference.pointer, _id_nativeCrash as jni$_.JMethodIDPtr)
1328+
.check();
1329+
}
1330+
13081331
static final _id_getDisplayRefreshRate = _class.instanceMethodId(
13091332
r'getDisplayRefreshRate',
13101333
r'()Ljava/lang/Integer;',
@@ -1816,6 +1839,30 @@ class SentryFlutterPlugin extends jni$_.JObject {
18161839
.object<ReplayIntegration?>(const $ReplayIntegration$NullableType());
18171840
}
18181841

1842+
static final _id_nativeCrash = _class.staticMethodId(
1843+
r'nativeCrash',
1844+
r'()V',
1845+
);
1846+
1847+
static final _nativeCrash = jni$_.ProtectedJniExtensions.lookup<
1848+
jni$_.NativeFunction<
1849+
jni$_.JThrowablePtr Function(
1850+
jni$_.Pointer<jni$_.Void>,
1851+
jni$_.JMethodIDPtr,
1852+
)>>('globalEnv_CallStaticVoidMethod')
1853+
.asFunction<
1854+
jni$_.JThrowablePtr Function(
1855+
jni$_.Pointer<jni$_.Void>,
1856+
jni$_.JMethodIDPtr,
1857+
)>();
1858+
1859+
/// from: `static public final void nativeCrash()`
1860+
static void nativeCrash() {
1861+
_nativeCrash(
1862+
_class.reference.pointer, _id_nativeCrash as jni$_.JMethodIDPtr)
1863+
.check();
1864+
}
1865+
18191866
static final _id_getDisplayRefreshRate = _class.staticMethodId(
18201867
r'getDisplayRefreshRate',
18211868
r'()Ljava/lang/Integer;',

packages/flutter/lib/src/native/java/sentry_native_java.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,21 @@ class SentryNativeJava extends SentryNativeChannel {
199199
});
200200
}
201201

202+
@override
203+
void nativeCrash() {
204+
native.SentryFlutterPlugin.Companion.nativeCrash();
205+
}
206+
207+
@override
208+
void pauseAppHangTracking() {
209+
assert(false, 'pauseAppHangTracking is not supported on Android.');
210+
}
211+
212+
@override
213+
void resumeAppHangTracking() {
214+
assert(false, 'resumeAppHangTracking is not supported on Android.');
215+
}
216+
202217
@override
203218
Future<void> close() async {
204219
await _replayRecorder?.stop();

packages/flutter/lib/src/native/sentry_native_channel.dart

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,15 +230,21 @@ class SentryNativeChannel
230230
}
231231

232232
@override
233-
Future<void> pauseAppHangTracking() =>
234-
channel.invokeMethod('pauseAppHangTracking');
233+
FutureOr<void> pauseAppHangTracking() {
234+
assert(false,
235+
'pauseAppHangTracking should not be used through method channels.');
236+
}
235237

236238
@override
237-
Future<void> resumeAppHangTracking() =>
238-
channel.invokeMethod('resumeAppHangTracking');
239+
FutureOr<void> resumeAppHangTracking() {
240+
assert(false,
241+
'resumeAppHangTracking should not be used through method channels.');
242+
}
239243

240244
@override
241-
Future<void> nativeCrash() => channel.invokeMethod('nativeCrash');
245+
FutureOr<void> nativeCrash() {
246+
assert(false, 'nativeCrash should not be used through method channels.');
247+
}
242248

243249
@override
244250
bool get supportsReplay => false;

packages/flutter/test/sentry_native_channel_test.dart

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -263,30 +263,49 @@ void main() {
263263
});
264264

265265
test('pauseAppHangTracking', () async {
266-
when(channel.invokeMethod('pauseAppHangTracking'))
267-
.thenAnswer((_) => Future.value());
268-
269-
await sut.pauseAppHangTracking();
266+
if (mockPlatform.isAndroid) {
267+
// Android doesn't support app hang tracking, so it should hit the assertion
268+
expect(() => sut.pauseAppHangTracking(), throwsAssertionError);
269+
} else {
270+
// iOS/macOS should throw FFI exceptions in tests
271+
final matcher = _nativeUnavailableMatcher(
272+
mockPlatform,
273+
includeLookupSymbol: true,
274+
includeFailedToLoadClassException: true,
275+
);
276+
expect(() => sut.pauseAppHangTracking(), matcher);
277+
}
270278

271-
verify(channel.invokeMethod('pauseAppHangTracking'));
279+
verifyZeroInteractions(channel);
272280
});
273281

274282
test('resumeAppHangTracking', () async {
275-
when(channel.invokeMethod('resumeAppHangTracking'))
276-
.thenAnswer((_) => Future.value());
277-
278-
await sut.resumeAppHangTracking();
283+
if (mockPlatform.isAndroid) {
284+
// Android doesn't support app hang tracking, so it should hit the assertion
285+
expect(() => sut.resumeAppHangTracking(), throwsAssertionError);
286+
} else {
287+
// iOS/macOS should throw FFI exceptions in tests
288+
final matcher = _nativeUnavailableMatcher(
289+
mockPlatform,
290+
includeLookupSymbol: true,
291+
includeFailedToLoadClassException: true,
292+
);
293+
expect(() => sut.resumeAppHangTracking(), matcher);
294+
}
279295

280-
verify(channel.invokeMethod('resumeAppHangTracking'));
296+
verifyZeroInteractions(channel);
281297
});
282298

283299
test('nativeCrash', () async {
284-
when(channel.invokeMethod('nativeCrash'))
285-
.thenAnswer((_) => Future.value());
300+
final matcher = _nativeUnavailableMatcher(
301+
mockPlatform,
302+
includeLookupSymbol: true,
303+
includeFailedToLoadClassException: true,
304+
);
286305

287-
await sut.nativeCrash();
306+
expect(() => sut.nativeCrash(), matcher);
288307

289-
verify(channel.invokeMethod('nativeCrash'));
308+
verifyZeroInteractions(channel);
290309
});
291310

292311
test('setReplayConfig', () async {

0 commit comments

Comments
 (0)