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:collection"; | 10 import "dart:collection"; |
11 import "dart:io"; | 11 import "dart:io"; |
12 | 12 |
13 import "path.dart"; | 13 import "path.dart"; |
14 import "utils.dart"; | 14 import "utils.dart"; |
15 | 15 |
16 class AdbCommandResult { | 16 class AdbCommandResult { |
17 final String command; | 17 final String command; |
18 final String stdout; | 18 final String stdout; |
19 final String stderr; | 19 final String stderr; |
20 final int exitCode; | 20 final int exitCode; |
21 final bool timedOut; | |
21 | 22 |
22 AdbCommandResult(this.command, this.stdout, this.stderr, this.exitCode); | 23 AdbCommandResult(this.command, this.stdout, this.stderr, this.exitCode, |
24 this.timedOut); | |
23 | 25 |
24 void throwIfFailed() { | 26 void throwIfFailed() { |
25 if (exitCode != 0) { | 27 if (exitCode != 0) { |
26 var error = "Running: $command failed:" | 28 var error = "Running: $command failed:" |
27 "stdout: \n $stdout" | 29 "stdout:\n ${stdout.trim()}\n" |
28 "stderr: \n $stderr" | 30 "stderr:\n ${stderr.trim()}\n" |
29 "exitCode: \n $exitCode"; | 31 "exitCode: $exitCode\n" |
32 "timedOut: $timedOut"; | |
30 throw new Exception(error); | 33 throw new Exception(error); |
31 } | 34 } |
32 } | 35 } |
33 } | 36 } |
34 | 37 |
35 /** | 38 /** |
36 * [_executeCommand] will write [stdin] to the standard input of the created | 39 * [_executeCommand] will write [stdin] to the standard input of the created |
37 * process and will return a tuple (stdout, stderr). | 40 * process and will return a tuple (stdout, stderr). |
38 * | 41 * |
39 * If the exit code of the process was nonzero it will complete with an error. | 42 * If the exit code of the process was nonzero it will complete with an error. |
40 * If starting the process failed, it will complete with an error as well. | 43 * If starting the process failed, it will complete with an error as well. |
41 */ | 44 */ |
42 Future<AdbCommandResult> _executeCommand( | 45 Future<AdbCommandResult> _executeCommand( |
43 String executable, List<String> args, [String stdin = ""]) { | 46 String executable, List<String> args, |
47 [String stdin = "", Duration timeout]) { | |
Bill Hesse
2016/05/02 11:54:17
Should these now be named, not positional, optiona
kustermann
2016/05/02 11:59:40
Done.
| |
44 Future<String> getOutput(Stream<List<int>> stream) { | 48 Future<String> getOutput(Stream<List<int>> stream) { |
45 return stream | 49 return stream |
46 .transform(UTF8.decoder) | 50 .transform(UTF8.decoder) |
47 .toList() | 51 .toList() |
48 .then((data) => data.join("")); | 52 .then((data) => data.join("")); |
49 } | 53 } |
50 | 54 |
51 return Process.start(executable, args).then((Process process) async { | 55 return Process.start(executable, args).then((Process process) async { |
52 if (stdin != null && stdin != '') { | 56 if (stdin != null && stdin != '') { |
53 process.stdin.write(stdin); | 57 process.stdin.write(stdin); |
54 } | 58 } |
55 process.stdin.close(); | 59 process.stdin.close(); |
56 | 60 |
61 Timer timer; | |
62 bool timedOut = false; | |
63 if (timeout != null) { | |
64 timer = new Timer(timeout, () { | |
65 timedOut = true; | |
66 process.kill(ProcessSignal.SIGTERM); | |
67 timer = null; | |
68 }); | |
69 } | |
70 | |
57 var results = await Future.wait([ | 71 var results = await Future.wait([ |
58 getOutput(process.stdout), | 72 getOutput(process.stdout), |
59 getOutput(process.stderr), | 73 getOutput(process.stderr), |
60 process.exitCode | 74 process.exitCode |
61 ]); | 75 ]); |
76 if (timer != null) timer.cancel(); | |
77 | |
62 String command = "$executable ${args.join(' ')}"; | 78 String command = "$executable ${args.join(' ')}"; |
63 return new AdbCommandResult(command, results[0], results[1], results[2]); | 79 return new AdbCommandResult( |
80 command, results[0], results[1], results[2], timedOut); | |
64 }); | 81 }); |
65 } | 82 } |
66 | 83 |
67 /** | 84 /** |
68 * Helper class to loop through all adb ports. | 85 * Helper class to loop through all adb ports. |
69 * | 86 * |
70 * The ports come in pairs: | 87 * The ports come in pairs: |
71 * - even number: console connection | 88 * - even number: console connection |
72 * - odd number: adb connection | 89 * - odd number: adb connection |
73 * Note that this code doesn't check if the ports are used. | 90 * Note that this code doesn't check if the ports are used. |
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
281 } | 298 } |
282 | 299 |
283 /** | 300 /** |
284 * Kill all background processes. | 301 * Kill all background processes. |
285 */ | 302 */ |
286 Future killAll() { | 303 Future killAll() { |
287 var arguments = ['shell', 'am', 'kill-all']; | 304 var arguments = ['shell', 'am', 'kill-all']; |
288 return _adbCommand(arguments); | 305 return _adbCommand(arguments); |
289 } | 306 } |
290 | 307 |
291 Future<AdbCommandResult> runAdbCommand(List<String> adbArgs) { | 308 Future<AdbCommandResult> runAdbCommand(List<String> adbArgs, |
292 return _executeCommand("adb", _deviceSpecificArgs(adbArgs)); | 309 {Duration timeout}) { |
310 return _executeCommand("adb", _deviceSpecificArgs(adbArgs), '', timeout); | |
293 } | 311 } |
294 | 312 |
295 Future<AdbCommandResult> runAdbShellCommand(List<String> shellArgs) async { | 313 Future<AdbCommandResult> runAdbShellCommand(List<String> shellArgs, |
314 {Duration timeout}) async { | |
296 const MARKER = 'AdbShellExitCode: '; | 315 const MARKER = 'AdbShellExitCode: '; |
297 | 316 |
298 // The exitcode of 'adb shell ...' can be 0 even though the command failed | 317 // The exitcode of 'adb shell ...' can be 0 even though the command failed |
299 // with a non-zero exit code. We therefore explicitly print it to stdout and | 318 // with a non-zero exit code. We therefore explicitly print it to stdout and |
300 // search for it. | 319 // search for it. |
301 | 320 |
302 var args = ['shell', | 321 var args = ['shell', |
303 "${shellArgs.join(' ')} ; echo $MARKER \$?"]; | 322 "${shellArgs.join(' ')} ; echo $MARKER \$?"]; |
304 AdbCommandResult result = await _executeCommand( | 323 AdbCommandResult result = await _executeCommand( |
305 "adb", _deviceSpecificArgs(args)); | 324 "adb", _deviceSpecificArgs(args), '', timeout); |
306 int exitCode = result.exitCode; | 325 int exitCode = result.exitCode; |
307 var lines = result | 326 var lines = result |
308 .stdout.split('\n') | 327 .stdout.split('\n') |
309 .where((line) => line.trim().length > 0) | 328 .where((line) => line.trim().length > 0) |
310 .toList(); | 329 .toList(); |
311 if (lines.length > 0) { | 330 if (lines.length > 0) { |
312 int index = lines.last.indexOf(MARKER); | 331 int index = lines.last.indexOf(MARKER); |
313 assert(index >= 0); | 332 if (index >= 0) { |
314 exitCode = int.parse(lines.last.substring(index + MARKER.length).trim()); | 333 exitCode = int.parse( |
334 lines.last.substring(index + MARKER.length).trim()); | |
335 } else { | |
336 // In case of timeouts, for example, we won't get the exitcode marker. | |
337 assert(result.exitCode != 0); | |
338 } | |
315 } | 339 } |
316 return new AdbCommandResult( | 340 return new AdbCommandResult( |
317 result.command, result.stdout, result.stderr, exitCode); | 341 result.command, result.stdout, result.stderr, exitCode, |
342 result.timedOut); | |
318 } | 343 } |
319 | 344 |
320 Future<AdbCommandResult> _adbCommand(List<String> adbArgs) async { | 345 Future<AdbCommandResult> _adbCommand(List<String> adbArgs) async { |
321 var result = await _executeCommand("adb", _deviceSpecificArgs(adbArgs)); | 346 var result = await _executeCommand("adb", _deviceSpecificArgs(adbArgs)); |
322 result.throwIfFailed(); | 347 result.throwIfFailed(); |
323 return result; | 348 return result; |
324 } | 349 } |
325 | 350 |
326 List<String> _deviceSpecificArgs(List<String> adbArgs) { | 351 List<String> _deviceSpecificArgs(List<String> adbArgs) { |
327 if (_deviceId != null) { | 352 if (_deviceId != null) { |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
400 | 425 |
401 void releaseDevice(AdbDevice device) { | 426 void releaseDevice(AdbDevice device) { |
402 if (_waiter.length > 0) { | 427 if (_waiter.length > 0) { |
403 Completer completer = _waiter.removeFirst(); | 428 Completer completer = _waiter.removeFirst(); |
404 completer.complete(device); | 429 completer.complete(device); |
405 } else { | 430 } else { |
406 _idleDevices.add(device); | 431 _idleDevices.add(device); |
407 } | 432 } |
408 } | 433 } |
409 } | 434 } |
OLD | NEW |