diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart index 382260e8167..3d6333c019d 100644 --- a/packages/flutter_tools/lib/src/android/android_device.dart +++ b/packages/flutter_tools/lib/src/android/android_device.dart @@ -51,6 +51,7 @@ class AndroidDevice extends Device { bool get isLocalEmulator => false; _AdbLogReader _logReader; + _AndroidDevicePortForwarder _portForwarder; List adbCommandForDevice(List args) { return [androidSdk.adbPath, '-s', id]..addAll(args); @@ -169,19 +170,10 @@ class AndroidDevice extends Device { Future _forwardObservatoryPort(int port) async { bool portWasZero = port == 0; - if (port == 0) { - // Auto-bind to a port. Set up forwarding for that port. Emit a stdout - // message similar to the command-line VM so that tools can parse the output. - // "Observatory listening on http://127.0.0.1:52111" - port = await findAvailablePort(); - } - try { // Set up port forwarding for observatory. - runCheckedSync(adbCommandForDevice([ - 'forward', 'tcp:$port', 'tcp:$observatoryDefaultPort' - ])); - + port = await portForwarder.forward(observatoryDefaultPort, + hostPort: port); if (portWasZero) printStatus('Observatory listening on http://127.0.0.1:$port'); } catch (e) { @@ -292,6 +284,13 @@ class AndroidDevice extends Device { return _logReader; } + DevicePortForwarder get portForwarder { + if (_portForwarder == null) + _portForwarder = new _AndroidDevicePortForwarder(this); + + return _portForwarder; + } + void startTracing(AndroidApk apk) { runCheckedSync(adbCommandForDevice([ 'shell', @@ -537,3 +536,74 @@ class _AdbLogReader extends DeviceLogReader { return other.device.id == device.id; } } + +class _AndroidDevicePortForwarder extends DevicePortForwarder { + _AndroidDevicePortForwarder(this.device); + + final AndroidDevice device; + + static int _extractPort(String portString) { + return int.parse(portString.trim(), onError: (_) => null); + } + + List get forwardedPorts { + final List ports = []; + + String stdout = runCheckedSync( + [ + androidSdk.adbPath, + 'forward', + '--list' + ]); + + List lines = LineSplitter.split(stdout).toList(); + for (String line in lines) { + if (line.startsWith(device.id)) { + List splitLine = line.split("tcp:"); + + // Sanity check splitLine. + if (splitLine.length != 3) + continue; + + // Attempt to extract ports. + int hostPort = _extractPort(splitLine[1]); + int devicePort = _extractPort(splitLine[2]); + + // Failed, skip. + if ((hostPort == null) || (devicePort == null)) + continue; + + ports.add(new ForwardedPort(hostPort, devicePort)); + } + } + + return ports; + } + + Future forward(int devicePort, {int hostPort: null}) async { + if ((hostPort == null) || (hostPort == 0)) { + // Auto select host port. + hostPort = await findAvailablePort(); + } + + runCheckedSync( + [ + androidSdk.adbPath, + 'forward', + hostPort.toString(), + devicePort.toString() + ]); + + return hostPort; + } + + Future unforward(ForwardedPort forwardedPort) async { + runCheckedSync( + [ + androidSdk.adbPath, + 'forward', + '--remove', + forwardedPort.hostPort.toString() + ]); + } +} diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 822b8bbf1eb..e43c41afcf3 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -151,6 +151,9 @@ abstract class Device { /// Get the log reader for this device. DeviceLogReader get logReader; + /// Get the port forwarder for this device. + DevicePortForwarder get portForwarder; + /// Clear the device's logs. void clearLogs(); @@ -186,6 +189,30 @@ abstract class Device { String toString() => '$runtimeType $id'; } +class ForwardedPort { + ForwardedPort(this.hostPort, this.devicePort); + + final int hostPort; + final int devicePort; + + String toString() => 'ForwardedPort HOST:$hostPort to DEVICE:$devicePort'; +} + +/// Forward ports from the host machine to the device. +abstract class DevicePortForwarder { + /// Returns a Future that completes with the current list of forwarded + /// ports for this device. + List get forwardedPorts; + + /// Forward [hostPort] on the host to [devicePort] on the device. + /// If [hostPort] is null, will auto select a host port. + /// Returns a Future that completes with the host port. + Future forward(int devicePort, {int hostPort: null}); + + /// Stops forwarding [forwardedPort]. + Future unforward(ForwardedPort forwardedPort); +} + /// Read the log for a particular device. Subclasses must implement `hashCode` /// and `operator ==` so that log readers that read from the same location can be /// de-duped. For example, two Android devices will both try and log using diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart index 8af09f4dd4d..27832960005 100644 --- a/packages/flutter_tools/lib/src/ios/devices.dart +++ b/packages/flutter_tools/lib/src/ios/devices.dart @@ -10,6 +10,7 @@ import 'package:path/path.dart' as path; import '../application_package.dart'; import '../base/common.dart'; +import '../base/os.dart'; import '../base/process.dart'; import '../build_configuration.dart'; import '../device.dart'; @@ -65,6 +66,8 @@ class IOSDevice extends Device { _IOSDeviceLogReader _logReader; + _IOSDevicePortForwarder _portForwarder; + bool get isLocalEmulator => false; bool get supportsStartPaused => false; @@ -230,6 +233,13 @@ class IOSDevice extends Device { return _logReader; } + DevicePortForwarder get portForwarder { + if (_portForwarder == null) + _portForwarder = new _IOSDevicePortForwarder(this); + + return _portForwarder; + } + void clearLogs() { } } @@ -310,3 +320,28 @@ class _IOSDeviceLogReader extends DeviceLogReader { return other.name == name; } } + +class _IOSDevicePortForwarder extends DevicePortForwarder { + _IOSDevicePortForwarder(this.device); + + final IOSDevice device; + + List get forwardedPorts { + final List ports = []; + // TODO(chinmaygarde): Implement. + return ports; + } + + Future forward(int devicePort, {int hostPort: null}) async { + if ((hostPort == null) || (hostPort == 0)) { + // Auto select host port. + hostPort = await findAvailablePort(); + } + // TODO(chinmaygarde): Implement. + return hostPort; + } + + Future unforward(ForwardedPort forwardedPort) async { + // TODO(chinmaygarde): Implement. + } +} diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart index cda2e4793b4..5475d593a40 100644 --- a/packages/flutter_tools/lib/src/ios/simulators.dart +++ b/packages/flutter_tools/lib/src/ios/simulators.dart @@ -321,6 +321,7 @@ class IOSSimulator extends Device { bool get isLocalEmulator => true; _IOSSimulatorLogReader _logReader; + _IOSSimulatorDevicePortForwarder _portForwarder; String get xcrunPath => path.join('/usr', 'bin', 'xcrun'); @@ -544,6 +545,13 @@ class IOSSimulator extends Device { return _logReader; } + DevicePortForwarder get portForwarder { + if (_portForwarder == null) + _portForwarder = new _IOSSimulatorDevicePortForwarder(this); + + return _portForwarder; + } + void clearLogs() { File logFile = new File(logFilePath); if (logFile.existsSync()) { @@ -772,3 +780,28 @@ int compareIphoneVersions(String id1, String id2) { int q2 = qualifiers.indexOf(m2[2]); return q1.compareTo(q2); } + +class _IOSSimulatorDevicePortForwarder extends DevicePortForwarder { + _IOSSimulatorDevicePortForwarder(this.device); + + final IOSSimulator device; + + final List _ports = []; + + List get forwardedPorts { + return _ports; + } + + Future forward(int devicePort, {int hostPort: null}) async { + if ((hostPort == null) || (hostPort == 0)) { + hostPort = devicePort; + } + assert(devicePort == hostPort); + _ports.add(new ForwardedPort(devicePort, hostPort)); + return hostPort; + } + + Future unforward(ForwardedPort forwardedPort) async { + _ports.remove(forwardedPort); + } +}