Disallow running on unsupported devices (#97338)

diff --git a/packages/flutter_tools/lib/src/base/user_messages.dart b/packages/flutter_tools/lib/src/base/user_messages.dart
index f9c952c..362f4cc 100644
--- a/packages/flutter_tools/lib/src/base/user_messages.dart
+++ b/packages/flutter_tools/lib/src/base/user_messages.dart
@@ -247,8 +247,8 @@
   String get flutterNoDevelopmentDevice =>
       "Unable to locate a development device; please run 'flutter doctor' "
       'for information about installing additional components.';
-  String flutterNoMatchingDevice(String deviceId) => 'No devices found with name or id '
-      "matching '$deviceId'";
+  String flutterNoMatchingDevice(String deviceId) => 'No supported devices found with name or id '
+      "matching '$deviceId'.";
   String get flutterNoDevicesFound => 'No devices found';
   String get flutterNoSupportedDevices => 'No supported devices connected.';
   String flutterMissPlatformProjects(List<String> unsupportedDevicesType) =>
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 9dd0eb5..3136079 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -254,7 +254,8 @@
       await refreshAllConnectedDevices(timeout: timeout);
     }
 
-    List<Device> devices = await getDevices();
+    List<Device> devices = (await getDevices())
+        .where((Device device) => device.isSupported()).toList();
 
     // Always remove web and fuchsia devices from `--all`. This setting
     // currently requires devices to share a frontend_server and resident
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index 267f7c3..87844dc 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -1364,6 +1364,12 @@
 
     if (devices.isEmpty && deviceManager.hasSpecifiedDeviceId) {
       globals.printStatus(userMessages.flutterNoMatchingDevice(deviceManager.specifiedDeviceId!));
+      final List<Device> allDevices = await deviceManager.getAllConnectedDevices();
+      if (allDevices.isNotEmpty) {
+        globals.printStatus('');
+        globals.printStatus('The following devices were found:');
+        await Device.printDevices(allDevices, globals.logger);
+      }
       return null;
     } else if (devices.isEmpty) {
       if (deviceManager.hasSpecifiedAllDevices) {
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
index e282859..40455d6 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
@@ -195,6 +195,33 @@
         Cache: () => Cache.test(processManager: FakeProcessManager.any()),
       });
 
+      testUsingContext('exits and lists available devices when specified device not found', () async {
+        final RunCommand command = RunCommand();
+        final FakeDevice device = FakeDevice(isLocalEmulator: true);
+        mockDeviceManager
+          ..devices = <Device>[device]
+          ..hasSpecifiedDeviceId = true;
+
+        await expectLater(
+              () => createTestCommandRunner(command).run(<String>[
+            'run',
+            '-d',
+            'invalid-device-id',
+            '--no-pub',
+            '--no-hot',
+          ]),
+          throwsToolExit(),
+        );
+        expect(testLogger.statusText, contains("No supported devices found with name or id matching 'invalid-device-id'"));
+        expect(testLogger.statusText, contains('The following devices were found:'));
+        expect(testLogger.statusText, contains('FakeDevice (mobile) • fake_device • ios •  (simulator)'));
+      }, overrides: <Type, Generator>{
+        DeviceManager: () => mockDeviceManager,
+        FileSystem: () => fs,
+        ProcessManager: () => FakeProcessManager.any(),
+        Cache: () => Cache.test(processManager: FakeProcessManager.any()),
+      });
+
       testUsingContext('fails when targeted device is not Android with --device-user', () async {
         fs.file('pubspec.yaml').createSync();
         fs.file('.packages').writeAsStringSync('\n');
@@ -642,6 +669,9 @@
   Future<List<Device>> findTargetDevices(FlutterProject flutterProject, {Duration timeout}) async {
     return targetDevices;
   }
+
+  @override
+  Future<List<Device>> getAllConnectedDevices() async => devices;
 }
 
 class FakeAndroidSdk extends Fake implements AndroidSdk {
diff --git a/packages/flutter_tools/test/general.shard/device_test.dart b/packages/flutter_tools/test/general.shard/device_test.dart
index 2bd6117..6349645 100644
--- a/packages/flutter_tools/test/general.shard/device_test.dart
+++ b/packages/flutter_tools/test/general.shard/device_test.dart
@@ -179,6 +179,7 @@
     final FakeDevice nonEphemeralOne = FakeDevice('nonEphemeralOne', 'nonEphemeralOne', ephemeral: false);
     final FakeDevice nonEphemeralTwo = FakeDevice('nonEphemeralTwo', 'nonEphemeralTwo', ephemeral: false);
     final FakeDevice unsupported = FakeDevice('unsupported', 'unsupported', isSupported: false);
+    final FakeDevice unsupportedForProject = FakeDevice('unsupportedForProject', 'unsupportedForProject', isSupportedForProject: false);
     final FakeDevice webDevice = FakeDevice('webby', 'webby')
       ..targetPlatform = Future<TargetPlatform>.value(TargetPlatform.web_javascript);
     final FakeDevice fuchsiaDevice = FakeDevice('fuchsiay', 'fuchsiay')
@@ -190,6 +191,7 @@
         nonEphemeralOne,
         nonEphemeralTwo,
         unsupported,
+        unsupportedForProject,
       ];
 
       final DeviceManager deviceManager = TestDeviceManager(
@@ -327,9 +329,29 @@
       );
     });
 
