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 17 import os 18 import os.path 19 import zipfile 20 21 import common 22 import test_utils 23 from add_img_to_target_files import ( 24 AddCareMapForAbOta, AddPackRadioImages, AppendVBMetaArgsForPartition, 25 CheckAbOtaImages, GetCareMap) 26 from rangelib import RangeSet 27 28 29 OPTIONS = common.OPTIONS 30 31 32 class AddImagesToTargetFilesTest(test_utils.ReleaseToolsTestCase): 33 34 def setUp(self): 35 OPTIONS.input_tmp = common.MakeTempDir() 36 37 def _verifyCareMap(self, expected, file_name): 38 """Parses the care_map.pb; and checks the content in plain text.""" 39 text_file = common.MakeTempFile(prefix="caremap-", suffix=".txt") 40 41 # Calls an external binary to convert the proto message. 42 cmd = ["care_map_generator", "--parse_proto", file_name, text_file] 43 common.RunAndCheckOutput(cmd) 44 45 with open(text_file, 'r') as verify_fp: 46 plain_text = verify_fp.read() 47 self.assertEqual('\n'.join(expected), plain_text) 48 49 @staticmethod 50 def _create_images(images, prefix): 51 """Creates images under OPTIONS.input_tmp/prefix.""" 52 path = os.path.join(OPTIONS.input_tmp, prefix) 53 if not os.path.exists(path): 54 os.mkdir(path) 55 56 for image in images: 57 image_path = os.path.join(path, image + '.img') 58 with open(image_path, 'wb') as image_fp: 59 image_fp.write(image.encode()) 60 61 images_path = os.path.join(OPTIONS.input_tmp, 'IMAGES') 62 if not os.path.exists(images_path): 63 os.mkdir(images_path) 64 return images, images_path 65 66 def test_CheckAbOtaImages_imageExistsUnderImages(self): 67 """Tests the case with existing images under IMAGES/.""" 68 images, _ = self._create_images(['aboot', 'xbl'], 'IMAGES') 69 CheckAbOtaImages(None, images) 70 71 def test_CheckAbOtaImages_imageExistsUnderRadio(self): 72 """Tests the case with some image under RADIO/.""" 73 images, _ = self._create_images(['system', 'vendor'], 'IMAGES') 74 radio_path = os.path.join(OPTIONS.input_tmp, 'RADIO') 75 if not os.path.exists(radio_path): 76 os.mkdir(radio_path) 77 with open(os.path.join(radio_path, 'modem.img'), 'wb') as image_fp: 78 image_fp.write('modem'.encode()) 79 CheckAbOtaImages(None, images + ['modem']) 80 81 def test_CheckAbOtaImages_missingImages(self): 82 images, _ = self._create_images(['aboot', 'xbl'], 'RADIO') 83 self.assertRaises( 84 AssertionError, CheckAbOtaImages, None, images + ['baz']) 85 86 def test_AddPackRadioImages(self): 87 images, images_path = self._create_images(['foo', 'bar'], 'RADIO') 88 AddPackRadioImages(None, images) 89 90 for image in images: 91 self.assertTrue( 92 os.path.exists(os.path.join(images_path, image + '.img'))) 93 94 def test_AddPackRadioImages_with_suffix(self): 95 images, images_path = self._create_images(['foo', 'bar'], 'RADIO') 96 images_with_suffix = [image + '.img' for image in images] 97 AddPackRadioImages(None, images_with_suffix) 98 99 for image in images: 100 self.assertTrue( 101 os.path.exists(os.path.join(images_path, image + '.img'))) 102 103 def test_AddPackRadioImages_zipOutput(self): 104 images, _ = self._create_images(['foo', 'bar'], 'RADIO') 105 106 # Set up the output zip. 107 output_file = common.MakeTempFile(suffix='.zip') 108 with zipfile.ZipFile(output_file, 'w') as output_zip: 109 AddPackRadioImages(output_zip, images) 110 111 with zipfile.ZipFile(output_file, 'r') as verify_zip: 112 for image in images: 113 self.assertIn('IMAGES/' + image + '.img', verify_zip.namelist()) 114 115 def test_AddPackRadioImages_imageExists(self): 116 images, images_path = self._create_images(['foo', 'bar'], 'RADIO') 117 118 # Additionally create images under IMAGES/ so that they should be skipped. 119 images, images_path = self._create_images(['foo', 'bar'], 'IMAGES') 120 121 AddPackRadioImages(None, images) 122 123 for image in images: 124 self.assertTrue( 125 os.path.exists(os.path.join(images_path, image + '.img'))) 126 127 def test_AddPackRadioImages_missingImages(self): 128 images, _ = self._create_images(['foo', 'bar'], 'RADIO') 129 AddPackRadioImages(None, images) 130 131 self.assertRaises(AssertionError, AddPackRadioImages, None, 132 images + ['baz']) 133 134 @staticmethod 135 def _test_AddCareMapForAbOta(): 136 """Helper function to set up the test for test_AddCareMapForAbOta().""" 137 OPTIONS.info_dict = { 138 'system_verity_block_device': '/dev/block/system', 139 'vendor_verity_block_device': '/dev/block/vendor', 140 'system.build.prop': { 141 'ro.system.build.fingerprint': 142 'google/sailfish/12345:user/dev-keys', 143 }, 144 'vendor.build.prop': { 145 'ro.vendor.build.fingerprint': 'google/sailfish/678:user/dev-keys', 146 } 147 } 148 149 # Prepare the META/ folder. 150 meta_path = os.path.join(OPTIONS.input_tmp, 'META') 151 if not os.path.exists(meta_path): 152 os.mkdir(meta_path) 153 154 system_image = test_utils.construct_sparse_image([ 155 (0xCAC1, 6), 156 (0xCAC3, 4), 157 (0xCAC1, 6)]) 158 vendor_image = test_utils.construct_sparse_image([ 159 (0xCAC2, 10)]) 160 161 image_paths = { 162 'system' : system_image, 163 'vendor' : vendor_image, 164 } 165 return image_paths 166 167 def test_AddCareMapForAbOta(self): 168 image_paths = self._test_AddCareMapForAbOta() 169 170 AddCareMapForAbOta(None, ['system', 'vendor'], image_paths) 171 172 care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') 173 expected = ['system', RangeSet("0-5 10-15").to_string_raw(), 174 "ro.system.build.fingerprint", 175 "google/sailfish/12345:user/dev-keys", 176 'vendor', RangeSet("0-9").to_string_raw(), 177 "ro.vendor.build.fingerprint", 178 "google/sailfish/678:user/dev-keys"] 179 180 self._verifyCareMap(expected, care_map_file) 181 182 def test_AddCareMapForAbOta_withNonCareMapPartitions(self): 183 """Partitions without care_map should be ignored.""" 184 image_paths = self._test_AddCareMapForAbOta() 185 186 AddCareMapForAbOta( 187 None, ['boot', 'system', 'vendor', 'vbmeta'], image_paths) 188 189 care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') 190 expected = ['system', RangeSet("0-5 10-15").to_string_raw(), 191 "ro.system.build.fingerprint", 192 "google/sailfish/12345:user/dev-keys", 193 'vendor', RangeSet("0-9").to_string_raw(), 194 "ro.vendor.build.fingerprint", 195 "google/sailfish/678:user/dev-keys"] 196 197 self._verifyCareMap(expected, care_map_file) 198 199 def test_AddCareMapForAbOta_withAvb(self): 200 """Tests the case for device using AVB.""" 201 image_paths = self._test_AddCareMapForAbOta() 202 OPTIONS.info_dict = { 203 'avb_system_hashtree_enable' : 'true', 204 'avb_vendor_hashtree_enable' : 'true', 205 'system.build.prop': { 206 'ro.system.build.fingerprint': 207 'google/sailfish/12345:user/dev-keys', 208 }, 209 'vendor.build.prop': { 210 'ro.vendor.build.fingerprint': 'google/sailfish/678:user/dev-keys', 211 } 212 } 213 214 AddCareMapForAbOta(None, ['system', 'vendor'], image_paths) 215 216 care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') 217 expected = ['system', RangeSet("0-5 10-15").to_string_raw(), 218 "ro.system.build.fingerprint", 219 "google/sailfish/12345:user/dev-keys", 220 'vendor', RangeSet("0-9").to_string_raw(), 221 "ro.vendor.build.fingerprint", 222 "google/sailfish/678:user/dev-keys"] 223 224 self._verifyCareMap(expected, care_map_file) 225 226 def test_AddCareMapForAbOta_noFingerprint(self): 227 """Tests the case for partitions without fingerprint.""" 228 image_paths = self._test_AddCareMapForAbOta() 229 OPTIONS.info_dict = { 230 'system_verity_block_device': '/dev/block/system', 231 'vendor_verity_block_device': '/dev/block/vendor', 232 } 233 234 AddCareMapForAbOta(None, ['system', 'vendor'], image_paths) 235 236 care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') 237 expected = ['system', RangeSet("0-5 10-15").to_string_raw(), "unknown", 238 "unknown", 'vendor', RangeSet("0-9").to_string_raw(), "unknown", 239 "unknown"] 240 241 self._verifyCareMap(expected, care_map_file) 242 243 def test_AddCareMapForAbOta_withThumbprint(self): 244 """Tests the case for partitions with thumbprint.""" 245 image_paths = self._test_AddCareMapForAbOta() 246 OPTIONS.info_dict = { 247 'system_verity_block_device': '/dev/block/system', 248 'vendor_verity_block_device': '/dev/block/vendor', 249 'system.build.prop': { 250 'ro.system.build.thumbprint': 'google/sailfish/123:user/dev-keys', 251 }, 252 'vendor.build.prop' : { 253 'ro.vendor.build.thumbprint': 'google/sailfish/456:user/dev-keys', 254 } 255 } 256 257 AddCareMapForAbOta(None, ['system', 'vendor'], image_paths) 258 259 care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') 260 expected = ['system', RangeSet("0-5 10-15").to_string_raw(), 261 "ro.system.build.thumbprint", 262 "google/sailfish/123:user/dev-keys", 263 'vendor', RangeSet("0-9").to_string_raw(), 264 "ro.vendor.build.thumbprint", 265 "google/sailfish/456:user/dev-keys"] 266 267 self._verifyCareMap(expected, care_map_file) 268 269 def test_AddCareMapForAbOta_verityNotEnabled(self): 270 """No care_map.pb should be generated if verity not enabled.""" 271 image_paths = self._test_AddCareMapForAbOta() 272 OPTIONS.info_dict = {} 273 AddCareMapForAbOta(None, ['system', 'vendor'], image_paths) 274 275 care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') 276 self.assertFalse(os.path.exists(care_map_file)) 277 278 def test_AddCareMapForAbOta_missingImageFile(self): 279 """Missing image file should be considered fatal.""" 280 image_paths = self._test_AddCareMapForAbOta() 281 image_paths['vendor'] = '' 282 self.assertRaises(AssertionError, AddCareMapForAbOta, None, 283 ['system', 'vendor'], image_paths) 284 285 def test_AddCareMapForAbOta_zipOutput(self): 286 """Tests the case with ZIP output.""" 287 image_paths = self._test_AddCareMapForAbOta() 288 289 output_file = common.MakeTempFile(suffix='.zip') 290 with zipfile.ZipFile(output_file, 'w') as output_zip: 291 AddCareMapForAbOta(output_zip, ['system', 'vendor'], image_paths) 292 293 care_map_name = "META/care_map.pb" 294 temp_dir = common.MakeTempDir() 295 with zipfile.ZipFile(output_file, 'r') as verify_zip: 296 self.assertTrue(care_map_name in verify_zip.namelist()) 297 verify_zip.extract(care_map_name, path=temp_dir) 298 299 expected = ['system', RangeSet("0-5 10-15").to_string_raw(), 300 "ro.system.build.fingerprint", 301 "google/sailfish/12345:user/dev-keys", 302 'vendor', RangeSet("0-9").to_string_raw(), 303 "ro.vendor.build.fingerprint", 304 "google/sailfish/678:user/dev-keys"] 305 self._verifyCareMap(expected, os.path.join(temp_dir, care_map_name)) 306 307 def test_AddCareMapForAbOta_zipOutput_careMapEntryExists(self): 308 """Tests the case with ZIP output which already has care_map entry.""" 309 image_paths = self._test_AddCareMapForAbOta() 310 311 output_file = common.MakeTempFile(suffix='.zip') 312 with zipfile.ZipFile(output_file, 'w') as output_zip: 313 # Create an existing META/care_map.pb entry. 314 common.ZipWriteStr(output_zip, 'META/care_map.pb', 315 'dummy care_map.pb') 316 317 # Request to add META/care_map.pb again. 318 AddCareMapForAbOta(output_zip, ['system', 'vendor'], image_paths) 319 320 # The one under OPTIONS.input_tmp must have been replaced. 321 care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb') 322 expected = ['system', RangeSet("0-5 10-15").to_string_raw(), 323 "ro.system.build.fingerprint", 324 "google/sailfish/12345:user/dev-keys", 325 'vendor', RangeSet("0-9").to_string_raw(), 326 "ro.vendor.build.fingerprint", 327 "google/sailfish/678:user/dev-keys"] 328 329 self._verifyCareMap(expected, care_map_file) 330 331 # The existing entry should be scheduled to be replaced. 332 self.assertIn('META/care_map.pb', OPTIONS.replace_updated_files_list) 333 334 def test_AppendVBMetaArgsForPartition(self): 335 OPTIONS.info_dict = {} 336 cmd = [] 337 AppendVBMetaArgsForPartition(cmd, 'system', '/path/to/system.img') 338 self.assertEqual( 339 ['--include_descriptors_from_image', '/path/to/system.img'], cmd) 340 341 def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self): 342 testdata_dir = test_utils.get_testdata_dir() 343 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem') 344 OPTIONS.info_dict = { 345 'avb_avbtool': 'avbtool', 346 'avb_vendor_key_path': pubkey, 347 'avb_vendor_rollback_index_location': 5, 348 } 349 cmd = [] 350 AppendVBMetaArgsForPartition(cmd, 'vendor', '/path/to/vendor.img') 351 self.assertEqual(2, len(cmd)) 352 self.assertEqual('--chain_partition', cmd[0]) 353 chained_partition_args = cmd[1].split(':') 354 self.assertEqual(3, len(chained_partition_args)) 355 self.assertEqual('vendor', chained_partition_args[0]) 356 self.assertEqual('5', chained_partition_args[1]) 357 self.assertTrue(os.path.exists(chained_partition_args[2])) 358 359 def test_GetCareMap(self): 360 sparse_image = test_utils.construct_sparse_image([ 361 (0xCAC1, 6), 362 (0xCAC3, 4), 363 (0xCAC1, 6)]) 364 OPTIONS.info_dict = { 365 'system_image_size' : 53248, 366 } 367 name, care_map = GetCareMap('system', sparse_image) 368 self.assertEqual('system', name) 369 self.assertEqual(RangeSet("0-5 10-12").to_string_raw(), care_map) 370 371 def test_GetCareMap_invalidPartition(self): 372 self.assertRaises(AssertionError, GetCareMap, 'oem', None) 373 374 def test_GetCareMap_invalidAdjustedPartitionSize(self): 375 sparse_image = test_utils.construct_sparse_image([ 376 (0xCAC1, 6), 377 (0xCAC3, 4), 378 (0xCAC1, 6)]) 379 OPTIONS.info_dict = { 380 'system_image_size' : -45056, 381 } 382 self.assertRaises(AssertionError, GetCareMap, 'system', sparse_image) 383