Skip to content

Commit 7640fd6

Browse files
[CP-stable]Handle "Service connection disposed" error from VmService disconnecting while requests are outstanding (#153769)
This pull request is created by [automatic cherry pick workflow](https://github.com/flutter/flutter/blob/main/docs/releases/Flutter-Cherrypick-Process.md#automatically-creates-a-cherry-pick-request) Please fill in the form below, and a flutter domain expert will evaluate this cherry pick request. ### Issue Link: What is the link to the issue this cherry-pick is addressing? flutter/flutter#153471 ### Changelog Description: Explain this cherry pick in one line that is accessible to most Flutter developers. See [best practices](https://github.com/flutter/flutter/blob/main/docs/releases/Hotfix-Documentation-Best-Practices.md) for examples [CLI] When running a Flutter app, a noisy crash message is displayed when the connection to the device is lost. ### Impact Description: What is the impact (ex. visual jank on Samsung phones, app crash, cannot ship an iOS app)? Does it impact development (ex. flutter doctor crashes when Android Studio is installed), or the shipping production app (the app crashes on launch) When running an app (e.g. using `flutter run`) and the connection to the device is lost (e.g. user shutdown the device), a noisy crash message is displayed instead of showing something concise and useful such as "Lost connection to device." ### Workaround: Is there a workaround for this issue? Not necessary. Most users will be able to rerun their app without problems. ### Risk: What is the risk level of this cherry-pick? ### Test Coverage: Are you confident that your fix is well-tested by automated tests? ### Validation Steps: What are the steps to validate that this fix works? N/A. No repro is known.
1 parent a0bde75 commit 7640fd6

File tree

4 files changed

+48
-5
lines changed

4 files changed

+48
-5
lines changed

packages/flutter_tools/lib/src/commands/attach.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,8 @@ known, it can be explicitly provided to attach via the command-line, e.g.
416416
_logger.printStatus('Waiting for a new connection from Flutter on ${device.name}...');
417417
}
418418
} on RPCError catch (err) {
419-
if (err.code == RPCErrorCodes.kServiceDisappeared) {
419+
if (err.code == RPCErrorCodes.kServiceDisappeared ||
420+
err.message.contains('Service connection disposed')) {
420421
throwToolExit('Lost connection to device.');
421422
}
422423
rethrow;

packages/flutter_tools/lib/src/devfs.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,8 @@ class DevFS {
516516
final vm_service.Response response = await _vmService.createDevFS(fsName);
517517
_baseUri = Uri.parse(response.json!['uri'] as String);
518518
} on vm_service.RPCError catch (rpcException) {
519-
if (rpcException.code == RPCErrorCodes.kServiceDisappeared) {
519+
if (rpcException.code == RPCErrorCodes.kServiceDisappeared ||
520+
rpcException.message.contains('Service connection disposed')) {
520521
// This can happen if the device has been disconnected, so translate to
521522
// a DevFSException, which the caller will handle.
522523
throw DevFSException('Service disconnected', rpcException);

packages/flutter_tools/lib/src/vmservice.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,8 @@ class FlutterVmService {
504504
// and should begin to shutdown due to the service connection closing.
505505
// Swallow the exception here and let the shutdown logic elsewhere deal
506506
// with cleaning up.
507-
if (e.code == RPCErrorCodes.kServiceDisappeared) {
507+
if (e.code == RPCErrorCodes.kServiceDisappeared ||
508+
e.message.contains('Service connection disposed')) {
508509
return null;
509510
}
510511
rethrow;
@@ -874,8 +875,9 @@ class FlutterVmService {
874875
} on vm_service.RPCError catch (err) {
875876
// If an application is not using the framework or the VM service
876877
// disappears while handling a request, return null.
877-
if ((err.code == RPCErrorCodes.kMethodNotFound)
878-
|| (err.code == RPCErrorCodes.kServiceDisappeared)) {
878+
if ((err.code == RPCErrorCodes.kMethodNotFound) ||
879+
(err.code == RPCErrorCodes.kServiceDisappeared) ||
880+
(err.message.contains('Service connection disposed'))) {
879881
return null;
880882
}
881883
rethrow;

packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,45 @@ void main() {
11181118
DeviceManager: () => testDeviceManager,
11191119
});
11201120

1121+
testUsingContext('Catches "Service connection disposed" error', () async {
1122+
final FakeAndroidDevice device = FakeAndroidDevice(id: '1')
1123+
..portForwarder = const NoOpDevicePortForwarder()
1124+
..onGetLogReader = () => NoOpDeviceLogReader('test');
1125+
final FakeHotRunner hotRunner = FakeHotRunner();
1126+
final FakeHotRunnerFactory hotRunnerFactory = FakeHotRunnerFactory()
1127+
..hotRunner = hotRunner;
1128+
hotRunner.onAttach = (
1129+
Completer<DebugConnectionInfo>? connectionInfoCompleter,
1130+
Completer<void>? appStartedCompleter,
1131+
bool allowExistingDdsInstance,
1132+
bool enableDevTools,
1133+
) async {
1134+
await null;
1135+
throw vm_service.RPCError('flutter._listViews', RPCErrorCodes.kServerError, 'Service connection disposed');
1136+
};
1137+
1138+
testDeviceManager.devices = <Device>[device];
1139+
testFileSystem.file('lib/main.dart').createSync();
1140+
1141+
final AttachCommand command = AttachCommand(
1142+
hotRunnerFactory: hotRunnerFactory,
1143+
stdio: stdio,
1144+
logger: logger,
1145+
terminal: terminal,
1146+
signals: signals,
1147+
platform: platform,
1148+
processInfo: processInfo,
1149+
fileSystem: testFileSystem,
1150+
);
1151+
await expectLater(createTestCommandRunner(command).run(<String>[
1152+
'attach',
1153+
]), throwsToolExit(message: 'Lost connection to device.'));
1154+
}, overrides: <Type, Generator>{
1155+
FileSystem: () => testFileSystem,
1156+
ProcessManager: () => FakeProcessManager.any(),
1157+
DeviceManager: () => testDeviceManager,
1158+
});
1159+
11211160
testUsingContext('Does not catch generic RPC error', () async {
11221161
final FakeAndroidDevice device = FakeAndroidDevice(id: '1')
11231162
..portForwarder = const NoOpDevicePortForwarder()

0 commit comments

Comments
 (0)