Detect USB/network interface from iOS devices (#58257)
This commit is contained in:
@@ -58,7 +58,7 @@ class IOSDevices extends PollingDeviceDiscovery {
|
||||
);
|
||||
}
|
||||
|
||||
return await _xcdevice.getAvailableTetheredIOSDevices(timeout: timeout);
|
||||
return await _xcdevice.getAvailableIOSDevices(timeout: timeout);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -73,11 +73,18 @@ class IOSDevices extends PollingDeviceDiscovery {
|
||||
}
|
||||
}
|
||||
|
||||
enum IOSDeviceInterface {
|
||||
none,
|
||||
usb,
|
||||
network,
|
||||
}
|
||||
|
||||
class IOSDevice extends Device {
|
||||
IOSDevice(String id, {
|
||||
@required FileSystem fileSystem,
|
||||
@required this.name,
|
||||
@required this.cpuArchitecture,
|
||||
@required this.interfaceType,
|
||||
@required String sdkVersion,
|
||||
@required Platform platform,
|
||||
@required Artifacts artifacts,
|
||||
@@ -123,16 +130,21 @@ class IOSDevice extends Device {
|
||||
}
|
||||
|
||||
@override
|
||||
bool get supportsHotReload => true;
|
||||
bool get supportsHotReload => interfaceType == IOSDeviceInterface.usb;
|
||||
|
||||
@override
|
||||
bool get supportsHotRestart => true;
|
||||
bool get supportsHotRestart => interfaceType == IOSDeviceInterface.usb;
|
||||
|
||||
@override
|
||||
bool get supportsFlutterExit => interfaceType == IOSDeviceInterface.usb;
|
||||
|
||||
@override
|
||||
final String name;
|
||||
|
||||
final DarwinArch cpuArchitecture;
|
||||
|
||||
final IOSDeviceInterface interfaceType;
|
||||
|
||||
Map<IOSApp, DeviceLogReader> _logReaders;
|
||||
|
||||
DevicePortForwarder _portForwarder;
|
||||
@@ -178,6 +190,7 @@ class IOSDevice extends Device {
|
||||
deviceId: id,
|
||||
bundlePath: bundle.path,
|
||||
launchArguments: <String>[],
|
||||
interfaceType: interfaceType,
|
||||
);
|
||||
} on ProcessException catch (e) {
|
||||
_logger.printError(e.message);
|
||||
@@ -319,6 +332,7 @@ class IOSDevice extends Device {
|
||||
deviceId: id,
|
||||
bundlePath: bundle.path,
|
||||
launchArguments: launchArguments,
|
||||
interfaceType: interfaceType,
|
||||
);
|
||||
if (installationResult != 0) {
|
||||
_logger.printError('Could not run ${bundle.path} on $id.');
|
||||
@@ -410,7 +424,7 @@ class IOSDevice extends Device {
|
||||
void clearLogs() { }
|
||||
|
||||
@override
|
||||
bool get supportsScreenshot => _iMobileDevice.isInstalled;
|
||||
bool get supportsScreenshot => _iMobileDevice.isInstalled && interfaceType == IOSDeviceInterface.usb;
|
||||
|
||||
@override
|
||||
Future<void> takeScreenshot(File outputFile) async {
|
||||
|
||||
@@ -14,6 +14,7 @@ import '../base/process.dart';
|
||||
import '../build_info.dart';
|
||||
import '../cache.dart';
|
||||
import 'code_signing.dart';
|
||||
import 'devices.dart';
|
||||
|
||||
// Error message patterns from ios-deploy output
|
||||
const String noProvisioningProfileErrorOne = 'Error 0xe8008015';
|
||||
@@ -84,6 +85,7 @@ class IOSDeploy {
|
||||
@required String deviceId,
|
||||
@required String bundlePath,
|
||||
@required List<String>launchArguments,
|
||||
@required IOSDeviceInterface interfaceType,
|
||||
}) async {
|
||||
final List<String> launchCommand = <String>[
|
||||
_binaryPath,
|
||||
@@ -91,7 +93,8 @@ class IOSDeploy {
|
||||
deviceId,
|
||||
'--bundle',
|
||||
bundlePath,
|
||||
'--no-wifi',
|
||||
if (interfaceType != IOSDeviceInterface.network)
|
||||
'--no-wifi',
|
||||
if (launchArguments.isNotEmpty) ...<String>[
|
||||
'--args',
|
||||
launchArguments.join(' '),
|
||||
@@ -113,6 +116,7 @@ class IOSDeploy {
|
||||
@required String deviceId,
|
||||
@required String bundlePath,
|
||||
@required List<String> launchArguments,
|
||||
@required IOSDeviceInterface interfaceType,
|
||||
}) async {
|
||||
final List<String> launchCommand = <String>[
|
||||
_binaryPath,
|
||||
@@ -120,7 +124,8 @@ class IOSDeploy {
|
||||
deviceId,
|
||||
'--bundle',
|
||||
bundlePath,
|
||||
'--no-wifi',
|
||||
if (interfaceType != IOSDeviceInterface.network)
|
||||
'--no-wifi',
|
||||
'--justlaunch',
|
||||
if (launchArguments.isNotEmpty) ...<String>[
|
||||
'--args',
|
||||
|
||||
@@ -290,7 +290,7 @@ class XCDevice {
|
||||
List<dynamic> _cachedListResults;
|
||||
|
||||
/// [timeout] defaults to 2 seconds.
|
||||
Future<List<IOSDevice>> getAvailableTetheredIOSDevices({ Duration timeout }) async {
|
||||
Future<List<IOSDevice>> getAvailableIOSDevices({ Duration timeout }) async {
|
||||
final List<dynamic> allAvailableDevices = await _getAllDevices(timeout: timeout ?? const Duration(seconds: 2));
|
||||
|
||||
if (allAvailableDevices == null) {
|
||||
@@ -364,8 +364,11 @@ class XCDevice {
|
||||
}
|
||||
}
|
||||
|
||||
final IOSDeviceInterface interface = _interfaceType(deviceProperties);
|
||||
|
||||
// Only support USB devices, skip "network" interface (Xcode > Window > Devices and Simulators > Connect via network).
|
||||
if (!_isUSBTethered(deviceProperties)) {
|
||||
// TODO(jmagman): Remove this check once wirelessly detected devices can be observed and attached, https://github.com/flutter/flutter/issues/15072.
|
||||
if (interface != IOSDeviceInterface.usb) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -373,6 +376,7 @@ class XCDevice {
|
||||
device['identifier'] as String,
|
||||
name: device['name'] as String,
|
||||
cpuArchitecture: _cpuArchitecture(deviceProperties),
|
||||
interfaceType: interface,
|
||||
sdkVersion: _sdkVersion(deviceProperties),
|
||||
artifacts: globals.artifacts,
|
||||
fileSystem: globals.fs,
|
||||
@@ -409,10 +413,18 @@ class XCDevice {
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool _isUSBTethered(Map<String, dynamic> deviceProperties) {
|
||||
// Interface can be "usb", "network", or not present for simulators.
|
||||
return deviceProperties.containsKey('interface') &&
|
||||
(deviceProperties['interface'] as String).toLowerCase() == 'usb';
|
||||
static IOSDeviceInterface _interfaceType(Map<String, dynamic> deviceProperties) {
|
||||
// Interface can be "usb", "network", or "none" for simulators
|
||||
// and unknown future interfaces.
|
||||
if (deviceProperties.containsKey('interface')) {
|
||||
if ((deviceProperties['interface'] as String).toLowerCase() == 'network') {
|
||||
return IOSDeviceInterface.network;
|
||||
} else {
|
||||
return IOSDeviceInterface.usb;
|
||||
}
|
||||
}
|
||||
|
||||
return IOSDeviceInterface.none;
|
||||
}
|
||||
|
||||
static String _sdkVersion(Map<String, dynamic> deviceProperties) {
|
||||
|
||||
@@ -72,7 +72,8 @@ void main() {
|
||||
iMobileDevice: iMobileDevice,
|
||||
name: 'iPhone 1',
|
||||
sdkVersion: '13.3',
|
||||
cpuArchitecture: DarwinArch.arm64
|
||||
cpuArchitecture: DarwinArch.arm64,
|
||||
interfaceType: IOSDeviceInterface.usb,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -87,7 +88,8 @@ void main() {
|
||||
iMobileDevice: iMobileDevice,
|
||||
name: 'iPhone 1',
|
||||
cpuArchitecture: DarwinArch.arm64,
|
||||
sdkVersion: '1.0.0'
|
||||
sdkVersion: '1.0.0',
|
||||
interfaceType: IOSDeviceInterface.usb,
|
||||
).majorSdkVersion, 1);
|
||||
expect(IOSDevice(
|
||||
'device-123',
|
||||
@@ -99,7 +101,8 @@ void main() {
|
||||
iMobileDevice: iMobileDevice,
|
||||
name: 'iPhone 1',
|
||||
cpuArchitecture: DarwinArch.arm64,
|
||||
sdkVersion: '13.1.1'
|
||||
sdkVersion: '13.1.1',
|
||||
interfaceType: IOSDeviceInterface.usb,
|
||||
).majorSdkVersion, 13);
|
||||
expect(IOSDevice(
|
||||
'device-123',
|
||||
@@ -111,7 +114,8 @@ void main() {
|
||||
iMobileDevice: iMobileDevice,
|
||||
name: 'iPhone 1',
|
||||
cpuArchitecture: DarwinArch.arm64,
|
||||
sdkVersion: '10'
|
||||
sdkVersion: '10',
|
||||
interfaceType: IOSDeviceInterface.usb,
|
||||
).majorSdkVersion, 10);
|
||||
expect(IOSDevice(
|
||||
'device-123',
|
||||
@@ -123,7 +127,8 @@ void main() {
|
||||
iMobileDevice: iMobileDevice,
|
||||
name: 'iPhone 1',
|
||||
cpuArchitecture: DarwinArch.arm64,
|
||||
sdkVersion: '0'
|
||||
sdkVersion: '0',
|
||||
interfaceType: IOSDeviceInterface.usb,
|
||||
).majorSdkVersion, 0);
|
||||
expect(IOSDevice(
|
||||
'device-123',
|
||||
@@ -135,7 +140,8 @@ void main() {
|
||||
iMobileDevice: iMobileDevice,
|
||||
name: 'iPhone 1',
|
||||
cpuArchitecture: DarwinArch.arm64,
|
||||
sdkVersion: 'bogus'
|
||||
sdkVersion: 'bogus',
|
||||
interfaceType: IOSDeviceInterface.usb,
|
||||
).majorSdkVersion, 0);
|
||||
});
|
||||
|
||||
@@ -154,6 +160,7 @@ void main() {
|
||||
name: 'iPhone 1',
|
||||
sdkVersion: '13.3',
|
||||
cpuArchitecture: DarwinArch.arm64,
|
||||
interfaceType: IOSDeviceInterface.usb,
|
||||
);
|
||||
},
|
||||
throwsAssertionError,
|
||||
@@ -237,6 +244,7 @@ void main() {
|
||||
name: 'iPhone 1',
|
||||
sdkVersion: '13.3',
|
||||
cpuArchitecture: DarwinArch.arm64,
|
||||
interfaceType: IOSDeviceInterface.usb,
|
||||
);
|
||||
logReader1 = createLogReader(device, appPackage1, mockProcess1);
|
||||
logReader2 = createLogReader(device, appPackage2, mockProcess2);
|
||||
@@ -321,8 +329,9 @@ void main() {
|
||||
logger: logger,
|
||||
platform: macPlatform,
|
||||
fileSystem: MemoryFileSystem.test(),
|
||||
interfaceType: IOSDeviceInterface.usb,
|
||||
);
|
||||
when(mockXcdevice.getAvailableTetheredIOSDevices())
|
||||
when(mockXcdevice.getAvailableIOSDevices())
|
||||
.thenAnswer((Invocation invocation) => Future<List<IOSDevice>>.value(<IOSDevice>[device]));
|
||||
|
||||
final List<Device> devices = await iosDevices.pollingGetDevices();
|
||||
|
||||
@@ -25,7 +25,7 @@ const Map<String, String> kDyLdLibEntry = <String, String>{
|
||||
};
|
||||
|
||||
void main() {
|
||||
testWithoutContext('IOSDevice.installApp calls ios-deploy correctly', () async {
|
||||
testWithoutContext('IOSDevice.installApp calls ios-deploy correctly with USB', () async {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final IOSApp iosApp = PrebuiltIOSApp(
|
||||
projectBundleId: 'app',
|
||||
@@ -47,6 +47,36 @@ void main() {
|
||||
final IOSDevice device = setUpIOSDevice(
|
||||
processManager: processManager,
|
||||
fileSystem: fileSystem,
|
||||
interfaceType: IOSDeviceInterface.usb,
|
||||
);
|
||||
final bool wasInstalled = await device.installApp(iosApp);
|
||||
|
||||
expect(wasInstalled, true);
|
||||
expect(processManager.hasRemainingExpectations, false);
|
||||
});
|
||||
|
||||
testWithoutContext('IOSDevice.installApp calls ios-deploy correctly with network', () async {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
final IOSApp iosApp = PrebuiltIOSApp(
|
||||
projectBundleId: 'app',
|
||||
bundleDir: fileSystem.currentDirectory,
|
||||
);
|
||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||
const FakeCommand(command: <String>[
|
||||
'ios-deploy',
|
||||
'--id',
|
||||
'1234',
|
||||
'--bundle',
|
||||
'/',
|
||||
], environment: <String, String>{
|
||||
'PATH': '/usr/bin:null',
|
||||
...kDyLdLibEntry,
|
||||
})
|
||||
]);
|
||||
final IOSDevice device = setUpIOSDevice(
|
||||
processManager: processManager,
|
||||
fileSystem: fileSystem,
|
||||
interfaceType: IOSDeviceInterface.network,
|
||||
);
|
||||
final bool wasInstalled = await device.installApp(iosApp);
|
||||
|
||||
@@ -237,6 +267,7 @@ IOSDevice setUpIOSDevice({
|
||||
@required ProcessManager processManager,
|
||||
FileSystem fileSystem,
|
||||
Logger logger,
|
||||
IOSDeviceInterface interfaceType,
|
||||
}) {
|
||||
logger ??= BufferLogger.test();
|
||||
final FakePlatform platform = FakePlatform(
|
||||
@@ -270,6 +301,7 @@ IOSDevice setUpIOSDevice({
|
||||
cache: cache,
|
||||
),
|
||||
artifacts: artifacts,
|
||||
interfaceType: interfaceType,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,7 @@ IOSDevice setUpIOSDevice(FileSystem fileSystem) {
|
||||
sdkVersion: '13.3',
|
||||
cpuArchitecture: DarwinArch.arm64,
|
||||
artifacts: artifacts,
|
||||
interfaceType: IOSDeviceInterface.usb,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -329,6 +329,7 @@ IOSDevice setUpIOSDevice({
|
||||
cache: cache,
|
||||
),
|
||||
cpuArchitecture: DarwinArch.arm64,
|
||||
interfaceType: IOSDeviceInterface.usb,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -398,6 +398,7 @@ IOSDevice setUpIOSDevice({
|
||||
cache: cache,
|
||||
),
|
||||
cpuArchitecture: DarwinArch.arm64,
|
||||
interfaceType: IOSDeviceInterface.usb,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ void main() {
|
||||
when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '2']))
|
||||
.thenThrow(const ProcessException('xcrun', <String>['xcdevice', 'list', '--timeout', '2']));
|
||||
|
||||
expect(await xcdevice.getAvailableTetheredIOSDevices(), isEmpty);
|
||||
expect(await xcdevice.getAvailableIOSDevices(), isEmpty);
|
||||
});
|
||||
|
||||
testWithoutContext('diagnostics xcdevice fails', () async {
|
||||
@@ -359,7 +359,7 @@ void main() {
|
||||
testWithoutContext('Xcode not installed', () async {
|
||||
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(false);
|
||||
|
||||
expect(await xcdevice.getAvailableTetheredIOSDevices(), isEmpty);
|
||||
expect(await xcdevice.getAvailableIOSDevices(), isEmpty);
|
||||
});
|
||||
|
||||
testUsingContext('returns devices', () async {
|
||||
@@ -466,7 +466,7 @@ void main() {
|
||||
command: <String>['xcrun', 'xcdevice', 'list', '--timeout', '2'],
|
||||
stdout: devicesOutput,
|
||||
));
|
||||
final List<IOSDevice> devices = await xcdevice.getAvailableTetheredIOSDevices();
|
||||
final List<IOSDevice> devices = await xcdevice.getAvailableIOSDevices();
|
||||
expect(devices, hasLength(3));
|
||||
expect(devices[0].id, 'd83d5bc53967baa0ee18626ba87b6254b2ab5418');
|
||||
expect(devices[0].name, 'An iPhone (Space Gray)');
|
||||
@@ -496,7 +496,7 @@ void main() {
|
||||
command: <String>['xcrun', 'xcdevice', 'list', '--timeout', '20'],
|
||||
stdout: '[]',
|
||||
));
|
||||
await xcdevice.getAvailableTetheredIOSDevices(timeout: const Duration(seconds: 20));
|
||||
await xcdevice.getAvailableIOSDevices(timeout: const Duration(seconds: 20));
|
||||
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
|
||||
});
|
||||
|
||||
@@ -535,7 +535,7 @@ void main() {
|
||||
command: <String>['xcrun', 'xcdevice', 'list', '--timeout', '2'],
|
||||
stdout: devicesOutput,
|
||||
));
|
||||
final List<IOSDevice> devices = await xcdevice.getAvailableTetheredIOSDevices();
|
||||
final List<IOSDevice> devices = await xcdevice.getAvailableIOSDevices();
|
||||
expect(devices, hasLength(1));
|
||||
expect(devices[0].id, '43ad2fda7991b34fe1acbda82f9e2fd3d6ddc9f7');
|
||||
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
|
||||
@@ -583,7 +583,7 @@ void main() {
|
||||
command: <String>['xcrun', 'xcdevice', 'list', '--timeout', '2'],
|
||||
stdout: devicesOutput,
|
||||
));
|
||||
final List<IOSDevice> devices = await xcdevice.getAvailableTetheredIOSDevices();
|
||||
final List<IOSDevice> devices = await xcdevice.getAvailableIOSDevices();
|
||||
expect(devices[0].cpuArchitecture, DarwinArch.armv7);
|
||||
expect(devices[1].cpuArchitecture, DarwinArch.arm64);
|
||||
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
|
||||
@@ -634,7 +634,7 @@ void main() {
|
||||
stdout: devicesOutput,
|
||||
));
|
||||
|
||||
await xcdevice.getAvailableTetheredIOSDevices();
|
||||
await xcdevice.getAvailableIOSDevices();
|
||||
final List<String> errors = await xcdevice.getDiagnostics();
|
||||
expect(errors, hasLength(1));
|
||||
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
|
||||
|
||||
Reference in New Issue
Block a user