summaryrefslogtreecommitdiff
path: root/harnesses/host_controller/command_processor/command_flash.py
blob: b8a2d7e9c731f57ddea5d913efe8ff0bfbfcbc81 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#
# Copyright (C) 2018 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 importlib
import os
import stat

from host_controller import common
from host_controller.build import build_flasher
from host_controller.command_processor import base_command_processor


class CommandFlash(base_command_processor.BaseCommandProcessor):
    """Command processor for flash command.

    Attributes:
        arg_parser: ConsoleArgumentParser object, argument parser.
        console: cmd.Cmd console object.
        command: string, command name which this processor will handle.
        command_detail: string, detailed explanation for the command.
    """

    command = "flash"
    command_detail = "Flash images to a device."

    # @Override
    def SetUp(self):
        """Initializes the parser for flash command."""
        self.arg_parser.add_argument(
            "--image",
            help=("The file name of an image to flash."
                  " Used to flash a single image."))
        self.arg_parser.add_argument(
            "--current",
            metavar="PARTITION_IMAGE",
            nargs="*",
            type=lambda x: x.split("="),
            help="The partitions and images to be flashed. The format is "
            "<partition>=<image>. If PARTITION_IMAGE list is empty, "
            "currently fetched " + ", ".join(common._DEFAULT_FLASH_IMAGES) +
            " will be flashed.")
        self.arg_parser.add_argument(
            "--serial", default="", help="Serial number for device.")
        self.arg_parser.add_argument(
            "--build_dir",
            help="Directory containing build images to be flashed.")
        self.arg_parser.add_argument(
            "--gsi", help="Path to generic system image")
        self.arg_parser.add_argument("--vbmeta", help="Path to vbmeta image")
        self.arg_parser.add_argument(
            "--flasher_type",
            default="fastboot",
            help="Flasher type. Valid arguments are \"fastboot\", \"custom\", "
            "and full module name followed by class name. The class must "
            "inherit build_flasher.BuildFlasher, and implement "
            "__init__(serial, flasher_path) and "
            "Flash(device_images, additional_files, *flasher_args).")
        self.arg_parser.add_argument(
            "--flasher_path", default=None, help="Path to a flasher binary")
        self.arg_parser.add_argument(
            "flasher_args",
            metavar="ARGUMENTS",
            nargs="*",
            help="The arguments passed to the flasher binary. If any argument "
            "starts with \"-\", place all of them after \"--\" at end of "
            "line.")
        self.arg_parser.add_argument(
            "--reboot_mode",
            default="bootloader",
            choices=("bootloader", "download"),
            help="Reboot device to bootloader/download mode")
        self.arg_parser.add_argument(
            "--repackage",
            default="tar.md5",
            choices=("tar.md5"),
            help="Repackage artifacts into given format before flashing.")
        self.arg_parser.add_argument(
            "--wait-for-boot",
            default="true",
            help="false to not wait for device booting.")
        self.arg_parser.add_argument(
            "--reboot", default="false", help="true to reboot the device(s).")
        self.arg_parser.add_argument(
            "--skip-vbmeta",
            default=False,
            type=bool,
            help="true to skip flashing vbmeta.img if the device does not have "
            "the vbmeta slot .")

    # @Override
    def Run(self, arg_line):
        """Flash GSI or build images to a device connected with ADB."""
        args = self.arg_parser.ParseLine(arg_line)

        # path
        if (self.console.tools_info is not None
                and args.flasher_path in self.console.tools_info):
            flasher_path = self.console.tools_info[args.flasher_path]
        elif args.flasher_path:
            flasher_path = args.flasher_path
        else:
            flasher_path = ""
        if os.path.exists(flasher_path):
            flasher_mode = os.stat(flasher_path).st_mode
            os.chmod(flasher_path,
                     flasher_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)

        # serial numbers
        if args.serial:
            flasher_serials = [args.serial]
        elif self.console._serials:
            flasher_serials = self.console._serials
        else:
            flasher_serials = [""]

        # images
        if args.image:
            partition_image = {}
            partition_image[args.image] = self.console.device_image_info[
                args.image]
        else:
            if args.current:
                partition_image = dict((partition,
                                        self.console.device_image_info[image])
                                       for partition, image in args.current)
            else:
                partition_image = dict(
                    (image.rsplit(".img", 1)[0],
                     self.console.device_image_info[image])
                    for image in common._DEFAULT_FLASH_IMAGES
                    if image in self.console.device_image_info)

        # type
        if args.flasher_type in ("fastboot", "custom"):
            flasher_class = build_flasher.BuildFlasher
        else:
            class_path = args.flasher_type.rsplit(".", 1)
            flasher_module = importlib.import_module(class_path[0])
            flasher_class = getattr(flasher_module, class_path[1])
            if not issubclass(flasher_class, build_flasher.BuildFlasher):
                raise TypeError(
                    "%s is not a subclass of BuildFlasher." % class_path[1])

        flashers = [flasher_class(s, flasher_path) for s in flasher_serials]

        # Can be parallelized as long as that's proven reliable.
        for flasher in flashers:
            ret_flash = True
            if args.flasher_type == "fastboot":
                if args.image is not None:
                    ret_flash = flasher.FlashImage(partition_image, True
                                                   if args.reboot == "true"
                                                   else False)
                elif args.current is not None:
                    ret_flash = flasher.Flash(partition_image,
                                              args.skip_vbmeta)
                else:
                    if args.gsi is None and args.build_dir is None:
                        self.arg_parser.error("Nothing requested: "
                                              "specify --gsi or --build_dir")
                        return False
                    if args.build_dir is not None:
                        ret_flash = flasher.Flashall(args.build_dir)
                    if args.gsi is not None:
                        ret_flash = flasher.FlashGSI(
                            args.gsi,
                            args.vbmeta,
                            skip_vbmeta=args.skip_vbmeta)
            elif args.flasher_type == "custom":
                if flasher_path is not None:
                    if args.repackage is not None:
                        flasher.RepackageArtifacts(
                            self.console.device_image_info, args.repackage)
                    ret_flash = flasher.FlashUsingCustomBinary(
                        self.console.device_image_info, args.reboot_mode,
                        args.flasher_args, 300)
                else:
                    self.arg_parser.error(
                        "Please specify the path to custom flash tool.")
                    return False
            else:
                ret_flash = flasher.Flash(partition_image,
                                          self.console.tools_info,
                                          *args.flasher_args)
            if ret_flash == False:
                return False

        if args.wait_for_boot == "true":
            for flasher in flashers:
                ret_wait = flasher.WaitForDevice()
                if ret_wait == False:
                    self.console.device_status[
                        flasher.device.serial] = common._DEVICE_STATUS_DICT[
                            "error"]
                    self.console.vti_endpoint_client.SetJobStatusFromLeasedTo(
                        "bootup-err")
                    return False