-    testWithoutContext('Removes a single unsupported device', () async {
+    testWithoutContext('Unsupported devices listed in all connected devices', () async {
       final List<Device> devices = <Device>[
         unsupported,
+        unsupportedForProject,
+      ];
+
+      final DeviceManager deviceManager = TestDeviceManager(
+        devices,
+        logger: BufferLogger.test(),
+        terminal: Terminal.test(),
+      );
+      final List<Device> filtered = await deviceManager.getAllConnectedDevices();
+
+      expect(filtered, <Device>[
+        unsupported,
+        unsupportedForProject,
+      ]);
+    });
+
+    testWithoutContext('Removes a unsupported devices', () async {
+      final List<Device> devices = <Device>[
+        unsupported,
+        unsupportedForProject,
       ];
 
       final DeviceManager deviceManager = TestDeviceManager(
@@ -342,9 +364,10 @@
       expect(filtered, <Device>[]);
     });
 
-    testWithoutContext('Does not remove an unsupported device if FlutterProject is null', () async {
+    testWithoutContext('Retains devices unsupported by the project if FlutterProject is null', () async {
       final List<Device> devices = <Device>[
         unsupported,
+        unsupportedForProject,
       ];
 
       final DeviceManager deviceManager = TestDeviceManager(
@@ -354,7 +377,7 @@
       );
       final List<Device> filtered = await deviceManager.findTargetDevices(null);
 
-      expect(filtered, <Device>[unsupported]);
+      expect(filtered, <Device>[unsupportedForProject]);
     });
 
     testWithoutContext('Removes web and fuchsia from --all', () async {
@@ -374,11 +397,12 @@
       expect(filtered, <Device>[]);
     });
 
-    testWithoutContext('Removes unsupported devices from --all', () async {
+    testWithoutContext('Removes devices unsupported by the project from --all', () async {
       final List<Device> devices = <Device>[
         nonEphemeralOne,
         nonEphemeralTwo,
         unsupported,
+        unsupportedForProject,
       ];
       final DeviceManager deviceManager = TestDeviceManager(
         devices,
@@ -398,18 +422,19 @@
     testWithoutContext('uses DeviceManager.isDeviceSupportedForProject instead of device.isSupportedForProject', () async {
       final List<Device> devices = <Device>[
         unsupported,
+        unsupportedForProject,
       ];
       final TestDeviceManager deviceManager = TestDeviceManager(
         devices,
         logger: BufferLogger.test(),
         terminal: Terminal.test(),
       );
-      deviceManager.isAlwaysSupportedOverride = true;
+      deviceManager.isAlwaysSupportedForProjectOverride = true;
 
       final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
 
       expect(filtered, <Device>[
-        unsupported,
+        unsupportedForProject,
       ]);
     });
 
@@ -513,12 +538,12 @@
     _fakeDeviceDiscoverer.setDevices(allDevices);
   }
 
-  bool? isAlwaysSupportedOverride;
+  bool? isAlwaysSupportedForProjectOverride;
 
   @override
   bool isDeviceSupportedForProject(Device device, FlutterProject? flutterProject) {
-    if (isAlwaysSupportedOverride != null) {
-      return isAlwaysSupportedOverride!;
+    if (isAlwaysSupportedForProjectOverride != null) {
+      return isAlwaysSupportedForProjectOverride!;
     }
     return super.isDeviceSupportedForProject(device, flutterProject);
   }
diff --git a/packages/flutter_tools/test/integration.shard/flutter_run_test.dart b/packages/flutter_tools/test/integration.shard/flutter_run_test.dart
index 3beeef3..9d1792f 100644
--- a/packages/flutter_tools/test/integration.shard/flutter_run_test.dart
+++ b/packages/flutter_tools/test/integration.shard/flutter_run_test.dart
@@ -45,7 +45,7 @@
     expect(proc.stdout, isNot(contains('flutter has exited unexpectedly')));
     expect(proc.stderr, isNot(contains('flutter has exited unexpectedly')));
     if (!proc.stderr.toString().contains('Unable to locate a development')
-        && !proc.stdout.toString().contains('No devices found with name or id matching')) {
+        && !proc.stdout.toString().contains('No supported devices found with name or id matching')) {
       fail("'flutter run -d invalid-device-id' did not produce the expected error");
     }
   });
diff --git a/packages/flutter_tools/test/src/fake_devices.dart b/packages/flutter_tools/test/src/fake_devices.dart
index 7202808..ffa74fa 100644
--- a/packages/flutter_tools/test/src/fake_devices.dart
+++ b/packages/flutter_tools/test/src/fake_devices.dart
@@ -61,9 +61,11 @@
   FakeDevice(this.name, String id, {
     bool ephemeral = true,
     bool isSupported = true,
+    bool isSupportedForProject = true,
     PlatformType type = PlatformType.web,
     LaunchResult? launchResult,
   }) : _isSupported = isSupported,
+      _isSupportedForProject = isSupportedForProject,
       _launchResult = launchResult ?? LaunchResult.succeeded(),
       super(
         id,
@@ -73,6 +75,7 @@
       );
 
   final bool _isSupported;
+  final bool _isSupportedForProject;
   final LaunchResult _launchResult;
 
   @override
@@ -110,7 +113,7 @@
   void noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
 
   @override
-  bool isSupportedForProject(FlutterProject flutterProject) => _isSupported;
+  bool isSupportedForProject(FlutterProject flutterProject) => _isSupportedForProject;
 
   @override
   bool isSupported() => _isSupported;