Re-enable use of instruments for iOS device lookup (#10838)
This reverts commit b2909a245a607995ce7ec286585cd1f643124f57.
This resubmits the following patches:
1. Use Xcode instruments to list devices (#10801)
Eliminates the dependency on idevice_id from libimobiledevice. Instead,
uses Xcode built-in functionality.
2. Make device discovery asynchronous (#10803)
Migrates DeviceDiscovery.devices and all device-specific lookup to be
asynchronous.
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart
index f6ed675..8584455 100644
--- a/packages/flutter_tools/lib/src/android/android_device.dart
+++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -48,7 +48,7 @@
bool get canListAnything => androidWorkflow.canListDevices;
@override
- List<Device> pollingGetDevices() => getAdbDevices();
+ Future<List<Device>> pollingGetDevices() async => getAdbDevices();
}
class AndroidDevice extends Device {
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index 8c39078..cb069f1 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -305,7 +305,7 @@
final String target = _getStringArg(args, 'target');
final bool enableHotReload = _getBoolArg(args, 'hot') ?? kHotReloadDefault;
- final Device device = daemon.deviceDomain._getOrLocateDevice(deviceId);
+ final Device device = await daemon.deviceDomain._getOrLocateDevice(deviceId);
if (device == null)
throw "device '$deviceId' not found";
@@ -493,7 +493,7 @@
Future<List<Map<String, dynamic>>> discover(Map<String, dynamic> args) async {
final String deviceId = _getStringArg(args, 'deviceId', required: true);
- final Device device = daemon.deviceDomain._getDevice(deviceId);
+ final Device device = await daemon.deviceDomain._getDevice(deviceId);
if (device == null)
throw "device '$deviceId' not found";
@@ -575,11 +575,12 @@
final List<PollingDeviceDiscovery> _discoverers = <PollingDeviceDiscovery>[];
- Future<List<Device>> getDevices([Map<String, dynamic> args]) {
- final List<Device> devices = _discoverers.expand((PollingDeviceDiscovery discoverer) {
- return discoverer.devices;
- }).toList();
- return new Future<List<Device>>.value(devices);
+ Future<List<Device>> getDevices([Map<String, dynamic> args]) async {
+ final List<Device> devices = <Device>[];
+ for (PollingDeviceDiscovery discoverer in _discoverers) {
+ devices.addAll(await discoverer.devices);
+ }
+ return devices;
}
/// Enable device events.
@@ -602,7 +603,7 @@
final int devicePort = _getIntArg(args, 'devicePort', required: true);
int hostPort = _getIntArg(args, 'hostPort');
- final Device device = daemon.deviceDomain._getDevice(deviceId);
+ final Device device = await daemon.deviceDomain._getDevice(deviceId);
if (device == null)
throw "device '$deviceId' not found";
@@ -617,7 +618,7 @@
final int devicePort = _getIntArg(args, 'devicePort', required: true);
final int hostPort = _getIntArg(args, 'hostPort', required: true);
- final Device device = daemon.deviceDomain._getDevice(deviceId);
+ final Device device = await daemon.deviceDomain._getDevice(deviceId);
if (device == null)
throw "device '$deviceId' not found";
@@ -631,23 +632,25 @@
}
/// Return the device matching the deviceId field in the args.
- Device _getDevice(String deviceId) {
- final List<Device> devices = _discoverers.expand((PollingDeviceDiscovery discoverer) {
- return discoverer.devices;
- }).toList();
- return devices.firstWhere((Device device) => device.id == deviceId, orElse: () => null);
+ Future<Device> _getDevice(String deviceId) async {
+ for (PollingDeviceDiscovery discoverer in _discoverers) {
+ final Device device = (await discoverer.devices).firstWhere((Device device) => device.id == deviceId, orElse: () => null);
+ if (device != null)
+ return device;
+ }
+ return null;
}
/// Return a known matching device, or scan for devices if no known match is found.
- Device _getOrLocateDevice(String deviceId) {
+ Future<Device> _getOrLocateDevice(String deviceId) async {
// Look for an already known device.
- final Device device = _getDevice(deviceId);
+ final Device device = await _getDevice(deviceId);
if (device != null)
return device;
// Scan the different device providers for a match.
for (PollingDeviceDiscovery discoverer in _discoverers) {
- final List<Device> devices = discoverer.pollingGetDevices();
+ final List<Device> devices = await discoverer.pollingGetDevices();
for (Device device in devices)
if (device.id == deviceId)
return device;
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index de3f873..8208adf 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -81,11 +81,17 @@
: getAllConnectedDevices();
}
+ Iterable<DeviceDiscovery> get _platformDiscoverers {
+ return _deviceDiscoverers.where((DeviceDiscovery discoverer) => discoverer.supportsPlatform);
+ }
+
/// Return the list of all connected devices.
- Stream<Device> getAllConnectedDevices() {
- return new Stream<Device>.fromIterable(_deviceDiscoverers
- .where((DeviceDiscovery discoverer) => discoverer.supportsPlatform)
- .expand((DeviceDiscovery discoverer) => discoverer.devices));
+ Stream<Device> getAllConnectedDevices() async* {
+ for (DeviceDiscovery discoverer in _platformDiscoverers) {
+ for (Device device in await discoverer.devices) {
+ yield device;
+ }
+ }
}
}
@@ -97,7 +103,7 @@
/// current environment configuration.
bool get canListAnything;
- List<Device> get devices;
+ Future<List<Device>> get devices;
}
/// A [DeviceDiscovery] implementation that uses polling to discover device adds
@@ -111,13 +117,13 @@
ItemListNotifier<Device> _items;
Timer _timer;
- List<Device> pollingGetDevices();
+ Future<List<Device>> pollingGetDevices();
void startPolling() {
if (_timer == null) {
_items ??= new ItemListNotifier<Device>();
- _timer = new Timer.periodic(_pollingDuration, (Timer timer) {
- _items.updateWithNewList(pollingGetDevices());
+ _timer = new Timer.periodic(_pollingDuration, (Timer timer) async {
+ _items.updateWithNewList(await pollingGetDevices());
});
}
}
@@ -128,8 +134,8 @@
}
@override
- List<Device> get devices {
- _items ??= new ItemListNotifier<Device>.from(pollingGetDevices());
+ Future<List<Device>> get devices async {
+ _items ??= new ItemListNotifier<Device>.from(await pollingGetDevices());
return _items.items;
}
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart
index bb3eca7..1cc5c96 100644
--- a/packages/flutter_tools/lib/src/ios/devices.dart
+++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -36,11 +36,11 @@
bool get canListAnything => iosWorkflow.canListDevices;
@override
- List<Device> pollingGetDevices() => IOSDevice.getAttachedDevices();
+ Future<List<Device>> pollingGetDevices() => IOSDevice.getAttachedDevices();
}
class IOSDevice extends Device {
- IOSDevice(String id, { this.name }) : super(id) {
+ IOSDevice(String id, { this.name, String sdkVersion }) : _sdkVersion = sdkVersion, super(id) {
_installerPath = _checkForCommand('ideviceinstaller');
_iproxyPath = _checkForCommand('iproxy');
_pusherPath = _checkForCommand(
@@ -55,6 +55,8 @@
String _iproxyPath;
String _pusherPath;
+ final String _sdkVersion;
+
@override
bool get supportsHotMode => true;
@@ -71,14 +73,30 @@
@override
bool get supportsStartPaused => false;
- static List<IOSDevice> getAttachedDevices() {
- if (!iMobileDevice.isInstalled)
+ // Physical device line format to be matched:
+ // My iPhone (10.3.2) [75b90e947c5f429fa67f3e9169fda0d89f0492f1]
+ //
+ // Other formats in output (desktop, simulator) to be ignored:
+ // my-mac-pro [2C10513E-4dA5-405C-8EF5-C44353DB3ADD]
+ // iPhone 6s (9.3) [F6CEE7CF-81EB-4448-81B4-1755288C7C11] (Simulator)
+ static final RegExp _deviceRegex = new RegExp(r'^(.*) +\((.*)\) +\[(.*)\]$');
+
+ static Future<List<IOSDevice>> getAttachedDevices() async {
+ if (!xcode.isInstalled)
return <IOSDevice>[];
final List<IOSDevice> devices = <IOSDevice>[];
- for (String id in iMobileDevice.getAttachedDeviceIDs()) {
- final String name = iMobileDevice.getInfoForDevice(id, 'DeviceName');
- devices.add(new IOSDevice(id, name: name));
+ final Iterable<String> deviceLines = (await xcode.getAvailableDevices())
+ .split('\n')
+ .map((String line) => line.trim());
+ for (String line in deviceLines) {
+ final Match match = _deviceRegex.firstMatch(line);
+ if (match != null) {
+ final String deviceName = match.group(1);
+ final String sdkVersion = match.group(2);
+ final String deviceID = match.group(3);
+ devices.add(new IOSDevice(deviceID, name: deviceName, sdkVersion: sdkVersion));
+ }
}
return devices;
}
@@ -311,11 +329,7 @@
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
@override
- Future<String> get sdkNameAndVersion async => 'iOS $_sdkVersion ($_buildVersion)';
-
- String get _sdkVersion => iMobileDevice.getInfoForDevice(id, 'ProductVersion');
-
- String get _buildVersion => iMobileDevice.getInfoForDevice(id, 'BuildVersion');
+ Future<String> get sdkNameAndVersion async => 'iOS $_sdkVersion';
@override
DeviceLogReader getLogReader({ApplicationPackage app}) {
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index 7d1bfa6..1b1317e 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -70,14 +70,6 @@
return await exitsHappyAsync(<String>['idevicename']);
}
- List<String> getAttachedDeviceIDs() {
- return runSync(<String>['idevice_id', '-l'])
- .trim()
- .split('\n')
- .where((String line) => line.isNotEmpty)
- .toList();
- }
-
/// Returns the value associated with the specified `ideviceinfo` key for a device.
///
/// If either the specified key or device does not exist, returns the empty string.
@@ -165,6 +157,13 @@
return _xcodeVersionCheckValid(_xcodeMajorVersion, _xcodeMinorVersion);
}
+
+ Future<String> getAvailableDevices() async {
+ final RunResult result = await runAsync(<String>['/usr/bin/instruments', '-s', 'devices']);
+ if (result.exitCode != 0)
+ throw new ToolExit('Failed to invoke /usr/bin/instruments. Is Xcode installed?');
+ return result.stdout;
+ }
}
bool _xcodeVersionCheckValid(int major, int minor) {
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index 697be3b..92788c6 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -37,7 +37,7 @@
bool get canListAnything => iosWorkflow.canListDevices;
@override
- List<Device> pollingGetDevices() => IOSSimulatorUtils.instance.getAttachedDevices();
+ Future<List<Device>> pollingGetDevices() async => IOSSimulatorUtils.instance.getAttachedDevices();
}
class IOSSimulatorUtils {