1# Copyright 2021 - The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Utility functions that process goldfish images and arguments.""" 16 17import os 18import shutil 19 20from acloud import errors 21from acloud.internal import constants 22from acloud.internal.lib import ota_tools 23 24 25# File names under working directory. 26_UNPACK_DIR_NAME = "unpacked_boot_img" 27_MIXED_RAMDISK_IMAGE_NAME = "mixed_ramdisk" 28# File names in unpacked boot image. 29_UNPACKED_KERNEL_IMAGE_NAME = "kernel" 30_UNPACKED_RAMDISK_IMAGE_NAME = "ramdisk" 31# File names in a build environment or an SDK repository. 32SYSTEM_QEMU_IMAGE_NAME = "system-qemu.img" 33_SDK_REPO_SYSTEM_IMAGE_NAME = "system.img" 34_MISC_INFO_FILE_NAME = "misc_info.txt" 35_SYSTEM_QEMU_CONFIG_FILE_NAME = "system-qemu-config.txt" 36# File names in the search order of emulator. 37_DISK_IMAGE_NAMES = (SYSTEM_QEMU_IMAGE_NAME, _SDK_REPO_SYSTEM_IMAGE_NAME) 38_KERNEL_IMAGE_NAMES = ("kernel-ranchu", "kernel-ranchu-64", "kernel") 39_RAMDISK_IMAGE_NAMES = ("ramdisk-qemu.img", "ramdisk.img") 40 41 42def _FindFileByNames(parent_dir, names): 43 """Find file under a directory by names. 44 45 Args: 46 parent_dir: The directory to find the file in. 47 names: A list of file names. 48 49 Returns: 50 The path to the first existing file in the list. 51 52 Raises: 53 errors.GetLocalImageError if none of the files exist. 54 """ 55 for name in names: 56 path = os.path.join(parent_dir, name) 57 if os.path.isfile(path): 58 return path 59 raise errors.GetLocalImageError("No %s in %s." % 60 (", ".join(names), parent_dir)) 61 62 63def _UnpackBootImage(output_dir, boot_image_path, ota): 64 """Unpack a boot image and find kernel images. 65 66 Args: 67 output_dir: The directory where the boot image is unpacked. 68 boot_image_path: The path to the boot image. 69 ota: An instance of ota_tools.OtaTools. 70 71 Returns: 72 The kernel image path and the ramdisk image path. 73 74 Raises: 75 errors.GetLocalImageError if the kernel or the ramdisk is not found. 76 """ 77 ota.UnpackBootImg(output_dir, boot_image_path) 78 79 kernel_path = os.path.join(output_dir, _UNPACKED_KERNEL_IMAGE_NAME) 80 ramdisk_path = os.path.join(output_dir, _UNPACKED_RAMDISK_IMAGE_NAME) 81 if not os.path.isfile(kernel_path): 82 raise errors.GetLocalImageError("No kernel in %s." % boot_image_path) 83 if not os.path.isfile(ramdisk_path): 84 raise errors.GetLocalImageError("No ramdisk in %s." % boot_image_path) 85 return kernel_path, ramdisk_path 86 87 88def _MixRamdiskImages(output_path, original_ramdisk_path, 89 boot_ramdisk_path): 90 """Mix an emulator ramdisk with a boot ramdisk. 91 92 An emulator ramdisk consists of a boot ramdisk and a vendor ramdisk. 93 This method overlays a new boot ramdisk on the emulator ramdisk by 94 concatenating them. 95 96 Args: 97 output_path: The path to the output ramdisk. 98 original_ramdisk_path: The path to the emulator ramdisk. 99 boot_ramdisk_path: The path to the boot ramdisk. 100 """ 101 with open(output_path, "wb") as mixed_ramdisk: 102 with open(original_ramdisk_path, "rb") as ramdisk: 103 shutil.copyfileobj(ramdisk, mixed_ramdisk) 104 with open(boot_ramdisk_path, "rb") as ramdisk: 105 shutil.copyfileobj(ramdisk, mixed_ramdisk) 106 107 108def MixWithBootImage(output_dir, image_dir, boot_image_path, ota): 109 """Mix emulator kernel images with a boot image. 110 111 Args: 112 output_dir: The directory containing the output and intermediate files. 113 image_dir: The directory containing emulator kernel and ramdisk images. 114 boot_image_path: The path to the boot image. 115 ota: An instance of ota_tools.OtaTools. 116 117 Returns: 118 The paths to the kernel and ramdisk images in output_dir. 119 120 Raises: 121 errors.GetLocalImageError if any image is not found. 122 """ 123 unpack_dir = os.path.join(output_dir, _UNPACK_DIR_NAME) 124 if os.path.exists(unpack_dir): 125 shutil.rmtree(unpack_dir) 126 os.makedirs(unpack_dir, exist_ok=True) 127 128 kernel_path, boot_ramdisk_path = _UnpackBootImage( 129 unpack_dir, boot_image_path, ota) 130 # The ramdisk unpacked from boot_image_path does not include emulator's 131 # kernel modules. The ramdisk in image_dir contains the modules. This 132 # method mixes the two ramdisks. 133 mixed_ramdisk_path = os.path.join(output_dir, _MIXED_RAMDISK_IMAGE_NAME) 134 original_ramdisk_path = _FindFileByNames(image_dir, _RAMDISK_IMAGE_NAMES) 135 _MixRamdiskImages(mixed_ramdisk_path, original_ramdisk_path, 136 boot_ramdisk_path) 137 return kernel_path, mixed_ramdisk_path 138 139 140def FindKernelImages(image_dir): 141 """Find emulator kernel images in a directory. 142 143 Args: 144 image_dir: The directory to find the images in. 145 146 Returns: 147 The paths to the kernel image and the ramdisk image. 148 149 Raises: 150 errors.GetLocalImageError if any image is not found. 151 """ 152 return (_FindFileByNames(image_dir, _KERNEL_IMAGE_NAMES), 153 _FindFileByNames(image_dir, _RAMDISK_IMAGE_NAMES)) 154 155 156def FindDiskImage(image_dir): 157 """Find an emulator disk image in a directory. 158 159 Args: 160 image_dir: The directory to find the image in. 161 162 Returns: 163 The path to the disk image. 164 165 Raises: 166 errors.GetLocalImageError if the image is not found. 167 """ 168 return _FindFileByNames(image_dir, _DISK_IMAGE_NAMES) 169 170 171def MixWithSystemImage(output_dir, image_dir, system_image_path, ota): 172 """Mix emulator images and a system image into a disk image. 173 174 Args: 175 output_dir: The path to the output directory. 176 image_dir: The input directory that provides images except 177 system.img. 178 system_image_path: The path to the system image. 179 ota: An instance of ota_tools.OtaTools. 180 181 Returns: 182 The path to the mixed disk image in output_dir. 183 184 Raises: 185 errors.GetLocalImageError if any required file is not found. 186 """ 187 os.makedirs(output_dir, exist_ok=True) 188 189 # Create the super image. 190 mixed_super_image_path = os.path.join(output_dir, "mixed_super.img") 191 ota.BuildSuperImage( 192 mixed_super_image_path, 193 _FindFileByNames(image_dir, [_MISC_INFO_FILE_NAME]), 194 lambda partition: ota_tools.GetImageForPartition( 195 partition, image_dir, system=system_image_path)) 196 197 # Create the vbmeta image. 198 vbmeta_image_path = os.path.join(output_dir, "disabled_vbmeta.img") 199 ota.MakeDisabledVbmetaImage(vbmeta_image_path) 200 201 # Create the disk image. 202 disk_image = os.path.join(output_dir, "mixed_disk.img") 203 ota.MkCombinedImg( 204 disk_image, 205 _FindFileByNames(image_dir, [_SYSTEM_QEMU_CONFIG_FILE_NAME]), 206 lambda partition: ota_tools.GetImageForPartition( 207 partition, image_dir, super=mixed_super_image_path, 208 vbmeta=vbmeta_image_path)) 209 return disk_image 210 211 212def ConvertAvdSpecToArgs(avd_spec): 213 """Convert hardware specification to emulator arguments. 214 215 Args: 216 avd_spec: The AvdSpec object. 217 218 Returns: 219 A list of strings, the arguments. 220 """ 221 args = [] 222 if avd_spec.gpu: 223 args.extend(("-gpu", avd_spec.gpu)) 224 225 if not avd_spec.hw_customize: 226 return args 227 228 cores = avd_spec.hw_property.get(constants.HW_ALIAS_CPUS) 229 if cores: 230 args.extend(("-cores", cores)) 231 x_res = avd_spec.hw_property.get(constants.HW_X_RES) 232 y_res = avd_spec.hw_property.get(constants.HW_Y_RES) 233 if x_res and y_res: 234 args.extend(("-skin", ("%sx%s" % (x_res, y_res)))) 235 dpi = avd_spec.hw_property.get(constants.HW_ALIAS_DPI) 236 if dpi: 237 args.extend(("-dpi-device", dpi)) 238 memory_size_mb = avd_spec.hw_property.get(constants.HW_ALIAS_MEMORY) 239 if memory_size_mb: 240 args.extend(("-memory", memory_size_mb)) 241 userdata_size_mb = avd_spec.hw_property.get(constants.HW_ALIAS_DISK) 242 if userdata_size_mb: 243 args.extend(("-partition-size", userdata_size_mb)) 244 245 return args 246