1#!/usr/bin/env python3
2#
3#   Copyright 2017 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import logging
18
19from acts.config_parser import ActsConfigError
20from acts.libs.ota.ota_runners import ota_runner
21from acts.libs.ota.ota_tools import ota_tool_factory
22from acts.libs.ota.ota_tools import adb_sideload_ota_tool
23
24_bound_devices = {}
25
26DEFAULT_OTA_TOOL = adb_sideload_ota_tool.AdbSideloadOtaTool.__name__
27DEFAULT_OTA_COMMAND = 'adb'
28
29
30def create_all_from_configs(config, android_devices):
31    """Creates a new OtaTool for each given AndroidDevice.
32
33    After an OtaTool is assigned to a device, another OtaTool cannot be created
34    for that device. This will prevent OTA Update tests that accidentally flash
35    the same build onto a device more than once.
36
37    Args:
38        config: the ACTS config user_params.
39        android_devices: The devices to run an OTA Update on.
40
41    Returns:
42        A list of OtaRunners responsible for updating the given devices. The
43        indexes match the indexes of the corresponding AndroidDevice in
44        android_devices.
45    """
46    return [create_from_configs(config, ad) for ad in android_devices]
47
48
49def create_from_configs(config, android_device):
50    """Creates a new OtaTool for the given AndroidDevice.
51
52    After an OtaTool is assigned to a device, another OtaTool cannot be created
53    for that device. This will prevent OTA Update tests that accidentally flash
54    the same build onto a device more than once.
55
56    Args:
57        config: the ACTS config user_params.
58        android_device: The device to run the OTA Update on.
59
60    Returns:
61        An OtaRunner responsible for updating the given device.
62    """
63    # Default to adb sideload
64    try:
65        ota_tool_class_name = get_ota_value_from_config(
66            config, 'ota_tool', android_device)
67    except ActsConfigError:
68        ota_tool_class_name = DEFAULT_OTA_TOOL
69
70    if ota_tool_class_name not in config:
71        if ota_tool_class_name is not DEFAULT_OTA_TOOL:
72            raise ActsConfigError(
73                'If the ota_tool is overloaded, the path to the tool must be '
74                'added to the ACTS config file under {"OtaToolName": '
75                '"path/to/tool"} (in this case, {"%s": "path/to/tool"}.' %
76                ota_tool_class_name)
77        else:
78            command = DEFAULT_OTA_COMMAND
79    else:
80        command = config[ota_tool_class_name]
81        if type(command) is list:
82            # If file came as a list in the config.
83            if len(command) == 1:
84                command = command[0]
85            else:
86                raise ActsConfigError(
87                    'Config value for "%s" must be either a string or a list '
88                    'of exactly one element' % ota_tool_class_name)
89
90    ota_package = get_ota_value_from_config(config, 'ota_package',
91                                            android_device)
92    ota_sl4a = get_ota_value_from_config(config, 'ota_sl4a', android_device)
93    if type(ota_sl4a) != type(ota_package):
94        raise ActsConfigError(
95            'The ota_package and ota_sl4a must either both be strings, or '
96            'both be lists. Device with serial "%s" has mismatched types.' %
97            android_device.serial)
98    return create(ota_package, ota_sl4a, android_device, ota_tool_class_name,
99                  command)
100
101
102def create(ota_package,
103           ota_sl4a,
104           android_device,
105           ota_tool_class_name=DEFAULT_OTA_TOOL,
106           command=DEFAULT_OTA_COMMAND,
107           use_cached_runners=True):
108    """
109    Args:
110        ota_package: A string or list of strings corresponding to the
111            update.zip package location(s) for running an OTA update.
112        ota_sl4a: A string or list of strings corresponding to the
113            sl4a.apk package location(s) for running an OTA update.
114        ota_tool_class_name: The class name for the desired ota_tool
115        command: The command line tool name for the updater
116        android_device: The AndroidDevice to run the OTA Update on.
117        use_cached_runners: Whether or not to use runners cached by previous
118            create calls.
119
120    Returns:
121        An OtaRunner with the given properties from the arguments.
122    """
123    ota_tool = ota_tool_factory.create(ota_tool_class_name, command)
124    return create_from_package(ota_package, ota_sl4a, android_device, ota_tool,
125                               use_cached_runners)
126
127
128def create_from_package(ota_package,
129                        ota_sl4a,
130                        android_device,
131                        ota_tool,
132                        use_cached_runners=True):
133    """
134    Args:
135        ota_package: A string or list of strings corresponding to the
136            update.zip package location(s) for running an OTA update.
137        ota_sl4a: A string or list of strings corresponding to the
138            sl4a.apk package location(s) for running an OTA update.
139        ota_tool: The OtaTool to be paired with the returned OtaRunner
140        android_device: The AndroidDevice to run the OTA Update on.
141        use_cached_runners: Whether or not to use runners cached by previous
142            create calls.
143
144    Returns:
145        An OtaRunner with the given properties from the arguments.
146    """
147    if android_device in _bound_devices and use_cached_runners:
148        logging.warning('Android device %s has already been assigned an '
149                        'OtaRunner. Returning previously created runner.')
150        return _bound_devices[android_device]
151
152    if type(ota_package) != type(ota_sl4a):
153        raise TypeError(
154            'The ota_package and ota_sl4a must either both be strings, or '
155            'both be lists. Device with serial "%s" has requested mismatched '
156            'types.' % android_device.serial)
157
158    if type(ota_package) is str:
159        runner = ota_runner.SingleUseOtaRunner(ota_tool, android_device,
160                                               ota_package, ota_sl4a)
161    elif type(ota_package) is list:
162        runner = ota_runner.MultiUseOtaRunner(ota_tool, android_device,
163                                              ota_package, ota_sl4a)
164    else:
165        raise TypeError('The "ota_package" value in the acts config must be '
166                        'either a list or a string.')
167
168    _bound_devices[android_device] = runner
169    return runner
170
171
172def get_ota_value_from_config(config, key, android_device):
173    """Returns a key for the given AndroidDevice.
174
175    Args:
176        config: The ACTS config
177        key: The base key desired (ota_tool, ota_sl4a, or ota_package)
178        android_device: An AndroidDevice
179
180    Returns: The value at the specified key.
181    Throws: ActsConfigError if the value cannot be determined from the config.
182    """
183    suffix = ''
184    if 'ota_map' in config:
185        if android_device.serial in config['ota_map']:
186            suffix = '_%s' % config['ota_map'][android_device.serial]
187
188    ota_package_key = '%s%s' % (key, suffix)
189    if ota_package_key not in config:
190        if suffix is not '':
191            raise ActsConfigError(
192                'Asked for an OTA Update without specifying a required value. '
193                '"ota_map" has entry {"%s": "%s"}, but there is no '
194                'corresponding entry {"%s":"/path/to/file"} found within the '
195                'ACTS config.' % (android_device.serial, suffix[1:],
196                                  ota_package_key))
197        else:
198            raise ActsConfigError(
199                'Asked for an OTA Update without specifying a required value. '
200                '"ota_map" does not exist or have a key for serial "%s", and '
201                'the default value entry "%s" cannot be found within the ACTS '
202                'config.' % (android_device.serial, ota_package_key))
203
204    return config[ota_package_key]
205