// Copyright 2016 The Chromium 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 'base/common.dart'; import 'base/port_scanner.dart'; import 'device.dart'; import 'globals.dart'; /// Discover service protocol on a device /// and forward the service protocol device port to the host. class ProtocolDiscovery { /// [logReader] - a [DeviceLogReader] to look for service messages in. ProtocolDiscovery(DeviceLogReader logReader, String serviceName, {this.portForwarder, this.hostPort, this.defaultHostPort}) : _logReader = logReader, _serviceName = serviceName { assert(_logReader != null); _subscription = _logReader.logLines.listen(_onLine); assert(portForwarder == null || defaultHostPort != null); } factory ProtocolDiscovery.observatory(DeviceLogReader logReader, {DevicePortForwarder portForwarder, int hostPort}) => new ProtocolDiscovery(logReader, kObservatoryService, portForwarder: portForwarder, hostPort: hostPort, defaultHostPort: kDefaultObservatoryPort); factory ProtocolDiscovery.diagnosticService(DeviceLogReader logReader, {DevicePortForwarder portForwarder, int hostPort}) => new ProtocolDiscovery(logReader, kDiagnosticService, portForwarder: portForwarder, hostPort: hostPort, defaultHostPort: kDefaultDiagnosticPort); static const String kObservatoryService = 'Observatory'; static const String kDiagnosticService = 'Diagnostic server'; final DeviceLogReader _logReader; final String _serviceName; final DevicePortForwarder portForwarder; int hostPort; final int defaultHostPort; Completer<Uri> _completer = new Completer<Uri>(); StreamSubscription<String> _subscription; /// The [Future] returned by this function will complete when the next service /// Uri is found. Future<Uri> nextUri() async { final Uri deviceUri = await _completer.future.timeout( const Duration(seconds: 60), onTimeout: () { throwToolExit('Timeout while attempting to retrieve Uri for $_serviceName'); } ); printTrace('$_serviceName Uri on device: $deviceUri'); Uri hostUri; if (portForwarder != null) { final int devicePort = deviceUri.port; hostPort ??= await portScanner.findPreferredPort(defaultHostPort); hostPort = await portForwarder .forward(devicePort, hostPort: hostPort) .timeout(const Duration(seconds: 60), onTimeout: () { throwToolExit('Timeout while atempting to foward device port $devicePort for $_serviceName'); }); printTrace('Forwarded host port $hostPort to device port $devicePort for $_serviceName'); hostUri = deviceUri.replace(port: hostPort); } else { hostUri = deviceUri; } return hostUri; } void cancel() { _subscription.cancel(); } void _onLine(String line) { Uri uri; final String prefix = '$_serviceName listening on '; final int index = line.indexOf(prefix + 'http://'); if (index >= 0) { try { uri = Uri.parse(line.substring(index + prefix.length)); } catch (_) { // Ignore errors. } } if (uri != null) _located(uri); } void _located(Uri uri) { assert(_completer != null); assert(!_completer.isCompleted); _completer.complete(uri); _completer = new Completer<Uri>(); } }