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