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
5import 'dart:async';
6
7import 'package:async/async.dart';
8
9import 'base/logger.dart';
10import 'device.dart';
11import 'device_port_forwarder.dart';
12import 'mdns_discovery.dart';
13import '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`.
18abstract 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.
30class 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.
64class 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.
101class 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