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=unused-argument | 9 # pylint: disable=unused-argument |
10 | 10 |
11 import collections | 11 import collections |
12 import itertools | 12 import itertools |
13 import json | |
13 import logging | 14 import logging |
14 import multiprocessing | 15 import multiprocessing |
15 import os | 16 import os |
16 import posixpath | 17 import posixpath |
17 import re | 18 import re |
18 import shutil | 19 import shutil |
19 import tempfile | 20 import tempfile |
20 import time | 21 import time |
21 import zipfile | 22 import zipfile |
22 | 23 |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
156 _MAX_ADB_OUTPUT_LENGTH = 32768 | 157 _MAX_ADB_OUTPUT_LENGTH = 32768 |
157 _LAUNCHER_FOCUSED_RE = re.compile( | 158 _LAUNCHER_FOCUSED_RE = re.compile( |
158 r'\s*mCurrentFocus.*(Launcher|launcher).*') | 159 r'\s*mCurrentFocus.*(Launcher|launcher).*') |
159 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') | 160 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') |
160 | 161 |
161 LOCAL_PROPERTIES_PATH = posixpath.join('/', 'data', 'local.prop') | 162 LOCAL_PROPERTIES_PATH = posixpath.join('/', 'data', 'local.prop') |
162 | 163 |
163 # Property in /data/local.prop that controls Java assertions. | 164 # Property in /data/local.prop that controls Java assertions. |
164 JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions' | 165 JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions' |
165 | 166 |
166 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, | 167 def __init__(self, device, enable_device_files_cache=False, |
jbudorick
2015/10/01 17:15:26
Document this parameter.
agrieve
2015/10/01 18:30:16
Done.
| |
168 default_timeout=_DEFAULT_TIMEOUT, | |
167 default_retries=_DEFAULT_RETRIES): | 169 default_retries=_DEFAULT_RETRIES): |
168 """DeviceUtils constructor. | 170 """DeviceUtils constructor. |
169 | 171 |
170 Args: | 172 Args: |
171 device: Either a device serial, an existing AdbWrapper instance, or an | 173 device: Either a device serial, an existing AdbWrapper instance, or an |
172 an existing AndroidCommands instance. | 174 an existing AndroidCommands instance. |
173 default_timeout: An integer containing the default number of seconds to | 175 default_timeout: An integer containing the default number of seconds to |
174 wait for an operation to complete if no explicit value | 176 wait for an operation to complete if no explicit value |
175 is provided. | 177 is provided. |
176 default_retries: An integer containing the default number or times an | 178 default_retries: An integer containing the default number or times an |
177 operation should be retried on failure if no explicit | 179 operation should be retried on failure if no explicit |
178 value is provided. | 180 value is provided. |
179 """ | 181 """ |
180 self.adb = None | 182 self.adb = None |
181 if isinstance(device, basestring): | 183 if isinstance(device, basestring): |
182 self.adb = adb_wrapper.AdbWrapper(device) | 184 self.adb = adb_wrapper.AdbWrapper(device) |
183 elif isinstance(device, adb_wrapper.AdbWrapper): | 185 elif isinstance(device, adb_wrapper.AdbWrapper): |
184 self.adb = device | 186 self.adb = device |
185 else: | 187 else: |
186 raise ValueError('Unsupported device value: %r' % device) | 188 raise ValueError('Unsupported device value: %r' % device) |
187 self._commands_installed = None | 189 self._commands_installed = None |
188 self._default_timeout = default_timeout | 190 self._default_timeout = default_timeout |
189 self._default_retries = default_retries | 191 self._default_retries = default_retries |
192 self._enable_device_files_cache = enable_device_files_cache | |
190 self._cache = {} | 193 self._cache = {} |
191 self._client_caches = {} | 194 self._client_caches = {} |
192 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR) | 195 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR) |
193 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR) | 196 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR) |
194 | 197 |
195 self._ClearCache() | 198 self._ClearCache() |
196 | 199 |
197 def __eq__(self, other): | 200 def __eq__(self, other): |
198 """Checks whether |other| refers to the same device as |self|. | 201 """Checks whether |other| refers to the same device as |self|. |
199 | 202 |
(...skipping 895 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1095 track_stale: whether to bother looking for stale files (slower) | 1098 track_stale: whether to bother looking for stale files (slower) |
1096 | 1099 |
1097 Returns: | 1100 Returns: |
1098 a three-element tuple | 1101 a three-element tuple |
1099 1st element: a list of (host_files_path, device_files_path) tuples to push | 1102 1st element: a list of (host_files_path, device_files_path) tuples to push |
1100 2nd element: a list of host_files_path that are up-to-date | 1103 2nd element: a list of host_files_path that are up-to-date |
1101 3rd element: a list of stale files under device_path, or [] when | 1104 3rd element: a list of stale files under device_path, or [] when |
1102 track_stale == False | 1105 track_stale == False |
1103 """ | 1106 """ |
1104 try: | 1107 try: |
1105 host_checksums = md5sum.CalculateHostMd5Sums([host_path]) | 1108 specific_device_paths = [device_path] |
1106 interesting_device_paths = [device_path] | 1109 ignore_other_files = not track_stale and os.path.isdir(host_path) |
1107 if not track_stale and os.path.isdir(host_path): | 1110 if ignore_other_files: |
1108 interesting_device_paths = [ | 1111 specific_device_paths = [] |
1109 posixpath.join(device_path, os.path.relpath(p, host_path)) | 1112 for root, _, filenames in os.walk(host_path): |
1110 for p in host_checksums.keys()] | 1113 relative_dir = root[len(host_path) + 1:] |
1111 device_checksums = md5sum.CalculateDeviceMd5Sums( | 1114 specific_device_paths.extend( |
1112 interesting_device_paths, self) | 1115 posixpath.join(device_path, relative_dir, f) for f in filenames) |
1116 | |
1117 def device_sums_helper(): | |
1118 if self._enable_device_files_cache: | |
1119 cache_entry = self._cache['device_path_checksums'].get(device_path) | |
1120 if cache_entry and cache_entry[0] == ignore_other_files: | |
1121 return dict(cache_entry[1]) | |
1122 | |
1123 sums = md5sum.CalculateDeviceMd5Sums(specific_device_paths, self) | |
1124 | |
1125 if self._enable_device_files_cache: | |
1126 cache_entry = [ignore_other_files, sums] | |
1127 self._cache['device_path_checksums'][device_path] = cache_entry | |
1128 return dict(sums) | |
1129 | |
1130 host_checksums, device_checksums = reraiser_thread.RunAsync(( | |
1131 lambda: md5sum.CalculateHostMd5Sums([host_path]), | |
1132 device_sums_helper)) | |
1113 except EnvironmentError as e: | 1133 except EnvironmentError as e: |
1114 logging.warning('Error calculating md5: %s', e) | 1134 logging.warning('Error calculating md5: %s', e) |
1115 return ([(host_path, device_path)], [], []) | 1135 return ([(host_path, device_path)], [], []) |
1116 | 1136 |
1117 to_push = [] | 1137 to_push = [] |
1118 up_to_date = [] | 1138 up_to_date = [] |
1119 to_delete = [] | 1139 to_delete = [] |
1120 if os.path.isfile(host_path): | 1140 if os.path.isfile(host_path): |
1121 host_checksum = host_checksums.get(host_path) | 1141 host_checksum = host_checksums.get(host_path) |
1122 device_checksum = device_checksums.get(device_path) | 1142 device_checksum = device_checksums.get(device_path) |
(...skipping 787 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1910 """Clears all caches.""" | 1930 """Clears all caches.""" |
1911 for client in self._client_caches: | 1931 for client in self._client_caches: |
1912 self._client_caches[client].clear() | 1932 self._client_caches[client].clear() |
1913 self._cache = { | 1933 self._cache = { |
1914 # Map of packageId -> list of on-device .apk paths | 1934 # Map of packageId -> list of on-device .apk paths |
1915 'package_apk_paths': {}, | 1935 'package_apk_paths': {}, |
1916 # Map of packageId -> set of on-device .apk checksums | 1936 # Map of packageId -> set of on-device .apk checksums |
1917 'package_apk_checksums': {}, | 1937 'package_apk_checksums': {}, |
1918 # Map of property_name -> value | 1938 # Map of property_name -> value |
1919 'getprop': {}, | 1939 'getprop': {}, |
1940 # Map of device_path -> [ignore_other_files, map of path->checksum] | |
jbudorick
2015/10/01 17:15:26
Isn't this just a map of device_path -> checksum?
agrieve
2015/10/01 18:30:16
The comment is correct. The reason it's not a map
| |
1941 'device_path_checksums': {}, | |
1920 } | 1942 } |
1921 | 1943 |
1944 def LoadCacheData(self, data): | |
1945 """Initializes the cache from data created using DumpCacheData.""" | |
1946 obj = json.loads(data) | |
1947 self._cache['package_apk_paths'] = obj.get('package_apk_paths', {}) | |
1948 package_apk_checksums = obj.get('package_apk_checksums', {}) | |
1949 for k, v in package_apk_checksums.iteritems(): | |
1950 package_apk_checksums[k] = set(v) | |
1951 self._cache['package_apk_checksums'] = package_apk_checksums | |
1952 device_path_checksums = obj.get('device_path_checksums', {}) | |
1953 self._cache['device_path_checksums'] = device_path_checksums | |
1954 | |
1955 def DumpCacheData(self): | |
1956 """Dumps the current cache state to a string.""" | |
1957 obj = {} | |
1958 obj['package_apk_paths'] = self._cache['package_apk_paths'] | |
1959 obj['package_apk_checksums'] = self._cache['package_apk_checksums'] | |
1960 # JSON can't handle sets. | |
1961 for k, v in obj['package_apk_checksums'].iteritems(): | |
1962 obj['package_apk_checksums'][k] = list(v) | |
1963 obj['device_path_checksums'] = self._cache['device_path_checksums'] | |
1964 return json.dumps(obj, separators=(',', ':')) | |
1965 | |
1922 @classmethod | 1966 @classmethod |
1923 def parallel(cls, devices, async=False): | 1967 def parallel(cls, devices, async=False): |
1924 """Creates a Parallelizer to operate over the provided list of devices. | 1968 """Creates a Parallelizer to operate over the provided list of devices. |
1925 | 1969 |
1926 If |devices| is either |None| or an empty list, the Parallelizer will | 1970 If |devices| is either |None| or an empty list, the Parallelizer will |
1927 operate over all attached devices that have not been blacklisted. | 1971 operate over all attached devices that have not been blacklisted. |
1928 | 1972 |
1929 Args: | 1973 Args: |
1930 devices: A list of either DeviceUtils instances or objects from | 1974 devices: A list of either DeviceUtils instances or objects from |
1931 from which DeviceUtils instances can be constructed. If None, | 1975 from which DeviceUtils instances can be constructed. If None, |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1979 if ('android.permission.WRITE_EXTERNAL_STORAGE' in permissions | 2023 if ('android.permission.WRITE_EXTERNAL_STORAGE' in permissions |
1980 and 'android.permission.READ_EXTERNAL_STORAGE' not in permissions): | 2024 and 'android.permission.READ_EXTERNAL_STORAGE' not in permissions): |
1981 permissions.append('android.permission.READ_EXTERNAL_STORAGE') | 2025 permissions.append('android.permission.READ_EXTERNAL_STORAGE') |
1982 cmd = ';'.join('pm grant %s %s' %(package, p) for p in permissions) | 2026 cmd = ';'.join('pm grant %s %s' %(package, p) for p in permissions) |
1983 if cmd: | 2027 if cmd: |
1984 output = self.RunShellCommand(cmd) | 2028 output = self.RunShellCommand(cmd) |
1985 if output: | 2029 if output: |
1986 logging.warning('Possible problem when granting permissions. Blacklist ' | 2030 logging.warning('Possible problem when granting permissions. Blacklist ' |
1987 'may need to be updated.') | 2031 'may need to be updated.') |
1988 logging.warning(output) | 2032 logging.warning(output) |
OLD | NEW |