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
This commit is contained in:
LouiseHsu 2023-05-22 15:06:15 -07:00 committed by GitHub
parent 487ed57388
commit e345a830ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 99 additions and 4 deletions

View File

@ -261,6 +261,7 @@ class IOSDevice extends Device {
required this.cpuArchitecture,
required this.connectionInterface,
required this.isConnected,
required this.devModeEnabled,
String? sdkVersion,
required Platform platform,
required IOSDeploy iosDeploy,
@ -323,6 +324,8 @@ class IOSDevice extends Device {
DevicePortForwarder? _portForwarder;
bool devModeEnabled = false;
@visibleForTesting
IOSDeployDebugger? iosDeployDebugger;

View File

@ -505,7 +505,7 @@ class XCDevice {
if (identifier == null || name == null) {
continue;
}
bool devModeEnabled = true;
bool isConnected = true;
final Map<String, Object?>? errorProperties = _errorProperties(device);
if (errorProperties != null) {
@ -525,6 +525,10 @@ class XCDevice {
if (code != -10) {
isConnected = false;
}
if (code == 6) {
devModeEnabled = false;
}
}
String? sdkVersion = _sdkVersion(device);
@ -549,6 +553,7 @@ class XCDevice {
iosDeploy: _iosDeploy,
iMobileDevice: _iMobileDevice,
platform: globals.platform,
devModeEnabled: devModeEnabled
));
}
}

View File

@ -29,6 +29,8 @@ String _foundSpecifiedDevicesMessage(int count, String deviceId) =>
'Found $count devices with name or id matching $deviceId:';
String _noMatchingDeviceMessage(String deviceId) => 'No supported devices found with name or id '
"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.
///
@ -486,18 +488,39 @@ class TargetDevicesWithExtendedWirelessDeviceDiscovery extends TargetDevices {
// If there are multiple matches, continue on to wait for all attached
// and wireless devices to load so the user can select between all
// connected matches.
final List<Device> devices = await _getDeviceById(
final List<Device> specifiedDevices = await _getDeviceById(
includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject,
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) {
matchedDevice = await _waitForIOSDeviceToConnect(matchedDevice);
}
if (matchedDevice != null && matchedDevice.isConnected) {
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)
);
}
}
}
}

View File

@ -76,6 +76,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
);
expect(device.isSupported(), isTrue);
});
@ -93,6 +94,7 @@ void main() {
cpuArchitecture: DarwinArch.armv7,
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
);
expect(device.isSupported(), isFalse);
});
@ -111,6 +113,7 @@ void main() {
sdkVersion: '1.0.0',
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
).majorSdkVersion, 1);
expect(IOSDevice(
'device-123',
@ -125,6 +128,7 @@ void main() {
sdkVersion: '13.1.1',
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
).majorSdkVersion, 13);
expect(IOSDevice(
'device-123',
@ -139,6 +143,7 @@ void main() {
sdkVersion: '10',
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
).majorSdkVersion, 10);
expect(IOSDevice(
'device-123',
@ -153,6 +158,7 @@ void main() {
sdkVersion: '0',
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
).majorSdkVersion, 0);
expect(IOSDevice(
'device-123',
@ -167,6 +173,7 @@ void main() {
sdkVersion: 'bogus',
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
).majorSdkVersion, 0);
});
@ -184,6 +191,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
);
expect(await device.sdkNameAndVersion,'iOS 13.3 17C54');
@ -203,6 +211,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
);
expect(device.supportsRuntimeMode(BuildMode.debug), true);
@ -228,6 +237,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
);
},
throwsAssertionError,
@ -319,6 +329,7 @@ void main() {
cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
);
logReader1 = createLogReader(device, appPackage1, process1);
logReader2 = createLogReader(device, appPackage2, process2);
@ -381,6 +392,7 @@ void main() {
fileSystem: MemoryFileSystem.test(),
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
);
device2 = IOSDevice(
@ -396,6 +408,7 @@ void main() {
fileSystem: MemoryFileSystem.test(),
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
);
});
@ -687,6 +700,7 @@ void main() {
fileSystem: MemoryFileSystem.test(),
connectionInterface: DeviceConnectionInterface.attached,
isConnected: false,
devModeEnabled: true,
);
});

View File

@ -360,5 +360,6 @@ IOSDevice setUpIOSDevice({
iProxy: IProxy.test(logger: logger, processManager: processManager),
connectionInterface: interfaceType ?? DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
);
}

View File

@ -101,5 +101,6 @@ IOSDevice setUpIOSDevice(FileSystem fileSystem) {
iProxy: IProxy.test(logger: logger, processManager: processManager),
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
);
}

View File

@ -339,6 +339,7 @@ IOSDevice setUpIOSDevice({
cpuArchitecture: DarwinArch.arm64,
connectionInterface: DeviceConnectionInterface.attached,
isConnected: true,
devModeEnabled: true,
);
}

View File

@ -649,6 +649,7 @@ IOSDevice setUpIOSDevice({
cpuArchitecture: DarwinArch.arm64,
connectionInterface: interfaceType,
isConnected: true,
devModeEnabled: true,
);
}

View File

@ -1262,6 +1262,45 @@ target-device (mobile) • xxx • ios • iOS 16 (unsupported)
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', () {
testUsingContext('filter of wireless', () async {
final FakeIOSDevice device1 = FakeIOSDevice.notConnectedWireless(deviceName: 'not-a-match');
@ -2691,6 +2730,7 @@ class FakeIOSDevice extends Fake implements IOSDevice {
FakeIOSDevice({
String? deviceId,
String? deviceName,
bool? devModeEnabled,
bool deviceSupported = true,
bool deviceSupportForProject = true,
this.ephemeral = true,
@ -2699,6 +2739,7 @@ class FakeIOSDevice extends Fake implements IOSDevice {
this.connectionInterface = DeviceConnectionInterface.attached,
}) : id = deviceId ?? 'xxx',
name = deviceName ?? 'test',
devModeEnabled = devModeEnabled ?? true,
_isSupported = deviceSupported,
_isSupportedForProject = deviceSupportForProject;
@ -2710,6 +2751,7 @@ class FakeIOSDevice extends Fake implements IOSDevice {
this.ephemeral = true,
this.isConnected = false,
this.platformType = PlatformType.ios,
this.devModeEnabled = true,
this.connectionInterface = DeviceConnectionInterface.wireless,
}) : id = deviceId ?? 'xxx',
name = deviceName ?? 'test',
@ -2723,6 +2765,7 @@ class FakeIOSDevice extends Fake implements IOSDevice {
bool deviceSupportForProject = true,
this.ephemeral = true,
this.isConnected = true,
this.devModeEnabled = true,
this.platformType = PlatformType.ios,
this.connectionInterface = DeviceConnectionInterface.wireless,
}) : id = deviceId ?? 'xxx',
@ -2739,6 +2782,9 @@ class FakeIOSDevice extends Fake implements IOSDevice {
@override
final bool ephemeral;
@override
final bool devModeEnabled;
@override
String id;