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 """Provides a variety of device interactions based on adb. | 5 """Provides a variety of device interactions based on adb. |
6 | 6 |
7 Eventually, this will be based on adb_wrapper. | 7 Eventually, this will be based on adb_wrapper. |
8 """ | 8 """ |
9 # pylint: disable=W0613 | 9 # pylint: disable=W0613 |
10 | 10 |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
123 timeout: timeout in seconds | 123 timeout: timeout in seconds |
124 retries: number of retries | 124 retries: number of retries |
125 | 125 |
126 Returns: | 126 Returns: |
127 True if adbd has root privileges, False otherwise. | 127 True if adbd has root privileges, False otherwise. |
128 | 128 |
129 Raises: | 129 Raises: |
130 CommandTimeoutError on timeout. | 130 CommandTimeoutError on timeout. |
131 DeviceUnreachableError on missing device. | 131 DeviceUnreachableError on missing device. |
132 """ | 132 """ |
133 return self._HasRootImpl() | |
134 | |
135 def _HasRootImpl(self): | |
136 try: | 133 try: |
137 self._RunShellCommandImpl('ls /root', check_return=True) | 134 self.RunShellCommand('ls /root', check_return=True) |
138 return True | 135 return True |
139 except device_errors.AdbShellCommandFailedError: | 136 except device_errors.AdbShellCommandFailedError: |
140 return False | 137 return False |
141 | 138 |
139 def NeedsSU(self, timeout=None, retries=None): | |
140 """Checks whether 'su' is needed to access protected resources. | |
141 | |
142 Args: | |
143 timeout: timeout in seconds | |
144 retries: number of retries | |
145 | |
146 Returns: | |
147 True if 'su' is available on the device and is needed to to access | |
148 protected resources; False otherwise if either 'su' is not available | |
149 (e.g. because the device has a user build), or not needed (because adbd | |
150 already has root privileges). | |
151 | |
152 Raises: | |
153 CommandTimeoutError on timeout. | |
154 DeviceUnreachableError on missing device. | |
155 """ | |
156 if 'needs_su' not in self._cache: | |
157 try: | |
158 self.RunShellCommand('su -c ls /root && ! ls /root', check_return=True, | |
159 timeout=timeout, retries=retries) | |
160 self._cache['needs_su'] = True | |
161 except device_errors.AdbShellCommandFailedError: | |
162 self._cache['needs_su'] = False | |
163 return self._cache['needs_su'] | |
164 | |
165 | |
142 @decorators.WithTimeoutAndRetriesFromInstance() | 166 @decorators.WithTimeoutAndRetriesFromInstance() |
143 def EnableRoot(self, timeout=None, retries=None): | 167 def EnableRoot(self, timeout=None, retries=None): |
144 """Restarts adbd with root privileges. | 168 """Restarts adbd with root privileges. |
145 | 169 |
146 Args: | 170 Args: |
147 timeout: timeout in seconds | 171 timeout: timeout in seconds |
148 retries: number of retries | 172 retries: number of retries |
149 | 173 |
150 Raises: | 174 Raises: |
151 CommandFailedError if root could not be enabled. | 175 CommandFailedError if root could not be enabled. |
152 CommandTimeoutError on timeout. | 176 CommandTimeoutError on timeout. |
153 """ | 177 """ |
178 if 'needs_su' in self._cache: | |
179 del self._cache['needs_su'] | |
154 if not self.old_interface.EnableAdbRoot(): | 180 if not self.old_interface.EnableAdbRoot(): |
155 raise device_errors.CommandFailedError( | 181 raise device_errors.CommandFailedError( |
156 'Could not enable root.', device=str(self)) | 182 'Could not enable root.', device=str(self)) |
157 | 183 |
158 @decorators.WithTimeoutAndRetriesFromInstance() | 184 @decorators.WithTimeoutAndRetriesFromInstance() |
159 def IsUserBuild(self, timeout=None, retries=None): | 185 def IsUserBuild(self, timeout=None, retries=None): |
160 """Checks whether or not the device is running a user build. | 186 """Checks whether or not the device is running a user build. |
161 | 187 |
162 Args: | 188 Args: |
163 timeout: timeout in seconds | 189 timeout: timeout in seconds |
(...skipping 24 matching lines...) Expand all Loading... | |
188 CommandFailedError if the external storage path could not be determined. | 214 CommandFailedError if the external storage path could not be determined. |
189 CommandTimeoutError on timeout. | 215 CommandTimeoutError on timeout. |
190 DeviceUnreachableError on missing device. | 216 DeviceUnreachableError on missing device. |
191 """ | 217 """ |
192 return self._GetExternalStoragePathImpl() | 218 return self._GetExternalStoragePathImpl() |
193 | 219 |
194 def _GetExternalStoragePathImpl(self): | 220 def _GetExternalStoragePathImpl(self): |
195 if 'external_storage' in self._cache: | 221 if 'external_storage' in self._cache: |
196 return self._cache['external_storage'] | 222 return self._cache['external_storage'] |
197 | 223 |
198 value = self._RunShellCommandImpl('echo $EXTERNAL_STORAGE', | 224 value = self.RunShellCommand('echo $EXTERNAL_STORAGE', |
199 single_line=True, | 225 single_line=True, |
200 check_return=True) | 226 check_return=True) |
201 if not value: | 227 if not value: |
202 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', | 228 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', |
203 str(self)) | 229 str(self)) |
204 self._cache['external_storage'] = value | 230 self._cache['external_storage'] = value |
205 return value | 231 return value |
206 | 232 |
207 @decorators.WithTimeoutAndRetriesFromInstance() | 233 @decorators.WithTimeoutAndRetriesFromInstance() |
208 def GetApplicationPath(self, package, timeout=None, retries=None): | 234 def GetApplicationPath(self, package, timeout=None, retries=None): |
209 """Get the path of the installed apk on the device for the given package. | 235 """Get the path of the installed apk on the device for the given package. |
210 | 236 |
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
386 (with the optional newline at the end stripped). | 412 (with the optional newline at the end stripped). |
387 | 413 |
388 Raises: | 414 Raises: |
389 AdbShellCommandFailedError if check_return is True and the exit code of | 415 AdbShellCommandFailedError if check_return is True and the exit code of |
390 the command run on the device is non-zero. | 416 the command run on the device is non-zero. |
391 CommandFailedError if single_line is True but the output contains two or | 417 CommandFailedError if single_line is True but the output contains two or |
392 more lines. | 418 more lines. |
393 CommandTimeoutError on timeout. | 419 CommandTimeoutError on timeout. |
394 DeviceUnreachableError on missing device. | 420 DeviceUnreachableError on missing device. |
395 """ | 421 """ |
396 return self._RunShellCommandImpl(cmd, check_return=check_return, cwd=cwd, | |
397 env=env, as_root=as_root, single_line=single_line, timeout=timeout) | |
398 | |
399 def _RunShellCommandImpl(self, cmd, check_return=False, cwd=None, env=None, | |
jbudorick
2014/11/06 00:33:45
Nice cleanup!
| |
400 as_root=False, single_line=False, timeout=None): | |
401 def env_quote(key, value): | 422 def env_quote(key, value): |
402 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): | 423 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): |
403 raise KeyError('Invalid shell variable name %r' % key) | 424 raise KeyError('Invalid shell variable name %r' % key) |
404 # using double quotes here to allow interpolation of shell variables | 425 # using double quotes here to allow interpolation of shell variables |
405 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) | 426 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) |
406 | 427 |
407 if not isinstance(cmd, basestring): | 428 if not isinstance(cmd, basestring): |
408 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) | 429 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) |
409 if as_root and not self._HasRootImpl(): | 430 if as_root and self.NeedsSU(): |
410 cmd = 'su -c %s' % cmd | 431 cmd = 'su -c %s' % cmd |
411 if env: | 432 if env: |
412 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) | 433 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) |
413 cmd = '%s %s' % (env, cmd) | 434 cmd = '%s %s' % (env, cmd) |
414 if cwd: | 435 if cwd: |
415 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) | 436 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) |
416 if timeout is None: | 437 if timeout is None: |
417 timeout = self._default_timeout | 438 timeout = self._default_timeout |
418 | 439 |
419 try: | 440 try: |
420 # TODO(perezju) still need to make sure that we call a version of | 441 output = self.adb.Shell(cmd, expect_rc=0) |
421 # adb.Shell without a timeout-and-retries wrapper. | |
422 output = self.adb.Shell(cmd, expect_rc=0, timeout=timeout, retries=0) | |
423 except device_errors.AdbShellCommandFailedError as e: | 442 except device_errors.AdbShellCommandFailedError as e: |
424 if check_return: | 443 if check_return: |
425 raise | 444 raise |
426 else: | 445 else: |
427 output = e.output | 446 output = e.output |
428 | 447 |
429 output = output.splitlines() | 448 output = output.splitlines() |
430 if single_line: | 449 if single_line: |
431 if not output: | 450 if not output: |
432 return '' | 451 return '' |
(...skipping 25 matching lines...) Expand all Loading... | |
458 CommandFailedError if no process was killed. | 477 CommandFailedError if no process was killed. |
459 CommandTimeoutError on timeout. | 478 CommandTimeoutError on timeout. |
460 DeviceUnreachableError on missing device. | 479 DeviceUnreachableError on missing device. |
461 """ | 480 """ |
462 pids = self._GetPidsImpl(process_name) | 481 pids = self._GetPidsImpl(process_name) |
463 if not pids: | 482 if not pids: |
464 raise device_errors.CommandFailedError( | 483 raise device_errors.CommandFailedError( |
465 'No process "%s"' % process_name, device=str(self)) | 484 'No process "%s"' % process_name, device=str(self)) |
466 | 485 |
467 cmd = ['kill', '-%d' % signum] + pids.values() | 486 cmd = ['kill', '-%d' % signum] + pids.values() |
468 self._RunShellCommandImpl(cmd, as_root=as_root, check_return=True) | 487 self.RunShellCommand(cmd, as_root=as_root, check_return=True) |
469 | 488 |
470 if blocking: | 489 if blocking: |
471 wait_period = 0.1 | 490 wait_period = 0.1 |
472 while self._GetPidsImpl(process_name): | 491 while self._GetPidsImpl(process_name): |
473 time.sleep(wait_period) | 492 time.sleep(wait_period) |
474 | 493 |
475 return len(pids) | 494 return len(pids) |
476 | 495 |
477 @decorators.WithTimeoutAndRetriesFromInstance() | 496 @decorators.WithTimeoutAndRetriesFromInstance() |
478 def StartActivity(self, intent, blocking=False, trace_file_name=None, | 497 def StartActivity(self, intent, blocking=False, trace_file_name=None, |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
609 | 628 |
610 Raises: | 629 Raises: |
611 CommandFailedError on failure. | 630 CommandFailedError on failure. |
612 CommandTimeoutError on timeout. | 631 CommandTimeoutError on timeout. |
613 DeviceUnreachableError on missing device. | 632 DeviceUnreachableError on missing device. |
614 """ | 633 """ |
615 | 634 |
616 files = [] | 635 files = [] |
617 for h, d in host_device_tuples: | 636 for h, d in host_device_tuples: |
618 if os.path.isdir(h): | 637 if os.path.isdir(h): |
619 self._RunShellCommandImpl(['mkdir', '-p', d], check_return=True) | 638 self.RunShellCommand(['mkdir', '-p', d], check_return=True) |
620 files += self._GetChangedFilesImpl(h, d) | 639 files += self._GetChangedFilesImpl(h, d) |
621 | 640 |
622 if not files: | 641 if not files: |
623 return | 642 return |
624 | 643 |
625 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files) | 644 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files) |
626 file_count = len(files) | 645 file_count = len(files) |
627 dir_size = sum(host_utils.GetRecursiveDiskUsage(h) | 646 dir_size = sum(host_utils.GetRecursiveDiskUsage(h) |
628 for h, _ in host_device_tuples) | 647 for h, _ in host_device_tuples) |
629 dir_file_count = 0 | 648 dir_file_count = 0 |
(...skipping 11 matching lines...) Expand all Loading... | |
641 | 660 |
642 self._InstallCommands() | 661 self._InstallCommands() |
643 | 662 |
644 if dir_push_duration < push_duration and ( | 663 if dir_push_duration < push_duration and ( |
645 dir_push_duration < zip_duration or not self._commands_installed): | 664 dir_push_duration < zip_duration or not self._commands_installed): |
646 self._PushChangedFilesIndividually(host_device_tuples) | 665 self._PushChangedFilesIndividually(host_device_tuples) |
647 elif push_duration < zip_duration or not self._commands_installed: | 666 elif push_duration < zip_duration or not self._commands_installed: |
648 self._PushChangedFilesIndividually(files) | 667 self._PushChangedFilesIndividually(files) |
649 else: | 668 else: |
650 self._PushChangedFilesZipped(files) | 669 self._PushChangedFilesZipped(files) |
651 self._RunShellCommandImpl( | 670 self.RunShellCommand( |
652 ['chmod', '-R', '777'] + [d for _, d in host_device_tuples], | 671 ['chmod', '-R', '777'] + [d for _, d in host_device_tuples], |
653 as_root=True, check_return=True) | 672 as_root=True, check_return=True) |
654 | 673 |
655 def _GetChangedFilesImpl(self, host_path, device_path): | 674 def _GetChangedFilesImpl(self, host_path, device_path): |
656 real_host_path = os.path.realpath(host_path) | 675 real_host_path = os.path.realpath(host_path) |
657 try: | 676 try: |
658 real_device_path = self._RunShellCommandImpl( | 677 real_device_path = self.RunShellCommand( |
659 ['realpath', device_path], single_line=True, check_return=True) | 678 ['realpath', device_path], single_line=True, check_return=True) |
660 except device_errors.CommandFailedError: | 679 except device_errors.CommandFailedError: |
661 real_device_path = None | 680 real_device_path = None |
662 if not real_device_path: | 681 if not real_device_path: |
663 return [(host_path, device_path)] | 682 return [(host_path, device_path)] |
664 | 683 |
665 # TODO(jbudorick): Move the md5 logic up into DeviceUtils or base | 684 # TODO(jbudorick): Move the md5 logic up into DeviceUtils or base |
666 # this function on mtime. | 685 # this function on mtime. |
667 # pylint: disable=W0212 | 686 # pylint: disable=W0212 |
668 host_hash_tuples, device_hash_tuples = self.old_interface._RunMd5Sum( | 687 host_hash_tuples, device_hash_tuples = self.old_interface._RunMd5Sum( |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
741 with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file: | 760 with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file: |
742 zip_proc = multiprocessing.Process( | 761 zip_proc = multiprocessing.Process( |
743 target=DeviceUtils._CreateDeviceZip, | 762 target=DeviceUtils._CreateDeviceZip, |
744 args=(zip_file.name, files)) | 763 args=(zip_file.name, files)) |
745 zip_proc.start() | 764 zip_proc.start() |
746 zip_proc.join() | 765 zip_proc.join() |
747 | 766 |
748 zip_on_device = '%s/tmp.zip' % self._GetExternalStoragePathImpl() | 767 zip_on_device = '%s/tmp.zip' % self._GetExternalStoragePathImpl() |
749 try: | 768 try: |
750 self.adb.Push(zip_file.name, zip_on_device) | 769 self.adb.Push(zip_file.name, zip_on_device) |
751 self._RunShellCommandImpl( | 770 self.RunShellCommand( |
752 ['unzip', zip_on_device], | 771 ['unzip', zip_on_device], |
753 as_root=True, | 772 as_root=True, |
754 env={'PATH': '$PATH:%s' % install_commands.BIN_DIR}, | 773 env={'PATH': '$PATH:%s' % install_commands.BIN_DIR}, |
755 check_return=True) | 774 check_return=True) |
756 finally: | 775 finally: |
757 if zip_proc.is_alive(): | 776 if zip_proc.is_alive(): |
758 zip_proc.terminate() | 777 zip_proc.terminate() |
759 if self.IsOnline(): | 778 if self.IsOnline(): |
760 self._RunShellCommandImpl(['rm', zip_on_device], check_return=True) | 779 self.RunShellCommand(['rm', zip_on_device], check_return=True) |
761 | 780 |
762 @staticmethod | 781 @staticmethod |
763 def _CreateDeviceZip(zip_path, host_device_tuples): | 782 def _CreateDeviceZip(zip_path, host_device_tuples): |
764 with zipfile.ZipFile(zip_path, 'w') as zip_file: | 783 with zipfile.ZipFile(zip_path, 'w') as zip_file: |
765 for host_path, device_path in host_device_tuples: | 784 for host_path, device_path in host_device_tuples: |
766 if os.path.isfile(host_path): | 785 if os.path.isfile(host_path): |
767 zip_file.write(host_path, device_path, zipfile.ZIP_DEFLATED) | 786 zip_file.write(host_path, device_path, zipfile.ZIP_DEFLATED) |
768 else: | 787 else: |
769 for hd, _, files in os.walk(host_path): | 788 for hd, _, files in os.walk(host_path): |
770 dd = '%s/%s' % (device_path, os.path.relpath(host_path, hd)) | 789 dd = '%s/%s' % (device_path, os.path.relpath(host_path, hd)) |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
888 timeout: timeout in seconds | 907 timeout: timeout in seconds |
889 retries: number of retries | 908 retries: number of retries |
890 | 909 |
891 Raises: | 910 Raises: |
892 CommandFailedError if the file could not be written on the device. | 911 CommandFailedError if the file could not be written on the device. |
893 CommandTimeoutError on timeout. | 912 CommandTimeoutError on timeout. |
894 DeviceUnreachableError on missing device. | 913 DeviceUnreachableError on missing device. |
895 """ | 914 """ |
896 cmd = 'echo %s > %s' % (cmd_helper.SingleQuote(text), | 915 cmd = 'echo %s > %s' % (cmd_helper.SingleQuote(text), |
897 cmd_helper.SingleQuote(device_path)) | 916 cmd_helper.SingleQuote(device_path)) |
898 self._RunShellCommandImpl(cmd, as_root=as_root, check_return=True) | 917 self.RunShellCommand(cmd, as_root=as_root, check_return=True) |
899 | 918 |
900 @decorators.WithTimeoutAndRetriesFromInstance() | 919 @decorators.WithTimeoutAndRetriesFromInstance() |
901 def Ls(self, device_path, timeout=None, retries=None): | 920 def Ls(self, device_path, timeout=None, retries=None): |
902 """Lists the contents of a directory on the device. | 921 """Lists the contents of a directory on the device. |
903 | 922 |
904 Args: | 923 Args: |
905 device_path: A string containing the path of the directory on the device | 924 device_path: A string containing the path of the directory on the device |
906 to list. | 925 to list. |
907 timeout: timeout in seconds | 926 timeout: timeout in seconds |
908 retries: number of retries | 927 retries: number of retries |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1031 provided |process_name|. | 1050 provided |process_name|. |
1032 | 1051 |
1033 Raises: | 1052 Raises: |
1034 CommandTimeoutError on timeout. | 1053 CommandTimeoutError on timeout. |
1035 DeviceUnreachableError on missing device. | 1054 DeviceUnreachableError on missing device. |
1036 """ | 1055 """ |
1037 return self._GetPidsImpl(process_name) | 1056 return self._GetPidsImpl(process_name) |
1038 | 1057 |
1039 def _GetPidsImpl(self, process_name): | 1058 def _GetPidsImpl(self, process_name): |
1040 procs_pids = {} | 1059 procs_pids = {} |
1041 for line in self._RunShellCommandImpl('ps', check_return=True): | 1060 for line in self.RunShellCommand('ps', check_return=True): |
1042 try: | 1061 try: |
1043 ps_data = line.split() | 1062 ps_data = line.split() |
1044 if process_name in ps_data[-1]: | 1063 if process_name in ps_data[-1]: |
1045 procs_pids[ps_data[-1]] = ps_data[1] | 1064 procs_pids[ps_data[-1]] = ps_data[1] |
1046 except IndexError: | 1065 except IndexError: |
1047 pass | 1066 pass |
1048 return procs_pids | 1067 return procs_pids |
1049 | 1068 |
1050 @decorators.WithTimeoutAndRetriesFromInstance() | 1069 @decorators.WithTimeoutAndRetriesFromInstance() |
1051 def TakeScreenshot(self, host_path=None, timeout=None, retries=None): | 1070 def TakeScreenshot(self, host_path=None, timeout=None, retries=None): |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1129 Returns: | 1148 Returns: |
1130 A Parallelizer operating over |devices|. | 1149 A Parallelizer operating over |devices|. |
1131 """ | 1150 """ |
1132 if not devices or len(devices) == 0: | 1151 if not devices or len(devices) == 0: |
1133 devices = pylib.android_commands.GetAttachedDevices() | 1152 devices = pylib.android_commands.GetAttachedDevices() |
1134 parallelizer_type = (parallelizer.Parallelizer if async | 1153 parallelizer_type = (parallelizer.Parallelizer if async |
1135 else parallelizer.SyncParallelizer) | 1154 else parallelizer.SyncParallelizer) |
1136 return parallelizer_type([ | 1155 return parallelizer_type([ |
1137 d if isinstance(d, DeviceUtils) else DeviceUtils(d) | 1156 d if isinstance(d, DeviceUtils) else DeviceUtils(d) |
1138 for d in devices]) | 1157 for d in devices]) |
OLD | NEW |