ota_from_target_files revision 91a99c28e0d15a753bc303982c970c96d6dfe0f5
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 -o (--oem_settings) <file> 41 Use the file to specify the expected OEM-specific properties 42 on the OEM partition of the intended device. 43 44 -w (--wipe_user_data) 45 Generate an OTA package that will wipe the user data partition 46 when installed. 47 48 -n (--no_prereq) 49 Omit the timestamp prereq check normally included at the top of 50 the build scripts (used for developer OTA packages which 51 legitimately need to go back and forth). 52 53 -e (--extra_script) <file> 54 Insert the contents of file at the end of the update script. 55 56 -a (--aslr_mode) <on|off> 57 Specify whether to turn on ASLR for the package (on by default). 58 59 -2 (--two_step) 60 Generate a 'two-step' OTA package, where recovery is updated 61 first, so that any changes made to the system partition are done 62 using the new recovery (new kernel, etc.). 63 64 --block 65 Generate a block-based OTA if possible. Will fall back to a 66 file-based OTA if the target_files is older and doesn't support 67 block-based OTAs. 68 69 -b (--binary) <file> 70 Use the given binary as the update-binary in the output package, 71 instead of the binary in the build's target_files. Use for 72 development only. 73 74""" 75 76import sys 77 78if sys.hexversion < 0x02070000: 79 print >> sys.stderr, "Python 2.7 or newer is required." 80 sys.exit(1) 81 82import copy 83import errno 84import os 85import re 86import subprocess 87import tempfile 88import time 89import zipfile 90 91try: 92 from hashlib import sha1 as sha1 93except ImportError: 94 from sha import sha as sha1 95 96import common 97import img_from_target_files 98import edify_generator 99import build_image 100 101OPTIONS = common.OPTIONS 102OPTIONS.package_key = None 103OPTIONS.incremental_source = None 104OPTIONS.require_verbatim = set() 105OPTIONS.prohibit_verbatim = set(("system/build.prop",)) 106OPTIONS.patch_threshold = 0.95 107OPTIONS.wipe_user_data = False 108OPTIONS.omit_prereq = False 109OPTIONS.extra_script = None 110OPTIONS.aslr_mode = True 111OPTIONS.worker_threads = 3 112OPTIONS.two_step = False 113OPTIONS.no_signing = False 114OPTIONS.block_based = False 115OPTIONS.updater_binary = None 116OPTIONS.oem_source = None 117 118def MostPopularKey(d, default): 119 """Given a dict, return the key corresponding to the largest 120 value. Returns 'default' if the dict is empty.""" 121 x = [(v, k) for (k, v) in d.iteritems()] 122 if not x: return default 123 x.sort() 124 return x[-1][1] 125 126 127def IsSymlink(info): 128 """Return true if the zipfile.ZipInfo object passed in represents a 129 symlink.""" 130 return (info.external_attr >> 16) == 0120777 131 132def IsRegular(info): 133 """Return true if the zipfile.ZipInfo object passed in represents a 134 symlink.""" 135 return (info.external_attr >> 28) == 010 136 137def ClosestFileMatch(src, tgtfiles, existing): 138 """Returns the closest file match between a source file and list 139 of potential matches. The exact filename match is preferred, 140 then the sha1 is searched for, and finally a file with the same 141 basename is evaluated. Rename support in the updater-binary is 142 required for the latter checks to be used.""" 143 144 result = tgtfiles.get("path:" + src.name) 145 if result is not None: 146 return result 147 148 if not OPTIONS.target_info_dict.get("update_rename_support", False): 149 return None 150 151 if src.size < 1000: 152 return None 153 154 result = tgtfiles.get("sha1:" + src.sha1) 155 if result is not None and existing.get(result.name) is None: 156 return result 157 result = tgtfiles.get("file:" + src.name.split("/")[-1]) 158 if result is not None and existing.get(result.name) is None: 159 return result 160 return None 161 162class Item: 163 """Items represent the metadata (user, group, mode) of files and 164 directories in the system image.""" 165 ITEMS = {} 166 def __init__(self, name, dir=False): 167 self.name = name 168 self.uid = None 169 self.gid = None 170 self.mode = None 171 self.selabel = None 172 self.capabilities = None 173 self.dir = dir 174 175 if name: 176 self.parent = Item.Get(os.path.dirname(name), dir=True) 177 self.parent.children.append(self) 178 else: 179 self.parent = None 180 if dir: 181 self.children = [] 182 183 def Dump(self, indent=0): 184 if self.uid is not None: 185 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode) 186 else: 187 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode) 188 if self.dir: 189 print "%s%s" % (" "*indent, self.descendants) 190 print "%s%s" % (" "*indent, self.best_subtree) 191 for i in self.children: 192 i.Dump(indent=indent+1) 193 194 @classmethod 195 def Get(cls, name, dir=False): 196 if name not in cls.ITEMS: 197 cls.ITEMS[name] = Item(name, dir=dir) 198 return cls.ITEMS[name] 199 200 @classmethod 201 def GetMetadata(cls, input_zip): 202 203 # The target_files contains a record of what the uid, 204 # gid, and mode are supposed to be. 205 output = input_zip.read("META/filesystem_config.txt") 206 207 for line in output.split("\n"): 208 if not line: continue 209 columns = line.split() 210 name, uid, gid, mode = columns[:4] 211 selabel = None 212 capabilities = None 213 214 # After the first 4 columns, there are a series of key=value 215 # pairs. Extract out the fields we care about. 216 for element in columns[4:]: 217 key, value = element.split("=") 218 if key == "selabel": 219 selabel = value 220 if key == "capabilities": 221 capabilities = value 222 223 i = cls.ITEMS.get(name, None) 224 if i is not None: 225 i.uid = int(uid) 226 i.gid = int(gid) 227 i.mode = int(mode, 8) 228 i.selabel = selabel 229 i.capabilities = capabilities 230 if i.dir: 231 i.children.sort(key=lambda i: i.name) 232 233 # set metadata for the files generated by this script. 234 i = cls.ITEMS.get("system/recovery-from-boot.p", None) 235 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None 236 i = cls.ITEMS.get("system/etc/install-recovery.sh", None) 237 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None 238 239 def CountChildMetadata(self): 240 """Count up the (uid, gid, mode, selabel, capabilities) tuples for 241 all children and determine the best strategy for using set_perm_recursive and 242 set_perm to correctly chown/chmod all the files to their desired 243 values. Recursively calls itself for all descendants. 244 245 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up 246 all descendants of this node. (dmode or fmode may be None.) Also 247 sets the best_subtree of each directory Item to the (uid, gid, 248 dmode, fmode, selabel, capabilities) tuple that will match the most 249 descendants of that Item. 250 """ 251 252 assert self.dir 253 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1} 254 for i in self.children: 255 if i.dir: 256 for k, v in i.CountChildMetadata().iteritems(): 257 d[k] = d.get(k, 0) + v 258 else: 259 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities) 260 d[k] = d.get(k, 0) + 1 261 262 # Find the (uid, gid, dmode, fmode, selabel, capabilities) 263 # tuple that matches the most descendants. 264 265 # First, find the (uid, gid) pair that matches the most 266 # descendants. 267 ug = {} 268 for (uid, gid, _, _, _, _), count in d.iteritems(): 269 ug[(uid, gid)] = ug.get((uid, gid), 0) + count 270 ug = MostPopularKey(ug, (0, 0)) 271 272 # Now find the dmode, fmode, selabel, and capabilities that match 273 # the most descendants with that (uid, gid), and choose those. 274 best_dmode = (0, 0755) 275 best_fmode = (0, 0644) 276 best_selabel = (0, None) 277 best_capabilities = (0, None) 278 for k, count in d.iteritems(): 279 if k[:2] != ug: continue 280 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2]) 281 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3]) 282 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4]) 283 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5]) 284 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1]) 285 286 return d 287 288 def SetPermissions(self, script): 289 """Append set_perm/set_perm_recursive commands to 'script' to 290 set all permissions, users, and groups for the tree of files 291 rooted at 'self'.""" 292 293 self.CountChildMetadata() 294 295 def recurse(item, current): 296 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current 297 # item (and all its children) have already been set to. We only 298 # need to issue set_perm/set_perm_recursive commands if we're 299 # supposed to be something different. 300 if item.dir: 301 if current != item.best_subtree: 302 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree) 303 current = item.best_subtree 304 305 if item.uid != current[0] or item.gid != current[1] or \ 306 item.mode != current[2] or item.selabel != current[4] or \ 307 item.capabilities != current[5]: 308 script.SetPermissions("/"+item.name, item.uid, item.gid, 309 item.mode, item.selabel, item.capabilities) 310 311 for i in item.children: 312 recurse(i, current) 313 else: 314 if item.uid != current[0] or item.gid != current[1] or \ 315 item.mode != current[3] or item.selabel != current[4] or \ 316 item.capabilities != current[5]: 317 script.SetPermissions("/"+item.name, item.uid, item.gid, 318 item.mode, item.selabel, item.capabilities) 319 320 recurse(self, (-1, -1, -1, -1, None, None)) 321 322 323def CopySystemFiles(input_zip, output_zip=None, 324 substitute=None): 325 """Copies files underneath system/ in the input zip to the output 326 zip. Populates the Item class with their metadata, and returns a 327 list of symlinks. output_zip may be None, in which case the copy is 328 skipped (but the other side effects still happen). substitute is an 329 optional dict of {output filename: contents} to be output instead of 330 certain input files. 331 """ 332 333 symlinks = [] 334 335 for info in input_zip.infolist(): 336 if info.filename.startswith("SYSTEM/"): 337 basefilename = info.filename[7:] 338 if IsSymlink(info): 339 symlinks.append((input_zip.read(info.filename), 340 "/system/" + basefilename)) 341 else: 342 info2 = copy.copy(info) 343 fn = info2.filename = "system/" + basefilename 344 if substitute and fn in substitute and substitute[fn] is None: 345 continue 346 if output_zip is not None: 347 if substitute and fn in substitute: 348 data = substitute[fn] 349 else: 350 data = input_zip.read(info.filename) 351 output_zip.writestr(info2, data) 352 if fn.endswith("/"): 353 Item.Get(fn[:-1], dir=True) 354 else: 355 Item.Get(fn, dir=False) 356 357 symlinks.sort() 358 return symlinks 359 360 361def SignOutput(temp_zip_name, output_zip_name): 362 key_passwords = common.GetKeyPasswords([OPTIONS.package_key]) 363 pw = key_passwords[OPTIONS.package_key] 364 365 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw, 366 whole_file=True) 367 368 369def AppendAssertions(script, info_dict, oem_dict = None): 370 oem_props = info_dict.get("oem_fingerprint_properties") 371 if oem_props is None: 372 device = GetBuildProp("ro.product.device", info_dict) 373 script.AssertDevice(device) 374 else: 375 if oem_dict is None: 376 raise common.ExternalError("No OEM file provided to answer expected assertions") 377 for prop in oem_props.split(): 378 if oem_dict.get(prop) is None: 379 raise common.ExternalError("The OEM file is missing the property %s" % prop) 380 script.AssertOemProperty(prop, oem_dict.get(prop)) 381 382 383def HasRecoveryPatch(target_files_zip): 384 try: 385 target_files_zip.getinfo("SYSTEM/recovery-from-boot.p") 386 return True 387 except KeyError: 388 return False 389 390def GetOemProperty(name, oem_props, oem_dict, info_dict): 391 if oem_props is not None and name in oem_props: 392 return oem_dict[name] 393 return GetBuildProp(name, info_dict) 394 395 396def CalculateFingerprint(oem_props, oem_dict, info_dict): 397 if oem_props is None: 398 return GetBuildProp("ro.build.fingerprint", info_dict) 399 return "%s/%s/%s:%s" % ( 400 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict), 401 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict), 402 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict), 403 GetBuildProp("ro.build.thumbprint", info_dict)) 404 405def WriteFullOTAPackage(input_zip, output_zip): 406 # TODO: how to determine this? We don't know what version it will 407 # be installed on top of. For now, we expect the API just won't 408 # change very often. 409 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict) 410 411 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties") 412 oem_dict = None 413 if oem_props is not None: 414 if OPTIONS.oem_source is None: 415 raise common.ExternalError("OEM source required for this build") 416 script.Mount("/oem") 417 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines()) 418 419 metadata = {"post-build": CalculateFingerprint( 420 oem_props, oem_dict, OPTIONS.info_dict), 421 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict, 422 OPTIONS.info_dict), 423 "post-timestamp": GetBuildProp("ro.build.date.utc", 424 OPTIONS.info_dict), 425 } 426 427 device_specific = common.DeviceSpecificParams( 428 input_zip=input_zip, 429 input_version=OPTIONS.info_dict["recovery_api_version"], 430 output_zip=output_zip, 431 script=script, 432 input_tmp=OPTIONS.input_tmp, 433 metadata=metadata, 434 info_dict=OPTIONS.info_dict) 435 436 has_recovery_patch = HasRecoveryPatch(input_zip) 437 block_based = OPTIONS.block_based and has_recovery_patch 438 439 if not OPTIONS.omit_prereq: 440 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict) 441 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict) 442 script.AssertOlderBuild(ts, ts_text) 443 444 AppendAssertions(script, OPTIONS.info_dict, oem_dict) 445 device_specific.FullOTA_Assertions() 446 447 # Two-step package strategy (in chronological order, which is *not* 448 # the order in which the generated script has things): 449 # 450 # if stage is not "2/3" or "3/3": 451 # write recovery image to boot partition 452 # set stage to "2/3" 453 # reboot to boot partition and restart recovery 454 # else if stage is "2/3": 455 # write recovery image to recovery partition 456 # set stage to "3/3" 457 # reboot to recovery partition and restart recovery 458 # else: 459 # (stage must be "3/3") 460 # set stage to "" 461 # do normal full package installation: 462 # wipe and install system, boot image, etc. 463 # set up system to update recovery partition on first boot 464 # complete script normally (allow recovery to mark itself finished and reboot) 465 466 recovery_img = common.GetBootableImage("recovery.img", "recovery.img", 467 OPTIONS.input_tmp, "RECOVERY") 468 if OPTIONS.two_step: 469 if not OPTIONS.info_dict.get("multistage_support", None): 470 assert False, "two-step packages not supported by this build" 471 fs = OPTIONS.info_dict["fstab"]["/misc"] 472 assert fs.fs_type.upper() == "EMMC", \ 473 "two-step packages only supported on devices with EMMC /misc partitions" 474 bcb_dev = {"bcb_dev": fs.device} 475 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data) 476 script.AppendExtra(""" 477if get_stage("%(bcb_dev)s", "stage") == "2/3" then 478""" % bcb_dev) 479 script.WriteRawImage("/recovery", "recovery.img") 480 script.AppendExtra(""" 481set_stage("%(bcb_dev)s", "3/3"); 482reboot_now("%(bcb_dev)s", "recovery"); 483else if get_stage("%(bcb_dev)s", "stage") == "3/3" then 484""" % bcb_dev) 485 486 device_specific.FullOTA_InstallBegin() 487 488 system_progress = 0.75 489 490 if OPTIONS.wipe_user_data: 491 system_progress -= 0.1 492 493 if "selinux_fc" in OPTIONS.info_dict: 494 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip) 495 496 script.ShowProgress(system_progress, 30) 497 if block_based: 498 mapdata, data = img_from_target_files.BuildSystem( 499 OPTIONS.input_tmp, OPTIONS.info_dict, 500 sparse=False, map_file=True) 501 502 common.ZipWriteStr(output_zip, "system.map", mapdata) 503 common.ZipWriteStr(output_zip, "system.muimg", data) 504 script.WipeBlockDevice("/system") 505 script.WriteRawImage("/system", "system.muimg", mapfn="system.map") 506 else: 507 script.FormatPartition("/system") 508 script.Mount("/system") 509 if not has_recovery_patch: 510 script.UnpackPackageDir("recovery", "/system") 511 script.UnpackPackageDir("system", "/system") 512 513 symlinks = CopySystemFiles(input_zip, output_zip) 514 script.MakeSymlinks(symlinks) 515 516 boot_img = common.GetBootableImage("boot.img", "boot.img", 517 OPTIONS.input_tmp, "BOOT") 518 519 if not block_based: 520 def output_sink(fn, data): 521 common.ZipWriteStr(output_zip, "recovery/" + fn, data) 522 Item.Get("system/" + fn, dir=False) 523 524 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, 525 recovery_img, boot_img) 526 527 Item.GetMetadata(input_zip) 528 Item.Get("system").SetPermissions(script) 529 530 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict) 531 common.ZipWriteStr(output_zip, "boot.img", boot_img.data) 532 533 script.ShowProgress(0.05, 5) 534 script.WriteRawImage("/boot", "boot.img") 535 536 script.ShowProgress(0.2, 10) 537 device_specific.FullOTA_InstallEnd() 538 539 if OPTIONS.extra_script is not None: 540 script.AppendExtra(OPTIONS.extra_script) 541 542 script.UnmountAll() 543 544 if OPTIONS.wipe_user_data: 545 script.ShowProgress(0.1, 10) 546 script.FormatPartition("/data") 547 548 if OPTIONS.two_step: 549 script.AppendExtra(""" 550set_stage("%(bcb_dev)s", ""); 551""" % bcb_dev) 552 script.AppendExtra("else\n") 553 script.WriteRawImage("/boot", "recovery.img") 554 script.AppendExtra(""" 555set_stage("%(bcb_dev)s", "2/3"); 556reboot_now("%(bcb_dev)s", ""); 557endif; 558endif; 559""" % bcb_dev) 560 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary) 561 WriteMetadata(metadata, output_zip) 562 563def WritePolicyConfig(file_context, output_zip): 564 f = open(file_context, 'r'); 565 basename = os.path.basename(file_context) 566 common.ZipWriteStr(output_zip, basename, f.read()) 567 568 569def WriteMetadata(metadata, output_zip): 570 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata", 571 "".join(["%s=%s\n" % kv 572 for kv in sorted(metadata.iteritems())])) 573 574def LoadSystemFiles(z): 575 """Load all the files from SYSTEM/... in a given target-files 576 ZipFile, and return a dict of {filename: File object}.""" 577 out = {} 578 for info in z.infolist(): 579 if info.filename.startswith("SYSTEM/") and not IsSymlink(info): 580 basefilename = info.filename[7:] 581 fn = "system/" + basefilename 582 data = z.read(info.filename) 583 out[fn] = common.File(fn, data) 584 return out 585 586 587def GetBuildProp(prop, info_dict): 588 """Return the fingerprint of the build of a given target-files info_dict.""" 589 try: 590 return info_dict.get("build.prop", {})[prop] 591 except KeyError: 592 raise common.ExternalError("couldn't find %s in build.prop" % (prop,)) 593 594def AddToKnownPaths(filename, known_paths): 595 if filename[-1] == "/": 596 return 597 dirs = filename.split("/")[:-1] 598 while len(dirs) > 0: 599 path = "/".join(dirs) 600 if path in known_paths: 601 break; 602 known_paths.add(path) 603 dirs.pop() 604 605def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip): 606 source_version = OPTIONS.source_info_dict["recovery_api_version"] 607 target_version = OPTIONS.target_info_dict["recovery_api_version"] 608 609 if source_version == 0: 610 print ("WARNING: generating edify script for a source that " 611 "can't install it.") 612 script = edify_generator.EdifyGenerator(source_version, 613 OPTIONS.target_info_dict) 614 615 metadata = {"pre-device": GetBuildProp("ro.product.device", 616 OPTIONS.source_info_dict), 617 "post-timestamp": GetBuildProp("ro.build.date.utc", 618 OPTIONS.target_info_dict), 619 } 620 621 device_specific = common.DeviceSpecificParams( 622 source_zip=source_zip, 623 source_version=source_version, 624 target_zip=target_zip, 625 target_version=target_version, 626 output_zip=output_zip, 627 script=script, 628 metadata=metadata, 629 info_dict=OPTIONS.info_dict) 630 631 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict) 632 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict) 633 metadata["pre-build"] = source_fp 634 metadata["post-build"] = target_fp 635 636 source_boot = common.GetBootableImage( 637 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", 638 OPTIONS.source_info_dict) 639 target_boot = common.GetBootableImage( 640 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT") 641 updating_boot = (not OPTIONS.two_step and 642 (source_boot.data != target_boot.data)) 643 644 source_recovery = common.GetBootableImage( 645 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY", 646 OPTIONS.source_info_dict) 647 target_recovery = common.GetBootableImage( 648 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY") 649 updating_recovery = (source_recovery.data != target_recovery.data) 650 651 with tempfile.NamedTemporaryFile() as src_file: 652 with tempfile.NamedTemporaryFile() as tgt_file: 653 print "building source system image..." 654 src_file = tempfile.NamedTemporaryFile() 655 src_mapdata, src_data = img_from_target_files.BuildSystem( 656 OPTIONS.source_tmp, OPTIONS.source_info_dict, 657 sparse=False, map_file=True) 658 659 src_sys_sha1 = sha1(src_data).hexdigest() 660 print "source system sha1:", src_sys_sha1 661 src_file.write(src_data) 662 663 print "building target system image..." 664 tgt_file = tempfile.NamedTemporaryFile() 665 tgt_mapdata, tgt_data = img_from_target_files.BuildSystem( 666 OPTIONS.target_tmp, OPTIONS.target_info_dict, 667 sparse=False, map_file=True) 668 tgt_sys_sha1 = sha1(tgt_data).hexdigest() 669 print "target system sha1:", tgt_sys_sha1 670 tgt_sys_len = len(tgt_data) 671 tgt_file.write(tgt_data) 672 673 system_type, system_device = common.GetTypeAndDevice("/system", OPTIONS.info_dict) 674 system_patch = common.MakeSystemPatch(src_file, tgt_file) 675 676 TestBlockPatch(src_data, src_mapdata, system_patch.data, tgt_mapdata, tgt_sys_sha1) 677 src_data = None 678 tgt_data = None 679 680 system_patch.AddToZip(output_zip, compression=zipfile.ZIP_STORED) 681 src_mapfilename = system_patch.name + ".src.map" 682 common.ZipWriteStr(output_zip, src_mapfilename, src_mapdata) 683 tgt_mapfilename = system_patch.name + ".tgt.map" 684 common.ZipWriteStr(output_zip, tgt_mapfilename, tgt_mapdata) 685 686 oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties") 687 oem_dict = None 688 if oem_props is not None: 689 if OPTIONS.oem_source is None: 690 raise common.ExternalError("OEM source required for this build") 691 script.Mount("/oem") 692 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines()) 693 694 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict) 695 device_specific.IncrementalOTA_Assertions() 696 697 # Two-step incremental package strategy (in chronological order, 698 # which is *not* the order in which the generated script has 699 # things): 700 # 701 # if stage is not "2/3" or "3/3": 702 # do verification on current system 703 # write recovery image to boot partition 704 # set stage to "2/3" 705 # reboot to boot partition and restart recovery 706 # else if stage is "2/3": 707 # write recovery image to recovery partition 708 # set stage to "3/3" 709 # reboot to recovery partition and restart recovery 710 # else: 711 # (stage must be "3/3") 712 # perform update: 713 # patch system files, etc. 714 # force full install of new boot image 715 # set up system to update recovery partition on first boot 716 # complete script normally (allow recovery to mark itself finished and reboot) 717 718 if OPTIONS.two_step: 719 if not OPTIONS.info_dict.get("multistage_support", None): 720 assert False, "two-step packages not supported by this build" 721 fs = OPTIONS.info_dict["fstab"]["/misc"] 722 assert fs.fs_type.upper() == "EMMC", \ 723 "two-step packages only supported on devices with EMMC /misc partitions" 724 bcb_dev = {"bcb_dev": fs.device} 725 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data) 726 script.AppendExtra(""" 727if get_stage("%(bcb_dev)s", "stage") == "2/3" then 728""" % bcb_dev) 729 script.AppendExtra("sleep(20);\n"); 730 script.WriteRawImage("/recovery", "recovery.img") 731 script.AppendExtra(""" 732set_stage("%(bcb_dev)s", "3/3"); 733reboot_now("%(bcb_dev)s", "recovery"); 734else if get_stage("%(bcb_dev)s", "stage") != "3/3" then 735""" % bcb_dev) 736 737 script.Print("Verifying current system...") 738 739 device_specific.IncrementalOTA_VerifyBegin() 740 741 if oem_props is None: 742 script.AssertSomeFingerprint(source_fp, target_fp) 743 else: 744 script.AssertSomeThumbprint( 745 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict), 746 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict)) 747 748 if updating_boot: 749 d = common.Difference(target_boot, source_boot) 750 _, _, d = d.ComputePatch() 751 print "boot target: %d source: %d diff: %d" % ( 752 target_boot.size, source_boot.size, len(d)) 753 754 common.ZipWriteStr(output_zip, "patch/boot.img.p", d) 755 756 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict) 757 758 script.PatchCheck("%s:%s:%d:%s:%d:%s" % 759 (boot_type, boot_device, 760 source_boot.size, source_boot.sha1, 761 target_boot.size, target_boot.sha1)) 762 763 device_specific.IncrementalOTA_VerifyEnd() 764 765 if OPTIONS.two_step: 766 script.WriteRawImage("/boot", "recovery.img") 767 script.AppendExtra(""" 768set_stage("%(bcb_dev)s", "2/3"); 769reboot_now("%(bcb_dev)s", ""); 770else 771""" % bcb_dev) 772 773 script.Comment("---- start making changes here ----") 774 775 device_specific.IncrementalOTA_InstallBegin() 776 777 script.Print("Patching system image...") 778 script.Syspatch(system_device, 779 tgt_mapfilename, tgt_sys_sha1, 780 src_mapfilename, src_sys_sha1, 781 system_patch.name) 782 783 if OPTIONS.two_step: 784 common.ZipWriteStr(output_zip, "boot.img", target_boot.data) 785 script.WriteRawImage("/boot", "boot.img") 786 print "writing full boot image (forced by two-step mode)" 787 788 if not OPTIONS.two_step: 789 if updating_boot: 790 # Produce the boot image by applying a patch to the current 791 # contents of the boot partition, and write it back to the 792 # partition. 793 script.Print("Patching boot image...") 794 script.ApplyPatch("%s:%s:%d:%s:%d:%s" 795 % (boot_type, boot_device, 796 source_boot.size, source_boot.sha1, 797 target_boot.size, target_boot.sha1), 798 "-", 799 target_boot.size, target_boot.sha1, 800 source_boot.sha1, "patch/boot.img.p") 801 print "boot image changed; including." 802 else: 803 print "boot image unchanged; skipping." 804 805 # Do device-specific installation (eg, write radio image). 806 device_specific.IncrementalOTA_InstallEnd() 807 808 if OPTIONS.extra_script is not None: 809 script.AppendExtra(OPTIONS.extra_script) 810 811 if OPTIONS.wipe_user_data: 812 script.Print("Erasing user data...") 813 script.FormatPartition("/data") 814 815 if OPTIONS.two_step: 816 script.AppendExtra(""" 817set_stage("%(bcb_dev)s", ""); 818endif; 819endif; 820""" % bcb_dev) 821 822 script.SetProgress(1) 823 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary) 824 WriteMetadata(metadata, output_zip) 825 826def ParseMap(map_str): 827 x = map_str.split() 828 assert int(x[0]) == 4096 829 assert int(x[1]) == len(x)-2 830 return int(x[0]), [int(i) for i in x[2:]] 831 832def TestBlockPatch(src_muimg, src_map, patch_data, tgt_map, tgt_sha1): 833 src_blksize, src_regions = ParseMap(src_map) 834 tgt_blksize, tgt_regions = ParseMap(tgt_map) 835 836 with tempfile.NamedTemporaryFile() as src_file,\ 837 tempfile.NamedTemporaryFile() as patch_file,\ 838 tempfile.NamedTemporaryFile() as tgt_file,\ 839 tempfile.NamedTemporaryFile() as src_map_file,\ 840 tempfile.NamedTemporaryFile() as tgt_map_file: 841 842 src_total = sum(src_regions) * src_blksize 843 src_file.truncate(src_total) 844 p = 0 845 for i in range(0, len(src_regions), 2): 846 c, dc = src_regions[i:i+2] 847 src_file.write(src_muimg[p:(p+c*src_blksize)]) 848 p += c*src_blksize 849 src_file.seek(dc*src_blksize, 1) 850 assert src_file.tell() == src_total 851 852 patch_file.write(patch_data) 853 854 tgt_total = sum(tgt_regions) * tgt_blksize 855 tgt_file.truncate(tgt_total) 856 857 src_map_file.write(src_map) 858 tgt_map_file.write(tgt_map) 859 860 src_file.flush() 861 src_map_file.flush() 862 patch_file.flush() 863 tgt_file.flush() 864 tgt_map_file.flush() 865 866 p = common.Run(["syspatch_host", src_file.name, src_map_file.name, 867 patch_file.name, tgt_file.name, tgt_map_file.name], 868 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 869 stdoutdata, _ = p.communicate() 870 if p.returncode != 0: 871 print stdoutdata 872 raise ValueError("failed to reconstruct target system image from patch") 873 874 h = sha1() 875 for i in range(0, len(tgt_regions), 2): 876 c, dc = tgt_regions[i:i+2] 877 h.update(tgt_file.read(c*tgt_blksize)) 878 tgt_file.seek(dc*tgt_blksize, 1) 879 880 if h.hexdigest() != tgt_sha1: 881 raise ValueError("patch reconstructed incorrect target system image") 882 883 print "test of system image patch succeeded" 884 885 886def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): 887 target_has_recovery_patch = HasRecoveryPatch(target_zip) 888 source_has_recovery_patch = HasRecoveryPatch(source_zip) 889 890 if (OPTIONS.block_based and 891 target_has_recovery_patch and 892 source_has_recovery_patch): 893 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip) 894 895 source_version = OPTIONS.source_info_dict["recovery_api_version"] 896 target_version = OPTIONS.target_info_dict["recovery_api_version"] 897 898 if source_version == 0: 899 print ("WARNING: generating edify script for a source that " 900 "can't install it.") 901 script = edify_generator.EdifyGenerator(source_version, 902 OPTIONS.target_info_dict) 903 904 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties") 905 oem_dict = None 906 if oem_props is not None: 907 if OPTIONS.oem_source is None: 908 raise common.ExternalError("OEM source required for this build") 909 script.Mount("/oem") 910 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines()) 911 912 metadata = {"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict, 913 OPTIONS.source_info_dict), 914 "post-timestamp": GetBuildProp("ro.build.date.utc", 915 OPTIONS.target_info_dict), 916 } 917 918 device_specific = common.DeviceSpecificParams( 919 source_zip=source_zip, 920 source_version=source_version, 921 target_zip=target_zip, 922 target_version=target_version, 923 output_zip=output_zip, 924 script=script, 925 metadata=metadata, 926 info_dict=OPTIONS.info_dict) 927 928 print "Loading target..." 929 target_data = LoadSystemFiles(target_zip) 930 print "Loading source..." 931 source_data = LoadSystemFiles(source_zip) 932 933 verbatim_targets = [] 934 patch_list = [] 935 diffs = [] 936 renames = {} 937 known_paths = set() 938 largest_source_size = 0 939 940 matching_file_cache = {} 941 for fn, sf in source_data.items(): 942 assert fn == sf.name 943 matching_file_cache["path:" + fn] = sf 944 if fn in target_data.keys(): 945 AddToKnownPaths(fn, known_paths) 946 # Only allow eligibility for filename/sha matching 947 # if there isn't a perfect path match. 948 if target_data.get(sf.name) is None: 949 matching_file_cache["file:" + fn.split("/")[-1]] = sf 950 matching_file_cache["sha:" + sf.sha1] = sf 951 952 for fn in sorted(target_data.keys()): 953 tf = target_data[fn] 954 assert fn == tf.name 955 sf = ClosestFileMatch(tf, matching_file_cache, renames) 956 if sf is not None and sf.name != tf.name: 957 print "File has moved from " + sf.name + " to " + tf.name 958 renames[sf.name] = tf 959 960 if sf is None or fn in OPTIONS.require_verbatim: 961 # This file should be included verbatim 962 if fn in OPTIONS.prohibit_verbatim: 963 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,)) 964 print "send", fn, "verbatim" 965 tf.AddToZip(output_zip) 966 verbatim_targets.append((fn, tf.size)) 967 if fn in target_data.keys(): 968 AddToKnownPaths(fn, known_paths) 969 elif tf.sha1 != sf.sha1: 970 # File is different; consider sending as a patch 971 diffs.append(common.Difference(tf, sf)) 972 else: 973 # Target file data identical to source (may still be renamed) 974 pass 975 976 common.ComputeDifferences(diffs) 977 978 for diff in diffs: 979 tf, sf, d = diff.GetPatch() 980 path = "/".join(tf.name.split("/")[:-1]) 981 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \ 982 path not in known_paths: 983 # patch is almost as big as the file; don't bother patching 984 # or a patch + rename cannot take place due to the target 985 # directory not existing 986 tf.AddToZip(output_zip) 987 verbatim_targets.append((tf.name, tf.size)) 988 if sf.name in renames: 989 del renames[sf.name] 990 AddToKnownPaths(tf.name, known_paths) 991 else: 992 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d) 993 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest())) 994 largest_source_size = max(largest_source_size, sf.size) 995 996 script.Mount("/system") 997 998 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.target_info_dict) 999 source_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.source_info_dict) 1000 1001 if oem_props is None: 1002 script.AssertSomeFingerprint(source_fp, target_fp) 1003 else: 1004 script.AssertSomeThumbprint( 1005 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict), 1006 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict)) 1007 1008 metadata["pre-build"] = source_fp 1009 metadata["post-build"] = target_fp 1010 1011 source_boot = common.GetBootableImage( 1012 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", 1013 OPTIONS.source_info_dict) 1014 target_boot = common.GetBootableImage( 1015 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT") 1016 updating_boot = (not OPTIONS.two_step and 1017 (source_boot.data != target_boot.data)) 1018 1019 source_recovery = common.GetBootableImage( 1020 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY", 1021 OPTIONS.source_info_dict) 1022 target_recovery = common.GetBootableImage( 1023 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY") 1024 updating_recovery = (source_recovery.data != target_recovery.data) 1025 1026 # Here's how we divide up the progress bar: 1027 # 0.1 for verifying the start state (PatchCheck calls) 1028 # 0.8 for applying patches (ApplyPatch calls) 1029 # 0.1 for unpacking verbatim files, symlinking, and doing the 1030 # device-specific commands. 1031 1032 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict) 1033 device_specific.IncrementalOTA_Assertions() 1034 1035 # Two-step incremental package strategy (in chronological order, 1036 # which is *not* the order in which the generated script has 1037 # things): 1038 # 1039 # if stage is not "2/3" or "3/3": 1040 # do verification on current system 1041 # write recovery image to boot partition 1042 # set stage to "2/3" 1043 # reboot to boot partition and restart recovery 1044 # else if stage is "2/3": 1045 # write recovery image to recovery partition 1046 # set stage to "3/3" 1047 # reboot to recovery partition and restart recovery 1048 # else: 1049 # (stage must be "3/3") 1050 # perform update: 1051 # patch system files, etc. 1052 # force full install of new boot image 1053 # set up system to update recovery partition on first boot 1054 # complete script normally (allow recovery to mark itself finished and reboot) 1055 1056 if OPTIONS.two_step: 1057 if not OPTIONS.info_dict.get("multistage_support", None): 1058 assert False, "two-step packages not supported by this build" 1059 fs = OPTIONS.info_dict["fstab"]["/misc"] 1060 assert fs.fs_type.upper() == "EMMC", \ 1061 "two-step packages only supported on devices with EMMC /misc partitions" 1062 bcb_dev = {"bcb_dev": fs.device} 1063 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data) 1064 script.AppendExtra(""" 1065if get_stage("%(bcb_dev)s", "stage") == "2/3" then 1066""" % bcb_dev) 1067 script.AppendExtra("sleep(20);\n"); 1068 script.WriteRawImage("/recovery", "recovery.img") 1069 script.AppendExtra(""" 1070set_stage("%(bcb_dev)s", "3/3"); 1071reboot_now("%(bcb_dev)s", "recovery"); 1072else if get_stage("%(bcb_dev)s", "stage") != "3/3" then 1073""" % bcb_dev) 1074 1075 script.Print("Verifying current system...") 1076 1077 device_specific.IncrementalOTA_VerifyBegin() 1078 1079 script.ShowProgress(0.1, 0) 1080 so_far = 0 1081 1082 for tf, sf, size, patch_sha in patch_list: 1083 if tf.name != sf.name: 1084 script.SkipNextActionIfTargetExists(tf.name, tf.sha1) 1085 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1) 1086 so_far += sf.size 1087 1088 if updating_boot: 1089 d = common.Difference(target_boot, source_boot) 1090 _, _, d = d.ComputePatch() 1091 print "boot target: %d source: %d diff: %d" % ( 1092 target_boot.size, source_boot.size, len(d)) 1093 1094 common.ZipWriteStr(output_zip, "patch/boot.img.p", d) 1095 1096 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict) 1097 1098 script.PatchCheck("%s:%s:%d:%s:%d:%s" % 1099 (boot_type, boot_device, 1100 source_boot.size, source_boot.sha1, 1101 target_boot.size, target_boot.sha1)) 1102 so_far += source_boot.size 1103 1104 if patch_list or updating_recovery or updating_boot: 1105 script.CacheFreeSpaceCheck(largest_source_size) 1106 1107 device_specific.IncrementalOTA_VerifyEnd() 1108 1109 if OPTIONS.two_step: 1110 script.WriteRawImage("/boot", "recovery.img") 1111 script.AppendExtra(""" 1112set_stage("%(bcb_dev)s", "2/3"); 1113reboot_now("%(bcb_dev)s", ""); 1114else 1115""" % bcb_dev) 1116 1117 script.Comment("---- start making changes here ----") 1118 1119 device_specific.IncrementalOTA_InstallBegin() 1120 1121 if OPTIONS.two_step: 1122 common.ZipWriteStr(output_zip, "boot.img", target_boot.data) 1123 script.WriteRawImage("/boot", "boot.img") 1124 print "writing full boot image (forced by two-step mode)" 1125 1126 script.Print("Removing unneeded files...") 1127 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] + 1128 ["/"+i for i in sorted(source_data) 1129 if i not in target_data and 1130 i not in renames] + 1131 ["/system/recovery.img"]) 1132 1133 script.ShowProgress(0.8, 0) 1134 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1) 1135 if updating_boot: 1136 total_patch_size += target_boot.size 1137 so_far = 0 1138 1139 script.Print("Patching system files...") 1140 deferred_patch_list = [] 1141 for item in patch_list: 1142 tf, sf, size, _ = item 1143 if tf.name == "system/build.prop": 1144 deferred_patch_list.append(item) 1145 continue 1146 if (sf.name != tf.name): 1147 script.SkipNextActionIfTargetExists(tf.name, tf.sha1) 1148 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p") 1149 so_far += tf.size 1150 script.SetProgress(so_far / total_patch_size) 1151 1152 if not OPTIONS.two_step: 1153 if updating_boot: 1154 # Produce the boot image by applying a patch to the current 1155 # contents of the boot partition, and write it back to the 1156 # partition. 1157 script.Print("Patching boot image...") 1158 script.ApplyPatch("%s:%s:%d:%s:%d:%s" 1159 % (boot_type, boot_device, 1160 source_boot.size, source_boot.sha1, 1161 target_boot.size, target_boot.sha1), 1162 "-", 1163 target_boot.size, target_boot.sha1, 1164 source_boot.sha1, "patch/boot.img.p") 1165 so_far += target_boot.size 1166 script.SetProgress(so_far / total_patch_size) 1167 print "boot image changed; including." 1168 else: 1169 print "boot image unchanged; skipping." 1170 1171 if updating_recovery: 1172 # Recovery is generated as a patch using both the boot image 1173 # (which contains the same linux kernel as recovery) and the file 1174 # /system/etc/recovery-resource.dat (which contains all the images 1175 # used in the recovery UI) as sources. This lets us minimize the 1176 # size of the patch, which must be included in every OTA package. 1177 # 1178 # For older builds where recovery-resource.dat is not present, we 1179 # use only the boot image as the source. 1180 1181 if not target_has_recovery_patch: 1182 def output_sink(fn, data): 1183 common.ZipWriteStr(output_zip, "recovery/" + fn, data) 1184 Item.Get("system/" + fn, dir=False) 1185 1186 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink, 1187 target_recovery, target_boot) 1188 script.DeleteFiles(["/system/recovery-from-boot.p", 1189 "/system/etc/install-recovery.sh"]) 1190 print "recovery image changed; including as patch from boot." 1191 else: 1192 print "recovery image unchanged; skipping." 1193 1194 script.ShowProgress(0.1, 10) 1195 1196 target_symlinks = CopySystemFiles(target_zip, None) 1197 1198 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks]) 1199 temp_script = script.MakeTemporary() 1200 Item.GetMetadata(target_zip) 1201 Item.Get("system").SetPermissions(temp_script) 1202 1203 # Note that this call will mess up the tree of Items, so make sure 1204 # we're done with it. 1205 source_symlinks = CopySystemFiles(source_zip, None) 1206 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks]) 1207 1208 # Delete all the symlinks in source that aren't in target. This 1209 # needs to happen before verbatim files are unpacked, in case a 1210 # symlink in the source is replaced by a real file in the target. 1211 to_delete = [] 1212 for dest, link in source_symlinks: 1213 if link not in target_symlinks_d: 1214 to_delete.append(link) 1215 script.DeleteFiles(to_delete) 1216 1217 if verbatim_targets: 1218 script.Print("Unpacking new files...") 1219 script.UnpackPackageDir("system", "/system") 1220 1221 if updating_recovery and not target_has_recovery_patch: 1222 script.Print("Unpacking new recovery...") 1223 script.UnpackPackageDir("recovery", "/system") 1224 1225 if len(renames) > 0: 1226 script.Print("Renaming files...") 1227 1228 for src in renames: 1229 print "Renaming " + src + " to " + renames[src].name 1230 script.RenameFile(src, renames[src].name) 1231 1232 script.Print("Symlinks and permissions...") 1233 1234 # Create all the symlinks that don't already exist, or point to 1235 # somewhere different than what we want. Delete each symlink before 1236 # creating it, since the 'symlink' command won't overwrite. 1237 to_create = [] 1238 for dest, link in target_symlinks: 1239 if link in source_symlinks_d: 1240 if dest != source_symlinks_d[link]: 1241 to_create.append((dest, link)) 1242 else: 1243 to_create.append((dest, link)) 1244 script.DeleteFiles([i[1] for i in to_create]) 1245 script.MakeSymlinks(to_create) 1246 1247 # Now that the symlinks are created, we can set all the 1248 # permissions. 1249 script.AppendScript(temp_script) 1250 1251 # Do device-specific installation (eg, write radio image). 1252 device_specific.IncrementalOTA_InstallEnd() 1253 1254 if OPTIONS.extra_script is not None: 1255 script.AppendExtra(OPTIONS.extra_script) 1256 1257 # Patch the build.prop file last, so if something fails but the 1258 # device can still come up, it appears to be the old build and will 1259 # get set the OTA package again to retry. 1260 script.Print("Patching remaining system files...") 1261 for item in deferred_patch_list: 1262 tf, sf, size, _ = item 1263 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p") 1264 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None) 1265 1266 if OPTIONS.wipe_user_data: 1267 script.Print("Erasing user data...") 1268 script.FormatPartition("/data") 1269 1270 if OPTIONS.two_step: 1271 script.AppendExtra(""" 1272set_stage("%(bcb_dev)s", ""); 1273endif; 1274endif; 1275""" % bcb_dev) 1276 1277 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary) 1278 WriteMetadata(metadata, output_zip) 1279 1280 1281def main(argv): 1282 1283 def option_handler(o, a): 1284 if o == "--board_config": 1285 pass # deprecated 1286 elif o in ("-k", "--package_key"): 1287 OPTIONS.package_key = a 1288 elif o in ("-i", "--incremental_from"): 1289 OPTIONS.incremental_source = a 1290 elif o in ("-w", "--wipe_user_data"): 1291 OPTIONS.wipe_user_data = True 1292 elif o in ("-n", "--no_prereq"): 1293 OPTIONS.omit_prereq = True 1294 elif o in ("-o", "--oem_settings"): 1295 OPTIONS.oem_source = a 1296 elif o in ("-e", "--extra_script"): 1297 OPTIONS.extra_script = a 1298 elif o in ("-a", "--aslr_mode"): 1299 if a in ("on", "On", "true", "True", "yes", "Yes"): 1300 OPTIONS.aslr_mode = True 1301 else: 1302 OPTIONS.aslr_mode = False 1303 elif o in ("--worker_threads"): 1304 OPTIONS.worker_threads = int(a) 1305 elif o in ("-2", "--two_step"): 1306 OPTIONS.two_step = True 1307 elif o == "--no_signing": 1308 OPTIONS.no_signing = True 1309 elif o == "--block": 1310 OPTIONS.block_based = True 1311 elif o in ("-b", "--binary"): 1312 OPTIONS.updater_binary = a 1313 else: 1314 return False 1315 return True 1316 1317 args = common.ParseOptions(argv, __doc__, 1318 extra_opts="b:k:i:d:wne:a:2o:", 1319 extra_long_opts=["board_config=", 1320 "package_key=", 1321 "incremental_from=", 1322 "wipe_user_data", 1323 "no_prereq", 1324 "extra_script=", 1325 "worker_threads=", 1326 "aslr_mode=", 1327 "two_step", 1328 "no_signing", 1329 "block", 1330 "binary=", 1331 "oem_settings=", 1332 ], 1333 extra_option_handler=option_handler) 1334 1335 if len(args) != 2: 1336 common.Usage(__doc__) 1337 sys.exit(1) 1338 1339 if OPTIONS.extra_script is not None: 1340 OPTIONS.extra_script = open(OPTIONS.extra_script).read() 1341 1342 print "unzipping target target-files..." 1343 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0]) 1344 1345 OPTIONS.target_tmp = OPTIONS.input_tmp 1346 OPTIONS.info_dict = common.LoadInfoDict(input_zip) 1347 1348 # If this image was originally labelled with SELinux contexts, make sure we 1349 # also apply the labels in our new image. During building, the "file_contexts" 1350 # is in the out/ directory tree, but for repacking from target-files.zip it's 1351 # in the root directory of the ramdisk. 1352 if "selinux_fc" in OPTIONS.info_dict: 1353 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK", 1354 "file_contexts") 1355 1356 if OPTIONS.verbose: 1357 print "--- target info ---" 1358 common.DumpInfoDict(OPTIONS.info_dict) 1359 1360 # If the caller explicitly specified the device-specific extensions 1361 # path via -s/--device_specific, use that. Otherwise, use 1362 # META/releasetools.py if it is present in the target target_files. 1363 # Otherwise, take the path of the file from 'tool_extensions' in the 1364 # info dict and look for that in the local filesystem, relative to 1365 # the current directory. 1366 1367 if OPTIONS.device_specific is None: 1368 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py") 1369 if os.path.exists(from_input): 1370 print "(using device-specific extensions from target_files)" 1371 OPTIONS.device_specific = from_input 1372 else: 1373 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None) 1374 1375 if OPTIONS.device_specific is not None: 1376 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific) 1377 1378 if OPTIONS.no_signing: 1379 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED) 1380 else: 1381 temp_zip_file = tempfile.NamedTemporaryFile() 1382 output_zip = zipfile.ZipFile(temp_zip_file, "w", 1383 compression=zipfile.ZIP_DEFLATED) 1384 1385 if OPTIONS.incremental_source is None: 1386 WriteFullOTAPackage(input_zip, output_zip) 1387 if OPTIONS.package_key is None: 1388 OPTIONS.package_key = OPTIONS.info_dict.get( 1389 "default_system_dev_certificate", 1390 "build/target/product/security/testkey") 1391 else: 1392 print "unzipping source target-files..." 1393 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source) 1394 OPTIONS.target_info_dict = OPTIONS.info_dict 1395 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip) 1396 if "selinux_fc" in OPTIONS.source_info_dict: 1397 OPTIONS.source_info_dict["selinux_fc"] = os.path.join(OPTIONS.source_tmp, "BOOT", "RAMDISK", 1398 "file_contexts") 1399 if OPTIONS.package_key is None: 1400 OPTIONS.package_key = OPTIONS.source_info_dict.get( 1401 "default_system_dev_certificate", 1402 "build/target/product/security/testkey") 1403 if OPTIONS.verbose: 1404 print "--- source info ---" 1405 common.DumpInfoDict(OPTIONS.source_info_dict) 1406 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip) 1407 1408 output_zip.close() 1409 1410 if not OPTIONS.no_signing: 1411 SignOutput(temp_zip_file.name, args[1]) 1412 temp_zip_file.close() 1413 1414 common.Cleanup() 1415 1416 print "done." 1417 1418 1419if __name__ == '__main__': 1420 try: 1421 common.CloseInheritedPipes() 1422 main(sys.argv[1:]) 1423 except common.ExternalError, e: 1424 print 1425 print " ERROR: %s" % (e,) 1426 print 1427 sys.exit(1) 1428