Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library android; | 5 library android; |
| 6 | 6 |
| 7 import "dart:async"; | 7 import "dart:async"; |
| 8 import "dart:convert" show LineSplitter, UTF8; | 8 import "dart:convert" show LineSplitter, UTF8; |
| 9 import "dart:core"; | 9 import "dart:core"; |
| 10 import "dart:io"; | 10 import "dart:io"; |
| 11 | 11 |
| 12 import "path.dart"; | 12 import "path.dart"; |
| 13 import "utils.dart"; | 13 import "utils.dart"; |
| 14 | 14 |
| 15 class AdbCommandResult { | |
| 16 final String command; | |
| 17 final String stdout; | |
| 18 final String stderr; | |
| 19 final int exitCode; | |
| 20 | |
| 21 AdbCommandResult(this.command, this.stdout, this.stderr, this.exitCode); | |
| 22 } | |
| 23 | |
| 15 Future _executeCommand(String executable, List<String> args, | 24 Future _executeCommand(String executable, List<String> args, |
| 16 [String stdin = ""]) { | 25 [String stdin = ""]) { |
| 17 return _executeCommandRaw(executable, args, stdin).then((results) => null); | 26 return _executeCommandRaw(executable, args, stdin).then((result) { |
| 27 _throwIfFailedAdbCommand(result); | |
| 28 }); | |
| 18 } | 29 } |
| 19 | 30 |
| 20 Future _executeCommandGetOutput(String executable, List<String> args, | 31 Future<String> _executeCommandGetOutput(String executable, List<String> args, |
| 21 [String stdin = ""]) { | 32 [String stdin = ""]) async { |
| 22 return _executeCommandRaw(executable, args, stdin).then((output) => output); | 33 AdbCommandResult result = await _executeCommandRaw(executable, args, stdin); |
| 34 _throwIfFailedAdbCommand(result); | |
| 35 return result.stdout; | |
| 36 } | |
| 37 | |
| 38 void _throwIfFailedAdbCommand(AdbCommandResult result) { | |
| 39 if (result.exitCode != 0) { | |
| 40 var error = "Running: ${result.command} failed:" | |
| 41 "stdout: \n ${result.stdout}" | |
| 42 "stderr: \n ${result.stderr}" | |
| 43 "exitCode: \n ${result.exitCode}"; | |
| 44 throw new Exception(error); | |
| 45 } | |
| 23 } | 46 } |
| 24 | 47 |
| 25 /** | 48 /** |
| 26 * [_executeCommandRaw] will write [stdin] to the standard input of the created | 49 * [_executeCommandRaw] will write [stdin] to the standard input of the created |
| 27 * process and will return a tuple (stdout, stderr). | 50 * process and will return a tuple (stdout, stderr). |
| 28 * | 51 * |
| 29 * If the exit code of the process was nonzero it will complete with an error. | 52 * If the exit code of the process was nonzero it will complete with an error. |
| 30 * If starting the process failed, it will complete with an error as well. | 53 * If starting the process failed, it will complete with an error as well. |
| 31 */ | 54 */ |
| 32 Future _executeCommandRaw(String executable, List<String> args, | 55 Future<AdbCommandResult> _executeCommandRaw( |
| 33 [String stdin = ""]) { | 56 String executable, List<String> args, [String stdin = ""]) { |
| 34 Future<String> getOutput(Stream<List<int>> stream) { | 57 Future<String> getOutput(Stream<List<int>> stream) { |
| 35 return stream | 58 return stream |
| 36 .transform(UTF8.decoder) | 59 .transform(UTF8.decoder) |
| 37 .toList() | 60 .toList() |
| 38 .then((data) => data.join("")); | 61 .then((data) => data.join("")); |
| 39 } | 62 } |
| 40 | 63 |
| 41 DebugLogger.info("Running: '\$ $executable ${args.join(' ')}'"); | |
| 42 return Process.start(executable, args).then((Process process) { | 64 return Process.start(executable, args).then((Process process) { |
| 43 if (stdin != null && stdin != '') { | 65 if (stdin != null && stdin != '') { |
| 44 process.stdin.write(stdin); | 66 process.stdin.write(stdin); |
| 45 } | 67 } |
| 46 process.stdin.close(); | 68 process.stdin.close(); |
| 47 | 69 |
| 48 var futures = [ | 70 var futures = [ |
| 49 getOutput(process.stdout), | 71 getOutput(process.stdout), |
| 50 getOutput(process.stderr), | 72 getOutput(process.stderr), |
| 51 process.exitCode | 73 process.exitCode |
| 52 ]; | 74 ]; |
| 53 return Future.wait(futures).then((results) { | 75 return Future.wait(futures).then((results) { |
| 54 bool success = results[2] == 0; | 76 String command = '$executable ${args.join(' ')}'; |
| 55 if (!success) { | 77 return new AdbCommandResult(command, results[0], results[1], results[2]); |
| 56 var error = "Running: '\$ $executable ${args.join(' ')}' failed:" | |
| 57 "stdout: \n ${results[0]}" | |
| 58 "stderr: \n ${results[1]}" | |
| 59 "exitCode: \n ${results[2]}"; | |
| 60 throw new Exception(error); | |
| 61 } else { | |
| 62 DebugLogger.info("Success: $executable finished"); | |
| 63 } | |
| 64 return results[0]; | |
| 65 }); | 78 }); |
| 66 }); | 79 }); |
| 67 } | 80 } |
| 68 | 81 |
| 69 /** | 82 /** |
| 70 * Helper class to loop through all adb ports. | 83 * Helper class to loop through all adb ports. |
| 71 * | 84 * |
| 72 * The ports come in pairs: | 85 * The ports come in pairs: |
| 73 * - even number: console connection | 86 * - even number: console connection |
| 74 * - odd number: adb connection | 87 * - odd number: adb connection |
| (...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 292 } | 305 } |
| 293 | 306 |
| 294 /** | 307 /** |
| 295 * Kill all background processes. | 308 * Kill all background processes. |
| 296 */ | 309 */ |
| 297 Future killAll() { | 310 Future killAll() { |
| 298 var arguments = ['shell', 'am', 'kill-all']; | 311 var arguments = ['shell', 'am', 'kill-all']; |
| 299 return _adbCommand(arguments); | 312 return _adbCommand(arguments); |
| 300 } | 313 } |
| 301 | 314 |
| 315 Future<AdbCommandResult> runAdbCommand(List<String> adbArgs) { | |
| 316 return _executeCommandRaw("adb", _deviceSpecificArgs(adbArgs)); | |
| 317 } | |
| 318 | |
| 319 Future<AdbCommandResult> runAdbShellCommand(List<String> shellArgs) async { | |
| 320 const MARKER = 'AdbShellExitCode: '; | |
| 321 | |
| 322 // The exitcode of 'adb shell ...' can be 0 even though the command failed | |
| 323 // with a non-zero exit code. We therefore explicitly print it to stdout and | |
| 324 // search for it. | |
| 325 | |
| 326 var args = ['shell', 'sh', '-c', | |
|
Florian Schneider
2016/04/27 09:10:37
For some reason I had to remove "sh -c" and the si
kustermann
2016/04/27 10:36:00
Both seem to work for me, so I changed it.
| |
| 327 "'${shellArgs.join(' ')} ; echo $MARKER \$?'"]; | |
| 328 AdbCommandResult result = await _executeCommandRaw( | |
| 329 "adb", _deviceSpecificArgs(args)); | |
| 330 int exitCode = result.exitCode; | |
| 331 var lines = result | |
| 332 .stdout.split('\n') | |
| 333 .where((line) => line.trim().length > 0) | |
| 334 .toList(); | |
| 335 if (lines.length > 0) { | |
| 336 int index = lines.last.indexOf(MARKER); | |
| 337 assert(index >= 0); | |
| 338 exitCode = int.parse(lines.last.substring(index + MARKER.length).trim()); | |
| 339 } | |
| 340 return new AdbCommandResult( | |
| 341 result.command, result.stdout, result.stderr, exitCode); | |
| 342 } | |
| 343 | |
| 302 Future _adbCommand(List<String> adbArgs) { | 344 Future _adbCommand(List<String> adbArgs) { |
| 345 return _executeCommand("adb", _deviceSpecificArgs(adbArgs)); | |
| 346 } | |
| 347 | |
| 348 Future<String> _adbCommandGetOutput(List<String> adbArgs) { | |
| 349 return _executeCommandGetOutput("adb", _deviceSpecificArgs(adbArgs)); | |
| 350 } | |
| 351 | |
| 352 List<String> _deviceSpecificArgs(List<String> adbArgs) { | |
| 303 if (_deviceId != null) { | 353 if (_deviceId != null) { |
| 304 var extendedAdbArgs = ['-s', _deviceId]; | 354 var extendedAdbArgs = ['-s', _deviceId]; |
| 305 extendedAdbArgs.addAll(adbArgs); | 355 extendedAdbArgs.addAll(adbArgs); |
| 306 adbArgs = extendedAdbArgs; | 356 adbArgs = extendedAdbArgs; |
| 307 } | 357 } |
| 308 return _executeCommand("adb", adbArgs); | 358 return adbArgs; |
| 309 } | |
| 310 | |
| 311 Future<String> _adbCommandGetOutput(List<String> adbArgs) { | |
| 312 if (_deviceId != null) { | |
| 313 var extendedAdbArgs = ['-s', _deviceId]; | |
| 314 extendedAdbArgs.addAll(adbArgs); | |
| 315 adbArgs = extendedAdbArgs; | |
| 316 } | |
| 317 return _executeCommandGetOutput("adb", adbArgs); | |
| 318 } | 359 } |
| 319 } | 360 } |
| 320 | 361 |
| 321 /** | 362 /** |
| 322 * Helper to list all adb devices available. | 363 * Helper to list all adb devices available. |
| 323 */ | 364 */ |
| 324 class AdbHelper { | 365 class AdbHelper { |
| 325 static RegExp _deviceLineRegexp = | 366 static RegExp _deviceLineRegexp = |
| 326 new RegExp(r'^([a-zA-Z0-9_-]+)[ \t]+device$', multiLine: true); | 367 new RegExp(r'^([a-zA-Z0-9_-]+)[ \t]+device$', multiLine: true); |
| 327 | 368 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 343 * Represents an android intent. | 384 * Represents an android intent. |
| 344 */ | 385 */ |
| 345 class Intent { | 386 class Intent { |
| 346 String action; | 387 String action; |
| 347 String package; | 388 String package; |
| 348 String activity; | 389 String activity; |
| 349 String dataUri; | 390 String dataUri; |
| 350 | 391 |
| 351 Intent(this.action, this.package, this.activity, [this.dataUri]); | 392 Intent(this.action, this.package, this.activity, [this.dataUri]); |
| 352 } | 393 } |
| 394 | |
| 395 /** | |
| 396 * Discovers all available devices and supports aquire/release. | |
| 397 */ | |
| 398 class AdbDevicePool { | |
| 399 List<AdbDevice> _idleDevices; | |
| 400 List<Completer> _waiter = []; | |
| 401 | |
| 402 AdbDevicePool(this._idleDevices); | |
| 403 | |
| 404 static Future<AdbDevicePool> create() async { | |
| 405 var names = await AdbHelper.listDevices(); | |
| 406 var devices = names.map((id) => new AdbDevice(id)).toList(); | |
| 407 if (devices.length == 0) { | |
| 408 throw new Exception( | |
| 409 'No android devices found. ' | |
| 410 'Please make sure "adb devices" shows your device!'); | |
| 411 } | |
| 412 return new AdbDevicePool(devices); | |
| 413 } | |
| 414 | |
| 415 Future<AdbDevice> aquireDevice() async { | |
| 416 if (_idleDevices.length > 0) { | |
| 417 return _idleDevices.removeLast(); | |
| 418 } else { | |
| 419 var completer = new Completer(); | |
| 420 _waiter.add(completer); | |
| 421 return completer.future; | |
| 422 } | |
| 423 } | |
| 424 | |
| 425 void releaseDevice(AdbDevice device) { | |
| 426 if (_waiter.length > 0) { | |
| 427 Completer completer = _waiter.removeLast(); | |
| 428 completer.complete(device); | |
| 429 } else { | |
| 430 _idleDevices.add(device); | |
| 431 } | |
| 432 } | |
| 433 } | |
| OLD | NEW |