Merge "[WifiTetheringTest] Increased the file download size" into oc-dev
diff --git a/acts/framework/acts/libs/ota/__init__.py b/acts/framework/acts/libs/ota/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/libs/ota/__init__.py
diff --git a/acts/framework/acts/libs/ota/ota_runners/__init__.py b/acts/framework/acts/libs/ota/ota_runners/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/libs/ota/ota_runners/__init__.py
diff --git a/acts/framework/acts/libs/ota/ota_runners/ota_runner.py b/acts/framework/acts/libs/ota/ota_runners/ota_runner.py
new file mode 100644
index 0000000..dd58943
--- /dev/null
+++ b/acts/framework/acts/libs/ota/ota_runners/ota_runner.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import time
+
+SL4A_SERVICE_SETUP_TIME = 5
+
+
+class OtaError(Exception):
+ """Raised when an error in the OTA Update process occurs."""
+
+
+class OtaRunner(object):
+ """The base class for all OTA Update Runners."""
+
+ def __init__(self, ota_tool, android_device):
+ self.ota_tool = ota_tool
+ self.android_device = android_device
+ self.serial = self.android_device.serial
+
+ def _update(self):
+ logging.info('Stopping services.')
+ self.android_device.stop_services()
+ logging.info('Beginning tool.')
+ self.ota_tool.update(self)
+ logging.info('Tool finished. Waiting for boot completion.')
+ self.android_device.wait_for_boot_completion()
+ logging.info('Boot completed. Rooting adb.')
+ self.android_device.root_adb()
+ logging.info('Root complete. Installing new SL4A.')
+ output = self.android_device.adb.install('-r %s' % self.get_sl4a_apk)
+ logging.info('SL4A install output: %s' % output)
+ time.sleep(SL4A_SERVICE_SETUP_TIME)
+ logging.info('Starting services.')
+ self.android_device.start_services()
+ logging.info('Services started. Running ota tool cleanup.')
+ self.ota_tool.cleanup(self)
+ logging.info('Cleanup complete.')
+
+ def can_update(self):
+ """Whether or not an update package is available for the device."""
+ return NotImplementedError()
+
+ def get_ota_package(self):
+ raise NotImplementedError()
+
+ def get_sl4a_apk(self):
+ raise NotImplementedError()
+
+
+class SingleUseOtaRunner(OtaRunner):
+ """A single use OtaRunner.
+
+ SingleUseOtaRunners can only be ran once. If a user attempts to run it more
+ than once, an error will be thrown. Users can avoid the error by checking
+ can_update() before calling update().
+ """
+
+ def __init__(self, ota_tool, android_device, ota_package, sl4a_apk):
+ super(SingleUseOtaRunner, self).__init__(ota_tool, android_device)
+ self._ota_package = ota_package
+ self._sl4a_apk = sl4a_apk
+ self._called = False
+
+ def can_update(self):
+ return not self._called
+
+ def update(self):
+ """Starts the update process."""
+ if not self.can_update():
+ raise OtaError('A SingleUseOtaTool instance cannot update a phone '
+ 'multiple times.')
+ self._called = True
+ self._update()
+
+ def get_ota_package(self):
+ return self._ota_package
+
+ def get_sl4a_apk(self):
+ return self._sl4a_apk
+
+
+class MultiUseOtaRunner(OtaRunner):
+ """A multiple use OtaRunner.
+
+ MultiUseOtaRunner can only be ran for as many times as there have been
+ packages provided to them. If a user attempts to run it more than the number
+ of provided packages, an error will be thrown. Users can avoid the error by
+ checking can_update() before calling update().
+ """
+
+ def __init__(self, ota_tool, android_device, ota_packages, sl4a_apks):
+ super(MultiUseOtaRunner, self).__init__(ota_tool, android_device)
+ self._ota_packages = ota_packages
+ self._sl4a_apks = sl4a_apks
+ self.current_update_number = 0
+
+ def can_update(self):
+ return not self.current_update_number == len(self._ota_packages)
+
+ def update(self):
+ """Starts the update process."""
+ if not self.can_update():
+ raise OtaError('This MultiUseOtaRunner has already updated all '
+ 'given packages onto the phone.')
+ self._update()
+ self.current_update_number += 1
+
+ def get_ota_package(self):
+ return self._ota_packages[self.current_update_number]
+
+ def get_sl4a_apk(self):
+ return self._sl4a_apks[self.current_update_number]
diff --git a/acts/framework/acts/libs/ota/ota_runners/ota_runner_factory.py b/acts/framework/acts/libs/ota/ota_runners/ota_runner_factory.py
new file mode 100644
index 0000000..fa6ab19
--- /dev/null
+++ b/acts/framework/acts/libs/ota/ota_runners/ota_runner_factory.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+
+from acts.config_parser import ActsConfigError
+from acts.libs.ota.ota_runners import ota_runner
+from acts.libs.ota.ota_tools import ota_tool_factory
+from acts.libs.ota.ota_tools import adb_sideload_ota_tool
+
+_bound_devices = {}
+
+DEFAULT_OTA_TOOL = adb_sideload_ota_tool.AdbSideloadOtaTool.__name__
+DEFAULT_OTA_COMMAND = 'adb'
+
+
+def create_all_from_configs(config, android_devices):
+ """Creates a new OtaTool for each given AndroidDevice.
+
+ After an OtaTool is assigned to a device, another OtaTool cannot be created
+ for that device. This will prevent OTA Update tests that accidentally flash
+ the same build onto a device more than once.
+
+ Args:
+ config: the ACTS config user_params.
+ android_devices: The devices to run an OTA Update on.
+
+ Returns:
+ A list of OtaRunners responsible for updating the given devices. The
+ indexes match the indexes of the corresponding AndroidDevice in
+ android_devices.
+ """
+ return [create_from_configs(config, ad) for ad in android_devices]
+
+
+def create_from_configs(config, android_device):
+ """Creates a new OtaTool for the given AndroidDevice.
+
+ After an OtaTool is assigned to a device, another OtaTool cannot be created
+ for that device. This will prevent OTA Update tests that accidentally flash
+ the same build onto a device more than once.
+
+ Args:
+ config: the ACTS config user_params.
+ android_device: The device to run the OTA Update on.
+
+ Returns:
+ An OtaRunner responsible for updating the given device.
+ """
+ # Default to adb sideload
+ try:
+ ota_tool_class_name = get_ota_value_from_config(
+ config, 'ota_tool', android_device)
+ except ActsConfigError:
+ ota_tool_class_name = DEFAULT_OTA_TOOL
+
+ if ota_tool_class_name not in config:
+ if ota_tool_class_name is not DEFAULT_OTA_TOOL:
+ raise ActsConfigError(
+ 'If the ota_tool is overloaded, the path to the tool must be '
+ 'added to the ACTS config file under {"OtaToolName": '
+ '"path/to/tool"} (in this case, {"%s": "path/to/tool"}.' %
+ ota_tool_class_name)
+ else:
+ command = DEFAULT_OTA_COMMAND
+ else:
+ command = config[ota_tool_class_name]
+ if type(command) is list:
+ # If file came as a list in the config.
+ if len(command) == 1:
+ command = command[0]
+ else:
+ raise ActsConfigError(
+ 'Config value for "%s" must be either a string or a list '
+ 'of exactly one element' % ota_tool_class_name)
+
+ ota_package = get_ota_value_from_config(config, 'ota_package',
+ android_device)
+ ota_sl4a = get_ota_value_from_config(config, 'ota_sl4a', android_device)
+ if type(ota_sl4a) != type(ota_package):
+ raise ActsConfigError(
+ 'The ota_package and ota_sl4a must either both be strings, or '
+ 'both be lists. Device with serial "%s" has mismatched types.' %
+ android_device.serial)
+ return create(ota_package, ota_sl4a, android_device, ota_tool_class_name,
+ command)
+
+
+def create(ota_package,
+ ota_sl4a,
+ android_device,
+ ota_tool_class_name=DEFAULT_OTA_TOOL,
+ command=DEFAULT_OTA_COMMAND,
+ use_cached_runners=True):
+ """
+ Args:
+ ota_package: A string or list of strings corresponding to the
+ update.zip package location(s) for running an OTA update.
+ ota_sl4a: A string or list of strings corresponding to the
+ sl4a.apk package location(s) for running an OTA update.
+ ota_tool_class_name: The class name for the desired ota_tool
+ command: The command line tool name for the updater
+ android_device: The AndroidDevice to run the OTA Update on.
+ use_cached_runners: Whether or not to use runners cached by previous
+ create calls.
+
+ Returns:
+ An OtaRunner with the given properties from the arguments.
+ """
+ ota_tool = ota_tool_factory.create(ota_tool_class_name, command)
+ return create_from_package(ota_package, ota_sl4a, android_device, ota_tool,
+ use_cached_runners)
+
+
+def create_from_package(ota_package,
+ ota_sl4a,
+ android_device,
+ ota_tool,
+ use_cached_runners=True):
+ """
+ Args:
+ ota_package: A string or list of strings corresponding to the
+ update.zip package location(s) for running an OTA update.
+ ota_sl4a: A string or list of strings corresponding to the
+ sl4a.apk package location(s) for running an OTA update.
+ ota_tool: The OtaTool to be paired with the returned OtaRunner
+ android_device: The AndroidDevice to run the OTA Update on.
+ use_cached_runners: Whether or not to use runners cached by previous
+ create calls.
+
+ Returns:
+ An OtaRunner with the given properties from the arguments.
+ """
+ if android_device in _bound_devices and use_cached_runners:
+ logging.warning('Android device %s has already been assigned an '
+ 'OtaRunner. Returning previously created runner.')
+ return _bound_devices[android_device]
+
+ if type(ota_package) != type(ota_sl4a):
+ raise TypeError(
+ 'The ota_package and ota_sl4a must either both be strings, or '
+ 'both be lists. Device with serial "%s" has requested mismatched '
+ 'types.' % android_device.serial)
+
+ if type(ota_package) is str:
+ runner = ota_runner.SingleUseOtaRunner(ota_tool, android_device,
+ ota_package, ota_sl4a)
+ elif type(ota_package) is list:
+ runner = ota_runner.MultiUseOtaRunner(ota_tool, android_device,
+ ota_package, ota_sl4a)
+ else:
+ raise TypeError('The "ota_package" value in the acts config must be '
+ 'either a list or a string.')
+
+ _bound_devices[android_device] = runner
+ return runner
+
+
+def get_ota_value_from_config(config, key, android_device):
+ """Returns a key for the given AndroidDevice.
+
+ Args:
+ config: The ACTS config
+ key: The base key desired (ota_tool, ota_sl4a, or ota_package)
+ android_device: An AndroidDevice
+
+ Returns: The value at the specified key.
+ Throws: ActsConfigError if the value cannot be determined from the config.
+ """
+ suffix = ''
+ if 'ota_map' in config:
+ if android_device.serial in config['ota_map']:
+ suffix = '_%s' % config['ota_map'][android_device.serial]
+
+ ota_package_key = '%s%s' % (key, suffix)
+ if ota_package_key not in config:
+ if suffix is not '':
+ raise ActsConfigError(
+ 'Asked for an OTA Update without specifying a required value. '
+ '"ota_map" has entry {"%s": "%s"}, but there is no '
+ 'corresponding entry {"%s":"/path/to/file"} found within the '
+ 'ACTS config.' % (android_device.serial, suffix[1:],
+ ota_package_key))
+ else:
+ raise ActsConfigError(
+ 'Asked for an OTA Update without specifying a required value. '
+ '"ota_map" does not exist or have a key for serial "%s", and '
+ 'the default value entry "%s" cannot be found within the ACTS '
+ 'config.' % (android_device.serial, ota_package_key))
+
+ return config[ota_package_key]
diff --git a/acts/framework/acts/libs/ota/ota_tools/__init__.py b/acts/framework/acts/libs/ota/ota_tools/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/acts/framework/acts/libs/ota/ota_tools/__init__.py
diff --git a/acts/framework/acts/libs/ota/ota_tools/adb_sideload_ota_tool.py b/acts/framework/acts/libs/ota/ota_tools/adb_sideload_ota_tool.py
new file mode 100644
index 0000000..f94a762
--- /dev/null
+++ b/acts/framework/acts/libs/ota/ota_tools/adb_sideload_ota_tool.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+
+from acts.libs.ota.ota_tools.ota_tool import OtaTool
+
+# OTA Packages can be upwards of 1 GB. This may take some time to transfer over
+# USB 2.0.
+PUSH_TIMEOUT = 10 * 60
+
+
+class AdbSideloadOtaTool(OtaTool):
+ """Updates an AndroidDevice using adb sideload."""
+
+ def __init__(self, ignored_command):
+ # "command" is ignored. The ACTS adb version is used to prevent
+ # differing adb versions from constantly killing adbd.
+ super(AdbSideloadOtaTool, self).__init__(ignored_command)
+
+ def update(self, ota_runner):
+ logging.info('Rooting adb')
+ ota_runner.android_device.root_adb()
+ logging.info('Rebooting to sideload')
+ ota_runner.android_device.adb.reboot('sideload')
+ ota_runner.android_device.adb.wait_for_sideload()
+ logging.info('Sideloading ota package')
+ package_path = ota_runner.get_ota_package()
+ logging.info('Running adb sideload with package "%s"' % package_path)
+ sideload_result = ota_runner.android_device.adb.sideload(
+ package_path, timeout=PUSH_TIMEOUT)
+ logging.info('Sideload output: %s' % sideload_result)
+ logging.info('Sideload complete. Waiting for device to come back up.')
+ ota_runner.android_device.adb.wait_for_recovery()
+ ota_runner.android_device.adb.reboot()
+ logging.info('Device is up. Update complete.')
diff --git a/acts/framework/acts/libs/ota/ota_tools/ota_tool.py b/acts/framework/acts/libs/ota/ota_tools/ota_tool.py
new file mode 100644
index 0000000..e51fe6b
--- /dev/null
+++ b/acts/framework/acts/libs/ota/ota_tools/ota_tool.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+class OtaTool(object):
+ """A Wrapper for an OTA Update command or tool.
+
+ Each OtaTool acts as a facade to the underlying command or tool used to
+ update the device.
+ """
+
+ def __init__(self, command):
+ """Creates an OTA Update tool with the given properties.
+
+ Args:
+ command: A string that is used as the command line tool
+ """
+ self.command = command
+
+ def update(self, ota_runner):
+ """Begins the OTA Update. Returns after the update has installed.
+
+ Args:
+ ota_runner: The OTA Runner that handles the device information.
+ """
+ raise NotImplementedError()
+
+ def cleanup(self, ota_runner):
+ """A cleanup method for the OTA Tool to run after the update completes.
+
+ Args:
+ ota_runner: The OTA Runner that handles the device information.
+ """
+ pass
diff --git a/acts/framework/acts/libs/ota/ota_tools/ota_tool_factory.py b/acts/framework/acts/libs/ota/ota_tools/ota_tool_factory.py
new file mode 100644
index 0000000..ac81646
--- /dev/null
+++ b/acts/framework/acts/libs/ota/ota_tools/ota_tool_factory.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from acts.libs.ota.ota_tools.adb_sideload_ota_tool import AdbSideloadOtaTool
+from acts.libs.ota.ota_tools.update_device_ota_tool import UpdateDeviceOtaTool
+
+_CONSTRUCTORS = {
+ AdbSideloadOtaTool.__name__: lambda command: AdbSideloadOtaTool(command),
+ UpdateDeviceOtaTool.__name__: lambda command: UpdateDeviceOtaTool(command),
+}
+_constructed_tools = {}
+
+
+def create(ota_tool_class, command):
+ """Returns an OtaTool with the given class name.
+
+ If the tool has already been created, the existing instance will be
+ returned.
+
+ Args:
+ ota_tool_class: the class/type of the tool you wish to use.
+ command: the command line tool being used.
+
+ Returns:
+ An OtaTool.
+ """
+ if ota_tool_class in _constructed_tools:
+ return _constructed_tools[ota_tool_class]
+
+ if ota_tool_class not in _CONSTRUCTORS:
+ raise KeyError('Given Ota Tool class name does not match a known '
+ 'name. Found "%s". Expected any of %s. If this tool '
+ 'does exist, add it to the _CONSTRUCTORS dict in this '
+ 'module.' % (ota_tool_class, _CONSTRUCTORS.keys()))
+
+ new_update_tool = _CONSTRUCTORS[ota_tool_class](command)
+ _constructed_tools[ota_tool_class] = new_update_tool
+
+ return new_update_tool
diff --git a/acts/framework/acts/libs/ota/ota_tools/update_device_ota_tool.py b/acts/framework/acts/libs/ota/ota_tools/update_device_ota_tool.py
new file mode 100644
index 0000000..0ab9091
--- /dev/null
+++ b/acts/framework/acts/libs/ota/ota_tools/update_device_ota_tool.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import os
+import shutil
+import tempfile
+
+from acts.libs.ota.ota_tools import ota_tool
+from acts.libs.proc import job
+from acts import utils
+
+# OTA Packages can be upwards of 1 GB. This may take some time to transfer over
+# USB 2.0. A/B devices must also complete the update in the background.
+UPDATE_TIMEOUT = 20 * 60
+UPDATE_LOCATION = '/data/ota_package/update.zip'
+
+
+class UpdateDeviceOtaTool(ota_tool.OtaTool):
+ """Runs an OTA Update with system/update_engine/scripts/update_device.py."""
+
+ def __init__(self, command):
+ super(UpdateDeviceOtaTool, self).__init__(command)
+
+ self.unzip_path = tempfile.mkdtemp()
+ utils.unzip_maintain_permissions(self.command, self.unzip_path)
+
+ self.command = os.path.join(self.unzip_path, 'update_device.py')
+
+ def update(self, ota_runner):
+ logging.info('Forcing adb to be in root mode.')
+ ota_runner.android_device.root_adb()
+ update_command = '%s -s %s %s' % (self.command, ota_runner.serial,
+ ota_runner.get_ota_package())
+ logging.info('Running %s' % update_command)
+ result = job.run(update_command, timeout=UPDATE_TIMEOUT)
+ logging.info('Output: %s' % result.stdout)
+
+ logging.info('Rebooting device for update to go live.')
+ ota_runner.android_device.adb.reboot()
+ logging.info('Reboot sent.')
+
+ def __del__(self):
+ """Delete the unzipped update_device folder before ACTS exits."""
+ shutil.rmtree(self.unzip_path)
diff --git a/acts/framework/acts/libs/ota/ota_updater.py b/acts/framework/acts/libs/ota/ota_updater.py
new file mode 100644
index 0000000..ed300aa
--- /dev/null
+++ b/acts/framework/acts/libs/ota/ota_updater.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from acts.libs.ota.ota_runners import ota_runner_factory
+
+# Maps AndroidDevices to OtaRunners
+ota_runners = {}
+
+
+def initialize(user_params, android_devices):
+ """Initialize OtaRunners for each device.
+
+ Args:
+ user_params: The user_params from the ACTS config.
+ android_devices: The android_devices in the test.
+ """
+ for ad in android_devices:
+ ota_runners[ad] = ota_runner_factory.create_from_configs(
+ user_params, ad)
+
+
+def _check_initialization(android_device):
+ """Check if a given device was initialized."""
+ if android_device not in ota_runners:
+ raise KeyError('Android Device with serial "%s" has not been '
+ 'initialized for OTA Updates. Did you forget to call'
+ 'ota_updater.initialize()?' % android_device.serial)
+
+
+def update(android_device, ignore_update_errors=False):
+ """Update a given AndroidDevice.
+
+ Args:
+ android_device: The device to update
+ ignore_update_errors: Whether or not to ignore update errors such as
+ no more updates available for a given device. Default is false.
+ Throws:
+ OtaError if ignore_update_errors is false and the OtaRunner has run out
+ of packages to update the phone with.
+ """
+ _check_initialization(android_device)
+ try:
+ ota_runners[android_device].update()
+ except:
+ if ignore_update_errors:
+ return
+ raise
+
+
+def can_update(android_device):
+ """Whether or not a device can be updated."""
+ _check_initialization(android_device)
+ return ota_runners[android_device].can_update()
diff --git a/acts/framework/acts/test_utils/tel/tel_test_utils.py b/acts/framework/acts/test_utils/tel/tel_test_utils.py
index 5732351..c778466 100644
--- a/acts/framework/acts/test_utils/tel/tel_test_utils.py
+++ b/acts/framework/acts/test_utils/tel/tel_test_utils.py
@@ -4575,7 +4575,10 @@
"""
ad.log.debug("Ensuring no tcpdump is running in background")
- ad.adb.shell("killall -9 tcpdump")
+ try:
+ ad.adb.shell("killall -9 tcpdump")
+ except AdbError:
+ self.log.warn("Killing existing tcpdump processes failed")
begin_time = epoch_to_log_line_timestamp(get_current_epoch_time())
begin_time = normalize_log_line_timestamp(begin_time)
file_name = "/sdcard/tcpdump{}{}{}.pcap".format(ad.serial, test_name,
diff --git a/acts/framework/acts/utils.py b/acts/framework/acts/utils.py
index c5d401b..5faabec 100755
--- a/acts/framework/acts/utils.py
+++ b/acts/framework/acts/utils.py
@@ -28,6 +28,7 @@
import subprocess
import time
import traceback
+import zipfile
# File name length is limited to 255 chars on some OS, so we need to make sure
# the file names we output fits within the limit.
@@ -807,3 +808,28 @@
return False
finally:
ad.adb.shell("rm /data/ping.txt", timeout=10, ignore_status=True)
+
+
+def unzip_maintain_permissions(zip_path, extract_location):
+ """Unzip a .zip file while maintaining permissions.
+
+ Args:
+ zip_path: The path to the zipped file.
+ extract_location: the directory to extract to.
+ """
+ with zipfile.ZipFile(zip_path, 'r') as zip_file:
+ for info in zip_file.infolist():
+ _extract_file(zip_file, info, extract_location)
+
+
+def _extract_file(zip_file, zip_info, extract_location):
+ """Extracts a single entry from a ZipFile while maintaining permissions.
+
+ Args:
+ zip_file: A zipfile.ZipFile.
+ zip_info: A ZipInfo object from zip_file.
+ extract_location: The directory to extract to.
+ """
+ out_path = zip_file.extract(zip_info.filename, path=extract_location)
+ perm = zip_info.external_attr >> 16
+ os.chmod(out_path, perm)
diff --git a/acts/tests/google/wifi/WifiEnterpriseTest.py b/acts/tests/google/wifi/WifiEnterpriseTest.py
index b1a5391..785d91f 100755
--- a/acts/tests/google/wifi/WifiEnterpriseTest.py
+++ b/acts/tests/google/wifi/WifiEnterpriseTest.py
@@ -22,6 +22,8 @@
from acts import base_test
from acts import signals
from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.tel_test_utils import start_adb_tcpdump
+from acts.test_utils.tel.tel_test_utils import stop_adb_tcpdump
from acts.test_utils.wifi import wifi_test_utils as wutils
WifiEnums = wutils.WifiEnums
@@ -127,6 +129,8 @@
del self.config_passpoint_ttls[WifiEnums.SSID_KEY]
# Set screen lock password so ConfigStore is unlocked.
self.dut.droid.setDevicePassword(self.device_password)
+ self.tcpdump_pid = None
+ self.tcpdump_file = None
def teardown_class(self):
wutils.reset_wifi(self.dut)
@@ -139,8 +143,16 @@
self.dut.droid.wakeUpNow()
wutils.reset_wifi(self.dut)
self.dut.ed.clear_all_events()
+ (self.tcpdump_pid, self.tcpdump_file) = start_adb_tcpdump(
+ self.dut, self.test_name, mask='all')
def teardown_test(self):
+ if self.tcpdump_pid:
+ stop_adb_tcpdump(self.dut,
+ self.tcpdump_pid,
+ self.tcpdump_file,
+ pull_tcpdump=True)
+ self.tcpdump_pid = None
self.dut.droid.wakeLockRelease()
self.dut.droid.goToSleepNow()
self.dut.droid.wifiStopTrackingStateChange()
diff --git a/acts/tests/sample/OtaSampleTest.py b/acts/tests/sample/OtaSampleTest.py
new file mode 100644
index 0000000..aeb735e
--- /dev/null
+++ b/acts/tests/sample/OtaSampleTest.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2017 - The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from acts import base_test
+from acts.libs.ota import ota_updater
+
+
+class OtaSampleTest(base_test.BaseTestClass):
+ """Demonstrates an example OTA Update test."""
+
+ def setup_class(self):
+ ota_updater.initialize(self.user_params, self.android_devices)
+ self.dut = self.android_devices[0]
+
+ def test_my_test(self):
+ self.pre_ota()
+ ota_updater.update(self.dut)
+ self.post_ota()
+
+ def pre_ota(self):
+ pass
+
+ def post_ota(self):
+ pass