ota_from_target_files.py revision d522bdc9edbf64d15a59c6924853b2e2c8c39e90
1#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Given a target-files zipfile, produces an OTA package that installs
19that build.  An incremental OTA is produced if -i is given, otherwise
20a full OTA is produced.
21
22Usage:  ota_from_target_files [flags] input_target_files output_ota_package
23
24  --board_config  <file>
25      Deprecated.
26
27  -k (--package_key) <key> Key to use to sign the package (default is
28      the value of default_system_dev_certificate from the input
29      target-files's META/misc_info.txt, or
30      "build/target/product/security/testkey" if that value is not
31      specified).
32
33      For incremental OTAs, the default value is based on the source
34      target-file, not the target build.
35
36  -i  (--incremental_from)  <file>
37      Generate an incremental OTA using the given target-files zip as
38      the starting build.
39
40  --full_radio
41      When generating an incremental OTA, always include a full copy of
42      radio image. This option is only meaningful when -i is specified,
43      because a full radio is always included in a full OTA if applicable.
44
45  --full_bootloader
46      Similar to --full_radio. When generating an incremental OTA, always
47      include a full copy of bootloader image.
48
49  -v  (--verify)
50      Remount and verify the checksums of the files written to the
51      system and vendor (if used) partitions.  Incremental builds only.
52
53  -o  (--oem_settings)  <file>
54      Use the file to specify the expected OEM-specific properties
55      on the OEM partition of the intended device.
56
57  --oem_no_mount
58      For devices with OEM-specific properties but without an OEM partition,
59      do not mount the OEM partition in the updater-script. This should be
60      very rarely used, since it's expected to have a dedicated OEM partition
61      for OEM-specific properties. Only meaningful when -o is specified.
62
63  -w  (--wipe_user_data)
64      Generate an OTA package that will wipe the user data partition
65      when installed.
66
67  -n  (--no_prereq)
68      Omit the timestamp prereq check normally included at the top of
69      the build scripts (used for developer OTA packages which
70      legitimately need to go back and forth).
71
72  --downgrade
73      Intentionally generate an incremental OTA that updates from a newer
74      build to an older one (based on timestamp comparison). "post-timestamp"
75      will be replaced by "ota-downgrade=yes" in the metadata file. A data
76      wipe will always be enforced, so "ota-wipe=yes" will also be included in
77      the metadata file. The update-binary in the source build will be used in
78      the OTA package, unless --binary flag is specified.
79
80  -e  (--extra_script)  <file>
81      Insert the contents of file at the end of the update script.
82
83  -a  (--aslr_mode)  <on|off>
84      Specify whether to turn on ASLR for the package (on by default).
85
86  -2  (--two_step)
87      Generate a 'two-step' OTA package, where recovery is updated
88      first, so that any changes made to the system partition are done
89      using the new recovery (new kernel, etc.).
90
91  --block
92      Generate a block-based OTA if possible.  Will fall back to a
93      file-based OTA if the target_files is older and doesn't support
94      block-based OTAs.
95
96  -b  (--binary)  <file>
97      Use the given binary as the update-binary in the output package,
98      instead of the binary in the build's target_files.  Use for
99      development only.
100
101  -t  (--worker_threads) <int>
102      Specifies the number of worker-threads that will be used when
103      generating patches for incremental updates (defaults to 3).
104
105  --stash_threshold <float>
106      Specifies the threshold that will be used to compute the maximum
107      allowed stash size (defaults to 0.8).
108
109  --gen_verify
110      Generate an OTA package that verifies the partitions.
111
112  --log_diff <file>
113      Generate a log file that shows the differences in the source and target
114      builds for an incremental package. This option is only meaningful when
115      -i is specified.
116"""
117
118import sys
119
120if sys.hexversion < 0x02070000:
121  print >> sys.stderr, "Python 2.7 or newer is required."
122  sys.exit(1)
123
124import multiprocessing
125import os
126import subprocess
127import tempfile
128import zipfile
129
130import common
131import edify_generator
132import sparse_img
133
134OPTIONS = common.OPTIONS
135OPTIONS.package_key = None
136OPTIONS.incremental_source = None
137OPTIONS.verify = False
138OPTIONS.require_verbatim = set()
139OPTIONS.prohibit_verbatim = set(("system/build.prop",))
140OPTIONS.patch_threshold = 0.95
141OPTIONS.wipe_user_data = False
142OPTIONS.omit_prereq = False
143OPTIONS.downgrade = False
144OPTIONS.extra_script = None
145OPTIONS.aslr_mode = True
146OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
147if OPTIONS.worker_threads == 0:
148  OPTIONS.worker_threads = 1
149OPTIONS.two_step = False
150OPTIONS.no_signing = False
151OPTIONS.block_based = False
152OPTIONS.updater_binary = None
153OPTIONS.oem_source = None
154OPTIONS.oem_no_mount = False
155OPTIONS.fallback_to_full = True
156OPTIONS.full_radio = False
157OPTIONS.full_bootloader = False
158# Stash size cannot exceed cache_size * threshold.
159OPTIONS.cache_size = None
160OPTIONS.stash_threshold = 0.8
161OPTIONS.gen_verify = False
162OPTIONS.log_diff = None
163
164def MostPopularKey(d, default):
165  """Given a dict, return the key corresponding to the largest
166  value.  Returns 'default' if the dict is empty."""
167  x = [(v, k) for (k, v) in d.iteritems()]
168  if not x:
169    return default
170  x.sort()
171  return x[-1][1]
172
173
174def IsSymlink(info):
175  """Return true if the zipfile.ZipInfo object passed in represents a
176  symlink."""
177  return (info.external_attr >> 16) & 0o770000 == 0o120000
178
179def IsRegular(info):
180  """Return true if the zipfile.ZipInfo object passed in represents a
181  regular file."""
182  return (info.external_attr >> 16) & 0o770000 == 0o100000
183
184def ClosestFileMatch(src, tgtfiles, existing):
185  """Returns the closest file match between a source file and list
186     of potential matches.  The exact filename match is preferred,
187     then the sha1 is searched for, and finally a file with the same
188     basename is evaluated.  Rename support in the updater-binary is
189     required for the latter checks to be used."""
190
191  result = tgtfiles.get("path:" + src.name)
192  if result is not None:
193    return result
194
195  if not OPTIONS.target_info_dict.get("update_rename_support", False):
196    return None
197
198  if src.size < 1000:
199    return None
200
201  result = tgtfiles.get("sha1:" + src.sha1)
202  if result is not None and existing.get(result.name) is None:
203    return result
204  result = tgtfiles.get("file:" + src.name.split("/")[-1])
205  if result is not None and existing.get(result.name) is None:
206    return result
207  return None
208
209class ItemSet(object):
210  def __init__(self, partition, fs_config):
211    self.partition = partition
212    self.fs_config = fs_config
213    self.ITEMS = {}
214
215  def Get(self, name, is_dir=False):
216    if name not in self.ITEMS:
217      self.ITEMS[name] = Item(self, name, is_dir=is_dir)
218    return self.ITEMS[name]
219
220  def GetMetadata(self, input_zip):
221    # The target_files contains a record of what the uid,
222    # gid, and mode are supposed to be.
223    output = input_zip.read(self.fs_config)
224
225    for line in output.split("\n"):
226      if not line:
227        continue
228      columns = line.split()
229      name, uid, gid, mode = columns[:4]
230      selabel = None
231      capabilities = None
232
233      # After the first 4 columns, there are a series of key=value
234      # pairs. Extract out the fields we care about.
235      for element in columns[4:]:
236        key, value = element.split("=")
237        if key == "selabel":
238          selabel = value
239        if key == "capabilities":
240          capabilities = value
241
242      i = self.ITEMS.get(name, None)
243      if i is not None:
244        i.uid = int(uid)
245        i.gid = int(gid)
246        i.mode = int(mode, 8)
247        i.selabel = selabel
248        i.capabilities = capabilities
249        if i.is_dir:
250          i.children.sort(key=lambda i: i.name)
251
252    # Set metadata for the files generated by this script. For full recovery
253    # image at system/etc/recovery.img, it will be taken care by fs_config.
254    i = self.ITEMS.get("system/recovery-from-boot.p", None)
255    if i:
256      i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
257    i = self.ITEMS.get("system/etc/install-recovery.sh", None)
258    if i:
259      i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
260
261
262class Item(object):
263  """Items represent the metadata (user, group, mode) of files and
264  directories in the system image."""
265  def __init__(self, itemset, name, is_dir=False):
266    self.itemset = itemset
267    self.name = name
268    self.uid = None
269    self.gid = None
270    self.mode = None
271    self.selabel = None
272    self.capabilities = None
273    self.is_dir = is_dir
274    self.descendants = None
275    self.best_subtree = None
276
277    if name:
278      self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
279      self.parent.children.append(self)
280    else:
281      self.parent = None
282    if self.is_dir:
283      self.children = []
284
285  def Dump(self, indent=0):
286    if self.uid is not None:
287      print "%s%s %d %d %o" % (
288          "  " * indent, self.name, self.uid, self.gid, self.mode)
289    else:
290      print "%s%s %s %s %s" % (
291          "  " * indent, self.name, self.uid, self.gid, self.mode)
292    if self.is_dir:
293      print "%s%s" % ("  "*indent, self.descendants)
294      print "%s%s" % ("  "*indent, self.best_subtree)
295      for i in self.children:
296        i.Dump(indent=indent+1)
297
298  def CountChildMetadata(self):
299    """Count up the (uid, gid, mode, selabel, capabilities) tuples for
300    all children and determine the best strategy for using set_perm_recursive
301    and set_perm to correctly chown/chmod all the files to their desired
302    values.  Recursively calls itself for all descendants.
303
304    Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
305    counting up all descendants of this node.  (dmode or fmode may be None.)
306    Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
307    fmode, selabel, capabilities) tuple that will match the most descendants of
308    that Item.
309    """
310
311    assert self.is_dir
312    key = (self.uid, self.gid, self.mode, None, self.selabel,
313           self.capabilities)
314    self.descendants = {key: 1}
315    d = self.descendants
316    for i in self.children:
317      if i.is_dir:
318        for k, v in i.CountChildMetadata().iteritems():
319          d[k] = d.get(k, 0) + v
320      else:
321        k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
322        d[k] = d.get(k, 0) + 1
323
324    # Find the (uid, gid, dmode, fmode, selabel, capabilities)
325    # tuple that matches the most descendants.
326
327    # First, find the (uid, gid) pair that matches the most
328    # descendants.
329    ug = {}
330    for (uid, gid, _, _, _, _), count in d.iteritems():
331      ug[(uid, gid)] = ug.get((uid, gid), 0) + count
332    ug = MostPopularKey(ug, (0, 0))
333
334    # Now find the dmode, fmode, selabel, and capabilities that match
335    # the most descendants with that (uid, gid), and choose those.
336    best_dmode = (0, 0o755)
337    best_fmode = (0, 0o644)
338    best_selabel = (0, None)
339    best_capabilities = (0, None)
340    for k, count in d.iteritems():
341      if k[:2] != ug:
342        continue
343      if k[2] is not None and count >= best_dmode[0]:
344        best_dmode = (count, k[2])
345      if k[3] is not None and count >= best_fmode[0]:
346        best_fmode = (count, k[3])
347      if k[4] is not None and count >= best_selabel[0]:
348        best_selabel = (count, k[4])
349      if k[5] is not None and count >= best_capabilities[0]:
350        best_capabilities = (count, k[5])
351    self.best_subtree = ug + (
352        best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
353
354    return d
355
356  def SetPermissions(self, script):
357    """Append set_perm/set_perm_recursive commands to 'script' to
358    set all permissions, users, and groups for the tree of files
359    rooted at 'self'."""
360
361    self.CountChildMetadata()
362
363    def recurse(item, current):
364      # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
365      # that the current item (and all its children) have already been set to.
366      # We only need to issue set_perm/set_perm_recursive commands if we're
367      # supposed to be something different.
368      if item.is_dir:
369        if current != item.best_subtree:
370          script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
371          current = item.best_subtree
372
373        if item.uid != current[0] or item.gid != current[1] or \
374           item.mode != current[2] or item.selabel != current[4] or \
375           item.capabilities != current[5]:
376          script.SetPermissions("/"+item.name, item.uid, item.gid,
377                                item.mode, item.selabel, item.capabilities)
378
379        for i in item.children:
380          recurse(i, current)
381      else:
382        if item.uid != current[0] or item.gid != current[1] or \
383               item.mode != current[3] or item.selabel != current[4] or \
384               item.capabilities != current[5]:
385          script.SetPermissions("/"+item.name, item.uid, item.gid,
386                                item.mode, item.selabel, item.capabilities)
387
388    recurse(self, (-1, -1, -1, -1, None, None))
389
390
391def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
392  """Copies files for the partition in the input zip to the output
393  zip.  Populates the Item class with their metadata, and returns a
394  list of symlinks.  output_zip may be None, in which case the copy is
395  skipped (but the other side effects still happen).  substitute is an
396  optional dict of {output filename: contents} to be output instead of
397  certain input files.
398  """
399
400  symlinks = []
401
402  partition = itemset.partition
403
404  for info in input_zip.infolist():
405    prefix = partition.upper() + "/"
406    if info.filename.startswith(prefix):
407      basefilename = info.filename[len(prefix):]
408      if IsSymlink(info):
409        symlinks.append((input_zip.read(info.filename),
410                         "/" + partition + "/" + basefilename))
411      else:
412        import copy
413        info2 = copy.copy(info)
414        fn = info2.filename = partition + "/" + basefilename
415        if substitute and fn in substitute and substitute[fn] is None:
416          continue
417        if output_zip is not None:
418          if substitute and fn in substitute:
419            data = substitute[fn]
420          else:
421            data = input_zip.read(info.filename)
422          common.ZipWriteStr(output_zip, info2, data)
423        if fn.endswith("/"):
424          itemset.Get(fn[:-1], is_dir=True)
425        else:
426          itemset.Get(fn)
427
428  symlinks.sort()
429  return symlinks
430
431
432def SignOutput(temp_zip_name, output_zip_name):
433  key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
434  pw = key_passwords[OPTIONS.package_key]
435
436  common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
437                  whole_file=True)
438
439
440def AppendAssertions(script, info_dict, oem_dict=None):
441  oem_props = info_dict.get("oem_fingerprint_properties")
442  if oem_props is None or len(oem_props) == 0:
443    device = GetBuildProp("ro.product.device", info_dict)
444    script.AssertDevice(device)
445  else:
446    if oem_dict is None:
447      raise common.ExternalError(
448          "No OEM file provided to answer expected assertions")
449    for prop in oem_props.split():
450      if oem_dict.get(prop) is None:
451        raise common.ExternalError(
452            "The OEM file is missing the property %s" % prop)
453      script.AssertOemProperty(prop, oem_dict.get(prop))
454
455
456def HasRecoveryPatch(target_files_zip):
457  namelist = [name for name in target_files_zip.namelist()]
458  return ("SYSTEM/recovery-from-boot.p" in namelist or
459          "SYSTEM/etc/recovery.img" in namelist)
460
461def HasVendorPartition(target_files_zip):
462  try:
463    target_files_zip.getinfo("VENDOR/")
464    return True
465  except KeyError:
466    return False
467
468def GetOemProperty(name, oem_props, oem_dict, info_dict):
469  if oem_props is not None and name in oem_props:
470    return oem_dict[name]
471  return GetBuildProp(name, info_dict)
472
473
474def CalculateFingerprint(oem_props, oem_dict, info_dict):
475  if oem_props is None:
476    return GetBuildProp("ro.build.fingerprint", info_dict)
477  return "%s/%s/%s:%s" % (
478      GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
479      GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
480      GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
481      GetBuildProp("ro.build.thumbprint", info_dict))
482
483
484def GetImage(which, tmpdir, info_dict):
485  # Return an image object (suitable for passing to BlockImageDiff)
486  # for the 'which' partition (most be "system" or "vendor").  If a
487  # prebuilt image and file map are found in tmpdir they are used,
488  # otherwise they are reconstructed from the individual files.
489
490  assert which in ("system", "vendor")
491
492  path = os.path.join(tmpdir, "IMAGES", which + ".img")
493  mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
494  if os.path.exists(path) and os.path.exists(mappath):
495    print "using %s.img from target-files" % (which,)
496    # This is a 'new' target-files, which already has the image in it.
497
498  else:
499    print "building %s.img from target-files" % (which,)
500
501    # This is an 'old' target-files, which does not contain images
502    # already built.  Build them.
503
504    mappath = tempfile.mkstemp()[1]
505    OPTIONS.tempfiles.append(mappath)
506
507    import add_img_to_target_files
508    if which == "system":
509      path = add_img_to_target_files.BuildSystem(
510          tmpdir, info_dict, block_list=mappath)
511    elif which == "vendor":
512      path = add_img_to_target_files.BuildVendor(
513          tmpdir, info_dict, block_list=mappath)
514
515  # Bug: http://b/20939131
516  # In ext4 filesystems, block 0 might be changed even being mounted
517  # R/O. We add it to clobbered_blocks so that it will be written to the
518  # target unconditionally. Note that they are still part of care_map.
519  clobbered_blocks = "0"
520
521  return sparse_img.SparseImage(path, mappath, clobbered_blocks)
522
523
524def WriteFullOTAPackage(input_zip, output_zip):
525  # TODO: how to determine this?  We don't know what version it will
526  # be installed on top of. For now, we expect the API just won't
527  # change very often. Similarly for fstab, it might have changed
528  # in the target build.
529  script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
530
531  oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
532  recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
533  oem_dict = None
534  if oem_props is not None and len(oem_props) > 0:
535    if OPTIONS.oem_source is None:
536      raise common.ExternalError("OEM source required for this build")
537    if not OPTIONS.oem_no_mount:
538      script.Mount("/oem", recovery_mount_options)
539    oem_dict = common.LoadDictionaryFromLines(
540        open(OPTIONS.oem_source).readlines())
541
542  metadata = {
543      "post-build": CalculateFingerprint(oem_props, oem_dict,
544                                         OPTIONS.info_dict),
545      "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
546                                   OPTIONS.info_dict),
547      "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
548  }
549
550  device_specific = common.DeviceSpecificParams(
551      input_zip=input_zip,
552      input_version=OPTIONS.info_dict["recovery_api_version"],
553      output_zip=output_zip,
554      script=script,
555      input_tmp=OPTIONS.input_tmp,
556      metadata=metadata,
557      info_dict=OPTIONS.info_dict)
558
559  has_recovery_patch = HasRecoveryPatch(input_zip)
560  block_based = OPTIONS.block_based and has_recovery_patch
561
562  metadata["ota-type"] = "BLOCK" if block_based else "FILE"
563
564  if not OPTIONS.omit_prereq:
565    ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
566    ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
567    script.AssertOlderBuild(ts, ts_text)
568
569  AppendAssertions(script, OPTIONS.info_dict, oem_dict)
570  device_specific.FullOTA_Assertions()
571
572  # Two-step package strategy (in chronological order, which is *not*
573  # the order in which the generated script has things):
574  #
575  # if stage is not "2/3" or "3/3":
576  #    write recovery image to boot partition
577  #    set stage to "2/3"
578  #    reboot to boot partition and restart recovery
579  # else if stage is "2/3":
580  #    write recovery image to recovery partition
581  #    set stage to "3/3"
582  #    reboot to recovery partition and restart recovery
583  # else:
584  #    (stage must be "3/3")
585  #    set stage to ""
586  #    do normal full package installation:
587  #       wipe and install system, boot image, etc.
588  #       set up system to update recovery partition on first boot
589  #    complete script normally
590  #    (allow recovery to mark itself finished and reboot)
591
592  recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
593                                         OPTIONS.input_tmp, "RECOVERY")
594  if OPTIONS.two_step:
595    if not OPTIONS.info_dict.get("multistage_support", None):
596      assert False, "two-step packages not supported by this build"
597    fs = OPTIONS.info_dict["fstab"]["/misc"]
598    assert fs.fs_type.upper() == "EMMC", \
599        "two-step packages only supported on devices with EMMC /misc partitions"
600    bcb_dev = {"bcb_dev": fs.device}
601    common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
602    script.AppendExtra("""
603if get_stage("%(bcb_dev)s") == "2/3" then
604""" % bcb_dev)
605    script.WriteRawImage("/recovery", "recovery.img")
606    script.AppendExtra("""
607set_stage("%(bcb_dev)s", "3/3");
608reboot_now("%(bcb_dev)s", "recovery");
609else if get_stage("%(bcb_dev)s") == "3/3" then
610""" % bcb_dev)
611
612  # Dump fingerprints
613  script.Print("Target: %s" % CalculateFingerprint(
614      oem_props, oem_dict, OPTIONS.info_dict))
615
616  device_specific.FullOTA_InstallBegin()
617
618  system_progress = 0.75
619
620  if OPTIONS.wipe_user_data:
621    system_progress -= 0.1
622  if HasVendorPartition(input_zip):
623    system_progress -= 0.1
624
625  # Place a copy of file_contexts.bin into the OTA package which will be used
626  # by the recovery program.
627  if "selinux_fc" in OPTIONS.info_dict:
628    WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
629
630  recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
631
632  system_items = ItemSet("system", "META/filesystem_config.txt")
633  script.ShowProgress(system_progress, 0)
634
635  if block_based:
636    # Full OTA is done as an "incremental" against an empty source
637    # image.  This has the effect of writing new data from the package
638    # to the entire partition, but lets us reuse the updater code that
639    # writes incrementals to do it.
640    system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
641    system_tgt.ResetFileMap()
642    system_diff = common.BlockDifference("system", system_tgt, src=None)
643    system_diff.WriteScript(script, output_zip)
644  else:
645    script.FormatPartition("/system")
646    script.Mount("/system", recovery_mount_options)
647    if not has_recovery_patch:
648      script.UnpackPackageDir("recovery", "/system")
649    script.UnpackPackageDir("system", "/system")
650
651    symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
652    script.MakeSymlinks(symlinks)
653
654  boot_img = common.GetBootableImage(
655      "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
656
657  if not block_based:
658    def output_sink(fn, data):
659      common.ZipWriteStr(output_zip, "recovery/" + fn, data)
660      system_items.Get("system/" + fn)
661
662    common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
663                             recovery_img, boot_img)
664
665    system_items.GetMetadata(input_zip)
666    system_items.Get("system").SetPermissions(script)
667
668  if HasVendorPartition(input_zip):
669    vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
670    script.ShowProgress(0.1, 0)
671
672    if block_based:
673      vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
674      vendor_tgt.ResetFileMap()
675      vendor_diff = common.BlockDifference("vendor", vendor_tgt)
676      vendor_diff.WriteScript(script, output_zip)
677    else:
678      script.FormatPartition("/vendor")
679      script.Mount("/vendor", recovery_mount_options)
680      script.UnpackPackageDir("vendor", "/vendor")
681
682      symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
683      script.MakeSymlinks(symlinks)
684
685      vendor_items.GetMetadata(input_zip)
686      vendor_items.Get("vendor").SetPermissions(script)
687
688  common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
689  common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
690
691  script.ShowProgress(0.05, 5)
692  script.WriteRawImage("/boot", "boot.img")
693
694  script.ShowProgress(0.2, 10)
695  device_specific.FullOTA_InstallEnd()
696
697  if OPTIONS.extra_script is not None:
698    script.AppendExtra(OPTIONS.extra_script)
699
700  script.UnmountAll()
701
702  if OPTIONS.wipe_user_data:
703    script.ShowProgress(0.1, 10)
704    script.FormatPartition("/data")
705
706  if OPTIONS.two_step:
707    script.AppendExtra("""
708set_stage("%(bcb_dev)s", "");
709""" % bcb_dev)
710    script.AppendExtra("else\n")
711    script.WriteRawImage("/boot", "recovery.img")
712    script.AppendExtra("""
713set_stage("%(bcb_dev)s", "2/3");
714reboot_now("%(bcb_dev)s", "");
715endif;
716endif;
717""" % bcb_dev)
718
719  script.SetProgress(1)
720  script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
721  metadata["ota-required-cache"] = str(script.required_cache)
722  WriteMetadata(metadata, output_zip)
723
724
725def WritePolicyConfig(file_name, output_zip):
726  common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
727
728
729def WriteMetadata(metadata, output_zip):
730  common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
731                     "".join(["%s=%s\n" % kv
732                              for kv in sorted(metadata.iteritems())]))
733
734
735def LoadPartitionFiles(z, partition):
736  """Load all the files from the given partition in a given target-files
737  ZipFile, and return a dict of {filename: File object}."""
738  out = {}
739  prefix = partition.upper() + "/"
740  for info in z.infolist():
741    if info.filename.startswith(prefix) and not IsSymlink(info):
742      basefilename = info.filename[len(prefix):]
743      fn = partition + "/" + basefilename
744      data = z.read(info.filename)
745      out[fn] = common.File(fn, data)
746  return out
747
748
749def GetBuildProp(prop, info_dict):
750  """Return the fingerprint of the build of a given target-files info_dict."""
751  try:
752    return info_dict.get("build.prop", {})[prop]
753  except KeyError:
754    raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
755
756
757def AddToKnownPaths(filename, known_paths):
758  if filename[-1] == "/":
759    return
760  dirs = filename.split("/")[:-1]
761  while len(dirs) > 0:
762    path = "/".join(dirs)
763    if path in known_paths:
764      break
765    known_paths.add(path)
766    dirs.pop()
767
768
769def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
770  # TODO(tbao): We should factor out the common parts between
771  # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
772  source_version = OPTIONS.source_info_dict["recovery_api_version"]
773  target_version = OPTIONS.target_info_dict["recovery_api_version"]
774
775  if source_version == 0:
776    print ("WARNING: generating edify script for a source that "
777           "can't install it.")
778  script = edify_generator.EdifyGenerator(
779      source_version, OPTIONS.target_info_dict,
780      fstab=OPTIONS.source_info_dict["fstab"])
781
782  oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
783  recovery_mount_options = OPTIONS.source_info_dict.get(
784      "recovery_mount_options")
785  oem_dict = None
786  if oem_props is not None and len(oem_props) > 0:
787    if OPTIONS.oem_source is None:
788      raise common.ExternalError("OEM source required for this build")
789    if not OPTIONS.oem_no_mount:
790      script.Mount("/oem", recovery_mount_options)
791    oem_dict = common.LoadDictionaryFromLines(
792        open(OPTIONS.oem_source).readlines())
793
794  metadata = {
795      "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
796                                   OPTIONS.source_info_dict),
797      "ota-type": "BLOCK",
798  }
799
800  post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
801  pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
802  is_downgrade = long(post_timestamp) < long(pre_timestamp)
803
804  if OPTIONS.downgrade:
805    metadata["ota-downgrade"] = "yes"
806    if not is_downgrade:
807      raise RuntimeError("--downgrade specified but no downgrade detected: "
808                         "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
809  else:
810    if is_downgrade:
811      # Non-fatal here to allow generating such a package which may require
812      # manual work to adjust the post-timestamp. A legit use case is that we
813      # cut a new build C (after having A and B), but want to enfore the
814      # update path of A -> C -> B. Specifying --downgrade may not help since
815      # that would enforce a data wipe for C -> B update.
816      print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
817            "The package may not be deployed properly. "
818            "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
819    metadata["post-timestamp"] = post_timestamp
820
821  device_specific = common.DeviceSpecificParams(
822      source_zip=source_zip,
823      source_version=source_version,
824      target_zip=target_zip,
825      target_version=target_version,
826      output_zip=output_zip,
827      script=script,
828      metadata=metadata,
829      info_dict=OPTIONS.source_info_dict)
830
831  source_fp = CalculateFingerprint(oem_props, oem_dict,
832                                   OPTIONS.source_info_dict)
833  target_fp = CalculateFingerprint(oem_props, oem_dict,
834                                   OPTIONS.target_info_dict)
835  metadata["pre-build"] = source_fp
836  metadata["post-build"] = target_fp
837
838  source_boot = common.GetBootableImage(
839      "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
840      OPTIONS.source_info_dict)
841  target_boot = common.GetBootableImage(
842      "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
843  updating_boot = (not OPTIONS.two_step and
844                   (source_boot.data != target_boot.data))
845
846  target_recovery = common.GetBootableImage(
847      "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
848
849  system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
850  system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
851
852  blockimgdiff_version = 1
853  if OPTIONS.info_dict:
854    blockimgdiff_version = max(
855        int(i) for i in
856        OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
857
858  # Check first block of system partition for remount R/W only if
859  # disk type is ext4
860  system_partition = OPTIONS.source_info_dict["fstab"]["/system"]
861  check_first_block = system_partition.fs_type == "ext4"
862  system_diff = common.BlockDifference("system", system_tgt, system_src,
863                                       check_first_block,
864                                       version=blockimgdiff_version)
865
866  if HasVendorPartition(target_zip):
867    if not HasVendorPartition(source_zip):
868      raise RuntimeError("can't generate incremental that adds /vendor")
869    vendor_src = GetImage("vendor", OPTIONS.source_tmp,
870                          OPTIONS.source_info_dict)
871    vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
872                          OPTIONS.target_info_dict)
873
874    # Check first block of vendor partition for remount R/W only if
875    # disk type is ext4
876    vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
877    check_first_block = vendor_partition.fs_type == "ext4"
878    vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
879                                         check_first_block,
880                                         version=blockimgdiff_version)
881  else:
882    vendor_diff = None
883
884  AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
885  device_specific.IncrementalOTA_Assertions()
886
887  # Two-step incremental package strategy (in chronological order,
888  # which is *not* the order in which the generated script has
889  # things):
890  #
891  # if stage is not "2/3" or "3/3":
892  #    do verification on current system
893  #    write recovery image to boot partition
894  #    set stage to "2/3"
895  #    reboot to boot partition and restart recovery
896  # else if stage is "2/3":
897  #    write recovery image to recovery partition
898  #    set stage to "3/3"
899  #    reboot to recovery partition and restart recovery
900  # else:
901  #    (stage must be "3/3")
902  #    perform update:
903  #       patch system files, etc.
904  #       force full install of new boot image
905  #       set up system to update recovery partition on first boot
906  #    complete script normally
907  #    (allow recovery to mark itself finished and reboot)
908
909  if OPTIONS.two_step:
910    if not OPTIONS.source_info_dict.get("multistage_support", None):
911      assert False, "two-step packages not supported by this build"
912    fs = OPTIONS.source_info_dict["fstab"]["/misc"]
913    assert fs.fs_type.upper() == "EMMC", \
914        "two-step packages only supported on devices with EMMC /misc partitions"
915    bcb_dev = {"bcb_dev": fs.device}
916    common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
917    script.AppendExtra("""
918if get_stage("%(bcb_dev)s") == "2/3" then
919""" % bcb_dev)
920    script.AppendExtra("sleep(20);\n")
921    script.WriteRawImage("/recovery", "recovery.img")
922    script.AppendExtra("""
923set_stage("%(bcb_dev)s", "3/3");
924reboot_now("%(bcb_dev)s", "recovery");
925else if get_stage("%(bcb_dev)s") != "3/3" then
926""" % bcb_dev)
927
928  # Dump fingerprints
929  script.Print("Source: %s" % CalculateFingerprint(
930      oem_props, oem_dict, OPTIONS.source_info_dict))
931  script.Print("Target: %s" % CalculateFingerprint(
932      oem_props, oem_dict, OPTIONS.target_info_dict))
933
934  script.Print("Verifying current system...")
935
936  device_specific.IncrementalOTA_VerifyBegin()
937
938  if oem_props is None:
939    # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
940    # patching on a device that's already on the target build will damage the
941    # system. Because operations like move don't check the block state, they
942    # always apply the changes unconditionally.
943    if blockimgdiff_version <= 2:
944      script.AssertSomeFingerprint(source_fp)
945    else:
946      script.AssertSomeFingerprint(source_fp, target_fp)
947  else:
948    if blockimgdiff_version <= 2:
949      script.AssertSomeThumbprint(
950          GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
951    else:
952      script.AssertSomeThumbprint(
953          GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
954          GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
955
956  # Check the required cache size (i.e. stashed blocks).
957  size = []
958  if system_diff:
959    size.append(system_diff.required_cache)
960  if vendor_diff:
961    size.append(vendor_diff.required_cache)
962
963  if updating_boot:
964    boot_type, boot_device = common.GetTypeAndDevice(
965        "/boot", OPTIONS.source_info_dict)
966    d = common.Difference(target_boot, source_boot)
967    _, _, d = d.ComputePatch()
968    if d is None:
969      include_full_boot = True
970      common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
971    else:
972      include_full_boot = False
973
974      print "boot      target: %d  source: %d  diff: %d" % (
975          target_boot.size, source_boot.size, len(d))
976
977      common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
978
979      script.PatchCheck("%s:%s:%d:%s:%d:%s" %
980                        (boot_type, boot_device,
981                         source_boot.size, source_boot.sha1,
982                         target_boot.size, target_boot.sha1))
983      size.append(target_boot.size)
984
985  if size:
986    script.CacheFreeSpaceCheck(max(size))
987
988  device_specific.IncrementalOTA_VerifyEnd()
989
990  if OPTIONS.two_step:
991    script.WriteRawImage("/boot", "recovery.img")
992    script.AppendExtra("""
993set_stage("%(bcb_dev)s", "2/3");
994reboot_now("%(bcb_dev)s", "");
995else
996""" % bcb_dev)
997
998  # Verify the existing partitions.
999  system_diff.WriteVerifyScript(script, touched_blocks_only=True)
1000  if vendor_diff:
1001    vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
1002
1003  script.Comment("---- start making changes here ----")
1004
1005  device_specific.IncrementalOTA_InstallBegin()
1006
1007  system_diff.WriteScript(script, output_zip,
1008                          progress=0.8 if vendor_diff else 0.9)
1009
1010  if vendor_diff:
1011    vendor_diff.WriteScript(script, output_zip, progress=0.1)
1012
1013  if OPTIONS.two_step:
1014    common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1015    script.WriteRawImage("/boot", "boot.img")
1016    print "writing full boot image (forced by two-step mode)"
1017
1018  if not OPTIONS.two_step:
1019    if updating_boot:
1020      if include_full_boot:
1021        print "boot image changed; including full."
1022        script.Print("Installing boot image...")
1023        script.WriteRawImage("/boot", "boot.img")
1024      else:
1025        # Produce the boot image by applying a patch to the current
1026        # contents of the boot partition, and write it back to the
1027        # partition.
1028        print "boot image changed; including patch."
1029        script.Print("Patching boot image...")
1030        script.ShowProgress(0.1, 10)
1031        script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1032                          % (boot_type, boot_device,
1033                             source_boot.size, source_boot.sha1,
1034                             target_boot.size, target_boot.sha1),
1035                          "-",
1036                          target_boot.size, target_boot.sha1,
1037                          source_boot.sha1, "patch/boot.img.p")
1038    else:
1039      print "boot image unchanged; skipping."
1040
1041  # Do device-specific installation (eg, write radio image).
1042  device_specific.IncrementalOTA_InstallEnd()
1043
1044  if OPTIONS.extra_script is not None:
1045    script.AppendExtra(OPTIONS.extra_script)
1046
1047  if OPTIONS.wipe_user_data:
1048    script.Print("Erasing user data...")
1049    script.FormatPartition("/data")
1050    metadata["ota-wipe"] = "yes"
1051
1052  if OPTIONS.two_step:
1053    script.AppendExtra("""
1054set_stage("%(bcb_dev)s", "");
1055endif;
1056endif;
1057""" % bcb_dev)
1058
1059  script.SetProgress(1)
1060  # For downgrade OTAs, we prefer to use the update-binary in the source
1061  # build that is actually newer than the one in the target build.
1062  if OPTIONS.downgrade:
1063    script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1064  else:
1065    script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
1066  metadata["ota-required-cache"] = str(script.required_cache)
1067  WriteMetadata(metadata, output_zip)
1068
1069
1070def WriteVerifyPackage(input_zip, output_zip):
1071  script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1072
1073  oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1074  recovery_mount_options = OPTIONS.info_dict.get(
1075      "recovery_mount_options")
1076  oem_dict = None
1077  if oem_props is not None and len(oem_props) > 0:
1078    if OPTIONS.oem_source is None:
1079      raise common.ExternalError("OEM source required for this build")
1080    script.Mount("/oem", recovery_mount_options)
1081    oem_dict = common.LoadDictionaryFromLines(
1082        open(OPTIONS.oem_source).readlines())
1083
1084  target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1085  metadata = {
1086      "post-build": target_fp,
1087      "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1088                                   OPTIONS.info_dict),
1089      "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1090  }
1091
1092  device_specific = common.DeviceSpecificParams(
1093      input_zip=input_zip,
1094      input_version=OPTIONS.info_dict["recovery_api_version"],
1095      output_zip=output_zip,
1096      script=script,
1097      input_tmp=OPTIONS.input_tmp,
1098      metadata=metadata,
1099      info_dict=OPTIONS.info_dict)
1100
1101  AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1102
1103  script.Print("Verifying device images against %s..." % target_fp)
1104  script.AppendExtra("")
1105
1106  script.Print("Verifying boot...")
1107  boot_img = common.GetBootableImage(
1108      "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1109  boot_type, boot_device = common.GetTypeAndDevice(
1110      "/boot", OPTIONS.info_dict)
1111  script.Verify("%s:%s:%d:%s" % (
1112      boot_type, boot_device, boot_img.size, boot_img.sha1))
1113  script.AppendExtra("")
1114
1115  script.Print("Verifying recovery...")
1116  recovery_img = common.GetBootableImage(
1117      "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1118  recovery_type, recovery_device = common.GetTypeAndDevice(
1119      "/recovery", OPTIONS.info_dict)
1120  script.Verify("%s:%s:%d:%s" % (
1121      recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1122  script.AppendExtra("")
1123
1124  system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1125  system_tgt.ResetFileMap()
1126  system_diff = common.BlockDifference("system", system_tgt, src=None)
1127  system_diff.WriteStrictVerifyScript(script)
1128
1129  if HasVendorPartition(input_zip):
1130    vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1131    vendor_tgt.ResetFileMap()
1132    vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1133    vendor_diff.WriteStrictVerifyScript(script)
1134
1135  # Device specific partitions, such as radio, bootloader and etc.
1136  device_specific.VerifyOTA_Assertions()
1137
1138  script.SetProgress(1.0)
1139  script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
1140  metadata["ota-required-cache"] = str(script.required_cache)
1141  WriteMetadata(metadata, output_zip)
1142
1143
1144def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1145                                      source_file=None):
1146  """Generate an Android OTA package that has A/B update payload."""
1147
1148  # Setup signing keys.
1149  if OPTIONS.package_key is None:
1150    OPTIONS.package_key = OPTIONS.info_dict.get(
1151        "default_system_dev_certificate",
1152        "build/target/product/security/testkey")
1153
1154  # A/B updater expects key in RSA format.
1155  cmd = ["openssl", "pkcs8",
1156         "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1157         "-inform", "DER", "-nocrypt"]
1158  rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1159  cmd.extend(["-out", rsa_key])
1160  p1 = common.Run(cmd, stdout=subprocess.PIPE)
1161  p1.wait()
1162  assert p1.returncode == 0, "openssl pkcs8 failed"
1163
1164  # Stage the output zip package for signing.
1165  temp_zip_file = tempfile.NamedTemporaryFile()
1166  output_zip = zipfile.ZipFile(temp_zip_file, "w",
1167                               compression=zipfile.ZIP_DEFLATED)
1168
1169  # Metadata to comply with Android OTA package format.
1170  oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1171  oem_dict = None
1172  if oem_props:
1173    if OPTIONS.oem_source is None:
1174      raise common.ExternalError("OEM source required for this build")
1175    oem_dict = common.LoadDictionaryFromLines(
1176        open(OPTIONS.oem_source).readlines())
1177
1178  metadata = {
1179      "post-build": CalculateFingerprint(oem_props, oem_dict,
1180                                         OPTIONS.info_dict),
1181      "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1182                                   OPTIONS.info_dict),
1183      "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1184      "ota-required-cache": "0",
1185      "ota-type": "AB",
1186  }
1187
1188  if source_file is not None:
1189    metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1190                                                 OPTIONS.source_info_dict)
1191
1192  # 1. Generate payload.
1193  payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1194  cmd = ["brillo_update_payload", "generate",
1195         "--payload", payload_file,
1196         "--target_image", target_file]
1197  if source_file is not None:
1198    cmd.extend(["--source_image", source_file])
1199  p1 = common.Run(cmd, stdout=subprocess.PIPE)
1200  p1.wait()
1201  assert p1.returncode == 0, "brillo_update_payload generate failed"
1202
1203  # 2. Generate hashes of the payload and metadata files.
1204  payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1205  metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1206  cmd = ["brillo_update_payload", "hash",
1207         "--unsigned_payload", payload_file,
1208         "--signature_size", "256",
1209         "--metadata_hash_file", metadata_sig_file,
1210         "--payload_hash_file", payload_sig_file]
1211  p1 = common.Run(cmd, stdout=subprocess.PIPE)
1212  p1.wait()
1213  assert p1.returncode == 0, "brillo_update_payload hash failed"
1214
1215  # 3. Sign the hashes and insert them back into the payload file.
1216  signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1217                                                suffix=".bin")
1218  signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1219                                                 suffix=".bin")
1220  # 3a. Sign the payload hash.
1221  cmd = ["openssl", "pkeyutl", "-sign",
1222         "-inkey", rsa_key,
1223         "-pkeyopt", "digest:sha256",
1224         "-in", payload_sig_file,
1225         "-out", signed_payload_sig_file]
1226  p1 = common.Run(cmd, stdout=subprocess.PIPE)
1227  p1.wait()
1228  assert p1.returncode == 0, "openssl sign payload failed"
1229
1230  # 3b. Sign the metadata hash.
1231  cmd = ["openssl", "pkeyutl", "-sign",
1232         "-inkey", rsa_key,
1233         "-pkeyopt", "digest:sha256",
1234         "-in", metadata_sig_file,
1235         "-out", signed_metadata_sig_file]
1236  p1 = common.Run(cmd, stdout=subprocess.PIPE)
1237  p1.wait()
1238  assert p1.returncode == 0, "openssl sign metadata failed"
1239
1240  # 3c. Insert the signatures back into the payload file.
1241  signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1242                                            suffix=".bin")
1243  cmd = ["brillo_update_payload", "sign",
1244         "--unsigned_payload", payload_file,
1245         "--payload", signed_payload_file,
1246         "--signature_size", "256",
1247         "--metadata_signature_file", signed_metadata_sig_file,
1248         "--payload_signature_file", signed_payload_sig_file]
1249  p1 = common.Run(cmd, stdout=subprocess.PIPE)
1250  p1.wait()
1251  assert p1.returncode == 0, "brillo_update_payload sign failed"
1252
1253  # 4. Dump the signed payload properties.
1254  properties_file = common.MakeTempFile(prefix="payload-properties-",
1255                                        suffix=".txt")
1256  cmd = ["brillo_update_payload", "properties",
1257         "--payload", signed_payload_file,
1258         "--properties_file", properties_file]
1259  p1 = common.Run(cmd, stdout=subprocess.PIPE)
1260  p1.wait()
1261  assert p1.returncode == 0, "brillo_update_payload properties failed"
1262
1263  # Add the signed payload file and properties into the zip.
1264  common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
1265  common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1266                  compress_type=zipfile.ZIP_STORED)
1267  WriteMetadata(metadata, output_zip)
1268
1269  # Sign the whole package to comply with the Android OTA package format.
1270  common.ZipClose(output_zip)
1271  SignOutput(temp_zip_file.name, output_file)
1272  temp_zip_file.close()
1273
1274
1275class FileDifference(object):
1276  def __init__(self, partition, source_zip, target_zip, output_zip):
1277    self.deferred_patch_list = None
1278    print "Loading target..."
1279    self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1280    print "Loading source..."
1281    self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1282
1283    self.verbatim_targets = verbatim_targets = []
1284    self.patch_list = patch_list = []
1285    diffs = []
1286    self.renames = renames = {}
1287    known_paths = set()
1288    largest_source_size = 0
1289
1290    matching_file_cache = {}
1291    for fn, sf in source_data.items():
1292      assert fn == sf.name
1293      matching_file_cache["path:" + fn] = sf
1294      if fn in target_data.keys():
1295        AddToKnownPaths(fn, known_paths)
1296      # Only allow eligibility for filename/sha matching
1297      # if there isn't a perfect path match.
1298      if target_data.get(sf.name) is None:
1299        matching_file_cache["file:" + fn.split("/")[-1]] = sf
1300        matching_file_cache["sha:" + sf.sha1] = sf
1301
1302    for fn in sorted(target_data.keys()):
1303      tf = target_data[fn]
1304      assert fn == tf.name
1305      sf = ClosestFileMatch(tf, matching_file_cache, renames)
1306      if sf is not None and sf.name != tf.name:
1307        print "File has moved from " + sf.name + " to " + tf.name
1308        renames[sf.name] = tf
1309
1310      if sf is None or fn in OPTIONS.require_verbatim:
1311        # This file should be included verbatim
1312        if fn in OPTIONS.prohibit_verbatim:
1313          raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1314        print "send", fn, "verbatim"
1315        tf.AddToZip(output_zip)
1316        verbatim_targets.append((fn, tf.size, tf.sha1))
1317        if fn in target_data.keys():
1318          AddToKnownPaths(fn, known_paths)
1319      elif tf.sha1 != sf.sha1:
1320        # File is different; consider sending as a patch
1321        diffs.append(common.Difference(tf, sf))
1322      else:
1323        # Target file data identical to source (may still be renamed)
1324        pass
1325
1326    common.ComputeDifferences(diffs)
1327
1328    for diff in diffs:
1329      tf, sf, d = diff.GetPatch()
1330      path = "/".join(tf.name.split("/")[:-1])
1331      if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1332          path not in known_paths:
1333        # patch is almost as big as the file; don't bother patching
1334        # or a patch + rename cannot take place due to the target
1335        # directory not existing
1336        tf.AddToZip(output_zip)
1337        verbatim_targets.append((tf.name, tf.size, tf.sha1))
1338        if sf.name in renames:
1339          del renames[sf.name]
1340        AddToKnownPaths(tf.name, known_paths)
1341      else:
1342        common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1343        patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1344        largest_source_size = max(largest_source_size, sf.size)
1345
1346    self.largest_source_size = largest_source_size
1347
1348  def EmitVerification(self, script):
1349    so_far = 0
1350    for tf, sf, _, _ in self.patch_list:
1351      if tf.name != sf.name:
1352        script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1353      script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1354      so_far += sf.size
1355    return so_far
1356
1357  def EmitExplicitTargetVerification(self, script):
1358    for fn, _, sha1 in self.verbatim_targets:
1359      if fn[-1] != "/":
1360        script.FileCheck("/"+fn, sha1)
1361    for tf, _, _, _ in self.patch_list:
1362      script.FileCheck(tf.name, tf.sha1)
1363
1364  def RemoveUnneededFiles(self, script, extras=()):
1365    file_list = ["/" + i[0] for i in self.verbatim_targets]
1366    file_list += ["/" + i for i in self.source_data
1367                  if i not in self.target_data and i not in self.renames]
1368    file_list += list(extras)
1369    # Sort the list in descending order, which removes all the files first
1370    # before attempting to remove the folder. (Bug: 22960996)
1371    script.DeleteFiles(sorted(file_list, reverse=True))
1372
1373  def TotalPatchSize(self):
1374    return sum(i[1].size for i in self.patch_list)
1375
1376  def EmitPatches(self, script, total_patch_size, so_far):
1377    self.deferred_patch_list = deferred_patch_list = []
1378    for item in self.patch_list:
1379      tf, sf, _, _ = item
1380      if tf.name == "system/build.prop":
1381        deferred_patch_list.append(item)
1382        continue
1383      if sf.name != tf.name:
1384        script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1385      script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1386                        "patch/" + sf.name + ".p")
1387      so_far += tf.size
1388      script.SetProgress(so_far / total_patch_size)
1389    return so_far
1390
1391  def EmitDeferredPatches(self, script):
1392    for item in self.deferred_patch_list:
1393      tf, sf, _, _ = item
1394      script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1395                        "patch/" + sf.name + ".p")
1396    script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
1397
1398  def EmitRenames(self, script):
1399    if len(self.renames) > 0:
1400      script.Print("Renaming files...")
1401      for src, tgt in self.renames.iteritems():
1402        print "Renaming " + src + " to " + tgt.name
1403        script.RenameFile(src, tgt.name)
1404
1405
1406def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
1407  target_has_recovery_patch = HasRecoveryPatch(target_zip)
1408  source_has_recovery_patch = HasRecoveryPatch(source_zip)
1409
1410  if (OPTIONS.block_based and
1411      target_has_recovery_patch and
1412      source_has_recovery_patch):
1413    return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1414
1415  source_version = OPTIONS.source_info_dict["recovery_api_version"]
1416  target_version = OPTIONS.target_info_dict["recovery_api_version"]
1417
1418  if source_version == 0:
1419    print ("WARNING: generating edify script for a source that "
1420           "can't install it.")
1421  script = edify_generator.EdifyGenerator(
1422      source_version, OPTIONS.target_info_dict,
1423      fstab=OPTIONS.source_info_dict["fstab"])
1424
1425  oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1426  recovery_mount_options = OPTIONS.source_info_dict.get(
1427      "recovery_mount_options")
1428  oem_dict = None
1429  if oem_props is not None and len(oem_props) > 0:
1430    if OPTIONS.oem_source is None:
1431      raise common.ExternalError("OEM source required for this build")
1432    if not OPTIONS.oem_no_mount:
1433      script.Mount("/oem", recovery_mount_options)
1434    oem_dict = common.LoadDictionaryFromLines(
1435        open(OPTIONS.oem_source).readlines())
1436
1437  metadata = {
1438      "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1439                                   OPTIONS.source_info_dict),
1440      "ota-type": "FILE",
1441  }
1442
1443  post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
1444  pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
1445  is_downgrade = long(post_timestamp) < long(pre_timestamp)
1446
1447  if OPTIONS.downgrade:
1448    metadata["ota-downgrade"] = "yes"
1449    if not is_downgrade:
1450      raise RuntimeError("--downgrade specified but no downgrade detected: "
1451                         "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
1452  else:
1453    if is_downgrade:
1454      # Non-fatal here to allow generating such a package which may require
1455      # manual work to adjust the post-timestamp. A legit use case is that we
1456      # cut a new build C (after having A and B), but want to enfore the
1457      # update path of A -> C -> B. Specifying --downgrade may not help since
1458      # that would enforce a data wipe for C -> B update.
1459      print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
1460            "The package may not be deployed properly. "
1461            "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
1462    metadata["post-timestamp"] = post_timestamp
1463
1464  device_specific = common.DeviceSpecificParams(
1465      source_zip=source_zip,
1466      source_version=source_version,
1467      target_zip=target_zip,
1468      target_version=target_version,
1469      output_zip=output_zip,
1470      script=script,
1471      metadata=metadata,
1472      info_dict=OPTIONS.source_info_dict)
1473
1474  system_diff = FileDifference("system", source_zip, target_zip, output_zip)
1475  script.Mount("/system", recovery_mount_options)
1476  if HasVendorPartition(target_zip):
1477    vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
1478    script.Mount("/vendor", recovery_mount_options)
1479  else:
1480    vendor_diff = None
1481
1482  target_fp = CalculateFingerprint(oem_props, oem_dict,
1483                                   OPTIONS.target_info_dict)
1484  source_fp = CalculateFingerprint(oem_props, oem_dict,
1485                                   OPTIONS.source_info_dict)
1486
1487  if oem_props is None:
1488    script.AssertSomeFingerprint(source_fp, target_fp)
1489  else:
1490    script.AssertSomeThumbprint(
1491        GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1492        GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1493
1494  metadata["pre-build"] = source_fp
1495  metadata["post-build"] = target_fp
1496
1497  source_boot = common.GetBootableImage(
1498      "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1499      OPTIONS.source_info_dict)
1500  target_boot = common.GetBootableImage(
1501      "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
1502  updating_boot = (not OPTIONS.two_step and
1503                   (source_boot.data != target_boot.data))
1504
1505  source_recovery = common.GetBootableImage(
1506      "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1507      OPTIONS.source_info_dict)
1508  target_recovery = common.GetBootableImage(
1509      "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
1510  updating_recovery = (source_recovery.data != target_recovery.data)
1511
1512  # Here's how we divide up the progress bar:
1513  #  0.1 for verifying the start state (PatchCheck calls)
1514  #  0.8 for applying patches (ApplyPatch calls)
1515  #  0.1 for unpacking verbatim files, symlinking, and doing the
1516  #      device-specific commands.
1517
1518  AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
1519  device_specific.IncrementalOTA_Assertions()
1520
1521  # Two-step incremental package strategy (in chronological order,
1522  # which is *not* the order in which the generated script has
1523  # things):
1524  #
1525  # if stage is not "2/3" or "3/3":
1526  #    do verification on current system
1527  #    write recovery image to boot partition
1528  #    set stage to "2/3"
1529  #    reboot to boot partition and restart recovery
1530  # else if stage is "2/3":
1531  #    write recovery image to recovery partition
1532  #    set stage to "3/3"
1533  #    reboot to recovery partition and restart recovery
1534  # else:
1535  #    (stage must be "3/3")
1536  #    perform update:
1537  #       patch system files, etc.
1538  #       force full install of new boot image
1539  #       set up system to update recovery partition on first boot
1540  #    complete script normally
1541  #    (allow recovery to mark itself finished and reboot)
1542
1543  if OPTIONS.two_step:
1544    if not OPTIONS.source_info_dict.get("multistage_support", None):
1545      assert False, "two-step packages not supported by this build"
1546    fs = OPTIONS.source_info_dict["fstab"]["/misc"]
1547    assert fs.fs_type.upper() == "EMMC", \
1548        "two-step packages only supported on devices with EMMC /misc partitions"
1549    bcb_dev = {"bcb_dev": fs.device}
1550    common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1551    script.AppendExtra("""
1552if get_stage("%(bcb_dev)s") == "2/3" then
1553""" % bcb_dev)
1554    script.AppendExtra("sleep(20);\n")
1555    script.WriteRawImage("/recovery", "recovery.img")
1556    script.AppendExtra("""
1557set_stage("%(bcb_dev)s", "3/3");
1558reboot_now("%(bcb_dev)s", "recovery");
1559else if get_stage("%(bcb_dev)s") != "3/3" then
1560""" % bcb_dev)
1561
1562  # Dump fingerprints
1563  script.Print("Source: %s" % (source_fp,))
1564  script.Print("Target: %s" % (target_fp,))
1565
1566  script.Print("Verifying current system...")
1567
1568  device_specific.IncrementalOTA_VerifyBegin()
1569
1570  script.ShowProgress(0.1, 0)
1571  so_far = system_diff.EmitVerification(script)
1572  if vendor_diff:
1573    so_far += vendor_diff.EmitVerification(script)
1574
1575  size = []
1576  if system_diff.patch_list:
1577    size.append(system_diff.largest_source_size)
1578  if vendor_diff:
1579    if vendor_diff.patch_list:
1580      size.append(vendor_diff.largest_source_size)
1581
1582  if updating_boot:
1583    d = common.Difference(target_boot, source_boot)
1584    _, _, d = d.ComputePatch()
1585    print "boot      target: %d  source: %d  diff: %d" % (
1586        target_boot.size, source_boot.size, len(d))
1587
1588    common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
1589
1590    boot_type, boot_device = common.GetTypeAndDevice(
1591        "/boot", OPTIONS.source_info_dict)
1592
1593    script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1594                      (boot_type, boot_device,
1595                       source_boot.size, source_boot.sha1,
1596                       target_boot.size, target_boot.sha1))
1597    so_far += source_boot.size
1598    size.append(target_boot.size)
1599
1600  if size:
1601    script.CacheFreeSpaceCheck(max(size))
1602
1603  device_specific.IncrementalOTA_VerifyEnd()
1604
1605  if OPTIONS.two_step:
1606    script.WriteRawImage("/boot", "recovery.img")
1607    script.AppendExtra("""
1608set_stage("%(bcb_dev)s", "2/3");
1609reboot_now("%(bcb_dev)s", "");
1610else
1611""" % bcb_dev)
1612
1613  script.Comment("---- start making changes here ----")
1614
1615  device_specific.IncrementalOTA_InstallBegin()
1616
1617  if OPTIONS.two_step:
1618    common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1619    script.WriteRawImage("/boot", "boot.img")
1620    print "writing full boot image (forced by two-step mode)"
1621
1622  script.Print("Removing unneeded files...")
1623  system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1624  if vendor_diff:
1625    vendor_diff.RemoveUnneededFiles(script)
1626
1627  script.ShowProgress(0.8, 0)
1628  total_patch_size = 1.0 + system_diff.TotalPatchSize()
1629  if vendor_diff:
1630    total_patch_size += vendor_diff.TotalPatchSize()
1631  if updating_boot:
1632    total_patch_size += target_boot.size
1633
1634  script.Print("Patching system files...")
1635  so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1636  if vendor_diff:
1637    script.Print("Patching vendor files...")
1638    so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
1639
1640  if not OPTIONS.two_step:
1641    if updating_boot:
1642      # Produce the boot image by applying a patch to the current
1643      # contents of the boot partition, and write it back to the
1644      # partition.
1645      script.Print("Patching boot image...")
1646      script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1647                        % (boot_type, boot_device,
1648                           source_boot.size, source_boot.sha1,
1649                           target_boot.size, target_boot.sha1),
1650                        "-",
1651                        target_boot.size, target_boot.sha1,
1652                        source_boot.sha1, "patch/boot.img.p")
1653      so_far += target_boot.size
1654      script.SetProgress(so_far / total_patch_size)
1655      print "boot image changed; including."
1656    else:
1657      print "boot image unchanged; skipping."
1658
1659  system_items = ItemSet("system", "META/filesystem_config.txt")
1660  if vendor_diff:
1661    vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1662
1663  if updating_recovery:
1664    # Recovery is generated as a patch using both the boot image
1665    # (which contains the same linux kernel as recovery) and the file
1666    # /system/etc/recovery-resource.dat (which contains all the images
1667    # used in the recovery UI) as sources.  This lets us minimize the
1668    # size of the patch, which must be included in every OTA package.
1669    #
1670    # For older builds where recovery-resource.dat is not present, we
1671    # use only the boot image as the source.
1672
1673    if not target_has_recovery_patch:
1674      def output_sink(fn, data):
1675        common.ZipWriteStr(output_zip, "recovery/" + fn, data)
1676        system_items.Get("system/" + fn)
1677
1678      common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1679                               target_recovery, target_boot)
1680      script.DeleteFiles(["/system/recovery-from-boot.p",
1681                          "/system/etc/recovery.img",
1682                          "/system/etc/install-recovery.sh"])
1683    print "recovery image changed; including as patch from boot."
1684  else:
1685    print "recovery image unchanged; skipping."
1686
1687  script.ShowProgress(0.1, 10)
1688
1689  target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1690  if vendor_diff:
1691    target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1692
1693  temp_script = script.MakeTemporary()
1694  system_items.GetMetadata(target_zip)
1695  system_items.Get("system").SetPermissions(temp_script)
1696  if vendor_diff:
1697    vendor_items.GetMetadata(target_zip)
1698    vendor_items.Get("vendor").SetPermissions(temp_script)
1699
1700  # Note that this call will mess up the trees of Items, so make sure
1701  # we're done with them.
1702  source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1703  if vendor_diff:
1704    source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
1705
1706  target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
1707  source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1708
1709  # Delete all the symlinks in source that aren't in target.  This
1710  # needs to happen before verbatim files are unpacked, in case a
1711  # symlink in the source is replaced by a real file in the target.
1712
1713  # If a symlink in the source will be replaced by a regular file, we cannot
1714  # delete the symlink/file in case the package gets applied again. For such
1715  # a symlink, we prepend a sha1_check() to detect if it has been updated.
1716  # (Bug: 23646151)
1717  replaced_symlinks = dict()
1718  if system_diff:
1719    for i in system_diff.verbatim_targets:
1720      replaced_symlinks["/%s" % (i[0],)] = i[2]
1721  if vendor_diff:
1722    for i in vendor_diff.verbatim_targets:
1723      replaced_symlinks["/%s" % (i[0],)] = i[2]
1724
1725  if system_diff:
1726    for tf in system_diff.renames.values():
1727      replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1728  if vendor_diff:
1729    for tf in vendor_diff.renames.values():
1730      replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1731
1732  always_delete = []
1733  may_delete = []
1734  for dest, link in source_symlinks:
1735    if link not in target_symlinks_d:
1736      if link in replaced_symlinks:
1737        may_delete.append((link, replaced_symlinks[link]))
1738      else:
1739        always_delete.append(link)
1740  script.DeleteFiles(always_delete)
1741  script.DeleteFilesIfNotMatching(may_delete)
1742
1743  if system_diff.verbatim_targets:
1744    script.Print("Unpacking new system files...")
1745    script.UnpackPackageDir("system", "/system")
1746  if vendor_diff and vendor_diff.verbatim_targets:
1747    script.Print("Unpacking new vendor files...")
1748    script.UnpackPackageDir("vendor", "/vendor")
1749
1750  if updating_recovery and not target_has_recovery_patch:
1751    script.Print("Unpacking new recovery...")
1752    script.UnpackPackageDir("recovery", "/system")
1753
1754  system_diff.EmitRenames(script)
1755  if vendor_diff:
1756    vendor_diff.EmitRenames(script)
1757
1758  script.Print("Symlinks and permissions...")
1759
1760  # Create all the symlinks that don't already exist, or point to
1761  # somewhere different than what we want.  Delete each symlink before
1762  # creating it, since the 'symlink' command won't overwrite.
1763  to_create = []
1764  for dest, link in target_symlinks:
1765    if link in source_symlinks_d:
1766      if dest != source_symlinks_d[link]:
1767        to_create.append((dest, link))
1768    else:
1769      to_create.append((dest, link))
1770  script.DeleteFiles([i[1] for i in to_create])
1771  script.MakeSymlinks(to_create)
1772
1773  # Now that the symlinks are created, we can set all the
1774  # permissions.
1775  script.AppendScript(temp_script)
1776
1777  # Do device-specific installation (eg, write radio image).
1778  device_specific.IncrementalOTA_InstallEnd()
1779
1780  if OPTIONS.extra_script is not None:
1781    script.AppendExtra(OPTIONS.extra_script)
1782
1783  # Patch the build.prop file last, so if something fails but the
1784  # device can still come up, it appears to be the old build and will
1785  # get set the OTA package again to retry.
1786  script.Print("Patching remaining system files...")
1787  system_diff.EmitDeferredPatches(script)
1788
1789  if OPTIONS.wipe_user_data:
1790    script.Print("Erasing user data...")
1791    script.FormatPartition("/data")
1792    metadata["ota-wipe"] = "yes"
1793
1794  if OPTIONS.two_step:
1795    script.AppendExtra("""
1796set_stage("%(bcb_dev)s", "");
1797endif;
1798endif;
1799""" % bcb_dev)
1800
1801  if OPTIONS.verify and system_diff:
1802    script.Print("Remounting and verifying system partition files...")
1803    script.Unmount("/system")
1804    script.Mount("/system", recovery_mount_options)
1805    system_diff.EmitExplicitTargetVerification(script)
1806
1807  if OPTIONS.verify and vendor_diff:
1808    script.Print("Remounting and verifying vendor partition files...")
1809    script.Unmount("/vendor")
1810    script.Mount("/vendor", recovery_mount_options)
1811    vendor_diff.EmitExplicitTargetVerification(script)
1812
1813  # For downgrade OTAs, we prefer to use the update-binary in the source
1814  # build that is actually newer than the one in the target build.
1815  if OPTIONS.downgrade:
1816    script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1817  else:
1818    script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
1819
1820  metadata["ota-required-cache"] = str(script.required_cache)
1821  WriteMetadata(metadata, output_zip)
1822
1823
1824def main(argv):
1825
1826  def option_handler(o, a):
1827    if o == "--board_config":
1828      pass   # deprecated
1829    elif o in ("-k", "--package_key"):
1830      OPTIONS.package_key = a
1831    elif o in ("-i", "--incremental_from"):
1832      OPTIONS.incremental_source = a
1833    elif o == "--full_radio":
1834      OPTIONS.full_radio = True
1835    elif o == "--full_bootloader":
1836      OPTIONS.full_bootloader = True
1837    elif o in ("-w", "--wipe_user_data"):
1838      OPTIONS.wipe_user_data = True
1839    elif o in ("-n", "--no_prereq"):
1840      OPTIONS.omit_prereq = True
1841    elif o == "--downgrade":
1842      OPTIONS.downgrade = True
1843      OPTIONS.wipe_user_data = True
1844    elif o in ("-o", "--oem_settings"):
1845      OPTIONS.oem_source = a
1846    elif o == "--oem_no_mount":
1847      OPTIONS.oem_no_mount = True
1848    elif o in ("-e", "--extra_script"):
1849      OPTIONS.extra_script = a
1850    elif o in ("-a", "--aslr_mode"):
1851      if a in ("on", "On", "true", "True", "yes", "Yes"):
1852        OPTIONS.aslr_mode = True
1853      else:
1854        OPTIONS.aslr_mode = False
1855    elif o in ("-t", "--worker_threads"):
1856      if a.isdigit():
1857        OPTIONS.worker_threads = int(a)
1858      else:
1859        raise ValueError("Cannot parse value %r for option %r - only "
1860                         "integers are allowed." % (a, o))
1861    elif o in ("-2", "--two_step"):
1862      OPTIONS.two_step = True
1863    elif o == "--no_signing":
1864      OPTIONS.no_signing = True
1865    elif o == "--verify":
1866      OPTIONS.verify = True
1867    elif o == "--block":
1868      OPTIONS.block_based = True
1869    elif o in ("-b", "--binary"):
1870      OPTIONS.updater_binary = a
1871    elif o in ("--no_fallback_to_full",):
1872      OPTIONS.fallback_to_full = False
1873    elif o == "--stash_threshold":
1874      try:
1875        OPTIONS.stash_threshold = float(a)
1876      except ValueError:
1877        raise ValueError("Cannot parse value %r for option %r - expecting "
1878                         "a float" % (a, o))
1879    elif o == "--gen_verify":
1880      OPTIONS.gen_verify = True
1881    elif o == "--log_diff":
1882      OPTIONS.log_diff = a
1883    else:
1884      return False
1885    return True
1886
1887  args = common.ParseOptions(argv, __doc__,
1888                             extra_opts="b:k:i:d:wne:t:a:2o:",
1889                             extra_long_opts=[
1890                                 "board_config=",
1891                                 "package_key=",
1892                                 "incremental_from=",
1893                                 "full_radio",
1894                                 "full_bootloader",
1895                                 "wipe_user_data",
1896                                 "no_prereq",
1897                                 "downgrade",
1898                                 "extra_script=",
1899                                 "worker_threads=",
1900                                 "aslr_mode=",
1901                                 "two_step",
1902                                 "no_signing",
1903                                 "block",
1904                                 "binary=",
1905                                 "oem_settings=",
1906                                 "oem_no_mount",
1907                                 "verify",
1908                                 "no_fallback_to_full",
1909                                 "stash_threshold=",
1910                                 "gen_verify",
1911                                 "log_diff=",
1912                             ], extra_option_handler=option_handler)
1913
1914  if len(args) != 2:
1915    common.Usage(__doc__)
1916    sys.exit(1)
1917
1918  if OPTIONS.downgrade:
1919    # Sanity check to enforce a data wipe.
1920    if not OPTIONS.wipe_user_data:
1921      raise ValueError("Cannot downgrade without a data wipe")
1922
1923    # We should only allow downgrading incrementals (as opposed to full).
1924    # Otherwise the device may go back from arbitrary build with this full
1925    # OTA package.
1926    if OPTIONS.incremental_source is None:
1927      raise ValueError("Cannot generate downgradable full OTAs - consider"
1928                       "using --omit_prereq?")
1929
1930  # Load the dict file from the zip directly to have a peek at the OTA type.
1931  # For packages using A/B update, unzipping is not needed.
1932  input_zip = zipfile.ZipFile(args[0], "r")
1933  OPTIONS.info_dict = common.LoadInfoDict(input_zip)
1934  common.ZipClose(input_zip)
1935
1936  ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1937
1938  if ab_update:
1939    if OPTIONS.incremental_source is not None:
1940      OPTIONS.target_info_dict = OPTIONS.info_dict
1941      source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
1942      OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1943      common.ZipClose(source_zip)
1944
1945    if OPTIONS.verbose:
1946      print "--- target info ---"
1947      common.DumpInfoDict(OPTIONS.info_dict)
1948
1949      if OPTIONS.incremental_source is not None:
1950        print "--- source info ---"
1951        common.DumpInfoDict(OPTIONS.source_info_dict)
1952
1953    WriteABOTAPackageWithBrilloScript(
1954        target_file=args[0],
1955        output_file=args[1],
1956        source_file=OPTIONS.incremental_source)
1957
1958    print "done."
1959    return
1960
1961  if OPTIONS.extra_script is not None:
1962    OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1963
1964  print "unzipping target target-files..."
1965  OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
1966
1967  OPTIONS.target_tmp = OPTIONS.input_tmp
1968  OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
1969
1970  if OPTIONS.verbose:
1971    print "--- target info ---"
1972    common.DumpInfoDict(OPTIONS.info_dict)
1973
1974  # If the caller explicitly specified the device-specific extensions
1975  # path via -s/--device_specific, use that.  Otherwise, use
1976  # META/releasetools.py if it is present in the target target_files.
1977  # Otherwise, take the path of the file from 'tool_extensions' in the
1978  # info dict and look for that in the local filesystem, relative to
1979  # the current directory.
1980
1981  if OPTIONS.device_specific is None:
1982    from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1983    if os.path.exists(from_input):
1984      print "(using device-specific extensions from target_files)"
1985      OPTIONS.device_specific = from_input
1986    else:
1987      OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1988
1989  if OPTIONS.device_specific is not None:
1990    OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
1991
1992  if OPTIONS.info_dict.get("no_recovery") == "true":
1993    raise common.ExternalError(
1994        "--- target build has specified no recovery ---")
1995
1996  # Use the default key to sign the package if not specified with package_key.
1997  if not OPTIONS.no_signing:
1998    if OPTIONS.package_key is None:
1999      OPTIONS.package_key = OPTIONS.info_dict.get(
2000          "default_system_dev_certificate",
2001          "build/target/product/security/testkey")
2002
2003  # Set up the output zip. Create a temporary zip file if signing is needed.
2004  if OPTIONS.no_signing:
2005    if os.path.exists(args[1]):
2006      os.unlink(args[1])
2007    output_zip = zipfile.ZipFile(args[1], "w",
2008                                 compression=zipfile.ZIP_DEFLATED)
2009  else:
2010    temp_zip_file = tempfile.NamedTemporaryFile()
2011    output_zip = zipfile.ZipFile(temp_zip_file, "w",
2012                                 compression=zipfile.ZIP_DEFLATED)
2013
2014  # Non A/B OTAs rely on /cache partition to store temporary files.
2015  cache_size = OPTIONS.info_dict.get("cache_size", None)
2016  if cache_size is None:
2017    print "--- can't determine the cache partition size ---"
2018  OPTIONS.cache_size = cache_size
2019
2020  # Generate a verify package.
2021  if OPTIONS.gen_verify:
2022    WriteVerifyPackage(input_zip, output_zip)
2023
2024  # Generate a full OTA.
2025  elif OPTIONS.incremental_source is None:
2026    WriteFullOTAPackage(input_zip, output_zip)
2027
2028  # Generate an incremental OTA. It will fall back to generate a full OTA on
2029  # failure unless no_fallback_to_full is specified.
2030  else:
2031    print "unzipping source target-files..."
2032    OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2033        OPTIONS.incremental_source)
2034    OPTIONS.target_info_dict = OPTIONS.info_dict
2035    OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2036                                                   OPTIONS.source_tmp)
2037    if OPTIONS.verbose:
2038      print "--- source info ---"
2039      common.DumpInfoDict(OPTIONS.source_info_dict)
2040    try:
2041      WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
2042      if OPTIONS.log_diff:
2043        out_file = open(OPTIONS.log_diff, 'w')
2044        import target_files_diff
2045        target_files_diff.recursiveDiff('',
2046                                        OPTIONS.source_tmp,
2047                                        OPTIONS.input_tmp,
2048                                        out_file)
2049        out_file.close()
2050    except ValueError:
2051      if not OPTIONS.fallback_to_full:
2052        raise
2053      print "--- failed to build incremental; falling back to full ---"
2054      OPTIONS.incremental_source = None
2055      WriteFullOTAPackage(input_zip, output_zip)
2056
2057  common.ZipClose(output_zip)
2058
2059  # Sign the generated zip package unless no_signing is specified.
2060  if not OPTIONS.no_signing:
2061    SignOutput(temp_zip_file.name, args[1])
2062    temp_zip_file.close()
2063
2064  print "done."
2065
2066
2067if __name__ == '__main__':
2068  try:
2069    common.CloseInheritedPipes()
2070    main(sys.argv[1:])
2071  except common.ExternalError as e:
2072    print
2073    print "   ERROR: %s" % (e,)
2074    print
2075    sys.exit(1)
2076  finally:
2077    common.Cleanup()
2078