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 """ | 5 """Provides a variety of device interactions based on adb. |
6 Provides a variety of device interactions based on adb. | |
7 | 6 |
8 Eventually, this will be based on adb_wrapper. | 7 Eventually, this will be based on adb_wrapper. |
9 """ | 8 """ |
10 # pylint: disable=W0613 | 9 # pylint: disable=W0613 |
11 | 10 |
12 import time | 11 import time |
13 | 12 |
14 import pylib.android_commands | 13 import pylib.android_commands |
15 from pylib.device import adb_wrapper | 14 from pylib.device import adb_wrapper |
16 from pylib.device import decorators | 15 from pylib.device import decorators |
17 from pylib.device import device_errors | 16 from pylib.device import device_errors |
| 17 from pylib.utils import apk_helper |
18 from pylib.utils import parallelizer | 18 from pylib.utils import parallelizer |
19 | 19 |
20 _DEFAULT_TIMEOUT = 30 | 20 _DEFAULT_TIMEOUT = 30 |
21 _DEFAULT_RETRIES = 3 | 21 _DEFAULT_RETRIES = 3 |
22 | 22 |
23 | 23 |
24 @decorators.WithExplicitTimeoutAndRetries( | 24 @decorators.WithExplicitTimeoutAndRetries( |
25 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) | 25 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) |
26 def GetAVDs(): | 26 def GetAVDs(): |
27 """ Returns a list of Android Virtual Devices. | 27 """Returns a list of Android Virtual Devices. |
28 | 28 |
29 Returns: | 29 Returns: |
30 A list containing the configured AVDs. | 30 A list containing the configured AVDs. |
31 """ | 31 """ |
32 return pylib.android_commands.GetAVDs() | 32 return pylib.android_commands.GetAVDs() |
33 | 33 |
34 | 34 |
35 @decorators.WithExplicitTimeoutAndRetries( | 35 @decorators.WithExplicitTimeoutAndRetries( |
36 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) | 36 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) |
37 def RestartServer(): | 37 def RestartServer(): |
38 """ Restarts the adb server. | 38 """Restarts the adb server. |
39 | 39 |
40 Raises: | 40 Raises: |
41 CommandFailedError if we fail to kill or restart the server. | 41 CommandFailedError if we fail to kill or restart the server. |
42 """ | 42 """ |
43 pylib.android_commands.AndroidCommands().RestartAdbServer() | 43 pylib.android_commands.AndroidCommands().RestartAdbServer() |
44 | 44 |
45 | 45 |
46 class DeviceUtils(object): | 46 class DeviceUtils(object): |
47 | 47 |
48 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, | 48 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, |
49 default_retries=_DEFAULT_RETRIES): | 49 default_retries=_DEFAULT_RETRIES): |
50 """ DeviceUtils constructor. | 50 """DeviceUtils constructor. |
51 | 51 |
52 Args: | 52 Args: |
53 device: Either a device serial, an existing AdbWrapper instance, an | 53 device: Either a device serial, an existing AdbWrapper instance, an |
54 an existing AndroidCommands instance, or nothing. | 54 an existing AndroidCommands instance, or nothing. |
55 default_timeout: An integer containing the default number of seconds to | 55 default_timeout: An integer containing the default number of seconds to |
56 wait for an operation to complete if no explicit value | 56 wait for an operation to complete if no explicit value |
57 is provided. | 57 is provided. |
58 default_retries: An integer containing the default number or times an | 58 default_retries: An integer containing the default number or times an |
59 operation should be retried on failure if no explicit | 59 operation should be retried on failure if no explicit |
60 value is provided. | 60 value is provided. |
61 """ | 61 """ |
62 self.old_interface = None | 62 self.old_interface = None |
63 if isinstance(device, basestring): | 63 if isinstance(device, basestring): |
64 self.old_interface = pylib.android_commands.AndroidCommands(device) | 64 self.old_interface = pylib.android_commands.AndroidCommands(device) |
65 elif isinstance(device, adb_wrapper.AdbWrapper): | 65 elif isinstance(device, adb_wrapper.AdbWrapper): |
66 self.old_interface = pylib.android_commands.AndroidCommands(str(device)) | 66 self.old_interface = pylib.android_commands.AndroidCommands(str(device)) |
67 elif isinstance(device, pylib.android_commands.AndroidCommands): | 67 elif isinstance(device, pylib.android_commands.AndroidCommands): |
68 self.old_interface = device | 68 self.old_interface = device |
69 elif not device: | 69 elif not device: |
70 self.old_interface = pylib.android_commands.AndroidCommands() | 70 self.old_interface = pylib.android_commands.AndroidCommands() |
71 else: | 71 else: |
72 raise ValueError('Unsupported type passed for argument "device"') | 72 raise ValueError('Unsupported type passed for argument "device"') |
73 self._default_timeout = default_timeout | 73 self._default_timeout = default_timeout |
74 self._default_retries = default_retries | 74 self._default_retries = default_retries |
75 assert(hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)) | 75 assert(hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)) |
76 assert(hasattr(self, decorators.DEFAULT_RETRIES_ATTR)) | 76 assert(hasattr(self, decorators.DEFAULT_RETRIES_ATTR)) |
77 | 77 |
78 @decorators.WithTimeoutAndRetriesFromInstance() | 78 @decorators.WithTimeoutAndRetriesFromInstance() |
79 def IsOnline(self, timeout=None, retries=None): | 79 def IsOnline(self, timeout=None, retries=None): |
80 """ Checks whether the device is online. | 80 """Checks whether the device is online. |
81 | 81 |
82 Args: | 82 Args: |
83 timeout: An integer containing the number of seconds to wait for the | 83 timeout: An integer containing the number of seconds to wait for the |
84 operation to complete. | 84 operation to complete. |
85 retries: An integer containing the number of times the operation should | 85 retries: An integer containing the number of times the operation should |
86 be retried if it fails. | 86 be retried if it fails. |
87 Returns: | 87 Returns: |
88 True if the device is online, False otherwise. | 88 True if the device is online, False otherwise. |
89 """ | 89 """ |
90 return self.old_interface.IsOnline() | 90 return self.old_interface.IsOnline() |
91 | 91 |
92 @decorators.WithTimeoutAndRetriesFromInstance() | 92 @decorators.WithTimeoutAndRetriesFromInstance() |
93 def HasRoot(self, timeout=None, retries=None): | 93 def HasRoot(self, timeout=None, retries=None): |
94 """ Checks whether or not adbd has root privileges. | 94 """Checks whether or not adbd has root privileges. |
95 | 95 |
96 Args: | 96 Args: |
97 timeout: Same as for |IsOnline|. | 97 timeout: Same as for |IsOnline|. |
98 retries: Same as for |IsOnline|. | 98 retries: Same as for |IsOnline|. |
99 Returns: | 99 Returns: |
100 True if adbd has root privileges, False otherwise. | 100 True if adbd has root privileges, False otherwise. |
101 """ | 101 """ |
102 return self.old_interface.IsRootEnabled() | 102 return self.old_interface.IsRootEnabled() |
103 | 103 |
104 @decorators.WithTimeoutAndRetriesFromInstance() | 104 @decorators.WithTimeoutAndRetriesFromInstance() |
105 def EnableRoot(self, timeout=None, retries=None): | 105 def EnableRoot(self, timeout=None, retries=None): |
106 """ Restarts adbd with root privileges. | 106 """Restarts adbd with root privileges. |
107 | 107 |
108 Args: | 108 Args: |
109 timeout: Same as for |IsOnline|. | 109 timeout: Same as for |IsOnline|. |
110 retries: Same as for |IsOnline|. | 110 retries: Same as for |IsOnline|. |
111 Raises: | 111 Raises: |
112 CommandFailedError if root could not be enabled. | 112 CommandFailedError if root could not be enabled. |
113 """ | 113 """ |
114 if not self.old_interface.EnableAdbRoot(): | 114 if not self.old_interface.EnableAdbRoot(): |
115 raise device_errors.CommandFailedError( | 115 raise device_errors.CommandFailedError( |
116 'adb root', 'Could not enable root.') | 116 ['adb', 'root'], 'Could not enable root.') |
117 | 117 |
118 @decorators.WithTimeoutAndRetriesFromInstance() | 118 @decorators.WithTimeoutAndRetriesFromInstance() |
119 def GetExternalStoragePath(self, timeout=None, retries=None): | 119 def GetExternalStoragePath(self, timeout=None, retries=None): |
120 """ Get the device's path to its SD card. | 120 """Get the device's path to its SD card. |
121 | 121 |
122 Args: | 122 Args: |
123 timeout: Same as for |IsOnline|. | 123 timeout: Same as for |IsOnline|. |
124 retries: Same as for |IsOnline|. | 124 retries: Same as for |IsOnline|. |
125 Returns: | 125 Returns: |
126 The device's path to its SD card. | 126 The device's path to its SD card. |
127 """ | 127 """ |
128 try: | 128 try: |
129 return self.old_interface.GetExternalStorage() | 129 return self.old_interface.GetExternalStorage() |
130 except AssertionError as e: | 130 except AssertionError as e: |
131 raise device_errors.CommandFailedError(str(e)) | 131 raise device_errors.CommandFailedError( |
| 132 ['adb', 'shell', 'echo', '$EXTERNAL_STORAGE'], str(e)) |
132 | 133 |
133 @decorators.WithTimeoutAndRetriesFromInstance() | 134 @decorators.WithTimeoutAndRetriesFromInstance() |
134 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): | 135 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): |
135 """ Wait for the device to fully boot. | 136 """Wait for the device to fully boot. |
136 | 137 |
137 This means waiting for the device to boot, the package manager to be | 138 This means waiting for the device to boot, the package manager to be |
138 available, and the SD card to be ready. It can optionally mean waiting | 139 available, and the SD card to be ready. It can optionally mean waiting |
139 for wifi to come up, too. | 140 for wifi to come up, too. |
140 | 141 |
141 Args: | 142 Args: |
142 wifi: A boolean indicating if we should wait for wifi to come up or not. | 143 wifi: A boolean indicating if we should wait for wifi to come up or not. |
143 timeout: Same as for |IsOnline|. | 144 timeout: Same as for |IsOnline|. |
144 retries: Same as for |IsOnline|. | 145 retries: Same as for |IsOnline|. |
145 Raises: | 146 Raises: |
146 CommandTimeoutError if one of the component waits times out. | 147 CommandTimeoutError if one of the component waits times out. |
147 DeviceUnreachableError if the device becomes unresponsive. | 148 DeviceUnreachableError if the device becomes unresponsive. |
148 """ | 149 """ |
| 150 self._WaitUntilFullyBootedImpl(wifi=wifi, timeout=timeout) |
| 151 |
| 152 def _WaitUntilFullyBootedImpl(self, wifi=False, timeout=None): |
| 153 """ Implementation of WaitUntilFullyBooted. |
| 154 |
| 155 This is split from WaitUntilFullyBooted to allow other DeviceUtils methods |
| 156 to call WaitUntilFullyBooted without spawning a new timeout thread. |
| 157 |
| 158 TODO(jbudorick) Remove the timeout parameter once this is no longer |
| 159 implemented via AndroidCommands. |
| 160 |
| 161 Args: |
| 162 wifi: Same as for |WaitUntilFullyBooted|. |
| 163 timeout: Same as for |IsOnline|. |
| 164 Raises: |
| 165 Same as for |WaitUntilFullyBooted|. |
| 166 """ |
| 167 if timeout is None: |
| 168 timeout = self._default_timeout |
149 self.old_interface.WaitForSystemBootCompleted(timeout) | 169 self.old_interface.WaitForSystemBootCompleted(timeout) |
150 self.old_interface.WaitForDevicePm() | 170 self.old_interface.WaitForDevicePm() |
151 self.old_interface.WaitForSdCardReady(timeout) | 171 self.old_interface.WaitForSdCardReady(timeout) |
152 if wifi: | 172 if wifi: |
153 while not 'Wi-Fi is enabled' in ( | 173 while not 'Wi-Fi is enabled' in ( |
154 self.old_interface.RunShellCommand('dumpsys wifi')): | 174 self.old_interface.RunShellCommand('dumpsys wifi')): |
155 time.sleep(0.1) | 175 time.sleep(0.1) |
156 | 176 |
| 177 @decorators.WithTimeoutAndRetriesDefaults( |
| 178 10 * _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) |
| 179 def Reboot(self, block=True, timeout=None, retries=None): |
| 180 """Reboot the device. |
| 181 |
| 182 Args: |
| 183 block: A boolean indicating if we should wait for the reboot to complete. |
| 184 timeout: Same as for |IsOnline|. |
| 185 retries: Same as for |IsOnline|. |
| 186 """ |
| 187 self.old_interface.Reboot() |
| 188 if block: |
| 189 self._WaitUntilFullyBootedImpl(timeout=timeout) |
| 190 |
| 191 @decorators.WithTimeoutAndRetriesDefaults( |
| 192 4 * _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) |
| 193 def Install(self, apk_path, reinstall=False, timeout=None, retries=None): |
| 194 """Install an APK. |
| 195 |
| 196 Noop if an identical APK is already installed. |
| 197 |
| 198 Args: |
| 199 apk_path: A string containing the path to the APK to install. |
| 200 reinstall: A boolean indicating if we should keep any existing app data. |
| 201 timeout: Same as for |IsOnline|. |
| 202 retries: Same as for |IsOnline|. |
| 203 Raises: |
| 204 CommandFailedError if the installation fails. |
| 205 CommandTimeoutError if the installation times out. |
| 206 """ |
| 207 package_name = apk_helper.GetPackageName(apk_path) |
| 208 device_path = self.old_interface.GetApplicationPath(package_name) |
| 209 if device_path is not None: |
| 210 files_changed = self.old_interface.GetFilesChanged( |
| 211 apk_path, device_path, ignore_filenames=True) |
| 212 if len(files_changed) > 0: |
| 213 should_install = True |
| 214 if not reinstall: |
| 215 out = self.old_interface.Uninstall(package_name) |
| 216 for line in out.splitlines(): |
| 217 if 'Failure' in line: |
| 218 raise device_errors.CommandFailedError( |
| 219 ['adb', 'uninstall', package_name], line.strip()) |
| 220 else: |
| 221 should_install = False |
| 222 else: |
| 223 should_install = True |
| 224 if should_install: |
| 225 try: |
| 226 out = self.old_interface.Install(apk_path, reinstall=reinstall) |
| 227 for line in out.splitlines(): |
| 228 if 'Failure' in line: |
| 229 raise device_errors.CommandFailedError( |
| 230 ['adb', 'install', apk_path], line.strip()) |
| 231 except AssertionError as e: |
| 232 raise device_errors.CommandFailedError( |
| 233 ['adb', 'install', apk_path], str(e)) |
| 234 |
157 def __str__(self): | 235 def __str__(self): |
158 """Returns the device serial.""" | 236 """Returns the device serial.""" |
159 return self.old_interface.GetDevice() | 237 return self.old_interface.GetDevice() |
160 | 238 |
161 @staticmethod | 239 @staticmethod |
162 def parallel(devices=None, async=False): | 240 def parallel(devices=None, async=False): |
163 """ Creates a Parallelizer to operate over the provided list of devices. | 241 """Creates a Parallelizer to operate over the provided list of devices. |
164 | 242 |
165 If |devices| is either |None| or an empty list, the Parallelizer will | 243 If |devices| is either |None| or an empty list, the Parallelizer will |
166 operate over all attached devices. | 244 operate over all attached devices. |
167 | 245 |
168 Args: | 246 Args: |
169 devices: A list of either DeviceUtils instances or objects from | 247 devices: A list of either DeviceUtils instances or objects from |
170 from which DeviceUtils instances can be constructed. If None, | 248 from which DeviceUtils instances can be constructed. If None, |
171 all attached devices will be used. | 249 all attached devices will be used. |
172 async: If true, returns a Parallelizer that runs operations | 250 async: If true, returns a Parallelizer that runs operations |
173 asynchronously. | 251 asynchronously. |
174 Returns: | 252 Returns: |
175 A Parallelizer operating over |devices|. | 253 A Parallelizer operating over |devices|. |
176 """ | 254 """ |
177 if not devices or len(devices) == 0: | 255 if not devices or len(devices) == 0: |
178 devices = pylib.android_commands.GetAttachedDevices() | 256 devices = pylib.android_commands.GetAttachedDevices() |
179 parallelizer_type = (parallelizer.Parallelizer if async | 257 parallelizer_type = (parallelizer.Parallelizer if async |
180 else parallelizer.SyncParallelizer) | 258 else parallelizer.SyncParallelizer) |
181 return parallelizer_type([ | 259 return parallelizer_type([ |
182 d if isinstance(d, DeviceUtils) else DeviceUtils(d) | 260 d if isinstance(d, DeviceUtils) else DeviceUtils(d) |
183 for d in devices]) | 261 for d in devices]) |
184 | 262 |
OLD | NEW |