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