mirror of
https://github.com/flutter/flutter
synced 2024-08-25 02:46:31 +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('shutdownDartDevelopmentService', shutdownDartDevelopmentService);
|
||||
registerHandler('setExternalDevToolsUriForDartDevelopmentService', setExternalDevToolsUriForDartDevelopmentService);
|
||||
registerHandler('getDiagnostics', getDiagnostics);
|
||||
|
||||
// Use the device manager discovery so that client provided device types
|
||||
// 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.
|
||||
Future<List<Map<String, Object?>>> discoverDevices([ Map<String, Object?>? args ]) async {
|
||||
return <Map<String, Object?>>[
|
||||
Future<List<Map<String, Object?>>> discoverDevices(Map<String, Object?> args) async {
|
||||
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 Device device in await discoverer.discoverDevices())
|
||||
await _deviceToMap(device),
|
||||
discoverer.discoverDevices(timeout: timeout),
|
||||
]);
|
||||
|
||||
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.
|
||||
|
@ -1298,6 +1310,21 @@ class DeviceDomain extends Domain {
|
|||
}
|
||||
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 {
|
||||
|
@ -1333,6 +1360,8 @@ Future<Map<String, Object?>> _deviceToMap(Device device) async {
|
|||
'ephemeral': device.ephemeral,
|
||||
'emulatorId': await device.emulatorId,
|
||||
'sdk': await device.sdkNameAndVersion,
|
||||
'isConnected': device.isConnected,
|
||||
'connectionInterface': getNameForDeviceConnectionInterface(device.connectionInterface),
|
||||
'capabilities': <String, Object>{
|
||||
'hotReload': device.supportsHotReload,
|
||||
'hotRestart': device.supportsHotRestart,
|
||||
|
|
|
@ -586,6 +586,27 @@ enum DeviceConnectionInterface {
|
|||
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.
|
||||
///
|
||||
/// This may correspond to a connected iOS or Android device, or represent
|
||||
|
|
|
@ -102,6 +102,8 @@ class ProxiedDevices extends PollingDeviceDiscovery {
|
|||
@visibleForTesting
|
||||
ProxiedDevice deviceFromDaemonResult(Map<String, Object?> device) {
|
||||
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(
|
||||
connection, _cast<String>(device['id']),
|
||||
deltaFileTransfer: _deltaFileTransfer,
|
||||
|
@ -110,6 +112,8 @@ class ProxiedDevices extends PollingDeviceDiscovery {
|
|||
platformType: PlatformType.fromString(_cast<String>(device['platformType'])),
|
||||
targetPlatform: getTargetPlatformForName(_cast<String>(device['platform'])),
|
||||
ephemeral: _cast<bool>(device['ephemeral']),
|
||||
isConnected: _cast<bool?>(device['isConnected']) ?? true,
|
||||
connectionInterface: connectionInterface ?? DeviceConnectionInterface.attached,
|
||||
name: 'Proxied ${device['name']}',
|
||||
isLocalEmulator: _cast<bool>(device['emulator']),
|
||||
emulatorId: _cast<String?>(device['emulatorId']),
|
||||
|
@ -124,6 +128,21 @@ class ProxiedDevices extends PollingDeviceDiscovery {
|
|||
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.
|
||||
|
@ -143,6 +162,8 @@ class ProxiedDevice extends Device {
|
|||
required PlatformType? platformType,
|
||||
required TargetPlatform targetPlatform,
|
||||
required bool ephemeral,
|
||||
required this.isConnected,
|
||||
required this.connectionInterface,
|
||||
required this.name,
|
||||
required bool isLocalEmulator,
|
||||
required String? emulatorId,
|
||||
|
@ -180,6 +201,12 @@ class ProxiedDevice extends Device {
|
|||
|
||||
final FileTransfer _fileTransfer;
|
||||
|
||||
@override
|
||||
final bool isConnected;
|
||||
|
||||
@override
|
||||
final DeviceConnectionInterface connectionInterface;
|
||||
|
||||
@override
|
||||
final String name;
|
||||
|
||||
|
|
|
@ -459,6 +459,8 @@ void main() {
|
|||
'ephemeral': false,
|
||||
'emulatorId': 'device',
|
||||
'sdk': 'Android 12',
|
||||
'isConnected': true,
|
||||
'connectionInterface': 'attached',
|
||||
'capabilities': <String, Object?>{
|
||||
'hotReload': true,
|
||||
'hotRestart': true,
|
||||
|
@ -479,6 +481,8 @@ void main() {
|
|||
'ephemeral': false,
|
||||
'emulatorId': null,
|
||||
'sdk': 'preview',
|
||||
'isConnected': true,
|
||||
'connectionInterface': 'attached',
|
||||
'capabilities': <String, Object?>{
|
||||
'hotReload': true,
|
||||
'hotRestart': true,
|
||||
|
@ -725,6 +729,31 @@ void main() {
|
|||
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 {
|
||||
daemon = Daemon(
|
||||
daemonConnection,
|
||||
|
@ -1128,6 +1157,9 @@ class FakeAndroidDevice extends Fake implements AndroidDevice {
|
|||
@override
|
||||
final bool isConnected = true;
|
||||
|
||||
@override
|
||||
final DeviceConnectionInterface connectionInterface = DeviceConnectionInterface.attached;
|
||||
|
||||
@override
|
||||
Future<String> get sdkNameAndVersion async => 'Android 12';
|
||||
|
||||
|
|
|
@ -274,6 +274,9 @@ class FakeAndroidDevice extends Fake implements AndroidDevice {
|
|||
@override
|
||||
bool get isConnected => true;
|
||||
|
||||
@override
|
||||
final DeviceConnectionInterface connectionInterface = DeviceConnectionInterface.attached;
|
||||
|
||||
@override
|
||||
Future<String> get sdkNameAndVersion async => 'Android 12';
|
||||
|
||||
|
|
|
@ -562,6 +562,44 @@ void main() {
|
|||
expect(devicesAdded[0].id, fakeDevice['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', () {
|
||||
|
|
|
@ -269,6 +269,11 @@ class FakePollingDeviceDiscovery extends PollingDeviceDiscovery {
|
|||
|
||||
@override
|
||||
List<String> wellKnownIds = <String>[];
|
||||
|
||||
List<String> diagnostics = <String>[];
|
||||
|
||||
@override
|
||||
Future<List<String>> getDiagnostics() => Future<List<String>>.value(diagnostics);
|
||||
}
|
||||
|
||||
/// A fake implementation of the [DeviceLogReader].
|
||||
|
|
Loading…
Reference in a new issue