blob: 2e26a0543cb3282a6d6a9ea2b00646b5f6d82318 [file] [log] [blame]
Kelvin Zhangcff4d762020-07-29 16:37:51 -04001# Copyright (C) 2020 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
15import copy
16import itertools
17import os
18import zipfile
19
Tianjiea2076132020-08-19 17:25:32 -070020import ota_metadata_pb2
Kelvin Zhangcff4d762020-07-29 16:37:51 -040021from common import (ZipDelete, ZipClose, OPTIONS, MakeTempFile,
22 ZipWriteStr, BuildInfo, LoadDictionaryFromFile,
23 SignFile, PARTITIONS_WITH_CARE_MAP, PartitionBuildProps)
24
Kelvin Zhang2e417382020-08-20 11:33:11 -040025
26OPTIONS.no_signing = False
27OPTIONS.force_non_ab = False
28OPTIONS.wipe_user_data = False
29OPTIONS.downgrade = False
30OPTIONS.key_passwords = {}
31OPTIONS.package_key = None
32OPTIONS.incremental_source = None
33OPTIONS.retrofit_dynamic_partitions = False
34OPTIONS.output_metadata_path = None
35OPTIONS.boot_variable_file = None
36
Kelvin Zhangcff4d762020-07-29 16:37:51 -040037METADATA_NAME = 'META-INF/com/android/metadata'
Tianjiea2076132020-08-19 17:25:32 -070038METADATA_PROTO_NAME = 'META-INF/com/android/metadata.pb'
Kelvin Zhangcff4d762020-07-29 16:37:51 -040039UNZIP_PATTERN = ['IMAGES/*', 'META/*', 'OTA/*', 'RADIO/*']
40
41
42def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
43 """Finalizes the metadata and signs an A/B OTA package.
44
45 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
46 that contains the offsets and sizes for the ZIP entries. An example
47 property-files string is as follows.
48
49 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
50
51 OTA server can pass down this string, in addition to the package URL, to the
52 system update client. System update client can then fetch individual ZIP
53 entries (ZIP_STORED) directly at the given offset of the URL.
54
55 Args:
56 metadata: The metadata dict for the package.
57 input_file: The input ZIP filename that doesn't contain the package METADATA
58 entry yet.
59 output_file: The final output ZIP filename.
60 needed_property_files: The list of PropertyFiles' to be generated.
61 """
62
63 def ComputeAllPropertyFiles(input_file, needed_property_files):
64 # Write the current metadata entry with placeholders.
65 with zipfile.ZipFile(input_file) as input_zip:
66 for property_files in needed_property_files:
Tianjiea2076132020-08-19 17:25:32 -070067 metadata.property_files[property_files.name] = property_files.Compute(
68 input_zip)
Kelvin Zhangcff4d762020-07-29 16:37:51 -040069 namelist = input_zip.namelist()
70
Tianjiea2076132020-08-19 17:25:32 -070071 if METADATA_NAME in namelist or METADATA_PROTO_NAME in namelist:
72 ZipDelete(input_file, [METADATA_NAME, METADATA_PROTO_NAME])
Kelvin Zhangcff4d762020-07-29 16:37:51 -040073 output_zip = zipfile.ZipFile(input_file, 'a')
74 WriteMetadata(metadata, output_zip)
75 ZipClose(output_zip)
76
77 if OPTIONS.no_signing:
78 return input_file
79
80 prelim_signing = MakeTempFile(suffix='.zip')
81 SignOutput(input_file, prelim_signing)
82 return prelim_signing
83
84 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
85 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
86 for property_files in needed_property_files:
Tianjiea2076132020-08-19 17:25:32 -070087 metadata.property_files[property_files.name] = property_files.Finalize(
88 prelim_signing_zip,
89 len(metadata.property_files[property_files.name]))
Kelvin Zhangcff4d762020-07-29 16:37:51 -040090
91 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
92 # entries, as well as padding the entry headers. We do a preliminary signing
93 # (with an incomplete metadata entry) to allow that to happen. Then compute
94 # the ZIP entry offsets, write back the final metadata and do the final
95 # signing.
96 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
97 try:
98 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
99 except PropertyFiles.InsufficientSpaceException:
100 # Even with the preliminary signing, the entry orders may change
101 # dramatically, which leads to insufficiently reserved space during the
102 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
103 # preliminary signing works, based on the already ordered ZIP entries, to
104 # address the issue.
105 prelim_signing = ComputeAllPropertyFiles(
106 prelim_signing, needed_property_files)
107 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
108
109 # Replace the METADATA entry.
Tianjiea2076132020-08-19 17:25:32 -0700110 ZipDelete(prelim_signing, [METADATA_NAME, METADATA_PROTO_NAME])
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400111 output_zip = zipfile.ZipFile(prelim_signing, 'a')
112 WriteMetadata(metadata, output_zip)
113 ZipClose(output_zip)
114
115 # Re-sign the package after updating the metadata entry.
116 if OPTIONS.no_signing:
117 output_file = prelim_signing
118 else:
119 SignOutput(prelim_signing, output_file)
120
121 # Reopen the final signed zip to double check the streaming metadata.
122 with zipfile.ZipFile(output_file) as output_zip:
123 for property_files in needed_property_files:
Tianjiea2076132020-08-19 17:25:32 -0700124 property_files.Verify(
125 output_zip, metadata.property_files[property_files.name].strip())
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400126
127 # If requested, dump the metadata to a separate file.
128 output_metadata_path = OPTIONS.output_metadata_path
129 if output_metadata_path:
130 WriteMetadata(metadata, output_metadata_path)
131
132
Tianjiea2076132020-08-19 17:25:32 -0700133def WriteMetadata(metadata_proto, output):
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400134 """Writes the metadata to the zip archive or a file.
135
136 Args:
Tianjiea2076132020-08-19 17:25:32 -0700137 metadata_proto: The metadata protobuf for the package.
138 output: A ZipFile object or a string of the output file path. If a string
139 path is given, the metadata in the protobuf format will be written to
140 {output}.pb, e.g. ota_metadata.pb
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400141 """
142
Tianjiea2076132020-08-19 17:25:32 -0700143 metadata_dict = BuildLegacyOtaMetadata(metadata_proto)
144 legacy_metadata = "".join(["%s=%s\n" % kv for kv in
145 sorted(metadata_dict.items())])
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400146 if isinstance(output, zipfile.ZipFile):
Tianjiea2076132020-08-19 17:25:32 -0700147 ZipWriteStr(output, METADATA_PROTO_NAME, metadata_proto.SerializeToString(),
148 compress_type=zipfile.ZIP_STORED)
149 ZipWriteStr(output, METADATA_NAME, legacy_metadata,
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400150 compress_type=zipfile.ZIP_STORED)
151 return
152
Tianjiea2076132020-08-19 17:25:32 -0700153 with open('{}.pb'.format(output), 'w') as f:
154 f.write(metadata_proto.SerializeToString())
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400155 with open(output, 'w') as f:
Tianjiea2076132020-08-19 17:25:32 -0700156 f.write(legacy_metadata)
157
158
159def UpdateDeviceState(device_state, build_info, boot_variable_values,
160 is_post_build):
161 """Update the fields of the DeviceState proto with build info."""
162
Tianjie2bb14862020-08-28 16:24:34 -0700163 def UpdatePartitionStates(partition_states):
164 """Update the per-partition state according to its build.prop"""
165
166 build_info_set = ComputeRuntimeBuildInfos(build_info,
167 boot_variable_values)
168 for partition in PARTITIONS_WITH_CARE_MAP:
169 partition_prop = build_info.info_dict.get(
170 '{}.build.prop'.format(partition))
171 # Skip if the partition is missing, or it doesn't have a build.prop
172 if not partition_prop or not partition_prop.build_props:
173 continue
174
175 partition_state = partition_states.add()
176 partition_state.partition_name = partition
177 # Update the partition's runtime device names and fingerprints
178 partition_devices = set()
179 partition_fingerprints = set()
180 for runtime_build_info in build_info_set:
181 partition_devices.add(
182 runtime_build_info.GetPartitionBuildProp('ro.product.device',
183 partition))
184 partition_fingerprints.add(
185 runtime_build_info.GetPartitionFingerprint(partition))
186
187 partition_state.device.extend(sorted(partition_devices))
188 partition_state.build.extend(sorted(partition_fingerprints))
189
190 # TODO(xunchang) set the boot image's version with kmi. Note the boot
191 # image doesn't have a file map.
192 partition_state.version = build_info.GetPartitionBuildProp(
193 'ro.build.date.utc', partition)
194
195 # TODO(xunchang), we can save a call to ComputeRuntimeBuildInfos.
Tianjiea2076132020-08-19 17:25:32 -0700196 build_devices, build_fingerprints = \
197 CalculateRuntimeDevicesAndFingerprints(build_info, boot_variable_values)
198 device_state.device.extend(sorted(build_devices))
199 device_state.build.extend(sorted(build_fingerprints))
200 device_state.build_incremental = build_info.GetBuildProp(
201 'ro.build.version.incremental')
202
Tianjie2bb14862020-08-28 16:24:34 -0700203 UpdatePartitionStates(device_state.partition_state)
Tianjiea2076132020-08-19 17:25:32 -0700204
205 if is_post_build:
206 device_state.sdk_level = build_info.GetBuildProp(
207 'ro.build.version.sdk')
208 device_state.security_patch_level = build_info.GetBuildProp(
209 'ro.build.version.security_patch')
210 # Use the actual post-timestamp, even for a downgrade case.
211 device_state.timestamp = int(build_info.GetBuildProp('ro.build.date.utc'))
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400212
213
214def GetPackageMetadata(target_info, source_info=None):
Tianjiea2076132020-08-19 17:25:32 -0700215 """Generates and returns the metadata proto.
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400216
Tianjiea2076132020-08-19 17:25:32 -0700217 It generates a ota_metadata protobuf that contains the info to be written
218 into an OTA package (META-INF/com/android/metadata.pb). It also handles the
219 detection of downgrade / data wipe based on the global options.
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400220
221 Args:
222 target_info: The BuildInfo instance that holds the target build info.
223 source_info: The BuildInfo instance that holds the source build info, or
224 None if generating full OTA.
225
226 Returns:
Tianjiea2076132020-08-19 17:25:32 -0700227 A protobuf to be written into package metadata entry.
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400228 """
229 assert isinstance(target_info, BuildInfo)
230 assert source_info is None or isinstance(source_info, BuildInfo)
231
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400232 boot_variable_values = {}
233 if OPTIONS.boot_variable_file:
234 d = LoadDictionaryFromFile(OPTIONS.boot_variable_file)
235 for key, values in d.items():
236 boot_variable_values[key] = [val.strip() for val in values.split(',')]
237
Tianjiea2076132020-08-19 17:25:32 -0700238 metadata_proto = ota_metadata_pb2.OtaMetadata()
239 # TODO(xunchang) some fields, e.g. post-device isn't necessary. We can
240 # consider skipping them if they aren't used by clients.
241 UpdateDeviceState(metadata_proto.postcondition, target_info,
242 boot_variable_values, True)
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400243
244 if target_info.is_ab and not OPTIONS.force_non_ab:
Tianjiea2076132020-08-19 17:25:32 -0700245 metadata_proto.type = ota_metadata_pb2.OtaMetadata.AB
246 metadata_proto.required_cache = 0
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400247 else:
Tianjiea2076132020-08-19 17:25:32 -0700248 metadata_proto.type = ota_metadata_pb2.OtaMetadata.BLOCK
249 # cache requirement will be updated by the non-A/B codes.
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400250
251 if OPTIONS.wipe_user_data:
Tianjiea2076132020-08-19 17:25:32 -0700252 metadata_proto.wipe = True
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400253
254 if OPTIONS.retrofit_dynamic_partitions:
Tianjiea2076132020-08-19 17:25:32 -0700255 metadata_proto.retrofit_dynamic_partitions = True
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400256
257 is_incremental = source_info is not None
258 if is_incremental:
Tianjiea2076132020-08-19 17:25:32 -0700259 UpdateDeviceState(metadata_proto.precondition, source_info,
260 boot_variable_values, False)
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400261 else:
Tianjiea2076132020-08-19 17:25:32 -0700262 metadata_proto.precondition.device.extend(
263 metadata_proto.postcondition.device)
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400264
265 # Detect downgrades and set up downgrade flags accordingly.
266 if is_incremental:
Tianjiea2076132020-08-19 17:25:32 -0700267 HandleDowngradeMetadata(metadata_proto, target_info, source_info)
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400268
Tianjiea2076132020-08-19 17:25:32 -0700269 return metadata_proto
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400270
271
Tianjiea2076132020-08-19 17:25:32 -0700272def BuildLegacyOtaMetadata(metadata_proto):
273 """Converts the metadata proto to a legacy metadata dict.
274
275 This metadata dict is used to build the legacy metadata text file for
276 backward compatibility. We won't add new keys to the legacy metadata format.
277 If new information is needed, we should add it as a new field in OtaMetadata
278 proto definition.
279 """
280
281 separator = '|'
282
283 metadata_dict = {}
284 if metadata_proto.type == ota_metadata_pb2.OtaMetadata.AB:
285 metadata_dict['ota-type'] = 'AB'
286 elif metadata_proto.type == ota_metadata_pb2.OtaMetadata.BLOCK:
287 metadata_dict['ota-type'] = 'BLOCK'
288 if metadata_proto.wipe:
289 metadata_dict['ota-wipe'] = 'yes'
290 if metadata_proto.retrofit_dynamic_partitions:
291 metadata_dict['ota-retrofit-dynamic-partitions'] = 'yes'
292 if metadata_proto.downgrade:
293 metadata_dict['ota-downgrade'] = 'yes'
294
295 metadata_dict['ota-required-cache'] = str(metadata_proto.required_cache)
296
297 post_build = metadata_proto.postcondition
298 metadata_dict['post-build'] = separator.join(post_build.build)
299 metadata_dict['post-build-incremental'] = post_build.build_incremental
300 metadata_dict['post-sdk-level'] = post_build.sdk_level
301 metadata_dict['post-security-patch-level'] = post_build.security_patch_level
302 metadata_dict['post-timestamp'] = str(post_build.timestamp)
303
304 pre_build = metadata_proto.precondition
305 metadata_dict['pre-device'] = separator.join(pre_build.device)
306 # incremental updates
307 if len(pre_build.build) != 0:
308 metadata_dict['pre-build'] = separator.join(pre_build.build)
309 metadata_dict['pre-build-incremental'] = pre_build.build_incremental
310
311 metadata_dict.update(metadata_proto.property_files)
312
313 return metadata_dict
314
315
316def HandleDowngradeMetadata(metadata_proto, target_info, source_info):
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400317 # Only incremental OTAs are allowed to reach here.
318 assert OPTIONS.incremental_source is not None
319
320 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
321 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
322 is_downgrade = int(post_timestamp) < int(pre_timestamp)
323
324 if OPTIONS.downgrade:
325 if not is_downgrade:
326 raise RuntimeError(
327 "--downgrade or --override_timestamp specified but no downgrade "
328 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tianjiea2076132020-08-19 17:25:32 -0700329 metadata_proto.downgrade = True
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400330 else:
331 if is_downgrade:
332 raise RuntimeError(
333 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
334 "Need to specify --override_timestamp OR --downgrade to allow "
335 "building the incremental." % (pre_timestamp, post_timestamp))
336
337
Tianjie2bb14862020-08-28 16:24:34 -0700338def ComputeRuntimeBuildInfos(default_build_info, boot_variable_values):
339 """Returns a set of build info objects that may exist during runtime."""
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400340
Tianjie2bb14862020-08-28 16:24:34 -0700341 build_info_set = {default_build_info}
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400342 if not boot_variable_values:
Tianjie2bb14862020-08-28 16:24:34 -0700343 return build_info_set
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400344
345 # Calculate all possible combinations of the values for the boot variables.
346 keys = boot_variable_values.keys()
347 value_list = boot_variable_values.values()
348 combinations = [dict(zip(keys, values))
349 for values in itertools.product(*value_list)]
350 for placeholder_values in combinations:
351 # Reload the info_dict as some build properties may change their values
352 # based on the value of ro.boot* properties.
Tianjie2bb14862020-08-28 16:24:34 -0700353 info_dict = copy.deepcopy(default_build_info.info_dict)
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400354 for partition in PARTITIONS_WITH_CARE_MAP:
355 partition_prop_key = "{}.build.prop".format(partition)
356 input_file = info_dict[partition_prop_key].input_file
357 if isinstance(input_file, zipfile.ZipFile):
358 with zipfile.ZipFile(input_file.filename) as input_zip:
359 info_dict[partition_prop_key] = \
360 PartitionBuildProps.FromInputFile(input_zip, partition,
361 placeholder_values)
362 else:
363 info_dict[partition_prop_key] = \
364 PartitionBuildProps.FromInputFile(input_file, partition,
365 placeholder_values)
366 info_dict["build.prop"] = info_dict["system.build.prop"]
Tianjie2bb14862020-08-28 16:24:34 -0700367 build_info_set.add(BuildInfo(info_dict, default_build_info.oem_dicts))
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400368
Tianjie2bb14862020-08-28 16:24:34 -0700369 return build_info_set
370
371
372def CalculateRuntimeDevicesAndFingerprints(default_build_info,
373 boot_variable_values):
374 """Returns a tuple of sets for runtime devices and fingerprints"""
375
376 device_names = set()
377 fingerprints = set()
378 build_info_set = ComputeRuntimeBuildInfos(default_build_info,
379 boot_variable_values)
380 for runtime_build_info in build_info_set:
381 device_names.add(runtime_build_info.device)
382 fingerprints.add(runtime_build_info.fingerprint)
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400383 return device_names, fingerprints
384
385
386class PropertyFiles(object):
387 """A class that computes the property-files string for an OTA package.
388
389 A property-files string is a comma-separated string that contains the
390 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
391 can be fetched directly with the package URL along with the offset/size info.
392 These strings can be used for streaming A/B OTAs, or allowing an updater to
393 download package metadata entry directly, without paying the cost of
394 downloading entire package.
395
396 Computing the final property-files string requires two passes. Because doing
397 the whole package signing (with signapk.jar) will possibly reorder the ZIP
398 entries, which may in turn invalidate earlier computed ZIP entry offset/size
399 values.
400
401 This class provides functions to be called for each pass. The general flow is
402 as follows.
403
404 property_files = PropertyFiles()
405 # The first pass, which writes placeholders before doing initial signing.
406 property_files.Compute()
407 SignOutput()
408
409 # The second pass, by replacing the placeholders with actual data.
410 property_files.Finalize()
411 SignOutput()
412
413 And the caller can additionally verify the final result.
414
415 property_files.Verify()
416 """
417
418 def __init__(self):
419 self.name = None
420 self.required = ()
421 self.optional = ()
422
423 def Compute(self, input_zip):
424 """Computes and returns a property-files string with placeholders.
425
426 We reserve extra space for the offset and size of the metadata entry itself,
427 although we don't know the final values until the package gets signed.
428
429 Args:
430 input_zip: The input ZIP file.
431
432 Returns:
433 A string with placeholders for the metadata offset/size info, e.g.
434 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
435 """
436 return self.GetPropertyFilesString(input_zip, reserve_space=True)
437
438 class InsufficientSpaceException(Exception):
439 pass
440
441 def Finalize(self, input_zip, reserved_length):
442 """Finalizes a property-files string with actual METADATA offset/size info.
443
444 The input ZIP file has been signed, with the ZIP entries in the desired
445 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
446 the ZIP entry offsets and construct the property-files string with actual
447 data. Note that during this process, we must pad the property-files string
448 to the reserved length, so that the METADATA entry size remains the same.
449 Otherwise the entries' offsets and sizes may change again.
450
451 Args:
452 input_zip: The input ZIP file.
453 reserved_length: The reserved length of the property-files string during
454 the call to Compute(). The final string must be no more than this
455 size.
456
457 Returns:
458 A property-files string including the metadata offset/size info, e.g.
459 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
460
461 Raises:
462 InsufficientSpaceException: If the reserved length is insufficient to hold
463 the final string.
464 """
465 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
466 if len(result) > reserved_length:
467 raise self.InsufficientSpaceException(
468 'Insufficient reserved space: reserved={}, actual={}'.format(
469 reserved_length, len(result)))
470
471 result += ' ' * (reserved_length - len(result))
472 return result
473
474 def Verify(self, input_zip, expected):
475 """Verifies the input ZIP file contains the expected property-files string.
476
477 Args:
478 input_zip: The input ZIP file.
479 expected: The property-files string that's computed from Finalize().
480
481 Raises:
482 AssertionError: On finding a mismatch.
483 """
484 actual = self.GetPropertyFilesString(input_zip)
485 assert actual == expected, \
486 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
487
488 def GetPropertyFilesString(self, zip_file, reserve_space=False):
489 """
490 Constructs the property-files string per request.
491
492 Args:
493 zip_file: The input ZIP file.
494 reserved_length: The reserved length of the property-files string.
495
496 Returns:
497 A property-files string including the metadata offset/size info, e.g.
498 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
499 """
500
501 def ComputeEntryOffsetSize(name):
502 """Computes the zip entry offset and size."""
503 info = zip_file.getinfo(name)
504 offset = info.header_offset
505 offset += zipfile.sizeFileHeader
506 offset += len(info.extra) + len(info.filename)
507 size = info.file_size
508 return '%s:%d:%d' % (os.path.basename(name), offset, size)
509
510 tokens = []
511 tokens.extend(self._GetPrecomputed(zip_file))
512 for entry in self.required:
513 tokens.append(ComputeEntryOffsetSize(entry))
514 for entry in self.optional:
515 if entry in zip_file.namelist():
516 tokens.append(ComputeEntryOffsetSize(entry))
517
518 # 'META-INF/com/android/metadata' is required. We don't know its actual
519 # offset and length (as well as the values for other entries). So we reserve
520 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
521 # the space for metadata entry. Because 'offset' allows a max of 10-digit
522 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
523 # reserved space serves the metadata entry only.
524 if reserve_space:
525 tokens.append('metadata:' + ' ' * 15)
Tianjiea2076132020-08-19 17:25:32 -0700526 tokens.append('metadata.pb:' + ' ' * 15)
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400527 else:
528 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
Tianjiea2076132020-08-19 17:25:32 -0700529 tokens.append(ComputeEntryOffsetSize(METADATA_PROTO_NAME))
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400530
531 return ','.join(tokens)
532
533 def _GetPrecomputed(self, input_zip):
534 """Computes the additional tokens to be included into the property-files.
535
536 This applies to tokens without actual ZIP entries, such as
537 payload_metadata.bin. We want to expose the offset/size to updaters, so
538 that they can download the payload metadata directly with the info.
539
540 Args:
541 input_zip: The input zip file.
542
543 Returns:
544 A list of strings (tokens) to be added to the property-files string.
545 """
546 # pylint: disable=no-self-use
547 # pylint: disable=unused-argument
548 return []
549
550
551def SignOutput(temp_zip_name, output_zip_name):
552 pw = OPTIONS.key_passwords[OPTIONS.package_key]
553
554 SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
555 whole_file=True)