ota_from_target_files.py revision d06f07eef48005a3fa99fdd0ca5380d60c5ae459
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 metadata["pre-build-incremental"] = GetBuildProp( 838 "ro.build.version.incremental", OPTIONS.source_info_dict) 839 metadata["post-build-incremental"] = GetBuildProp( 840 "ro.build.version.incremental", OPTIONS.target_info_dict) 841 842 source_boot = common.GetBootableImage( 843 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", 844 OPTIONS.source_info_dict) 845 target_boot = common.GetBootableImage( 846 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT") 847 updating_boot = (not OPTIONS.two_step and 848 (source_boot.data != target_boot.data)) 849 850 target_recovery = common.GetBootableImage( 851 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY") 852 853 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict) 854 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict) 855 856 blockimgdiff_version = 1 857 if OPTIONS.info_dict: 858 blockimgdiff_version = max( 859 int(i) for i in 860 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(",")) 861 862 # Check first block of system partition for remount R/W only if 863 # disk type is ext4 864 system_partition = OPTIONS.source_info_dict["fstab"]["/system"] 865 check_first_block = system_partition.fs_type == "ext4" 866 system_diff = common.BlockDifference("system", system_tgt, system_src, 867 check_first_block, 868 version=blockimgdiff_version) 869 870 if HasVendorPartition(target_zip): 871 if not HasVendorPartition(source_zip): 872 raise RuntimeError("can't generate incremental that adds /vendor") 873 vendor_src = GetImage("vendor", OPTIONS.source_tmp, 874 OPTIONS.source_info_dict) 875 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp, 876 OPTIONS.target_info_dict) 877 878 # Check first block of vendor partition for remount R/W only if 879 # disk type is ext4 880 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"] 881 check_first_block = vendor_partition.fs_type == "ext4" 882 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src, 883 check_first_block, 884 version=blockimgdiff_version) 885 else: 886 vendor_diff = None 887 888 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict) 889 device_specific.IncrementalOTA_Assertions() 890 891 # Two-step incremental package strategy (in chronological order, 892 # which is *not* the order in which the generated script has 893 # things): 894 # 895 # if stage is not "2/3" or "3/3": 896 # do verification on current system 897 # write recovery image to boot partition 898 # set stage to "2/3" 899 # reboot to boot partition and restart recovery 900 # else if stage is "2/3": 901 # write recovery image to recovery partition 902 # set stage to "3/3" 903 # reboot to recovery partition and restart recovery 904 # else: 905 # (stage must be "3/3") 906 # perform update: 907 # patch system files, etc. 908 # force full install of new boot image 909 # set up system to update recovery partition on first boot 910 # complete script normally 911 # (allow recovery to mark itself finished and reboot) 912 913 if OPTIONS.two_step: 914 if not OPTIONS.source_info_dict.get("multistage_support", None): 915 assert False, "two-step packages not supported by this build" 916 fs = OPTIONS.source_info_dict["fstab"]["/misc"] 917 assert fs.fs_type.upper() == "EMMC", \ 918 "two-step packages only supported on devices with EMMC /misc partitions" 919 bcb_dev = {"bcb_dev": fs.device} 920 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data) 921 script.AppendExtra(""" 922if get_stage("%(bcb_dev)s") == "2/3" then 923""" % bcb_dev) 924 script.AppendExtra("sleep(20);\n") 925 script.WriteRawImage("/recovery", "recovery.img") 926 script.AppendExtra(""" 927set_stage("%(bcb_dev)s", "3/3"); 928reboot_now("%(bcb_dev)s", "recovery"); 929else if get_stage("%(bcb_dev)s") != "3/3" then 930""" % bcb_dev) 931 932 # Dump fingerprints 933 script.Print("Source: %s" % CalculateFingerprint( 934 oem_props, oem_dict, OPTIONS.source_info_dict)) 935 script.Print("Target: %s" % CalculateFingerprint( 936 oem_props, oem_dict, OPTIONS.target_info_dict)) 937 938 script.Print("Verifying current system...") 939 940 device_specific.IncrementalOTA_VerifyBegin() 941 942 if oem_props is None: 943 # When blockimgdiff version is less than 3 (non-resumable block-based OTA), 944 # patching on a device that's already on the target build will damage the 945 # system. Because operations like move don't check the block state, they 946 # always apply the changes unconditionally. 947 if blockimgdiff_version <= 2: 948 script.AssertSomeFingerprint(source_fp) 949 else: 950 script.AssertSomeFingerprint(source_fp, target_fp) 951 else: 952 if blockimgdiff_version <= 2: 953 script.AssertSomeThumbprint( 954 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict)) 955 else: 956 script.AssertSomeThumbprint( 957 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict), 958 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict)) 959 960 # Check the required cache size (i.e. stashed blocks). 961 size = [] 962 if system_diff: 963 size.append(system_diff.required_cache) 964 if vendor_diff: 965 size.append(vendor_diff.required_cache) 966 967 if updating_boot: 968 boot_type, boot_device = common.GetTypeAndDevice( 969 "/boot", OPTIONS.source_info_dict) 970 d = common.Difference(target_boot, source_boot) 971 _, _, d = d.ComputePatch() 972 if d is None: 973 include_full_boot = True 974 common.ZipWriteStr(output_zip, "boot.img", target_boot.data) 975 else: 976 include_full_boot = False 977 978 print "boot target: %d source: %d diff: %d" % ( 979 target_boot.size, source_boot.size, len(d)) 980 981 common.ZipWriteStr(output_zip, "patch/boot.img.p", d) 982 983 script.PatchCheck("%s:%s:%d:%s:%d:%s" % 984 (boot_type, boot_device, 985 source_boot.size, source_boot.sha1, 986 target_boot.size, target_boot.sha1)) 987 size.append(target_boot.size) 988 989 if size: 990 script.CacheFreeSpaceCheck(max(size)) 991 992 device_specific.IncrementalOTA_VerifyEnd() 993 994 if OPTIONS.two_step: 995 script.WriteRawImage("/boot", "recovery.img") 996 script.AppendExtra(""" 997set_stage("%(bcb_dev)s", "2/3"); 998reboot_now("%(bcb_dev)s", ""); 999else 1000""" % bcb_dev) 1001 1002 # Verify the existing partitions. 1003 system_diff.WriteVerifyScript(script, touched_blocks_only=True) 1004 if vendor_diff: 1005 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True) 1006 1007 script.Comment("---- start making changes here ----") 1008 1009 device_specific.IncrementalOTA_InstallBegin() 1010 1011 system_diff.WriteScript(script, output_zip, 1012 progress=0.8 if vendor_diff else 0.9) 1013 1014 if vendor_diff: 1015 vendor_diff.WriteScript(script, output_zip, progress=0.1) 1016 1017 if OPTIONS.two_step: 1018 common.ZipWriteStr(output_zip, "boot.img", target_boot.data) 1019 script.WriteRawImage("/boot", "boot.img") 1020 print "writing full boot image (forced by two-step mode)" 1021 1022 if not OPTIONS.two_step: 1023 if updating_boot: 1024 if include_full_boot: 1025 print "boot image changed; including full." 1026 script.Print("Installing boot image...") 1027 script.WriteRawImage("/boot", "boot.img") 1028 else: 1029 # Produce the boot image by applying a patch to the current 1030 # contents of the boot partition, and write it back to the 1031 # partition. 1032 print "boot image changed; including patch." 1033 script.Print("Patching boot image...") 1034 script.ShowProgress(0.1, 10) 1035 script.ApplyPatch("%s:%s:%d:%s:%d:%s" 1036 % (boot_type, boot_device, 1037 source_boot.size, source_boot.sha1, 1038 target_boot.size, target_boot.sha1), 1039 "-", 1040 target_boot.size, target_boot.sha1, 1041 source_boot.sha1, "patch/boot.img.p") 1042 else: 1043 print "boot image unchanged; skipping." 1044 1045 # Do device-specific installation (eg, write radio image). 1046 device_specific.IncrementalOTA_InstallEnd() 1047 1048 if OPTIONS.extra_script is not None: 1049 script.AppendExtra(OPTIONS.extra_script) 1050 1051 if OPTIONS.wipe_user_data: 1052 script.Print("Erasing user data...") 1053 script.FormatPartition("/data") 1054 metadata["ota-wipe"] = "yes" 1055 1056 if OPTIONS.two_step: 1057 script.AppendExtra(""" 1058set_stage("%(bcb_dev)s", ""); 1059endif; 1060endif; 1061""" % bcb_dev) 1062 1063 script.SetProgress(1) 1064 # For downgrade OTAs, we prefer to use the update-binary in the source 1065 # build that is actually newer than the one in the target build. 1066 if OPTIONS.downgrade: 1067 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary) 1068 else: 1069 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary) 1070 metadata["ota-required-cache"] = str(script.required_cache) 1071 WriteMetadata(metadata, output_zip) 1072 1073 1074def WriteVerifyPackage(input_zip, output_zip): 1075 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict) 1076 1077 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties") 1078 recovery_mount_options = OPTIONS.info_dict.get( 1079 "recovery_mount_options") 1080 oem_dict = None 1081 if oem_props is not None and len(oem_props) > 0: 1082 if OPTIONS.oem_source is None: 1083 raise common.ExternalError("OEM source required for this build") 1084 script.Mount("/oem", recovery_mount_options) 1085 oem_dict = common.LoadDictionaryFromLines( 1086 open(OPTIONS.oem_source).readlines()) 1087 1088 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict) 1089 metadata = { 1090 "post-build": target_fp, 1091 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict, 1092 OPTIONS.info_dict), 1093 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict), 1094 } 1095 1096 device_specific = common.DeviceSpecificParams( 1097 input_zip=input_zip, 1098 input_version=OPTIONS.info_dict["recovery_api_version"], 1099 output_zip=output_zip, 1100 script=script, 1101 input_tmp=OPTIONS.input_tmp, 1102 metadata=metadata, 1103 info_dict=OPTIONS.info_dict) 1104 1105 AppendAssertions(script, OPTIONS.info_dict, oem_dict) 1106 1107 script.Print("Verifying device images against %s..." % target_fp) 1108 script.AppendExtra("") 1109 1110 script.Print("Verifying boot...") 1111 boot_img = common.GetBootableImage( 1112 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") 1113 boot_type, boot_device = common.GetTypeAndDevice( 1114 "/boot", OPTIONS.info_dict) 1115 script.Verify("%s:%s:%d:%s" % ( 1116 boot_type, boot_device, boot_img.size, boot_img.sha1)) 1117 script.AppendExtra("") 1118 1119 script.Print("Verifying recovery...") 1120 recovery_img = common.GetBootableImage( 1121 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY") 1122 recovery_type, recovery_device = common.GetTypeAndDevice( 1123 "/recovery", OPTIONS.info_dict) 1124 script.Verify("%s:%s:%d:%s" % ( 1125 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1)) 1126 script.AppendExtra("") 1127 1128 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict) 1129 system_tgt.ResetFileMap() 1130 system_diff = common.BlockDifference("system", system_tgt, src=None) 1131 system_diff.WriteStrictVerifyScript(script) 1132 1133 if HasVendorPartition(input_zip): 1134 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict) 1135 vendor_tgt.ResetFileMap() 1136 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None) 1137 vendor_diff.WriteStrictVerifyScript(script) 1138 1139 # Device specific partitions, such as radio, bootloader and etc. 1140 device_specific.VerifyOTA_Assertions() 1141 1142 script.SetProgress(1.0) 1143 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary) 1144 metadata["ota-required-cache"] = str(script.required_cache) 1145 WriteMetadata(metadata, output_zip) 1146 1147 1148def WriteABOTAPackageWithBrilloScript(target_file, output_file, 1149 source_file=None): 1150 """Generate an Android OTA package that has A/B update payload.""" 1151 1152 # Setup signing keys. 1153 if OPTIONS.package_key is None: 1154 OPTIONS.package_key = OPTIONS.info_dict.get( 1155 "default_system_dev_certificate", 1156 "build/target/product/security/testkey") 1157 1158 # A/B updater expects key in RSA format. 1159 cmd = ["openssl", "pkcs8", 1160 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix, 1161 "-inform", "DER", "-nocrypt"] 1162 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key") 1163 cmd.extend(["-out", rsa_key]) 1164 p1 = common.Run(cmd, stdout=subprocess.PIPE) 1165 p1.wait() 1166 assert p1.returncode == 0, "openssl pkcs8 failed" 1167 1168 # Stage the output zip package for signing. 1169 temp_zip_file = tempfile.NamedTemporaryFile() 1170 output_zip = zipfile.ZipFile(temp_zip_file, "w", 1171 compression=zipfile.ZIP_DEFLATED) 1172 1173 # Metadata to comply with Android OTA package format. 1174 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None) 1175 oem_dict = None 1176 if oem_props: 1177 if OPTIONS.oem_source is None: 1178 raise common.ExternalError("OEM source required for this build") 1179 oem_dict = common.LoadDictionaryFromLines( 1180 open(OPTIONS.oem_source).readlines()) 1181 1182 metadata = { 1183 "post-build": CalculateFingerprint(oem_props, oem_dict, 1184 OPTIONS.info_dict), 1185 "post-build-incremental" : GetBuildProp("ro.build.version.incremental", 1186 OPTIONS.info_dict), 1187 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict, 1188 OPTIONS.info_dict), 1189 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict), 1190 "ota-required-cache": "0", 1191 "ota-type": "AB", 1192 } 1193 1194 if source_file is not None: 1195 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict, 1196 OPTIONS.source_info_dict) 1197 metadata["pre-build-incremental"] = GetBuildProp( 1198 "ro.build.version.incremental", OPTIONS.source_info_dict) 1199 1200 # 1. Generate payload. 1201 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin") 1202 cmd = ["brillo_update_payload", "generate", 1203 "--payload", payload_file, 1204 "--target_image", target_file] 1205 if source_file is not None: 1206 cmd.extend(["--source_image", source_file]) 1207 p1 = common.Run(cmd, stdout=subprocess.PIPE) 1208 p1.wait() 1209 assert p1.returncode == 0, "brillo_update_payload generate failed" 1210 1211 # 2. Generate hashes of the payload and metadata files. 1212 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin") 1213 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin") 1214 cmd = ["brillo_update_payload", "hash", 1215 "--unsigned_payload", payload_file, 1216 "--signature_size", "256", 1217 "--metadata_hash_file", metadata_sig_file, 1218 "--payload_hash_file", payload_sig_file] 1219 p1 = common.Run(cmd, stdout=subprocess.PIPE) 1220 p1.wait() 1221 assert p1.returncode == 0, "brillo_update_payload hash failed" 1222 1223 # 3. Sign the hashes and insert them back into the payload file. 1224 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-", 1225 suffix=".bin") 1226 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-", 1227 suffix=".bin") 1228 # 3a. Sign the payload hash. 1229 cmd = ["openssl", "pkeyutl", "-sign", 1230 "-inkey", rsa_key, 1231 "-pkeyopt", "digest:sha256", 1232 "-in", payload_sig_file, 1233 "-out", signed_payload_sig_file] 1234 p1 = common.Run(cmd, stdout=subprocess.PIPE) 1235 p1.wait() 1236 assert p1.returncode == 0, "openssl sign payload failed" 1237 1238 # 3b. Sign the metadata hash. 1239 cmd = ["openssl", "pkeyutl", "-sign", 1240 "-inkey", rsa_key, 1241 "-pkeyopt", "digest:sha256", 1242 "-in", metadata_sig_file, 1243 "-out", signed_metadata_sig_file] 1244 p1 = common.Run(cmd, stdout=subprocess.PIPE) 1245 p1.wait() 1246 assert p1.returncode == 0, "openssl sign metadata failed" 1247 1248 # 3c. Insert the signatures back into the payload file. 1249 signed_payload_file = common.MakeTempFile(prefix="signed-payload-", 1250 suffix=".bin") 1251 cmd = ["brillo_update_payload", "sign", 1252 "--unsigned_payload", payload_file, 1253 "--payload", signed_payload_file, 1254 "--signature_size", "256", 1255 "--metadata_signature_file", signed_metadata_sig_file, 1256 "--payload_signature_file", signed_payload_sig_file] 1257 p1 = common.Run(cmd, stdout=subprocess.PIPE) 1258 p1.wait() 1259 assert p1.returncode == 0, "brillo_update_payload sign failed" 1260 1261 # 4. Dump the signed payload properties. 1262 properties_file = common.MakeTempFile(prefix="payload-properties-", 1263 suffix=".txt") 1264 cmd = ["brillo_update_payload", "properties", 1265 "--payload", signed_payload_file, 1266 "--properties_file", properties_file] 1267 p1 = common.Run(cmd, stdout=subprocess.PIPE) 1268 p1.wait() 1269 assert p1.returncode == 0, "brillo_update_payload properties failed" 1270 1271 # Add the signed payload file and properties into the zip. 1272 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt") 1273 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin", 1274 compress_type=zipfile.ZIP_STORED) 1275 WriteMetadata(metadata, output_zip) 1276 1277 # Sign the whole package to comply with the Android OTA package format. 1278 common.ZipClose(output_zip) 1279 SignOutput(temp_zip_file.name, output_file) 1280 temp_zip_file.close() 1281 1282 1283class FileDifference(object): 1284 def __init__(self, partition, source_zip, target_zip, output_zip): 1285 self.deferred_patch_list = None 1286 print "Loading target..." 1287 self.target_data = target_data = LoadPartitionFiles(target_zip, partition) 1288 print "Loading source..." 1289 self.source_data = source_data = LoadPartitionFiles(source_zip, partition) 1290 1291 self.verbatim_targets = verbatim_targets = [] 1292 self.patch_list = patch_list = [] 1293 diffs = [] 1294 self.renames = renames = {} 1295 known_paths = set() 1296 largest_source_size = 0 1297 1298 matching_file_cache = {} 1299 for fn, sf in source_data.items(): 1300 assert fn == sf.name 1301 matching_file_cache["path:" + fn] = sf 1302 if fn in target_data.keys(): 1303 AddToKnownPaths(fn, known_paths) 1304 # Only allow eligibility for filename/sha matching 1305 # if there isn't a perfect path match. 1306 if target_data.get(sf.name) is None: 1307 matching_file_cache["file:" + fn.split("/")[-1]] = sf 1308 matching_file_cache["sha:" + sf.sha1] = sf 1309 1310 for fn in sorted(target_data.keys()): 1311 tf = target_data[fn] 1312 assert fn == tf.name 1313 sf = ClosestFileMatch(tf, matching_file_cache, renames) 1314 if sf is not None and sf.name != tf.name: 1315 print "File has moved from " + sf.name + " to " + tf.name 1316 renames[sf.name] = tf 1317 1318 if sf is None or fn in OPTIONS.require_verbatim: 1319 # This file should be included verbatim 1320 if fn in OPTIONS.prohibit_verbatim: 1321 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,)) 1322 print "send", fn, "verbatim" 1323 tf.AddToZip(output_zip) 1324 verbatim_targets.append((fn, tf.size, tf.sha1)) 1325 if fn in target_data.keys(): 1326 AddToKnownPaths(fn, known_paths) 1327 elif tf.sha1 != sf.sha1: 1328 # File is different; consider sending as a patch 1329 diffs.append(common.Difference(tf, sf)) 1330 else: 1331 # Target file data identical to source (may still be renamed) 1332 pass 1333 1334 common.ComputeDifferences(diffs) 1335 1336 for diff in diffs: 1337 tf, sf, d = diff.GetPatch() 1338 path = "/".join(tf.name.split("/")[:-1]) 1339 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \ 1340 path not in known_paths: 1341 # patch is almost as big as the file; don't bother patching 1342 # or a patch + rename cannot take place due to the target 1343 # directory not existing 1344 tf.AddToZip(output_zip) 1345 verbatim_targets.append((tf.name, tf.size, tf.sha1)) 1346 if sf.name in renames: 1347 del renames[sf.name] 1348 AddToKnownPaths(tf.name, known_paths) 1349 else: 1350 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d) 1351 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest())) 1352 largest_source_size = max(largest_source_size, sf.size) 1353 1354 self.largest_source_size = largest_source_size 1355 1356 def EmitVerification(self, script): 1357 so_far = 0 1358 for tf, sf, _, _ in self.patch_list: 1359 if tf.name != sf.name: 1360 script.SkipNextActionIfTargetExists(tf.name, tf.sha1) 1361 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1) 1362 so_far += sf.size 1363 return so_far 1364 1365 def EmitExplicitTargetVerification(self, script): 1366 for fn, _, sha1 in self.verbatim_targets: 1367 if fn[-1] != "/": 1368 script.FileCheck("/"+fn, sha1) 1369 for tf, _, _, _ in self.patch_list: 1370 script.FileCheck(tf.name, tf.sha1) 1371 1372 def RemoveUnneededFiles(self, script, extras=()): 1373 file_list = ["/" + i[0] for i in self.verbatim_targets] 1374 file_list += ["/" + i for i in self.source_data 1375 if i not in self.target_data and i not in self.renames] 1376 file_list += list(extras) 1377 # Sort the list in descending order, which removes all the files first 1378 # before attempting to remove the folder. (Bug: 22960996) 1379 script.DeleteFiles(sorted(file_list, reverse=True)) 1380 1381 def TotalPatchSize(self): 1382 return sum(i[1].size for i in self.patch_list) 1383 1384 def EmitPatches(self, script, total_patch_size, so_far): 1385 self.deferred_patch_list = deferred_patch_list = [] 1386 for item in self.patch_list: 1387 tf, sf, _, _ = item 1388 if tf.name == "system/build.prop": 1389 deferred_patch_list.append(item) 1390 continue 1391 if sf.name != tf.name: 1392 script.SkipNextActionIfTargetExists(tf.name, tf.sha1) 1393 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1, 1394 "patch/" + sf.name + ".p") 1395 so_far += tf.size 1396 script.SetProgress(so_far / total_patch_size) 1397 return so_far 1398 1399 def EmitDeferredPatches(self, script): 1400 for item in self.deferred_patch_list: 1401 tf, sf, _, _ = item 1402 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, 1403 "patch/" + sf.name + ".p") 1404 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None) 1405 1406 def EmitRenames(self, script): 1407 if len(self.renames) > 0: 1408 script.Print("Renaming files...") 1409 for src, tgt in self.renames.iteritems(): 1410 print "Renaming " + src + " to " + tgt.name 1411 script.RenameFile(src, tgt.name) 1412 1413 1414def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): 1415 target_has_recovery_patch = HasRecoveryPatch(target_zip) 1416 source_has_recovery_patch = HasRecoveryPatch(source_zip) 1417 1418 if (OPTIONS.block_based and 1419 target_has_recovery_patch and 1420 source_has_recovery_patch): 1421 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip) 1422 1423 source_version = OPTIONS.source_info_dict["recovery_api_version"] 1424 target_version = OPTIONS.target_info_dict["recovery_api_version"] 1425 1426 if source_version == 0: 1427 print ("WARNING: generating edify script for a source that " 1428 "can't install it.") 1429 script = edify_generator.EdifyGenerator( 1430 source_version, OPTIONS.target_info_dict, 1431 fstab=OPTIONS.source_info_dict["fstab"]) 1432 1433 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties") 1434 recovery_mount_options = OPTIONS.source_info_dict.get( 1435 "recovery_mount_options") 1436 oem_dict = None 1437 if oem_props is not None and len(oem_props) > 0: 1438 if OPTIONS.oem_source is None: 1439 raise common.ExternalError("OEM source required for this build") 1440 if not OPTIONS.oem_no_mount: 1441 script.Mount("/oem", recovery_mount_options) 1442 oem_dict = common.LoadDictionaryFromLines( 1443 open(OPTIONS.oem_source).readlines()) 1444 1445 metadata = { 1446 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict, 1447 OPTIONS.source_info_dict), 1448 "ota-type": "FILE", 1449 } 1450 1451 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict) 1452 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict) 1453 is_downgrade = long(post_timestamp) < long(pre_timestamp) 1454 1455 if OPTIONS.downgrade: 1456 metadata["ota-downgrade"] = "yes" 1457 if not is_downgrade: 1458 raise RuntimeError("--downgrade specified but no downgrade detected: " 1459 "pre: %s, post: %s" % (pre_timestamp, post_timestamp)) 1460 else: 1461 if is_downgrade: 1462 # Non-fatal here to allow generating such a package which may require 1463 # manual work to adjust the post-timestamp. A legit use case is that we 1464 # cut a new build C (after having A and B), but want to enfore the 1465 # update path of A -> C -> B. Specifying --downgrade may not help since 1466 # that would enforce a data wipe for C -> B update. 1467 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n" 1468 "The package may not be deployed properly. " 1469 "Try --downgrade?\n" % (pre_timestamp, post_timestamp)) 1470 metadata["post-timestamp"] = post_timestamp 1471 1472 device_specific = common.DeviceSpecificParams( 1473 source_zip=source_zip, 1474 source_version=source_version, 1475 target_zip=target_zip, 1476 target_version=target_version, 1477 output_zip=output_zip, 1478 script=script, 1479 metadata=metadata, 1480 info_dict=OPTIONS.source_info_dict) 1481 1482 system_diff = FileDifference("system", source_zip, target_zip, output_zip) 1483 script.Mount("/system", recovery_mount_options) 1484 if HasVendorPartition(target_zip): 1485 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip) 1486 script.Mount("/vendor", recovery_mount_options) 1487 else: 1488 vendor_diff = None 1489 1490 target_fp = CalculateFingerprint(oem_props, oem_dict, 1491 OPTIONS.target_info_dict) 1492 source_fp = CalculateFingerprint(oem_props, oem_dict, 1493 OPTIONS.source_info_dict) 1494 1495 if oem_props is None: 1496 script.AssertSomeFingerprint(source_fp, target_fp) 1497 else: 1498 script.AssertSomeThumbprint( 1499 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict), 1500 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict)) 1501 1502 metadata["pre-build"] = source_fp 1503 metadata["post-build"] = target_fp 1504 metadata["pre-build-incremental"] = GetBuildProp( 1505 "ro.build.version.incremental", OPTIONS.source_info_dict) 1506 metadata["post-build-incremental"] = GetBuildProp( 1507 "ro.build.version.incremental", OPTIONS.target_info_dict) 1508 1509 source_boot = common.GetBootableImage( 1510 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", 1511 OPTIONS.source_info_dict) 1512 target_boot = common.GetBootableImage( 1513 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT") 1514 updating_boot = (not OPTIONS.two_step and 1515 (source_boot.data != target_boot.data)) 1516 1517 source_recovery = common.GetBootableImage( 1518 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY", 1519 OPTIONS.source_info_dict) 1520 target_recovery = common.GetBootableImage( 1521 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY") 1522 updating_recovery = (source_recovery.data != target_recovery.data) 1523 1524 # Here's how we divide up the progress bar: 1525 # 0.1 for verifying the start state (PatchCheck calls) 1526 # 0.8 for applying patches (ApplyPatch calls) 1527 # 0.1 for unpacking verbatim files, symlinking, and doing the 1528 # device-specific commands. 1529 1530 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict) 1531 device_specific.IncrementalOTA_Assertions() 1532 1533 # Two-step incremental package strategy (in chronological order, 1534 # which is *not* the order in which the generated script has 1535 # things): 1536 # 1537 # if stage is not "2/3" or "3/3": 1538 # do verification on current system 1539 # write recovery image to boot partition 1540 # set stage to "2/3" 1541 # reboot to boot partition and restart recovery 1542 # else if stage is "2/3": 1543 # write recovery image to recovery partition 1544 # set stage to "3/3" 1545 # reboot to recovery partition and restart recovery 1546 # else: 1547 # (stage must be "3/3") 1548 # perform update: 1549 # patch system files, etc. 1550 # force full install of new boot image 1551 # set up system to update recovery partition on first boot 1552 # complete script normally 1553 # (allow recovery to mark itself finished and reboot) 1554 1555 if OPTIONS.two_step: 1556 if not OPTIONS.source_info_dict.get("multistage_support", None): 1557 assert False, "two-step packages not supported by this build" 1558 fs = OPTIONS.source_info_dict["fstab"]["/misc"] 1559 assert fs.fs_type.upper() == "EMMC", \ 1560 "two-step packages only supported on devices with EMMC /misc partitions" 1561 bcb_dev = {"bcb_dev": fs.device} 1562 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data) 1563 script.AppendExtra(""" 1564if get_stage("%(bcb_dev)s") == "2/3" then 1565""" % bcb_dev) 1566 script.AppendExtra("sleep(20);\n") 1567 script.WriteRawImage("/recovery", "recovery.img") 1568 script.AppendExtra(""" 1569set_stage("%(bcb_dev)s", "3/3"); 1570reboot_now("%(bcb_dev)s", "recovery"); 1571else if get_stage("%(bcb_dev)s") != "3/3" then 1572""" % bcb_dev) 1573 1574 # Dump fingerprints 1575 script.Print("Source: %s" % (source_fp,)) 1576 script.Print("Target: %s" % (target_fp,)) 1577 1578 script.Print("Verifying current system...") 1579 1580 device_specific.IncrementalOTA_VerifyBegin() 1581 1582 script.ShowProgress(0.1, 0) 1583 so_far = system_diff.EmitVerification(script) 1584 if vendor_diff: 1585 so_far += vendor_diff.EmitVerification(script) 1586 1587 size = [] 1588 if system_diff.patch_list: 1589 size.append(system_diff.largest_source_size) 1590 if vendor_diff: 1591 if vendor_diff.patch_list: 1592 size.append(vendor_diff.largest_source_size) 1593 1594 if updating_boot: 1595 d = common.Difference(target_boot, source_boot) 1596 _, _, d = d.ComputePatch() 1597 print "boot target: %d source: %d diff: %d" % ( 1598 target_boot.size, source_boot.size, len(d)) 1599 1600 common.ZipWriteStr(output_zip, "patch/boot.img.p", d) 1601 1602 boot_type, boot_device = common.GetTypeAndDevice( 1603 "/boot", OPTIONS.source_info_dict) 1604 1605 script.PatchCheck("%s:%s:%d:%s:%d:%s" % 1606 (boot_type, boot_device, 1607 source_boot.size, source_boot.sha1, 1608 target_boot.size, target_boot.sha1)) 1609 so_far += source_boot.size 1610 size.append(target_boot.size) 1611 1612 if size: 1613 script.CacheFreeSpaceCheck(max(size)) 1614 1615 device_specific.IncrementalOTA_VerifyEnd() 1616 1617 if OPTIONS.two_step: 1618 script.WriteRawImage("/boot", "recovery.img") 1619 script.AppendExtra(""" 1620set_stage("%(bcb_dev)s", "2/3"); 1621reboot_now("%(bcb_dev)s", ""); 1622else 1623""" % bcb_dev) 1624 1625 script.Comment("---- start making changes here ----") 1626 1627 device_specific.IncrementalOTA_InstallBegin() 1628 1629 if OPTIONS.two_step: 1630 common.ZipWriteStr(output_zip, "boot.img", target_boot.data) 1631 script.WriteRawImage("/boot", "boot.img") 1632 print "writing full boot image (forced by two-step mode)" 1633 1634 script.Print("Removing unneeded files...") 1635 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",)) 1636 if vendor_diff: 1637 vendor_diff.RemoveUnneededFiles(script) 1638 1639 script.ShowProgress(0.8, 0) 1640 total_patch_size = 1.0 + system_diff.TotalPatchSize() 1641 if vendor_diff: 1642 total_patch_size += vendor_diff.TotalPatchSize() 1643 if updating_boot: 1644 total_patch_size += target_boot.size 1645 1646 script.Print("Patching system files...") 1647 so_far = system_diff.EmitPatches(script, total_patch_size, 0) 1648 if vendor_diff: 1649 script.Print("Patching vendor files...") 1650 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far) 1651 1652 if not OPTIONS.two_step: 1653 if updating_boot: 1654 # Produce the boot image by applying a patch to the current 1655 # contents of the boot partition, and write it back to the 1656 # partition. 1657 script.Print("Patching boot image...") 1658 script.ApplyPatch("%s:%s:%d:%s:%d:%s" 1659 % (boot_type, boot_device, 1660 source_boot.size, source_boot.sha1, 1661 target_boot.size, target_boot.sha1), 1662 "-", 1663 target_boot.size, target_boot.sha1, 1664 source_boot.sha1, "patch/boot.img.p") 1665 so_far += target_boot.size 1666 script.SetProgress(so_far / total_patch_size) 1667 print "boot image changed; including." 1668 else: 1669 print "boot image unchanged; skipping." 1670 1671 system_items = ItemSet("system", "META/filesystem_config.txt") 1672 if vendor_diff: 1673 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt") 1674 1675 if updating_recovery: 1676 # Recovery is generated as a patch using both the boot image 1677 # (which contains the same linux kernel as recovery) and the file 1678 # /system/etc/recovery-resource.dat (which contains all the images 1679 # used in the recovery UI) as sources. This lets us minimize the 1680 # size of the patch, which must be included in every OTA package. 1681 # 1682 # For older builds where recovery-resource.dat is not present, we 1683 # use only the boot image as the source. 1684 1685 if not target_has_recovery_patch: 1686 def output_sink(fn, data): 1687 common.ZipWriteStr(output_zip, "recovery/" + fn, data) 1688 system_items.Get("system/" + fn) 1689 1690 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink, 1691 target_recovery, target_boot) 1692 script.DeleteFiles(["/system/recovery-from-boot.p", 1693 "/system/etc/recovery.img", 1694 "/system/etc/install-recovery.sh"]) 1695 print "recovery image changed; including as patch from boot." 1696 else: 1697 print "recovery image unchanged; skipping." 1698 1699 script.ShowProgress(0.1, 10) 1700 1701 target_symlinks = CopyPartitionFiles(system_items, target_zip, None) 1702 if vendor_diff: 1703 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None)) 1704 1705 temp_script = script.MakeTemporary() 1706 system_items.GetMetadata(target_zip) 1707 system_items.Get("system").SetPermissions(temp_script) 1708 if vendor_diff: 1709 vendor_items.GetMetadata(target_zip) 1710 vendor_items.Get("vendor").SetPermissions(temp_script) 1711 1712 # Note that this call will mess up the trees of Items, so make sure 1713 # we're done with them. 1714 source_symlinks = CopyPartitionFiles(system_items, source_zip, None) 1715 if vendor_diff: 1716 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None)) 1717 1718 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks]) 1719 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks]) 1720 1721 # Delete all the symlinks in source that aren't in target. This 1722 # needs to happen before verbatim files are unpacked, in case a 1723 # symlink in the source is replaced by a real file in the target. 1724 1725 # If a symlink in the source will be replaced by a regular file, we cannot 1726 # delete the symlink/file in case the package gets applied again. For such 1727 # a symlink, we prepend a sha1_check() to detect if it has been updated. 1728 # (Bug: 23646151) 1729 replaced_symlinks = dict() 1730 if system_diff: 1731 for i in system_diff.verbatim_targets: 1732 replaced_symlinks["/%s" % (i[0],)] = i[2] 1733 if vendor_diff: 1734 for i in vendor_diff.verbatim_targets: 1735 replaced_symlinks["/%s" % (i[0],)] = i[2] 1736 1737 if system_diff: 1738 for tf in system_diff.renames.values(): 1739 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1 1740 if vendor_diff: 1741 for tf in vendor_diff.renames.values(): 1742 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1 1743 1744 always_delete = [] 1745 may_delete = [] 1746 for dest, link in source_symlinks: 1747 if link not in target_symlinks_d: 1748 if link in replaced_symlinks: 1749 may_delete.append((link, replaced_symlinks[link])) 1750 else: 1751 always_delete.append(link) 1752 script.DeleteFiles(always_delete) 1753 script.DeleteFilesIfNotMatching(may_delete) 1754 1755 if system_diff.verbatim_targets: 1756 script.Print("Unpacking new system files...") 1757 script.UnpackPackageDir("system", "/system") 1758 if vendor_diff and vendor_diff.verbatim_targets: 1759 script.Print("Unpacking new vendor files...") 1760 script.UnpackPackageDir("vendor", "/vendor") 1761 1762 if updating_recovery and not target_has_recovery_patch: 1763 script.Print("Unpacking new recovery...") 1764 script.UnpackPackageDir("recovery", "/system") 1765 1766 system_diff.EmitRenames(script) 1767 if vendor_diff: 1768 vendor_diff.EmitRenames(script) 1769 1770 script.Print("Symlinks and permissions...") 1771 1772 # Create all the symlinks that don't already exist, or point to 1773 # somewhere different than what we want. Delete each symlink before 1774 # creating it, since the 'symlink' command won't overwrite. 1775 to_create = [] 1776 for dest, link in target_symlinks: 1777 if link in source_symlinks_d: 1778 if dest != source_symlinks_d[link]: 1779 to_create.append((dest, link)) 1780 else: 1781 to_create.append((dest, link)) 1782 script.DeleteFiles([i[1] for i in to_create]) 1783 script.MakeSymlinks(to_create) 1784 1785 # Now that the symlinks are created, we can set all the 1786 # permissions. 1787 script.AppendScript(temp_script) 1788 1789 # Do device-specific installation (eg, write radio image). 1790 device_specific.IncrementalOTA_InstallEnd() 1791 1792 if OPTIONS.extra_script is not None: 1793 script.AppendExtra(OPTIONS.extra_script) 1794 1795 # Patch the build.prop file last, so if something fails but the 1796 # device can still come up, it appears to be the old build and will 1797 # get set the OTA package again to retry. 1798 script.Print("Patching remaining system files...") 1799 system_diff.EmitDeferredPatches(script) 1800 1801 if OPTIONS.wipe_user_data: 1802 script.Print("Erasing user data...") 1803 script.FormatPartition("/data") 1804 metadata["ota-wipe"] = "yes" 1805 1806 if OPTIONS.two_step: 1807 script.AppendExtra(""" 1808set_stage("%(bcb_dev)s", ""); 1809endif; 1810endif; 1811""" % bcb_dev) 1812 1813 if OPTIONS.verify and system_diff: 1814 script.Print("Remounting and verifying system partition files...") 1815 script.Unmount("/system") 1816 script.Mount("/system", recovery_mount_options) 1817 system_diff.EmitExplicitTargetVerification(script) 1818 1819 if OPTIONS.verify and vendor_diff: 1820 script.Print("Remounting and verifying vendor partition files...") 1821 script.Unmount("/vendor") 1822 script.Mount("/vendor", recovery_mount_options) 1823 vendor_diff.EmitExplicitTargetVerification(script) 1824 1825 # For downgrade OTAs, we prefer to use the update-binary in the source 1826 # build that is actually newer than the one in the target build. 1827 if OPTIONS.downgrade: 1828 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary) 1829 else: 1830 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary) 1831 1832 metadata["ota-required-cache"] = str(script.required_cache) 1833 WriteMetadata(metadata, output_zip) 1834 1835 1836def main(argv): 1837 1838 def option_handler(o, a): 1839 if o == "--board_config": 1840 pass # deprecated 1841 elif o in ("-k", "--package_key"): 1842 OPTIONS.package_key = a 1843 elif o in ("-i", "--incremental_from"): 1844 OPTIONS.incremental_source = a 1845 elif o == "--full_radio": 1846 OPTIONS.full_radio = True 1847 elif o == "--full_bootloader": 1848 OPTIONS.full_bootloader = True 1849 elif o in ("-w", "--wipe_user_data"): 1850 OPTIONS.wipe_user_data = True 1851 elif o in ("-n", "--no_prereq"): 1852 OPTIONS.omit_prereq = True 1853 elif o == "--downgrade": 1854 OPTIONS.downgrade = True 1855 OPTIONS.wipe_user_data = True 1856 elif o in ("-o", "--oem_settings"): 1857 OPTIONS.oem_source = a 1858 elif o == "--oem_no_mount": 1859 OPTIONS.oem_no_mount = True 1860 elif o in ("-e", "--extra_script"): 1861 OPTIONS.extra_script = a 1862 elif o in ("-a", "--aslr_mode"): 1863 if a in ("on", "On", "true", "True", "yes", "Yes"): 1864 OPTIONS.aslr_mode = True 1865 else: 1866 OPTIONS.aslr_mode = False 1867 elif o in ("-t", "--worker_threads"): 1868 if a.isdigit(): 1869 OPTIONS.worker_threads = int(a) 1870 else: 1871 raise ValueError("Cannot parse value %r for option %r - only " 1872 "integers are allowed." % (a, o)) 1873 elif o in ("-2", "--two_step"): 1874 OPTIONS.two_step = True 1875 elif o == "--no_signing": 1876 OPTIONS.no_signing = True 1877 elif o == "--verify": 1878 OPTIONS.verify = True 1879 elif o == "--block": 1880 OPTIONS.block_based = True 1881 elif o in ("-b", "--binary"): 1882 OPTIONS.updater_binary = a 1883 elif o in ("--no_fallback_to_full",): 1884 OPTIONS.fallback_to_full = False 1885 elif o == "--stash_threshold": 1886 try: 1887 OPTIONS.stash_threshold = float(a) 1888 except ValueError: 1889 raise ValueError("Cannot parse value %r for option %r - expecting " 1890 "a float" % (a, o)) 1891 elif o == "--gen_verify": 1892 OPTIONS.gen_verify = True 1893 elif o == "--log_diff": 1894 OPTIONS.log_diff = a 1895 else: 1896 return False 1897 return True 1898 1899 args = common.ParseOptions(argv, __doc__, 1900 extra_opts="b:k:i:d:wne:t:a:2o:", 1901 extra_long_opts=[ 1902 "board_config=", 1903 "package_key=", 1904 "incremental_from=", 1905 "full_radio", 1906 "full_bootloader", 1907 "wipe_user_data", 1908 "no_prereq", 1909 "downgrade", 1910 "extra_script=", 1911 "worker_threads=", 1912 "aslr_mode=", 1913 "two_step", 1914 "no_signing", 1915 "block", 1916 "binary=", 1917 "oem_settings=", 1918 "oem_no_mount", 1919 "verify", 1920 "no_fallback_to_full", 1921 "stash_threshold=", 1922 "gen_verify", 1923 "log_diff=", 1924 ], extra_option_handler=option_handler) 1925 1926 if len(args) != 2: 1927 common.Usage(__doc__) 1928 sys.exit(1) 1929 1930 if OPTIONS.downgrade: 1931 # Sanity check to enforce a data wipe. 1932 if not OPTIONS.wipe_user_data: 1933 raise ValueError("Cannot downgrade without a data wipe") 1934 1935 # We should only allow downgrading incrementals (as opposed to full). 1936 # Otherwise the device may go back from arbitrary build with this full 1937 # OTA package. 1938 if OPTIONS.incremental_source is None: 1939 raise ValueError("Cannot generate downgradable full OTAs - consider" 1940 "using --omit_prereq?") 1941 1942 # Load the dict file from the zip directly to have a peek at the OTA type. 1943 # For packages using A/B update, unzipping is not needed. 1944 input_zip = zipfile.ZipFile(args[0], "r") 1945 OPTIONS.info_dict = common.LoadInfoDict(input_zip) 1946 common.ZipClose(input_zip) 1947 1948 ab_update = OPTIONS.info_dict.get("ab_update") == "true" 1949 1950 if ab_update: 1951 if OPTIONS.incremental_source is not None: 1952 OPTIONS.target_info_dict = OPTIONS.info_dict 1953 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r") 1954 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip) 1955 common.ZipClose(source_zip) 1956 1957 if OPTIONS.verbose: 1958 print "--- target info ---" 1959 common.DumpInfoDict(OPTIONS.info_dict) 1960 1961 if OPTIONS.incremental_source is not None: 1962 print "--- source info ---" 1963 common.DumpInfoDict(OPTIONS.source_info_dict) 1964 1965 WriteABOTAPackageWithBrilloScript( 1966 target_file=args[0], 1967 output_file=args[1], 1968 source_file=OPTIONS.incremental_source) 1969 1970 print "done." 1971 return 1972 1973 if OPTIONS.extra_script is not None: 1974 OPTIONS.extra_script = open(OPTIONS.extra_script).read() 1975 1976 print "unzipping target target-files..." 1977 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0]) 1978 1979 OPTIONS.target_tmp = OPTIONS.input_tmp 1980 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp) 1981 1982 if OPTIONS.verbose: 1983 print "--- target info ---" 1984 common.DumpInfoDict(OPTIONS.info_dict) 1985 1986 # If the caller explicitly specified the device-specific extensions 1987 # path via -s/--device_specific, use that. Otherwise, use 1988 # META/releasetools.py if it is present in the target target_files. 1989 # Otherwise, take the path of the file from 'tool_extensions' in the 1990 # info dict and look for that in the local filesystem, relative to 1991 # the current directory. 1992 1993 if OPTIONS.device_specific is None: 1994 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py") 1995 if os.path.exists(from_input): 1996 print "(using device-specific extensions from target_files)" 1997 OPTIONS.device_specific = from_input 1998 else: 1999 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None) 2000 2001 if OPTIONS.device_specific is not None: 2002 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific) 2003 2004 if OPTIONS.info_dict.get("no_recovery") == "true": 2005 raise common.ExternalError( 2006 "--- target build has specified no recovery ---") 2007 2008 # Use the default key to sign the package if not specified with package_key. 2009 if not OPTIONS.no_signing: 2010 if OPTIONS.package_key is None: 2011 OPTIONS.package_key = OPTIONS.info_dict.get( 2012 "default_system_dev_certificate", 2013 "build/target/product/security/testkey") 2014 2015 # Set up the output zip. Create a temporary zip file if signing is needed. 2016 if OPTIONS.no_signing: 2017 if os.path.exists(args[1]): 2018 os.unlink(args[1]) 2019 output_zip = zipfile.ZipFile(args[1], "w", 2020 compression=zipfile.ZIP_DEFLATED) 2021 else: 2022 temp_zip_file = tempfile.NamedTemporaryFile() 2023 output_zip = zipfile.ZipFile(temp_zip_file, "w", 2024 compression=zipfile.ZIP_DEFLATED) 2025 2026 # Non A/B OTAs rely on /cache partition to store temporary files. 2027 cache_size = OPTIONS.info_dict.get("cache_size", None) 2028 if cache_size is None: 2029 print "--- can't determine the cache partition size ---" 2030 OPTIONS.cache_size = cache_size 2031 2032 # Generate a verify package. 2033 if OPTIONS.gen_verify: 2034 WriteVerifyPackage(input_zip, output_zip) 2035 2036 # Generate a full OTA. 2037 elif OPTIONS.incremental_source is None: 2038 WriteFullOTAPackage(input_zip, output_zip) 2039 2040 # Generate an incremental OTA. It will fall back to generate a full OTA on 2041 # failure unless no_fallback_to_full is specified. 2042 else: 2043 print "unzipping source target-files..." 2044 OPTIONS.source_tmp, source_zip = common.UnzipTemp( 2045 OPTIONS.incremental_source) 2046 OPTIONS.target_info_dict = OPTIONS.info_dict 2047 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip, 2048 OPTIONS.source_tmp) 2049 if OPTIONS.verbose: 2050 print "--- source info ---" 2051 common.DumpInfoDict(OPTIONS.source_info_dict) 2052 try: 2053 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip) 2054 if OPTIONS.log_diff: 2055 out_file = open(OPTIONS.log_diff, 'w') 2056 import target_files_diff 2057 target_files_diff.recursiveDiff('', 2058 OPTIONS.source_tmp, 2059 OPTIONS.input_tmp, 2060 out_file) 2061 out_file.close() 2062 except ValueError: 2063 if not OPTIONS.fallback_to_full: 2064 raise 2065 print "--- failed to build incremental; falling back to full ---" 2066 OPTIONS.incremental_source = None 2067 WriteFullOTAPackage(input_zip, output_zip) 2068 2069 common.ZipClose(output_zip) 2070 2071 # Sign the generated zip package unless no_signing is specified. 2072 if not OPTIONS.no_signing: 2073 SignOutput(temp_zip_file.name, args[1]) 2074 temp_zip_file.close() 2075 2076 print "done." 2077 2078 2079if __name__ == '__main__': 2080 try: 2081 common.CloseInheritedPipes() 2082 main(sys.argv[1:]) 2083 except common.ExternalError as e: 2084 print 2085 print " ERROR: %s" % (e,) 2086 print 2087 sys.exit(1) 2088 finally: 2089 common.Cleanup() 2090