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