| 1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | import 'dart:async'; |
| 6 | |
| 7 | import 'package:async/async.dart' ; |
| 8 | |
| 9 | import 'base/logger.dart'; |
| 10 | import 'device.dart'; |
| 11 | import 'device_port_forwarder.dart'; |
| 12 | import 'mdns_discovery.dart'; |
| 13 | import 'protocol_discovery.dart'; |
| 14 | |
| 15 | /// Discovers the VM service uri on a device, and forwards the port to the host. |
| 16 | /// |
| 17 | /// This is mainly used during a `flutter attach`. |
| 18 | abstract class VMServiceDiscoveryForAttach { |
| 19 | VMServiceDiscoveryForAttach(); |
| 20 | |
| 21 | /// The discovered VM service URis. |
| 22 | /// |
| 23 | /// Port forwarding is only attempted when this is invoked, for each VM |
| 24 | /// Service URI in the stream. |
| 25 | Stream<Uri> get uris; |
| 26 | } |
| 27 | |
| 28 | /// An implementation of [VMServiceDiscoveryForAttach] that uses log scanning |
| 29 | /// for the discovery. |
| 30 | class LogScanningVMServiceDiscoveryForAttach extends VMServiceDiscoveryForAttach { |
| 31 | LogScanningVMServiceDiscoveryForAttach( |
| 32 | Future<DeviceLogReader> logReader, { |
| 33 | DevicePortForwarder? portForwarder, |
| 34 | int? hostPort, |
| 35 | int? devicePort, |
| 36 | required bool ipv6, |
| 37 | required Logger logger, |
| 38 | }) { |
| 39 | _protocolDiscovery = (() async => ProtocolDiscovery.vmService( |
| 40 | await logReader, |
| 41 | portForwarder: portForwarder, |
| 42 | ipv6: ipv6, |
| 43 | devicePort: devicePort, |
| 44 | hostPort: hostPort, |
| 45 | logger: logger, |
| 46 | ))(); |
| 47 | } |
| 48 | |
| 49 | late final Future<ProtocolDiscovery> _protocolDiscovery; |
| 50 | |
| 51 | @override |
| 52 | Stream<Uri> get uris { |
| 53 | final controller = StreamController<Uri>(); |
| 54 | _protocolDiscovery.then((ProtocolDiscovery protocolDiscovery) async { |
| 55 | await controller.addStream(protocolDiscovery.uris); |
| 56 | await controller.close(); |
| 57 | }, onError: (Object error) => controller.addError(error)); |
| 58 | return controller.stream; |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | /// An implementation of [VMServiceDiscoveryForAttach] that uses mdns for the |
| 63 | /// discovery. |
| 64 | class MdnsVMServiceDiscoveryForAttach extends VMServiceDiscoveryForAttach { |
| 65 | MdnsVMServiceDiscoveryForAttach({ |
| 66 | required this.device, |
| 67 | this.appId, |
| 68 | required this.usesIpv6, |
| 69 | required this.useDeviceIPAsHost, |
| 70 | this.deviceVmservicePort, |
| 71 | this.hostVmservicePort, |
| 72 | }); |
| 73 | |
| 74 | final Device device; |
| 75 | final String? appId; |
| 76 | final bool usesIpv6; |
| 77 | final bool useDeviceIPAsHost; |
| 78 | final int? deviceVmservicePort; |
| 79 | final int? hostVmservicePort; |
| 80 | |
| 81 | @override |
| 82 | Stream<Uri> get uris { |
| 83 | final Future<Uri?> mDNSDiscoveryFuture = MDnsVmServiceDiscovery.instance! |
| 84 | .getVMServiceUriForAttach( |
| 85 | appId, |
| 86 | device, |
| 87 | usesIpv6: usesIpv6, |
| 88 | useDeviceIPAsHost: useDeviceIPAsHost, |
| 89 | deviceVmservicePort: deviceVmservicePort, |
| 90 | hostVmservicePort: hostVmservicePort, |
| 91 | ); |
| 92 | |
| 93 | return Stream<Uri?>.fromFuture( |
| 94 | mDNSDiscoveryFuture, |
| 95 | ).where((Uri? uri) => uri != null).cast<Uri>().asBroadcastStream(); |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | /// An implementation of [VMServiceDiscoveryForAttach] that delegates to other |
| 100 | /// [VMServiceDiscoveryForAttach] instances for discovery. |
| 101 | class DelegateVMServiceDiscoveryForAttach extends VMServiceDiscoveryForAttach { |
| 102 | DelegateVMServiceDiscoveryForAttach(this.delegates); |
| 103 | |
| 104 | final List<VMServiceDiscoveryForAttach> delegates; |
| 105 | |
| 106 | @override |
| 107 | Stream<Uri> get uris => StreamGroup.merge<Uri>( |
| 108 | delegates.map((VMServiceDiscoveryForAttach delegate) => delegate.uris), |
| 109 | ); |
| 110 | } |
| 111 | |