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   if block_based:
    562     # Full OTA is done as an "incremental" against an empty source
    563     # image.  This has the effect of writing new data from the package
    564     # to the entire partition, but lets us reuse the updater code that
    565     # writes incrementals to do it.
    566     system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
    567     system_tgt.ResetFileMap()
    568     system_diff = common.BlockDifference("system", system_tgt, src=None)
    569     system_diff.WriteScript(script, output_zip)
    570   else:
    571     script.FormatPartition("/system")
    572     script.Mount("/system", recovery_mount_options)
    573     if not has_recovery_patch:
    574       script.UnpackPackageDir("recovery", "/system")
    575     script.UnpackPackageDir("system", "/system")
    576 
    577     symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
    578     script.MakeSymlinks(symlinks)
    579 
    580   boot_img = common.GetBootableImage("boot.img", "boot.img",
    581                                      OPTIONS.input_tmp, "BOOT")
    582 
    583   if not block_based:
    584     def output_sink(fn, data):
    585       common.ZipWriteStr(output_zip, "recovery/" + fn, data)
    586       system_items.Get("system/" + fn, dir=False)
    587 
    588     common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
    589                              recovery_img, boot_img)
    590 
    591     system_items.GetMetadata(input_zip)
    592     system_items.Get("system").SetPermissions(script)
    593 
    594   if HasVendorPartition(input_zip):
    595     vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
    596     script.ShowProgress(0.1, 0)
    597 
    598     if block_based:
    599       vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
    600       vendor_tgt.ResetFileMap()
    601       vendor_diff = common.BlockDifference("vendor", vendor_tgt)
    602       vendor_diff.WriteScript(script, output_zip)
    603     else:
    604       script.FormatPartition("/vendor")
    605       script.Mount("/vendor", recovery_mount_options)
    606       script.UnpackPackageDir("vendor", "/vendor")
    607 
    608       symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
    609       script.MakeSymlinks(symlinks)
    610 
    611       vendor_items.GetMetadata(input_zip)
    612       vendor_items.Get("vendor").SetPermissions(script)
    613 
    614   common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
    615   common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
    616 
    617   script.ShowProgress(0.05, 5)
    618   script.WriteRawImage("/boot", "boot.img")
    619 
    620   script.ShowProgress(0.2, 10)
    621   device_specific.FullOTA_InstallEnd()
    622 
    623   if OPTIONS.extra_script is not None:
    624     script.AppendExtra(OPTIONS.extra_script)
    625 
    626   script.UnmountAll()
    627 
    628   if OPTIONS.wipe_user_data:
    629     script.ShowProgress(0.1, 10)
    630     script.FormatPartition("/data")
    631 
    632   if OPTIONS.two_step:
    633     script.AppendExtra("""
    634 set_stage("%(bcb_dev)s", "");
    635 """ % bcb_dev)
    636     script.AppendExtra("else\n")
    637     script.WriteRawImage("/boot", "recovery.img")
    638     script.AppendExtra("""
    639 set_stage("%(bcb_dev)s", "2/3");
    640 reboot_now("%(bcb_dev)s", "");
    641 endif;
    642 endif;
    643 """ % bcb_dev)
    644   script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
    645   WriteMetadata(metadata, output_zip)
    646 
    647 
    648 def WritePolicyConfig(file_context, output_zip):
    649   f = open(file_context, 'r');
    650   basename = os.path.basename(file_context)
    651   common.ZipWriteStr(output_zip, basename, f.read())
    652 
    653 
    654 def WriteMetadata(metadata, output_zip):
    655   common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
    656                      "".join(["%s=%s\n" % kv
    657                               for kv in sorted(metadata.iteritems())]))
    658 
    659 
    660 def LoadPartitionFiles(z, partition):
    661   """Load all the files from the given partition in a given target-files
    662   ZipFile, and return a dict of {filename: File object}."""
    663   out = {}
    664   prefix = partition.upper() + "/"
    665   for info in z.infolist():
    666     if info.filename.startswith(prefix) and not IsSymlink(info):
    667       basefilename = info.filename[7:]
    668       fn = partition + "/" + basefilename
    669       data = z.read(info.filename)
    670       out[fn] = common.File(fn, data)
    671   return out
    672 
    673 
    674 def GetBuildProp(prop, info_dict):
    675   """Return the fingerprint of the build of a given target-files info_dict."""
    676   try:
    677     return info_dict.get("build.prop", {})[prop]
    678   except KeyError:
    679     raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
    680 
    681 
    682 def AddToKnownPaths(filename, known_paths):
    683   if filename[-1] == "/":
    684     return
    685   dirs = filename.split("/")[:-1]
    686   while len(dirs) > 0:
    687     path = "/".join(dirs)
    688     if path in known_paths:
    689       break;
    690     known_paths.add(path)
    691     dirs.pop()
    692 
    693 
    694 def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
    695   source_version = OPTIONS.source_info_dict["recovery_api_version"]
    696   target_version = OPTIONS.target_info_dict["recovery_api_version"]
    697 
    698   if source_version == 0:
    699     print ("WARNING: generating edify script for a source that "
    700            "can't install it.")
    701   script = edify_generator.EdifyGenerator(source_version,
    702                                           OPTIONS.target_info_dict)
    703 
    704   metadata = {"pre-device": GetBuildProp("ro.product.device",
    705                                          OPTIONS.source_info_dict),
    706               "post-timestamp": GetBuildProp("ro.build.date.utc",
    707                                              OPTIONS.target_info_dict),
    708               }
    709 
    710   device_specific = common.DeviceSpecificParams(
    711       source_zip=source_zip,
    712       source_version=source_version,
    713       target_zip=target_zip,
    714       target_version=target_version,
    715       output_zip=output_zip,
    716       script=script,
    717       metadata=metadata,
    718       info_dict=OPTIONS.info_dict)
    719 
    720   source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
    721   target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
    722   metadata["pre-build"] = source_fp
    723   metadata["post-build"] = target_fp
    724 
    725   source_boot = common.GetBootableImage(
    726       "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
    727       OPTIONS.source_info_dict)
    728   target_boot = common.GetBootableImage(
    729       "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
    730   updating_boot = (not OPTIONS.two_step and
    731                    (source_boot.data != target_boot.data))
    732 
    733   source_recovery = common.GetBootableImage(
    734       "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
    735       OPTIONS.source_info_dict)
    736   target_recovery = common.GetBootableImage(
    737       "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
    738   updating_recovery = (source_recovery.data != target_recovery.data)
    739 
    740   system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
    741   system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
    742   system_diff = common.BlockDifference("system", system_tgt, system_src,
    743                                        check_first_block=True)
    744 
    745   if HasVendorPartition(target_zip):
    746     if not HasVendorPartition(source_zip):
    747       raise RuntimeError("can't generate incremental that adds /vendor")
    748     vendor_src = GetImage("vendor", OPTIONS.source_tmp, OPTIONS.source_info_dict)
    749     vendor_tgt = GetImage("vendor", OPTIONS.target_tmp, OPTIONS.target_info_dict)
    750     vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
    751                                          check_first_block=True)
    752   else:
    753     vendor_diff = None
    754 
    755   oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
    756   recovery_mount_options = OPTIONS.target_info_dict.get("recovery_mount_options")
    757   oem_dict = None
    758   if oem_props is not None and len(oem_props) > 0:
    759     if OPTIONS.oem_source is None:
    760       raise common.ExternalError("OEM source required for this build")
    761     script.Mount("/oem", recovery_mount_options)
    762     oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
    763 
    764   AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
    765   device_specific.IncrementalOTA_Assertions()
    766 
    767   # Two-step incremental package strategy (in chronological order,
    768   # which is *not* the order in which the generated script has
    769   # things):
    770   #
    771   # if stage is not "2/3" or "3/3":
    772   #    do verification on current system
    773   #    write recovery image to boot partition
    774   #    set stage to "2/3"
    775   #    reboot to boot partition and restart recovery
    776   # else if stage is "2/3":
    777   #    write recovery image to recovery partition
    778   #    set stage to "3/3"
    779   #    reboot to recovery partition and restart recovery
    780   # else:
    781   #    (stage must be "3/3")
    782   #    perform update:
    783   #       patch system files, etc.
    784   #       force full install of new boot image
    785   #       set up system to update recovery partition on first boot
    786   #    complete script normally (allow recovery to mark itself finished and reboot)
    787 
    788   if OPTIONS.two_step:
    789     if not OPTIONS.info_dict.get("multistage_support", None):
    790       assert False, "two-step packages not supported by this build"
    791     fs = OPTIONS.info_dict["fstab"]["/misc"]
    792     assert fs.fs_type.upper() == "EMMC", \
    793         "two-step packages only supported on devices with EMMC /misc partitions"
    794     bcb_dev = {"bcb_dev": fs.device}
    795     common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
    796     script.AppendExtra("""
    797 if get_stage("%(bcb_dev)s") == "2/3" then
    798 """ % bcb_dev)
    799     script.AppendExtra("sleep(20);\n");
    800     script.WriteRawImage("/recovery", "recovery.img")
    801     script.AppendExtra("""
    802 set_stage("%(bcb_dev)s", "3/3");
    803 reboot_now("%(bcb_dev)s", "recovery");
    804 else if get_stage("%(bcb_dev)s") != "3/3" then
    805 """ % bcb_dev)
    806 
    807   script.Print("Verifying current system...")
    808 
    809   device_specific.IncrementalOTA_VerifyBegin()
    810 
    811   if oem_props is None:
    812     script.AssertSomeFingerprint(source_fp, target_fp)
    813   else:
    814     script.AssertSomeThumbprint(
    815         GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
    816         GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
    817 
    818   if updating_boot:
    819     boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
    820     d = common.Difference(target_boot, source_boot)
    821     _, _, d = d.ComputePatch()
    822     if d is None:
    823       include_full_boot = True
    824       common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
    825     else:
    826       include_full_boot = False
    827 
    828       print "boot      target: %d  source: %d  diff: %d" % (
    829           target_boot.size, source_boot.size, len(d))
    830 
    831       common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
    832 
    833       script.PatchCheck("%s:%s:%d:%s:%d:%s" %
    834                         (boot_type, boot_device,
    835                          source_boot.size, source_boot.sha1,
    836                          target_boot.size, target_boot.sha1))
    837 
    838   device_specific.IncrementalOTA_VerifyEnd()
    839 
    840   if OPTIONS.two_step:
    841     script.WriteRawImage("/boot", "recovery.img")
    842     script.AppendExtra("""
    843 set_stage("%(bcb_dev)s", "2/3");
    844 reboot_now("%(bcb_dev)s", "");
    845 else
    846 """ % bcb_dev)
    847 
    848   script.Comment("---- start making changes here ----")
    849 
    850   device_specific.IncrementalOTA_InstallBegin()
    851 
    852   system_diff.WriteScript(script, output_zip,
    853                           progress=0.8 if vendor_diff else 0.9)
    854   if vendor_diff:
    855     vendor_diff.WriteScript(script, output_zip, progress=0.1)
    856 
    857   if OPTIONS.two_step:
    858     common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
    859     script.WriteRawImage("/boot", "boot.img")
    860     print "writing full boot image (forced by two-step mode)"
    861 
    862   if not OPTIONS.two_step:
    863     if updating_boot:
    864       if include_full_boot:
    865         print "boot image changed; including full."
    866         script.Print("Installing boot image...")
    867         script.WriteRawImage("/boot", "boot.img")
    868       else:
    869         # Produce the boot image by applying a patch to the current
    870         # contents of the boot partition, and write it back to the
    871         # partition.
    872         print "boot image changed; including patch."
    873         script.Print("Patching boot image...")
    874         script.ShowProgress(0.1, 10)
    875         script.ApplyPatch("%s:%s:%d:%s:%d:%s"
    876                           % (boot_type, boot_device,
    877                              source_boot.size, source_boot.sha1,
    878                              target_boot.size, target_boot.sha1),
    879                           "-",
    880                           target_boot.size, target_boot.sha1,
    881                           source_boot.sha1, "patch/boot.img.p")
    882     else:
    883       print "boot image unchanged; skipping."
    884 
    885   # Do device-specific installation (eg, write radio image).
    886   device_specific.IncrementalOTA_InstallEnd()
    887 
    888   if OPTIONS.extra_script is not None:
    889     script.AppendExtra(OPTIONS.extra_script)
    890 
    891   if OPTIONS.wipe_user_data:
    892     script.Print("Erasing user data...")
    893     script.FormatPartition("/data")
    894 
    895   if OPTIONS.two_step:
    896     script.AppendExtra("""
    897 set_stage("%(bcb_dev)s", "");
    898 endif;
    899 endif;
    900 """ % bcb_dev)
    901 
    902   script.SetProgress(1)
    903   script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
    904   WriteMetadata(metadata, output_zip)
    905 
    906 
    907 class FileDifference:
    908   def __init__(self, partition, source_zip, target_zip, output_zip):
    909     print "Loading target..."
    910     self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
    911     print "Loading source..."
    912     self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
    913 
    914     self.verbatim_targets = verbatim_targets = []
    915     self.patch_list = patch_list = []
    916     diffs = []
    917     self.renames = renames = {}
    918     known_paths = set()
    919     largest_source_size = 0
    920 
    921     matching_file_cache = {}
    922     for fn, sf in source_data.items():
    923       assert fn == sf.name
    924       matching_file_cache["path:" + fn] = sf
    925       if fn in target_data.keys():
    926         AddToKnownPaths(fn, known_paths)
    927       # Only allow eligibility for filename/sha matching
    928       # if there isn't a perfect path match.
    929       if target_data.get(sf.name) is None:
    930         matching_file_cache["file:" + fn.split("/")[-1]] = sf
    931         matching_file_cache["sha:" + sf.sha1] = sf
    932 
    933     for fn in sorted(target_data.keys()):
    934       tf = target_data[fn]
    935       assert fn == tf.name
    936       sf = ClosestFileMatch(tf, matching_file_cache, renames)
    937       if sf is not None and sf.name != tf.name:
    938         print "File has moved from " + sf.name + " to " + tf.name
    939         renames[sf.name] = tf
    940 
    941       if sf is None or fn in OPTIONS.require_verbatim:
    942         # This file should be included verbatim
    943         if fn in OPTIONS.prohibit_verbatim:
    944           raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
    945         print "send", fn, "verbatim"
    946         tf.AddToZip(output_zip)
    947         verbatim_targets.append((fn, tf.size, tf.sha1))
    948         if fn in target_data.keys():
    949           AddToKnownPaths(fn, known_paths)
    950       elif tf.sha1 != sf.sha1:
    951         # File is different; consider sending as a patch
    952         diffs.append(common.Difference(tf, sf))
    953       else:
    954         # Target file data identical to source (may still be renamed)
    955         pass
    956 
    957     common.ComputeDifferences(diffs)
    958 
    959     for diff in diffs:
    960       tf, sf, d = diff.GetPatch()
    961       path = "/".join(tf.name.split("/")[:-1])
    962       if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
    963           path not in known_paths:
    964         # patch is almost as big as the file; don't bother patching
    965         # or a patch + rename cannot take place due to the target
    966         # directory not existing
    967         tf.AddToZip(output_zip)
    968         verbatim_targets.append((tf.name, tf.size, tf.sha1))
    969         if sf.name in renames:
    970           del renames[sf.name]
    971         AddToKnownPaths(tf.name, known_paths)
    972       else:
    973         common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
    974         patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
    975         largest_source_size = max(largest_source_size, sf.size)
    976 
    977     self.largest_source_size = largest_source_size
    978 
    979   def EmitVerification(self, script):
    980     so_far = 0
    981     for tf, sf, size, patch_sha in self.patch_list:
    982       if tf.name != sf.name:
    983         script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
    984       script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
    985       so_far += sf.size
    986     return so_far
    987 
    988   def EmitExplicitTargetVerification(self, script):
    989     for fn, size, sha1 in self.verbatim_targets:
    990       if (fn[-1] != "/"):
    991         script.FileCheck("/"+fn, sha1)
    992     for tf, _, _, _ in self.patch_list:
    993       script.FileCheck(tf.name, tf.sha1)
    994 
    995   def RemoveUnneededFiles(self, script, extras=()):
    996     script.DeleteFiles(["/"+i[0] for i in self.verbatim_targets] +
    997                        ["/"+i for i in sorted(self.source_data)
    998                               if i not in self.target_data and
    999                               i not in self.renames] +
   1000                        list(extras))
   1001 
   1002   def TotalPatchSize(self):
   1003     return sum(i[1].size for i in self.patch_list)
   1004 
   1005   def EmitPatches(self, script, total_patch_size, so_far):
   1006     self.deferred_patch_list = deferred_patch_list = []
   1007     for item in self.patch_list:
   1008       tf, sf, size, _ = item
   1009       if tf.name == "system/build.prop":
   1010         deferred_patch_list.append(item)
   1011         continue
   1012       if (sf.name != tf.name):
   1013         script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
   1014       script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
   1015       so_far += tf.size
   1016       script.SetProgress(so_far / total_patch_size)
   1017     return so_far
   1018 
   1019   def EmitDeferredPatches(self, script):
   1020     for item in self.deferred_patch_list:
   1021       tf, sf, size, _ = item
   1022       script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
   1023     script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
   1024 
   1025   def EmitRenames(self, script):
   1026     if len(self.renames) > 0:
   1027       script.Print("Renaming files...")
   1028       for src, tgt in self.renames.iteritems():
   1029         print "Renaming " + src + " to " + tgt.name
   1030         script.RenameFile(src, tgt.name)
   1031 
   1032 
   1033 
   1034 
   1035 def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
   1036   target_has_recovery_patch = HasRecoveryPatch(target_zip)
   1037   source_has_recovery_patch = HasRecoveryPatch(source_zip)
   1038 
   1039   if (OPTIONS.block_based and
   1040       target_has_recovery_patch and
   1041       source_has_recovery_patch):
   1042     return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
   1043 
   1044   source_version = OPTIONS.source_info_dict["recovery_api_version"]
   1045   target_version = OPTIONS.target_info_dict["recovery_api_version"]
   1046 
   1047   if source_version == 0:
   1048     print ("WARNING: generating edify script for a source that "
   1049            "can't install it.")
   1050   script = edify_generator.EdifyGenerator(source_version,
   1051                                           OPTIONS.target_info_dict)
   1052 
   1053   oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
   1054   recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
   1055   oem_dict = None
   1056   if oem_props is not None and len(oem_props) > 0:
   1057     if OPTIONS.oem_source is None:
   1058       raise common.ExternalError("OEM source required for this build")
   1059     script.Mount("/oem", recovery_mount_options)
   1060     oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
   1061 
   1062   metadata = {"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
   1063                                          OPTIONS.source_info_dict),
   1064               "post-timestamp": GetBuildProp("ro.build.date.utc",
   1065                                              OPTIONS.target_info_dict),
   1066               }
   1067 
   1068   device_specific = common.DeviceSpecificParams(
   1069       source_zip=source_zip,
   1070       source_version=source_version,
   1071       target_zip=target_zip,
   1072       target_version=target_version,
   1073       output_zip=output_zip,
   1074       script=script,
   1075       metadata=metadata,
   1076       info_dict=OPTIONS.info_dict)
   1077 
   1078   system_diff = FileDifference("system", source_zip, target_zip, output_zip)
   1079   script.Mount("/system", recovery_mount_options)
   1080   if HasVendorPartition(target_zip):
   1081     vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
   1082     script.Mount("/vendor", recovery_mount_options)
   1083   else:
   1084     vendor_diff = None
   1085 
   1086   target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.target_info_dict)
   1087   source_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.source_info_dict)
   1088 
   1089   if oem_props is None:
   1090     script.AssertSomeFingerprint(source_fp, target_fp)
   1091   else:
   1092     script.AssertSomeThumbprint(
   1093         GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
   1094         GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
   1095 
   1096   metadata["pre-build"] = source_fp
   1097   metadata["post-build"] = target_fp
   1098 
   1099   source_boot = common.GetBootableImage(
   1100       "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
   1101       OPTIONS.source_info_dict)
   1102   target_boot = common.GetBootableImage(
   1103       "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
   1104   updating_boot = (not OPTIONS.two_step and
   1105                    (source_boot.data != target_boot.data))
   1106 
   1107   source_recovery = common.GetBootableImage(
   1108       "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
   1109       OPTIONS.source_info_dict)
   1110   target_recovery = common.GetBootableImage(
   1111       "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
   1112   updating_recovery = (source_recovery.data != target_recovery.data)
   1113 
   1114   # Here's how we divide up the progress bar:
   1115   #  0.1 for verifying the start state (PatchCheck calls)
   1116   #  0.8 for applying patches (ApplyPatch calls)
   1117   #  0.1 for unpacking verbatim files, symlinking, and doing the
   1118   #      device-specific commands.
   1119 
   1120   AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
   1121   device_specific.IncrementalOTA_Assertions()
   1122 
   1123   # Two-step incremental package strategy (in chronological order,
   1124   # which is *not* the order in which the generated script has
   1125   # things):
   1126   #
   1127   # if stage is not "2/3" or "3/3":
   1128   #    do verification on current system
   1129   #    write recovery image to boot partition
   1130   #    set stage to "2/3"
   1131   #    reboot to boot partition and restart recovery
   1132   # else if stage is "2/3":
   1133   #    write recovery image to recovery partition
   1134   #    set stage to "3/3"
   1135   #    reboot to recovery partition and restart recovery
   1136   # else:
   1137   #    (stage must be "3/3")
   1138   #    perform update:
   1139   #       patch system files, etc.
   1140   #       force full install of new boot image
   1141   #       set up system to update recovery partition on first boot
   1142   #    complete script normally (allow recovery to mark itself finished and reboot)
   1143 
   1144   if OPTIONS.two_step:
   1145     if not OPTIONS.info_dict.get("multistage_support", None):
   1146       assert False, "two-step packages not supported by this build"
   1147     fs = OPTIONS.info_dict["fstab"]["/misc"]
   1148     assert fs.fs_type.upper() == "EMMC", \
   1149         "two-step packages only supported on devices with EMMC /misc partitions"
   1150     bcb_dev = {"bcb_dev": fs.device}
   1151     common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
   1152     script.AppendExtra("""
   1153 if get_stage("%(bcb_dev)s") == "2/3" then
   1154 """ % bcb_dev)
   1155     script.AppendExtra("sleep(20);\n");
   1156     script.WriteRawImage("/recovery", "recovery.img")
   1157     script.AppendExtra("""
   1158 set_stage("%(bcb_dev)s", "3/3");
   1159 reboot_now("%(bcb_dev)s", "recovery");
   1160 else if get_stage("%(bcb_dev)s") != "3/3" then
   1161 """ % bcb_dev)
   1162 
   1163   script.Print("Verifying current system...")
   1164 
   1165   device_specific.IncrementalOTA_VerifyBegin()
   1166 
   1167   script.ShowProgress(0.1, 0)
   1168   so_far = system_diff.EmitVerification(script)
   1169   if vendor_diff:
   1170     so_far += vendor_diff.EmitVerification(script)
   1171 
   1172   if updating_boot:
   1173     d = common.Difference(target_boot, source_boot)
   1174     _, _, d = d.ComputePatch()
   1175     print "boot      target: %d  source: %d  diff: %d" % (
   1176         target_boot.size, source_boot.size, len(d))
   1177 
   1178     common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
   1179 
   1180     boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
   1181 
   1182     script.PatchCheck("%s:%s:%d:%s:%d:%s" %
   1183                       (boot_type, boot_device,
   1184                        source_boot.size, source_boot.sha1,
   1185                        target_boot.size, target_boot.sha1))
   1186     so_far += source_boot.size
   1187 
   1188   size = []
   1189   if system_diff.patch_list: size.append(system_diff.largest_source_size)
   1190   if vendor_diff:
   1191     if vendor_diff.patch_list: size.append(vendor_diff.largest_source_size)
   1192   if size or updating_recovery or updating_boot:
   1193     script.CacheFreeSpaceCheck(max(size))
   1194 
   1195   device_specific.IncrementalOTA_VerifyEnd()
   1196 
   1197   if OPTIONS.two_step:
   1198     script.WriteRawImage("/boot", "recovery.img")
   1199     script.AppendExtra("""
   1200 set_stage("%(bcb_dev)s", "2/3");
   1201 reboot_now("%(bcb_dev)s", "");
   1202 else
   1203 """ % bcb_dev)
   1204 
   1205   script.Comment("---- start making changes here ----")
   1206 
   1207   device_specific.IncrementalOTA_InstallBegin()
   1208 
   1209   if OPTIONS.two_step:
   1210     common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
   1211     script.WriteRawImage("/boot", "boot.img")
   1212     print "writing full boot image (forced by two-step mode)"
   1213 
   1214   script.Print("Removing unneeded files...")
   1215   system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
   1216   if vendor_diff:
   1217     vendor_diff.RemoveUnneededFiles(script)
   1218 
   1219   script.ShowProgress(0.8, 0)
   1220   total_patch_size = 1.0 + system_diff.TotalPatchSize()
   1221   if vendor_diff:
   1222     total_patch_size += vendor_diff.TotalPatchSize()
   1223   if updating_boot:
   1224     total_patch_size += target_boot.size
   1225 
   1226   script.Print("Patching system files...")
   1227   so_far = system_diff.EmitPatches(script, total_patch_size, 0)
   1228   if vendor_diff:
   1229     script.Print("Patching vendor files...")
   1230     so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
   1231 
   1232   if not OPTIONS.two_step:
   1233     if updating_boot:
   1234       # Produce the boot image by applying a patch to the current
   1235       # contents of the boot partition, and write it back to the
   1236       # partition.
   1237       script.Print("Patching boot image...")
   1238       script.ApplyPatch("%s:%s:%d:%s:%d:%s"
   1239                         % (boot_type, boot_device,
   1240                            source_boot.size, source_boot.sha1,
   1241                            target_boot.size, target_boot.sha1),
   1242                         "-",
   1243                         target_boot.size, target_boot.sha1,
   1244                         source_boot.sha1, "patch/boot.img.p")
   1245       so_far += target_boot.size
   1246       script.SetProgress(so_far / total_patch_size)
   1247       print "boot image changed; including."
   1248     else:
   1249       print "boot image unchanged; skipping."
   1250 
   1251   system_items = ItemSet("system", "META/filesystem_config.txt")
   1252   if vendor_diff:
   1253     vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
   1254 
   1255   if updating_recovery:
   1256     # Recovery is generated as a patch using both the boot image
   1257     # (which contains the same linux kernel as recovery) and the file
   1258     # /system/etc/recovery-resource.dat (which contains all the images
   1259     # used in the recovery UI) as sources.  This lets us minimize the
   1260     # size of the patch, which must be included in every OTA package.
   1261     #
   1262     # For older builds where recovery-resource.dat is not present, we
   1263     # use only the boot image as the source.
   1264 
   1265     if not target_has_recovery_patch:
   1266       def output_sink(fn, data):
   1267         common.ZipWriteStr(output_zip, "recovery/" + fn, data)
   1268         system_items.Get("system/" + fn, dir=False)
   1269 
   1270       common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
   1271                                target_recovery, target_boot)
   1272       script.DeleteFiles(["/system/recovery-from-boot.p",
   1273                           "/system/etc/install-recovery.sh"])
   1274     print "recovery image changed; including as patch from boot."
   1275   else:
   1276     print "recovery image unchanged; skipping."
   1277 
   1278   script.ShowProgress(0.1, 10)
   1279 
   1280   target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
   1281   if vendor_diff:
   1282     target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
   1283 
   1284   temp_script = script.MakeTemporary()
   1285   system_items.GetMetadata(target_zip)
   1286   system_items.Get("system").SetPermissions(temp_script)
   1287   if vendor_diff:
   1288     vendor_items.GetMetadata(target_zip)
   1289     vendor_items.Get("vendor").SetPermissions(temp_script)
   1290 
   1291   # Note that this call will mess up the trees of Items, so make sure
   1292   # we're done with them.
   1293   source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
   1294   if vendor_diff:
   1295     source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
   1296 
   1297   target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
   1298   source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
   1299 
   1300   # Delete all the symlinks in source that aren't in target.  This
   1301   # needs to happen before verbatim files are unpacked, in case a
   1302   # symlink in the source is replaced by a real file in the target.
   1303   to_delete = []
   1304   for dest, link in source_symlinks:
   1305     if link not in target_symlinks_d:
   1306       to_delete.append(link)
   1307   script.DeleteFiles(to_delete)
   1308 
   1309   if system_diff.verbatim_targets:
   1310     script.Print("Unpacking new system files...")
   1311     script.UnpackPackageDir("system", "/system")
   1312   if vendor_diff and vendor_diff.verbatim_targets:
   1313     script.Print("Unpacking new vendor files...")
   1314     script.UnpackPackageDir("vendor", "/vendor")
   1315 
   1316   if updating_recovery and not target_has_recovery_patch:
   1317     script.Print("Unpacking new recovery...")
   1318     script.UnpackPackageDir("recovery", "/system")
   1319 
   1320   system_diff.EmitRenames(script)
   1321   if vendor_diff:
   1322     vendor_diff.EmitRenames(script)
   1323 
   1324   script.Print("Symlinks and permissions...")
   1325 
   1326   # Create all the symlinks that don't already exist, or point to
   1327   # somewhere different than what we want.  Delete each symlink before
   1328   # creating it, since the 'symlink' command won't overwrite.
   1329   to_create = []
   1330   for dest, link in target_symlinks:
   1331     if link in source_symlinks_d:
   1332       if dest != source_symlinks_d[link]:
   1333         to_create.append((dest, link))
   1334     else:
   1335       to_create.append((dest, link))
   1336   script.DeleteFiles([i[1] for i in to_create])
   1337   script.MakeSymlinks(to_create)
   1338 
   1339   # Now that the symlinks are created, we can set all the
   1340   # permissions.
   1341   script.AppendScript(temp_script)
   1342 
   1343   # Do device-specific installation (eg, write radio image).
   1344   device_specific.IncrementalOTA_InstallEnd()
   1345 
   1346   if OPTIONS.extra_script is not None:
   1347     script.AppendExtra(OPTIONS.extra_script)
   1348 
   1349   # Patch the build.prop file last, so if something fails but the
   1350   # device can still come up, it appears to be the old build and will
   1351   # get set the OTA package again to retry.
   1352   script.Print("Patching remaining system files...")
   1353   system_diff.EmitDeferredPatches(script)
   1354 
   1355   if OPTIONS.wipe_user_data:
   1356     script.Print("Erasing user data...")
   1357     script.FormatPartition("/data")
   1358 
   1359   if OPTIONS.two_step:
   1360     script.AppendExtra("""
   1361 set_stage("%(bcb_dev)s", "");
   1362 endif;
   1363 endif;
   1364 """ % bcb_dev)
   1365 
   1366   if OPTIONS.verify and system_diff:
   1367     script.Print("Remounting and verifying system partition files...")
   1368     script.Unmount("/system")
   1369     script.Mount("/system")
   1370     system_diff.EmitExplicitTargetVerification(script)
   1371 
   1372   if OPTIONS.verify and vendor_diff:
   1373     script.Print("Remounting and verifying vendor partition files...")
   1374     script.Unmount("/vendor")
   1375     script.Mount("/vendor")
   1376     vendor_diff.EmitExplicitTargetVerification(script)
   1377   script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
   1378 
   1379   WriteMetadata(metadata, output_zip)
   1380 
   1381 
   1382 def main(argv):
   1383 
   1384   def option_handler(o, a):
   1385     if o == "--board_config":
   1386       pass   # deprecated
   1387     elif o in ("-k", "--package_key"):
   1388       OPTIONS.package_key = a
   1389     elif o in ("-i", "--incremental_from"):
   1390       OPTIONS.incremental_source = a
   1391     elif o in ("-w", "--wipe_user_data"):
   1392       OPTIONS.wipe_user_data = True
   1393     elif o in ("-n", "--no_prereq"):
   1394       OPTIONS.omit_prereq = True
   1395     elif o in ("-o", "--oem_settings"):
   1396       OPTIONS.oem_source = a
   1397     elif o in ("-e", "--extra_script"):
   1398       OPTIONS.extra_script = a
   1399     elif o in ("-a", "--aslr_mode"):
   1400       if a in ("on", "On", "true", "True", "yes", "Yes"):
   1401         OPTIONS.aslr_mode = True
   1402       else:
   1403         OPTIONS.aslr_mode = False
   1404     elif o in ("-t", "--worker_threads"):
   1405       if a.isdigit():
   1406         OPTIONS.worker_threads = int(a)
   1407       else:
   1408         raise ValueError("Cannot parse value %r for option %r - only "
   1409                          "integers are allowed." % (a, o))
   1410     elif o in ("-2", "--two_step"):
   1411       OPTIONS.two_step = True
   1412     elif o == "--no_signing":
   1413       OPTIONS.no_signing = True
   1414     elif o in ("--verify"):
   1415       OPTIONS.verify = True
   1416     elif o == "--block":
   1417       OPTIONS.block_based = True
   1418     elif o in ("-b", "--binary"):
   1419       OPTIONS.updater_binary = a
   1420     elif o in ("--no_fallback_to_full",):
   1421       OPTIONS.fallback_to_full = False
   1422     else:
   1423       return False
   1424     return True
   1425 
   1426   args = common.ParseOptions(argv, __doc__,
   1427                              extra_opts="b:k:i:d:wne:t:a:2o:",
   1428                              extra_long_opts=["board_config=",
   1429                                               "package_key=",
   1430                                               "incremental_from=",
   1431                                               "wipe_user_data",
   1432                                               "no_prereq",
   1433                                               "extra_script=",
   1434                                               "worker_threads=",
   1435                                               "aslr_mode=",
   1436                                               "two_step",
   1437                                               "no_signing",
   1438                                               "block",
   1439                                               "binary=",
   1440                                               "oem_settings=",
   1441                                               "verify",
   1442                                               "no_fallback_to_full",
   1443                                               ],
   1444                              extra_option_handler=option_handler)
   1445 
   1446   if len(args) != 2:
   1447     common.Usage(__doc__)
   1448     sys.exit(1)
   1449 
   1450   if OPTIONS.extra_script is not None:
   1451     OPTIONS.extra_script = open(OPTIONS.extra_script).read()
   1452 
   1453   print "unzipping target target-files..."
   1454   OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
   1455 
   1456   OPTIONS.target_tmp = OPTIONS.input_tmp
   1457   OPTIONS.info_dict = common.LoadInfoDict(input_zip)
   1458 
   1459   # If this image was originally labelled with SELinux contexts, make sure we
   1460   # also apply the labels in our new image. During building, the "file_contexts"
   1461   # is in the out/ directory tree, but for repacking from target-files.zip it's
   1462   # in the root directory of the ramdisk.
   1463   if "selinux_fc" in OPTIONS.info_dict:
   1464     OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
   1465         "file_contexts")
   1466 
   1467   if OPTIONS.verbose:
   1468     print "--- target info ---"
   1469     common.DumpInfoDict(OPTIONS.info_dict)
   1470 
   1471   # If the caller explicitly specified the device-specific extensions
   1472   # path via -s/--device_specific, use that.  Otherwise, use
   1473   # META/releasetools.py if it is present in the target target_files.
   1474   # Otherwise, take the path of the file from 'tool_extensions' in the
   1475   # info dict and look for that in the local filesystem, relative to
   1476   # the current directory.
   1477 
   1478   if OPTIONS.device_specific is None:
   1479     from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
   1480     if os.path.exists(from_input):
   1481       print "(using device-specific extensions from target_files)"
   1482       OPTIONS.device_specific = from_input
   1483     else:
   1484       OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
   1485 
   1486   if OPTIONS.device_specific is not None:
   1487     OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
   1488 
   1489   while True:
   1490 
   1491     if OPTIONS.no_signing:
   1492       if os.path.exists(args[1]): os.unlink(args[1])
   1493       output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
   1494     else:
   1495       temp_zip_file = tempfile.NamedTemporaryFile()
   1496       output_zip = zipfile.ZipFile(temp_zip_file, "w",
   1497                                    compression=zipfile.ZIP_DEFLATED)
   1498 
   1499     if OPTIONS.incremental_source is None:
   1500       WriteFullOTAPackage(input_zip, output_zip)
   1501       if OPTIONS.package_key is None:
   1502         OPTIONS.package_key = OPTIONS.info_dict.get(
   1503             "default_system_dev_certificate",
   1504             "build/target/product/security/testkey")
   1505       break
   1506 
   1507     else:
   1508       print "unzipping source target-files..."
   1509       OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
   1510       OPTIONS.target_info_dict = OPTIONS.info_dict
   1511       OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
   1512       if "selinux_fc" in OPTIONS.source_info_dict:
   1513         OPTIONS.source_info_dict["selinux_fc"] = os.path.join(OPTIONS.source_tmp, "BOOT", "RAMDISK",
   1514                                                               "file_contexts")
   1515       if OPTIONS.package_key is None:
   1516         OPTIONS.package_key = OPTIONS.source_info_dict.get(
   1517             "default_system_dev_certificate",
   1518             "build/target/product/security/testkey")
   1519       if OPTIONS.verbose:
   1520         print "--- source info ---"
   1521         common.DumpInfoDict(OPTIONS.source_info_dict)
   1522       try:
   1523         WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
   1524         break
   1525       except ValueError:
   1526         if not OPTIONS.fallback_to_full: raise
   1527         print "--- failed to build incremental; falling back to full ---"
   1528         OPTIONS.incremental_source = None
   1529         output_zip.close()
   1530 
   1531   output_zip.close()
   1532 
   1533   if not OPTIONS.no_signing:
   1534     SignOutput(temp_zip_file.name, args[1])
   1535     temp_zip_file.close()
   1536 
   1537   print "done."
   1538 
   1539 
   1540 if __name__ == '__main__':
   1541   try:
   1542     common.CloseInheritedPipes()
   1543     main(sys.argv[1:])
   1544   except common.ExternalError, e:
   1545     print
   1546     print "   ERROR: %s" % (e,)
   1547     print
   1548     sys.exit(1)
   1549   finally:
   1550     common.Cleanup()
   1551