From b72d67a8fea84e1b508ee8319033147df155b1a5 Mon Sep 17 00:00:00 2001 From: Ian Fischer Date: Tue, 15 Sep 2015 16:17:47 -0700 Subject: [PATCH] Private setup methods for AndroidDevice. --- packages/flutter_tools/bin/sky_tools.dart | 6 + packages/flutter_tools/lib/src/device.dart | 122 +++++++++++++++++- .../lib/src/process_wrapper.dart | 22 ++++ 3 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 packages/flutter_tools/lib/src/process_wrapper.dart diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart index d79e402430b..efc0dcbbd93 100644 --- a/packages/flutter_tools/bin/sky_tools.dart +++ b/packages/flutter_tools/bin/sky_tools.dart @@ -14,6 +14,12 @@ void main(List args) { Logger.root.level = Level.WARNING; Logger.root.onRecord.listen((LogRecord rec) { print('${rec.level.name}: ${rec.message}'); + if (rec.error != null) { + print(rec.error); + } + if (rec.stackTrace != null) { + print(rec.stackTrace); + } }); Map handlers = {}; diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 510e51ca4b5..ae439341be1 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -4,6 +4,15 @@ library sky_tools.device; +import 'dart:io'; + +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; + +import 'process_wrapper.dart'; + +final Logger _logging = new Logger('sky_tools.device'); + abstract class _Device { final String id; static Map _deviceCache = {}; @@ -41,14 +50,28 @@ abstract class _Device { } class AndroidDevice extends _Device { + static const String _ADB_PATH = 'adb'; + static const String className = 'AndroidDevice'; static final String defaultDeviceID = 'default'; + String _adbPath; + String get adbPath => _adbPath; + factory AndroidDevice([String id = null]) { return new _Device(className, id); } - AndroidDevice._(id) : super._(id); + AndroidDevice._(id) : super._(id) { + _updatePaths(); + + // Checking for lollipop only needs to be done if we are starting an + // app, but it has an important side effect, which is to discard any + // progress messages if the adb server is restarted. + if (!_checkForAdb() || !_checkForLollipopOrLater()) { + _logging.severe('Unable to run on Android.'); + } + } @override bool installApp(String path) { @@ -64,4 +87,101 @@ class AndroidDevice extends _Device { bool isConnected() { return true; } + + void _updatePaths() { + if (Platform.environment.containsKey('ANDROID_HOME')) { + String androidHomeDir = Platform.environment['ANDROID_HOME']; + String adbPath1 = + path.join(androidHomeDir, 'sdk', 'platform-tools', 'adb'); + String adbPath2 = path.join(androidHomeDir, 'platform-tools', 'adb'); + if (FileSystemEntity.isFileSync(adbPath1)) { + _adbPath = adbPath1; + } else if (FileSystemEntity.isFileSync(adbPath2)) { + _adbPath = adbPath2; + } else { + _logging.info('"adb" not found at\n "$adbPath1" or\n "$adbPath2"\n' + + 'using default path "$_ADB_PATH"'); + _adbPath = _ADB_PATH; + } + } else { + _adbPath = _ADB_PATH; + } + } + + bool _isValidAdbVersion(String adbVersion) { + // Sample output: 'Android Debug Bridge version 1.0.31' + Match versionFields = + new RegExp(r'(\d+)\.(\d+)\.(\d+)').firstMatch(adbVersion); + if (versionFields != null) { + int majorVersion = int.parse(versionFields[1]); + int minorVersion = int.parse(versionFields[2]); + int patchVersion = int.parse(versionFields[3]); + if (majorVersion > 1) { + return true; + } + if (majorVersion == 1 && minorVersion > 0) { + return true; + } + if (majorVersion == 1 && minorVersion == 0 && patchVersion >= 32) { + return true; + } + return false; + } + _logging.warning( + 'Unrecognized adb version string $adbVersion. Skipping version check.'); + return true; + } + + bool _checkForAdb() { + try { + String adbVersion = runCheckedSync([adbPath, 'version']); + if (_isValidAdbVersion(adbVersion)) { + return true; + } + + String locatedAdbPath = runCheckedSync(['which', 'adb']); + _logging.severe('"$locatedAdbPath" is too old. ' + 'Please install version 1.0.32 or later.\n' + 'Try setting ANDROID_HOME to the path to your Android SDK install. ' + 'Android builds are unavailable.'); + } catch (e, stack) { + _logging.severe('"adb" not found in \$PATH. ' + 'Please install the Android SDK or set ANDROID_HOME ' + 'to the path of your Android SDK install.'); + _logging.info(e); + _logging.info(stack); + } + return false; + } + + bool _checkForLollipopOrLater() { + 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 * + runCheckedSync([adbPath, 'start-server']); + + // Sample output: '22' + String sdkVersion = + runCheckedSync([adbPath, 'shell', 'getprop', 'ro.build.version.sdk']) + .trimRight(); + + int sdkVersionParsed = + int.parse(sdkVersion, onError: (String source) => null); + if (sdkVersionParsed == null) { + _logging.severe('Unexpected response from getprop: "$sdkVersion"'); + return false; + } + if (sdkVersionParsed < 22) { + _logging.severe('Version "$sdkVersion" of the Android SDK is too old. ' + 'Please install Lollipop (version 22) or later.'); + return false; + } + return true; + } catch (e, stack) { + _logging.severe('Unexpected failure from adb: ', e, stack); + } + return false; + } } diff --git a/packages/flutter_tools/lib/src/process_wrapper.dart b/packages/flutter_tools/lib/src/process_wrapper.dart new file mode 100644 index 00000000000..4a17f3419d8 --- /dev/null +++ b/packages/flutter_tools/lib/src/process_wrapper.dart @@ -0,0 +1,22 @@ +// Copyright 2015 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. + +library sky_tools.process_wrapper; + +import 'dart:io'; +import 'package:logging/logging.dart'; + +final Logger _logging = new Logger('sky_tools.process_wrapper'); +String runCheckedSync(List cmd) { + _logging.info(cmd.join(' ')); + ProcessResult results = + Process.runSync(cmd[0], cmd.getRange(1, cmd.length).toList()); + if (results.exitCode != 0) { + throw 'Error code ' + + results.exitCode.toString() + + ' returned when attempting to run command: ' + + cmd.join(' '); + } + return results.stdout; +}