OLD | NEW |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import atexit | 5 import atexit |
6 import datetime | 6 import datetime |
7 import email.utils | 7 import email.utils |
8 import hashlib | 8 import hashlib |
9 import itertools | 9 import itertools |
10 import json | 10 import json |
(...skipping 19 matching lines...) Expand all Loading... |
30 'MojoMain', | 30 'MojoMain', |
31 'MojoShellActivity', | 31 'MojoShellActivity', |
32 'MojoShellApplication', | 32 'MojoShellApplication', |
33 'chromium', | 33 'chromium', |
34 ] | 34 ] |
35 | 35 |
36 MOJO_SHELL_PACKAGE_NAME = 'org.chromium.mojo.shell' | 36 MOJO_SHELL_PACKAGE_NAME = 'org.chromium.mojo.shell' |
37 | 37 |
38 MAPPING_PREFIX = '--map-origin=' | 38 MAPPING_PREFIX = '--map-origin=' |
39 | 39 |
40 DEFAULT_BASE_PORT = 31337 | |
41 | |
42 ZERO = datetime.timedelta(0) | 40 ZERO = datetime.timedelta(0) |
43 | 41 |
44 class UTC_TZINFO(datetime.tzinfo): | 42 class UTC_TZINFO(datetime.tzinfo): |
45 """UTC time zone representation.""" | 43 """UTC time zone representation.""" |
46 | 44 |
47 def utcoffset(self, _): | 45 def utcoffset(self, _): |
48 return ZERO | 46 return ZERO |
49 | 47 |
50 def tzname(self, _): | 48 def tzname(self, _): |
51 return "UTC" | 49 return "UTC" |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
265 | 263 |
266 def _UnmapPort(): | 264 def _UnmapPort(): |
267 subprocess.Popen(unmap_command) | 265 subprocess.Popen(unmap_command) |
268 atexit.register(_UnmapPort) | 266 atexit.register(_UnmapPort) |
269 return device_port | 267 return device_port |
270 | 268 |
271 def _StartHttpServerForDirectory(self, path, port=0): | 269 def _StartHttpServerForDirectory(self, path, port=0): |
272 """Starts an http server serving files from |path|. Returns the local | 270 """Starts an http server serving files from |path|. Returns the local |
273 url.""" | 271 url.""" |
274 assert path | 272 assert path |
275 print 'starting http for', path | |
276 httpd = _SilentTCPServer(('127.0.0.1', 0), _GetHandlerClassForPath(path)) | 273 httpd = _SilentTCPServer(('127.0.0.1', 0), _GetHandlerClassForPath(path)) |
277 atexit.register(httpd.shutdown) | 274 atexit.register(httpd.shutdown) |
278 | 275 |
279 http_thread = threading.Thread(target=httpd.serve_forever) | 276 http_thread = threading.Thread(target=httpd.serve_forever) |
280 http_thread.daemon = True | 277 http_thread.daemon = True |
281 http_thread.start() | 278 http_thread.start() |
282 | 279 |
283 print 'local port=%d' % httpd.server_address[1] | 280 print 'Hosting %s at http://127.0.0.1:%d' % (path, httpd.server_address[1]) |
284 return 'http://127.0.0.1:%d/' % self._MapPort(port, httpd.server_address[1]) | 281 return 'http://127.0.0.1:%d/' % self._MapPort(port, httpd.server_address[1]) |
285 | 282 |
286 def _StartHttpServerForOriginMapping(self, mapping, port): | 283 def _StartHttpServerForOriginMapping(self, mapping, port): |
287 """If |mapping| points at a local file starts an http server to serve files | 284 """If |mapping| points at a local file starts an http server to serve files |
288 from the directory and returns the new mapping. | 285 from the directory and returns the new mapping. |
289 | 286 |
290 This is intended to be called for every --map-origin value.""" | 287 This is intended to be called for every --map-origin value.""" |
291 parts = mapping.split('=') | 288 parts = mapping.split('=') |
292 if len(parts) != 2: | 289 if len(parts) != 2: |
293 return mapping | 290 return mapping |
294 dest = parts[1] | 291 dest = parts[1] |
295 # If the destination is a url, don't map it. | 292 # If the destination is a url, don't map it. |
296 if urlparse.urlparse(dest)[0]: | 293 if urlparse.urlparse(dest)[0]: |
297 return mapping | 294 return mapping |
298 # Assume the destination is a local file. Start a local server that | 295 # Assume the destination is a local file. Start a local server that |
299 # redirects to it. | 296 # redirects to it. |
300 localUrl = self._StartHttpServerForDirectory(dest, port) | 297 localUrl = self._StartHttpServerForDirectory(dest, port) |
301 print 'started server at %s for %s' % (dest, localUrl) | 298 print 'started server at %s for %s' % (dest, localUrl) |
302 return parts[0] + '=' + localUrl | 299 return parts[0] + '=' + localUrl |
303 | 300 |
304 def _StartHttpServerForOriginMappings(self, map_parameters, fixed_port): | 301 def _StartHttpServerForOriginMappings(self, map_parameters): |
305 """Calls _StartHttpServerForOriginMapping for every --map-origin | 302 """Calls _StartHttpServerForOriginMapping for every --map-origin |
306 argument.""" | 303 argument.""" |
307 if not map_parameters: | 304 if not map_parameters: |
308 return [] | 305 return [] |
309 | 306 |
310 original_values = list(itertools.chain( | 307 original_values = list(itertools.chain( |
311 *map(lambda x: x[len(MAPPING_PREFIX):].split(','), map_parameters))) | 308 *map(lambda x: x[len(MAPPING_PREFIX):].split(','), map_parameters))) |
312 sorted(original_values) | 309 sorted(original_values) |
313 result = [] | 310 result = [] |
314 for i, value in enumerate(original_values): | 311 for value in original_values: |
315 result.append(self._StartHttpServerForOriginMapping( | 312 result.append(self._StartHttpServerForOriginMapping(value, 0)) |
316 value, DEFAULT_BASE_PORT + 1 + i if fixed_port else 0)) | |
317 return [MAPPING_PREFIX + ','.join(result)] | 313 return [MAPPING_PREFIX + ','.join(result)] |
318 | 314 |
319 def PrepareShellRun(self, origin=None, fixed_port=True): | 315 def PrepareShellRun(self, origin=None): |
320 """ Prepares for StartShell: runs adb as root and installs the apk. If no | 316 """ Prepares for StartShell: runs adb as root and installs the apk. If the |
321 --origin is specified, local http server will be set up to serve files from | 317 origin specified is 'localhost', a local http server will be set up to serve |
322 the build directory along with port forwarding. | 318 files from the build directory along with port forwarding. |
323 | 319 |
324 Returns arguments that should be appended to shell argument list.""" | 320 Returns arguments that should be appended to shell argument list.""" |
325 if 'cannot run as root' in subprocess.check_output( | 321 if 'cannot run as root' in subprocess.check_output( |
326 self._CreateADBCommand(['root'])): | 322 self._CreateADBCommand(['root'])): |
327 raise Exception("Unable to run adb as root.") | 323 raise Exception("Unable to run adb as root.") |
328 subprocess.check_call( | 324 subprocess.check_call( |
329 self._CreateADBCommand(['install', '-r', self.shell_apk_path, '-i', | 325 self._CreateADBCommand(['install', '-r', self.shell_apk_path, '-i', |
330 self.target_package])) | 326 self.target_package])) |
331 atexit.register(self.StopShell) | 327 atexit.register(self.StopShell) |
332 | 328 |
333 extra_shell_args = [] | 329 extra_shell_args = [] |
| 330 if origin is 'localhost': |
| 331 origin = self._StartHttpServerForDirectory(self.local_dir, 0) |
334 if origin: | 332 if origin: |
335 origin_url = origin if origin else self._StartHttpServerForDirectory( | 333 extra_shell_args.append("--origin=" + origin) |
336 self.local_dir, DEFAULT_BASE_PORT if fixed_port else 0) | |
337 extra_shell_args.append("--origin=" + origin_url) | |
338 | |
339 return extra_shell_args | 334 return extra_shell_args |
340 | 335 |
341 def StartShell(self, | 336 def StartShell(self, |
342 arguments, | 337 arguments, |
343 stdout=None, | 338 stdout=None, |
344 on_application_stop=None, | 339 on_application_stop=None): |
345 fixed_port=True): | |
346 """ | 340 """ |
347 Starts the mojo shell, passing it the given arguments. | 341 Starts the mojo shell, passing it the given arguments. |
348 | 342 |
349 The |arguments| list must contain the "--origin=" arg from PrepareShellRun. | 343 The |arguments| list must contain the "--origin=" arg from PrepareShellRun. |
350 If |stdout| is not None, it should be a valid argument for subprocess.Popen. | 344 If |stdout| is not None, it should be a valid argument for subprocess.Popen. |
351 """ | 345 """ |
352 STDOUT_PIPE = "/data/data/%s/stdout.fifo" % self.target_package | 346 STDOUT_PIPE = "/data/data/%s/stdout.fifo" % self.target_package |
353 | 347 |
354 cmd = self._CreateADBCommand([ | 348 cmd = self._CreateADBCommand([ |
355 'shell', | 349 'shell', |
356 'am', | 350 'am', |
357 'start', | 351 'start', |
358 '-S', | 352 '-S', |
359 '-a', 'android.intent.action.VIEW', | 353 '-a', 'android.intent.action.VIEW', |
360 '-n', '%s/%s.MojoShellActivity' % (self.target_package, | 354 '-n', '%s/%s.MojoShellActivity' % (self.target_package, |
361 MOJO_SHELL_PACKAGE_NAME)]) | 355 MOJO_SHELL_PACKAGE_NAME)]) |
362 | 356 |
363 parameters = [] | 357 parameters = [] |
364 if stdout or on_application_stop: | 358 if stdout or on_application_stop: |
365 subprocess.check_call(self._CreateADBCommand( | 359 subprocess.check_call(self._CreateADBCommand( |
366 ['shell', 'rm', '-f', STDOUT_PIPE])) | 360 ['shell', 'rm', '-f', STDOUT_PIPE])) |
367 parameters.append('--fifo-path=%s' % STDOUT_PIPE) | 361 parameters.append('--fifo-path=%s' % STDOUT_PIPE) |
368 self._ReadFifo(STDOUT_PIPE, stdout, on_application_stop) | 362 self._ReadFifo(STDOUT_PIPE, stdout, on_application_stop) |
369 | 363 |
370 # Extract map-origin arguments. | 364 # Extract map-origin arguments. |
371 map_parameters, other_parameters = _Split(arguments, _IsMapOrigin) | 365 map_parameters, other_parameters = _Split(arguments, _IsMapOrigin) |
372 parameters += other_parameters | 366 parameters += other_parameters |
373 parameters += self._StartHttpServerForOriginMappings(map_parameters, | 367 parameters += self._StartHttpServerForOriginMappings(map_parameters) |
374 fixed_port) | |
375 | 368 |
376 if parameters: | 369 if parameters: |
377 encodedParameters = json.dumps(parameters) | 370 encodedParameters = json.dumps(parameters) |
378 cmd += ['--es', 'encodedParameters', encodedParameters] | 371 cmd += ['--es', 'encodedParameters', encodedParameters] |
379 | 372 |
380 with open(os.devnull, 'w') as devnull: | 373 with open(os.devnull, 'w') as devnull: |
381 subprocess.Popen(cmd, stdout=devnull).wait() | 374 subprocess.Popen(cmd, stdout=devnull).wait() |
382 | 375 |
383 def StopShell(self): | 376 def StopShell(self): |
384 """ | 377 """ |
(...skipping 16 matching lines...) Expand all Loading... |
401 | 394 |
402 Returns the process responsible for reading the logs. | 395 Returns the process responsible for reading the logs. |
403 """ | 396 """ |
404 logcat = subprocess.Popen(self._CreateADBCommand([ | 397 logcat = subprocess.Popen(self._CreateADBCommand([ |
405 'logcat', | 398 'logcat', |
406 '-s', | 399 '-s', |
407 ' '.join(LOGCAT_TAGS)]), | 400 ' '.join(LOGCAT_TAGS)]), |
408 stdout=sys.stdout) | 401 stdout=sys.stdout) |
409 atexit.register(_ExitIfNeeded, logcat) | 402 atexit.register(_ExitIfNeeded, logcat) |
410 return logcat | 403 return logcat |
OLD | NEW |