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