1# 2# Copyright (C) 2018 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the 'License'); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an 'AS IS' BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# 16 17import importlib 18import os 19import stat 20 21from host_controller import common 22from host_controller.build import build_flasher 23from host_controller.command_processor import base_command_processor 24 25 26class CommandFlash(base_command_processor.BaseCommandProcessor): 27 """Command processor for flash command. 28 29 Attributes: 30 arg_parser: ConsoleArgumentParser object, argument parser. 31 console: cmd.Cmd console object. 32 command: string, command name which this processor will handle. 33 command_detail: string, detailed explanation for the command. 34 """ 35 36 command = "flash" 37 command_detail = "Flash images to a device." 38 39 # @Override 40 def SetUp(self): 41 """Initializes the parser for flash command.""" 42 self.arg_parser.add_argument( 43 "--image", 44 help=("The file name of an image to flash." 45 " Used to flash a single image.")) 46 self.arg_parser.add_argument( 47 "--current", 48 metavar="PARTITION_IMAGE", 49 nargs="*", 50 type=lambda x: x.split("="), 51 help="The partitions and images to be flashed. The format is " 52 "<partition>=<image>. If PARTITION_IMAGE list is empty, " 53 "currently fetched " + ", ".join(common._DEFAULT_FLASH_IMAGES) + 54 " will be flashed.") 55 self.arg_parser.add_argument( 56 "--serial", default="", help="Serial number for device.") 57 self.arg_parser.add_argument( 58 "--build_dir", 59 help="Directory containing build images to be flashed.") 60 self.arg_parser.add_argument( 61 "--gsi", help="Path to generic system image") 62 self.arg_parser.add_argument("--vbmeta", help="Path to vbmeta image") 63 self.arg_parser.add_argument( 64 "--flasher_type", 65 default="fastboot", 66 help="Flasher type. Valid arguments are \"fastboot\", \"custom\", " 67 "and full module name followed by class name. The class must " 68 "inherit build_flasher.BuildFlasher, and implement " 69 "__init__(serial, flasher_path) and " 70 "Flash(device_images, additional_files, *flasher_args).") 71 self.arg_parser.add_argument( 72 "--flasher_path", default=None, help="Path to a flasher binary") 73 self.arg_parser.add_argument( 74 "flasher_args", 75 metavar="ARGUMENTS", 76 nargs="*", 77 help="The arguments passed to the flasher binary. If any argument " 78 "starts with \"-\", place all of them after \"--\" at end of " 79 "line.") 80 self.arg_parser.add_argument( 81 "--reboot_mode", 82 default="bootloader", 83 choices=("bootloader", "download"), 84 help="Reboot device to bootloader/download mode") 85 self.arg_parser.add_argument( 86 "--repackage", 87 default="tar.md5", 88 choices=("tar.md5"), 89 help="Repackage artifacts into given format before flashing.") 90 self.arg_parser.add_argument( 91 "--wait-for-boot", 92 default="true", 93 help="false to not wait for device booting.") 94 self.arg_parser.add_argument( 95 "--reboot", default="false", help="true to reboot the device(s).") 96 self.arg_parser.add_argument( 97 "--skip-vbmeta", 98 default=False, 99 type=bool, 100 help="true to skip flashing vbmeta.img if the device does not have " 101 "the vbmeta slot .") 102 103 # @Override 104 def Run(self, arg_line): 105 """Flash GSI or build images to a device connected with ADB.""" 106 args = self.arg_parser.ParseLine(arg_line) 107 108 # path 109 if (self.console.tools_info is not None 110 and args.flasher_path in self.console.tools_info): 111 flasher_path = self.console.tools_info[args.flasher_path] 112 elif args.flasher_path: 113 flasher_path = args.flasher_path 114 else: 115 flasher_path = "" 116 if os.path.exists(flasher_path): 117 flasher_mode = os.stat(flasher_path).st_mode 118 os.chmod(flasher_path, 119 flasher_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) 120 121 # serial numbers 122 if args.serial: 123 flasher_serials = [args.serial] 124 elif self.console._serials: 125 flasher_serials = self.console._serials 126 else: 127 flasher_serials = [""] 128 129 # images 130 if args.image: 131 partition_image = {} 132 partition_image[args.image] = self.console.device_image_info[ 133 args.image] 134 else: 135 if args.current: 136 partition_image = dict((partition, 137 self.console.device_image_info[image]) 138 for partition, image in args.current) 139 else: 140 partition_image = dict( 141 (image.rsplit(".img", 1)[0], 142 self.console.device_image_info[image]) 143 for image in common._DEFAULT_FLASH_IMAGES 144 if image in self.console.device_image_info) 145 146 # type 147 if args.flasher_type in ("fastboot", "custom"): 148 flasher_class = build_flasher.BuildFlasher 149 else: 150 class_path = args.flasher_type.rsplit(".", 1) 151 flasher_module = importlib.import_module(class_path[0]) 152 flasher_class = getattr(flasher_module, class_path[1]) 153 if not issubclass(flasher_class, build_flasher.BuildFlasher): 154 raise TypeError( 155 "%s is not a subclass of BuildFlasher." % class_path[1]) 156 157 flashers = [flasher_class(s, flasher_path) for s in flasher_serials] 158 159 # Can be parallelized as long as that's proven reliable. 160 for flasher in flashers: 161 ret_flash = True 162 if args.flasher_type == "fastboot": 163 if args.image is not None: 164 ret_flash = flasher.FlashImage(partition_image, True 165 if args.reboot == "true" 166 else False) 167 elif args.current is not None: 168 ret_flash = flasher.Flash(partition_image, 169 args.skip_vbmeta) 170 else: 171 if args.gsi is None and args.build_dir is None: 172 self.arg_parser.error("Nothing requested: " 173 "specify --gsi or --build_dir") 174 return False 175 if args.build_dir is not None: 176 ret_flash = flasher.Flashall(args.build_dir) 177 if args.gsi is not None: 178 ret_flash = flasher.FlashGSI( 179 args.gsi, 180 args.vbmeta, 181 skip_vbmeta=args.skip_vbmeta) 182 elif args.flasher_type == "custom": 183 if flasher_path is not None: 184 if args.repackage is not None: 185 flasher.RepackageArtifacts( 186 self.console.device_image_info, args.repackage) 187 ret_flash = flasher.FlashUsingCustomBinary( 188 self.console.device_image_info, args.reboot_mode, 189 args.flasher_args, 300) 190 else: 191 self.arg_parser.error( 192 "Please specify the path to custom flash tool.") 193 return False 194 else: 195 ret_flash = flasher.Flash(partition_image, 196 self.console.tools_info, 197 *args.flasher_args) 198 if ret_flash == False: 199 return False 200 201 if args.wait_for_boot == "true": 202 for flasher in flashers: 203 ret_wait = flasher.WaitForDevice() 204 if ret_wait == False: 205 self.console.device_status[ 206 flasher.device.serial] = common._DEVICE_STATUS_DICT[ 207 "error"] 208 self.console.vti_endpoint_client.SetJobStatusFromLeasedTo( 209 "bootup-err") 210 return False 211