ota_from_target_files.py revision 6e836116f764cf5cebf1654df2f17d8222554f6e
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): 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 has_recovery_patch: 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 AppendAssertions(script, OPTIONS.target_info_dict) 687 device_specific.IncrementalOTA_Assertions() 688 689 # Two-step incremental package strategy (in chronological order, 690 # which is *not* the order in which the generated script has 691 # things): 692 # 693 # if stage is not "2/3" or "3/3": 694 # do verification on current system 695 # write recovery image to boot partition 696 # set stage to "2/3" 697 # reboot to boot partition and restart recovery 698 # else if stage is "2/3": 699 # write recovery image to recovery partition 700 # set stage to "3/3" 701 # reboot to recovery partition and restart recovery 702 # else: 703 # (stage must be "3/3") 704 # perform update: 705 # patch system files, etc. 706 # force full install of new boot image 707 # set up system to update recovery partition on first boot 708 # complete script normally (allow recovery to mark itself finished and reboot) 709 710 if OPTIONS.two_step: 711 if not OPTIONS.info_dict.get("multistage_support", None): 712 assert False, "two-step packages not supported by this build" 713 fs = OPTIONS.info_dict["fstab"]["/misc"] 714 assert fs.fs_type.upper() == "EMMC", \ 715 "two-step packages only supported on devices with EMMC /misc partitions" 716 bcb_dev = {"bcb_dev": fs.device} 717 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data) 718 script.AppendExtra(""" 719if get_stage("%(bcb_dev)s", "stage") == "2/3" then 720""" % bcb_dev) 721 script.AppendExtra("sleep(20);\n"); 722 script.WriteRawImage("/recovery", "recovery.img") 723 script.AppendExtra(""" 724set_stage("%(bcb_dev)s", "3/3"); 725reboot_now("%(bcb_dev)s", "recovery"); 726else if get_stage("%(bcb_dev)s", "stage") != "3/3" then 727""" % bcb_dev) 728 729 script.Print("Verifying current system...") 730 731 device_specific.IncrementalOTA_VerifyBegin() 732 733 script.AssertRecoveryFingerprint(source_fp, target_fp) 734 735 if updating_boot: 736 d = common.Difference(target_boot, source_boot) 737 _, _, d = d.ComputePatch() 738 print "boot target: %d source: %d diff: %d" % ( 739 target_boot.size, source_boot.size, len(d)) 740 741 common.ZipWriteStr(output_zip, "patch/boot.img.p", d) 742 743 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict) 744 745 script.PatchCheck("%s:%s:%d:%s:%d:%s" % 746 (boot_type, boot_device, 747 source_boot.size, source_boot.sha1, 748 target_boot.size, target_boot.sha1)) 749 750 device_specific.IncrementalOTA_VerifyEnd() 751 752 if OPTIONS.two_step: 753 script.WriteRawImage("/boot", "recovery.img") 754 script.AppendExtra(""" 755set_stage("%(bcb_dev)s", "2/3"); 756reboot_now("%(bcb_dev)s", ""); 757else 758""" % bcb_dev) 759 760 script.Comment("---- start making changes here ----") 761 762 device_specific.IncrementalOTA_InstallBegin() 763 764 script.Print("Patching system image...") 765 script.Syspatch(system_device, 766 tgt_mapfilename, tgt_sys_sha1, 767 src_mapfilename, src_sys_sha1, 768 system_patch.name) 769 770 if OPTIONS.two_step: 771 common.ZipWriteStr(output_zip, "boot.img", target_boot.data) 772 script.WriteRawImage("/boot", "boot.img") 773 print "writing full boot image (forced by two-step mode)" 774 775 if not OPTIONS.two_step: 776 if updating_boot: 777 # Produce the boot image by applying a patch to the current 778 # contents of the boot partition, and write it back to the 779 # partition. 780 script.Print("Patching boot image...") 781 script.ApplyPatch("%s:%s:%d:%s:%d:%s" 782 % (boot_type, boot_device, 783 source_boot.size, source_boot.sha1, 784 target_boot.size, target_boot.sha1), 785 "-", 786 target_boot.size, target_boot.sha1, 787 source_boot.sha1, "patch/boot.img.p") 788 print "boot image changed; including." 789 else: 790 print "boot image unchanged; skipping." 791 792 # Do device-specific installation (eg, write radio image). 793 device_specific.IncrementalOTA_InstallEnd() 794 795 if OPTIONS.extra_script is not None: 796 script.AppendExtra(OPTIONS.extra_script) 797 798 if OPTIONS.wipe_user_data: 799 script.Print("Erasing user data...") 800 script.FormatPartition("/data") 801 802 if OPTIONS.two_step: 803 script.AppendExtra(""" 804set_stage("%(bcb_dev)s", ""); 805endif; 806endif; 807""" % bcb_dev) 808 809 script.SetProgress(1) 810 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary) 811 WriteMetadata(metadata, output_zip) 812 813def ParseMap(map_str): 814 x = map_str.split() 815 assert int(x[0]) == 4096 816 assert int(x[1]) == len(x)-2 817 return int(x[0]), [int(i) for i in x[2:]] 818 819def TestBlockPatch(src_muimg, src_map, patch_data, tgt_map, tgt_sha1): 820 src_blksize, src_regions = ParseMap(src_map) 821 tgt_blksize, tgt_regions = ParseMap(tgt_map) 822 823 with tempfile.NamedTemporaryFile() as src_file,\ 824 tempfile.NamedTemporaryFile() as patch_file,\ 825 tempfile.NamedTemporaryFile() as tgt_file,\ 826 tempfile.NamedTemporaryFile() as src_map_file,\ 827 tempfile.NamedTemporaryFile() as tgt_map_file: 828 829 src_total = sum(src_regions) * src_blksize 830 src_file.truncate(src_total) 831 p = 0 832 for i in range(0, len(src_regions), 2): 833 c, dc = src_regions[i:i+2] 834 src_file.write(src_muimg[p:(p+c*src_blksize)]) 835 p += c*src_blksize 836 src_file.seek(dc*src_blksize, 1) 837 assert src_file.tell() == src_total 838 839 patch_file.write(patch_data) 840 841 tgt_total = sum(tgt_regions) * tgt_blksize 842 tgt_file.truncate(tgt_total) 843 844 src_map_file.write(src_map) 845 tgt_map_file.write(tgt_map) 846 847 src_file.flush() 848 src_map_file.flush() 849 patch_file.flush() 850 tgt_file.flush() 851 tgt_map_file.flush() 852 853 p = common.Run(["syspatch_host", src_file.name, src_map_file.name, 854 patch_file.name, tgt_file.name, tgt_map_file.name], 855 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 856 stdoutdata, _ = p.communicate() 857 if p.returncode != 0: 858 print stdoutdata 859 raise ValueError("failed to reconstruct target system image from patch") 860 861 h = sha1() 862 for i in range(0, len(tgt_regions), 2): 863 c, dc = tgt_regions[i:i+2] 864 h.update(tgt_file.read(c*tgt_blksize)) 865 tgt_file.seek(dc*tgt_blksize, 1) 866 867 if h.hexdigest() != tgt_sha1: 868 raise ValueError("patch reconstructed incorrect target system image") 869 870 print "test of system image patch succeeded" 871 872 873def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip): 874 target_has_recovery_patch = HasRecoveryPatch(target_zip) 875 source_has_recovery_patch = HasRecoveryPatch(source_zip) 876 877 if (OPTIONS.block_based and 878 target_has_recovery_patch and 879 source_has_recovery_patch): 880 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip) 881 882 source_version = OPTIONS.source_info_dict["recovery_api_version"] 883 target_version = OPTIONS.target_info_dict["recovery_api_version"] 884 885 if source_version == 0: 886 print ("WARNING: generating edify script for a source that " 887 "can't install it.") 888 script = edify_generator.EdifyGenerator(source_version, 889 OPTIONS.target_info_dict) 890 891 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties") 892 oem_dict = None 893 if oem_props is not None: 894 if OPTIONS.oem_source is None: 895 raise common.ExternalError("OEM source required for this build") 896 script.Mount("/oem") 897 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines()) 898 899 metadata = {"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict, 900 OPTIONS.source_info_dict), 901 "post-timestamp": GetBuildProp("ro.build.date.utc", 902 OPTIONS.target_info_dict), 903 } 904 905 device_specific = common.DeviceSpecificParams( 906 source_zip=source_zip, 907 source_version=source_version, 908 target_zip=target_zip, 909 target_version=target_version, 910 output_zip=output_zip, 911 script=script, 912 metadata=metadata, 913 info_dict=OPTIONS.info_dict) 914 915 print "Loading target..." 916 target_data = LoadSystemFiles(target_zip) 917 print "Loading source..." 918 source_data = LoadSystemFiles(source_zip) 919 920 verbatim_targets = [] 921 patch_list = [] 922 diffs = [] 923 renames = {} 924 known_paths = set() 925 largest_source_size = 0 926 927 matching_file_cache = {} 928 for fn, sf in source_data.items(): 929 assert fn == sf.name 930 matching_file_cache["path:" + fn] = sf 931 if fn in target_data.keys(): 932 AddToKnownPaths(fn, known_paths) 933 # Only allow eligibility for filename/sha matching 934 # if there isn't a perfect path match. 935 if target_data.get(sf.name) is None: 936 matching_file_cache["file:" + fn.split("/")[-1]] = sf 937 matching_file_cache["sha:" + sf.sha1] = sf 938 939 for fn in sorted(target_data.keys()): 940 tf = target_data[fn] 941 assert fn == tf.name 942 sf = ClosestFileMatch(tf, matching_file_cache, renames) 943 if sf is not None and sf.name != tf.name: 944 print "File has moved from " + sf.name + " to " + tf.name 945 renames[sf.name] = tf 946 947 if sf is None or fn in OPTIONS.require_verbatim: 948 # This file should be included verbatim 949 if fn in OPTIONS.prohibit_verbatim: 950 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,)) 951 print "send", fn, "verbatim" 952 tf.AddToZip(output_zip) 953 verbatim_targets.append((fn, tf.size)) 954 if fn in target_data.keys(): 955 AddToKnownPaths(fn, known_paths) 956 elif tf.sha1 != sf.sha1: 957 # File is different; consider sending as a patch 958 diffs.append(common.Difference(tf, sf)) 959 else: 960 # Target file data identical to source (may still be renamed) 961 pass 962 963 common.ComputeDifferences(diffs) 964 965 for diff in diffs: 966 tf, sf, d = diff.GetPatch() 967 path = "/".join(tf.name.split("/")[:-1]) 968 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \ 969 path not in known_paths: 970 # patch is almost as big as the file; don't bother patching 971 # or a patch + rename cannot take place due to the target 972 # directory not existing 973 tf.AddToZip(output_zip) 974 verbatim_targets.append((tf.name, tf.size)) 975 if sf.name in renames: 976 del renames[sf.name] 977 AddToKnownPaths(tf.name, known_paths) 978 else: 979 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d) 980 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest())) 981 largest_source_size = max(largest_source_size, sf.size) 982 983 script.Mount("/system") 984 985 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.target_info_dict) 986 source_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.source_info_dict) 987 988 if oem_props is None: 989 script.AssertSomeFingerprint(source_fp, target_fp) 990 else: 991 script.AssertSomeThumbprint( 992 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict), 993 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict)) 994 995 metadata["pre-build"] = source_fp 996 metadata["post-build"] = target_fp 997 998 source_boot = common.GetBootableImage( 999 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", 1000 OPTIONS.source_info_dict) 1001 target_boot = common.GetBootableImage( 1002 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT") 1003 updating_boot = (not OPTIONS.two_step and 1004 (source_boot.data != target_boot.data)) 1005 1006 source_recovery = common.GetBootableImage( 1007 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY", 1008 OPTIONS.source_info_dict) 1009 target_recovery = common.GetBootableImage( 1010 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY") 1011 updating_recovery = (source_recovery.data != target_recovery.data) 1012 1013 # Here's how we divide up the progress bar: 1014 # 0.1 for verifying the start state (PatchCheck calls) 1015 # 0.8 for applying patches (ApplyPatch calls) 1016 # 0.1 for unpacking verbatim files, symlinking, and doing the 1017 # device-specific commands. 1018 1019 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict) 1020 device_specific.IncrementalOTA_Assertions() 1021 1022 # Two-step incremental package strategy (in chronological order, 1023 # which is *not* the order in which the generated script has 1024 # things): 1025 # 1026 # if stage is not "2/3" or "3/3": 1027 # do verification on current system 1028 # write recovery image to boot partition 1029 # set stage to "2/3" 1030 # reboot to boot partition and restart recovery 1031 # else if stage is "2/3": 1032 # write recovery image to recovery partition 1033 # set stage to "3/3" 1034 # reboot to recovery partition and restart recovery 1035 # else: 1036 # (stage must be "3/3") 1037 # perform update: 1038 # patch system files, etc. 1039 # force full install of new boot image 1040 # set up system to update recovery partition on first boot 1041 # complete script normally (allow recovery to mark itself finished and reboot) 1042 1043 if OPTIONS.two_step: 1044 if not OPTIONS.info_dict.get("multistage_support", None): 1045 assert False, "two-step packages not supported by this build" 1046 fs = OPTIONS.info_dict["fstab"]["/misc"] 1047 assert fs.fs_type.upper() == "EMMC", \ 1048 "two-step packages only supported on devices with EMMC /misc partitions" 1049 bcb_dev = {"bcb_dev": fs.device} 1050 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data) 1051 script.AppendExtra(""" 1052if get_stage("%(bcb_dev)s", "stage") == "2/3" then 1053""" % bcb_dev) 1054 script.AppendExtra("sleep(20);\n"); 1055 script.WriteRawImage("/recovery", "recovery.img") 1056 script.AppendExtra(""" 1057set_stage("%(bcb_dev)s", "3/3"); 1058reboot_now("%(bcb_dev)s", "recovery"); 1059else if get_stage("%(bcb_dev)s", "stage") != "3/3" then 1060""" % bcb_dev) 1061 1062 script.Print("Verifying current system...") 1063 1064 device_specific.IncrementalOTA_VerifyBegin() 1065 1066 script.ShowProgress(0.1, 0) 1067 so_far = 0 1068 1069 for tf, sf, size, patch_sha in patch_list: 1070 if tf.name != sf.name: 1071 script.SkipNextActionIfTargetExists(tf.name, tf.sha1) 1072 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1) 1073 so_far += sf.size 1074 1075 if updating_boot: 1076 d = common.Difference(target_boot, source_boot) 1077 _, _, d = d.ComputePatch() 1078 print "boot target: %d source: %d diff: %d" % ( 1079 target_boot.size, source_boot.size, len(d)) 1080 1081 common.ZipWriteStr(output_zip, "patch/boot.img.p", d) 1082 1083 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict) 1084 1085 script.PatchCheck("%s:%s:%d:%s:%d:%s" % 1086 (boot_type, boot_device, 1087 source_boot.size, source_boot.sha1, 1088 target_boot.size, target_boot.sha1)) 1089 so_far += source_boot.size 1090 1091 if patch_list or updating_recovery or updating_boot: 1092 script.CacheFreeSpaceCheck(largest_source_size) 1093 1094 device_specific.IncrementalOTA_VerifyEnd() 1095 1096 if OPTIONS.two_step: 1097 script.WriteRawImage("/boot", "recovery.img") 1098 script.AppendExtra(""" 1099set_stage("%(bcb_dev)s", "2/3"); 1100reboot_now("%(bcb_dev)s", ""); 1101else 1102""" % bcb_dev) 1103 1104 script.Comment("---- start making changes here ----") 1105 1106 device_specific.IncrementalOTA_InstallBegin() 1107 1108 if OPTIONS.two_step: 1109 common.ZipWriteStr(output_zip, "boot.img", target_boot.data) 1110 script.WriteRawImage("/boot", "boot.img") 1111 print "writing full boot image (forced by two-step mode)" 1112 1113 script.Print("Removing unneeded files...") 1114 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] + 1115 ["/"+i for i in sorted(source_data) 1116 if i not in target_data and 1117 i not in renames] + 1118 ["/system/recovery.img"]) 1119 1120 script.ShowProgress(0.8, 0) 1121 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1) 1122 if updating_boot: 1123 total_patch_size += target_boot.size 1124 so_far = 0 1125 1126 script.Print("Patching system files...") 1127 deferred_patch_list = [] 1128 for item in patch_list: 1129 tf, sf, size, _ = item 1130 if tf.name == "system/build.prop": 1131 deferred_patch_list.append(item) 1132 continue 1133 if (sf.name != tf.name): 1134 script.SkipNextActionIfTargetExists(tf.name, tf.sha1) 1135 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p") 1136 so_far += tf.size 1137 script.SetProgress(so_far / total_patch_size) 1138 1139 if not OPTIONS.two_step: 1140 if updating_boot: 1141 # Produce the boot image by applying a patch to the current 1142 # contents of the boot partition, and write it back to the 1143 # partition. 1144 script.Print("Patching boot image...") 1145 script.ApplyPatch("%s:%s:%d:%s:%d:%s" 1146 % (boot_type, boot_device, 1147 source_boot.size, source_boot.sha1, 1148 target_boot.size, target_boot.sha1), 1149 "-", 1150 target_boot.size, target_boot.sha1, 1151 source_boot.sha1, "patch/boot.img.p") 1152 so_far += target_boot.size 1153 script.SetProgress(so_far / total_patch_size) 1154 print "boot image changed; including." 1155 else: 1156 print "boot image unchanged; skipping." 1157 1158 if updating_recovery: 1159 # Recovery is generated as a patch using both the boot image 1160 # (which contains the same linux kernel as recovery) and the file 1161 # /system/etc/recovery-resource.dat (which contains all the images 1162 # used in the recovery UI) as sources. This lets us minimize the 1163 # size of the patch, which must be included in every OTA package. 1164 # 1165 # For older builds where recovery-resource.dat is not present, we 1166 # use only the boot image as the source. 1167 1168 if not target_has_recovery_patch: 1169 def output_sink(fn, data): 1170 common.ZipWriteStr(output_zip, "recovery/" + fn, data) 1171 Item.Get("system/" + fn, dir=False) 1172 1173 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink, 1174 target_recovery, target_boot) 1175 script.DeleteFiles(["/system/recovery-from-boot.p", 1176 "/system/etc/install-recovery.sh"]) 1177 print "recovery image changed; including as patch from boot." 1178 else: 1179 print "recovery image unchanged; skipping." 1180 1181 script.ShowProgress(0.1, 10) 1182 1183 target_symlinks = CopySystemFiles(target_zip, None) 1184 1185 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks]) 1186 temp_script = script.MakeTemporary() 1187 Item.GetMetadata(target_zip) 1188 Item.Get("system").SetPermissions(temp_script) 1189 1190 # Note that this call will mess up the tree of Items, so make sure 1191 # we're done with it. 1192 source_symlinks = CopySystemFiles(source_zip, None) 1193 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks]) 1194 1195 # Delete all the symlinks in source that aren't in target. This 1196 # needs to happen before verbatim files are unpacked, in case a 1197 # symlink in the source is replaced by a real file in the target. 1198 to_delete = [] 1199 for dest, link in source_symlinks: 1200 if link not in target_symlinks_d: 1201 to_delete.append(link) 1202 script.DeleteFiles(to_delete) 1203 1204 if verbatim_targets: 1205 script.Print("Unpacking new files...") 1206 script.UnpackPackageDir("system", "/system") 1207 1208 if updating_recovery and not target_has_recovery_patch: 1209 script.Print("Unpacking new recovery...") 1210 script.UnpackPackageDir("recovery", "/system") 1211 1212 if len(renames) > 0: 1213 script.Print("Renaming files...") 1214 1215 for src in renames: 1216 print "Renaming " + src + " to " + renames[src].name 1217 script.RenameFile(src, renames[src].name) 1218 1219 script.Print("Symlinks and permissions...") 1220 1221 # Create all the symlinks that don't already exist, or point to 1222 # somewhere different than what we want. Delete each symlink before 1223 # creating it, since the 'symlink' command won't overwrite. 1224 to_create = [] 1225 for dest, link in target_symlinks: 1226 if link in source_symlinks_d: 1227 if dest != source_symlinks_d[link]: 1228 to_create.append((dest, link)) 1229 else: 1230 to_create.append((dest, link)) 1231 script.DeleteFiles([i[1] for i in to_create]) 1232 script.MakeSymlinks(to_create) 1233 1234 # Now that the symlinks are created, we can set all the 1235 # permissions. 1236 script.AppendScript(temp_script) 1237 1238 # Do device-specific installation (eg, write radio image). 1239 device_specific.IncrementalOTA_InstallEnd() 1240 1241 if OPTIONS.extra_script is not None: 1242 script.AppendExtra(OPTIONS.extra_script) 1243 1244 # Patch the build.prop file last, so if something fails but the 1245 # device can still come up, it appears to be the old build and will 1246 # get set the OTA package again to retry. 1247 script.Print("Patching remaining system files...") 1248 for item in deferred_patch_list: 1249 tf, sf, size, _ = item 1250 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p") 1251 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None) 1252 1253 if OPTIONS.wipe_user_data: 1254 script.Print("Erasing user data...") 1255 script.FormatPartition("/data") 1256 1257 if OPTIONS.two_step: 1258 script.AppendExtra(""" 1259set_stage("%(bcb_dev)s", ""); 1260endif; 1261endif; 1262""" % bcb_dev) 1263 1264 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary) 1265 WriteMetadata(metadata, output_zip) 1266 1267 1268def main(argv): 1269 1270 def option_handler(o, a): 1271 if o == "--board_config": 1272 pass # deprecated 1273 elif o in ("-k", "--package_key"): 1274 OPTIONS.package_key = a 1275 elif o in ("-i", "--incremental_from"): 1276 OPTIONS.incremental_source = a 1277 elif o in ("-w", "--wipe_user_data"): 1278 OPTIONS.wipe_user_data = True 1279 elif o in ("-n", "--no_prereq"): 1280 OPTIONS.omit_prereq = True 1281 elif o in ("-o", "--oem_settings"): 1282 OPTIONS.oem_source = a 1283 elif o in ("-e", "--extra_script"): 1284 OPTIONS.extra_script = a 1285 elif o in ("-a", "--aslr_mode"): 1286 if a in ("on", "On", "true", "True", "yes", "Yes"): 1287 OPTIONS.aslr_mode = True 1288 else: 1289 OPTIONS.aslr_mode = False 1290 elif o in ("--worker_threads"): 1291 OPTIONS.worker_threads = int(a) 1292 elif o in ("-2", "--two_step"): 1293 OPTIONS.two_step = True 1294 elif o == "--no_signing": 1295 OPTIONS.no_signing = True 1296 elif o == "--block": 1297 OPTIONS.block_based = True 1298 elif o in ("-b", "--binary"): 1299 OPTIONS.updater_binary = a 1300 else: 1301 return False 1302 return True 1303 1304 args = common.ParseOptions(argv, __doc__, 1305 extra_opts="b:k:i:d:wne:a:2o:", 1306 extra_long_opts=["board_config=", 1307 "package_key=", 1308 "incremental_from=", 1309 "wipe_user_data", 1310 "no_prereq", 1311 "extra_script=", 1312 "worker_threads=", 1313 "aslr_mode=", 1314 "two_step", 1315 "no_signing", 1316 "block", 1317 "binary=", 1318 "oem_settings=", 1319 ], 1320 extra_option_handler=option_handler) 1321 1322 if len(args) != 2: 1323 common.Usage(__doc__) 1324 sys.exit(1) 1325 1326 if OPTIONS.extra_script is not None: 1327 OPTIONS.extra_script = open(OPTIONS.extra_script).read() 1328 1329 print "unzipping target target-files..." 1330 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0]) 1331 1332 OPTIONS.target_tmp = OPTIONS.input_tmp 1333 OPTIONS.info_dict = common.LoadInfoDict(input_zip) 1334 1335 # If this image was originally labelled with SELinux contexts, make sure we 1336 # also apply the labels in our new image. During building, the "file_contexts" 1337 # is in the out/ directory tree, but for repacking from target-files.zip it's 1338 # in the root directory of the ramdisk. 1339 if "selinux_fc" in OPTIONS.info_dict: 1340 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK", 1341 "file_contexts") 1342 1343 if OPTIONS.verbose: 1344 print "--- target info ---" 1345 common.DumpInfoDict(OPTIONS.info_dict) 1346 1347 # If the caller explicitly specified the device-specific extensions 1348 # path via -s/--device_specific, use that. Otherwise, use 1349 # META/releasetools.py if it is present in the target target_files. 1350 # Otherwise, take the path of the file from 'tool_extensions' in the 1351 # info dict and look for that in the local filesystem, relative to 1352 # the current directory. 1353 1354 if OPTIONS.device_specific is None: 1355 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py") 1356 if os.path.exists(from_input): 1357 print "(using device-specific extensions from target_files)" 1358 OPTIONS.device_specific = from_input 1359 else: 1360 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None) 1361 1362 if OPTIONS.device_specific is not None: 1363 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific) 1364 1365 if OPTIONS.no_signing: 1366 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED) 1367 else: 1368 temp_zip_file = tempfile.NamedTemporaryFile() 1369 output_zip = zipfile.ZipFile(temp_zip_file, "w", 1370 compression=zipfile.ZIP_DEFLATED) 1371 1372 if OPTIONS.incremental_source is None: 1373 WriteFullOTAPackage(input_zip, output_zip) 1374 if OPTIONS.package_key is None: 1375 OPTIONS.package_key = OPTIONS.info_dict.get( 1376 "default_system_dev_certificate", 1377 "build/target/product/security/testkey") 1378 else: 1379 print "unzipping source target-files..." 1380 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source) 1381 OPTIONS.target_info_dict = OPTIONS.info_dict 1382 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip) 1383 if "selinux_fc" in OPTIONS.source_info_dict: 1384 OPTIONS.source_info_dict["selinux_fc"] = os.path.join(OPTIONS.source_tmp, "BOOT", "RAMDISK", 1385 "file_contexts") 1386 if OPTIONS.package_key is None: 1387 OPTIONS.package_key = OPTIONS.source_info_dict.get( 1388 "default_system_dev_certificate", 1389 "build/target/product/security/testkey") 1390 if OPTIONS.verbose: 1391 print "--- source info ---" 1392 common.DumpInfoDict(OPTIONS.source_info_dict) 1393 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip) 1394 1395 output_zip.close() 1396 1397 if not OPTIONS.no_signing: 1398 SignOutput(temp_zip_file.name, args[1]) 1399 temp_zip_file.close() 1400 1401 common.Cleanup() 1402 1403 print "done." 1404 1405 1406if __name__ == '__main__': 1407 try: 1408 common.CloseInheritedPipes() 1409 main(sys.argv[1:]) 1410 except common.ExternalError, e: 1411 print 1412 print " ERROR: %s" % (e,) 1413 print 1414 sys.exit(1) 1415