Unverified Commit 0c9a4205 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Migrate android_device to null safety (#92128)

parent 53e04de6
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'dart:async';
import 'package:meta/meta.dart';
......@@ -60,13 +58,13 @@ class AndroidDevice extends Device {
AndroidDevice(
String id, {
this.productID,
this.modelID,
required this.modelID,
this.deviceCodeName,
@required Logger logger,
@required ProcessManager processManager,
@required Platform platform,
@required AndroidSdk androidSdk,
@required FileSystem fileSystem,
required Logger logger,
required ProcessManager processManager,
required Platform platform,
required AndroidSdk androidSdk,
required FileSystem fileSystem,
AndroidConsoleSocketFactory androidConsoleSocketFactory = kAndroidConsoleSocketFactory,
}) : _logger = logger,
_processManager = processManager,
......@@ -90,60 +88,53 @@ class AndroidDevice extends Device {
final ProcessUtils _processUtils;
final AndroidConsoleSocketFactory _androidConsoleSocketFactory;
final String productID;
final String? productID;
final String modelID;
final String deviceCodeName;
Map<String, String> _properties;
bool _isLocalEmulator;
TargetPlatform _applicationPlatform;
final String? deviceCodeName;
Future<String> _getProperty(String name) async {
if (_properties == null) {
_properties = <String, String>{};
late final Future<Map<String, String>> _properties = () async {
Map<String, String> properties = <String, String>{};
final List<String> propCommand = adbCommandForDevice(<String>['shell', 'getprop']);
_logger.printTrace(propCommand.join(' '));
final List<String> propCommand = adbCommandForDevice(<String>['shell', 'getprop']);
_logger.printTrace(propCommand.join(' '));
try {
// We pass an encoding of latin1 so that we don't try and interpret the
// `adb shell getprop` result as UTF8.
final ProcessResult result = await _processManager.run(
propCommand,
stdoutEncoding: latin1,
stderrEncoding: latin1,
);
if (result.exitCode == 0 || _allowHeapCorruptionOnWindows(result.exitCode, _platform)) {
_properties = parseAdbDeviceProperties(result.stdout as String);
} else {
_logger.printError('Error ${result.exitCode} retrieving device properties for $name:');
_logger.printError(result.stderr as String);
}
} on ProcessException catch (error) {
_logger.printError('Error retrieving device properties for $name: $error');
try {
// We pass an encoding of latin1 so that we don't try and interpret the
// `adb shell getprop` result as UTF8.
final ProcessResult result = await _processManager.run(
propCommand,
stdoutEncoding: latin1,
stderrEncoding: latin1,
);
if (result.exitCode == 0 || _allowHeapCorruptionOnWindows(result.exitCode, _platform)) {
properties = parseAdbDeviceProperties(result.stdout as String);
} else {
_logger.printError('Error ${result.exitCode} retrieving device properties for $name:');
_logger.printError(result.stderr as String);
}
} on ProcessException catch (error) {
_logger.printError('Error retrieving device properties for $name: $error');
}
return properties;
}();
return _properties[name];
Future<String?> _getProperty(String name) async {
return (await _properties)[name];
}
@override
Future<bool> get isLocalEmulator async {
if (_isLocalEmulator == null) {
final String hardware = await _getProperty('ro.hardware');
_logger.printTrace('ro.hardware = $hardware');
if (kKnownHardware.containsKey(hardware)) {
// Look for known hardware models.
_isLocalEmulator = kKnownHardware[hardware] == HardwareType.emulator;
} else {
// Fall back to a best-effort heuristic-based approach.
final String characteristics = await _getProperty('ro.build.characteristics');
_logger.printTrace('ro.build.characteristics = $characteristics');
_isLocalEmulator = characteristics != null && characteristics.contains('emulator');
}
late final Future<bool> isLocalEmulator = () async {
final String? hardware = await _getProperty('ro.hardware');
_logger.printTrace('ro.hardware = $hardware');
if (kKnownHardware.containsKey(hardware)) {
// Look for known hardware models.
return kKnownHardware[hardware] == HardwareType.emulator;
}
return _isLocalEmulator;
}
// Fall back to a best-effort heuristic-based approach.
final String? characteristics = await _getProperty('ro.build.characteristics');
_logger.printTrace('ro.build.characteristics = $characteristics');
return characteristics != null && characteristics.contains('emulator');
}();
/// The unique identifier for the emulator that corresponds to this device, or
/// null if it is not an emulator.
......@@ -152,7 +143,7 @@ class AndroidDevice extends Device {
/// this name may require connecting to the device and if an error occurs null
/// will be returned.
@override
Future<String> get emulatorId async {
Future<String?> get emulatorId async {
if (!(await isLocalEmulator)) {
return null;
}
......@@ -161,13 +152,13 @@ class AndroidDevice extends Device {
// Android Console port number.
final RegExp emulatorPortRegex = RegExp(r'emulator-(\d+)');
final Match portMatch = emulatorPortRegex.firstMatch(id);
final Match? portMatch = emulatorPortRegex.firstMatch(id);
if (portMatch == null || portMatch.groupCount < 1) {
return null;
}
const String host = 'localhost';
final int port = int.parse(portMatch.group(1));
final int port = int.parse(portMatch.group(1)!);
_logger.printTrace('Fetching avd name for $name via Android console on $host:$port');
try {
......@@ -196,36 +187,28 @@ class AndroidDevice extends Device {
}
@override
Future<TargetPlatform> get targetPlatform async {
if (_applicationPlatform == null) {
// http://developer.android.com/ndk/guides/abis.html (x86, armeabi-v7a, ...)
switch (await _getProperty('ro.product.cpu.abi')) {
case 'arm64-v8a':
// Perform additional verification for 64 bit ABI. Some devices,
// like the Kindle Fire 8, misreport the abilist. We might not
// be able to retrieve this property, in which case we fall back
// to assuming 64 bit.
final String abilist = await _getProperty('ro.product.cpu.abilist');
if (abilist == null || abilist.contains('arm64-v8a')) {
_applicationPlatform = TargetPlatform.android_arm64;
} else {
_applicationPlatform = TargetPlatform.android_arm;
}
break;
case 'x86_64':
_applicationPlatform = TargetPlatform.android_x64;
break;
case 'x86':
_applicationPlatform = TargetPlatform.android_x86;
break;
default:
_applicationPlatform = TargetPlatform.android_arm;
break;
}
late final Future<TargetPlatform> targetPlatform = () async {
// http://developer.android.com/ndk/guides/abis.html (x86, armeabi-v7a, ...)
switch (await _getProperty('ro.product.cpu.abi')) {
case 'arm64-v8a':
// Perform additional verification for 64 bit ABI. Some devices,
// like the Kindle Fire 8, misreport the abilist. We might not
// be able to retrieve this property, in which case we fall back
// to assuming 64 bit.
final String? abilist = await _getProperty('ro.product.cpu.abilist');
if (abilist == null || abilist.contains('arm64-v8a')) {
return TargetPlatform.android_arm64;
} else {
return TargetPlatform.android_arm;
}
case 'x86_64':
return TargetPlatform.android_x64;
case 'x86':
return TargetPlatform.android_x86;
default:
return TargetPlatform.android_arm;
}
return _applicationPlatform;
}
}();
@override
Future<bool> supportsRuntimeMode(BuildMode buildMode) async {
......@@ -249,28 +232,27 @@ class AndroidDevice extends Device {
case TargetPlatform.windows_x64:
throw UnsupportedError('Invalid target platform for Android');
}
throw null; // dead code, remove after null migration
}
@override
Future<String> get sdkNameAndVersion async => 'Android ${await _sdkVersion} (API ${await apiVersion})';
Future<String> get _sdkVersion => _getProperty('ro.build.version.release');
Future<String?> get _sdkVersion => _getProperty('ro.build.version.release');
@visibleForTesting
Future<String> get apiVersion => _getProperty('ro.build.version.sdk');
Future<String?> get apiVersion => _getProperty('ro.build.version.sdk');
AdbLogReader _logReader;
AdbLogReader _pastLogReader;
AndroidDevicePortForwarder _portForwarder;
AdbLogReader? _logReader;
AdbLogReader? _pastLogReader;
AndroidDevicePortForwarder? _portForwarder;
List<String> adbCommandForDevice(List<String> args) {
return <String>[_androidSdk.adbPath, '-s', id, ...args];
return <String>[_androidSdk.adbPath!, '-s', id, ...args];
}
Future<RunResult> runAdbCheckedAsync(
List<String> params, {
String workingDirectory,
String? workingDirectory,
bool allowReentrantFlutter = false,
}) async {
return _processUtils.run(
......@@ -284,11 +266,11 @@ class AndroidDevice extends Device {
bool _isValidAdbVersion(String adbVersion) {
// Sample output: 'Android Debug Bridge version 1.0.31'
final Match versionFields = RegExp(r'(\d+)\.(\d+)\.(\d+)').firstMatch(adbVersion);
final Match? versionFields = RegExp(r'(\d+)\.(\d+)\.(\d+)').firstMatch(adbVersion);
if (versionFields != null) {
final int majorVersion = int.parse(versionFields[1]);
final int minorVersion = int.parse(versionFields[2]);
final int patchVersion = int.parse(versionFields[3]);
final int majorVersion = int.parse(versionFields[1]!);
final int minorVersion = int.parse(versionFields[2]!);
final int patchVersion = int.parse(versionFields[3]!);
if (majorVersion > 1) {
return true;
}
......@@ -306,19 +288,20 @@ class AndroidDevice extends Device {
}
Future<bool> _checkForSupportedAdbVersion() async {
if (_androidSdk == null) {
final String? adbPath = _androidSdk.adbPath;
if (adbPath == null) {
return false;
}
try {
final RunResult adbVersion = await _processUtils.run(
<String>[_androidSdk.adbPath, 'version'],
<String>[adbPath, 'version'],
throwOnError: true,
);
if (_isValidAdbVersion(adbVersion.stdout)) {
return true;
}
_logger.printError('The ADB at "${_androidSdk.adbPath}" is too old; please install version 1.0.39 or later.');
_logger.printError('The ADB at "$adbPath" is too old; please install version 1.0.39 or later.');
} on Exception catch (error, trace) {
_logger.printError('Error running ADB: $error', stackTrace: trace);
}
......@@ -327,13 +310,17 @@ class AndroidDevice extends Device {
}
Future<bool> _checkForSupportedAndroidVersion() async {
final String? adbPath = _androidSdk.adbPath;
if (adbPath == null) {
return false;
}
try {
// If the server is automatically restarted, then we get irrelevant
// output lines like this, which we want to ignore:
// adb server is out of date. killing..
// * daemon started successfully *
await _processUtils.run(
<String>[_androidSdk.adbPath, 'start-server'],
<String>[adbPath, 'start-server'],
throwOnError: true,
);
......@@ -343,7 +330,7 @@ class AndroidDevice extends Device {
final String sdkVersion = await _getProperty('ro.build.version.sdk')
?? minApiLevel.toString();
final int sdkVersionParsed = int.tryParse(sdkVersion);
final int? sdkVersionParsed = int.tryParse(sdkVersion);
if (sdkVersionParsed == null) {
_logger.printError('Unexpected response from getprop: "$sdkVersion"');
return false;
......@@ -385,7 +372,7 @@ class AndroidDevice extends Device {
@override
Future<bool> isAppInstalled(
AndroidApk app, {
String userIdentifier,
String? userIdentifier,
}) async {
// This call takes 400ms - 600ms.
try {
......@@ -414,9 +401,9 @@ class AndroidDevice extends Device {
@override
Future<bool> installApp(
AndroidApk app, {
String userIdentifier,
String? userIdentifier,
}) async {
if (!await _isAdbValid()) {
if (!await _adbIsValid) {
return false;
}
final bool wasInstalled = await isAppInstalled(app, userIdentifier: userIdentifier);
......@@ -446,7 +433,7 @@ class AndroidDevice extends Device {
Future<bool> _installApp(
AndroidApk app, {
String userIdentifier,
String? userIdentifier,
}) async {
if (!app.file.existsSync()) {
_logger.printError('"${_fileSystem.path.relative(app.file.path)}" does not exist.');
......@@ -469,7 +456,7 @@ class AndroidDevice extends Device {
// Some versions of adb exit with exit code 0 even on failure :(
// Parsing the output to check for failures.
final RegExp failureExp = RegExp(r'^Failure.*$', multiLine: true);
final String failure = failureExp.stringMatch(installResult.stdout);
final String? failure = failureExp.stringMatch(installResult.stdout);
if (failure != null) {
_logger.printError('Package install error: $failure');
return false;
......@@ -497,9 +484,9 @@ class AndroidDevice extends Device {
@override
Future<bool> uninstallApp(
AndroidApk app, {
String userIdentifier,
String? userIdentifier,
}) async {
if (!await _isAdbValid()) {
if (!await _adbIsValid) {
return false;
}
......@@ -519,7 +506,7 @@ class AndroidDevice extends Device {
return false;
}
final RegExp failureExp = RegExp(r'^Failure.*$', multiLine: true);
final String failure = failureExp.stringMatch(uninstallOut);
final String? failure = failureExp.stringMatch(uninstallOut);
if (failure != null) {
_logger.printError('Package uninstall error: $failure');
return false;
......@@ -528,25 +515,24 @@ class AndroidDevice extends Device {
}
// Whether the adb and Android versions are aligned.
bool _adbIsValid;
Future<bool> _isAdbValid() async {
return _adbIsValid ??= await _checkForSupportedAdbVersion() && await _checkForSupportedAndroidVersion();
}
late final Future<bool> _adbIsValid = () async {
return await _checkForSupportedAdbVersion() && await _checkForSupportedAndroidVersion();
}();
AndroidApk _package;
AndroidApk? _package;
@override
Future<LaunchResult> startApp(
AndroidApk package, {
String mainPath,
String route,
DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs = const <String, Object>{},
String? mainPath,
String? route,
required DebuggingOptions debuggingOptions,
Map<String, Object?> platformArgs = const <String, Object>{},
bool prebuiltApplication = false,
bool ipv6 = false,
String userIdentifier,
String? userIdentifier,
}) async {
if (!await _isAdbValid()) {
if (!await _adbIsValid) {
return LaunchResult.failed();
}
......@@ -589,19 +575,19 @@ class AndroidDevice extends Device {
if (!prebuiltApplication || _androidSdk.licensesAvailable && _androidSdk.latestVersion == null) {
_logger.printTrace('Building APK');
final FlutterProject project = FlutterProject.current();
await androidBuilder.buildApk(
await androidBuilder!.buildApk(
project: project,
target: mainPath,
target: mainPath ?? 'lib/main.dart',
androidBuildInfo: AndroidBuildInfo(
debuggingOptions.buildInfo,
targetArchs: <AndroidArch>[androidArch],
fastStart: debuggingOptions.fastStart,
multidexEnabled: (platformArgs['multidex'] as bool) ?? false,
multidexEnabled: (platformArgs['multidex'] as bool?) ?? false,
),
);
// Package has been built, so we can get the updated application ID and
// activity name from the .apk.
package = await ApplicationPackageFactory.instance
package = await ApplicationPackageFactory.instance!
.getPackageForPlatform(devicePlatform, buildInfo: debuggingOptions.buildInfo) as AndroidApk;
}
// There was a failure parsing the android project information.
......@@ -616,8 +602,8 @@ class AndroidDevice extends Device {
return LaunchResult.failed();
}
final bool traceStartup = platformArgs['trace-startup'] as bool ?? false;
ProtocolDiscovery observatoryDiscovery;
final bool traceStartup = platformArgs['trace-startup'] as bool? ?? false;
ProtocolDiscovery? observatoryDiscovery;
if (debuggingOptions.debuggingEnabled) {
observatoryDiscovery = ProtocolDiscovery.observatory(
......@@ -637,6 +623,8 @@ class AndroidDevice extends Device {
}
final String dartVmFlags = computeDartVmFlags(debuggingOptions);
final String? traceAllowlist = debuggingOptions.traceAllowlist;
final String? traceSkiaAllowlist = debuggingOptions.traceSkiaAllowlist;
final List<String> cmd = <String>[
'shell', 'am', 'start',
'-a', 'android.intent.action.RUN',
......@@ -653,10 +641,10 @@ class AndroidDevice extends Device {
...<String>['--ez', 'skia-deterministic-rendering', 'true'],
if (debuggingOptions.traceSkia)
...<String>['--ez', 'trace-skia', 'true'],
if (debuggingOptions.traceAllowlist != null)
...<String>['--es', 'trace-allowlist', debuggingOptions.traceAllowlist],
if (debuggingOptions.traceSkiaAllowlist != null)
...<String>['--es', 'trace-skia-allowlist', debuggingOptions.traceSkiaAllowlist],
if (traceAllowlist != null)
...<String>['--es', 'trace-allowlist', traceAllowlist],
if (traceSkiaAllowlist != null)
...<String>['--es', 'trace-skia-allowlist', traceSkiaAllowlist],
if (debuggingOptions.traceSystrace)
...<String>['--ez', 'trace-systrace', 'true'],
if (debuggingOptions.endlessTraceBuffer)
......@@ -703,9 +691,9 @@ class AndroidDevice extends Device {
// device has printed "Observatory is listening on...".
_logger.printTrace('Waiting for observatory port to be available...');
try {
Uri observatoryUri;
Uri? observatoryUri;
if (debuggingOptions.buildInfo.isDebug || debuggingOptions.buildInfo.isProfile) {
observatoryUri = await observatoryDiscovery.uri;
observatoryUri = await observatoryDiscovery?.uri;
if (observatoryUri == null) {
_logger.printError(
'Error waiting for a debug connection: '
......@@ -719,7 +707,7 @@ class AndroidDevice extends Device {
_logger.printError('Error waiting for a debug connection: $error');
return LaunchResult.failed();
} finally {
await observatoryDiscovery.cancel();
await observatoryDiscovery?.cancel();
}
}
......@@ -735,7 +723,7 @@ class AndroidDevice extends Device {
@override
Future<bool> stopApp(
AndroidApk app, {
String userIdentifier,
String? userIdentifier,
}) {
if (app == null) {
return Future<bool>.value(false);
......@@ -754,11 +742,16 @@ class AndroidDevice extends Device {
@override
Future<MemoryInfo> queryMemoryInfo() async {
final AndroidApk? package = _package;
if (package == null) {
_logger.printError('Android package unknown, skipping dumpsys meminfo.');
return const MemoryInfo.empty();
}
final RunResult runResult = await _processUtils.run(adbCommandForDevice(<String>[
'shell',
'dumpsys',
'meminfo',
_package.id,
package.id,
'-d',
]));
......@@ -775,7 +768,7 @@ class AndroidDevice extends Device {
@override
FutureOr<DeviceLogReader> getLogReader({
AndroidApk app,
AndroidApk? app,
bool includePastLogs = false,
}) async {
// The Android log reader isn't app-specific. The `app` parameter isn't used.
......@@ -794,19 +787,25 @@ class AndroidDevice extends Device {
}
@override
DevicePortForwarder get portForwarder => _portForwarder ??= AndroidDevicePortForwarder(
processManager: _processManager,
logger: _logger,
deviceId: id,
adbPath: _androidSdk.adbPath,
);
late final DevicePortForwarder? portForwarder = () {
final String? adbPath = _androidSdk.adbPath;
if (adbPath == null) {
return null;
}
return AndroidDevicePortForwarder(
processManager: _processManager,
logger: _logger,
deviceId: id,
adbPath: adbPath,
);
}();
static final RegExp _timeRegExp = RegExp(r'^\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}', multiLine: true);
/// Return the most recent timestamp in the Android log or [null] if there is
/// no available timestamp. The format can be passed to logcat's -T option.
@visibleForTesting
Future<String> lastLogcatTimestamp() async {
Future<String?> lastLogcatTimestamp() async {
RunResult output;
try {
output = await runAdbCheckedAsync(<String>[
......@@ -816,7 +815,7 @@ class AndroidDevice extends Device {
_logger.printError('Failed to extract the most recent timestamp from the Android log: $error.');
return null;
}
final Match timeMatch = _timeRegExp.firstMatch(output.stdout);
final Match? timeMatch = _timeRegExp.firstMatch(output.stdout);
return timeMatch?.group(0);
}
......@@ -854,7 +853,7 @@ Map<String, String> parseAdbDeviceProperties(String str) {
final Map<String, String> properties = <String, String>{};
final RegExp propertyExp = RegExp(r'\[(.*?)\]: \[(.*?)\]');
for (final Match match in propertyExp.allMatches(str)) {
properties[match.group(1)] = match.group(2);
properties[match.group(1)!] = match.group(2)!;
}
return properties;
}
......@@ -1011,12 +1010,7 @@ class AndroidMemoryInfo extends MemoryInfo {
/// A log reader that logs from `adb logcat`.
class AdbLogReader extends DeviceLogReader {
AdbLogReader._(this._adbProcess, this.name) {
_linesController = StreamController<String>.broadcast(
onListen: _start,
onCancel: _stop,
);
}
AdbLogReader._(this._adbProcess, this.name);
@visibleForTesting
factory AdbLogReader.test(Process adbProcess, String name) = AdbLogReader._;
......@@ -1029,7 +1023,7 @@ class AdbLogReader extends DeviceLogReader {
}) async {
// logcat -T is not supported on Android releases before Lollipop.
const int kLollipopVersionCode = 21;
final int apiVersion = (String v) {
final int? apiVersion = (String? v) {
// If the API version string isn't found, conservatively assume that the
// version is less recent than the one we're looking for.
return v == null ? kLollipopVersionCode - 1 : int.tryParse(v);
......@@ -1052,7 +1046,7 @@ class AdbLogReader extends DeviceLogReader {
} else if (apiVersion != null && apiVersion >= kLollipopVersionCode) {
// Otherwise, filter for logs appearing past the present.
// '-T 0` means the timestamp of the logcat command invocation.
final String lastLogcatTimestamp = await device.lastLogcatTimestamp();
final String? lastLogcatTimestamp = await device.lastLogcatTimestamp();
args.addAll(<String>[
'-T',
if (lastLogcatTimestamp != null) "'$lastLogcatTimestamp'" else '0',
......@@ -1067,7 +1061,10 @@ class AdbLogReader extends DeviceLogReader {
@override
final String name;
StreamController<String> _linesController;
late final StreamController<String> _linesController = StreamController<String>.broadcast(
onListen: _start,
onCancel: _stop,
);
@override
Stream<String> get logLines => _linesController.stream;
......@@ -1128,14 +1125,14 @@ class AdbLogReader extends DeviceLogReader {
if (_linesController.isClosed) {
return;
}
final Match timeMatch = AndroidDevice._timeRegExp.firstMatch(line);
final Match? timeMatch = AndroidDevice._timeRegExp.firstMatch(line);
if (timeMatch == null || line.length == timeMatch.end) {
_acceptedLastLine = false;
return;
}
// Chop off the time.
line = line.substring(timeMatch.end + 1);
final Match logMatch = _logFormat.firstMatch(line);
final Match? logMatch = _logFormat.firstMatch(line);
if (logMatch != null) {
bool acceptLine = false;
......@@ -1143,19 +1140,19 @@ class AdbLogReader extends DeviceLogReader {
// While a fatal crash is going on, only accept lines from the crash
// Otherwise the crash log in the console may get interrupted
final Match fatalMatch = _tombstoneLine.firstMatch(line);
final Match? fatalMatch = _tombstoneLine.firstMatch(line);
if (fatalMatch != null) {
acceptLine = true;
line = fatalMatch[1];
line = fatalMatch[1]!;
if (_tombstoneTerminator.hasMatch(fatalMatch[1])) {
if (_tombstoneTerminator.hasMatch(line)) {
// Hit crash terminator, stop logging the crash info
_fatalCrash = false;
}
}
} else if (appPid != null && int.parse(logMatch.group(1)) == appPid) {
} else if (appPid != null && int.parse(logMatch.group(1)!) == appPid) {
acceptLine = true;
if (_fatalLog.hasMatch(line)) {
......@@ -1189,7 +1186,7 @@ class AdbLogReader extends DeviceLogReader {
void _stop() {
_linesController.close();
_adbProcess?.kill();
_adbProcess.kill();
}
@override
......@@ -1201,10 +1198,10 @@ class AdbLogReader extends DeviceLogReader {
/// A [DevicePortForwarder] implemented for Android devices that uses adb.
class AndroidDevicePortForwarder extends DevicePortForwarder {
AndroidDevicePortForwarder({
@required ProcessManager processManager,
@required Logger logger,
@required String deviceId,
@required String adbPath,
required ProcessManager processManager,
required Logger logger,
required String deviceId,
required String adbPath,
}) : _deviceId = deviceId,
_adbPath = adbPath,
_logger = logger,
......@@ -1215,7 +1212,7 @@ class AndroidDevicePortForwarder extends DevicePortForwarder {
final Logger _logger;
final ProcessUtils _processUtils;
static int _extractPort(String portString) {
static int? _extractPort(String portString) {
return int.tryParse(portString.trim());
}
......@@ -1253,8 +1250,8 @@ class AndroidDevicePortForwarder extends DevicePortForwarder {
}
// Attempt to extract ports.
final int hostPort = _extractPort(splitLine[1]);
final int devicePort = _extractPort(splitLine[2]);
final int? hostPort = _extractPort(splitLine[1]);
final int? devicePort = _extractPort(splitLine[2]);
// Failed, skip.
if (hostPort == null || devicePort == null) {
......@@ -1268,7 +1265,7 @@ class AndroidDevicePortForwarder extends DevicePortForwarder {
}
@override
Future<int> forward(int devicePort, { int hostPort }) async {
Future<int> forward(int devicePort, { int? hostPort }) async {
hostPort ??= 0;
final RunResult process = await _processUtils.run(
<String>[
......@@ -1320,7 +1317,7 @@ class AndroidDevicePortForwarder extends DevicePortForwarder {
}
}
return hostPort;
return hostPort!;
}
@override
......@@ -1335,7 +1332,6 @@ class AndroidDevicePortForwarder extends DevicePortForwarder {
'--remove',
tcpLine,
],
throwOnError: false,
);
if (runResult.exitCode == 0) {
return;
......
......@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:meta/meta.dart';
import 'package:process/process.dart';
import '../base/common.dart';
......@@ -29,13 +26,13 @@ import 'android_workflow.dart';
/// * [AndroidDevice], the type of discovered device.
class AndroidDevices extends PollingDeviceDiscovery {
AndroidDevices({
@required AndroidWorkflow androidWorkflow,
@required ProcessManager processManager,
@required Logger logger,
@required AndroidSdk androidSdk,
@required FileSystem fileSystem,
@required Platform platform,
@required UserMessages userMessages,
required AndroidWorkflow androidWorkflow,
required ProcessManager processManager,
required Logger logger,
AndroidSdk? androidSdk,
required FileSystem fileSystem,
required Platform platform,
required UserMessages userMessages,
}) : _androidWorkflow = androidWorkflow,
_androidSdk = androidSdk,
_processUtils = ProcessUtils(
......@@ -51,7 +48,7 @@ class AndroidDevices extends PollingDeviceDiscovery {
final AndroidWorkflow _androidWorkflow;
final ProcessUtils _processUtils;
final AndroidSdk _androidSdk;
final AndroidSdk? _androidSdk;
final ProcessManager _processManager;
final Logger _logger;
final FileSystem _fileSystem;
......@@ -65,13 +62,13 @@ class AndroidDevices extends PollingDeviceDiscovery {
bool get canListAnything => _androidWorkflow.canListDevices;
@override
Future<List<Device>> pollingGetDevices({ Duration timeout }) async {
Future<List<Device>> pollingGetDevices({ Duration? timeout }) async {
if (_doesNotHaveAdb()) {
return <AndroidDevice>[];
}
String text;
try {
text = (await _processUtils.run(<String>[_androidSdk.adbPath, 'devices', '-l'],
text = (await _processUtils.run(<String>[_androidSdk!.adbPath!, 'devices', '-l'],
throwOnError: true,
)).stdout.trim();
} on ProcessException catch (exception) {
......@@ -94,7 +91,7 @@ class AndroidDevices extends PollingDeviceDiscovery {
return <String>[];
}
final RunResult result = await _processUtils.run(<String>[_androidSdk.adbPath, 'devices', '-l']);
final RunResult result = await _processUtils.run(<String>[_androidSdk!.adbPath!, 'devices', '-l']);
if (result.exitCode != 0) {
return <String>[];
}
......@@ -108,8 +105,8 @@ class AndroidDevices extends PollingDeviceDiscovery {
bool _doesNotHaveAdb() {
return _androidSdk == null ||
_androidSdk.adbPath == null ||
!_processManager.canRun(_androidSdk.adbPath);
_androidSdk?.adbPath == null ||
!_processManager.canRun(_androidSdk!.adbPath);
}
// 015d172c98400a03 device usb:340787200X product:nakasi model:Nexus_7 device:grouper
......@@ -120,8 +117,8 @@ class AndroidDevices extends PollingDeviceDiscovery {
/// in which case information for that parameter won't be populated.
void _parseADBDeviceOutput(
String text, {
List<AndroidDevice> devices,
List<String> diagnostics,
List<AndroidDevice>? devices,
List<String>? diagnostics,
}) {
// Check for error messages from adb
if (!text.contains('List of devices')) {
......@@ -146,11 +143,11 @@ class AndroidDevices extends PollingDeviceDiscovery {
}
if (_kDeviceRegex.hasMatch(line)) {
final Match match = _kDeviceRegex.firstMatch(line);
final Match match = _kDeviceRegex.firstMatch(line)!;
final String deviceID = match[1];
final String deviceState = match[2];
String rest = match[3];
final String deviceID = match[1]!;
final String deviceState = match[2]!;
String rest = match[3]!;
final Map<String, String> info = <String, String>{};
if (rest != null && rest.isNotEmpty) {
......@@ -163,8 +160,9 @@ class AndroidDevices extends PollingDeviceDiscovery {
}
}
if (info['model'] != null) {
info['model'] = cleanAdbDeviceName(info['model']);
final String? model = info['model'];
if (model != null) {
info['model'] = cleanAdbDeviceName(model);
}
if (deviceState == 'unauthorized') {
......@@ -180,7 +178,7 @@ class AndroidDevices extends PollingDeviceDiscovery {
productID: info['product'],
modelID: info['model'] ?? deviceID,
deviceCodeName: info['device'],
androidSdk: _androidSdk,
androidSdk: _androidSdk!,
fileSystem: _fileSystem,
logger: _logger,
platform: _platform,
......
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'dart:async';
import 'package:flutter_tools/src/android/android_device.dart';
......@@ -183,7 +181,7 @@ void main() {
});
}
AndroidDevice createFakeDevice(int sdkLevel) {
AndroidDevice createFakeDevice(int? sdkLevel) {
return FakeAndroidDevice(
sdkLevel.toString(),
kLastLogcatTimestamp,
......
......@@ -72,7 +72,6 @@ void main() {
testWithoutContext('AndroidDevices returns empty device list and diagnostics on null Android SDK', () async {
final AndroidDevices androidDevices = AndroidDevices(
androidSdk: null,
logger: BufferLogger.test(),
androidWorkflow: AndroidWorkflow(
androidSdk: FakeAndroidSdk(null),
......
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:flutter_tools/src/android/android_device.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/device_port_forwarder.dart';
......
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_device.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
......@@ -42,9 +40,9 @@ const FakeCommand kShaCommand = FakeCommand(
);
void main() {
FileSystem fileSystem;
FakeProcessManager processManager;
AndroidSdk androidSdk;
late FileSystem fileSystem;
late FakeProcessManager processManager;
late AndroidSdk androidSdk;
setUp(() {
processManager = FakeProcessManager.empty();
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_device.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:test/fake.dart';
import '../../src/common.dart';
import '../../src/fake_process_manager.dart';
void main() {
testWithoutContext('AndroidDevice.stopApp handles a null ApplicationPackage', () async {
final AndroidDevice androidDevice = AndroidDevice('1234',
androidSdk: FakeAndroidSdk(),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
platform: FakePlatform(),
processManager: FakeProcessManager.any(),
);
expect(await androidDevice.stopApp(null), false);
});
}
class FakeAndroidSdk extends Fake implements AndroidSdk { }
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
......@@ -459,15 +457,16 @@ Uptime: 441088659 Realtime: 521464097
}
AndroidDevice setUpAndroidDevice({
String id,
AndroidSdk androidSdk,
FileSystem fileSystem,
ProcessManager processManager,
Platform platform,
String? id,
AndroidSdk? androidSdk,
FileSystem? fileSystem,
ProcessManager? processManager,
Platform? platform,
AndroidConsoleSocketFactory androidConsoleSocketFactory = kAndroidConsoleSocketFactory,
}) {
androidSdk ??= FakeAndroidSdk();
return AndroidDevice(id ?? '1234',
modelID: 'TestModel',
logger: BufferLogger.test(),
platform: platform ?? FakePlatform(),
androidSdk: androidSdk,
......
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_device.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
......@@ -41,8 +39,8 @@ const FakeCommand kStoreShaCommand = FakeCommand(
);
void main() {
FileSystem fileSystem;
BufferLogger logger;
late FileSystem fileSystem;
late BufferLogger logger;
setUp(() {
fileSystem = MemoryFileSystem.test();
......@@ -50,15 +48,16 @@ void main() {
});
AndroidDevice setUpAndroidDevice({
AndroidSdk androidSdk,
ProcessManager processManager,
AndroidSdk? androidSdk,
ProcessManager? processManager,
}) {
androidSdk ??= FakeAndroidSdk();
return AndroidDevice('1234',
modelID: 'TestModel',
logger: logger,
platform: FakePlatform(),
androidSdk: androidSdk,
fileSystem: fileSystem ?? MemoryFileSystem.test(),
fileSystem: fileSystem,
processManager: processManager ?? FakeProcessManager.any(),
);
}
......
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