mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
136 lines
4.3 KiB
Dart
136 lines
4.3 KiB
Dart
// Copyright 2018 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:meta/meta.dart';
|
|
|
|
import '../android/android_sdk.dart';
|
|
import '../android/android_workflow.dart';
|
|
import '../base/file_system.dart';
|
|
import '../base/io.dart';
|
|
import '../base/process_manager.dart';
|
|
import '../device.dart';
|
|
import '../emulator.dart';
|
|
import 'android_sdk.dart';
|
|
|
|
class AndroidEmulators extends EmulatorDiscovery {
|
|
@override
|
|
bool get supportsPlatform => true;
|
|
|
|
@override
|
|
bool get canListAnything => androidWorkflow.canListEmulators;
|
|
|
|
@override
|
|
Future<List<Emulator>> get emulators async => getEmulatorAvds();
|
|
}
|
|
|
|
class AndroidEmulator extends Emulator {
|
|
AndroidEmulator(String id, [this._properties])
|
|
: super(id, _properties != null && _properties.isNotEmpty);
|
|
|
|
final Map<String, String> _properties;
|
|
|
|
// Android Studio uses the ID with underscores replaced with spaces
|
|
// for the name if displayname is not set so we do the same.
|
|
@override
|
|
String get name => _prop('avd.ini.displayname') ?? id.replaceAll('_', ' ').trim();
|
|
|
|
@override
|
|
String get manufacturer => _prop('hw.device.manufacturer');
|
|
|
|
@override
|
|
Category get category => Category.mobile;
|
|
|
|
@override
|
|
PlatformType get platformType => PlatformType.android;
|
|
|
|
String _prop(String name) => _properties != null ? _properties[name] : null;
|
|
|
|
@override
|
|
Future<void> launch() async {
|
|
final Future<void> launchResult =
|
|
processManager.run(<String>[getEmulatorPath(), '-avd', id])
|
|
.then((ProcessResult runResult) {
|
|
if (runResult.exitCode != 0) {
|
|
throw '${runResult.stdout}\n${runResult.stderr}'.trimRight();
|
|
}
|
|
});
|
|
// The emulator continues running on a successful launch, so if it hasn't
|
|
// quit within 3 seconds we assume that's a success and just return. This
|
|
// means that on a slow machine, a failure that takes more than three
|
|
// seconds won't be recognized as such... :-/
|
|
return Future.any<void>(<Future<void>>[
|
|
launchResult,
|
|
Future<void>.delayed(const Duration(seconds: 3)),
|
|
]);
|
|
}
|
|
}
|
|
|
|
/// Return the list of available emulator AVDs.
|
|
List<AndroidEmulator> getEmulatorAvds() {
|
|
final String emulatorPath = getEmulatorPath(androidSdk);
|
|
if (emulatorPath == null) {
|
|
return <AndroidEmulator>[];
|
|
}
|
|
|
|
final String listAvdsOutput = processManager.runSync(<String>[emulatorPath, '-list-avds']).stdout;
|
|
|
|
final List<AndroidEmulator> emulators = <AndroidEmulator>[];
|
|
if (listAvdsOutput != null) {
|
|
extractEmulatorAvdInfo(listAvdsOutput, emulators);
|
|
}
|
|
return emulators;
|
|
}
|
|
|
|
/// Parse the given `emulator -list-avds` output in [text], and fill out the given list
|
|
/// of emulators by reading information from the relevant ini files.
|
|
void extractEmulatorAvdInfo(String text, List<AndroidEmulator> emulators) {
|
|
for (String id in text.trim().split('\n').where((String l) => l != '')) {
|
|
emulators.add(_loadEmulatorInfo(id));
|
|
}
|
|
}
|
|
|
|
AndroidEmulator _loadEmulatorInfo(String id) {
|
|
id = id.trim();
|
|
final String avdPath = getAvdPath();
|
|
if (avdPath != null) {
|
|
final File iniFile = fs.file(fs.path.join(avdPath, '$id.ini'));
|
|
if (iniFile.existsSync()) {
|
|
final Map<String, String> ini = parseIniLines(iniFile.readAsLinesSync());
|
|
if (ini['path'] != null) {
|
|
final File configFile =
|
|
fs.file(fs.path.join(ini['path'], 'config.ini'));
|
|
if (configFile.existsSync()) {
|
|
final Map<String, String> properties =
|
|
parseIniLines(configFile.readAsLinesSync());
|
|
return AndroidEmulator(id, properties);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return AndroidEmulator(id);
|
|
}
|
|
|
|
@visibleForTesting
|
|
Map<String, String> parseIniLines(List<String> contents) {
|
|
final Map<String, String> results = <String, String>{};
|
|
|
|
final Iterable<List<String>> properties = contents
|
|
.map<String>((String l) => l.trim())
|
|
// Strip blank lines/comments
|
|
.where((String l) => l != '' && !l.startsWith('#'))
|
|
// Discard anything that isn't simple name=value
|
|
.where((String l) => l.contains('='))
|
|
// Split into name/value
|
|
.map<List<String>>((String l) => l.split('='));
|
|
|
|
for (List<String> property in properties) {
|
|
results[property[0].trim()] = property[1].trim();
|
|
}
|
|
|
|
return results;
|
|
}
|