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