blob: ac912130ebe4d7554afe8e76253cf705b450ccd2 [file] [log] [blame]
// Copyright 2019 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:io';
import 'package:meta/meta.dart';
import 'package:process/process.dart';
/// A wrapper for the Fuchsia SDK `device-finder` tool.
@immutable
class DevFinder {
/// Creates a new wrapper for the `device-finder` tool.
///
/// All parameters must not be null.
const DevFinder(
this.devFinderPath, {
this.processManager = const LocalProcessManager(),
}) : assert(devFinderPath != null),
assert(processManager != null);
/// The path to the Fuchsia SDK `device-finder` tool on disk.
final String devFinderPath;
/// The [ProcessManager] to use for launching the `device-finder` tool.
final ProcessManager processManager;
Future<String> _runDevFinderWithRetries(
String deviceName,
int numTries,
int sleepDelay, {
bool local = false,
@required bool nullOk,
}) async {
assert(numTries != null);
assert(sleepDelay != null);
assert(local != null);
assert(nullOk != null);
if (deviceName == null) {
stderr.writeln('Warning: device name not specified; if '
'multiple devices are attached you may not get the right one.');
}
final List<String> command = <String>[
devFinderPath,
if (deviceName != null) 'resolve' else 'list',
'-device-limit', '1', //
if (local) '-local',
if (deviceName != null) deviceName,
];
for (int i = 0; i < numTries; i++) {
final ProcessResult result = await processManager.run(command);
if (result.exitCode == 0) {
return result.stdout.toString().trim();
}
await Future<void>.delayed(Duration(seconds: sleepDelay));
}
if (!nullOk) {
throw DevFinderException(
'Failed to get ${local ? 'local' : 'target'} IP for $deviceName');
}
return null;
}
/// Gets the target address for the specified `deviceName`.
///
/// If `deviceName` is null, will attempt to get the target address of the
/// first discoverable device.
///
/// The `numTries` parameter must not be null, and specifies how many
/// times to retry finding the device. This is useful e.g. after paving a
/// device and waiting for it to become available. The `sleepDelay` also
/// must not be null, and specifies the number of seconds to wait between
/// retries.
///
/// The `nullOk` parameter must not be null. If true, this method will
/// return null if it cannot find a device; otherwise, it will throw. The
/// default value is false.
Future<String> getTargetAddress(
String deviceName, {
int numTries = 75,
int sleepDelay = 4,
bool nullOk = false,
}) {
return _runDevFinderWithRetries(
deviceName,
numTries,
sleepDelay,
nullOk: nullOk,
local: false,
);
}
/// Gets the local interface address for the specified `deviceName`.
///
/// If `deviceName` is null, will attempt to get the target address of the
/// first discoverable device.
///
/// The `numTries` parameter must not be null, and specifies how many
/// times to retry finding the device. This is useful e.g. after paving a
/// device and waiting for it to become available. The `sleepDelay` also
/// must not be null, and specifies the number of seconds to wait between
/// retries.
///
/// The `nullOk` parameter must not be null. If true, this method will
/// return null if it cannot find a device; otherwise, it will throw. The
/// default value is false.
Future<String> getLocalAddress(
String deviceName, {
int numTries = 30,
int sleepDelay = 2,
bool nullOk = false,
}) {
return _runDevFinderWithRetries(
deviceName,
numTries,
sleepDelay,
nullOk: nullOk,
local: true,
);
}
}
/// The exception thrown when a [DevFinder] lookup fails.
class DevFinderException implements Exception {
/// Creates a new [DevFinderException], such as when device-finder fails to find
/// a device.
const DevFinderException(this.message);
/// The user-facing message to display.
final String message;
@override
String toString() => message;
}