Unverified Commit e345a830 authored by LouiseHsu's avatar LouiseHsu Committed by GitHub

Show warning when attempting to flutter run on an ios device with developer...

Show warning when attempting to flutter run on an ios device with developer mode turned off (#125710)

This PR adds a warning when a user attempt to `flutter run -d <device id>` on a device without developer mode enabled.
<img width="738" alt="Screenshot 2023-05-09 at 3 53 18 AM" src="https://github.com/flutter/flutter/assets/36148254/6f473a6a-5a0d-438b-9e6f-06d09eb1f3a9">

Also handles multiple partial matches.
<img width="788" alt="Screenshot 2023-05-09 at 3 52 24 AM" src="https://github.com/flutter/flutter/assets/36148254/60c82b3c-d501-4a01-95ad-d6309fe39576">

Fixes https://github.com/flutter/flutter/issues/111988
parent 487ed573
...@@ -261,6 +261,7 @@ class IOSDevice extends Device { ...@@ -261,6 +261,7 @@ class IOSDevice extends Device {
required this.cpuArchitecture, required this.cpuArchitecture,
required this.connectionInterface, required this.connectionInterface,
required this.isConnected, required this.isConnected,
required this.devModeEnabled,
String? sdkVersion, String? sdkVersion,
required Platform platform, required Platform platform,
required IOSDeploy iosDeploy, required IOSDeploy iosDeploy,
...@@ -323,6 +324,8 @@ class IOSDevice extends Device { ...@@ -323,6 +324,8 @@ class IOSDevice extends Device {
DevicePortForwarder? _portForwarder; DevicePortForwarder? _portForwarder;
bool devModeEnabled = false;
@visibleForTesting @visibleForTesting
IOSDeployDebugger? iosDeployDebugger; IOSDeployDebugger? iosDeployDebugger;
......
...@@ -505,7 +505,7 @@ class XCDevice { ...@@ -505,7 +505,7 @@ class XCDevice {
if (identifier == null || name == null) { if (identifier == null || name == null) {
continue; continue;
} }
bool devModeEnabled = true;
bool isConnected = true; bool isConnected = true;
final Map<String, Object?>? errorProperties = _errorProperties(device); final Map<String, Object?>? errorProperties = _errorProperties(device);
if (errorProperties != null) { if (errorProperties != null) {
...@@ -525,6 +525,10 @@ class XCDevice { ...@@ -525,6 +525,10 @@ class XCDevice {
if (code != -10) { if (code != -10) {
isConnected = false; isConnected = false;
} }
if (code == 6) {
devModeEnabled = false;
}
} }
String? sdkVersion = _sdkVersion(device); String? sdkVersion = _sdkVersion(device);
...@@ -549,6 +553,7 @@ class XCDevice { ...@@ -549,6 +553,7 @@ class XCDevice {
iosDeploy: _iosDeploy, iosDeploy: _iosDeploy,
iMobileDevice: _iMobileDevice, iMobileDevice: _iMobileDevice,
platform: globals.platform, platform: globals.platform,
devModeEnabled: devModeEnabled
)); ));
} }
} }
......
...@@ -29,6 +29,8 @@ String _foundSpecifiedDevicesMessage(int count, String deviceId) => ...@@ -29,6 +29,8 @@ String _foundSpecifiedDevicesMessage(int count, String deviceId) =>
'Found $count devices with name or id matching $deviceId:'; 'Found $count devices with name or id matching $deviceId:';
String _noMatchingDeviceMessage(String deviceId) => 'No supported devices found with name or id ' String _noMatchingDeviceMessage(String deviceId) => 'No supported devices found with name or id '
"matching '$deviceId'."; "matching '$deviceId'.";
String flutterSpecifiedDeviceDevModeDisabled(String deviceName) => 'To use '
"'$deviceName' for development, enable Developer Mode in Settings → Privacy & Security.";
/// This class handles functionality of finding and selecting target devices. /// This class handles functionality of finding and selecting target devices.
/// ///
...@@ -486,18 +488,39 @@ class TargetDevicesWithExtendedWirelessDeviceDiscovery extends TargetDevices { ...@@ -486,18 +488,39 @@ class TargetDevicesWithExtendedWirelessDeviceDiscovery extends TargetDevices {
// If there are multiple matches, continue on to wait for all attached // If there are multiple matches, continue on to wait for all attached
// and wireless devices to load so the user can select between all // and wireless devices to load so the user can select between all
// connected matches. // connected matches.
final List<Device> devices = await _getDeviceById( final List<Device> specifiedDevices = await _getDeviceById(
includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject, includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject,
includeDisconnected: true, includeDisconnected: true,
); );
if (devices.length == 1) {
Device? matchedDevice = devices.first; 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.isConnected && matchedDevice is IOSDevice) { if (!matchedDevice.isConnected && matchedDevice is IOSDevice) {
matchedDevice = await _waitForIOSDeviceToConnect(matchedDevice); matchedDevice = await _waitForIOSDeviceToConnect(matchedDevice);
} }
if (matchedDevice != null && matchedDevice.isConnected) { if (matchedDevice != null && matchedDevice.isConnected) {
return <Device>[matchedDevice]; return <Device>[matchedDevice];
} }
} else {
for (final Device device in specifiedDevices) {
// Print warning for every matching device that does not have Developer Mode enabled.
if (device is IOSDevice && !device.devModeEnabled) {
_logger.printStatus(
flutterSpecifiedDeviceDevModeDisabled(device.name)
);
}
}
} }
} }
......
...@@ -76,6 +76,7 @@ void main() { ...@@ -76,6 +76,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
); );
expect(device.isSupported(), isTrue); expect(device.isSupported(), isTrue);
}); });
...@@ -93,6 +94,7 @@ void main() { ...@@ -93,6 +94,7 @@ void main() {
cpuArchitecture: DarwinArch.armv7, cpuArchitecture: DarwinArch.armv7,
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
); );
expect(device.isSupported(), isFalse); expect(device.isSupported(), isFalse);
}); });
...@@ -111,6 +113,7 @@ void main() { ...@@ -111,6 +113,7 @@ void main() {
sdkVersion: '1.0.0', sdkVersion: '1.0.0',
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
).majorSdkVersion, 1); ).majorSdkVersion, 1);
expect(IOSDevice( expect(IOSDevice(
'device-123', 'device-123',
...@@ -125,6 +128,7 @@ void main() { ...@@ -125,6 +128,7 @@ void main() {
sdkVersion: '13.1.1', sdkVersion: '13.1.1',
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
).majorSdkVersion, 13); ).majorSdkVersion, 13);
expect(IOSDevice( expect(IOSDevice(
'device-123', 'device-123',
...@@ -139,6 +143,7 @@ void main() { ...@@ -139,6 +143,7 @@ void main() {
sdkVersion: '10', sdkVersion: '10',
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
).majorSdkVersion, 10); ).majorSdkVersion, 10);
expect(IOSDevice( expect(IOSDevice(
'device-123', 'device-123',
...@@ -153,6 +158,7 @@ void main() { ...@@ -153,6 +158,7 @@ void main() {
sdkVersion: '0', sdkVersion: '0',
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
).majorSdkVersion, 0); ).majorSdkVersion, 0);
expect(IOSDevice( expect(IOSDevice(
'device-123', 'device-123',
...@@ -167,6 +173,7 @@ void main() { ...@@ -167,6 +173,7 @@ void main() {
sdkVersion: 'bogus', sdkVersion: 'bogus',
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
).majorSdkVersion, 0); ).majorSdkVersion, 0);
}); });
...@@ -184,6 +191,7 @@ void main() { ...@@ -184,6 +191,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
); );
expect(await device.sdkNameAndVersion,'iOS 13.3 17C54'); expect(await device.sdkNameAndVersion,'iOS 13.3 17C54');
...@@ -203,6 +211,7 @@ void main() { ...@@ -203,6 +211,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
); );
expect(device.supportsRuntimeMode(BuildMode.debug), true); expect(device.supportsRuntimeMode(BuildMode.debug), true);
...@@ -228,6 +237,7 @@ void main() { ...@@ -228,6 +237,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
); );
}, },
throwsAssertionError, throwsAssertionError,
...@@ -319,6 +329,7 @@ void main() { ...@@ -319,6 +329,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
); );
logReader1 = createLogReader(device, appPackage1, process1); logReader1 = createLogReader(device, appPackage1, process1);
logReader2 = createLogReader(device, appPackage2, process2); logReader2 = createLogReader(device, appPackage2, process2);
...@@ -381,6 +392,7 @@ void main() { ...@@ -381,6 +392,7 @@ void main() {
fileSystem: MemoryFileSystem.test(), fileSystem: MemoryFileSystem.test(),
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
); );
device2 = IOSDevice( device2 = IOSDevice(
...@@ -396,6 +408,7 @@ void main() { ...@@ -396,6 +408,7 @@ void main() {
fileSystem: MemoryFileSystem.test(), fileSystem: MemoryFileSystem.test(),
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
); );
}); });
...@@ -687,6 +700,7 @@ void main() { ...@@ -687,6 +700,7 @@ void main() {
fileSystem: MemoryFileSystem.test(), fileSystem: MemoryFileSystem.test(),
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: false, isConnected: false,
devModeEnabled: true,
); );
}); });
......
...@@ -360,5 +360,6 @@ IOSDevice setUpIOSDevice({ ...@@ -360,5 +360,6 @@ IOSDevice setUpIOSDevice({
iProxy: IProxy.test(logger: logger, processManager: processManager), iProxy: IProxy.test(logger: logger, processManager: processManager),
connectionInterface: interfaceType ?? DeviceConnectionInterface.attached, connectionInterface: interfaceType ?? DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
); );
} }
...@@ -101,5 +101,6 @@ IOSDevice setUpIOSDevice(FileSystem fileSystem) { ...@@ -101,5 +101,6 @@ IOSDevice setUpIOSDevice(FileSystem fileSystem) {
iProxy: IProxy.test(logger: logger, processManager: processManager), iProxy: IProxy.test(logger: logger, processManager: processManager),
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
); );
} }
...@@ -339,6 +339,7 @@ IOSDevice setUpIOSDevice({ ...@@ -339,6 +339,7 @@ IOSDevice setUpIOSDevice({
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached, connectionInterface: DeviceConnectionInterface.attached,
isConnected: true, isConnected: true,
devModeEnabled: true,
); );
} }
......
...@@ -649,6 +649,7 @@ IOSDevice setUpIOSDevice({ ...@@ -649,6 +649,7 @@ IOSDevice setUpIOSDevice({
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
connectionInterface: interfaceType, connectionInterface: interfaceType,
isConnected: true, isConnected: true,
devModeEnabled: true,
); );
} }
......
...@@ -1262,6 +1262,45 @@ target-device (mobile) • xxx • ios • iOS 16 (unsupported) ...@@ -1262,6 +1262,45 @@ target-device (mobile) • xxx • ios • iOS 16 (unsupported)
expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse); expect(deviceManager.iosDiscoverer.xcdevice.waitedForDeviceToConnect, isFalse);
}); });
testUsingContext('when only matching device is dev mode disabled', () async {
deviceManager.iosDiscoverer.deviceList = <Device>[FakeIOSDevice(deviceName: 'target-device', devModeEnabled: false)];
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
To use 'target-device' for development, enable Developer Mode in Settings → Privacy & Security.
'''));
expect(devices, isNull);
});
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)];
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
To use 'target-device-1' for development, enable Developer Mode in Settings → Privacy & Security.
Checking for wireless devices...
'''));
expect(devices, isNotNull);
});
testUsingContext('when all matching devices are dev mode disabled', () async {
deviceManager.iosDiscoverer.deviceList = <Device>[FakeIOSDevice(deviceName: 'target-device-1', devModeEnabled: false, isConnected: false),
FakeIOSDevice(deviceName: 'target-device-2', devModeEnabled: false, isConnected: false)];
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
To use 'target-device-1' for development, enable Developer Mode in Settings → Privacy & Security.
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);
});
group('when deviceConnectionInterface does not match', () { group('when deviceConnectionInterface does not match', () {
testUsingContext('filter of wireless', () async { testUsingContext('filter of wireless', () async {
final FakeIOSDevice device1 = FakeIOSDevice.notConnectedWireless(deviceName: 'not-a-match'); final FakeIOSDevice device1 = FakeIOSDevice.notConnectedWireless(deviceName: 'not-a-match');
...@@ -2691,6 +2730,7 @@ class FakeIOSDevice extends Fake implements IOSDevice { ...@@ -2691,6 +2730,7 @@ class FakeIOSDevice extends Fake implements IOSDevice {
FakeIOSDevice({ FakeIOSDevice({
String? deviceId, String? deviceId,
String? deviceName, String? deviceName,
bool? devModeEnabled,
bool deviceSupported = true, bool deviceSupported = true,
bool deviceSupportForProject = true, bool deviceSupportForProject = true,
this.ephemeral = true, this.ephemeral = true,
...@@ -2699,6 +2739,7 @@ class FakeIOSDevice extends Fake implements IOSDevice { ...@@ -2699,6 +2739,7 @@ class FakeIOSDevice extends Fake implements IOSDevice {
this.connectionInterface = DeviceConnectionInterface.attached, this.connectionInterface = DeviceConnectionInterface.attached,
}) : id = deviceId ?? 'xxx', }) : id = deviceId ?? 'xxx',
name = deviceName ?? 'test', name = deviceName ?? 'test',
devModeEnabled = devModeEnabled ?? true,
_isSupported = deviceSupported, _isSupported = deviceSupported,
_isSupportedForProject = deviceSupportForProject; _isSupportedForProject = deviceSupportForProject;
...@@ -2710,6 +2751,7 @@ class FakeIOSDevice extends Fake implements IOSDevice { ...@@ -2710,6 +2751,7 @@ class FakeIOSDevice extends Fake implements IOSDevice {
this.ephemeral = true, this.ephemeral = true,
this.isConnected = false, this.isConnected = false,
this.platformType = PlatformType.ios, this.platformType = PlatformType.ios,
this.devModeEnabled = true,
this.connectionInterface = DeviceConnectionInterface.wireless, this.connectionInterface = DeviceConnectionInterface.wireless,
}) : id = deviceId ?? 'xxx', }) : id = deviceId ?? 'xxx',
name = deviceName ?? 'test', name = deviceName ?? 'test',
...@@ -2723,6 +2765,7 @@ class FakeIOSDevice extends Fake implements IOSDevice { ...@@ -2723,6 +2765,7 @@ class FakeIOSDevice extends Fake implements IOSDevice {
bool deviceSupportForProject = true, bool deviceSupportForProject = true,
this.ephemeral = true, this.ephemeral = true,
this.isConnected = true, this.isConnected = true,
this.devModeEnabled = true,
this.platformType = PlatformType.ios, this.platformType = PlatformType.ios,
this.connectionInterface = DeviceConnectionInterface.wireless, this.connectionInterface = DeviceConnectionInterface.wireless,
}) : id = deviceId ?? 'xxx', }) : id = deviceId ?? 'xxx',
...@@ -2739,6 +2782,9 @@ class FakeIOSDevice extends Fake implements IOSDevice { ...@@ -2739,6 +2782,9 @@ class FakeIOSDevice extends Fake implements IOSDevice {
@override @override
final bool ephemeral; final bool ephemeral;
@override
final bool devModeEnabled;
@override @override
String id; 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