• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2016 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""
6This module provides utilities needed to provision and run test on Android
7devices.
8"""
9
10
11import logging
12import re
13
14import common
15from autotest_lib.client.common_lib import global_config
16
17
18CONFIG = global_config.global_config
19
20def get_config_value_regex(section, regex):
21    """Get config values from global config based on regex of the key.
22
23    @param section: Section of the config, e.g., CLIENT.
24    @param regex: Regular expression of the key pattern.
25
26    @return: A dictionary of all config values matching the regex. Value is
27             assumed to be comma separated, and is converted to a list.
28    """
29    configs = CONFIG.get_config_value_regex(section, regex)
30    result = {}
31    for key, value in configs.items():
32        match = re.match(regex, key)
33        result[match.group(1)] = [v.strip() for v in value.split(',')
34                                  if v.strip()]
35    return result
36
37
38class AndroidAliases(object):
39    """Wrapper class for getting alias names for an android device.
40
41    On android it is only possible to get a devices product name
42    (eg. marlin, sailfish). However a product may have several aliases
43    that it is called by such as the name of its board, or a public name,
44    etc. This wrapper allows for mapping the product name to different
45    aliases.
46
47    Terms:
48        product: The name a device reports itself as.
49        board: The name of the hardware board in a device.
50        alias: Some name a device is called, this includes both product and
51               board.
52    """
53
54    # regex pattern for CLIENT/android_aliases_[product]. For example,
55    # global config can have following config in CLIENT section to indicate that
56    # android product name `zzz` has following aliases.
57    # ['my_board', 'xyz'].
58    # android_board_aliases_zzz: my_board,xyz
59    ALIASES_PATTERN = 'android_aliases_(.*)'
60
61    # A dict of product:aliases for product aliases, can be defined in global
62    # config CLIENT/android_aliases_[product]
63    aliases_map = get_config_value_regex('CLIENT',
64                                         ALIASES_PATTERN)
65
66    # regex pattern for CLIENT/android_board_name[product]. For example,
67    # global config can have following config in CLIENT section to indicate that
68    # android product `zzz` has following board name.
69    # xyz.
70    # android_board_name_zzz: xyz
71    BOARD_NAME_PATTERN = 'android_board_name_(.*)'
72
73
74    # A dict of product:board for product board names, can be defined in global
75    # config CLIENT/android_board_name_[product]
76    board_name_map = get_config_value_regex('CLIENT', BOARD_NAME_PATTERN)
77
78    @classmethod
79    def get_product_aliases(cls, product):
80        """Get all aliases for a android product name.
81
82        Androids can have multiple aliases for a single product. These aliases
83        may be what the device is called in different configs. For example
84        bat has a board name of bat_land. Therefore the product name bat
85        can be referenced as either bat or batland.
86
87        @param product: The name of the product that is reported for a device.
88        @returns: All aliases for that product (including the product name).
89        """
90        aliases = set(cls.aliases_map.get(product, []))
91        aliases.add(cls.get_board_name(product))
92        aliases.add(product)
93
94        return aliases
95
96    @classmethod
97    def get_board_name(cls, product):
98        """Get the board name of a product.
99
100        The board name of an android device is what the hardware is named.
101        In many cases this is the same name as the reported product name,
102        however some devices have boards that differ from the product name.
103
104        @param product: The name of the product.
105        @returns: The board name of the given product.
106        """
107        boards = cls.board_name_map.get(product, None)
108        if boards:
109            return boards[0]
110        return product
111
112
113class AndroidImageFiles(object):
114    """A wrapper class for constants and methods related to image files.
115    """
116
117    BOOTLOADER = 'bootloader.img'
118    RADIO = 'radio.img'
119    BOOT = 'boot.img'
120    SYSTEM = 'system.img'
121    VENDOR = 'vendor.img'
122    VBMETA = 'vbmeta.img'
123    DTBO = 'dtbo.img'
124
125    # Image files not inside the image zip file. These files should be
126    # downloaded directly from devserver.
127    DEFAULT_STANDALONE_IMAGES = [BOOTLOADER, RADIO]
128
129    # Default image files that are packaged in a zip file, e.g.,
130    # shamu-img-123456.zip
131    DEFAULT_ZIPPED_IMAGES = [BOOT, SYSTEM, VENDOR, VBMETA, DTBO]
132
133    # Default image files to be flashed to an Android device.
134    DEFAULT_IMAGES = DEFAULT_STANDALONE_IMAGES + DEFAULT_ZIPPED_IMAGES
135
136    # regex pattern for CLIENT/android_standalone_images_[board]. For example,
137    # global config can have following config in CLIENT section to indicate that
138    # android board `xyz` has following standalone images.
139    # ['bootloader_image', 'radio_image'].
140    # android_standalone_xyz: bootloader.img,radio.img
141    STANDALONE_IMAGES_PATTERN = 'android_standalone_images_(.*)'
142
143    # A dict of board:images for standalone images, can be defined in global
144    # config CLIENT/android_standalone_images_[board]
145    standalone_images_map = get_config_value_regex('CLIENT',
146                                                   STANDALONE_IMAGES_PATTERN)
147
148    # regex pattern for CLIENT/android_standalone_images_[board]. For example,
149    # global config can have following config in CLIENT section to indicate that
150    # android board `xyz` has following standalone images.
151    # ['bootloader_image', 'radio_image'].
152    # android_zipped_xyz: bootloader.img,radio.img
153    ZIPPED_IMAGES_PATTERN = 'android_zipped_images_(.*)'
154
155    # A dict of board:images for zipped images, can be defined in global
156    # config CLIENT/android_zipped_images_[board]
157    zipped_images_map = get_config_value_regex('CLIENT', ZIPPED_IMAGES_PATTERN)
158
159    @classmethod
160    def get_standalone_images(cls, board):
161        """Get a list of standalone image files for given board.
162
163        @param board: Name of the board.
164
165        @return: A list of standalone image files.
166        """
167        if board in cls.standalone_images_map:
168            logging.debug('Found override of standalone image files for board '
169                          '%s: %s', board, cls.standalone_images_map[board])
170            return cls.standalone_images_map[board]
171        else:
172            return cls.DEFAULT_STANDALONE_IMAGES
173
174
175    @classmethod
176    def get_zipped_images(cls, board):
177        """Get a list of image files from zip_images artifact for given board.
178
179        @param board: Name of the board.
180
181        @return: A list of image files from `zip_images`.
182        """
183        if board in cls.zipped_images_map:
184            logging.debug('Found override of zip image files for board '
185                          '%s: %s', board, cls.zipped_images_map[board])
186            return cls.zipped_images_map[board]
187        else:
188            return cls.DEFAULT_ZIPPED_IMAGES
189
190
191class AndroidArtifacts(object):
192    """A wrapper class for constants and methods related to artifacts.
193    """
194
195    BOOTLOADER_IMAGE = 'bootloader_image'
196    DTB = 'dtb'
197    RADIO_IMAGE = 'radio_image'
198    TARGET_FILES = 'target_files'
199    VENDOR_PARTITIONS = 'vendor_partitions'
200    ZIP_IMAGE = 'zip_images'
201
202    # (os, board) = 'artifacts'
203    DEFAULT_ARTIFACTS_MAP = {
204        ('android', 'default'): [BOOTLOADER_IMAGE, RADIO_IMAGE, ZIP_IMAGE],
205        ('brillo', 'default'):  [ZIP_IMAGE, VENDOR_PARTITIONS],
206        ('emulated_brillo', 'default'): [TARGET_FILES, DTB],
207    }
208
209    # Default artifacts for Android provision
210    DEFAULT_ARTIFACTS_TO_BE_STAGED_FOR_IMAGE = (
211            ','.join([BOOTLOADER_IMAGE, RADIO_IMAGE, ZIP_IMAGE]))
212
213    # regex pattern for CLIENT/android_artifacts_[board]. For example, global
214    # config can have following config in CLIENT section to indicate that
215    # android board `xyz` needs to stage artifacts
216    # ['bootloader_image', 'radio_image'] for provision.
217    # android_artifacts_xyz: bootloader_image,radio_image
218    ARTIFACTS_LIST_PATTERN = 'android_artifacts_(.*)'
219
220    # A dict of board:artifacts, can be defined in global config
221    # CLIENT/android_artifacts_[board]
222    artifacts_map = get_config_value_regex('CLIENT', ARTIFACTS_LIST_PATTERN)
223
224    @classmethod
225    def get_artifacts_for_reimage(cls, board, os='android'):
226        """Get artifacts need to be staged for reimage for given board.
227
228        @param board: Name of the board.
229
230        @return: A string of artifacts to be staged.
231        """
232        logging.debug('artifacts for %s %s', os, board)
233        if board in cls.artifacts_map:
234            logging.debug('Found override of artifacts for board %s: %s', board,
235                          cls.artifacts_map[board])
236            artifacts = cls.artifacts_map[board]
237        elif (os, board) in cls.DEFAULT_ARTIFACTS_MAP:
238            artifacts = cls.DEFAULT_ARTIFACTS_MAP[(os, board)]
239        else:
240            artifacts = cls.DEFAULT_ARTIFACTS_MAP[(os, 'default')]
241        logging.debug('found %s', ','.join(artifacts))
242        return ','.join(artifacts)
243