ota_from_target_files.py revision d522bdc9edbf64d15a59c6924853b2e2c8c39e90
1#!/usr/bin/env python 2# 3# Copyright (C) 2008 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17""" 18Given a target-files zipfile, produces an OTA package that installs 19that build. An incremental OTA is produced if -i is given, otherwise 20a full OTA is produced. 21 22Usage: ota_from_target_files [flags] input_target_files output_ota_package 23 24 --board_config <file> 25 Deprecated. 26 27 -k (--package_key) <key> Key to use to sign the package (default is 28 the value of default_system_dev_certificate from the input 29 target-files's META/misc_info.txt, or 30 "build/target/product/security/testkey" if that value is not 31 specified). 32 33 For incremental OTAs, the default value is based on the source 34 target-file, not the target build. 35 36 -i (--incremental_from) <file> 37 Generate an incremental OTA using the given target-files zip as 38 the starting build. 39 40 --full_radio 41 When generating an incremental OTA, always include a full copy of 42 radio image. This option is only meaningful when -i is specified, 43 because a full radio is always included in a full OTA if applicable. 44 45 --full_bootloader 46 Similar to --full_radio. When generating an incremental OTA, always 47 include a full copy of bootloader image. 48 49 -v (--verify) 50 Remount and verify the checksums of the files written to the 51 system and vendor (if used) partitions. Incremental builds only. 52 53 -o (--oem_settings) <file> 54 Use the file to specify the expected OEM-specific properties 55 on the OEM partition of the intended device. 56 57 --oem_no_mount 58 For devices with OEM-specific properties but without an OEM partition, 59 do not mount the OEM partition in the updater-script. This should be 60 very rarely used, since it's expected to have a dedicated OEM partition 61 for OEM-specific properties. Only meaningful when -o is specified. 62 63 -w (--wipe_user_data) 64 Generate an OTA package that will wipe the user data partition 65 when installed. 66 67 -n (--no_prereq) 68 Omit the timestamp prereq check normally included at the top of 69 the build scripts (used for developer OTA packages which 70 legitimately need to go back and forth). 71 72 --downgrade 73 Intentionally generate an incremental OTA that updates from a newer 74 build to an older one (based on timestamp comparison). "post-timestamp" 75 will be replaced by "ota-downgrade=yes" in the metadata file. A data 76 wipe will always be enforced, so "ota-wipe=yes" will also be included in 77 the metadata file. The update-binary in the source build will be used in 78 the OTA package, unless --binary flag is specified. 79 80 -e (--extra_script) <file> 81 Insert the contents of file at the end of the update script. 82 83 -a (--aslr_mode) <on|off> 84 Specify whether to turn on ASLR for the package (on by default). 85 86 -2 (--two_step) 87 Generate a 'two-step' OTA package, where recovery is updated 88 first, so that any changes made to the system partition are done 89 using the new recovery (new kernel, etc.). 90 91 --block 92 Generate a block-based OTA if possible. Will fall back to a 93 file-based OTA if the target_files is older and doesn't support 94 block-based OTAs. 95 96 -b (--binary) <file> 97 Use the given binary as the update-binary in the output package, 98 instead of the binary in the build's target_files. Use for 99 development only. 100 101 -t (--worker_threads) <int> 102 Specifies the number of worker-threads that will be used when 103 generating patches for incremental updates (defaults to 3). 104 105 --stash_threshold <float> 106 Specifies the threshold that will be used to compute the maximum 107 allowed stash size (defaults to 0.8). 108 109 --gen_verify 110 Generate an OTA package that verifies the partitions. 111 112 --log_diff <file> 113 Generate a log file that shows the differences in the source and target 114 builds for an incremental package. This option is only meaningful when 115 -i is specified. 116""" 117 118import sys 119 120if sys.hexversion < 0x02070000: 121 print >> sys.stderr, "Python 2.7 or newer is required." 122 sys.exit(1) 123 124import multiprocessing 125import os 126import subprocess 127import tempfile 128import zipfile 129 130import common 131import edify_generator 132import sparse_img 133 134OPTIONS = common.OPTIONS 135OPTIONS.package_key = None 136OPTIONS.incremental_source = None 137OPTIONS.verify = False 138OPTIONS.require_verbatim = set() 139OPTIONS.prohibit_verbatim = set(("system/build.prop",)) 140OPTIONS.patch_threshold = 0.95 141OPTIONS.wipe_user_data = False 142OPTIONS.omit_prereq = False 143OPTIONS.downgrade = False 144OPTIONS.extra_script = None 145OPTIONS.aslr_mode = True 146OPTIONS.worker_threads = multiprocessing.cpu_count() // 2 147if OPTIONS.worker_threads == 0: 148 OPTIONS.worker_threads = 1 149OPTIONS.two_step = False 150OPTIONS.no_signing = False 151OPTIONS.block_based = False 152OPTIONS.updater_binary = None 153OPTIONS.oem_source = None 154OPTIONS.oem_no_mount = False 155OPTIONS.fallback_to_full = True 156OPTIONS.full_radio = False 157OPTIONS.full_bootloader = False 158# Stash size cannot exceed cache_size * threshold. 159OPTIONS.cache_size = None 160OPTIONS.stash_threshold = 0.8 161OPTIONS.gen_verify = False 162OPTIONS.log_diff = None 163 164def MostPopularKey(d, default): 165 """Given a dict, return the key corresponding to the largest 166 value. Returns 'default' if the dict is empty.""" 167 x = [(v, k) for (k, v) in d.iteritems()] 168 if not x: 169 return default 170 x.sort() 171 return x[-1][1] 172 173 174def IsSymlink(info): 175 """Return true if the zipfile.ZipInfo object passed in represents a 176 symlink.""" 177 return (info.external_attr >> 16) & 0o770000 == 0o120000 178 179def IsRegular(info): 180 """Return true if the zipfile.ZipInfo object passed in represents a 181 regular file.""" 182 return (info.external_attr >> 16) & 0o770000 == 0o100000 183 184def ClosestFileMatch(src, tgtfiles, existing): 185 """Returns the closest file match between a source file and list 186 of potential matches. The exact filename match is preferred, 187 then the sha1 is searched for, and finally a file with the same 188 basename is evaluated. Rename support in the updater-binary is 189 required for the latter checks to be used.""" 190 191 result = tgtfiles.get("path:" + src.name) 192 if result is not None: 193 return result 194 195 if not OPTIONS.target_info_dict.get("update_rename_support", False): 196 return None 197 198 if src.size < 1000: 199 return None 200 201 result = tgtfiles.get("sha1:" + src.sha1) 202 if result is not None and existing.get(result.name) is None: 203 return result 204 result = tgtfiles.get("file:" + src.name.split("/")[-1]) 205 if result is not None and existing.get(result.name) is None: 206 return result 207 return None 208 209class ItemSet(object): 210 def __init__(self, partition, fs_config): 211 self.partition = partition 212 self.fs_config = fs_config 213 self.ITEMS = {} 214 215 def Get(self, name, is_dir=False): 216 if name not in self.ITEMS: 217 self.ITEMS[name] = Item(self, name, is_dir=is_dir) 218 return self.ITEMS[name] 219 220 def GetMetadata(self, input_zip): 221 # The target_files contains a record of what the uid, 222 # gid, and mode are supposed to be. 223 output = input_zip.read(self.fs_config) 224 225 for line in output.split("\n"): 226 if not line: 227 continue 228 columns = line.split() 229 name, uid, gid, mode = columns[:4] 230 selabel = None 231 capabilities = None 232 233 # After the first 4 columns, there are a series of key=value 234 # pairs. Extract out the fields we care about. 235 for element in columns[4:]: 236 key, value = element.split("=") 237 if key == "selabel": 238 selabel = value 239 if key == "capabilities": 240 capabilities = value 241 242 i = self.ITEMS.get(name, None) 243 if i is not None: 244 i.uid = int(uid) 245 i.gid = int(gid) 246 i.mode = int(mode, 8) 247 i.selabel = selabel 248 i.capabilities = capabilities 249 if i.is_dir: 250 i.children.sort(key=lambda i: i.name) 251 252 # Set metadata for the files generated by this script. For full recovery 253 # image at system/etc/recovery.img, it will be taken care by fs_config. 254 i = self.ITEMS.get("system/recovery-from-boot.p", None) 255 if i: 256 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None 257 i = self.ITEMS.get("system/etc/install-recovery.sh", None) 258 if i: 259 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None 260 261 262class Item(object): 263 """Items represent the metadata (user, group, mode) of files and 264 directories in the system image.""" 265 def __init__(self, itemset, name, is_dir=False): 266 self.itemset = itemset 267 self.name = name 268 self.uid = None 269 self.gid = None 270 self.mode = None 271 self.selabel = None 272 self.capabilities = None 273 self.is_dir = is_dir 274 self.descendants = None 275 self.best_subtree = None 276 277 if name: 278 self.parent = itemset.Get(os.path.dirname(name), is_dir=True) 279 self.parent.children.append(self) 280 else: 281 self.parent = None 282 if self.is_dir: 283 self.children = [] 284 285 def Dump(self, indent=0): 286 if self.uid is not None: 287 print "%s%s %d %d %o" % ( 288 " " * indent, self.name, self.uid, self.gid, self.mode) 289 else: 290 print "%s%s %s %s %s" % ( 291 " " * indent, self.name, self.uid, self.gid, self.mode) 292 if self.is_dir: 293 print "%s%s" % (" "*indent, self.descendants) 294 print "%s%s" % (" "*indent, self.best_subtree) 295 for i in self.children: 296 i.Dump(indent=indent+1) 297 298 def CountChildMetadata(self): 299 """Count up the (uid, gid, mode, selabel, capabilities) tuples for 300 all children and determine the best strategy for using set_perm_recursive 301 and set_perm to correctly chown/chmod all the files to their desired 302 values. Recursively calls itself for all descendants. 303 304 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} 305 counting up all descendants of this node. (dmode or fmode may be None.) 306 Also sets the best_subtree of each directory Item to the (uid, gid, dmode, 307 fmode, selabel, capabilities) tuple that will match the most descendants of 308 that Item. 309 """ 310 311 assert self.is_dir 312 key = (self.uid, self.gid, self.mode, None, self.selabel, 313 self.capabilities) 314 self.descendants = {key: 1} 315 d = self.descendants 316 for i in self.children: 317 if i.is_dir: 318 for k, v in i.CountChildMetadata().iteritems(): 319 d[k] = d.get(k, 0) + v 320 else: 321 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities) 322 d[k] = d.get(k, 0) + 1 323 324 # Find the (uid, gid, dmode, fmode, selabel, capabilities) 325 # tuple that matches the most descendants. 326 327 # First, find the (uid, gid) pair that matches the most 328 # descendants. 329 ug = {} 330 for (uid, gid, _, _, _, _), count in d.iteritems(): 331 ug[(uid, gid)] = ug.get((uid, gid), 0) + count 332 ug = MostPopularKey(ug, (0, 0)) 333 334 # Now find the dmode, fmode, selabel, and capabilities that match 335 # the most descendants with that (uid, gid), and choose those. 336 best_dmode = (0, 0o755) 337 best_fmode = (0, 0o644) 338 best_selabel = (0, None) 339 best_capabilities = (0, None) 340 for k, count in d.iteritems(): 341 if k[:2] != ug: 342 continue 343 if k[2] is not None and count >= best_dmode[0]: 344 best_dmode = (count, k[2]) 345 if k[3] is not None and count >= best_fmode[0]: 346 best_fmode = (count, k[3]) 347 if k[4] is not None and count >= best_selabel[0]: 348 best_selabel = (count, k[4]) 349 if k[5] is not None and count >= best_capabilities[0]: 350 best_capabilities = (count, k[5]) 351 self.best_subtree = ug + ( 352 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1]) 353 354 return d 355 356 def SetPermissions(self, script): 357 """Append set_perm/set_perm_recursive commands to 'script' to 358 set all permissions, users, and groups for the tree of files 359 rooted at 'self'.""" 360 361 self.CountChildMetadata() 362 363 def recurse(item, current): 364 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple 365 # that the current item (and all its children) have already been set to. 366 # We only need to issue set_perm/set_perm_recursive commands if we're 367 # supposed to be something different. 368 if item.is_dir: 369 if current != item.best_subtree: 370 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree) 371 current = item.best_subtree 372 373 if item.uid != current[0] or item.gid != current[1] or \ 374 item.mode != current[2] or item.selabel != current[4] or \ 375 item.capabilities != current[5]: 376 script.SetPermissions("/"+item.name, item.uid, item.gid, 377 item.mode, item.selabel, item.capabilities) 378 379 for i in item.children: 380 recurse(i, current) 381 else: 382 if item.uid != current[0] or item.gid != current[1] or \ 383 item.mode != current[3] or item.selabel != current[4] or \ 384 item.capabilities != current[5]: 385 script.SetPermissions("/"+item.name, item.uid, item.gid, 386 item.mode, item.selabel, item.capabilities) 387 388 recurse(self, (-1, -1, -1, -1, None, None)) 389 390 391def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None): 392 """Copies files for the partition in the input zip to the output 393 zip. Populates the Item class with their metadata, and returns a 394 list of symlinks. output_zip may be None, in which case the copy is 395 skipped (but the other side effects still happen). substitute is an 396 optional dict of {output filename: contents} to be output instead of 397 certain input files. 398 """ 399 400 symlinks = [] 401 402 partition = itemset.partition 403 404 for info in input_zip.infolist(): 405 prefix = partition.upper() + "/" 406 if info.filename.startswith(prefix): 407 basefilename = info.filename[len(prefix):] 408 if IsSymlink(info): 409 symlinks.append((input_zip.read(info.filename), 410 "/" + partition + "/" + basefilename)) 411 else: 412 import copy 413 info2 = copy.copy(info) 414 fn = info2.filename = partition + "/" + basefilename 415 if substitute and fn in substitute and substitute[fn] is None: 416 continue 417 if output_zip is not None: 418 if substitute and fn in substitute: 419 data = substitute[fn] 420 else: 421 data = input_zip.read(info.filename) 422 common.ZipWriteStr(output_zip, info2, data) 423 if fn.endswith("/"): 424 itemset.Get(fn[:-1], is_dir=True) 425 else: 426 itemset.Get(fn) 427 428 symlinks.sort() 429 return symlinks 430 431 432def SignOutput(temp_zip_name, output_zip_name): 433 key_passwords = common.GetKeyPasswords([OPTIONS.package_key]) 434 pw = key_passwords[OPTIONS.package_key] 435 436 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw, 437 whole_file=True) 438 439 440def AppendAssertions(script, info_dict, oem_dict=None): 441 oem_props = info_dict.get("oem_fingerprint_properties") 442 if oem_props is None or len(oem_props) == 0: 443 device = GetBuildProp("ro.product.device", info_dict) 444 script.AssertDevice(device) 445 else: 446 if oem_dict is None: 447 raise common.ExternalError( 448 "No OEM file provided to answer expected assertions") 449 for prop in oem_props.split(): 450 if oem_dict.get(prop) is None: 451 raise common.ExternalError( 452 "The OEM file is missing the property %s" % prop) 453 script.AssertOemProperty(prop, oem_dict.get(prop)) 454 455 456def HasRecoveryPatch(target_files_zip): 457 namelist = [name for name in target_files_zip.namelist()] 458 return ("SYSTEM/recovery-from-boot.p" in namelist or 459 "SYSTEM/etc/recovery.img" in namelist) 460 461def HasVendorPartition(target_files_zip): 462 try: 463 target_files_zip.getinfo("VENDOR/") 464 return True 465 except KeyError: 466 return False 467 468def GetOemProperty(name, oem_props, oem_dict, info_dict): 469 if oem_props is not None and name in oem_props: 470 return oem_dict[name] 471 return GetBuildProp(name, info_dict) 472 473 474def CalculateFingerprint(oem_props, oem_dict, info_dict): 475 if oem_props is None: 476 return GetBuildProp("ro.build.fingerprint", info_dict) 477 return "%s/%s/%s:%s" % ( 478 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict), 479 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict), 480 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict), 481 GetBuildProp("ro.build.thumbprint", info_dict)) 482 483 484def GetImage(which, tmpdir, info_dict): 485 # Return an image object (suitable for passing to BlockImageDiff) 486 # for the 'which' partition (most be "system" or "vendor"). If a 487 # prebuilt image and file map are found in tmpdir they are used, 488 # otherwise they are reconstructed from the individual files. 489 490 assert which in ("system", "vendor") 491 492 path = os.path.join(tmpdir, "IMAGES", which + ".img") 493 mappath = os.path.join(tmpdir, "IMAGES", which + ".map") 494 if os.path.exists(path) and os.path.exists(mappath): 495 print "using %s.img from target-files" % (which,) 496 # This is a 'new' target-files, which already has the image in it. 497 498 else: 499 print "building %s.img from target-files" % (which,) 500 501 # This is an 'old' target-files, which does not contain images 502 # already built. Build them. 503 504 mappath = tempfile.mkstemp()[1] 505 OPTIONS.tempfiles.append(mappath) 506 507 import add_img_to_target_files 508 if which == "system": 509 path = add_img_to_target_files.BuildSystem( 510 tmpdir, info_dict, block_list=mappath) 511 elif which == "vendor": 512 path = add_img_to_target_files.BuildVendor( 513 tmpdir, info_dict, block_list=mappath) 514 515 # Bug: http://b/20939131 516 # In ext4 filesystems, block 0 might be changed even being mounted 517 # R/O. We add it to clobbered_blocks so that it will be written to the 518 # target unconditionally. Note that they are still part of care_map. 519 clobbered_blocks = "0" 520 521 return sparse_img.SparseImage(path, mappath, clobbered_blocks) 522 523 524def WriteFullOTAPackage(input_zip, output_zip): 525 # TODO: how to determine this? We don't know what version it will 526 # be installed on top of. For now, we expect the API just won't 527 # change very often. Similarly for fstab, it might have changed 528 # in the target build. 529 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict) 530 531 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties") 532 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options") 533 oem_dict = None 534 if oem_props is not None and len(oem_props) > 0: 535 if OPTIONS.oem_source is None: 536 raise common.ExternalError("OEM source required for this build") 537 if not OPTIONS.oem_no_mount: 538 script.Mount("/oem", recovery_mount_options) 539 oem_dict = common.LoadDictionaryFromLines( 540 open(OPTIONS.oem_source).readlines()) 541 542 metadata = { 543 "post-build": CalculateFingerprint(oem_props, oem_dict, 544 OPTIONS.info_dict), 545 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict, 546 OPTIONS.info_dict), 547 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict), 548 } 549 550 device_specific = common.DeviceSpecificParams( 551 input_zip=input_zip, 552 input_version=OPTIONS.info_dict["recovery_api_version"], 553 output_zip=output_zip, 554 script=script, 555 input_tmp=OPTIONS.input_tmp, 556 metadata=metadata, 557 info_dict=OPTIONS.info_dict) 558 559 has_recovery_patch = HasRecoveryPatch(input_zip) 560 block_based = OPTIONS.block_based and has_recovery_patch 561 562 metadata["ota-type"] = "BLOCK" if block_based else "FILE" 563 564 if not OPTIONS.omit_prereq: 565 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict) 566 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict) 567 script.AssertOlderBuild(ts, ts_text) 568 569 AppendAssertions(script, OPTIONS.info_dict, oem_dict) 570 device_specific.FullOTA_Assertions() 571 572 # Two-step package strategy (in chronological order, which is *not* 573 # the order in which the generated script has things): 574 # 575 # if stage is not "2/3" or "3/3": 576 # write recovery image to boot partition 577 # set stage to "2/3" 578 # reboot to boot partition and restart recovery 579 # else if stage is "2/3": 580 # write recovery image to recovery partition 581 # set stage to "3/3" 582 # reboot to recovery partition and restart recovery 583 # else: 584 # (stage must be "3/3") 585 # set stage to "" 586 # do normal full package installation: 587 # wipe and install system, boot image, etc. 588 # set up system to update recovery partition on first boot 589 # complete script normally 590 # (allow recovery to mark itself finished and reboot) 591 592 recovery_img = common.GetBootableImage("recovery.img", "recovery.img", 593 OPTIONS.input_tmp, "RECOVERY") 594 if OPTIONS.two_step: 595 if not OPTIONS.info_dict.get("multistage_support", None): 596 assert False, "two-step packages not supported by this build" 597 fs = OPTIONS.info_dict["fstab"]["/misc"] 598 assert fs.fs_type.upper() == "EMMC", \ 599 "two-step packages only supported on devices with EMMC /misc partitions" 600 bcb_dev = {"bcb_dev": fs.device} 601 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data) 602 script.AppendExtra(""" 603if get_stage("%(bcb_dev)s") == "2/3" then 604""" % bcb_dev) 605 script.WriteRawImage("/recovery", "recovery.img") 606 script.AppendExtra(""" 607set_stage("%(bcb_dev)s", "3/3"); 608reboot_now("%(bcb_dev)s", "recovery"); 609else if get_stage("%(bcb_dev)s") == "3/3" then 610""" % bcb_dev) 611 612 # Dump fingerprints 613 script.Print("Target: %s" % CalculateFingerprint( 614 oem_props, oem_dict, OPTIONS.info_dict)) 615 616 device_specific.FullOTA_InstallBegin() 617 618 system_progress = 0.75 619 620 if OPTIONS.wipe_user_data: 621 system_progress -= 0.1 622 if HasVendorPartition(input_zip): 623 system_progress -= 0.1 624 625 # Place a copy of file_contexts.bin into the OTA package which will be used 626 # by the recovery program. 627 if "selinux_fc" in OPTIONS.info_dict: 628 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip) 629 630 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options") 631 632 system_items = ItemSet("system", "META/filesystem_config.txt") 633 script.ShowProgress(system_progress, 0) 634 635 if block_based: 636 # Full OTA is done as an "incremental" against an empty source 637 # image. This has the effect of writing new data from the package 638 # to the entire partition, but lets us reuse the updater code that 639 # writes incrementals to do it. 640 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict) 641 system_tgt.ResetFileMap() 642 system_diff = common.BlockDifference("system", system_tgt, src=None) 643 system_diff.WriteScript(script, output_zip) 644 else: 645 script.FormatPartition("/system") 646 script.Mount("/system", recovery_mount_options) 647 if not has_recovery_patch: 648 script.UnpackPackageDir("recovery", "/system") 649 script.UnpackPackageDir("system", "/system") 650 651 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip) 652 script.MakeSymlinks(symlinks) 653 654 boot_img = common.GetBootableImage( 655 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") 656 657 if not block_based: 658 def output_sink(fn, data): 659 common.ZipWriteStr(output_zip, "recovery/" + fn, data) 660 system_items.Get("system/" + fn) 661 662 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, 663 recovery_img, boot_img) 664 665 system_items.GetMetadata(input_zip) 666 system_items.Get("system").SetPermissions(script) 667 668 if HasVendorPartition(input_zip): 669 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt") 670 script.ShowProgress(0.1, 0) 671 672 if block_based: 673 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict) 674 vendor_tgt.ResetFileMap() 675 vendor_diff = common.BlockDifference("vendor", vendor_tgt) 676 vendor_diff.WriteScript(script, output_zip) 677 else: 678 script.FormatPartition("/vendor") 679 script.Mount("/vendor", recovery_mount_options) 680 script.UnpackPackageDir("vendor", "/vendor") 681 682 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip) 683 script.MakeSymlinks(symlinks) 684 685 vendor_items.GetMetadata(input_zip) 686 vendor_items.Get("vendor").SetPermissions(script) 687 688 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict) 689 common.ZipWriteStr(output_zip, "boot.img", boot_img.data) 690 691 script.ShowProgress(0.05, 5) 692 script.WriteRawImage("/boot", "boot.img") 693 694 script.ShowProgress(0.2, 10) 695 device_specific.FullOTA_InstallEnd() 696 697 if OPTIONS.extra_script is not None: 698 script.AppendExtra(OPTIONS.extra_script) 699 700 script.UnmountAll() 701 702 if OPTIONS.wipe_user_data: 703 script.ShowProgress(0.1, 10) 704 script.FormatPartition("/data") 705 706 if OPTIONS.two_step: 707 script.AppendExtra(""" 708set_stage("%(bcb_dev)s", ""); 709""" % bcb_dev) 710 script.AppendExtra("else\n") 711 script.WriteRawImage("/boot", "recovery.img") 712 script.AppendExtra(""" 713set_stage("%(bcb_dev)s", "2/3"); 714reboot_now("%(bcb_dev)s", ""); 715endif; 716endif; 717""" % bcb_dev) 718 719 script.SetProgress(1) 720 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary) 721 metadata["ota-required-cache"] = str(script.required_cache) 722 WriteMetadata(metadata, output_zip) 723 724 725def WritePolicyConfig(file_name, output_zip): 726 common.ZipWrite(output_zip, file_name, os.path.basename(file_name)) 727 728 729def WriteMetadata(metadata, output_zip): 730 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata", 731 "".join(["%s=%s\n" % kv 732 for kv in sorted(metadata.iteritems())])) 733 734 735def LoadPartitionFiles(z, partition): 736 """Load all the files from the given partition in a given target-files 737 ZipFile, and return a dict of {filename: File object}.""" 738 out = {} 739 prefix = partition.upper() + "/" 740 for info in z.infolist(): 741 if info.filename.startswith(prefix) and not IsSymlink(info): 742 basefilename = info.filename[len(prefix):] 743 fn = partition + "/" + basefilename 744 data = z.read(info.filename) 745 out[fn] = common.File(fn, data) 746 return out 747 748 749def GetBuildProp(prop, info_dict): 750 """Return the fingerprint of the build of a given target-files info_dict.""" 751 try: 752 return info_dict.get("build.prop", {})[prop] 753 except KeyError: 754 raise common.ExternalError("couldn't find %s in build.prop" % (prop,)) 755 756 757def AddToKnownPaths(filename, known_paths): 758 if filename[-1] == "/": 759 return 760 dirs = filename.split("/")[:-1] 761 while len(dirs) > 0: 762 path = "/".join(dirs) 763 if path in known_paths: 764 break 765 known_paths.add(path) 766 dirs.pop() 767 768 769def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip): 770 # TODO(tbao): We should factor out the common parts between 771 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage(). 772 source_version = OPTIONS.source_info_dict["recovery_api_version"] 773 target_version = OPTIONS.target_info_dict["recovery_api_version"] 774 775 if source_version == 0: 776 print ("WARNING: generating edify script for a source that " 777 "can't install it.") 778 script = edify_generator.EdifyGenerator( 779 source_version, OPTIONS.target_info_dict, 780 fstab=OPTIONS.source_info_dict["fstab"]) 781 782 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties") 783 recovery_mount_options = OPTIONS.source_info_dict.get( 784 "recovery_mount_options") 785 oem_dict = None 786 if oem_props is not None and len(oem_props) > 0: 787 if OPTIONS.oem_source is None: 788 raise common.ExternalError("OEM source required for this build") 789 if not OPTIONS.oem_no_mount: 790 script.Mount("/oem", recovery_mount_options) 791 oem_dict = common.LoadDictionaryFromLines( 792 open(OPTIONS.oem_source).readlines()) 793 794 metadata = { 795 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict, 796 OPTIONS.source_info_dict), 797 "ota-type": "BLOCK", 798 } 799 800 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict) 801 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict) 802 is_downgrade = long(post_timestamp) < long(pre_timestamp) 803 804 if OPTIONS.downgrade: 805 metadata["ota-downgrade"] = "yes" 806 if not is_downgrade: 807 raise RuntimeError("--downgrade specified but no downgrade detected: " 808 "pre: %s, post: %s" % (pre_timestamp, post_timestamp)) 809 else: 810 if is_downgrade: 811 # Non-fatal here to allow generating such a package which may require 812 # manual work to adjust the post-timestamp. A legit use case is that we 813 # cut a new build C (after having A and B), but want to enfore the 814 # update path of A -> C -> B. Specifying --downgrade may not help since 815 # that would enforce a data wipe for C -> B update. 816 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n" 817 "The package may not be deployed properly. " 818 "Try --downgrade?\n" % (pre_timestamp, post_timestamp)) 819 metadata["post-timestamp"] = post_timestamp 820 821 device_specific = common.DeviceSpecificParams( 822 source_zip=source_zip, 823 source_version=source_version, 824 target_zip=target_zip, 825 target_version=target_version, 826 output_zip=output_zip, 827 script=script, 828 metadata=metadata, 829 info_dict=OPTIONS.source_info_dict) 830 831 source_fp = CalculateFingerprint(oem_props, oem_dict, 832 OPTIONS.source_info_dict) 833 target_fp = CalculateFingerprint(oem_props, oem_dict, 834 OPTIONS.target_info_dict) 835 metadata["pre-build"] = source_fp 836 metadata["post-build"] = target_fp 837 838 source_boot = common.GetBootableImage( 839 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", 840 OPTIONS.source_info_dict) 841 target_boot = common.GetBootableImage( 842 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT") 843 updating_boot = (not OPTIONS.two_step and 844 (source_boot.data != target_boot.data)) 845 846 target_recovery = common.GetBootableImage( 847 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY") 848 849 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict) 850 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict) 851 852 blockimgdiff_version = 1 853 if OPTIONS.info_dict: 854 blockimgdiff_version = max( 855 int(i) for i in 856 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(",")) 857 858 # Check first block of system partition for remount R/W only if 859 # disk type is ext4 860 system_partition = OPTIONS.source_info_dict["fstab"]["/system"] 861 check_first_block = system_partition.fs_type == "ext4" 862 system_diff = common.BlockDifference("system", system_tgt, system_src, 863 check_first_block, 864 version=blockimgdiff_version) 865 866 if HasVendorPartition(target_zip): 867 if not HasVendorPartition(source_zip): 868 raise RuntimeError("can't generate incremental that adds /vendor") 869 vendor_src = GetImage("vendor", OPTIONS.source_tmp, 870 OPTIONS.source_info_dict) 871 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp, 872 OPTIONS.target_info_dict) 873 874 # Check first block of vendor partition for remount R/W only if 875 # disk type is ext4 876 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"] 877 check_first_block = vendor_partition.fs_type == "ext4" 878 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src, 879 check_first_block, 880 version=blockimgdiff_version) 881 else: 882 vendor_diff = None 883 884 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict) 885 device_specific.IncrementalOTA_Assertions() 886 887 # Two-step incremental package strategy (in chronological order, 888 # which is *not* the order in which the generated script has 889 # things): 890 # 891 # if stage is not "2/3" or "3/3": 892 # do verification on current system 893 # write recovery image to boot partition 894 # set stage to "2/3" 895 # reboot to boot partition and restart recovery 896 # else if stage is "2/3": 897 # write recovery image to recovery partition 898 # set stage to "3/3" 899 # reboot to recovery partition and restart recovery 900 # else: 901 # (stage must be "3/3") 902 # perform update: 903 # patch system files, etc. 904 # force full install of new boot image 905 # set up system to update recovery partition on first boot 906 # complete script normally 907 # (allow recovery to mark itself finished and reboot) 908 909 if OPTIONS.two_step: 910 if not OPTIONS.source_info_dict.get("multistage_support", None): 911 assert False, "two-step packages not supported by this build" 912 fs = OPTIONS.source_info_dict["fstab"]["/misc"] 913 assert fs.fs_type.upper() == "EMMC", \ 914 "two-step packages only supported on devices with EMMC /misc partitions" 915 bcb_dev = {"bcb_dev": fs.device} 916 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data) 917 script.AppendExtra(""" 918if get_stage("%(bcb_dev)s") == "2/3" then 919""" % bcb_dev) 920 script.AppendExtra("sleep(20);\n") 921 script.WriteRawImage("/recovery", "recovery.img") 922 script.AppendExtra(""" 923set_stage("%(bcb_dev)s", "3/3"); 924reboot_now("%(bcb_dev)s", "recovery"); 925else if get_stage("%(bcb_dev)s") != "3/3" then 926""" % bcb_dev) 927 928 # Dump fingerprints 929 script.Print("Source: %s" % CalculateFingerprint( 930 oem_props, oem_dict, OPTIONS.source_info_dict)) 931 script.Print("Target: %s" % CalculateFingerprint( 932 oem_props, oem_dict, OPTIONS.target_info_dict)) 933 934 script.Print("Verifying current system...") 935 936 device_specific.IncrementalOTA_VerifyBegin() 937 938 if oem_props is None: 939 # When blockimgdiff version is less than 3 (non-resumable block-based OTA), 940 # patching on a device that's already on the target build will damage the 941 # system. Because operations like move don't check the block state, they 942 # always apply the changes unconditionally. 943 if blockimgdiff_version <= 2: 944 script.AssertSomeFingerprint(source_fp) 945 else: 946 script.AssertSomeFingerprint(source_fp, target_fp) 947 else: 948 if blockimgdiff_version <= 2: 949 script.AssertSomeThumbprint( 950 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict)) 951 else: 952 script.AssertSomeThumbprint( 953 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict), 954 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict)) 955 956 # Check the required cache size (i.e. stashed blocks). 957 size = [] 958 if system_diff: 959 size.append(system_diff.required_cache) 960 if vendor_diff: 961 size.append(vendor_diff.required_cache) 962 963 if updating_boot: 964 boot_type, boot_device = common.GetTypeAndDevice( 965 "/boot", OPTIONS.source_info_dict) 966 d = common.Difference(target_boot, source_boot) 967 _, _, d = d.ComputePatch() 968 if d is None: 969 include_full_boot = True 970 common.ZipWriteStr(output_zip, "boot.img", target_boot.data) 971 else: 972 include_full_boot = False 973 974 print "boot target: %d source: %d diff: %d" % ( 975 target_boot.size, source_boot.size, len(d)) 976 977 common.ZipWriteStr(output_zip, "patch/boot.img.p", d) 978 979 script.PatchCheck("%s:%s:%d:%s:%d:%s" % 980 (boot_type, boot_device, 981 source_boot.size, source_boot.sha1, 982 target_boot.size, target_boot.sha1)) 983 size.append(target_boot.size) 984 985 if size: 986 script.CacheFreeSpaceCheck(max(size)) 987 988 device_specific.IncrementalOTA_VerifyEnd() 989 990 if OPTIONS.two_step: 991 script.WriteRawImage("/boot", "recovery.img") 992 script.AppendExtra(""" 993set_stage("%(bcb_dev)s", "2/3"); 994reboot_now("%(bcb_dev)s", ""); 995else 996""" % bcb_dev) 997 998 # Verify the existing partitions. 999 system_diff.WriteVerifyScript(script, touched_blocks_only=True) 1000 if vendor_diff: 1001 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True) 1002 1003 script.Comment("---- start making changes here ----") 1004 1005 device_specific.IncrementalOTA_InstallBegin() 1006 1007 system_diff.WriteScript(script, output_zip, 1008 progress=0.8 if vendor_diff else 0.9) 1009 1010 if vendor_diff: 1011 vendor_diff.WriteScript(script, output_zip, progress=0.1) 1012 1013 if OPTIONS.two_step: 1014 common.ZipWriteStr(output_zip, "boot.img", target_boot.data) 1015 script.WriteRawImage("/boot", "boot.img") 1016 print "writing full boot image (forced by two-step mode)" 1017 1018 if not OPTIONS.two_step: 1019 if updating_boot: 1020 if include_full_boot: 1021 print "boot image changed; including full." 1022 script.Print("Installing boot image...") 1023 script.WriteRawImage("/boot", "boot.img") 1024 else: 1025 # Produce the boot image by applying a patch to the current 1026 # contents of the boot partition, and write it back to the 1027 # partition. 1028 print "boot image changed; including patch." 1029 script.Print("Patching boot image...") 1030 script.ShowProgress(0.1, 10) 1031 script.ApplyPatch("%s:%s:%d:%s:%d:%s" 1032 % (boot_type, boot_device, 1033 source_boot.size, source_boot.sha1, 1034 target_boot.size, target_boot.sha1), 1035 "-", 1036 target_boot.size, target_boot.sha1, 1037 source_boot.sha1, "patch/boot.img.p") 1038 else: 1039 print "boot image unchanged; skipping." 1040 1041 # Do device-specific installation (eg, write radio image). 1042 device_specific.IncrementalOTA_InstallEnd() 1043 1044 if OPTIONS.extra_script is not None: 1045 script.AppendExtra(OPTIONS.extra_script) 1046 1047 if OPTIONS.wipe_user_data: 1048 script.Print("Erasing user data...") 1049 script.FormatPartition("/data") 1050 metadata["ota-wipe"] = "yes" 1051 1052 if OPTIONS.two_step: 1053 script.AppendExtra(""" 1054set_stage("%(bcb_dev)s", ""); 1055endif; 1056endif; 1057""" % bcb_dev) 1058 1059 script.SetProgress(1) 1060 # For downgrade OTAs, we prefer to use the update-binary in the source 1061 # build that is actually newer than the one in the target build. 1062 if OPTIONS.downgrade: 1063 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary) 1064 else: 1065 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary) 1066 metadata["ota-required-cache"] = str(script.required_cache) 1067 WriteMetadata(metadata, output_zip) 1068 1069 1070def WriteVerifyPackage(input_zip, output_zip): 1071 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict) 1072 1073 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties") 1074 recovery_mount_options = OPTIONS.info_dict.get( 1075 "recovery_mount_options") 1076 oem_dict = None 1077 if oem_props is not None and len(oem_props) > 0: 1078 if OPTIONS.oem_source is None: 1079 raise common.ExternalError("OEM source required for this build") 1080 script.Mount("/oem", recovery_mount_options) 1081 oem_dict = common.LoadDictionaryFromLines( 1082 open(OPTIONS.oem_source).readlines()) 1083 1084 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict) 1085 metadata = { 1086 "post-build": target_fp, 1087 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict, 1088 OPTIONS.info_dict), 1089 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict), 1090 } 1091 1092 device_specific = common.DeviceSpecificParams( 1093 input_zip=input_zip, 1094 input_version=OPTIONS.info_dict["recovery_api_version"], 1095 output_zip=output_zip, 1096 script=script, 1097 input_tmp=OPTIONS.input_tmp, 1098 metadata=metadata, 1099 info_dict=OPTIONS.info_dict) 1100 1101 AppendAssertions(script, OPTIONS.info_dict, oem_dict) 1102 1103 script.Print("Verifying device images against %s..." % target_fp) 1104 script.AppendExtra("") 1105 1106 script.Print("Verifying boot...") 1107 boot_img = common.GetBootableImage( 1108 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") 1109 boot_type, boot_device = common.GetTypeAndDevice( 1110 "/boot", OPTIONS.info_dict) 1111 script.Verify("%s:%s:%d:%s" % ( 1112 boot_type, boot_device, boot_img.size, boot_img.sha1)) 1113 script.AppendExtra("") 1114 1115 script.Print("Verifying recovery...") 1116 recovery_img = common.GetBootableImage( 1117 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") 1118 recovery_type, recovery_device = common.GetTypeAndDevice( 1119 "/recovery", OPTIONS.info_dict) 1120 script.Verify("%s:%s:%d:%s" % ( 1121 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1)) 1122 script.AppendExtra("") 1123 1124 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict) 1125 system_tgt.ResetFileMap() 1126 system_diff = common.BlockDifference("system", system_tgt, src=None) 1127 system_diff.WriteStrictVerifyScript(script) 1128 1129 if HasVendorPartition(input_zip): 1130 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict) 1131 vendor_tgt.ResetFileMap() 1132 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None) 1133 vendor_diff.WriteStrictVerifyScript(script) 1134 1135 # Device specific partitions, such as radio, bootloader and etc. 1136 device_specific.VerifyOTA_Assertions() 1137 1138 script.SetProgress(1.0) 1139 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary) 1140 metadata["ota-required-cache"] = str(script.required_cache) 1141 WriteMetadata(metadata, output_zip) 1142 1143 1144def WriteABOTAPackageWithBrilloScript(target_file, output_file, 1145 source_file=None): 1146 """Generate an Android OTA package that has A/B update payload.""" 1147 1148 # Setup signing keys. 1149 if OPTIONS.package_key is None: 1150 OPTIONS.package_key = OPTIONS.info_dict.get( 1151 "default_system_dev_certificate", 1152 "build/target/product/security/testkey") 1153 1154 # A/B updater expects key in RSA format. 1155 cmd = ["openssl", "pkcs8", 1156 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix, 1157 "-inform", "DER", "-nocrypt"] 1158 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key") 1159 cmd.extend(["-out", rsa_key]) 1160 p1 = common.Run(cmd, stdout=subprocess.PIPE) 1161 p1.wait() 1162 assert p1.returncode == 0, "openssl pkcs8 failed" 1163 1164 # Stage the output zip package for signing. 1165 temp_zip_file = tempfile.NamedTemporaryFile() 1166 output_zip = zipfile.ZipFile(temp_zip_file, "w", 1167 compression=zipfile.ZIP_DEFLATED) 1168 1169 # Metadata to comply with Android OTA package format. 1170 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None) 1171 oem_dict = None 1172 if oem_props: 1173 if OPTIONS.oem_source is None: 1174 raise common.ExternalError("OEM source required for this build") 1175 oem_dict = common.LoadDictionaryFromLines( 1176 open(OPTIONS.oem_source).readlines()) 1177 1178 metadata = { 1179 "post-build": CalculateFingerprint(oem_props, oem_dict, 1180 OPTIONS.info_dict), 1181 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict, 1182 OPTIONS.info_dict), 1183 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict), 1184 "ota-required-cache": "0", 1185 "ota-type": "AB", 1186 } 1187 1188 if source_file is not None: 1189 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict, 1190 OPTIONS.source_info_dict) 1191 1192 # 1. Generate payload. 1193 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin") 1194 cmd = ["brillo_update_payload", "generate", 1195 "--payload", payload_file, 1196 "--target_image", target_file] 1197 if source_file is not None: 1198 cmd.extend(["--source_image", source_file]) 1199 p1 = common.Run(cmd, stdout=subprocess.PIPE) 1200 p1.wait() 1201 assert p1.returncode == 0, "brillo_update_payload generate failed" 1202 1203 # 2. Generate hashes of the payload and metadata files. 1204 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin") 1205 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin") 1206 cmd = ["brillo_update_payload", "hash", 1207 "--unsigned_payload", payload_file, 1208 "--signature_size", "256", 1209 "--metadata_hash_file", metadata_sig_file, 1210 "--payload_hash_file", payload_sig_file] 1211 p1 = common.Run(cmd, stdout=subprocess.PIPE) 1212 p1.wait() 1213 assert p1.returncode == 0, "brillo_update_payload hash failed" 1214 1215 # 3. Sign the hashes and insert them back into the payload file. 1216 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-", 1217 suffix=".bin") 1218 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-", 1219 suffix=".bin") 1220 # 3a. Sign the payload hash. 1221 cmd = ["openssl", "pkeyutl", "-sign", 1222 "-inkey", rsa_key, 1223 "-pkeyopt", "digest:sha256", 1224 "-in", payload_sig_file, 1225 "-out", signed_payload_sig_file] 1226 p1 = common.Run(cmd, stdout=subprocess.PIPE) 1227 p1.wait() 1228 assert p1.returncode == 0, "openssl sign payload failed" 1229 1230 # 3b. Sign the metadata hash. 1231 cmd = ["openssl", "pkeyutl", "-sign", 1232 "-inkey", rsa_key, 1233 "-pkeyopt", "digest:sha256", 1234 "-in", metadata_sig_file, 1235 "-out", signed_metadata_sig_file] 1236 p1 = common.Run(cmd, stdout=subprocess.PIPE) 1237 p1.wait() 1238 assert p1.returncode == 0, "openssl sign metadata failed" 1239 1240 # 3c. Insert the signatures back into the payload file. 1241 signed_payload_file = common.MakeTempFile(prefix="signed-payload-", 1242 suffix=".bin") 1243 cmd = ["brillo_update_payload", "sign", 1244 "--unsigned_payload", payload_file, 1245 "--payload", signed_payload_file, 1246 "--signature_size", "256", 1247 "--metadata_signature_file", signed_metadata_sig_file, 1248 "--payload_signature_file", signed_payload_sig_file] 1249 p1 = common.Run(cmd, stdout=subprocess.PIPE) 1250 p1.wait() 1251 assert p1.returncode == 0, "brillo_update_payload sign failed" 1252 1253 # 4. Dump the signed payload properties. 1254 properties_file = common.MakeTempFile(prefix="payload-properties-", 1255 suffix=".txt") 1256 cmd = ["brillo_update_payload", "properties", 1257 "--payload", signed_payload_file, 1258 "--properties_file", properties_file] 1259 p1 = common.Run(cmd, stdout=subprocess.PIPE) 1260 p1.wait() 1261 assert p1.returncode == 0, "brillo_update_payload properties failed" 1262 1263 # Add the signed payload file and properties into the zip. 1264 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt") 1265 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin", 1266 compress_type=zipfile.ZIP_STORED) 1267 WriteMetadata(metadata, output_zip) 1268 1269 # Sign the whole package to comply with the Android OTA package format. 1270 common.ZipClose(output_zip) 1271 SignOutput(temp_zip_file.name, output_file) 1272 temp_zip_file.close() 1273 1274 1275class FileDifference(object): 1276 def __init__(self, partition, source_zip, target_zip, output_zip): 1277 self.deferred_patch_list = None 1278 print "Loading target..." 1279 self.target_data = target_data = LoadPartitionFiles(target_zip, partition) 1280 print "Loading source..." 1281 self.source_data = source_data = LoadPartitionFiles(source_zip, partition) 1282 1283 self.verbatim_targets = verbatim_targets = [] 1284 self.patch_list = patch_list = [] 1285 diffs = [] 1286 self.renames = renames = {} 1287 known_paths = set() 1288 largest_source_size = 0 1289 1290 matching_file_cache = {} 1291 for fn, sf in source_data.items(): 1292 assert fn == sf.name 1293 matching_file_cache["path:" + fn] = sf 1294 if fn in target_data.keys(): 1295 AddToKnownPaths(fn, known_paths) 1296 # Only allow eligibility for filename/sha matching 1297 # if there isn't a perfect path match. 1298 if target_data.get(sf.name) is None: 1299 matching_file_cache["file:" + fn.split("/")[-1]] = sf 1300 matching_file_cache["sha:" + sf.sha1] = sf 1301 1302 for fn in sorted(target_data.keys()): 1303 tf = target_data[fn] 1304 assert fn == tf.name 1305 sf = ClosestFileMatch(tf, matching_file_cache, renames) 1306 if sf is not None and sf.name != tf.name: 1307 print "File has moved from " + sf.name + " to " + tf.name 1308 renames[sf.name] = tf 1309 1310 if sf is None or fn in OPTIONS.require_verbatim: 1311 # This file should be included verbatim 1312 if fn in OPTIONS.prohibit_verbatim: 1313 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,)) 1314 print "send", fn, "verbatim" 1315 tf.AddToZip(output_zip) 1316 verbatim_targets.append((fn, tf.size, tf.sha1)) 1317 if fn in target_data.keys(): 1318 AddToKnownPaths(fn, known_paths) 1319 elif tf.sha1 != sf.sha1: 1320 # File is different; consider sending as a patch 1321 diffs.append(common.Difference(tf, sf)) 1322 else: 1323 # Target file data identical to source (may still be renamed) 1324 pass 1325 1326 common.ComputeDifferences(diffs) 1327 1328 for diff in diffs: 1329 tf, sf, d = diff.GetPatch() 1330 path = "/".join(tf.name.split("/")[:-1]) 1331 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \ 1332 path not in known_paths: 1333 # patch is almost as big as the file; don't bother patching 1334 # or a patch + rename cannot take place due to the target 1335 # directory not existing 1336 tf.AddToZip(output_zip) 1337 verbatim_targets.append((tf.name, tf.size, tf.sha1)) 1338 if sf.name in renames: 1339 del renames[sf.name] 1340 AddToKnownPaths(tf.name, known_paths) 1341 else: 1342 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d) 1343 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest())) 1344 largest_source_size = max(largest_source_size, sf.size) 1345 1346 self.largest_source_size = largest_source_size 1347 1348 def EmitVerification(self, script): 1349 so_far = 0 1350 for tf, sf, _, _ in self.patch_list: 1351 if tf.name != sf.name: 1352 script.SkipNextActionIfTargetExists(tf.name, tf.sha1) 1353 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1) 1354 so_far += sf.size 1355 return so_far 1356 1357 def EmitExplicitTargetVerification(self, script): 1358 for fn, _, sha1 in self.verbatim_targets: 1359 if fn[-1] != "/": 1360 script.FileCheck("/"+fn, sha1) 1361 for tf, _, _, _ in self.patch_list: 1362 script.FileCheck(tf.name, tf.sha1) 1363 1364 def RemoveUnneededFiles(self, script, extras=()): 1365 file_list = ["/" + i[0] for i in self.verbatim_targets] 1366 file_list += ["/" + i for i in self.source_data 1367 if i not in self.target_data and i not in self.renames] 1368 file_list += list(extras) 1369 # Sort the list in descending order, which removes all the files first 1370 # before attempting to remove the folder. (Bug: 22960996) 1371 script.DeleteFiles(sorted(file_list, reverse=True)) 1372 1373 def TotalPatchSize(self): 1374 return sum(i[1].size for i in self.patch_list) 1375 1376 def EmitPatches(self, script, total_patch_size, so_far): 1377 self.deferred_patch_list = deferred_patch_list = [] 1378 for item in self.patch_list: 1379 tf, sf, _, _ = item 1380 if tf.name == "system/build.prop": 1381 deferred_patch_list.append(item) 1382 continue 1383 if sf.name != tf.name: 1384 script.SkipNextActionIfTargetExists(tf.name, tf.sha1) 1385 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1, 1386 "patch/" + sf.name + ".p") 1387 so_far += tf.size 1388 script.SetProgress(so_far / total_patch_size) 1389 return so_far 1390 1391 def EmitDeferredPatches(self, script): 1392 for item in self.deferred_patch_list: 1393 tf, sf, _, _ = item 1394 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, 1395 "patch/" + sf.name + ".p") 1396 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None) 1397 1398 def EmitRenames(self, script): 1399 if len(self.renames) > 0: 1400 script.Print("Renaming files...") 1401 for src, tgt in self.renames.iteritems(): 1402 print "Renaming " + src + " to " + tgt.name 1403 script.RenameFile(src, tgt.name) 1404 1405 1406def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): 1407 target_has_recovery_patch = HasRecoveryPatch(target_zip) 1408 source_has_recovery_patch = HasRecoveryPatch(source_zip) 1409 1410 if (OPTIONS.block_based and 1411 target_has_recovery_patch and 1412 source_has_recovery_patch): 1413 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip) 1414 1415 source_version = OPTIONS.source_info_dict["recovery_api_version"] 1416 target_version = OPTIONS.target_info_dict["recovery_api_version"] 1417 1418 if source_version == 0: 1419 print ("WARNING: generating edify script for a source that " 1420 "can't install it.") 1421 script = edify_generator.EdifyGenerator( 1422 source_version, OPTIONS.target_info_dict, 1423 fstab=OPTIONS.source_info_dict["fstab"]) 1424 1425 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties") 1426 recovery_mount_options = OPTIONS.source_info_dict.get( 1427 "recovery_mount_options") 1428 oem_dict = None 1429 if oem_props is not None and len(oem_props) > 0: 1430 if OPTIONS.oem_source is None: 1431 raise common.ExternalError("OEM source required for this build") 1432 if not OPTIONS.oem_no_mount: 1433 script.Mount("/oem", recovery_mount_options) 1434 oem_dict = common.LoadDictionaryFromLines( 1435 open(OPTIONS.oem_source).readlines()) 1436 1437 metadata = { 1438 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict, 1439 OPTIONS.source_info_dict), 1440 "ota-type": "FILE", 1441 } 1442 1443 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict) 1444 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict) 1445 is_downgrade = long(post_timestamp) < long(pre_timestamp) 1446 1447 if OPTIONS.downgrade: 1448 metadata["ota-downgrade"] = "yes" 1449 if not is_downgrade: 1450 raise RuntimeError("--downgrade specified but no downgrade detected: " 1451 "pre: %s, post: %s" % (pre_timestamp, post_timestamp)) 1452 else: 1453 if is_downgrade: 1454 # Non-fatal here to allow generating such a package which may require 1455 # manual work to adjust the post-timestamp. A legit use case is that we 1456 # cut a new build C (after having A and B), but want to enfore the 1457 # update path of A -> C -> B. Specifying --downgrade may not help since 1458 # that would enforce a data wipe for C -> B update. 1459 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n" 1460 "The package may not be deployed properly. " 1461 "Try --downgrade?\n" % (pre_timestamp, post_timestamp)) 1462 metadata["post-timestamp"] = post_timestamp 1463 1464 device_specific = common.DeviceSpecificParams( 1465 source_zip=source_zip, 1466 source_version=source_version, 1467 target_zip=target_zip, 1468 target_version=target_version, 1469 output_zip=output_zip, 1470 script=script, 1471 metadata=metadata, 1472 info_dict=OPTIONS.source_info_dict) 1473 1474 system_diff = FileDifference("system", source_zip, target_zip, output_zip) 1475 script.Mount("/system", recovery_mount_options) 1476 if HasVendorPartition(target_zip): 1477 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip) 1478 script.Mount("/vendor", recovery_mount_options) 1479 else: 1480 vendor_diff = None 1481 1482 target_fp = CalculateFingerprint(oem_props, oem_dict, 1483 OPTIONS.target_info_dict) 1484 source_fp = CalculateFingerprint(oem_props, oem_dict, 1485 OPTIONS.source_info_dict) 1486 1487 if oem_props is None: 1488 script.AssertSomeFingerprint(source_fp, target_fp) 1489 else: 1490 script.AssertSomeThumbprint( 1491 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict), 1492 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict)) 1493 1494 metadata["pre-build"] = source_fp 1495 metadata["post-build"] = target_fp 1496 1497 source_boot = common.GetBootableImage( 1498 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", 1499 OPTIONS.source_info_dict) 1500 target_boot = common.GetBootableImage( 1501 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT") 1502 updating_boot = (not OPTIONS.two_step and 1503 (source_boot.data != target_boot.data)) 1504 1505 source_recovery = common.GetBootableImage( 1506 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY", 1507 OPTIONS.source_info_dict) 1508 target_recovery = common.GetBootableImage( 1509 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY") 1510 updating_recovery = (source_recovery.data != target_recovery.data) 1511 1512 # Here's how we divide up the progress bar: 1513 # 0.1 for verifying the start state (PatchCheck calls) 1514 # 0.8 for applying patches (ApplyPatch calls) 1515 # 0.1 for unpacking verbatim files, symlinking, and doing the 1516 # device-specific commands. 1517 1518 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict) 1519 device_specific.IncrementalOTA_Assertions() 1520 1521 # Two-step incremental package strategy (in chronological order, 1522 # which is *not* the order in which the generated script has 1523 # things): 1524 # 1525 # if stage is not "2/3" or "3/3": 1526 # do verification on current system 1527 # write recovery image to boot partition 1528 # set stage to "2/3" 1529 # reboot to boot partition and restart recovery 1530 # else if stage is "2/3": 1531 # write recovery image to recovery partition 1532 # set stage to "3/3" 1533 # reboot to recovery partition and restart recovery 1534 # else: 1535 # (stage must be "3/3") 1536 # perform update: 1537 # patch system files, etc. 1538 # force full install of new boot image 1539 # set up system to update recovery partition on first boot 1540 # complete script normally 1541 # (allow recovery to mark itself finished and reboot) 1542 1543 if OPTIONS.two_step: 1544 if not OPTIONS.source_info_dict.get("multistage_support", None): 1545 assert False, "two-step packages not supported by this build" 1546 fs = OPTIONS.source_info_dict["fstab"]["/misc"] 1547 assert fs.fs_type.upper() == "EMMC", \ 1548 "two-step packages only supported on devices with EMMC /misc partitions" 1549 bcb_dev = {"bcb_dev": fs.device} 1550 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data) 1551 script.AppendExtra(""" 1552if get_stage("%(bcb_dev)s") == "2/3" then 1553""" % bcb_dev) 1554 script.AppendExtra("sleep(20);\n") 1555 script.WriteRawImage("/recovery", "recovery.img") 1556 script.AppendExtra(""" 1557set_stage("%(bcb_dev)s", "3/3"); 1558reboot_now("%(bcb_dev)s", "recovery"); 1559else if get_stage("%(bcb_dev)s") != "3/3" then 1560""" % bcb_dev) 1561 1562 # Dump fingerprints 1563 script.Print("Source: %s" % (source_fp,)) 1564 script.Print("Target: %s" % (target_fp,)) 1565 1566 script.Print("Verifying current system...") 1567 1568 device_specific.IncrementalOTA_VerifyBegin() 1569 1570 script.ShowProgress(0.1, 0) 1571 so_far = system_diff.EmitVerification(script) 1572 if vendor_diff: 1573 so_far += vendor_diff.EmitVerification(script) 1574 1575 size = [] 1576 if system_diff.patch_list: 1577 size.append(system_diff.largest_source_size) 1578 if vendor_diff: 1579 if vendor_diff.patch_list: 1580 size.append(vendor_diff.largest_source_size) 1581 1582 if updating_boot: 1583 d = common.Difference(target_boot, source_boot) 1584 _, _, d = d.ComputePatch() 1585 print "boot target: %d source: %d diff: %d" % ( 1586 target_boot.size, source_boot.size, len(d)) 1587 1588 common.ZipWriteStr(output_zip, "patch/boot.img.p", d) 1589 1590 boot_type, boot_device = common.GetTypeAndDevice( 1591 "/boot", OPTIONS.source_info_dict) 1592 1593 script.PatchCheck("%s:%s:%d:%s:%d:%s" % 1594 (boot_type, boot_device, 1595 source_boot.size, source_boot.sha1, 1596 target_boot.size, target_boot.sha1)) 1597 so_far += source_boot.size 1598 size.append(target_boot.size) 1599 1600 if size: 1601 script.CacheFreeSpaceCheck(max(size)) 1602 1603 device_specific.IncrementalOTA_VerifyEnd() 1604 1605 if OPTIONS.two_step: 1606 script.WriteRawImage("/boot", "recovery.img") 1607 script.AppendExtra(""" 1608set_stage("%(bcb_dev)s", "2/3"); 1609reboot_now("%(bcb_dev)s", ""); 1610else 1611""" % bcb_dev) 1612 1613 script.Comment("---- start making changes here ----") 1614 1615 device_specific.IncrementalOTA_InstallBegin() 1616 1617 if OPTIONS.two_step: 1618 common.ZipWriteStr(output_zip, "boot.img", target_boot.data) 1619 script.WriteRawImage("/boot", "boot.img") 1620 print "writing full boot image (forced by two-step mode)" 1621 1622 script.Print("Removing unneeded files...") 1623 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",)) 1624 if vendor_diff: 1625 vendor_diff.RemoveUnneededFiles(script) 1626 1627 script.ShowProgress(0.8, 0) 1628 total_patch_size = 1.0 + system_diff.TotalPatchSize() 1629 if vendor_diff: 1630 total_patch_size += vendor_diff.TotalPatchSize() 1631 if updating_boot: 1632 total_patch_size += target_boot.size 1633 1634 script.Print("Patching system files...") 1635 so_far = system_diff.EmitPatches(script, total_patch_size, 0) 1636 if vendor_diff: 1637 script.Print("Patching vendor files...") 1638 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far) 1639 1640 if not OPTIONS.two_step: 1641 if updating_boot: 1642 # Produce the boot image by applying a patch to the current 1643 # contents of the boot partition, and write it back to the 1644 # partition. 1645 script.Print("Patching boot image...") 1646 script.ApplyPatch("%s:%s:%d:%s:%d:%s" 1647 % (boot_type, boot_device, 1648 source_boot.size, source_boot.sha1, 1649 target_boot.size, target_boot.sha1), 1650 "-", 1651 target_boot.size, target_boot.sha1, 1652 source_boot.sha1, "patch/boot.img.p") 1653 so_far += target_boot.size 1654 script.SetProgress(so_far / total_patch_size) 1655 print "boot image changed; including." 1656 else: 1657 print "boot image unchanged; skipping." 1658 1659 system_items = ItemSet("system", "META/filesystem_config.txt") 1660 if vendor_diff: 1661 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt") 1662 1663 if updating_recovery: 1664 # Recovery is generated as a patch using both the boot image 1665 # (which contains the same linux kernel as recovery) and the file 1666 # /system/etc/recovery-resource.dat (which contains all the images 1667 # used in the recovery UI) as sources. This lets us minimize the 1668 # size of the patch, which must be included in every OTA package. 1669 # 1670 # For older builds where recovery-resource.dat is not present, we 1671 # use only the boot image as the source. 1672 1673 if not target_has_recovery_patch: 1674 def output_sink(fn, data): 1675 common.ZipWriteStr(output_zip, "recovery/" + fn, data) 1676 system_items.Get("system/" + fn) 1677 1678 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink, 1679 target_recovery, target_boot) 1680 script.DeleteFiles(["/system/recovery-from-boot.p", 1681 "/system/etc/recovery.img", 1682 "/system/etc/install-recovery.sh"]) 1683 print "recovery image changed; including as patch from boot." 1684 else: 1685 print "recovery image unchanged; skipping." 1686 1687 script.ShowProgress(0.1, 10) 1688 1689 target_symlinks = CopyPartitionFiles(system_items, target_zip, None) 1690 if vendor_diff: 1691 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None)) 1692 1693 temp_script = script.MakeTemporary() 1694 system_items.GetMetadata(target_zip) 1695 system_items.Get("system").SetPermissions(temp_script) 1696 if vendor_diff: 1697 vendor_items.GetMetadata(target_zip) 1698 vendor_items.Get("vendor").SetPermissions(temp_script) 1699 1700 # Note that this call will mess up the trees of Items, so make sure 1701 # we're done with them. 1702 source_symlinks = CopyPartitionFiles(system_items, source_zip, None) 1703 if vendor_diff: 1704 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None)) 1705 1706 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks]) 1707 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks]) 1708 1709 # Delete all the symlinks in source that aren't in target. This 1710 # needs to happen before verbatim files are unpacked, in case a 1711 # symlink in the source is replaced by a real file in the target. 1712 1713 # If a symlink in the source will be replaced by a regular file, we cannot 1714 # delete the symlink/file in case the package gets applied again. For such 1715 # a symlink, we prepend a sha1_check() to detect if it has been updated. 1716 # (Bug: 23646151) 1717 replaced_symlinks = dict() 1718 if system_diff: 1719 for i in system_diff.verbatim_targets: 1720 replaced_symlinks["/%s" % (i[0],)] = i[2] 1721 if vendor_diff: 1722 for i in vendor_diff.verbatim_targets: 1723 replaced_symlinks["/%s" % (i[0],)] = i[2] 1724 1725 if system_diff: 1726 for tf in system_diff.renames.values(): 1727 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1 1728 if vendor_diff: 1729 for tf in vendor_diff.renames.values(): 1730 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1 1731 1732 always_delete = [] 1733 may_delete = [] 1734 for dest, link in source_symlinks: 1735 if link not in target_symlinks_d: 1736 if link in replaced_symlinks: 1737 may_delete.append((link, replaced_symlinks[link])) 1738 else: 1739 always_delete.append(link) 1740 script.DeleteFiles(always_delete) 1741 script.DeleteFilesIfNotMatching(may_delete) 1742 1743 if system_diff.verbatim_targets: 1744 script.Print("Unpacking new system files...") 1745 script.UnpackPackageDir("system", "/system") 1746 if vendor_diff and vendor_diff.verbatim_targets: 1747 script.Print("Unpacking new vendor files...") 1748 script.UnpackPackageDir("vendor", "/vendor") 1749 1750 if updating_recovery and not target_has_recovery_patch: 1751 script.Print("Unpacking new recovery...") 1752 script.UnpackPackageDir("recovery", "/system") 1753 1754 system_diff.EmitRenames(script) 1755 if vendor_diff: 1756 vendor_diff.EmitRenames(script) 1757 1758 script.Print("Symlinks and permissions...") 1759 1760 # Create all the symlinks that don't already exist, or point to 1761 # somewhere different than what we want. Delete each symlink before 1762 # creating it, since the 'symlink' command won't overwrite. 1763 to_create = [] 1764 for dest, link in target_symlinks: 1765 if link in source_symlinks_d: 1766 if dest != source_symlinks_d[link]: 1767 to_create.append((dest, link)) 1768 else: 1769 to_create.append((dest, link)) 1770 script.DeleteFiles([i[1] for i in to_create]) 1771 script.MakeSymlinks(to_create) 1772 1773 # Now that the symlinks are created, we can set all the 1774 # permissions. 1775 script.AppendScript(temp_script) 1776 1777 # Do device-specific installation (eg, write radio image). 1778 device_specific.IncrementalOTA_InstallEnd() 1779 1780 if OPTIONS.extra_script is not None: 1781 script.AppendExtra(OPTIONS.extra_script) 1782 1783 # Patch the build.prop file last, so if something fails but the 1784 # device can still come up, it appears to be the old build and will 1785 # get set the OTA package again to retry. 1786 script.Print("Patching remaining system files...") 1787 system_diff.EmitDeferredPatches(script) 1788 1789 if OPTIONS.wipe_user_data: 1790 script.Print("Erasing user data...") 1791 script.FormatPartition("/data") 1792 metadata["ota-wipe"] = "yes" 1793 1794 if OPTIONS.two_step: 1795 script.AppendExtra(""" 1796set_stage("%(bcb_dev)s", ""); 1797endif; 1798endif; 1799""" % bcb_dev) 1800 1801 if OPTIONS.verify and system_diff: 1802 script.Print("Remounting and verifying system partition files...") 1803 script.Unmount("/system") 1804 script.Mount("/system", recovery_mount_options) 1805 system_diff.EmitExplicitTargetVerification(script) 1806 1807 if OPTIONS.verify and vendor_diff: 1808 script.Print("Remounting and verifying vendor partition files...") 1809 script.Unmount("/vendor") 1810 script.Mount("/vendor", recovery_mount_options) 1811 vendor_diff.EmitExplicitTargetVerification(script) 1812 1813 # For downgrade OTAs, we prefer to use the update-binary in the source 1814 # build that is actually newer than the one in the target build. 1815 if OPTIONS.downgrade: 1816 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary) 1817 else: 1818 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary) 1819 1820 metadata["ota-required-cache"] = str(script.required_cache) 1821 WriteMetadata(metadata, output_zip) 1822 1823 1824def main(argv): 1825 1826 def option_handler(o, a): 1827 if o == "--board_config": 1828 pass # deprecated 1829 elif o in ("-k", "--package_key"): 1830 OPTIONS.package_key = a 1831 elif o in ("-i", "--incremental_from"): 1832 OPTIONS.incremental_source = a 1833 elif o == "--full_radio": 1834 OPTIONS.full_radio = True 1835 elif o == "--full_bootloader": 1836 OPTIONS.full_bootloader = True 1837 elif o in ("-w", "--wipe_user_data"): 1838 OPTIONS.wipe_user_data = True 1839 elif o in ("-n", "--no_prereq"): 1840 OPTIONS.omit_prereq = True 1841 elif o == "--downgrade": 1842 OPTIONS.downgrade = True 1843 OPTIONS.wipe_user_data = True 1844 elif o in ("-o", "--oem_settings"): 1845 OPTIONS.oem_source = a 1846 elif o == "--oem_no_mount": 1847 OPTIONS.oem_no_mount = True 1848 elif o in ("-e", "--extra_script"): 1849 OPTIONS.extra_script = a 1850 elif o in ("-a", "--aslr_mode"): 1851 if a in ("on", "On", "true", "True", "yes", "Yes"): 1852 OPTIONS.aslr_mode = True 1853 else: 1854 OPTIONS.aslr_mode = False 1855 elif o in ("-t", "--worker_threads"): 1856 if a.isdigit(): 1857 OPTIONS.worker_threads = int(a) 1858 else: 1859 raise ValueError("Cannot parse value %r for option %r - only " 1860 "integers are allowed." % (a, o)) 1861 elif o in ("-2", "--two_step"): 1862 OPTIONS.two_step = True 1863 elif o == "--no_signing": 1864 OPTIONS.no_signing = True 1865 elif o == "--verify": 1866 OPTIONS.verify = True 1867 elif o == "--block": 1868 OPTIONS.block_based = True 1869 elif o in ("-b", "--binary"): 1870 OPTIONS.updater_binary = a 1871 elif o in ("--no_fallback_to_full",): 1872 OPTIONS.fallback_to_full = False 1873 elif o == "--stash_threshold": 1874 try: 1875 OPTIONS.stash_threshold = float(a) 1876 except ValueError: 1877 raise ValueError("Cannot parse value %r for option %r - expecting " 1878 "a float" % (a, o)) 1879 elif o == "--gen_verify": 1880 OPTIONS.gen_verify = True 1881 elif o == "--log_diff": 1882 OPTIONS.log_diff = a 1883 else: 1884 return False 1885 return True 1886 1887 args = common.ParseOptions(argv, __doc__, 1888 extra_opts="b:k:i:d:wne:t:a:2o:", 1889 extra_long_opts=[ 1890 "board_config=", 1891 "package_key=", 1892 "incremental_from=", 1893 "full_radio", 1894 "full_bootloader", 1895 "wipe_user_data", 1896 "no_prereq", 1897 "downgrade", 1898 "extra_script=", 1899 "worker_threads=", 1900 "aslr_mode=", 1901 "two_step", 1902 "no_signing", 1903 "block", 1904 "binary=", 1905 "oem_settings=", 1906 "oem_no_mount", 1907 "verify", 1908 "no_fallback_to_full", 1909 "stash_threshold=", 1910 "gen_verify", 1911 "log_diff=", 1912 ], extra_option_handler=option_handler) 1913 1914 if len(args) != 2: 1915 common.Usage(__doc__) 1916 sys.exit(1) 1917 1918 if OPTIONS.downgrade: 1919 # Sanity check to enforce a data wipe. 1920 if not OPTIONS.wipe_user_data: 1921 raise ValueError("Cannot downgrade without a data wipe") 1922 1923 # We should only allow downgrading incrementals (as opposed to full). 1924 # Otherwise the device may go back from arbitrary build with this full 1925 # OTA package. 1926 if OPTIONS.incremental_source is None: 1927 raise ValueError("Cannot generate downgradable full OTAs - consider" 1928 "using --omit_prereq?") 1929 1930 # Load the dict file from the zip directly to have a peek at the OTA type. 1931 # For packages using A/B update, unzipping is not needed. 1932 input_zip = zipfile.ZipFile(args[0], "r") 1933 OPTIONS.info_dict = common.LoadInfoDict(input_zip) 1934 common.ZipClose(input_zip) 1935 1936 ab_update = OPTIONS.info_dict.get("ab_update") == "true" 1937 1938 if ab_update: 1939 if OPTIONS.incremental_source is not None: 1940 OPTIONS.target_info_dict = OPTIONS.info_dict 1941 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r") 1942 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip) 1943 common.ZipClose(source_zip) 1944 1945 if OPTIONS.verbose: 1946 print "--- target info ---" 1947 common.DumpInfoDict(OPTIONS.info_dict) 1948 1949 if OPTIONS.incremental_source is not None: 1950 print "--- source info ---" 1951 common.DumpInfoDict(OPTIONS.source_info_dict) 1952 1953 WriteABOTAPackageWithBrilloScript( 1954 target_file=args[0], 1955 output_file=args[1], 1956 source_file=OPTIONS.incremental_source) 1957 1958 print "done." 1959 return 1960 1961 if OPTIONS.extra_script is not None: 1962 OPTIONS.extra_script = open(OPTIONS.extra_script).read() 1963 1964 print "unzipping target target-files..." 1965 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0]) 1966 1967 OPTIONS.target_tmp = OPTIONS.input_tmp 1968 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp) 1969 1970 if OPTIONS.verbose: 1971 print "--- target info ---" 1972 common.DumpInfoDict(OPTIONS.info_dict) 1973 1974 # If the caller explicitly specified the device-specific extensions 1975 # path via -s/--device_specific, use that. Otherwise, use 1976 # META/releasetools.py if it is present in the target target_files. 1977 # Otherwise, take the path of the file from 'tool_extensions' in the 1978 # info dict and look for that in the local filesystem, relative to 1979 # the current directory. 1980 1981 if OPTIONS.device_specific is None: 1982 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py") 1983 if os.path.exists(from_input): 1984 print "(using device-specific extensions from target_files)" 1985 OPTIONS.device_specific = from_input 1986 else: 1987 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None) 1988 1989 if OPTIONS.device_specific is not None: 1990 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific) 1991 1992 if OPTIONS.info_dict.get("no_recovery") == "true": 1993 raise common.ExternalError( 1994 "--- target build has specified no recovery ---") 1995 1996 # Use the default key to sign the package if not specified with package_key. 1997 if not OPTIONS.no_signing: 1998 if OPTIONS.package_key is None: 1999 OPTIONS.package_key = OPTIONS.info_dict.get( 2000 "default_system_dev_certificate", 2001 "build/target/product/security/testkey") 2002 2003 # Set up the output zip. Create a temporary zip file if signing is needed. 2004 if OPTIONS.no_signing: 2005 if os.path.exists(args[1]): 2006 os.unlink(args[1]) 2007 output_zip = zipfile.ZipFile(args[1], "w", 2008 compression=zipfile.ZIP_DEFLATED) 2009 else: 2010 temp_zip_file = tempfile.NamedTemporaryFile() 2011 output_zip = zipfile.ZipFile(temp_zip_file, "w", 2012 compression=zipfile.ZIP_DEFLATED) 2013 2014 # Non A/B OTAs rely on /cache partition to store temporary files. 2015 cache_size = OPTIONS.info_dict.get("cache_size", None) 2016 if cache_size is None: 2017 print "--- can't determine the cache partition size ---" 2018 OPTIONS.cache_size = cache_size 2019 2020 # Generate a verify package. 2021 if OPTIONS.gen_verify: 2022 WriteVerifyPackage(input_zip, output_zip) 2023 2024 # Generate a full OTA. 2025 elif OPTIONS.incremental_source is None: 2026 WriteFullOTAPackage(input_zip, output_zip) 2027 2028 # Generate an incremental OTA. It will fall back to generate a full OTA on 2029 # failure unless no_fallback_to_full is specified. 2030 else: 2031 print "unzipping source target-files..." 2032 OPTIONS.source_tmp, source_zip = common.UnzipTemp( 2033 OPTIONS.incremental_source) 2034 OPTIONS.target_info_dict = OPTIONS.info_dict 2035 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip, 2036 OPTIONS.source_tmp) 2037 if OPTIONS.verbose: 2038 print "--- source info ---" 2039 common.DumpInfoDict(OPTIONS.source_info_dict) 2040 try: 2041 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip) 2042 if OPTIONS.log_diff: 2043 out_file = open(OPTIONS.log_diff, 'w') 2044 import target_files_diff 2045 target_files_diff.recursiveDiff('', 2046 OPTIONS.source_tmp, 2047 OPTIONS.input_tmp, 2048 out_file) 2049 out_file.close() 2050 except ValueError: 2051 if not OPTIONS.fallback_to_full: 2052 raise 2053 print "--- failed to build incremental; falling back to full ---" 2054 OPTIONS.incremental_source = None 2055 WriteFullOTAPackage(input_zip, output_zip) 2056 2057 common.ZipClose(output_zip) 2058 2059 # Sign the generated zip package unless no_signing is specified. 2060 if not OPTIONS.no_signing: 2061 SignOutput(temp_zip_file.name, args[1]) 2062 temp_zip_file.close() 2063 2064 print "done." 2065 2066 2067if __name__ == '__main__': 2068 try: 2069 common.CloseInheritedPipes() 2070 main(sys.argv[1:]) 2071 except common.ExternalError as e: 2072 print 2073 print " ERROR: %s" % (e,) 2074 print 2075 sys.exit(1) 2076 finally: 2077 common.Cleanup() 2078