Handle "Service connection disposed" error from VmService disconnecting while requests are outstanding (#153714)
Fixes umbrella issue https://github.com/flutter/flutter/issues/153471, including its children: https://github.com/flutter/flutter/issues/153472, https://github.com/flutter/flutter/issues/153473, and https://github.com/flutter/flutter/issues/153474. The VM service can be disposed at any time during requests (e.g. the user closes the app or stops debugging in VSCode)[^1].a479f91e80
anda7d8707a59
updated package:vm_service to throw new `RPCError`s when the service disconnects while requests are inflight. Therefore, we need to handle these exceptions in the tool. See umbrella issue for more details. I plan on cherry-picking this change to the stable channel. [^1]: https://github.com/flutter/flutter/issues/153471#issuecomment-2296294221
This commit is contained in:
parent
8bf1757b9e
commit
4a03b76c68
@ -414,7 +414,8 @@ known, it can be explicitly provided to attach via the command-line, e.g.
|
||||
_logger.printStatus('Waiting for a new connection from Flutter on ${device.name}...');
|
||||
}
|
||||
} on RPCError catch (err) {
|
||||
if (err.code == RPCErrorCodes.kServiceDisappeared) {
|
||||
if (err.code == RPCErrorCodes.kServiceDisappeared ||
|
||||
err.message.contains('Service connection disposed')) {
|
||||
throwToolExit('Lost connection to device.');
|
||||
}
|
||||
rethrow;
|
||||
|
@ -516,7 +516,8 @@ class DevFS {
|
||||
final vm_service.Response response = await _vmService.createDevFS(fsName);
|
||||
_baseUri = Uri.parse(response.json!['uri'] as String);
|
||||
} on vm_service.RPCError catch (rpcException) {
|
||||
if (rpcException.code == RPCErrorCodes.kServiceDisappeared) {
|
||||
if (rpcException.code == RPCErrorCodes.kServiceDisappeared ||
|
||||
rpcException.message.contains('Service connection disposed')) {
|
||||
// This can happen if the device has been disconnected, so translate to
|
||||
// a DevFSException, which the caller will handle.
|
||||
throw DevFSException('Service disconnected', rpcException);
|
||||
|
@ -503,7 +503,8 @@ class FlutterVmService {
|
||||
// and should begin to shutdown due to the service connection closing.
|
||||
// Swallow the exception here and let the shutdown logic elsewhere deal
|
||||
// with cleaning up.
|
||||
if (e.code == RPCErrorCodes.kServiceDisappeared) {
|
||||
if (e.code == RPCErrorCodes.kServiceDisappeared ||
|
||||
e.message.contains('Service connection disposed')) {
|
||||
return null;
|
||||
}
|
||||
rethrow;
|
||||
@ -873,8 +874,9 @@ class FlutterVmService {
|
||||
} on vm_service.RPCError catch (err) {
|
||||
// If an application is not using the framework or the VM service
|
||||
// disappears while handling a request, return null.
|
||||
if ((err.code == RPCErrorCodes.kMethodNotFound)
|
||||
|| (err.code == RPCErrorCodes.kServiceDisappeared)) {
|
||||
if ((err.code == RPCErrorCodes.kMethodNotFound) ||
|
||||
(err.code == RPCErrorCodes.kServiceDisappeared) ||
|
||||
(err.message.contains('Service connection disposed'))) {
|
||||
return null;
|
||||
}
|
||||
rethrow;
|
||||
|
@ -1125,6 +1125,45 @@ void main() {
|
||||
DeviceManager: () => testDeviceManager,
|
||||
});
|
||||
|
||||
testUsingContext('Catches "Service connection disposed" error', () async {
|
||||
final FakeAndroidDevice device = FakeAndroidDevice(id: '1')
|
||||
..portForwarder = const NoOpDevicePortForwarder()
|
||||
..onGetLogReader = () => NoOpDeviceLogReader('test');
|
||||
final FakeHotRunner hotRunner = FakeHotRunner();
|
||||
final FakeHotRunnerFactory hotRunnerFactory = FakeHotRunnerFactory()
|
||||
..hotRunner = hotRunner;
|
||||
hotRunner.onAttach = (
|
||||
Completer<DebugConnectionInfo>? connectionInfoCompleter,
|
||||
Completer<void>? appStartedCompleter,
|
||||
bool allowExistingDdsInstance,
|
||||
bool enableDevTools,
|
||||
) async {
|
||||
await null;
|
||||
throw vm_service.RPCError('flutter._listViews', RPCErrorCodes.kServerError, 'Service connection disposed');
|
||||
};
|
||||
|
||||
testDeviceManager.devices = <Device>[device];
|
||||
testFileSystem.file('lib/main.dart').createSync();
|
||||
|
||||
final AttachCommand command = AttachCommand(
|
||||
hotRunnerFactory: hotRunnerFactory,
|
||||
stdio: stdio,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
signals: signals,
|
||||
platform: platform,
|
||||
processInfo: processInfo,
|
||||
fileSystem: testFileSystem,
|
||||
);
|
||||
await expectLater(createTestCommandRunner(command).run(<String>[
|
||||
'attach',
|
||||
]), throwsToolExit(message: 'Lost connection to device.'));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => testFileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
DeviceManager: () => testDeviceManager,
|
||||
});
|
||||
|
||||
testUsingContext('Does not catch generic RPC error', () async {
|
||||
final FakeAndroidDevice device = FakeAndroidDevice(id: '1')
|
||||
..portForwarder = const NoOpDevicePortForwarder()
|
||||
|
Loading…
x
Reference in New Issue
Block a user