From f5b40e3d6400cf09948666b477943fb41ab33fd8 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Thu, 27 Apr 2017 10:07:30 +0100 Subject: [PATCH 1/6] AndroidUtils: Updated Android_Version_Map Added Marshmallow and Nougat SDK versions codes to the version map. --- wlauto/utils/android.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wlauto/utils/android.py b/wlauto/utils/android.py index affb8ade..8ed7bded 100644 --- a/wlauto/utils/android.py +++ b/wlauto/utils/android.py @@ -38,6 +38,9 @@ logger = logging.getLogger('android') # See: # http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels ANDROID_VERSION_MAP = { + 25: 'NOUGAT_MR1', + 24: 'NOUGAT', + 23: 'MARSHMALLOW', 22: 'LOLLIPOP_MR1', 21: 'LOLLIPOP', 20: 'KITKAT_WATCH', From e866cfed1219febb26d867e159c220740bb76196 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Fri, 5 May 2017 11:00:40 +0100 Subject: [PATCH 2/6] AndroidDevice: Add `as_root` flag to `broadcast_media_mounted` Allows the `MEDIA_MOUNTED` broadcast to be performed as root as this now requires elevated permission in android N. --- wlauto/common/android/device.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/wlauto/common/android/device.py b/wlauto/common/android/device.py index 35c8a79d..df7c64d3 100644 --- a/wlauto/common/android/device.py +++ b/wlauto/common/android/device.py @@ -716,12 +716,13 @@ class AndroidDevice(BaseLinuxDevice): # pylint: disable=W0223 except KeyError: return None - def broadcast_media_mounted(self, dirpath): + def broadcast_media_mounted(self, dirpath, as_root=False): """ Force a re-index of the mediaserver cache for the specified directory. """ - command = 'am broadcast -a android.intent.action.MEDIA_MOUNTED -d file://' - self.execute(command + dirpath) + command = 'am broadcast -a android.intent.action.MEDIA_MOUNTED -d file://' + self.execute(command + dirpath, as_root=as_root) + # Internal methods: do not use outside of the class. From b10b5970c34fa3a47e8507970f375079a85066b3 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Thu, 11 May 2017 09:44:49 +0100 Subject: [PATCH 3/6] AndroidDevice: Add a `broadcast_media_scan_file` method In android N it is no longer allowed to trigger a media refresh of a directory without root, therefore this method has been added to trigger a refresh of an individual file. --- wlauto/common/android/device.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/wlauto/common/android/device.py b/wlauto/common/android/device.py index df7c64d3..0687c01b 100644 --- a/wlauto/common/android/device.py +++ b/wlauto/common/android/device.py @@ -716,6 +716,13 @@ class AndroidDevice(BaseLinuxDevice): # pylint: disable=W0223 except KeyError: return None + def broadcast_media_scan_file(self, filepath): + """ + Force a re-index of the mediaserver cache for the specified file. + """ + command = 'am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://' + self.execute(command + filepath) + def broadcast_media_mounted(self, dirpath, as_root=False): """ Force a re-index of the mediaserver cache for the specified directory. From b1da0fe958aaeab51378eaa9e26f980667f40523 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Thu, 11 May 2017 13:48:02 +0100 Subject: [PATCH 4/6] AndroidDevice: Add a common_base_path utility function Adds a utility function to determine the lowest common base path of a passed list of files. --- wlauto/utils/misc.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/wlauto/utils/misc.py b/wlauto/utils/misc.py index 98e79e05..a66e985b 100644 --- a/wlauto/utils/misc.py +++ b/wlauto/utils/misc.py @@ -848,3 +848,16 @@ def memoized(func): return __memo_cache[id_string] return memoize_wrapper + + +def commonprefix(file_list, sep=os.sep): + """ + Find the lowest common base folder of a passed list of files. + """ + common_path = os.path.commonprefix(file_list) + cp_split = common_path.split(sep) + other_split = file_list[0].split(sep) + last = len(cp_split) - 1 + if cp_split[last] != other_split[last]: + cp_split = cp_split[:-1] + return sep.join(cp_split) From e98b653b3e53f398c26f86becaa6297bb359d471 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Thu, 11 May 2017 13:47:36 +0100 Subject: [PATCH 5/6] AndroidDevice: Add `refresh_device_files` method Adds a method to determine the appropriate method of triggering a media refresh of a given list of file based on the devices android version and root status. If a device is running android marshmallow and below or has root, trigger a refresh of the files containing folder otherwise trigger a refresh of each individual file. --- wlauto/common/android/device.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/wlauto/common/android/device.py b/wlauto/common/android/device.py index 0687c01b..452dd1f8 100644 --- a/wlauto/common/android/device.py +++ b/wlauto/common/android/device.py @@ -30,7 +30,7 @@ from wlauto.common.resources import Executable from wlauto.core.resource import NO_ONE from wlauto.common.linux.device import BaseLinuxDevice, PsEntry from wlauto.exceptions import DeviceError, WorkerThreadError, TimeoutError, DeviceNotRespondingError -from wlauto.utils.misc import convert_new_lines, ABI_MAP +from wlauto.utils.misc import convert_new_lines, ABI_MAP, commonprefix from wlauto.utils.types import boolean, regex from wlauto.utils.android import (adb_shell, adb_background_shell, adb_list_devices, adb_command, AndroidProperties, ANDROID_VERSION_MAP) @@ -716,6 +716,19 @@ class AndroidDevice(BaseLinuxDevice): # pylint: disable=W0223 except KeyError: return None + def refresh_device_files(self, file_list): + """ + Depending on the devices android version and root status, determine the + appropriate method of forcing a re-index of the mediaserver cache for a given + list of files. + """ + if self.device.is_rooted or self.device.get_sdk_version() < 24: # MM and below + common_path = commonprefix(file_list, sep=self.device.path.sep) + self.broadcast_media_mounted(common_path, self.device.is_rooted) + else: + for f in file_list: + self.broadcast_media_scan_file(f) + def broadcast_media_scan_file(self, filepath): """ Force a re-index of the mediaserver cache for the specified file. From 51c92cb2f54158ab57df52299a665a14489b9d83 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Fri, 5 May 2017 11:29:30 +0100 Subject: [PATCH 6/6] Workloads: Updated to use new media refresh method Updated the base android workload and google photos workload to pass a list of updated files rather than their directory. --- wlauto/common/android/workload.py | 12 +++++++++--- wlauto/workloads/googlephotos/__init__.py | 11 ++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/wlauto/common/android/workload.py b/wlauto/common/android/workload.py index 7bc4ed55..fa2454ba 100755 --- a/wlauto/common/android/workload.py +++ b/wlauto/common/android/workload.py @@ -653,20 +653,26 @@ class AndroidUxPerfWorkload(AndroidUiAutoBenchmark): def push_assets(self, context): pushed = False + file_list = [] for f in self.deployable_assets: fpath = context.resolver.get(File(self, f)) device_path = self._path_on_device(fpath) if self.force_push_assets or not self.device.file_exists(device_path): self.device.push_file(fpath, device_path, timeout=300) + file_list.append(device_path) pushed = True if pushed: - self.device.broadcast_media_mounted(self.device.working_directory) + self.device.refresh_device_files(file_list) def delete_assets(self): if self.deployable_assets: + file_list = [] for f in self.deployable_assets: - self.device.delete_file(self._path_on_device(f)) - self.device.broadcast_media_mounted(self.device.working_directory) + f = self._path_on_device(f) + self.device.delete_file(f) + file_list.append(f) + self.device.delete_file(f) + self.device.refresh_device_files(file_list) def __init__(self, device, **kwargs): super(AndroidUxPerfWorkload, self).__init__(device, **kwargs) diff --git a/wlauto/workloads/googlephotos/__init__.py b/wlauto/workloads/googlephotos/__init__.py index 4b6e2006..57c5030b 100755 --- a/wlauto/workloads/googlephotos/__init__.py +++ b/wlauto/workloads/googlephotos/__init__.py @@ -83,17 +83,22 @@ class Googlephotos(AndroidUxPerfWorkload): # This is to guarantee ordering and allows the workload to select a specific # image by subfolder, as filenames are not shown easily within the app d = self.device.working_directory + file_list = [] for i, f in enumerate(self.test_images): self.device.execute('mkdir -p {0}/wa-{1}'.format(d, i + 1)) self.device.execute('mv {0}/{2} {0}/wa-{1}/{2}'.format(d, i + 1, f)) + file_list.append('{0}/wa-{1}/{2}'.format(d, i + 1, f)) # Force rescan - self.device.broadcast_media_mounted(self.device.working_directory) + self.device.refresh_device_files(file_list) def teardown(self, context): super(Googlephotos, self).teardown(context) # Remove the subfolders and its content d = self.device.working_directory + file_list = [] for i in xrange(len(self.test_images)): - self.device.execute('rm -rf {0}/wa-{1}'.format(d, i + 1)) + f = '{0}/wa-{1}'.format(d, i + 1) + self.device.execute('rm -rf {}'.format(f)) + file_list.append(f) # Force rescan - self.device.broadcast_media_mounted(self.device.working_directory) + self.device.refresh_device_files(file_list)