Support mdns when attaching to proxied devices. (#146021)
Also move the vm service discovery logic into platform-specific implementation of `Device`s. This is to avoid having platform-specific code in attach.dart.
This commit is contained in:
parent
51e70fa16b
commit
80042124ad
@ -18,6 +18,7 @@ import '../build_info.dart';
|
||||
import '../convert.dart';
|
||||
import '../device.dart';
|
||||
import '../device_port_forwarder.dart';
|
||||
import '../device_vm_service_discovery_for_attach.dart';
|
||||
import '../project.dart';
|
||||
import '../protocol_discovery.dart';
|
||||
import 'android.dart';
|
||||
@ -800,6 +801,26 @@ class AndroidDevice extends Device {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
VMServiceDiscoveryForAttach getVMServiceDiscoveryForAttach({
|
||||
String? appId,
|
||||
String? fuchsiaModule,
|
||||
int? filterDevicePort,
|
||||
int? expectedHostPort,
|
||||
required bool ipv6,
|
||||
required Logger logger,
|
||||
}) =>
|
||||
LogScanningVMServiceDiscoveryForAttach(
|
||||
// If it's an Android device, attaching relies on past log searching
|
||||
// to find the service protocol.
|
||||
Future<DeviceLogReader>.value(getLogReader(includePastLogs: true)),
|
||||
portForwarder: portForwarder,
|
||||
ipv6: ipv6,
|
||||
devicePort: filterDevicePort,
|
||||
hostPort: expectedHostPort,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
@override
|
||||
late final DevicePortForwarder? portForwarder = () {
|
||||
final String? adbPath = _androidSdk.adbPath;
|
||||
|
@ -21,13 +21,11 @@ import '../compile.dart';
|
||||
import '../daemon.dart';
|
||||
import '../device.dart';
|
||||
import '../device_port_forwarder.dart';
|
||||
import '../fuchsia/fuchsia_device.dart';
|
||||
import '../device_vm_service_discovery_for_attach.dart';
|
||||
import '../ios/devices.dart';
|
||||
import '../ios/simulators.dart';
|
||||
import '../macos/macos_ipad_device.dart';
|
||||
import '../mdns_discovery.dart';
|
||||
import '../project.dart';
|
||||
import '../protocol_discovery.dart';
|
||||
import '../resident_runner.dart';
|
||||
import '../run_cold.dart';
|
||||
import '../run_hot.dart';
|
||||
@ -286,116 +284,48 @@ known, it can be explicitly provided to attach via the command-line, e.g.
|
||||
: null;
|
||||
|
||||
Stream<Uri>? vmServiceUri;
|
||||
bool usesIpv6 = ipv6!;
|
||||
final bool usesIpv6 = ipv6!;
|
||||
final String ipv6Loopback = InternetAddress.loopbackIPv6.address;
|
||||
final String ipv4Loopback = InternetAddress.loopbackIPv4.address;
|
||||
final String hostname = usesIpv6 ? ipv6Loopback : ipv4Loopback;
|
||||
final bool isWirelessIOSDevice = (device is IOSDevice) && device.isWirelesslyConnected;
|
||||
|
||||
if ((debugPort == null && debugUri == null) || isWirelessIOSDevice) {
|
||||
if (device is FuchsiaDevice) {
|
||||
final String? module = stringArg('module');
|
||||
if (module == null) {
|
||||
throwToolExit("'--module' is required for attaching to a Fuchsia device");
|
||||
}
|
||||
usesIpv6 = device.ipv6;
|
||||
FuchsiaIsolateDiscoveryProtocol? isolateDiscoveryProtocol;
|
||||
try {
|
||||
isolateDiscoveryProtocol = device.getIsolateDiscoveryProtocol(module);
|
||||
vmServiceUri = Stream<Uri>.value(await isolateDiscoveryProtocol.uri).asBroadcastStream();
|
||||
} on Exception {
|
||||
isolateDiscoveryProtocol?.dispose();
|
||||
final List<ForwardedPort> ports = device.portForwarder.forwardedPorts.toList();
|
||||
for (final ForwardedPort port in ports) {
|
||||
await device.portForwarder.unforward(port);
|
||||
// The device port we expect to have the debug port be listening
|
||||
final int? devicePort = debugPort ?? debugUri?.port ?? deviceVmservicePort;
|
||||
|
||||
final VMServiceDiscoveryForAttach vmServiceDiscovery = device.getVMServiceDiscoveryForAttach(
|
||||
appId: appId,
|
||||
fuchsiaModule: stringArg('module'),
|
||||
filterDevicePort: devicePort,
|
||||
expectedHostPort: hostVmservicePort,
|
||||
ipv6: usesIpv6,
|
||||
logger: _logger,
|
||||
);
|
||||
|
||||
_logger.printStatus('Waiting for a connection from Flutter on ${device.name}...');
|
||||
final Status discoveryStatus = _logger.startSpinner(
|
||||
timeout: const Duration(seconds: 30),
|
||||
slowWarningCallback: () {
|
||||
// On iOS we rely on mDNS to find Dart VM Service. Remind the user to allow local network permissions on the device.
|
||||
if (_isIOSDevice(device)) {
|
||||
return 'The Dart VM Service was not discovered after 30 seconds. This is taking much longer than expected...\n\n'
|
||||
'Click "Allow" to the prompt on your device asking if you would like to find and connect devices on your local network. '
|
||||
'If you selected "Don\'t Allow", you can turn it on in Settings > Your App Name > Local Network. '
|
||||
"If you don't see your app in the Settings, uninstall the app and rerun to see the prompt again.\n";
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
} else if (_isIOSDevice(device)) {
|
||||
// Protocol Discovery relies on logging. On iOS earlier than 13, logging is gathered using syslog.
|
||||
// syslog is not available for iOS 13+. For iOS 13+, Protocol Discovery gathers logs from the VMService.
|
||||
// Since we don't have access to the VMService yet, Protocol Discovery cannot be used for iOS 13+.
|
||||
// Also, wireless devices must be found using mDNS and cannot use Protocol Discovery.
|
||||
final bool compatibleWithProtocolDiscovery = (device is IOSDevice) &&
|
||||
device.majorSdkVersion < IOSDeviceLogReader.minimumUniversalLoggingSdkVersion &&
|
||||
!isWirelessIOSDevice;
|
||||
|
||||
_logger.printStatus('Waiting for a connection from Flutter on ${device.name}...');
|
||||
final Status discoveryStatus = _logger.startSpinner(
|
||||
timeout: const Duration(seconds: 30),
|
||||
slowWarningCallback: () {
|
||||
// If relying on mDNS to find Dart VM Service, remind the user to allow local network permissions.
|
||||
if (!compatibleWithProtocolDiscovery) {
|
||||
return 'The Dart VM Service was not discovered after 30 seconds. This is taking much longer than expected...\n\n'
|
||||
'Click "Allow" to the prompt asking if you would like to find and connect devices on your local network. '
|
||||
'If you selected "Don\'t Allow", you can turn it on in Settings > Your App Name > Local Network. '
|
||||
"If you don't see your app in the Settings, uninstall the app and rerun to see the prompt again.\n";
|
||||
}
|
||||
return 'The Dart VM Service was not discovered after 30 seconds. This is taking much longer than expected...\n';
|
||||
},
|
||||
);
|
||||
|
||||
return 'The Dart VM Service was not discovered after 30 seconds. This is taking much longer than expected...\n';
|
||||
},
|
||||
);
|
||||
vmServiceUri = vmServiceDiscovery.uris;
|
||||
|
||||
int? devicePort;
|
||||
if (debugPort != null) {
|
||||
devicePort = debugPort;
|
||||
} else if (debugUri != null) {
|
||||
devicePort = debugUri?.port;
|
||||
} else if (deviceVmservicePort != null) {
|
||||
devicePort = deviceVmservicePort;
|
||||
}
|
||||
|
||||
final Future<Uri?> mDNSDiscoveryFuture = MDnsVmServiceDiscovery.instance!.getVMServiceUriForAttach(
|
||||
appId,
|
||||
device,
|
||||
usesIpv6: usesIpv6,
|
||||
useDeviceIPAsHost: isWirelessIOSDevice,
|
||||
deviceVmservicePort: devicePort,
|
||||
);
|
||||
|
||||
Future<Uri?>? protocolDiscoveryFuture;
|
||||
if (compatibleWithProtocolDiscovery) {
|
||||
final ProtocolDiscovery vmServiceDiscovery = ProtocolDiscovery.vmService(
|
||||
device.getLogReader(),
|
||||
portForwarder: device.portForwarder,
|
||||
ipv6: ipv6!,
|
||||
devicePort: devicePort,
|
||||
hostPort: hostVmservicePort,
|
||||
logger: _logger,
|
||||
);
|
||||
protocolDiscoveryFuture = vmServiceDiscovery.uri;
|
||||
}
|
||||
|
||||
final Uri? foundUrl;
|
||||
if (protocolDiscoveryFuture == null) {
|
||||
foundUrl = await mDNSDiscoveryFuture;
|
||||
} else {
|
||||
foundUrl = await Future.any(
|
||||
<Future<Uri?>>[mDNSDiscoveryFuture, protocolDiscoveryFuture]
|
||||
);
|
||||
}
|
||||
// Stop the timer once we receive the first uri.
|
||||
vmServiceUri = vmServiceUri.map((Uri uri) {
|
||||
discoveryStatus.stop();
|
||||
|
||||
vmServiceUri = foundUrl == null
|
||||
? null
|
||||
: Stream<Uri>.value(foundUrl).asBroadcastStream();
|
||||
}
|
||||
// If MDNS discovery fails or we're not on iOS, fallback to ProtocolDiscovery.
|
||||
if (vmServiceUri == null) {
|
||||
final ProtocolDiscovery vmServiceDiscovery =
|
||||
ProtocolDiscovery.vmService(
|
||||
// If it's an Android device, attaching relies on past log searching
|
||||
// to find the service protocol.
|
||||
await device.getLogReader(includePastLogs: device is AndroidDevice),
|
||||
portForwarder: device.portForwarder,
|
||||
ipv6: ipv6!,
|
||||
devicePort: deviceVmservicePort,
|
||||
hostPort: hostVmservicePort,
|
||||
logger: _logger,
|
||||
);
|
||||
_logger.printStatus('Waiting for a connection from Flutter on ${device.name}...');
|
||||
vmServiceUri = vmServiceDiscovery.uris;
|
||||
}
|
||||
return uri;
|
||||
});
|
||||
} else {
|
||||
vmServiceUri = Stream<Uri>
|
||||
.fromFuture(
|
||||
@ -559,8 +489,7 @@ known, it can be explicitly provided to attach via the command-line, e.g.
|
||||
Future<void> _validateArguments() async { }
|
||||
|
||||
bool _isIOSDevice(Device device) {
|
||||
return (device is IOSDevice) ||
|
||||
(device is IOSSimulator) ||
|
||||
return (device.platformType == PlatformType.ios) ||
|
||||
(device is MacOSDesignedForIPadDevice);
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import '../convert.dart';
|
||||
import '../daemon.dart';
|
||||
import '../device.dart';
|
||||
import '../device_port_forwarder.dart';
|
||||
import '../device_vm_service_discovery_for_attach.dart';
|
||||
import '../emulator.dart';
|
||||
import '../features.dart';
|
||||
import '../globals.dart' as globals;
|
||||
@ -1009,6 +1010,8 @@ class DeviceDomain extends Domain {
|
||||
registerHandler('shutdownDartDevelopmentService', shutdownDartDevelopmentService);
|
||||
registerHandler('setExternalDevToolsUriForDartDevelopmentService', setExternalDevToolsUriForDartDevelopmentService);
|
||||
registerHandler('getDiagnostics', getDiagnostics);
|
||||
registerHandler('startVMServiceDiscoveryForAttach', startVMServiceDiscoveryForAttach);
|
||||
registerHandler('stopVMServiceDiscoveryForAttach', stopVMServiceDiscoveryForAttach);
|
||||
|
||||
// Use the device manager discovery so that client provided device types
|
||||
// are usable via the daemon protocol.
|
||||
@ -1325,6 +1328,41 @@ class DeviceDomain extends Domain {
|
||||
...diagnostics,
|
||||
];
|
||||
}
|
||||
|
||||
final Map<String, StreamSubscription<Uri>> _vmServiceDiscoverySubscriptions = <String, StreamSubscription<Uri>>{};
|
||||
|
||||
Future<String> startVMServiceDiscoveryForAttach(Map<String, Object?> args) async {
|
||||
final String? deviceId = _getStringArg(args, 'deviceId', required: true);
|
||||
final String? appId = _getStringArg(args, 'appId');
|
||||
final String? fuchsiaModule = _getStringArg(args, 'fuchsiaModule');
|
||||
final int? filterDevicePort = _getIntArg(args, 'filterDevicePort');
|
||||
final bool? ipv6 = _getBoolArg(args, 'ipv6');
|
||||
|
||||
final Device? device = await daemon.deviceDomain._getDevice(deviceId);
|
||||
if (device == null) {
|
||||
throw DaemonException("device '$deviceId' not found");
|
||||
}
|
||||
|
||||
final String id = '${_id++}';
|
||||
|
||||
final VMServiceDiscoveryForAttach discovery = device.getVMServiceDiscoveryForAttach(
|
||||
appId: appId,
|
||||
fuchsiaModule: fuchsiaModule,
|
||||
filterDevicePort: filterDevicePort,
|
||||
ipv6: ipv6 ?? false,
|
||||
logger: globals.logger
|
||||
);
|
||||
_vmServiceDiscoverySubscriptions[id] = discovery.uris.listen(
|
||||
(Uri uri) => sendEvent('device.VMServiceDiscoveryForAttach.$id', uri.toString()),
|
||||
);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
Future<void> stopVMServiceDiscoveryForAttach(Map<String, Object?> args) async {
|
||||
final String? id = _getStringArg(args, 'id', required: true);
|
||||
await _vmServiceDiscoverySubscriptions.remove(id)?.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
class DevToolsDomain extends Domain {
|
||||
|
@ -16,6 +16,7 @@ import 'base/utils.dart';
|
||||
import 'build_info.dart';
|
||||
import 'devfs.dart';
|
||||
import 'device_port_forwarder.dart';
|
||||
import 'device_vm_service_discovery_for_attach.dart';
|
||||
import 'project.dart';
|
||||
import 'vmservice.dart';
|
||||
import 'web/compile.dart';
|
||||
@ -737,6 +738,35 @@ abstract class Device {
|
||||
/// Clear the device's logs.
|
||||
void clearLogs();
|
||||
|
||||
/// Get the [VMServiceDiscoveryForAttach] instance for this device, which
|
||||
/// discovers, and forwards any necessary ports to the vm service uri of a
|
||||
/// running app on the device.
|
||||
///
|
||||
/// If `appId` is specified, on supported platforms, the service discovery
|
||||
/// will only return the VM service URI from the given app.
|
||||
///
|
||||
/// If `fuchsiaModule` is specified, this will only return the VM service uri
|
||||
/// from the specified Fuchsia module.
|
||||
///
|
||||
/// If `filterDevicePort` is specified, this will only return the VM service
|
||||
/// uri that matches the given port on the device.
|
||||
VMServiceDiscoveryForAttach getVMServiceDiscoveryForAttach({
|
||||
String? appId,
|
||||
String? fuchsiaModule,
|
||||
int? filterDevicePort,
|
||||
int? expectedHostPort,
|
||||
required bool ipv6,
|
||||
required Logger logger,
|
||||
}) =>
|
||||
LogScanningVMServiceDiscoveryForAttach(
|
||||
Future<DeviceLogReader>.value(getLogReader()),
|
||||
portForwarder: portForwarder,
|
||||
devicePort: filterDevicePort,
|
||||
hostPort: expectedHostPort,
|
||||
ipv6: ipv6,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
/// Start an app package on the current device.
|
||||
///
|
||||
/// [platformArgs] allows callers to pass platform-specific arguments to the
|
||||
|
@ -0,0 +1,110 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:async/async.dart';
|
||||
|
||||
import 'base/logger.dart';
|
||||
import 'device.dart';
|
||||
import 'device_port_forwarder.dart';
|
||||
import 'mdns_discovery.dart';
|
||||
import 'protocol_discovery.dart';
|
||||
|
||||
/// Discovers the VM service uri on a device, and forwards the port to the host.
|
||||
///
|
||||
/// This is mainly used during a `flutter attach`.
|
||||
abstract class VMServiceDiscoveryForAttach {
|
||||
VMServiceDiscoveryForAttach();
|
||||
|
||||
/// The discovered VM service URis.
|
||||
///
|
||||
/// Port forwarding is only attempted when this is invoked, for each VM
|
||||
/// Service URI in the stream.
|
||||
Stream<Uri> get uris;
|
||||
}
|
||||
|
||||
/// An implementation of [VMServiceDiscoveryForAttach] that uses log scanning
|
||||
/// for the discovery.
|
||||
class LogScanningVMServiceDiscoveryForAttach extends VMServiceDiscoveryForAttach {
|
||||
LogScanningVMServiceDiscoveryForAttach(
|
||||
Future<DeviceLogReader> logReader, {
|
||||
DevicePortForwarder? portForwarder,
|
||||
int? hostPort,
|
||||
int? devicePort,
|
||||
required bool ipv6,
|
||||
required Logger logger,
|
||||
}) {
|
||||
_protocolDiscovery = (() async => ProtocolDiscovery.vmService(
|
||||
await logReader,
|
||||
portForwarder: portForwarder,
|
||||
ipv6: ipv6,
|
||||
devicePort: devicePort,
|
||||
hostPort: hostPort,
|
||||
logger: logger,
|
||||
))();
|
||||
}
|
||||
|
||||
late final Future<ProtocolDiscovery> _protocolDiscovery;
|
||||
|
||||
@override
|
||||
Stream<Uri> get uris {
|
||||
final StreamController<Uri> controller = StreamController<Uri>();
|
||||
_protocolDiscovery.then(
|
||||
(ProtocolDiscovery protocolDiscovery) async {
|
||||
await controller.addStream(protocolDiscovery.uris);
|
||||
await controller.close();
|
||||
},
|
||||
onError: (Object error) => controller.addError(error),
|
||||
);
|
||||
return controller.stream;
|
||||
}
|
||||
}
|
||||
|
||||
/// An implementation of [VMServiceDiscoveryForAttach] that uses mdns for the
|
||||
/// discovery.
|
||||
class MdnsVMServiceDiscoveryForAttach extends VMServiceDiscoveryForAttach {
|
||||
MdnsVMServiceDiscoveryForAttach({
|
||||
required this.device,
|
||||
this.appId,
|
||||
required this.usesIpv6,
|
||||
required this.useDeviceIPAsHost,
|
||||
this.deviceVmservicePort,
|
||||
this.hostVmservicePort,
|
||||
});
|
||||
|
||||
final Device device;
|
||||
final String? appId;
|
||||
final bool usesIpv6;
|
||||
final bool useDeviceIPAsHost;
|
||||
final int? deviceVmservicePort;
|
||||
final int? hostVmservicePort;
|
||||
|
||||
@override
|
||||
Stream<Uri> get uris {
|
||||
final Future<Uri?> mDNSDiscoveryFuture = MDnsVmServiceDiscovery.instance!.getVMServiceUriForAttach(
|
||||
appId,
|
||||
device,
|
||||
usesIpv6: usesIpv6,
|
||||
useDeviceIPAsHost: useDeviceIPAsHost,
|
||||
deviceVmservicePort: deviceVmservicePort,
|
||||
hostVmservicePort: hostVmservicePort,
|
||||
);
|
||||
|
||||
return Stream<Uri?>.fromFuture(mDNSDiscoveryFuture).where((Uri? uri) => uri != null).cast<Uri>().asBroadcastStream();
|
||||
}
|
||||
}
|
||||
|
||||
/// An implementation of [VMServiceDiscoveryForAttach] that delegates to other
|
||||
/// [VMServiceDiscoveryForAttach] instances for discovery.
|
||||
class DelegateVMServiceDiscoveryForAttach extends VMServiceDiscoveryForAttach {
|
||||
DelegateVMServiceDiscoveryForAttach(this.delegates);
|
||||
|
||||
final List<VMServiceDiscoveryForAttach> delegates;
|
||||
|
||||
@override
|
||||
Stream<Uri> get uris =>
|
||||
StreamGroup.merge<Uri>(
|
||||
delegates.map((VMServiceDiscoveryForAttach delegate) => delegate.uris));
|
||||
}
|
@ -21,6 +21,7 @@ import '../base/time.dart';
|
||||
import '../build_info.dart';
|
||||
import '../device.dart';
|
||||
import '../device_port_forwarder.dart';
|
||||
import '../device_vm_service_discovery_for_attach.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../project.dart';
|
||||
import '../runner/flutter_command.dart';
|
||||
@ -595,6 +596,24 @@ class FuchsiaDevice extends Device {
|
||||
@override
|
||||
void clearLogs() {}
|
||||
|
||||
@override
|
||||
VMServiceDiscoveryForAttach getVMServiceDiscoveryForAttach({
|
||||
String? appId,
|
||||
String? fuchsiaModule,
|
||||
int? filterDevicePort,
|
||||
int? expectedHostPort,
|
||||
required bool ipv6,
|
||||
required Logger logger,
|
||||
}) {
|
||||
if (fuchsiaModule == null) {
|
||||
throwToolExit("'--module' is required for attaching to a Fuchsia device");
|
||||
}
|
||||
if (expectedHostPort != null) {
|
||||
throwToolExit("'--host-vmservice-port' is not supported when attaching to a Fuchsia device");
|
||||
}
|
||||
return FuchsiaIsolateVMServiceDiscoveryForAttach(getIsolateDiscoveryProtocol(fuchsiaModule));
|
||||
}
|
||||
|
||||
/// [true] if the current host address is IPv6.
|
||||
late final bool ipv6 = isIPv6Address(id);
|
||||
|
||||
@ -739,6 +758,30 @@ class FuchsiaDevice extends Device {
|
||||
}
|
||||
}
|
||||
|
||||
class FuchsiaIsolateVMServiceDiscoveryForAttach extends VMServiceDiscoveryForAttach {
|
||||
FuchsiaIsolateVMServiceDiscoveryForAttach(this.isolateDiscoveryProtocol);
|
||||
final FuchsiaIsolateDiscoveryProtocol isolateDiscoveryProtocol;
|
||||
|
||||
@override
|
||||
Stream<Uri> get uris {
|
||||
final Future<Uri> uriFuture = (() async {
|
||||
// Wrapping the call in an anonymous async function for easier error handling.
|
||||
try {
|
||||
return await isolateDiscoveryProtocol.uri;
|
||||
} on Exception {
|
||||
final FuchsiaDevice device = isolateDiscoveryProtocol._device;
|
||||
isolateDiscoveryProtocol.dispose();
|
||||
final List<ForwardedPort> ports = device.portForwarder.forwardedPorts.toList();
|
||||
for (final ForwardedPort port in ports) {
|
||||
await device.portForwarder.unforward(port);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
})();
|
||||
return Stream<Uri>.fromFuture(uriFuture).asBroadcastStream();
|
||||
}
|
||||
}
|
||||
|
||||
class FuchsiaIsolateDiscoveryProtocol {
|
||||
FuchsiaIsolateDiscoveryProtocol(
|
||||
this._device,
|
||||
|
@ -22,6 +22,7 @@ import '../build_info.dart';
|
||||
import '../convert.dart';
|
||||
import '../device.dart';
|
||||
import '../device_port_forwarder.dart';
|
||||
import '../device_vm_service_discovery_for_attach.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../macos/xcdevice.dart';
|
||||
import '../mdns_discovery.dart';
|
||||
@ -1026,6 +1027,43 @@ class IOSDevice extends Device {
|
||||
@override
|
||||
void clearLogs() { }
|
||||
|
||||
@override
|
||||
VMServiceDiscoveryForAttach getVMServiceDiscoveryForAttach({
|
||||
String? appId,
|
||||
String? fuchsiaModule,
|
||||
int? filterDevicePort,
|
||||
int? expectedHostPort,
|
||||
required bool ipv6,
|
||||
required Logger logger,
|
||||
}) {
|
||||
final bool compatibleWithProtocolDiscovery = majorSdkVersion < IOSDeviceLogReader.minimumUniversalLoggingSdkVersion &&
|
||||
!isWirelesslyConnected;
|
||||
final MdnsVMServiceDiscoveryForAttach mdnsVMServiceDiscoveryForAttach = MdnsVMServiceDiscoveryForAttach(
|
||||
device: this,
|
||||
appId: appId,
|
||||
deviceVmservicePort: filterDevicePort,
|
||||
hostVmservicePort: expectedHostPort,
|
||||
usesIpv6: ipv6,
|
||||
useDeviceIPAsHost: isWirelesslyConnected,
|
||||
);
|
||||
|
||||
if (compatibleWithProtocolDiscovery) {
|
||||
return DelegateVMServiceDiscoveryForAttach(<VMServiceDiscoveryForAttach>[
|
||||
mdnsVMServiceDiscoveryForAttach,
|
||||
super.getVMServiceDiscoveryForAttach(
|
||||
appId: appId,
|
||||
fuchsiaModule: fuchsiaModule,
|
||||
filterDevicePort: filterDevicePort,
|
||||
expectedHostPort: expectedHostPort,
|
||||
ipv6: ipv6,
|
||||
logger: logger,
|
||||
),
|
||||
]);
|
||||
} else {
|
||||
return mdnsVMServiceDiscoveryForAttach;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool get supportsScreenshot {
|
||||
if (isCoreDevice) {
|
||||
|
@ -21,6 +21,7 @@ import '../convert.dart';
|
||||
import '../devfs.dart';
|
||||
import '../device.dart';
|
||||
import '../device_port_forwarder.dart';
|
||||
import '../device_vm_service_discovery_for_attach.dart';
|
||||
import '../globals.dart' as globals;
|
||||
import '../macos/xcode.dart';
|
||||
import '../project.dart';
|
||||
@ -654,6 +655,37 @@ class IOSSimulator extends Device {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
VMServiceDiscoveryForAttach getVMServiceDiscoveryForAttach({
|
||||
String? appId,
|
||||
String? fuchsiaModule,
|
||||
int? filterDevicePort,
|
||||
int? expectedHostPort,
|
||||
required bool ipv6,
|
||||
required Logger logger,
|
||||
}) {
|
||||
final MdnsVMServiceDiscoveryForAttach mdnsVMServiceDiscoveryForAttach = MdnsVMServiceDiscoveryForAttach(
|
||||
device: this,
|
||||
appId: appId,
|
||||
deviceVmservicePort: filterDevicePort,
|
||||
hostVmservicePort: expectedHostPort,
|
||||
usesIpv6: ipv6,
|
||||
useDeviceIPAsHost: false,
|
||||
);
|
||||
|
||||
return DelegateVMServiceDiscoveryForAttach(<VMServiceDiscoveryForAttach>[
|
||||
mdnsVMServiceDiscoveryForAttach,
|
||||
super.getVMServiceDiscoveryForAttach(
|
||||
appId: appId,
|
||||
fuchsiaModule: fuchsiaModule,
|
||||
filterDevicePort: filterDevicePort,
|
||||
expectedHostPort: expectedHostPort,
|
||||
ipv6: ipv6,
|
||||
logger: logger,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get supportsScreenshot => true;
|
||||
|
||||
|
@ -14,6 +14,7 @@ import '../base/platform.dart';
|
||||
import '../build_info.dart';
|
||||
import '../desktop_device.dart';
|
||||
import '../device.dart';
|
||||
import '../device_vm_service_discovery_for_attach.dart';
|
||||
import '../ios/ios_workflow.dart';
|
||||
import '../project.dart';
|
||||
|
||||
@ -59,6 +60,37 @@ class MacOSDesignedForIPadDevice extends DesktopDevice {
|
||||
@override
|
||||
String? executablePathForDevice(ApplicationPackage package, BuildInfo buildInfo) => null;
|
||||
|
||||
@override
|
||||
VMServiceDiscoveryForAttach getVMServiceDiscoveryForAttach({
|
||||
String? appId,
|
||||
String? fuchsiaModule,
|
||||
int? filterDevicePort,
|
||||
int? expectedHostPort,
|
||||
required bool ipv6,
|
||||
required Logger logger,
|
||||
}) {
|
||||
final MdnsVMServiceDiscoveryForAttach mdnsVMServiceDiscoveryForAttach = MdnsVMServiceDiscoveryForAttach(
|
||||
device: this,
|
||||
appId: appId,
|
||||
deviceVmservicePort: filterDevicePort,
|
||||
hostVmservicePort: expectedHostPort,
|
||||
usesIpv6: ipv6,
|
||||
useDeviceIPAsHost: false,
|
||||
);
|
||||
|
||||
return DelegateVMServiceDiscoveryForAttach(<VMServiceDiscoveryForAttach>[
|
||||
mdnsVMServiceDiscoveryForAttach,
|
||||
super.getVMServiceDiscoveryForAttach(
|
||||
appId: appId,
|
||||
fuchsiaModule: fuchsiaModule,
|
||||
filterDevicePort: filterDevicePort,
|
||||
expectedHostPort: expectedHostPort,
|
||||
ipv6: ipv6,
|
||||
logger: logger,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LaunchResult> startApp(
|
||||
ApplicationPackage? package, {
|
||||
|
@ -17,6 +17,7 @@ import '../convert.dart';
|
||||
import '../daemon.dart';
|
||||
import '../device.dart';
|
||||
import '../device_port_forwarder.dart';
|
||||
import '../device_vm_service_discovery_for_attach.dart';
|
||||
import '../project.dart';
|
||||
import 'debounce_data_stream.dart';
|
||||
import 'file_transfer.dart';
|
||||
@ -296,6 +297,35 @@ class ProxiedDevice extends Device {
|
||||
@override
|
||||
void clearLogs() => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
VMServiceDiscoveryForAttach getVMServiceDiscoveryForAttach({
|
||||
String? appId,
|
||||
String? fuchsiaModule,
|
||||
int? filterDevicePort,
|
||||
int? expectedHostPort,
|
||||
required bool ipv6,
|
||||
required Logger logger,
|
||||
}) =>
|
||||
ProxiedVMServiceDiscoveryForAttach(
|
||||
connection,
|
||||
id,
|
||||
proxiedPortForwarder: proxiedPortForwarder,
|
||||
appId: appId,
|
||||
fuchsiaModule: fuchsiaModule,
|
||||
filterDevicePort: filterDevicePort,
|
||||
expectedHostPort: expectedHostPort,
|
||||
ipv6: ipv6,
|
||||
logger: logger,
|
||||
fallbackDiscovery: () => super.getVMServiceDiscoveryForAttach(
|
||||
appId: appId,
|
||||
fuchsiaModule: fuchsiaModule,
|
||||
filterDevicePort: filterDevicePort,
|
||||
expectedHostPort: expectedHostPort,
|
||||
ipv6: ipv6,
|
||||
logger: logger,
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Future<LaunchResult> startApp(
|
||||
PrebuiltApplicationPackage package, {
|
||||
@ -863,3 +893,84 @@ class ProxiedDartDevelopmentService implements DartDevelopmentService {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ProxiedVMServiceDiscoveryForAttach extends VMServiceDiscoveryForAttach {
|
||||
ProxiedVMServiceDiscoveryForAttach(
|
||||
this.connection,
|
||||
this.deviceId, {
|
||||
required this.proxiedPortForwarder,
|
||||
required this.fallbackDiscovery,
|
||||
this.appId,
|
||||
this.fuchsiaModule,
|
||||
this.filterDevicePort,
|
||||
this.expectedHostPort,
|
||||
required this.ipv6,
|
||||
required this.logger,
|
||||
});
|
||||
|
||||
/// [DaemonConnection] used to communicate with the daemon.
|
||||
final DaemonConnection connection;
|
||||
|
||||
final String deviceId;
|
||||
|
||||
final String? appId;
|
||||
final String? fuchsiaModule;
|
||||
final int? filterDevicePort;
|
||||
final int? expectedHostPort;
|
||||
final bool ipv6;
|
||||
final Logger logger;
|
||||
|
||||
final ProxiedPortForwarder proxiedPortForwarder;
|
||||
|
||||
VMServiceDiscoveryForAttach Function() fallbackDiscovery;
|
||||
|
||||
Stream<Uri>? _uris;
|
||||
|
||||
@override
|
||||
Stream<Uri> get uris {
|
||||
if (_uris == null) {
|
||||
String? requestId;
|
||||
final StreamController<Uri> controller = StreamController<Uri>();
|
||||
|
||||
controller.onListen = () {
|
||||
connection.sendRequest('device.startVMServiceDiscoveryForAttach', <String, Object?>{
|
||||
'deviceId': deviceId,
|
||||
'appId': appId,
|
||||
'fuchsiaModule': fuchsiaModule,
|
||||
'filterDevicePort': filterDevicePort,
|
||||
'ipv6': ipv6,
|
||||
}).then(
|
||||
(Object? response) async {
|
||||
requestId = _cast<String>(response);
|
||||
final Stream<Uri> vmService = connection
|
||||
.listenToEvent('device.VMServiceDiscoveryForAttach.$requestId')
|
||||
.asyncMap((DaemonEventData event) async {
|
||||
// Forward the port.
|
||||
final Uri remoteUri = Uri.parse(_cast<String>(event.data));
|
||||
final int port = remoteUri.port;
|
||||
final int localPort = await proxiedPortForwarder.forward(port, hostPort: expectedHostPort, ipv6: ipv6);
|
||||
return remoteUri.replace(port: localPort);
|
||||
});
|
||||
await controller.addStream(vmService);
|
||||
},
|
||||
onError: (Object e) {
|
||||
// Daemon throws string types.
|
||||
if (e is String && e.contains('command not understood')) {
|
||||
// Use a fallback if the daemon does not support VM service discovery.
|
||||
controller.addStream(fallbackDiscovery().uris);
|
||||
} else {
|
||||
controller.addError(e);
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
controller.onCancel = () {
|
||||
if (requestId != null) {
|
||||
connection.sendRequest('device.stopVMServiceDiscoveryForAttach', <String, Object?>{'id': requestId});
|
||||
}
|
||||
};
|
||||
_uris = controller.stream;
|
||||
}
|
||||
return _uris!;
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import 'package:flutter_tools/src/commands/attach.dart';
|
||||
import 'package:flutter_tools/src/compile.dart';
|
||||
import 'package:flutter_tools/src/device.dart';
|
||||
import 'package:flutter_tools/src/device_port_forwarder.dart';
|
||||
import 'package:flutter_tools/src/device_vm_service_discovery_for_attach.dart';
|
||||
import 'package:flutter_tools/src/ios/application_package.dart';
|
||||
import 'package:flutter_tools/src/ios/devices.dart';
|
||||
import 'package:flutter_tools/src/mdns_discovery.dart';
|
||||
@ -204,8 +205,8 @@ void main() {
|
||||
processInfo: processInfo,
|
||||
fileSystem: testFileSystem,
|
||||
)).run(<String>['attach']);
|
||||
await completer.future;
|
||||
await Future.wait<void>(<Future<void>>[
|
||||
completer.future,
|
||||
fakeLogReader.dispose(),
|
||||
loggerSubscription.cancel(),
|
||||
]);
|
||||
@ -275,8 +276,8 @@ void main() {
|
||||
processInfo: processInfo,
|
||||
fileSystem: testFileSystem,
|
||||
)).run(<String>['attach', '--local-engine-src-path=$localEngineSrc', '--local-engine=$localEngineDir', '--local-engine-host=$localEngineDir']);
|
||||
await completer.future;
|
||||
await Future.wait<void>(<Future<void>>[
|
||||
completer.future,
|
||||
fakeLogReader.dispose(),
|
||||
loggerSubscription.cancel(),
|
||||
]);
|
||||
@ -331,12 +332,15 @@ void main() {
|
||||
)).run(<String>['attach']);
|
||||
await fakeLogReader.dispose();
|
||||
|
||||
expect(portForwarder.devicePort, devicePort);
|
||||
expect(portForwarder.hostPort, hostPort);
|
||||
expect(hotRunnerFactory.devices, hasLength(1));
|
||||
// Listen to the URI before checking port forwarder. Port forwarding
|
||||
// is done as a side effect when generating the uri.
|
||||
final FlutterDevice flutterDevice = hotRunnerFactory.devices.first;
|
||||
final Uri? vmServiceUri = await flutterDevice.vmServiceUris?.first;
|
||||
expect(vmServiceUri.toString(), 'http://127.0.0.1:$hostPort/xyz/');
|
||||
|
||||
expect(portForwarder.devicePort, devicePort);
|
||||
expect(portForwarder.hostPort, hostPort);
|
||||
expect(hotRunnerFactory.devices, hasLength(1));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => testFileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
@ -396,13 +400,15 @@ void main() {
|
||||
)).run(<String>['attach']);
|
||||
await fakeLogReader.dispose();
|
||||
|
||||
expect(portForwarder.devicePort, null);
|
||||
expect(portForwarder.hostPort, hostPort);
|
||||
expect(hotRunnerFactory.devices, hasLength(1));
|
||||
|
||||
// Listen to the URI before checking port forwarder. Port forwarding
|
||||
// is done as a side effect when generating the uri.
|
||||
final FlutterDevice flutterDevice = hotRunnerFactory.devices.first;
|
||||
final Uri? vmServiceUri = await flutterDevice.vmServiceUris?.first;
|
||||
expect(vmServiceUri.toString(), 'http://111.111.111.111:123/xyz/');
|
||||
|
||||
expect(portForwarder.devicePort, null);
|
||||
expect(portForwarder.hostPort, hostPort);
|
||||
expect(hotRunnerFactory.devices, hasLength(1));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => testFileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
@ -467,13 +473,15 @@ void main() {
|
||||
)).run(<String>['attach', '--debug-port', '123']);
|
||||
await fakeLogReader.dispose();
|
||||
|
||||
expect(portForwarder.devicePort, null);
|
||||
expect(portForwarder.hostPort, hostPort);
|
||||
expect(hotRunnerFactory.devices, hasLength(1));
|
||||
|
||||
// Listen to the URI before checking port forwarder. Port forwarding
|
||||
// is done as a side effect when generating the uri.
|
||||
final FlutterDevice flutterDevice = hotRunnerFactory.devices.first;
|
||||
final Uri? vmServiceUri = await flutterDevice.vmServiceUris?.first;
|
||||
expect(vmServiceUri.toString(), 'http://111.111.111.111:123/xyz/');
|
||||
|
||||
expect(portForwarder.devicePort, null);
|
||||
expect(portForwarder.hostPort, hostPort);
|
||||
expect(hotRunnerFactory.devices, hasLength(1));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => testFileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
@ -542,13 +550,15 @@ void main() {
|
||||
)).run(<String>['attach', '--debug-url', 'https://0.0.0.0:123']);
|
||||
await fakeLogReader.dispose();
|
||||
|
||||
expect(portForwarder.devicePort, null);
|
||||
expect(portForwarder.hostPort, hostPort);
|
||||
expect(hotRunnerFactory.devices, hasLength(1));
|
||||
|
||||
// Listen to the URI before checking port forwarder. Port forwarding
|
||||
// is done as a side effect when generating the uri.
|
||||
final FlutterDevice flutterDevice = hotRunnerFactory.devices.first;
|
||||
final Uri? vmServiceUri = await flutterDevice.vmServiceUris?.first;
|
||||
expect(vmServiceUri.toString(), 'http://111.111.111.111:123/xyz/');
|
||||
|
||||
expect(portForwarder.devicePort, null);
|
||||
expect(portForwarder.hostPort, hostPort);
|
||||
expect(hotRunnerFactory.devices, hasLength(1));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => testFileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
@ -1464,6 +1474,24 @@ class FakeAndroidDevice extends Fake implements AndroidDevice {
|
||||
|
||||
@override
|
||||
bool get ephemeral => true;
|
||||
|
||||
@override
|
||||
VMServiceDiscoveryForAttach getVMServiceDiscoveryForAttach({
|
||||
String? appId,
|
||||
String? fuchsiaModule,
|
||||
int? filterDevicePort,
|
||||
int? expectedHostPort,
|
||||
required bool ipv6,
|
||||
required Logger logger,
|
||||
}) =>
|
||||
LogScanningVMServiceDiscoveryForAttach(
|
||||
Future<DeviceLogReader>.value(getLogReader()),
|
||||
portForwarder: portForwarder,
|
||||
devicePort: filterDevicePort,
|
||||
hostPort: expectedHostPort,
|
||||
ipv6: ipv6,
|
||||
logger: logger,
|
||||
);
|
||||
}
|
||||
|
||||
class FakeIOSDevice extends Fake implements IOSDevice {
|
||||
@ -1527,6 +1555,43 @@ class FakeIOSDevice extends Fake implements IOSDevice {
|
||||
|
||||
@override
|
||||
bool get ephemeral => true;
|
||||
|
||||
@override
|
||||
VMServiceDiscoveryForAttach getVMServiceDiscoveryForAttach({
|
||||
String? appId,
|
||||
String? fuchsiaModule,
|
||||
int? filterDevicePort,
|
||||
int? expectedHostPort,
|
||||
required bool ipv6,
|
||||
required Logger logger,
|
||||
}) {
|
||||
final bool compatibleWithProtocolDiscovery = majorSdkVersion < IOSDeviceLogReader.minimumUniversalLoggingSdkVersion &&
|
||||
!isWirelesslyConnected;
|
||||
final MdnsVMServiceDiscoveryForAttach mdnsVMServiceDiscoveryForAttach = MdnsVMServiceDiscoveryForAttach(
|
||||
device: this,
|
||||
appId: appId,
|
||||
deviceVmservicePort: filterDevicePort,
|
||||
hostVmservicePort: expectedHostPort,
|
||||
usesIpv6: ipv6,
|
||||
useDeviceIPAsHost: isWirelesslyConnected,
|
||||
);
|
||||
|
||||
if (compatibleWithProtocolDiscovery) {
|
||||
return DelegateVMServiceDiscoveryForAttach(<VMServiceDiscoveryForAttach>[
|
||||
mdnsVMServiceDiscoveryForAttach,
|
||||
LogScanningVMServiceDiscoveryForAttach(
|
||||
Future<DeviceLogReader>.value(getLogReader()),
|
||||
portForwarder: portForwarder,
|
||||
devicePort: filterDevicePort,
|
||||
hostPort: expectedHostPort,
|
||||
ipv6: ipv6,
|
||||
logger: logger,
|
||||
),
|
||||
]);
|
||||
} else {
|
||||
return mdnsVMServiceDiscoveryForAttach;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FakeMDnsClient extends Fake implements MDnsClient {
|
||||
|
@ -0,0 +1,132 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/device_port_forwarder.dart';
|
||||
import 'package:flutter_tools/src/device_vm_service_discovery_for_attach.dart';
|
||||
import 'package:test/fake.dart';
|
||||
|
||||
import '../src/common.dart';
|
||||
import '../src/fake_devices.dart';
|
||||
|
||||
void main() {
|
||||
group('LogScanningVMServiceDiscoveryForAttach', () {
|
||||
testWithoutContext('can discover the port', () async {
|
||||
final FakeDeviceLogReader logReader = FakeDeviceLogReader();
|
||||
final LogScanningVMServiceDiscoveryForAttach discovery = LogScanningVMServiceDiscoveryForAttach(
|
||||
Future<FakeDeviceLogReader>.value(logReader),
|
||||
ipv6: false,
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
|
||||
logReader.addLine('The Dart VM service is listening on http://127.0.0.1:9999');
|
||||
|
||||
expect(await discovery.uris.first, Uri.parse('http://127.0.0.1:9999'));
|
||||
});
|
||||
|
||||
testWithoutContext('ignores the port that does not match devicePort', () async {
|
||||
final FakeDeviceLogReader logReader = FakeDeviceLogReader();
|
||||
final LogScanningVMServiceDiscoveryForAttach discovery = LogScanningVMServiceDiscoveryForAttach(
|
||||
Future<FakeDeviceLogReader>.value(logReader),
|
||||
devicePort: 9998,
|
||||
ipv6: false,
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
|
||||
logReader.addLine('The Dart VM service is listening on http://127.0.0.1:9999');
|
||||
logReader.addLine('The Dart VM service is listening on http://127.0.0.1:9998');
|
||||
|
||||
expect(await discovery.uris.first, Uri.parse('http://127.0.0.1:9998'));
|
||||
});
|
||||
|
||||
testWithoutContext('forwards the port if given a port forwarder', () async {
|
||||
final FakeDeviceLogReader logReader = FakeDeviceLogReader();
|
||||
final FakePortForwarder portForwarder = FakePortForwarder(9900);
|
||||
final LogScanningVMServiceDiscoveryForAttach discovery = LogScanningVMServiceDiscoveryForAttach(
|
||||
Future<FakeDeviceLogReader>.value(logReader),
|
||||
portForwarder: portForwarder,
|
||||
ipv6: false,
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
|
||||
logReader.addLine('The Dart VM service is listening on http://127.0.0.1:9999');
|
||||
|
||||
expect(await discovery.uris.first, Uri.parse('http://127.0.0.1:9900'));
|
||||
expect(portForwarder.forwardDevicePort, 9999);
|
||||
expect(portForwarder.forwardHostPort, null);
|
||||
});
|
||||
|
||||
testWithoutContext('uses the host port if given', () async {
|
||||
final FakeDeviceLogReader logReader = FakeDeviceLogReader();
|
||||
final FakePortForwarder portForwarder = FakePortForwarder(9900);
|
||||
final LogScanningVMServiceDiscoveryForAttach discovery = LogScanningVMServiceDiscoveryForAttach(
|
||||
Future<FakeDeviceLogReader>.value(logReader),
|
||||
portForwarder: portForwarder,
|
||||
hostPort: 9901,
|
||||
ipv6: false,
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
|
||||
logReader.addLine('The Dart VM service is listening on http://127.0.0.1:9999');
|
||||
|
||||
expect(await discovery.uris.first, Uri.parse('http://127.0.0.1:9900'));
|
||||
expect(portForwarder.forwardDevicePort, 9999);
|
||||
expect(portForwarder.forwardHostPort, 9901);
|
||||
});
|
||||
});
|
||||
|
||||
group('DelegateVMServiceDiscoveryForAttach', () {
|
||||
late List<Uri> uris1;
|
||||
late List<Uri> uris2;
|
||||
late FakeVmServiceDiscoveryForAttach fakeDiscovery1;
|
||||
late FakeVmServiceDiscoveryForAttach fakeDiscovery2;
|
||||
late DelegateVMServiceDiscoveryForAttach delegateDiscovery;
|
||||
|
||||
setUp(() {
|
||||
uris1 = <Uri>[];
|
||||
uris2 = <Uri>[];
|
||||
fakeDiscovery1 = FakeVmServiceDiscoveryForAttach(uris1);
|
||||
fakeDiscovery2 = FakeVmServiceDiscoveryForAttach(uris2);
|
||||
delegateDiscovery = DelegateVMServiceDiscoveryForAttach(<VMServiceDiscoveryForAttach>[fakeDiscovery1, fakeDiscovery2]);
|
||||
});
|
||||
|
||||
testWithoutContext('uris returns from both delegates', () async {
|
||||
uris1.add(Uri.parse('http://127.0.0.1:1'));
|
||||
uris1.add(Uri.parse('http://127.0.0.2:2'));
|
||||
uris2.add(Uri.parse('http://127.0.0.3:3'));
|
||||
uris2.add(Uri.parse('http://127.0.0.4:4'));
|
||||
|
||||
expect(await delegateDiscovery.uris.toList(), unorderedEquals(<Uri>[
|
||||
Uri.parse('http://127.0.0.1:1'),
|
||||
Uri.parse('http://127.0.0.2:2'),
|
||||
Uri.parse('http://127.0.0.3:3'),
|
||||
Uri.parse('http://127.0.0.4:4'),
|
||||
]));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class FakePortForwarder extends Fake implements DevicePortForwarder {
|
||||
FakePortForwarder(this.forwardReturnValue);
|
||||
|
||||
int? forwardDevicePort;
|
||||
int? forwardHostPort;
|
||||
final int forwardReturnValue;
|
||||
|
||||
@override
|
||||
Future<int> forward(int devicePort, { int? hostPort }) async {
|
||||
forwardDevicePort = devicePort;
|
||||
forwardHostPort = hostPort;
|
||||
return forwardReturnValue;
|
||||
}
|
||||
}
|
||||
|
||||
class FakeVmServiceDiscoveryForAttach extends Fake implements VMServiceDiscoveryForAttach {
|
||||
FakeVmServiceDiscoveryForAttach(this._uris);
|
||||
|
||||
final List<Uri> _uris;
|
||||
|
||||
@override
|
||||
Stream<Uri> get uris => Stream<Uri>.fromIterable(_uris);
|
||||
}
|
@ -14,6 +14,7 @@ import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/utils.dart';
|
||||
import 'package:flutter_tools/src/daemon.dart';
|
||||
import 'package:flutter_tools/src/device.dart';
|
||||
import 'package:flutter_tools/src/device_vm_service_discovery_for_attach.dart';
|
||||
import 'package:flutter_tools/src/proxied_devices/devices.dart';
|
||||
import 'package:flutter_tools/src/proxied_devices/file_transfer.dart';
|
||||
import 'package:test/fake.dart';
|
||||
@ -803,6 +804,187 @@ void main() {
|
||||
expect(localDds.shutdownCalled, true);
|
||||
});
|
||||
});
|
||||
|
||||
group('ProxiedVMServiceDiscoveryForAttach', () {
|
||||
testWithoutContext('sends the request and forwards the port', () async {
|
||||
final FakeProxiedPortForwarder portForwarder = FakeProxiedPortForwarder();
|
||||
portForwarder.forwardReturnValue = 400;
|
||||
final ProxiedVMServiceDiscoveryForAttach discovery = ProxiedVMServiceDiscoveryForAttach(
|
||||
clientDaemonConnection,
|
||||
'test_device',
|
||||
proxiedPortForwarder: portForwarder,
|
||||
fallbackDiscovery: () => throw UnimplementedError(),
|
||||
ipv6: false,
|
||||
logger: bufferLogger,
|
||||
);
|
||||
|
||||
final Completer<Uri> uriCompleter = Completer<Uri>();
|
||||
|
||||
// Start listening on the stream to trigger sending the request.
|
||||
discovery.uris.listen(uriCompleter.complete);
|
||||
|
||||
final Stream<DaemonMessage> broadcastOutput = serverDaemonConnection.incomingCommands.asBroadcastStream();
|
||||
final DaemonMessage startMessage = await broadcastOutput.first;
|
||||
expect(startMessage.data['id'], isNotNull);
|
||||
expect(startMessage.data['method'], 'device.startVMServiceDiscoveryForAttach');
|
||||
expect(startMessage.data['params'], <String, Object?>{
|
||||
'deviceId': 'test_device',
|
||||
'appId': null,
|
||||
'fuchsiaModule': null,
|
||||
'filterDevicePort': null,
|
||||
'ipv6': false,
|
||||
});
|
||||
|
||||
serverDaemonConnection.sendResponse(startMessage.data['id']!, 'request_id');
|
||||
serverDaemonConnection.sendEvent('device.VMServiceDiscoveryForAttach.request_id', 'http://127.0.0.1:300/auth_code');
|
||||
|
||||
expect(await uriCompleter.future, Uri.parse('http://127.0.0.1:400/auth_code'));
|
||||
expect(portForwarder.forwardedDevicePort, 300);
|
||||
expect(portForwarder.forwardedHostPort, null);
|
||||
});
|
||||
|
||||
testWithoutContext('sends additional information, and forwards the correct port', () async {
|
||||
final FakeProxiedPortForwarder portForwarder = FakeProxiedPortForwarder();
|
||||
portForwarder.forwardReturnValue = 400;
|
||||
final ProxiedVMServiceDiscoveryForAttach discovery = ProxiedVMServiceDiscoveryForAttach(
|
||||
clientDaemonConnection,
|
||||
'test_device',
|
||||
proxiedPortForwarder: portForwarder,
|
||||
fallbackDiscovery: () => throw UnimplementedError(),
|
||||
appId: 'test_app_id',
|
||||
fuchsiaModule: 'test_fuchsia_module',
|
||||
filterDevicePort: 100,
|
||||
expectedHostPort: 200,
|
||||
ipv6: false,
|
||||
logger: bufferLogger,
|
||||
);
|
||||
|
||||
final Completer<Uri> uriCompleter = Completer<Uri>();
|
||||
|
||||
// Start listening on the stream to trigger sending the request.
|
||||
discovery.uris.listen(uriCompleter.complete);
|
||||
|
||||
final Stream<DaemonMessage> broadcastOutput = serverDaemonConnection.incomingCommands.asBroadcastStream();
|
||||
final DaemonMessage startMessage = await broadcastOutput.first;
|
||||
expect(startMessage.data['id'], isNotNull);
|
||||
expect(startMessage.data['method'], 'device.startVMServiceDiscoveryForAttach');
|
||||
expect(startMessage.data['params'], <String, Object?>{
|
||||
'deviceId': 'test_device',
|
||||
'appId': 'test_app_id',
|
||||
'fuchsiaModule': 'test_fuchsia_module',
|
||||
'filterDevicePort': 100,
|
||||
'ipv6': false,
|
||||
});
|
||||
|
||||
serverDaemonConnection.sendResponse(startMessage.data['id']!, 'request_id');
|
||||
serverDaemonConnection.sendEvent('device.VMServiceDiscoveryForAttach.request_id', 'http://127.0.0.1:300/auth_code');
|
||||
|
||||
expect(await uriCompleter.future, Uri.parse('http://127.0.0.1:400/auth_code'));
|
||||
expect(portForwarder.forwardedDevicePort, 300);
|
||||
expect(portForwarder.forwardedHostPort, 200);
|
||||
});
|
||||
|
||||
testWithoutContext('use the fallback discovery if the remote daemon does not support proxied discovery', () async {
|
||||
final FakeProxiedPortForwarder portForwarder = FakeProxiedPortForwarder();
|
||||
final Stream<Uri> fallbackUri = Stream<Uri>.value(Uri.parse('http://127.0.0.1:500/fallback_auth_code'));
|
||||
final ProxiedVMServiceDiscoveryForAttach discovery = ProxiedVMServiceDiscoveryForAttach(
|
||||
clientDaemonConnection,
|
||||
'test_device',
|
||||
proxiedPortForwarder: portForwarder,
|
||||
fallbackDiscovery: () => FakeVMServiceDiscoveryForAttach(fallbackUri),
|
||||
ipv6: false,
|
||||
logger: bufferLogger,
|
||||
);
|
||||
|
||||
final Completer<Uri> uriCompleter = Completer<Uri>();
|
||||
|
||||
// Start listening on the stream to trigger sending the request.
|
||||
discovery.uris.listen(uriCompleter.complete);
|
||||
|
||||
final Stream<DaemonMessage> broadcastOutput = serverDaemonConnection.incomingCommands.asBroadcastStream();
|
||||
final DaemonMessage startMessage = await broadcastOutput.first;
|
||||
expect(startMessage.data['id'], isNotNull);
|
||||
expect(startMessage.data['method'], 'device.startVMServiceDiscoveryForAttach');
|
||||
expect(startMessage.data['params'], <String, Object?>{
|
||||
'deviceId': 'test_device',
|
||||
'appId': null,
|
||||
'fuchsiaModule': null,
|
||||
'filterDevicePort': null,
|
||||
'ipv6': false,
|
||||
});
|
||||
serverDaemonConnection.sendErrorResponse(startMessage.data['id']!, 'command not understood: device.startDartDevelopmentService', StackTrace.current);
|
||||
|
||||
expect(await uriCompleter.future, Uri.parse('http://127.0.0.1:500/fallback_auth_code'));
|
||||
expect(portForwarder.forwardedDevicePort, null);
|
||||
expect(portForwarder.forwardedHostPort, null);
|
||||
});
|
||||
|
||||
testWithoutContext('forwards other error from the daemon', () async {
|
||||
final FakeProxiedPortForwarder portForwarder = FakeProxiedPortForwarder();
|
||||
final Stream<Uri> fallbackUri = Stream<Uri>.value(Uri.parse('http://127.0.0.1:500/fallback_auth_code'));
|
||||
final ProxiedVMServiceDiscoveryForAttach discovery = ProxiedVMServiceDiscoveryForAttach(
|
||||
clientDaemonConnection,
|
||||
'test_device',
|
||||
proxiedPortForwarder: portForwarder,
|
||||
fallbackDiscovery: () => FakeVMServiceDiscoveryForAttach(fallbackUri),
|
||||
ipv6: false,
|
||||
logger: bufferLogger,
|
||||
);
|
||||
|
||||
// Start listening on the stream to trigger sending the request.
|
||||
final Future<Uri> uriFuture = discovery.uris.first;
|
||||
|
||||
final Stream<DaemonMessage> broadcastOutput = serverDaemonConnection.incomingCommands.asBroadcastStream();
|
||||
final DaemonMessage startMessage = await broadcastOutput.first;
|
||||
expect(startMessage.data['id'], isNotNull);
|
||||
expect(startMessage.data['method'], 'device.startVMServiceDiscoveryForAttach');
|
||||
expect(startMessage.data['params'], <String, Object?>{
|
||||
'deviceId': 'test_device',
|
||||
'appId': null,
|
||||
'fuchsiaModule': null,
|
||||
'filterDevicePort': null,
|
||||
'ipv6': false,
|
||||
});
|
||||
serverDaemonConnection.sendErrorResponse(startMessage.data['id']!, 'other error', StackTrace.current);
|
||||
|
||||
expect(uriFuture, throwsA('other error'));
|
||||
expect(portForwarder.forwardedDevicePort, null);
|
||||
expect(portForwarder.forwardedHostPort, null);
|
||||
});
|
||||
|
||||
testWithoutContext('forwards the port forwarder error', () async {
|
||||
final FakeProxiedPortForwarder portForwarder = FakeProxiedPortForwarder();
|
||||
portForwarder.forwardThrowException = TestException();
|
||||
final ProxiedVMServiceDiscoveryForAttach discovery = ProxiedVMServiceDiscoveryForAttach(
|
||||
clientDaemonConnection,
|
||||
'test_device',
|
||||
proxiedPortForwarder: portForwarder,
|
||||
fallbackDiscovery: () => throw UnimplementedError(),
|
||||
ipv6: false,
|
||||
logger: bufferLogger,
|
||||
);
|
||||
|
||||
// Start listening on the stream to trigger sending the request.
|
||||
final Future<Uri> uriFuture = discovery.uris.first;
|
||||
|
||||
final Stream<DaemonMessage> broadcastOutput = serverDaemonConnection.incomingCommands.asBroadcastStream();
|
||||
final DaemonMessage startMessage = await broadcastOutput.first;
|
||||
expect(startMessage.data['id'], isNotNull);
|
||||
expect(startMessage.data['method'], 'device.startVMServiceDiscoveryForAttach');
|
||||
expect(startMessage.data['params'], <String, Object?>{
|
||||
'deviceId': 'test_device',
|
||||
'appId': null,
|
||||
'fuchsiaModule': null,
|
||||
'filterDevicePort': null,
|
||||
'ipv6': false,
|
||||
});
|
||||
|
||||
serverDaemonConnection.sendResponse(startMessage.data['id']!, 'request_id');
|
||||
serverDaemonConnection.sendEvent('device.VMServiceDiscoveryForAttach.request_id', 'http://127.0.0.1:300/auth_code');
|
||||
|
||||
expect(uriFuture, throwsA(isA<TestException>()));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class FakeDaemonStreams implements DaemonStreams {
|
||||
@ -934,6 +1116,7 @@ class FakeProxiedPortForwarder extends Fake implements ProxiedPortForwarder {
|
||||
int? originalRemotePortReturnValue;
|
||||
int? receivedLocalForwardedPort;
|
||||
|
||||
Exception? forwardThrowException;
|
||||
int? forwardReturnValue;
|
||||
int? forwardedDevicePort;
|
||||
int? forwardedHostPort;
|
||||
@ -950,6 +1133,9 @@ class FakeProxiedPortForwarder extends Fake implements ProxiedPortForwarder {
|
||||
forwardedDevicePort = devicePort;
|
||||
forwardedHostPort = hostPort;
|
||||
forwardedIpv6 = ipv6;
|
||||
if (forwardThrowException != null) {
|
||||
throw forwardThrowException!;
|
||||
}
|
||||
return forwardReturnValue!;
|
||||
}
|
||||
}
|
||||
@ -999,3 +1185,12 @@ class FakeFileTransfer extends Fake implements FileTransfer {
|
||||
@override
|
||||
Future<Uint8List> binaryForRebuilding(File file, List<FileDeltaBlock> delta) async => binary!;
|
||||
}
|
||||
|
||||
class FakeVMServiceDiscoveryForAttach extends Fake implements VMServiceDiscoveryForAttach {
|
||||
FakeVMServiceDiscoveryForAttach(this.uris);
|
||||
|
||||
@override
|
||||
Stream<Uri> uris;
|
||||
}
|
||||
|
||||
class TestException implements Exception {}
|
||||
|
Loading…
x
Reference in New Issue
Block a user