Home | History | Annotate | Download | only in releasetools
      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