mirror of
https://github.com/flutter/flutter
synced 2024-08-24 18:36:03 +00:00
Update proxied devices to handle connection interface and diagnostics. (#145061)
Also improve the performance of daemon device discovery by parallelizing the calls.
This commit is contained in:
parent
19087442ce
commit
3dba3f2a0f
|
@ -1008,6 +1008,7 @@ class DeviceDomain extends Domain {
|
||||||
registerHandler('startDartDevelopmentService', startDartDevelopmentService);
|
registerHandler('startDartDevelopmentService', startDartDevelopmentService);
|
||||||
registerHandler('shutdownDartDevelopmentService', shutdownDartDevelopmentService);
|
registerHandler('shutdownDartDevelopmentService', shutdownDartDevelopmentService);
|
||||||
registerHandler('setExternalDevToolsUriForDartDevelopmentService', setExternalDevToolsUriForDartDevelopmentService);
|
registerHandler('setExternalDevToolsUriForDartDevelopmentService', setExternalDevToolsUriForDartDevelopmentService);
|
||||||
|
registerHandler('getDiagnostics', getDiagnostics);
|
||||||
|
|
||||||
// Use the device manager discovery so that client provided device types
|
// Use the device manager discovery so that client provided device types
|
||||||
// are usable via the daemon protocol.
|
// are usable via the daemon protocol.
|
||||||
|
@ -1059,12 +1060,23 @@ class DeviceDomain extends Domain {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a list of the current devices, discarding existing cache of devices.
|
/// Return a list of the current devices, discarding existing cache of devices.
|
||||||
Future<List<Map<String, Object?>>> discoverDevices([ Map<String, Object?>? args ]) async {
|
Future<List<Map<String, Object?>>> discoverDevices(Map<String, Object?> args) async {
|
||||||
return <Map<String, Object?>>[
|
final int? timeoutInMilliseconds = _getIntArg(args, 'timeoutInMilliseconds');
|
||||||
|
final Duration? timeout = timeoutInMilliseconds != null ? Duration(milliseconds: timeoutInMilliseconds) : null;
|
||||||
|
|
||||||
|
// Calling `discoverDevices()` and `_deviceToMap()` in parallel for better performance.
|
||||||
|
final List<List<Device>> devicesListList = await Future.wait(<Future<List<Device>>>[
|
||||||
for (final PollingDeviceDiscovery discoverer in _discoverers)
|
for (final PollingDeviceDiscovery discoverer in _discoverers)
|
||||||
for (final Device device in await discoverer.discoverDevices())
|
discoverer.discoverDevices(timeout: timeout),
|
||||||
await _deviceToMap(device),
|
]);
|
||||||
|
|
||||||
|
final List<Device> devices = <Device>[
|
||||||
|
for (final List<Device> devicesList in devicesListList)
|
||||||
|
...devicesList,
|
||||||
];
|
];
|
||||||
|
return Future.wait(<Future<Map<String, Object?>>>[
|
||||||
|
for (final Device device in devices) _deviceToMap(device),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enable device events.
|
/// Enable device events.
|
||||||
|
@ -1298,6 +1310,21 @@ class DeviceDomain extends Domain {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a list of diagnostic messages pertaining to issues with any connected
|
||||||
|
/// devices.
|
||||||
|
Future<List<String>> getDiagnostics(Map<String, Object?> args) async {
|
||||||
|
// Call `getDiagnostics()` in parallel to improve performance.
|
||||||
|
final List<List<String>> diagnosticsLists = await Future.wait(<Future<List<String>>>[
|
||||||
|
for (final PollingDeviceDiscovery discoverer in _discoverers)
|
||||||
|
discoverer.getDiagnostics(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return <String>[
|
||||||
|
for (final List<String> diagnostics in diagnosticsLists)
|
||||||
|
...diagnostics,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DevToolsDomain extends Domain {
|
class DevToolsDomain extends Domain {
|
||||||
|
@ -1333,6 +1360,8 @@ Future<Map<String, Object?>> _deviceToMap(Device device) async {
|
||||||
'ephemeral': device.ephemeral,
|
'ephemeral': device.ephemeral,
|
||||||
'emulatorId': await device.emulatorId,
|
'emulatorId': await device.emulatorId,
|
||||||
'sdk': await device.sdkNameAndVersion,
|
'sdk': await device.sdkNameAndVersion,
|
||||||
|
'isConnected': device.isConnected,
|
||||||
|
'connectionInterface': getNameForDeviceConnectionInterface(device.connectionInterface),
|
||||||
'capabilities': <String, Object>{
|
'capabilities': <String, Object>{
|
||||||
'hotReload': device.supportsHotReload,
|
'hotReload': device.supportsHotReload,
|
||||||
'hotRestart': device.supportsHotRestart,
|
'hotRestart': device.supportsHotRestart,
|
||||||
|
|
|
@ -586,6 +586,27 @@ enum DeviceConnectionInterface {
|
||||||
wireless,
|
wireless,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the `DeviceConnectionInterface` enum based on its string name.
|
||||||
|
DeviceConnectionInterface getDeviceConnectionInterfaceForName(String name) {
|
||||||
|
switch (name) {
|
||||||
|
case 'attached':
|
||||||
|
return DeviceConnectionInterface.attached;
|
||||||
|
case 'wireless':
|
||||||
|
return DeviceConnectionInterface.wireless;
|
||||||
|
}
|
||||||
|
throw Exception('Unsupported DeviceConnectionInterface name "$name"');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `DeviceConnectionInterface`'s string name.
|
||||||
|
String getNameForDeviceConnectionInterface(DeviceConnectionInterface connectionInterface) {
|
||||||
|
switch (connectionInterface) {
|
||||||
|
case DeviceConnectionInterface.attached:
|
||||||
|
return 'attached';
|
||||||
|
case DeviceConnectionInterface.wireless:
|
||||||
|
return 'wireless';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A device is a physical hardware that can run a Flutter application.
|
/// A device is a physical hardware that can run a Flutter application.
|
||||||
///
|
///
|
||||||
/// This may correspond to a connected iOS or Android device, or represent
|
/// This may correspond to a connected iOS or Android device, or represent
|
||||||
|
|
|
@ -102,6 +102,8 @@ class ProxiedDevices extends PollingDeviceDiscovery {
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
ProxiedDevice deviceFromDaemonResult(Map<String, Object?> device) {
|
ProxiedDevice deviceFromDaemonResult(Map<String, Object?> device) {
|
||||||
final Map<String, Object?> capabilities = _cast<Map<String, Object?>>(device['capabilities']);
|
final Map<String, Object?> capabilities = _cast<Map<String, Object?>>(device['capabilities']);
|
||||||
|
final String? connectionInterfaceName = _cast<String?>(device['connectionInterface']);
|
||||||
|
final DeviceConnectionInterface? connectionInterface = connectionInterfaceName != null ? getDeviceConnectionInterfaceForName(connectionInterfaceName) : null;
|
||||||
return ProxiedDevice(
|
return ProxiedDevice(
|
||||||
connection, _cast<String>(device['id']),
|
connection, _cast<String>(device['id']),
|
||||||
deltaFileTransfer: _deltaFileTransfer,
|
deltaFileTransfer: _deltaFileTransfer,
|
||||||
|
@ -110,6 +112,8 @@ class ProxiedDevices extends PollingDeviceDiscovery {
|
||||||
platformType: PlatformType.fromString(_cast<String>(device['platformType'])),
|
platformType: PlatformType.fromString(_cast<String>(device['platformType'])),
|
||||||
targetPlatform: getTargetPlatformForName(_cast<String>(device['platform'])),
|
targetPlatform: getTargetPlatformForName(_cast<String>(device['platform'])),
|
||||||
ephemeral: _cast<bool>(device['ephemeral']),
|
ephemeral: _cast<bool>(device['ephemeral']),
|
||||||
|
isConnected: _cast<bool?>(device['isConnected']) ?? true,
|
||||||
|
connectionInterface: connectionInterface ?? DeviceConnectionInterface.attached,
|
||||||
name: 'Proxied ${device['name']}',
|
name: 'Proxied ${device['name']}',
|
||||||
isLocalEmulator: _cast<bool>(device['emulator']),
|
isLocalEmulator: _cast<bool>(device['emulator']),
|
||||||
emulatorId: _cast<String?>(device['emulatorId']),
|
emulatorId: _cast<String?>(device['emulatorId']),
|
||||||
|
@ -124,6 +128,21 @@ class ProxiedDevices extends PollingDeviceDiscovery {
|
||||||
fileTransfer: _fileTransfer,
|
fileTransfer: _fileTransfer,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<String>> getDiagnostics() async {
|
||||||
|
try {
|
||||||
|
final List<String> diagnostics = _cast<List<dynamic>>(await connection.sendRequest('device.getDiagnostics')).cast<String>();
|
||||||
|
return diagnostics;
|
||||||
|
} on String catch (e) { // Daemon actually does throw string types.
|
||||||
|
if (e.contains('command not understood')) {
|
||||||
|
_logger.printTrace('The daemon is on an older version that does not support `device.getDiagnostics`.');
|
||||||
|
// Silently ignore.
|
||||||
|
return <String>[];
|
||||||
|
}
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A [Device] that acts as a proxy to remotely connected device.
|
/// A [Device] that acts as a proxy to remotely connected device.
|
||||||
|
@ -143,6 +162,8 @@ class ProxiedDevice extends Device {
|
||||||
required PlatformType? platformType,
|
required PlatformType? platformType,
|
||||||
required TargetPlatform targetPlatform,
|
required TargetPlatform targetPlatform,
|
||||||
required bool ephemeral,
|
required bool ephemeral,
|
||||||
|
required this.isConnected,
|
||||||
|
required this.connectionInterface,
|
||||||
required this.name,
|
required this.name,
|
||||||
required bool isLocalEmulator,
|
required bool isLocalEmulator,
|
||||||
required String? emulatorId,
|
required String? emulatorId,
|
||||||
|
@ -180,6 +201,12 @@ class ProxiedDevice extends Device {
|
||||||
|
|
||||||
final FileTransfer _fileTransfer;
|
final FileTransfer _fileTransfer;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final bool isConnected;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final DeviceConnectionInterface connectionInterface;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final String name;
|
final String name;
|
||||||
|
|
||||||
|
|
|
@ -459,6 +459,8 @@ void main() {
|
||||||
'ephemeral': false,
|
'ephemeral': false,
|
||||||
'emulatorId': 'device',
|
'emulatorId': 'device',
|
||||||
'sdk': 'Android 12',
|
'sdk': 'Android 12',
|
||||||
|
'isConnected': true,
|
||||||
|
'connectionInterface': 'attached',
|
||||||
'capabilities': <String, Object?>{
|
'capabilities': <String, Object?>{
|
||||||
'hotReload': true,
|
'hotReload': true,
|
||||||
'hotRestart': true,
|
'hotRestart': true,
|
||||||
|
@ -479,6 +481,8 @@ void main() {
|
||||||
'ephemeral': false,
|
'ephemeral': false,
|
||||||
'emulatorId': null,
|
'emulatorId': null,
|
||||||
'sdk': 'preview',
|
'sdk': 'preview',
|
||||||
|
'isConnected': true,
|
||||||
|
'connectionInterface': 'attached',
|
||||||
'capabilities': <String, Object?>{
|
'capabilities': <String, Object?>{
|
||||||
'hotReload': true,
|
'hotReload': true,
|
||||||
'hotRestart': true,
|
'hotRestart': true,
|
||||||
|
@ -725,6 +729,31 @@ void main() {
|
||||||
expect(device.dds.shutdownCalled, true);
|
expect(device.dds.shutdownCalled, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testUsingContext('device.getDiagnostics returns correct value', () async {
|
||||||
|
daemon = Daemon(
|
||||||
|
daemonConnection,
|
||||||
|
notifyingLogger: notifyingLogger,
|
||||||
|
);
|
||||||
|
final FakePollingDeviceDiscovery discoverer1 = FakePollingDeviceDiscovery();
|
||||||
|
discoverer1.diagnostics = <String>['fake diagnostic 1', 'fake diagnostic 2'];
|
||||||
|
final FakePollingDeviceDiscovery discoverer2 = FakePollingDeviceDiscovery();
|
||||||
|
discoverer2.diagnostics = <String>['fake diagnostic 3', 'fake diagnostic 4'];
|
||||||
|
daemon.deviceDomain.addDeviceDiscoverer(discoverer1);
|
||||||
|
daemon.deviceDomain.addDeviceDiscoverer(discoverer2);
|
||||||
|
daemonStreams.inputs.add(DaemonMessage(<String, Object?>{
|
||||||
|
'id': 0,
|
||||||
|
'method': 'device.getDiagnostics',
|
||||||
|
}));
|
||||||
|
final DaemonMessage response = await daemonStreams.outputs.stream.firstWhere(_notEvent);
|
||||||
|
expect(response.data['id'], 0);
|
||||||
|
expect(response.data['result'], <String>[
|
||||||
|
'fake diagnostic 1',
|
||||||
|
'fake diagnostic 2',
|
||||||
|
'fake diagnostic 3',
|
||||||
|
'fake diagnostic 4',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
testUsingContext('emulator.launch without an emulatorId should report an error', () async {
|
testUsingContext('emulator.launch without an emulatorId should report an error', () async {
|
||||||
daemon = Daemon(
|
daemon = Daemon(
|
||||||
daemonConnection,
|
daemonConnection,
|
||||||
|
@ -1128,6 +1157,9 @@ class FakeAndroidDevice extends Fake implements AndroidDevice {
|
||||||
@override
|
@override
|
||||||
final bool isConnected = true;
|
final bool isConnected = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final DeviceConnectionInterface connectionInterface = DeviceConnectionInterface.attached;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> get sdkNameAndVersion async => 'Android 12';
|
Future<String> get sdkNameAndVersion async => 'Android 12';
|
||||||
|
|
||||||
|
|
|
@ -274,6 +274,9 @@ class FakeAndroidDevice extends Fake implements AndroidDevice {
|
||||||
@override
|
@override
|
||||||
bool get isConnected => true;
|
bool get isConnected => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final DeviceConnectionInterface connectionInterface = DeviceConnectionInterface.attached;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> get sdkNameAndVersion async => 'Android 12';
|
Future<String> get sdkNameAndVersion async => 'Android 12';
|
||||||
|
|
||||||
|
|
|
@ -562,6 +562,44 @@ void main() {
|
||||||
expect(devicesAdded[0].id, fakeDevice['id']);
|
expect(devicesAdded[0].id, fakeDevice['id']);
|
||||||
expect(devicesAdded[1].id, fakeDevice2['id']);
|
expect(devicesAdded[1].id, fakeDevice2['id']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWithoutContext('handles getDiagnostics', () async {
|
||||||
|
bufferLogger = BufferLogger.test();
|
||||||
|
final ProxiedDevices proxiedDevices = ProxiedDevices(
|
||||||
|
clientDaemonConnection,
|
||||||
|
logger: bufferLogger,
|
||||||
|
);
|
||||||
|
|
||||||
|
final Future<List<String>> resultFuture = proxiedDevices.getDiagnostics();
|
||||||
|
|
||||||
|
final DaemonMessage message = await serverDaemonConnection.incomingCommands.first;
|
||||||
|
expect(message.data['id'], isNotNull);
|
||||||
|
expect(message.data['method'], 'device.getDiagnostics');
|
||||||
|
|
||||||
|
serverDaemonConnection.sendResponse(message.data['id']!, <String>['1', '2']);
|
||||||
|
|
||||||
|
final List<String> result = await resultFuture;
|
||||||
|
expect(result, <String>['1', '2']);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('returns empty result when daemon does not understand getDiagnostics', () async {
|
||||||
|
bufferLogger = BufferLogger.test();
|
||||||
|
final ProxiedDevices proxiedDevices = ProxiedDevices(
|
||||||
|
clientDaemonConnection,
|
||||||
|
logger: bufferLogger,
|
||||||
|
);
|
||||||
|
|
||||||
|
final Future<List<String>> resultFuture = proxiedDevices.getDiagnostics();
|
||||||
|
|
||||||
|
final DaemonMessage message = await serverDaemonConnection.incomingCommands.first;
|
||||||
|
expect(message.data['id'], isNotNull);
|
||||||
|
expect(message.data['method'], 'device.getDiagnostics');
|
||||||
|
|
||||||
|
serverDaemonConnection.sendErrorResponse(message.data['id']!, 'command not understood: device.getDiagnostics', StackTrace.current);
|
||||||
|
|
||||||
|
final List<String> result = await resultFuture;
|
||||||
|
expect(result, isEmpty);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('ProxiedDartDevelopmentService', () {
|
group('ProxiedDartDevelopmentService', () {
|
||||||
|
|
|
@ -269,6 +269,11 @@ class FakePollingDeviceDiscovery extends PollingDeviceDiscovery {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<String> wellKnownIds = <String>[];
|
List<String> wellKnownIds = <String>[];
|
||||||
|
|
||||||
|
List<String> diagnostics = <String>[];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<String>> getDiagnostics() => Future<List<String>>.value(diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A fake implementation of the [DeviceLogReader].
|
/// A fake implementation of the [DeviceLogReader].
|
||||||
|
|
Loading…
Reference in a new issue