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