Unverified Commit 9b442b27 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Print warning and exit when iOS device is unpaired (#144551)

Explicitly handle the case where the iOS device is not paired.  On `flutter run` show an error and bail instead of trying and failing to launch on the device.

On this PR:
```
$ flutter run -d 00008110-0009588C2651401E
'iPhone' is not paired. Open Xcode and trust this computer when prompted.
$
```

Fixes https://github.com/flutter/flutter/issues/144447
Closes https://github.com/flutter/flutter/pull/144095
parent d9eea796
......@@ -267,6 +267,7 @@ class IOSDevice extends Device {
required this.cpuArchitecture,
required this.connectionInterface,
required this.isConnected,
required this.isPaired,
required this.devModeEnabled,
required this.isCoreDevice,
String? sdkVersion,
......@@ -336,6 +337,11 @@ class IOSDevice extends Device {
@override
bool isConnected;
bool devModeEnabled = false;
/// Device has trusted this computer and paired.
bool isPaired = false;
/// CoreDevice is a device connectivity stack introduced in Xcode 15. Devices
/// with iOS 17 or greater are CoreDevices.
final bool isCoreDevice;
......@@ -344,8 +350,6 @@ class IOSDevice extends Device {
DevicePortForwarder? _portForwarder;
bool devModeEnabled = false;
@visibleForTesting
IOSDeployDebugger? iosDeployDebugger;
......
......@@ -544,6 +544,7 @@ class XCDevice {
}
bool devModeEnabled = true;
bool isConnected = true;
bool isPaired = true;
final Map<String, Object?>? errorProperties = _errorProperties(device);
if (errorProperties != null) {
final String? errorMessage = _parseErrorMessage(errorProperties);
......@@ -564,6 +565,10 @@ class XCDevice {
if (code != -10) {
isConnected = false;
}
// Error: iPhone is not paired with your computer. To use iPhone with Xcode, unlock it and choose to trust this computer when prompted. (code -9)
if (code == -9) {
isPaired = false;
}
if (code == 6) {
devModeEnabled = false;
......@@ -635,6 +640,7 @@ class XCDevice {
xcodeDebug: _xcodeDebug,
platform: globals.platform,
devModeEnabled: devModeEnabled,
isPaired: isPaired,
isCoreDevice: coreDevice != null,
);
}
......
......@@ -30,6 +30,8 @@ String _noMatchingDeviceMessage(String deviceId) => 'No supported devices found
"matching '$deviceId'.";
String flutterSpecifiedDeviceDevModeDisabled(String deviceName) => 'To use '
"'$deviceName' for development, enable Developer Mode in Settings → Privacy & Security.";
String flutterSpecifiedDeviceUnpaired(String deviceName) => "'$deviceName' is not paired. "
'Open Xcode and trust this computer when prompted.';
/// This class handles functionality of finding and selecting target devices.
///
......@@ -494,17 +496,24 @@ class TargetDevicesWithExtendedWirelessDeviceDiscovery extends TargetDevices {
if (specifiedDevices.length == 1) {
Device? matchedDevice = specifiedDevices.first;
// If the only matching device does not have Developer Mode enabled,
// print a warning
if (matchedDevice is IOSDevice && !matchedDevice.devModeEnabled) {
_logger.printStatus(
flutterSpecifiedDeviceDevModeDisabled(matchedDevice.name)
);
return null;
}
if (matchedDevice is IOSDevice) {
// If the only matching device is not paired, print a warning
if (!matchedDevice.isPaired) {
_logger.printStatus(flutterSpecifiedDeviceUnpaired(matchedDevice.name));
return null;
}
// If the only matching device does not have Developer Mode enabled,
// print a warning
if (!matchedDevice.devModeEnabled) {
_logger.printStatus(
flutterSpecifiedDeviceDevModeDisabled(matchedDevice.name)
);
return null;
}
if (!matchedDevice.isConnected && matchedDevice is IOSDevice) {
matchedDevice = await _waitForIOSDeviceToConnect(matchedDevice);
if (!matchedDevice.isConnected) {
matchedDevice = await _waitForIOSDeviceToConnect(matchedDevice);
}
}
if (matchedDevice != null && matchedDevice.isConnected) {
......@@ -512,9 +521,14 @@ class TargetDevicesWithExtendedWirelessDeviceDiscovery extends TargetDevices {
}
} else {
for (final Device device in specifiedDevices) {
for (final IOSDevice device in specifiedDevices.whereType<IOSDevice>()) {
// Print warning for every matching unpaired device.
if (!device.isPaired) {
_logger.printStatus(flutterSpecifiedDeviceUnpaired(device.name));
}
// Print warning for every matching device that does not have Developer Mode enabled.
if (device is IOSDevice && !device.devModeEnabled) {
if (!device.devModeEnabled) {
_logger.printStatus(
flutterSpecifiedDeviceDevModeDisabled(device.name)
);
......
......@@ -85,6 +85,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
);
......@@ -106,6 +107,7 @@ void main() {
cpuArchitecture: DarwinArch.armv7,
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
);
......@@ -128,6 +130,7 @@ void main() {
sdkVersion: '1.0.0',
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
).majorSdkVersion, 1);
......@@ -146,6 +149,7 @@ void main() {
sdkVersion: '13.1.1',
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
).majorSdkVersion, 13);
......@@ -164,6 +168,7 @@ void main() {
sdkVersion: '10',
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
).majorSdkVersion, 10);
......@@ -182,6 +187,7 @@ void main() {
sdkVersion: '0',
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
).majorSdkVersion, 0);
......@@ -200,6 +206,7 @@ void main() {
sdkVersion: 'bogus',
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
).majorSdkVersion, 0);
......@@ -221,6 +228,7 @@ void main() {
sdkVersion: '13.3.1',
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
).sdkVersion;
......@@ -244,6 +252,7 @@ void main() {
sdkVersion: '13.3.1 (20ADBC)',
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
).sdkVersion;
......@@ -267,6 +276,7 @@ void main() {
sdkVersion: '16.4.1(a) (20ADBC)',
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
).sdkVersion;
......@@ -290,6 +300,7 @@ void main() {
sdkVersion: '0',
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
).sdkVersion;
......@@ -312,6 +323,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
).sdkVersion;
......@@ -332,6 +344,7 @@ void main() {
sdkVersion: 'bogus',
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
).sdkVersion;
......@@ -354,6 +367,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
);
......@@ -377,6 +391,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
);
......@@ -406,6 +421,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
);
......@@ -501,6 +517,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
);
......@@ -571,6 +588,7 @@ void main() {
fileSystem: MemoryFileSystem.test(),
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
);
......@@ -590,6 +608,7 @@ void main() {
fileSystem: MemoryFileSystem.test(),
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
);
......@@ -889,6 +908,7 @@ void main() {
fileSystem: MemoryFileSystem.test(),
connectionInterface: DeviceConnectionInterface.attached,
isConnected: false,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
);
......
......@@ -434,6 +434,7 @@ IOSDevice setUpIOSDevice({
iProxy: IProxy.test(logger: logger, processManager: processManager),
connectionInterface: interfaceType ?? DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: isCoreDevice,
);
......
......@@ -106,6 +106,7 @@ IOSDevice setUpIOSDevice(FileSystem fileSystem) {
iProxy: IProxy.test(logger: logger, processManager: processManager),
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: false,
);
......
......@@ -869,6 +869,7 @@ IOSDevice setUpIOSDevice({
cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: isCoreDevice,
);
......
......@@ -1094,6 +1094,7 @@ IOSDevice setUpIOSDevice({
cpuArchitecture: DarwinArch.arm64,
connectionInterface: interfaceType,
isConnected: true,
isPaired: true,
devModeEnabled: true,
isCoreDevice: isCoreDevice,
);
......
......@@ -1275,7 +1275,7 @@ To use 'target-device' for development, enable Developer Mode in Settings → Pr
testUsingContext('when one of the matching devices has dev mode disabled', () async {
deviceManager.iosDiscoverer.deviceList = <Device>[FakeIOSDevice(deviceName: 'target-device-1', devModeEnabled: false, isConnected: false),
FakeIOSDevice(deviceName: 'target-device-2', devModeEnabled: true)];
FakeIOSDevice(deviceName: 'target-device-2')];
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
......@@ -1296,6 +1296,45 @@ To use 'target-device-1' for development, enable Developer Mode in Settings →
To use 'target-device-2' for development, enable Developer Mode in Settings → Privacy & Security.
No devices found yet. Checking for wireless devices...
No supported devices found with name or id matching 'target-device'.
'''));
expect(devices, isNull);
});
testUsingContext('when only matching device is unpaired', () async {
deviceManager.iosDiscoverer.deviceList = <Device>[FakeIOSDevice(deviceName: 'target-device', isPaired: false)];
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
'target-device' is not paired. Open Xcode and trust this computer when prompted.
'''));
expect(devices, isNull);
});
testUsingContext('when one of the matching devices is unpaired', () async {
deviceManager.iosDiscoverer.deviceList = <Device>[FakeIOSDevice(deviceName: 'target-device-1', isPaired: false, isConnected: false),
FakeIOSDevice(deviceName: 'target-device-2')];
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, contains('''
'target-device-1' is not paired. Open Xcode and trust this computer when prompted.
Checking for wireless devices...
'''));
expect(devices, isNotNull);
});
testUsingContext('when all matching devices are unpaired', () async {
deviceManager.iosDiscoverer.deviceList = <Device>[FakeIOSDevice(deviceName: 'target-device-1', isPaired: false, isConnected: false),
FakeIOSDevice(deviceName: 'target-device-2', isPaired: false, isConnected: false)];
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, contains('''
'target-device-1' is not paired. Open Xcode and trust this computer when prompted.
'target-device-2' is not paired. Open Xcode and trust this computer when prompted.
No devices found yet. Checking for wireless devices...
No supported devices found with name or id matching 'target-device'.
'''));
expect(devices, isNull);
......@@ -2777,16 +2816,16 @@ class FakeIOSDevice extends Fake implements IOSDevice {
FakeIOSDevice({
String? deviceId,
String? deviceName,
bool? devModeEnabled,
bool deviceSupported = true,
bool deviceSupportForProject = true,
this.ephemeral = true,
this.isConnected = true,
this.devModeEnabled = true,
this.isPaired = true,
this.platformType = PlatformType.ios,
this.connectionInterface = DeviceConnectionInterface.attached,
}) : id = deviceId ?? 'xxx',
name = deviceName ?? 'test',
devModeEnabled = devModeEnabled ?? true,
_isSupported = deviceSupported,
_isSupportedForProject = deviceSupportForProject;
......@@ -2799,6 +2838,7 @@ class FakeIOSDevice extends Fake implements IOSDevice {
this.isConnected = false,
this.platformType = PlatformType.ios,
this.devModeEnabled = true,
this.isPaired = true,
this.connectionInterface = DeviceConnectionInterface.wireless,
}) : id = deviceId ?? 'xxx',
name = deviceName ?? 'test',
......@@ -2813,6 +2853,7 @@ class FakeIOSDevice extends Fake implements IOSDevice {
this.ephemeral = true,
this.isConnected = true,
this.devModeEnabled = true,
this.isPaired = true,
this.platformType = PlatformType.ios,
this.connectionInterface = DeviceConnectionInterface.wireless,
}) : id = deviceId ?? 'xxx',
......@@ -2832,6 +2873,9 @@ class FakeIOSDevice extends Fake implements IOSDevice {
@override
final bool devModeEnabled;
@override
final bool isPaired;
@override
String id;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment