Chromium Code Reviews| 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 |