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 Future _executeCommand(String executable, | 15 Future _executeCommand(String executable, List<String> args, |
16 List<String> args, | 16 [String stdin = ""]) { |
17 [String stdin = ""]) { | |
18 return _executeCommandRaw(executable, args, stdin).then((results) => null); | 17 return _executeCommandRaw(executable, args, stdin).then((results) => null); |
19 } | 18 } |
20 | 19 |
21 Future _executeCommandGetOutput(String executable, | 20 Future _executeCommandGetOutput(String executable, List<String> args, |
22 List<String> args, | 21 [String stdin = ""]) { |
23 [String stdin = ""]) { | 22 return _executeCommandRaw(executable, args, stdin).then((output) => output); |
24 return _executeCommandRaw(executable, args, stdin) | |
25 .then((output) => output); | |
26 } | 23 } |
27 | 24 |
28 /** | 25 /** |
29 * [_executeCommandRaw] will write [stdin] to the standard input of the created | 26 * [_executeCommandRaw] will write [stdin] to the standard input of the created |
30 * process and will return a tuple (stdout, stderr). | 27 * process and will return a tuple (stdout, stderr). |
31 * | 28 * |
32 * If the exit code of the process was nonzero it will complete with an error. | 29 * If the exit code of the process was nonzero it will complete with an error. |
33 * If starting the process failed, it will complete with an error as well. | 30 * If starting the process failed, it will complete with an error as well. |
34 */ | 31 */ |
35 Future _executeCommandRaw(String executable, | 32 Future _executeCommandRaw(String executable, List<String> args, |
36 List<String> args, | 33 [String stdin = ""]) { |
37 [String stdin = ""]) { | |
38 Future<String> getOutput(Stream<List<int>> stream) { | 34 Future<String> getOutput(Stream<List<int>> stream) { |
39 return stream.transform(UTF8.decoder).toList() | 35 return stream |
| 36 .transform(UTF8.decoder) |
| 37 .toList() |
40 .then((data) => data.join("")); | 38 .then((data) => data.join("")); |
41 } | 39 } |
42 | 40 |
43 DebugLogger.info("Running: '\$ $executable ${args.join(' ')}'"); | 41 DebugLogger.info("Running: '\$ $executable ${args.join(' ')}'"); |
44 return Process.start(executable, args).then((Process process) { | 42 return Process.start(executable, args).then((Process process) { |
45 if (stdin != null && stdin != '') { | 43 if (stdin != null && stdin != '') { |
46 process.stdin.write(stdin); | 44 process.stdin.write(stdin); |
47 } | 45 } |
48 process.stdin.close(); | 46 process.stdin.close(); |
49 | 47 |
50 var futures = [getOutput(process.stdout), | 48 var futures = [ |
51 getOutput(process.stderr), | 49 getOutput(process.stdout), |
52 process.exitCode]; | 50 getOutput(process.stderr), |
| 51 process.exitCode |
| 52 ]; |
53 return Future.wait(futures).then((results) { | 53 return Future.wait(futures).then((results) { |
54 bool success = results[2] == 0; | 54 bool success = results[2] == 0; |
55 if (!success) { | 55 if (!success) { |
56 var error = "Running: '\$ $executable ${args.join(' ')}' failed:" | 56 var error = "Running: '\$ $executable ${args.join(' ')}' failed:" |
57 "stdout: \n ${results[0]}" | 57 "stdout: \n ${results[0]}" |
58 "stderr: \n ${results[1]}" | 58 "stderr: \n ${results[1]}" |
59 "exitCode: \n ${results[2]}"; | 59 "exitCode: \n ${results[2]}"; |
60 throw new Exception(error); | 60 throw new Exception(error); |
61 } else { | 61 } else { |
62 DebugLogger.info("Success: $executable finished"); | 62 DebugLogger.info("Success: $executable finished"); |
63 } | 63 } |
64 return results[0]; | 64 return results[0]; |
65 }); | 65 }); |
66 }); | 66 }); |
67 } | 67 } |
68 | 68 |
69 /** | 69 /** |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
146 void log(String msg) { | 146 void log(String msg) { |
147 DebugLogger.info("AndroidEmulator(${_adbDevice.deviceId}): $msg"); | 147 DebugLogger.info("AndroidEmulator(${_adbDevice.deviceId}): $msg"); |
148 } | 148 } |
149 } | 149 } |
150 | 150 |
151 /** | 151 /** |
152 * Helper class to create avd device configurations. | 152 * Helper class to create avd device configurations. |
153 */ | 153 */ |
154 class AndroidHelper { | 154 class AndroidHelper { |
155 static Future createAvd(String name, String target) { | 155 static Future createAvd(String name, String target) { |
156 var args = ['--silent', 'create', 'avd', '--name', '$name', | 156 var args = [ |
157 '--target', '$target', '--force', '--abi', 'armeabi-v7a']; | 157 '--silent', |
| 158 'create', |
| 159 'avd', |
| 160 '--name', |
| 161 '$name', |
| 162 '--target', |
| 163 '$target', |
| 164 '--force', |
| 165 '--abi', |
| 166 'armeabi-v7a' |
| 167 ]; |
158 // We're adding newlines to stdin to simulate <enter>. | 168 // We're adding newlines to stdin to simulate <enter>. |
159 return _executeCommand("android", args, "\n\n\n\n"); | 169 return _executeCommand("android", args, "\n\n\n\n"); |
160 } | 170 } |
161 } | 171 } |
162 | 172 |
163 /** | 173 /** |
164 * Used for communicating with an emulator or with a real device. | 174 * Used for communicating with an emulator or with a real device. |
165 */ | 175 */ |
166 class AdbDevice { | 176 class AdbDevice { |
167 static const _adbServerStartupTime = const Duration(seconds: 3); | 177 static const _adbServerStartupTime = const Duration(seconds: 3); |
(...skipping 14 matching lines...) Expand all Loading... |
182 * Polls the 'sys.boot_completed' property. Returns as soon as the property is | 192 * Polls the 'sys.boot_completed' property. Returns as soon as the property is |
183 * 1. | 193 * 1. |
184 */ | 194 */ |
185 Future waitForBootCompleted() { | 195 Future waitForBootCompleted() { |
186 var timeout = const Duration(seconds: 2); | 196 var timeout = const Duration(seconds: 2); |
187 var completer = new Completer(); | 197 var completer = new Completer(); |
188 | 198 |
189 checkUntilBooted() { | 199 checkUntilBooted() { |
190 _adbCommandGetOutput(['shell', 'getprop', 'sys.boot_completed']) | 200 _adbCommandGetOutput(['shell', 'getprop', 'sys.boot_completed']) |
191 .then((String stdout) { | 201 .then((String stdout) { |
192 stdout = stdout.trim(); | 202 stdout = stdout.trim(); |
193 if (stdout == '1') { | 203 if (stdout == '1') { |
194 completer.complete(); | 204 completer.complete(); |
195 } else { | 205 } else { |
196 new Timer(timeout, checkUntilBooted); | 206 new Timer(timeout, checkUntilBooted); |
197 } | 207 } |
198 }).catchError((error) { | 208 }).catchError((error) { |
199 new Timer(timeout, checkUntilBooted); | 209 new Timer(timeout, checkUntilBooted); |
200 }); | 210 }); |
201 } | 211 } |
202 checkUntilBooted(); | 212 checkUntilBooted(); |
203 return completer.future; | 213 return completer.future; |
204 } | 214 } |
205 | 215 |
206 /** | 216 /** |
207 * Put adb in root mode. | 217 * Put adb in root mode. |
208 */ | 218 */ |
209 Future adbRoot() { | 219 Future adbRoot() { |
210 var adbRootCompleter = new Completer(); | 220 var adbRootCompleter = new Completer(); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
243 */ | 253 */ |
244 Future installApk(Path filename) { | 254 Future installApk(Path filename) { |
245 return _adbCommand( | 255 return _adbCommand( |
246 ['install', '-i', 'com.google.android.feedback', '-r', '$filename']); | 256 ['install', '-i', 'com.google.android.feedback', '-r', '$filename']); |
247 } | 257 } |
248 | 258 |
249 /** | 259 /** |
250 * Start the given intent on the device. | 260 * Start the given intent on the device. |
251 */ | 261 */ |
252 Future startActivity(Intent intent) { | 262 Future startActivity(Intent intent) { |
253 var arguments = ['shell', 'am', 'start', '-W', | 263 var arguments = [ |
254 '-a', intent.action, | 264 'shell', |
255 '-n', "${intent.package}/${intent.activity}"]; | 265 'am', |
| 266 'start', |
| 267 '-W', |
| 268 '-a', |
| 269 intent.action, |
| 270 '-n', |
| 271 "${intent.package}/${intent.activity}" |
| 272 ]; |
256 if (intent.dataUri != null) { | 273 if (intent.dataUri != null) { |
257 arguments.addAll(['-d', intent.dataUri]); | 274 arguments.addAll(['-d', intent.dataUri]); |
258 } | 275 } |
259 return _adbCommand(arguments); | 276 return _adbCommand(arguments); |
260 } | 277 } |
261 | 278 |
262 /** | 279 /** |
263 * Force to stop everything associated with [package]. | 280 * Force to stop everything associated with [package]. |
264 */ | 281 */ |
265 Future forceStop(String package) { | 282 Future forceStop(String package) { |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
305 * Helper to list all adb devices available. | 322 * Helper to list all adb devices available. |
306 */ | 323 */ |
307 class AdbHelper { | 324 class AdbHelper { |
308 static RegExp _deviceLineRegexp = | 325 static RegExp _deviceLineRegexp = |
309 new RegExp(r'^([a-zA-Z0-9_-]+)[ \t]+device$', multiLine: true); | 326 new RegExp(r'^([a-zA-Z0-9_-]+)[ \t]+device$', multiLine: true); |
310 | 327 |
311 static Future<List<String>> listDevices() { | 328 static Future<List<String>> listDevices() { |
312 return Process.run('adb', ['devices']).then((ProcessResult result) { | 329 return Process.run('adb', ['devices']).then((ProcessResult result) { |
313 if (result.exitCode != 0) { | 330 if (result.exitCode != 0) { |
314 throw new Exception("Could not list devices [stdout: ${result.stdout}," | 331 throw new Exception("Could not list devices [stdout: ${result.stdout}," |
315 "stderr: ${result.stderr}]"); | 332 "stderr: ${result.stderr}]"); |
316 } | 333 } |
317 return _deviceLineRegexp.allMatches(result.stdout) | 334 return _deviceLineRegexp |
318 .map((Match m) => m.group(1)).toList(); | 335 .allMatches(result.stdout) |
| 336 .map((Match m) => m.group(1)) |
| 337 .toList(); |
319 }); | 338 }); |
320 } | 339 } |
321 } | 340 } |
322 | 341 |
323 /** | 342 /** |
324 * Represents an android intent. | 343 * Represents an android intent. |
325 */ | 344 */ |
326 class Intent { | 345 class Intent { |
327 String action; | 346 String action; |
328 String package; | 347 String package; |
329 String activity; | 348 String activity; |
330 String dataUri; | 349 String dataUri; |
331 | 350 |
332 Intent(this.action, this.package, this.activity, [this.dataUri]); | 351 Intent(this.action, this.package, this.activity, [this.dataUri]); |
333 } | 352 } |
334 | |
OLD | NEW |