Unverified Commit 5c52498d authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Add device category, ephemeral, platformType for daemon (#33990)

parent 73330e12
...@@ -45,7 +45,7 @@ Any params for that command should be passed in through a `params` field. Here's ...@@ -45,7 +45,7 @@ Any params for that command should be passed in through a `params` field. Here's
Events that come from the server will have an `event` field containing the type of event, along with a `params` field. Events that come from the server will have an `event` field containing the type of event, along with a `params` field.
``` ```
[{"event":"device.added","params":{"id":"1DD6786B-37D4-4355-AA15-B818A87A18B4","name":"iPhone XS Max","platform":"ios","emulator":true}}] [{"event":"device.added","params":{"id":"1DD6786B-37D4-4355-AA15-B818A87A18B4","name":"iPhone XS Max","platform":"ios","emulator":true,"ephemeral":false,"platformType":"ios","category":"mobile"}}]
``` ```
## Domains and Commands ## Domains and Commands
...@@ -157,7 +157,15 @@ This is sent when an app is stopped or detached from. The `params` field will be ...@@ -157,7 +157,15 @@ This is sent when an app is stopped or detached from. The `params` field will be
#### device.getDevices #### device.getDevices
Return a list of all connected devices. The `params` field will be a List; each item is a map with the fields `id`, `name`, `platform`, and `emulator` (a boolean). Return a list of all connected devices. The `params` field will be a List; each item is a map with the fields `id`, `name`, `platform`, `category`, `platformType`, `ephemeral`, and `emulator` (a boolean).
`category` is string description of the kind of workflow the device supports. The current categories are "mobile", "web" and "desktop", or null if none.
`platformType` is a string description of the platform sub-folder the device
supports. The current catgetories are "android", "ios", "linux", "macos",
"fuchsia", "windows", and "web". These are kept in sync with the response from `daemon.getSupportedPlatforms`.
`ephemeral` is a boolean which indicates where the device needs to be manually connected to a development machine. For example, a physical Android device is ephemeral, but the "web" device (that is always present) is not.
#### device.enable #### device.enable
...@@ -181,11 +189,11 @@ Removed a forwarded port. It takes `deviceId`, `devicePort`, and `hostPort` as r ...@@ -181,11 +189,11 @@ Removed a forwarded port. It takes `deviceId`, `devicePort`, and `hostPort` as r
#### device.added #### device.added
This is sent when a device is connected (and polling has been enabled via `enable()`). The `params` field will be a map with the fields `id`, `name`, `platform`, and `emulator`. This is sent when a device is connected (and polling has been enabled via `enable()`). The `params` field will be a map with the fields `id`, `name`, `platform`, `category`, `platformType`, `ephemeral`, and `emulator`. For more information on `platform`, `category`, `platformType`, and `ephemeral` see `device.getDevices`.
#### device.removed #### device.removed
This is sent when a device is disconnected (and polling has been enabled via `enable()`). The `params` field will be a map with the fields `id`, `name`, `platform`, and `emulator`. This is sent when a device is disconnected (and polling has been enabled via `enable()`). The `params` field will be a map with the fields `id`, `name`, `platform`, `category`, `platformType`, `ephemeral`, and `emulator`. For more information on `platform`, `category`, `platformType`, and `ephemeral` see `device.getDevices`.
### emulator domain ### emulator domain
...@@ -250,6 +258,7 @@ See the [source](https://github.com/flutter/flutter/blob/master/packages/flutter ...@@ -250,6 +258,7 @@ See the [source](https://github.com/flutter/flutter/blob/master/packages/flutter
## Changelog ## Changelog
- 0.5.1: Added `platformType`, `ephemeral`, and `category` field to device.
- 0.5.0: Added `daemon.getSupportedPlatforms` command - 0.5.0: Added `daemon.getSupportedPlatforms` command
- 0.4.2: Added `app.detach` command - 0.4.2: Added `app.detach` command
- 0.4.1: Added `flutter attach --machine` - 0.4.1: Added `flutter attach --machine`
......
...@@ -72,7 +72,12 @@ class AndroidDevice extends Device { ...@@ -72,7 +72,12 @@ class AndroidDevice extends Device {
this.productID, this.productID,
this.modelID, this.modelID,
this.deviceCodeName, this.deviceCodeName,
}) : super(id); }) : super(
id,
category: Category.mobile,
platformType: PlatformType.android,
ephemeral: true,
);
final String productID; final String productID;
final String modelID; final String modelID;
......
...@@ -26,7 +26,7 @@ import '../run_hot.dart'; ...@@ -26,7 +26,7 @@ import '../run_hot.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
import '../vmservice.dart'; import '../vmservice.dart';
const String protocolVersion = '0.5.0'; const String protocolVersion = '0.5.1';
/// A server process command. This command will start up a long-lived server. /// A server process command. This command will start up a long-lived server.
/// It reads JSON-RPC based commands from stdin, executes them, and returns /// It reads JSON-RPC based commands from stdin, executes them, and returns
...@@ -659,7 +659,12 @@ class DeviceDomain extends Domain { ...@@ -659,7 +659,12 @@ class DeviceDomain extends Domain {
_DeviceEventHandler _onDeviceEvent(String eventName) { _DeviceEventHandler _onDeviceEvent(String eventName) {
return (Device device) { return (Device device) {
_serializeDeviceEvents = _serializeDeviceEvents.then<void>((_) async { _serializeDeviceEvents = _serializeDeviceEvents.then<void>((_) async {
sendEvent(eventName, await _deviceToMap(device)); try {
final Map<String, Object> response = await _deviceToMap(device);
sendEvent(eventName, response);
} catch (err) {
printError(err);
}
}); });
}; };
} }
...@@ -768,6 +773,9 @@ Future<Map<String, dynamic>> _deviceToMap(Device device) async { ...@@ -768,6 +773,9 @@ Future<Map<String, dynamic>> _deviceToMap(Device device) async {
'name': device.name, 'name': device.name,
'platform': getNameForTargetPlatform(await device.targetPlatform), 'platform': getNameForTargetPlatform(await device.targetPlatform),
'emulator': await device.isLocalEmulator, 'emulator': await device.isLocalEmulator,
'category': device.category?.toString(),
'platformType': device.platformType?.toString(),
'ephemeral': device.ephemeral,
}; };
} }
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
import 'dart:async'; import 'dart:async';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:meta/meta.dart';
import 'android/android_device.dart'; import 'android/android_device.dart';
import 'application_package.dart'; import 'application_package.dart';
import 'artifacts.dart'; import 'artifacts.dart';
...@@ -28,6 +30,38 @@ import 'windows/windows_device.dart'; ...@@ -28,6 +30,38 @@ import 'windows/windows_device.dart';
DeviceManager get deviceManager => context.get<DeviceManager>(); DeviceManager get deviceManager => context.get<DeviceManager>();
/// A description of the kind of workflow the device supports.
class Category {
const Category._(this.value);
static const Category web = Category._('web');
static const Category desktop = Category._('desktop');
static const Category mobile = Category._('mobile');
final String value;
@override
String toString() => value;
}
/// The platform sub-folder that a device type supports.
class PlatformType {
const PlatformType._(this.value);
static const PlatformType web = PlatformType._('web');
static const PlatformType android = PlatformType._('android');
static const PlatformType ios = PlatformType._('ios');
static const PlatformType linux = PlatformType._('linux');
static const PlatformType macos = PlatformType._('macos');
static const PlatformType windows = PlatformType._('windows');
static const PlatformType fuchsia = PlatformType._('fuchsia');
final String value;
@override
String toString() => value;
}
/// A class to get all available devices. /// A class to get all available devices.
class DeviceManager { class DeviceManager {
...@@ -208,10 +242,19 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery { ...@@ -208,10 +242,19 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery {
abstract class Device { abstract class Device {
Device(this.id); Device(this.id, {@required this.category, @required this.platformType, @required this.ephemeral});
final String id; final String id;
/// The [Category] for this device type.
final Category category;
/// The [PlatformType] for this device.
final PlatformType platformType;
/// Whether this is an ephemeral device.
final bool ephemeral;
String get name; String get name;
bool get supportsStartPaused => true; bool get supportsStartPaused => true;
......
...@@ -175,7 +175,12 @@ List<FuchsiaDevice> parseListDevices(String text) { ...@@ -175,7 +175,12 @@ List<FuchsiaDevice> parseListDevices(String text) {
} }
class FuchsiaDevice extends Device { class FuchsiaDevice extends Device {
FuchsiaDevice(String id, {this.name}) : super(id); FuchsiaDevice(String id, {this.name}) : super(
id,
platformType: PlatformType.fuchsia,
category: null,
ephemeral: false,
);
@override @override
bool get supportsHotReload => true; bool get supportsHotReload => true;
......
...@@ -114,7 +114,12 @@ class IOSDevices extends PollingDeviceDiscovery { ...@@ -114,7 +114,12 @@ class IOSDevices extends PollingDeviceDiscovery {
class IOSDevice extends Device { class IOSDevice extends Device {
IOSDevice(String id, { this.name, String sdkVersion }) IOSDevice(String id, { this.name, String sdkVersion })
: _sdkVersion = sdkVersion, : _sdkVersion = sdkVersion,
super(id) { super(
id,
category: Category.mobile,
platformType: PlatformType.ios,
ephemeral: true,
) {
_installerPath = _checkForCommand('ideviceinstaller'); _installerPath = _checkForCommand('ideviceinstaller');
_iproxyPath = _checkForCommand('iproxy'); _iproxyPath = _checkForCommand('iproxy');
} }
......
...@@ -50,7 +50,7 @@ class IOSSimulatorUtils { ...@@ -50,7 +50,7 @@ class IOSSimulatorUtils {
return <IOSSimulator>[]; return <IOSSimulator>[];
return SimControl.instance.getConnectedDevices().map<IOSSimulator>((SimDevice device) { return SimControl.instance.getConnectedDevices().map<IOSSimulator>((SimDevice device) {
return IOSSimulator(device.udid, name: device.name, category: device.category); return IOSSimulator(device.udid, name: device.name, simulatorCategory: device.category);
}).toList(); }).toList();
} }
} }
...@@ -215,12 +215,17 @@ class SimDevice { ...@@ -215,12 +215,17 @@ class SimDevice {
} }
class IOSSimulator extends Device { class IOSSimulator extends Device {
IOSSimulator(String id, { this.name, this.category }) : super(id); IOSSimulator(String id, { this.name, this.simulatorCategory }) : super(
id,
category: Category.mobile,
platformType: PlatformType.ios,
ephemeral: true,
);
@override @override
final String name; final String name;
final String category; final String simulatorCategory;
@override @override
Future<bool> get isLocalEmulator async => true; Future<bool> get isLocalEmulator async => true;
...@@ -435,7 +440,7 @@ class IOSSimulator extends Device { ...@@ -435,7 +440,7 @@ class IOSSimulator extends Device {
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios; Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
@override @override
Future<String> get sdkNameAndVersion async => category; Future<String> get sdkNameAndVersion async => simulatorCategory;
final RegExp _iosSdkRegExp = RegExp(r'iOS( |-)(\d+)'); final RegExp _iosSdkRegExp = RegExp(r'iOS( |-)(\d+)');
......
...@@ -19,7 +19,12 @@ import 'linux_workflow.dart'; ...@@ -19,7 +19,12 @@ import 'linux_workflow.dart';
/// A device that represents a desktop Linux target. /// A device that represents a desktop Linux target.
class LinuxDevice extends Device { class LinuxDevice extends Device {
LinuxDevice() : super('Linux'); LinuxDevice() : super(
'Linux',
category: Category.desktop,
platformType: PlatformType.linux,
ephemeral: false,
);
@override @override
void clearLogs() { } void clearLogs() { }
......
...@@ -20,7 +20,12 @@ import 'macos_workflow.dart'; ...@@ -20,7 +20,12 @@ import 'macos_workflow.dart';
/// A device that represents a desktop MacOS target. /// A device that represents a desktop MacOS target.
class MacOSDevice extends Device { class MacOSDevice extends Device {
MacOSDevice() : super('macOS'); MacOSDevice() : super(
'macOS',
category: Category.desktop,
platformType: PlatformType.macos,
ephemeral: false,
);
@override @override
void clearLogs() { } void clearLogs() { }
......
...@@ -42,7 +42,12 @@ class FlutterTesterApp extends ApplicationPackage { ...@@ -42,7 +42,12 @@ class FlutterTesterApp extends ApplicationPackage {
// TODO(scheglov): This device does not currently work with full restarts. // TODO(scheglov): This device does not currently work with full restarts.
class FlutterTesterDevice extends Device { class FlutterTesterDevice extends Device {
FlutterTesterDevice(String deviceId) : super(deviceId); FlutterTesterDevice(String deviceId) : super(
deviceId,
platformType: null,
category: null,
ephemeral: false,
);
Process _process; Process _process;
final DevicePortForwarder _portForwarder = _NoopPortForwarder(); final DevicePortForwarder _portForwarder = _NoopPortForwarder();
......
...@@ -28,7 +28,12 @@ class WebApplicationPackage extends ApplicationPackage { ...@@ -28,7 +28,12 @@ class WebApplicationPackage extends ApplicationPackage {
} }
class WebDevice extends Device { class WebDevice extends Device {
WebDevice() : super('web'); WebDevice() : super(
'web',
category: Category.web,
platformType: PlatformType.web,
ephemeral: false,
);
@override @override
bool get supportsHotReload => true; bool get supportsHotReload => true;
......
...@@ -21,7 +21,12 @@ import 'windows_workflow.dart'; ...@@ -21,7 +21,12 @@ import 'windows_workflow.dart';
/// A device that represents a desktop Windows target. /// A device that represents a desktop Windows target.
class WindowsDevice extends Device { class WindowsDevice extends Device {
WindowsDevice() : super('Windows'); WindowsDevice() : super(
'Windows',
category: Category.desktop,
platformType: PlatformType.windows,
ephemeral: false,
);
@override @override
void clearLogs() { } void clearLogs() { }
......
...@@ -58,6 +58,7 @@ List of devices attached ...@@ -58,6 +58,7 @@ List of devices attached
''', devices: devices); ''', devices: devices);
expect(devices, hasLength(1)); expect(devices, hasLength(1));
expect(devices.first.name, 'Nexus 7'); expect(devices.first.name, 'Nexus 7');
expect(devices.first.category, Category.mobile);
}); });
testUsingContext('emulators and short listings', () { testUsingContext('emulators and short listings', () {
......
...@@ -50,7 +50,12 @@ class TestDeviceManager extends DeviceManager { ...@@ -50,7 +50,12 @@ class TestDeviceManager extends DeviceManager {
} }
class _MockDevice extends Device { class _MockDevice extends Device {
_MockDevice(this.name, String id) : super(id); _MockDevice(this.name, String id) : super(
id,
platformType: PlatformType.web,
category: Category.mobile,
ephemeral: true,
);
@override @override
final String name; final String name;
......
...@@ -182,6 +182,7 @@ f577a7903cc54959be2e34bc4f7f80b7009efcf4 ...@@ -182,6 +182,7 @@ f577a7903cc54959be2e34bc4f7f80b7009efcf4
'This is a multi-line message,', 'This is a multi-line message,',
' with a non-Flutter log message following it.', ' with a non-Flutter log message following it.',
]); ]);
expect(device.category, Category.mobile);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
IMobileDevice: () => mockIMobileDevice, IMobileDevice: () => mockIMobileDevice,
}); });
......
...@@ -109,16 +109,22 @@ void main() { ...@@ -109,16 +109,22 @@ void main() {
group('sdkMajorVersion', () { group('sdkMajorVersion', () {
// This new version string appears in SimulatorApp-850 CoreSimulator-518.16 beta. // This new version string appears in SimulatorApp-850 CoreSimulator-518.16 beta.
test('can be parsed from iOS-11-3', () async { test('can be parsed from iOS-11-3', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', category: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3'); final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3');
expect(await device.sdkMajorVersion, 11); expect(await device.sdkMajorVersion, 11);
}); });
test('can be parsed from iOS 11.2', () async { test('can be parsed from iOS 11.2', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', category: 'iOS 11.2'); final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 11.2');
expect(await device.sdkMajorVersion, 11); expect(await device.sdkMajorVersion, 11);
}); });
test('Has a simulator category', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 11.2');
expect(device.category, Category.mobile);
});
}); });
group('IOSSimulator.isSupported', () { group('IOSSimulator.isSupported', () {
...@@ -246,7 +252,7 @@ void main() { ...@@ -246,7 +252,7 @@ void main() {
}); });
testUsingContext('uses tail on iOS versions prior to iOS 11', () async { testUsingContext('uses tail on iOS versions prior to iOS 11', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', category: 'iOS 9.3'); final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 9.3');
await launchDeviceLogTool(device); await launchDeviceLogTool(device);
expect( expect(
verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single, verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single,
...@@ -258,7 +264,7 @@ void main() { ...@@ -258,7 +264,7 @@ void main() {
}); });
testUsingContext('uses /usr/bin/log on iOS 11 and above', () async { testUsingContext('uses /usr/bin/log on iOS 11 and above', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', category: 'iOS 11.0'); final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 11.0');
await launchDeviceLogTool(device); await launchDeviceLogTool(device);
expect( expect(
verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single, verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single,
...@@ -280,7 +286,7 @@ void main() { ...@@ -280,7 +286,7 @@ void main() {
}); });
testUsingContext('uses tail on iOS versions prior to iOS 11', () async { testUsingContext('uses tail on iOS versions prior to iOS 11', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', category: 'iOS 9.3'); final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 9.3');
await launchSystemLogTool(device); await launchSystemLogTool(device);
expect( expect(
verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single, verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single,
...@@ -292,7 +298,7 @@ void main() { ...@@ -292,7 +298,7 @@ void main() {
}); });
testUsingContext('uses /usr/bin/log on iOS 11 and above', () async { testUsingContext('uses /usr/bin/log on iOS 11 and above', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', category: 'iOS 11.0'); final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 11.0');
await launchSystemLogTool(device); await launchSystemLogTool(device);
verifyNever(mockProcessManager.start(any, environment: null, workingDirectory: null)); verifyNever(mockProcessManager.start(any, environment: null, workingDirectory: null));
}, },
...@@ -330,7 +336,7 @@ void main() { ...@@ -330,7 +336,7 @@ void main() {
return Future<Process>.value(mockProcess); return Future<Process>.value(mockProcess);
}); });
final IOSSimulator device = IOSSimulator('123456', category: 'iOS 11.0'); final IOSSimulator device = IOSSimulator('123456', simulatorCategory: 'iOS 11.0');
final DeviceLogReader logReader = device.getLogReader( final DeviceLogReader logReader = device.getLogReader(
app: BuildableIOSApp(mockIosProject), app: BuildableIOSApp(mockIosProject),
); );
...@@ -430,7 +436,7 @@ void main() { ...@@ -430,7 +436,7 @@ void main() {
}); });
testUsingContext("startApp uses compiled app's Info.plist to find CFBundleIdentifier", () async { testUsingContext("startApp uses compiled app's Info.plist to find CFBundleIdentifier", () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', category: 'iOS 11.2'); final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 11.2');
when(iosWorkflow.getPlistValueFromFile(any, any)).thenReturn('correct'); when(iosWorkflow.getPlistValueFromFile(any, any)).thenReturn('correct');
final Directory mockDir = fs.currentDirectory; final Directory mockDir = fs.currentDirectory;
......
...@@ -43,6 +43,7 @@ void main() { ...@@ -43,6 +43,7 @@ void main() {
expect(await device.isLatestBuildInstalled(linuxApp), true); expect(await device.isLatestBuildInstalled(linuxApp), true);
expect(await device.isAppInstalled(linuxApp), true); expect(await device.isAppInstalled(linuxApp), true);
expect(await device.stopApp(linuxApp), true); expect(await device.stopApp(linuxApp), true);
expect(device.category, Category.desktop);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
......
...@@ -44,6 +44,7 @@ void main() { ...@@ -44,6 +44,7 @@ void main() {
expect(await device.isLatestBuildInstalled(mockMacOSApp), true); expect(await device.isLatestBuildInstalled(mockMacOSApp), true);
expect(await device.isAppInstalled(mockMacOSApp), true); expect(await device.isAppInstalled(mockMacOSApp), true);
expect(await device.stopApp(mockMacOSApp), false); expect(await device.stopApp(mockMacOSApp), false);
expect(device.category, Category.desktop);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
......
...@@ -43,6 +43,7 @@ void main() { ...@@ -43,6 +43,7 @@ void main() {
expect(await device.isLatestBuildInstalled(windowsApp), true); expect(await device.isLatestBuildInstalled(windowsApp), true);
expect(await device.isAppInstalled(windowsApp), true); expect(await device.isAppInstalled(windowsApp), true);
expect(await device.stopApp(windowsApp), false); expect(await device.stopApp(windowsApp), false);
expect(device.category, Category.desktop);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
......
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