def test_AddCareMapForAbOta_zipOutput_careMapEntryExists(self): """Tests the case with ZIP output which already has care_map entry.""" image_paths = self._test_AddCareMapForAbOta() output_file = common.MakeTempFile(suffix='.zip') with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip: # Create an existing META/care_map.pb entry. common.ZipWriteStr(output_zip, 'META/care_map.pb', 'fake care_map.pb') # Request to add META/care_map.pb again. AddCareMapForAbOta(output_zip, ['system', 'vendor'], image_paths) # The one under OPTIONS.input_tmp must have been replaced. care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') expected = ['system', RangeSet("0-5 10-15").to_string_raw(), "ro.system.build.fingerprint", "google/sailfish/12345:user/dev-keys", 'vendor', RangeSet("0-9").to_string_raw(), "ro.vendor.build.fingerprint", "google/sailfish/678:user/dev-keys"] self._verifyCareMap(expected, care_map_file) # The existing entry should be scheduled to be replaced. self.assertIn('META/care_map.pb', OPTIONS.replace_updated_files_list)
def test_AddCareMapForAbOta_withThumbprint(self): """Tests the case for partitions with thumbprint.""" image_paths = self._test_AddCareMapForAbOta() OPTIONS.info_dict = { 'extfs_sparse_flag': '-s', 'system_image_size': 65536, 'vendor_image_size': 40960, 'system_verity_block_device': '/dev/block/system', 'vendor_verity_block_device': '/dev/block/vendor', 'system.build.prop': common.PartitionBuildProps.FromDictionary( 'system', { 'ro.system.build.thumbprint': 'google/sailfish/123:user/dev-keys'} ), 'vendor.build.prop': common.PartitionBuildProps.FromDictionary( 'vendor', { 'ro.vendor.build.thumbprint': 'google/sailfish/456:user/dev-keys'} ), } care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths) expected = ['system', RangeSet("0-5 10-15").to_string_raw(), "ro.system.build.thumbprint", "google/sailfish/123:user/dev-keys", 'vendor', RangeSet("0-9").to_string_raw(), "ro.vendor.build.thumbprint", "google/sailfish/456:user/dev-keys"] self._verifyCareMap(expected, care_map_file)
def test_AddCareMapForAbOta_withAvb(self): """Tests the case for device using AVB.""" image_paths = self._test_AddCareMapForAbOta() OPTIONS.info_dict = { 'extfs_sparse_flag': '-s', 'system_image_size': 65536, 'vendor_image_size': 40960, 'avb_system_hashtree_enable': 'true', 'avb_vendor_hashtree_enable': 'true', 'system.build.prop': common.PartitionBuildProps.FromDictionary( 'system', { 'ro.system.build.fingerprint': 'google/sailfish/12345:user/dev-keys'} ), 'vendor.build.prop': common.PartitionBuildProps.FromDictionary( 'vendor', { 'ro.vendor.build.fingerprint': 'google/sailfish/678:user/dev-keys'} ), } care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths) expected = ['system', RangeSet("0-5 10-15").to_string_raw(), "ro.system.build.fingerprint", "google/sailfish/12345:user/dev-keys", 'vendor', RangeSet("0-9").to_string_raw(), "ro.vendor.build.fingerprint", "google/sailfish/678:user/dev-keys"] self._verifyCareMap(expected, care_map_file)
def test_AddCareMapForAbOta_verityNotEnabled(self): """No care_map.pb should be generated if verity not enabled.""" image_paths = self._test_AddCareMapForAbOta() OPTIONS.info_dict = {} care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths) self.assertFalse(os.path.exists(care_map_file))
def test_AddCareMapForAbOta_skipAllPartitions(self): image_paths = self._test_AddCareMapForAbOta() # Remove the image_size properties for all the partitions. del OPTIONS.info_dict['system_image_size'] del OPTIONS.info_dict['vendor_image_size'] care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths) self.assertFalse(os.path.exists(care_map_file))
def test_AddCareMapForAbOta_skipPartition(self): image_paths = self._test_AddCareMapForAbOta() # Remove vendor_image_size to invalidate the care_map for vendor.img. del OPTIONS.info_dict['vendor_image_size'] care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths) expected = ['system', RangeSet("0-5 10-15").to_string_raw(), "ro.system.build.fingerprint", "google/sailfish/12345:user/dev-keys"] self._verifyCareMap(expected, care_map_file)
def test_AddCareMapForAbOta(self): image_paths = self._test_AddCareMapForAbOta() care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths) expected = ['system', RangeSet("0-5 10-15").to_string_raw(), "ro.system.build.fingerprint", "google/sailfish/12345:user/dev-keys", 'vendor', RangeSet("0-9").to_string_raw(), "ro.vendor.build.fingerprint", "google/sailfish/678:user/dev-keys"] self._verifyCareMap(expected, care_map_file)
def test_AddCareMapForAbOta_withNonCareMapPartitions(self): """Partitions without care_map should be ignored.""" image_paths = self._test_AddCareMapForAbOta() care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') AddCareMapForAbOta( care_map_file, ['boot', 'system', 'vendor', 'vbmeta'], image_paths) expected = ['system', RangeSet("0-5 10-15").to_string_raw(), "ro.system.build.fingerprint", "google/sailfish/12345:user/dev-keys", 'vendor', RangeSet("0-9").to_string_raw(), "ro.vendor.build.fingerprint", "google/sailfish/678:user/dev-keys"] self._verifyCareMap(expected, care_map_file)
def test_AddCareMapForAbOta_noFingerprint(self): """Tests the case for partitions without fingerprint.""" image_paths = self._test_AddCareMapForAbOta() OPTIONS.info_dict = { 'extfs_sparse_flag' : '-s', 'system_image_size' : 65536, 'vendor_image_size' : 40960, 'system_verity_block_device': '/dev/block/system', 'vendor_verity_block_device': '/dev/block/vendor', } care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths) expected = ['system', RangeSet("0-5 10-15").to_string_raw(), "unknown", "unknown", 'vendor', RangeSet("0-9").to_string_raw(), "unknown", "unknown"] self._verifyCareMap(expected, care_map_file)
def test_AddCareMapForAbOta_zipOutput(self): """Tests the case with ZIP output.""" image_paths = self._test_AddCareMapForAbOta() output_file = common.MakeTempFile(suffix='.zip') with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip: AddCareMapForAbOta(output_zip, ['system', 'vendor'], image_paths) care_map_name = "META/care_map.pb" temp_dir = common.MakeTempDir() with zipfile.ZipFile(output_file, 'r', allowZip64=True) as verify_zip: self.assertTrue(care_map_name in verify_zip.namelist()) verify_zip.extract(care_map_name, path=temp_dir) expected = ['system', RangeSet("0-5 10-15").to_string_raw(), "ro.system.build.fingerprint", "google/sailfish/12345:user/dev-keys", 'vendor', RangeSet("0-9").to_string_raw(), "ro.vendor.build.fingerprint", "google/sailfish/678:user/dev-keys"] self._verifyCareMap(expected, os.path.join(temp_dir, care_map_name))
def AddImagesToTargetFiles(filename): """Creates and adds images (boot/recovery/system/...) to a target_files.zip. It works with either a zip file (zip mode), or a directory that contains the files to be packed into a target_files.zip (dir mode). The latter is used when being called from build/make/core/Makefile. The images will be created under IMAGES/ in the input target_files.zip. Args: filename: the target_files.zip, or the zip root directory. """ if os.path.isdir(filename): OPTIONS.input_tmp = os.path.abspath(filename) else: OPTIONS.input_tmp = common.UnzipTemp(filename) if not OPTIONS.add_missing: if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")): logger.warning("target_files appears to already contain images.") sys.exit(1) OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, repacking=True) has_recovery = OPTIONS.info_dict.get("no_recovery") != "true" has_boot = OPTIONS.info_dict.get("no_boot") != "true" has_vendor_boot = OPTIONS.info_dict.get("vendor_boot") == "true" # {vendor,odm,product,system_ext,vendor_dlkm,odm_dlkm, system, system_other}.img # can be built from source, or dropped into target_files.zip as a prebuilt blob. has_vendor = HasPartition("vendor") has_odm = HasPartition("odm") has_vendor_dlkm = HasPartition("vendor_dlkm") has_odm_dlkm = HasPartition("odm_dlkm") has_product = HasPartition("product") has_system_ext = HasPartition("system_ext") has_system = HasPartition("system") has_system_other = HasPartition("system_other") has_userdata = OPTIONS.info_dict.get("building_userdata_image") == "true" has_cache = OPTIONS.info_dict.get("building_cache_image") == "true" # Set up the output destination. It writes to the given directory for dir # mode; otherwise appends to the given ZIP. if os.path.isdir(filename): output_zip = None else: output_zip = zipfile.ZipFile(filename, "a", compression=zipfile.ZIP_DEFLATED, allowZip64=True) # Always make input_tmp/IMAGES available, since we may stage boot / recovery # images there even under zip mode. The directory will be cleaned up as part # of OPTIONS.input_tmp. images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES") if not os.path.isdir(images_dir): os.makedirs(images_dir) # A map between partition names and their paths, which could be used when # generating AVB vbmeta image. partitions = {} def banner(s): logger.info("\n\n++++ %s ++++\n\n", s) boot_image = None if has_boot: banner("boot") boot_images = OPTIONS.info_dict.get("boot_images") if boot_images is None: boot_images = "boot.img" for index, b in enumerate(boot_images.split()): # common.GetBootableImage() returns the image directly if present. boot_image = common.GetBootableImage("IMAGES/" + b, b, OPTIONS.input_tmp, "BOOT") # boot.img may be unavailable in some targets (e.g. aosp_arm64). if boot_image: boot_image_path = os.path.join(OPTIONS.input_tmp, "IMAGES", b) # Although multiple boot images can be generated, include the image # descriptor of only the first boot image in vbmeta if index == 0: partitions['boot'] = boot_image_path if not os.path.exists(boot_image_path): boot_image.WriteToDir(OPTIONS.input_tmp) if output_zip: boot_image.AddToZip(output_zip) if has_vendor_boot: banner("vendor_boot") vendor_boot_image = common.GetVendorBootImage("IMAGES/vendor_boot.img", "vendor_boot.img", OPTIONS.input_tmp, "VENDOR_BOOT") if vendor_boot_image: partitions['vendor_boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES", "vendor_boot.img") if not os.path.exists(partitions['vendor_boot']): vendor_boot_image.WriteToDir(OPTIONS.input_tmp) if output_zip: vendor_boot_image.AddToZip(output_zip) recovery_image = None if has_recovery: banner("recovery") recovery_image = common.GetBootableImage("IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") assert recovery_image, "Failed to create recovery.img." partitions['recovery'] = os.path.join(OPTIONS.input_tmp, "IMAGES", "recovery.img") if not os.path.exists(partitions['recovery']): recovery_image.WriteToDir(OPTIONS.input_tmp) if output_zip: recovery_image.AddToZip(output_zip) banner("recovery (two-step image)") # The special recovery.img for two-step package use. recovery_two_step_image = common.GetBootableImage( "OTA/recovery-two-step.img", "recovery-two-step.img", OPTIONS.input_tmp, "RECOVERY", two_step_image=True) assert recovery_two_step_image, "Failed to create recovery-two-step.img." recovery_two_step_image_path = os.path.join( OPTIONS.input_tmp, "OTA", "recovery-two-step.img") if not os.path.exists(recovery_two_step_image_path): recovery_two_step_image.WriteToDir(OPTIONS.input_tmp) if output_zip: recovery_two_step_image.AddToZip(output_zip) if has_system: banner("system") partitions['system'] = AddSystem(output_zip, recovery_img=recovery_image, boot_img=boot_image) if has_vendor: banner("vendor") partitions['vendor'] = AddVendor(output_zip, recovery_img=recovery_image, boot_img=boot_image) if has_product: banner("product") partitions['product'] = AddProduct(output_zip) if has_system_ext: banner("system_ext") partitions['system_ext'] = AddSystemExt(output_zip) if has_odm: banner("odm") partitions['odm'] = AddOdm(output_zip) if has_vendor_dlkm: banner("vendor_dlkm") partitions['vendor_dlkm'] = AddVendorDlkm(output_zip) if has_odm_dlkm: banner("odm_dlkm") partitions['odm_dlkm'] = AddOdmDlkm(output_zip) if has_system_other: banner("system_other") AddSystemOther(output_zip) AddApexInfo(output_zip) if not OPTIONS.is_signing: banner("userdata") AddUserdata(output_zip) banner("cache") AddCache(output_zip) if OPTIONS.info_dict.get("board_bpt_enable") == "true": banner("partition-table") AddPartitionTable(output_zip) if OPTIONS.info_dict.get("has_dtbo") == "true": banner("dtbo") partitions['dtbo'] = AddDtbo(output_zip) if OPTIONS.info_dict.get("has_pvmfw") == "true": banner("pvmfw") partitions['pvmfw'] = AddPvmfw(output_zip) # Custom images. custom_partitions = OPTIONS.info_dict.get( "avb_custom_images_partition_list", "").strip().split() for partition_name in custom_partitions: partition_name = partition_name.strip() banner("custom images for " + partition_name) partitions[partition_name] = AddCustomImages(output_zip, partition_name) if OPTIONS.info_dict.get("avb_enable") == "true": # vbmeta_partitions includes the partitions that should be included into # top-level vbmeta.img, which are the ones that are not included in any # chained VBMeta image plus the chained VBMeta images themselves. # Currently custom_partitions are all chained to VBMeta image. vbmeta_partitions = common.AVB_PARTITIONS[:] + tuple(custom_partitions) vbmeta_system = OPTIONS.info_dict.get("avb_vbmeta_system", "").strip() if vbmeta_system: banner("vbmeta_system") partitions["vbmeta_system"] = AddVBMeta(output_zip, partitions, "vbmeta_system", vbmeta_system.split()) vbmeta_partitions = [ item for item in vbmeta_partitions if item not in vbmeta_system.split() ] vbmeta_partitions.append("vbmeta_system") vbmeta_vendor = OPTIONS.info_dict.get("avb_vbmeta_vendor", "").strip() if vbmeta_vendor: banner("vbmeta_vendor") partitions["vbmeta_vendor"] = AddVBMeta(output_zip, partitions, "vbmeta_vendor", vbmeta_vendor.split()) vbmeta_partitions = [ item for item in vbmeta_partitions if item not in vbmeta_vendor.split() ] vbmeta_partitions.append("vbmeta_vendor") if OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true": banner("vbmeta") AddVBMeta(output_zip, partitions, "vbmeta", vbmeta_partitions) if OPTIONS.info_dict.get("use_dynamic_partitions") == "true": if OPTIONS.info_dict.get("build_super_empty_partition") == "true": banner("super_empty") AddSuperEmpty(output_zip) if OPTIONS.info_dict.get("build_super_partition") == "true": if OPTIONS.info_dict.get( "build_retrofit_dynamic_partitions_ota_package") == "true": banner("super split images") AddSuperSplit(output_zip) banner("radio") ab_partitions_txt = os.path.join(OPTIONS.input_tmp, "META", "ab_partitions.txt") if os.path.exists(ab_partitions_txt): with open(ab_partitions_txt) as f: ab_partitions = f.readlines() # For devices using A/B update, make sure we have all the needed images # ready under IMAGES/ or RADIO/. CheckAbOtaImages(output_zip, ab_partitions) # Generate care_map.pb for ab_partitions, then write this file to # target_files package. output_care_map = os.path.join(OPTIONS.input_tmp, "META", "care_map.pb") AddCareMapForAbOta(output_zip if output_zip else output_care_map, ab_partitions, partitions) # Radio images that need to be packed into IMAGES/, and product-img.zip. pack_radioimages_txt = os.path.join(OPTIONS.input_tmp, "META", "pack_radioimages.txt") if os.path.exists(pack_radioimages_txt): with open(pack_radioimages_txt) as f: AddPackRadioImages(output_zip, f.readlines()) # Calculate the vbmeta digest and put the result in to META/ boot_images = OPTIONS.info_dict.get("boot_images") # Disable the digest calculation if the target_file is used as a container # for boot images. boot_container = boot_images and len(boot_images.split()) >= 2 if (OPTIONS.info_dict.get("avb_enable") == "true" and not boot_container and OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true"): avbtool = OPTIONS.info_dict["avb_avbtool"] digest = verity_utils.CalculateVbmetaDigest(OPTIONS.input_tmp, avbtool) vbmeta_digest_txt = os.path.join(OPTIONS.input_tmp, "META", "vbmeta_digest.txt") with open(vbmeta_digest_txt, 'w') as f: f.write(digest) if output_zip: common.ZipClose(output_zip) if OPTIONS.replace_updated_files_list: ReplaceUpdatedFiles(output_zip.filename, OPTIONS.replace_updated_files_list)