ota_from_target_files revision 283e2a1e1bae4e21824969a15da6420204633ddd
1eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker#!/usr/bin/env python
2eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker#
3eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker# Copyright (C) 2008 The Android Open Source Project
4eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker#
5eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker# Licensed under the Apache License, Version 2.0 (the "License");
6eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker# you may not use this file except in compliance with the License.
7eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker# You may obtain a copy of the License at
8eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker#
9eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker#      http://www.apache.org/licenses/LICENSE-2.0
10eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker#
11eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker# Unless required by applicable law or agreed to in writing, software
12eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker# distributed under the License is distributed on an "AS IS" BASIS,
13eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker# See the License for the specific language governing permissions and
15eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker# limitations under the License.
16eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
17eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker"""
18eef3944eb3673329b5e89cf188ac592805a0b08dDoug ZongkerGiven a target-files zipfile, produces an OTA package that installs
19eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerthat build.  An incremental OTA is produced if -i is given, otherwise
20eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkera full OTA is produced.
21eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
22eef3944eb3673329b5e89cf188ac592805a0b08dDoug ZongkerUsage:  ota_from_target_files [flags] input_target_files output_ota_package
23eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
24eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  -b  (--board_config)  <file>
25fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker      Deprecated.
26eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
27eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  -k  (--package_key)  <key>
28eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      Key to use to sign the package (default is
29eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      "build/target/product/security/testkey").
30eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
31eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  -i  (--incremental_from)  <file>
32eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      Generate an incremental OTA using the given target-files zip as
33eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      the starting build.
34eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
35dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker  -w  (--wipe_user_data)
36dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker      Generate an OTA package that will wipe the user data partition
37dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker      when installed.
38dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker
39962069ce59c85949d147874df2728a5ffd9193beDoug Zongker  -n  (--no_prereq)
40962069ce59c85949d147874df2728a5ffd9193beDoug Zongker      Omit the timestamp prereq check normally included at the top of
41962069ce59c85949d147874df2728a5ffd9193beDoug Zongker      the build scripts (used for developer OTA packages which
42962069ce59c85949d147874df2728a5ffd9193beDoug Zongker      legitimately need to go back and forth).
43962069ce59c85949d147874df2728a5ffd9193beDoug Zongker
441c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker  -e  (--extra_script)  <file>
451c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker      Insert the contents of file at the end of the update script.
461c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker
47c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  -m  (--script_mode)  <mode>
48c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      Specify 'amend' or 'edify' scripts, or 'auto' to pick
49c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      automatically (this is the default).
50c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker
51eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker"""
52eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
53eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport sys
54eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
55eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerif sys.hexversion < 0x02040000:
56eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  print >> sys.stderr, "Python 2.4 or newer is required."
57eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  sys.exit(1)
58eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
59eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport copy
60c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongkerimport errno
61eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport os
62eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport re
63eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport sha
64eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport subprocess
65eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport tempfile
66761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongkerimport threading
67eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport time
68eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport zipfile
69eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
70eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport common
71c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongkerimport amend_generator
72c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongkerimport edify_generator
73030614740c1a22e51c6513058852f9ab368fdf5dDoug Zongkerimport both_generator
74eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
75eef3944eb3673329b5e89cf188ac592805a0b08dDoug ZongkerOPTIONS = common.OPTIONS
76eef3944eb3673329b5e89cf188ac592805a0b08dDoug ZongkerOPTIONS.package_key = "build/target/product/security/testkey"
77eef3944eb3673329b5e89cf188ac592805a0b08dDoug ZongkerOPTIONS.incremental_source = None
78eef3944eb3673329b5e89cf188ac592805a0b08dDoug ZongkerOPTIONS.require_verbatim = set()
79eef3944eb3673329b5e89cf188ac592805a0b08dDoug ZongkerOPTIONS.prohibit_verbatim = set(("system/build.prop",))
80eef3944eb3673329b5e89cf188ac592805a0b08dDoug ZongkerOPTIONS.patch_threshold = 0.95
81dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug ZongkerOPTIONS.wipe_user_data = False
82962069ce59c85949d147874df2728a5ffd9193beDoug ZongkerOPTIONS.omit_prereq = False
831c390a2aa97127ef8af8b0df1d4028f501fdce64Doug ZongkerOPTIONS.extra_script = None
84c494d7cee85d980647ca915ea64355b71fe817ebDoug ZongkerOPTIONS.script_mode = 'auto'
85761e642d54eec743699c6c2ce1ea587853d08f33Doug ZongkerOPTIONS.worker_threads = 3
86eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
87eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef MostPopularKey(d, default):
88eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  """Given a dict, return the key corresponding to the largest
89eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  value.  Returns 'default' if the dict is empty."""
90eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  x = [(v, k) for (k, v) in d.iteritems()]
91eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if not x: return default
92eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  x.sort()
93eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  return x[-1][1]
94eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
95eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
96eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef IsSymlink(info):
97eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  """Return true if the zipfile.ZipInfo object passed in represents a
98eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  symlink."""
99eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  return (info.external_attr >> 16) == 0120777
100eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
101eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
102eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
103eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerclass Item:
104eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  """Items represent the metadata (user, group, mode) of files and
105eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  directories in the system image."""
106eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  ITEMS = {}
107eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  def __init__(self, name, dir=False):
108eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.name = name
109eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.uid = None
110eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.gid = None
111eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.mode = None
112eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.dir = dir
113eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
114eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if name:
115eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      self.parent = Item.Get(os.path.dirname(name), dir=True)
116eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      self.parent.children.append(self)
117eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    else:
118eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      self.parent = None
119eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if dir:
120eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      self.children = []
121eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
122eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  def Dump(self, indent=0):
123eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if self.uid is not None:
124eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      print "%s%s %d %d %o" % ("  "*indent, self.name, self.uid, self.gid, self.mode)
125eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    else:
126eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      print "%s%s %s %s %s" % ("  "*indent, self.name, self.uid, self.gid, self.mode)
127eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if self.dir:
128eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      print "%s%s" % ("  "*indent, self.descendants)
129eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      print "%s%s" % ("  "*indent, self.best_subtree)
130eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      for i in self.children:
131eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        i.Dump(indent=indent+1)
132eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
133eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  @classmethod
134eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  def Get(cls, name, dir=False):
135eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if name not in cls.ITEMS:
136eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      cls.ITEMS[name] = Item(name, dir=dir)
137eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    return cls.ITEMS[name]
138eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
139eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  @classmethod
140283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker  def GetMetadata(cls, input_zip):
141283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker
142283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    try:
143283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      # See if the target_files contains a record of what the uid,
144283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      # gid, and mode is supposed to be.
145283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      output = input_zip.read("META/filesystem_config.txt")
146283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    except KeyError:
147283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      # Run the external 'fs_config' program to determine the desired
148283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      # uid, gid, and mode for every Item object.  Note this uses the
149283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      # one in the client now, which might not be the same as the one
150283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      # used when this target_files was built.
151283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      p = common.Run(["fs_config"], stdin=subprocess.PIPE,
152283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker                     stdout=subprocess.PIPE, stderr=subprocess.PIPE)
153283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      suffix = { False: "", True: "/" }
154283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      input = "".join(["%s%s\n" % (i.name, suffix[i.dir])
155283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker                       for i in cls.ITEMS.itervalues() if i.name])
156283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      output2, error = p.communicate(input)
157283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      assert not error
158eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
159eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    for line in output.split("\n"):
160eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if not line: continue
161eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      name, uid, gid, mode = line.split()
162283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      i = cls.ITEMS.get(name, None)
163283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      if i is not None:
164283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker        i.uid = int(uid)
165283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker        i.gid = int(gid)
166283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker        i.mode = int(mode, 8)
167283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker        if i.dir:
168283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker          i.children.sort(key=lambda i: i.name)
169283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker
170283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    # set metadata for the files generated by this script.
171283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    i = cls.ITEMS.get("system/recovery-from-boot.p", None)
172283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    if i: i.uid, i.gid, i.mode = 0, 0, 0644
173283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
174283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    if i: i.uid, i.gid, i.mode = 0, 0, 0544
175eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
176eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  def CountChildMetadata(self):
177eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    """Count up the (uid, gid, mode) tuples for all children and
178eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    determine the best strategy for using set_perm_recursive and
179eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    set_perm to correctly chown/chmod all the files to their desired
180eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    values.  Recursively calls itself for all descendants.
181eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
182eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    Returns a dict of {(uid, gid, dmode, fmode): count} counting up
183eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    all descendants of this node.  (dmode or fmode may be None.)  Also
184eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    sets the best_subtree of each directory Item to the (uid, gid,
185eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    dmode, fmode) tuple that will match the most descendants of that
186eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    Item.
187eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    """
188eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
189eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    assert self.dir
190eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    d = self.descendants = {(self.uid, self.gid, self.mode, None): 1}
191eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    for i in self.children:
192eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if i.dir:
193eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        for k, v in i.CountChildMetadata().iteritems():
194eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          d[k] = d.get(k, 0) + v
195eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      else:
196eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        k = (i.uid, i.gid, None, i.mode)
197eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        d[k] = d.get(k, 0) + 1
198eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
199eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    # Find the (uid, gid, dmode, fmode) tuple that matches the most
200eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    # descendants.
201eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
202eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    # First, find the (uid, gid) pair that matches the most
203eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    # descendants.
204eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    ug = {}
205eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    for (uid, gid, _, _), count in d.iteritems():
206eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      ug[(uid, gid)] = ug.get((uid, gid), 0) + count
207eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    ug = MostPopularKey(ug, (0, 0))
208eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
209eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    # Now find the dmode and fmode that match the most descendants
210eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    # with that (uid, gid), and choose those.
211eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    best_dmode = (0, 0755)
212eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    best_fmode = (0, 0644)
213eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    for k, count in d.iteritems():
214eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if k[:2] != ug: continue
215eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
216eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
217eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.best_subtree = ug + (best_dmode[1], best_fmode[1])
218eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
219eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    return d
220eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
221c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  def SetPermissions(self, script):
222eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    """Append set_perm/set_perm_recursive commands to 'script' to
223eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    set all permissions, users, and groups for the tree of files
224c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    rooted at 'self'."""
225eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
226eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.CountChildMetadata()
227eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
228eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    def recurse(item, current):
229eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      # current is the (uid, gid, dmode, fmode) tuple that the current
230eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      # item (and all its children) have already been set to.  We only
231eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      # need to issue set_perm/set_perm_recursive commands if we're
232eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      # supposed to be something different.
233eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if item.dir:
234eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        if current != item.best_subtree:
235c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker          script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
236eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          current = item.best_subtree
237eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
238eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        if item.uid != current[0] or item.gid != current[1] or \
239eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker           item.mode != current[2]:
240c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker          script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
241eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
242eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        for i in item.children:
243eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          recurse(i, current)
244eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      else:
245eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        if item.uid != current[0] or item.gid != current[1] or \
246eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker               item.mode != current[3]:
247c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker          script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
248eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
249eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    recurse(self, (-1, -1, -1, -1))
250eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
251eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
252eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef CopySystemFiles(input_zip, output_zip=None,
253eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker                    substitute=None):
254eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  """Copies files underneath system/ in the input zip to the output
255eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  zip.  Populates the Item class with their metadata, and returns a
256eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  list of symlinks.  output_zip may be None, in which case the copy is
257eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  skipped (but the other side effects still happen).  substitute is an
258eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  optional dict of {output filename: contents} to be output instead of
259eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  certain input files.
260eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  """
261eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
262eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  symlinks = []
263eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
264eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  for info in input_zip.infolist():
265eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if info.filename.startswith("SYSTEM/"):
266eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      basefilename = info.filename[7:]
267eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if IsSymlink(info):
268eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        symlinks.append((input_zip.read(info.filename),
269c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                         "/system/" + basefilename))
270eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      else:
271eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        info2 = copy.copy(info)
272eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        fn = info2.filename = "system/" + basefilename
273eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        if substitute and fn in substitute and substitute[fn] is None:
274eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          continue
275eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        if output_zip is not None:
276eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          if substitute and fn in substitute:
277eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker            data = substitute[fn]
278eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          else:
279eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker            data = input_zip.read(info.filename)
280eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          output_zip.writestr(info2, data)
281eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        if fn.endswith("/"):
282eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          Item.Get(fn[:-1], dir=True)
283eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        else:
284eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          Item.Get(fn, dir=False)
285eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
286eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  symlinks.sort()
287eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  return symlinks
288eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
289eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
290eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef SignOutput(temp_zip_name, output_zip_name):
291eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
292eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  pw = key_passwords[OPTIONS.package_key]
293eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
294951495fc4802a3603f654c02c7acceda4859f5e1Doug Zongker  common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
295951495fc4802a3603f654c02c7acceda4859f5e1Doug Zongker                  whole_file=True)
296eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
297eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
298c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongkerdef AppendAssertions(script, input_zip):
299eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  device = GetBuildProp("ro.product.device", input_zip)
300c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.AssertDevice(device)
301eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
302eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
30373ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongkerdef MakeRecoveryPatch(output_zip, recovery_img, boot_img):
30473ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  """Generate a binary patch that creates the recovery image starting
30573ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  with the boot image.  (Most of the space in these images is just the
30673ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  kernel, which is identical for the two, so the resulting patch
30773ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  should be efficient.)  Add it to the output zip, along with a shell
30873ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  script that is run from init.rc on first boot to actually do the
30973ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  patching and install the new recovery image.
31073ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker
31173ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  recovery_img and boot_img should be File objects for the
31273ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  corresponding images.
31373ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker
31473ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  Returns an Item for the shell script, which must be made
31573ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  executable.
31673ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  """
31773ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker
318761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  d = Difference(recovery_img, boot_img)
319761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  _, _, patch = d.ComputePatch()
320cfd7db6d8494c7d3169a4eac0dc63737a24ff1d1Doug Zongker  common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
32173ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  Item.Get("system/recovery-from-boot.p", dir=False)
32273ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker
32373ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  # Images with different content will have a different first page, so
32473ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  # we check to see if this recovery has already been installed by
32573ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  # testing just the first 2k.
32673ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  HEADER_SIZE = 2048
32773ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  header_sha1 = sha.sha(recovery_img.data[:HEADER_SIZE]).hexdigest()
32873ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  sh = """#!/system/bin/sh
32973ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongkerif ! applypatch -c MTD:recovery:%(header_size)d:%(header_sha1)s; then
33073ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  log -t recovery "Installing new recovery image"
33173ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  applypatch MTD:boot:%(boot_size)d:%(boot_sha1)s MTD:recovery %(recovery_sha1)s %(recovery_size)d %(boot_sha1)s:/system/recovery-from-boot.p
33273ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongkerelse
33373ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  log -t recovery "Recovery image already installed"
33473ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongkerfi
33573ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker""" % { 'boot_size': boot_img.size,
33673ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker        'boot_sha1': boot_img.sha1,
33773ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker        'header_size': HEADER_SIZE,
33873ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker        'header_sha1': header_sha1,
33973ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker        'recovery_size': recovery_img.size,
34073ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker        'recovery_sha1': recovery_img.sha1 }
341cfd7db6d8494c7d3169a4eac0dc63737a24ff1d1Doug Zongker  common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
34273ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  return Item.Get("system/etc/install-recovery.sh", dir=False)
34373ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker
34473ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker
345eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef WriteFullOTAPackage(input_zip, output_zip):
346030614740c1a22e51c6513058852f9ab368fdf5dDoug Zongker  if OPTIONS.script_mode == "auto":
347030614740c1a22e51c6513058852f9ab368fdf5dDoug Zongker    script = both_generator.BothGenerator(2)
348030614740c1a22e51c6513058852f9ab368fdf5dDoug Zongker  elif OPTIONS.script_mode == "amend":
349c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script = amend_generator.AmendGenerator()
350c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  else:
351c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    # TODO: how to determine this?  We don't know what version it will
352c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    # be installed on top of.  For now, we expect the API just won't
353c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    # change very often.
354030614740c1a22e51c6513058852f9ab368fdf5dDoug Zongker    script = edify_generator.EdifyGenerator(2)
355eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
35605d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  device_specific = common.DeviceSpecificParams(
35705d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker      input_zip=input_zip,
35814833605d26bf970cd5335c02af4354b68d93348Doug Zongker      input_version=GetRecoveryAPIVersion(input_zip),
35905d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker      output_zip=output_zip,
36005d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker      script=script,
36105d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker      input_tmp=OPTIONS.input_tmp)
36205d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker
363962069ce59c85949d147874df2728a5ffd9193beDoug Zongker  if not OPTIONS.omit_prereq:
364962069ce59c85949d147874df2728a5ffd9193beDoug Zongker    ts = GetBuildProp("ro.build.date.utc", input_zip)
365c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.AssertOlderBuild(ts)
366eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
367eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  AppendAssertions(script, input_zip)
36805d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  device_specific.FullOTA_Assertions()
369171f1cde104891840b0c3c271935fae5433f1b25Doug Zongker
370c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.ShowProgress(0.5, 0)
371eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
372dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker  if OPTIONS.wipe_user_data:
373c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.FormatPartition("userdata")
374dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker
375c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.FormatPartition("system")
376c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.Mount("MTD", "system", "/system")
377cfd7db6d8494c7d3169a4eac0dc63737a24ff1d1Doug Zongker  script.UnpackPackageDir("recovery", "/system")
378c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.UnpackPackageDir("system", "/system")
379eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
380eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  symlinks = CopySystemFiles(input_zip, output_zip)
381c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.MakeSymlinks(symlinks)
382eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
38373ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  boot_img = File("boot.img", common.BuildBootableImage(
38473ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker      os.path.join(OPTIONS.input_tmp, "BOOT")))
38573ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  recovery_img = File("recovery.img", common.BuildBootableImage(
38673ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker      os.path.join(OPTIONS.input_tmp, "RECOVERY")))
387283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker  MakeRecoveryPatch(output_zip, recovery_img, boot_img)
38873ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker
389283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker  Item.GetMetadata(input_zip)
39073ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  Item.Get("system").SetPermissions(script)
391eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
39273ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  common.CheckSize(boot_img.data, "boot.img")
39373ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
394c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.ShowProgress(0.2, 0)
395c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker
396c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.ShowProgress(0.2, 10)
39705d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  script.WriteRawImage("boot", "boot.img")
39805d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker
39905d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  script.ShowProgress(0.1, 0)
40005d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  device_specific.FullOTA_InstallEnd()
401eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
4021c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker  if OPTIONS.extra_script is not None:
403c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.AppendExtra(OPTIONS.extra_script)
4041c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker
40514833605d26bf970cd5335c02af4354b68d93348Doug Zongker  script.UnmountAll()
406c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.AddToZip(input_zip, output_zip)
407eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
408eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
409eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerclass File(object):
410eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  def __init__(self, name, data):
411eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.name = name
412eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.data = data
413eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.size = len(data)
414eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.sha1 = sha.sha(data).hexdigest()
415eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
416eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  def WriteToTemp(self):
417eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    t = tempfile.NamedTemporaryFile()
418eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    t.write(self.data)
419eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    t.flush()
420eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    return t
421eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
422eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  def AddToZip(self, z):
423048e7ca15f6391681490ce564bc71194adf146aaDoug Zongker    common.ZipWriteStr(z, self.name, self.data)
424eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
425eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
426eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef LoadSystemFiles(z):
427eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  """Load all the files from SYSTEM/... in a given target-files
428eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  ZipFile, and return a dict of {filename: File object}."""
429eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  out = {}
430eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  for info in z.infolist():
431eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
432eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      fn = "system/" + info.filename[7:]
433eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      data = z.read(info.filename)
434eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      out[fn] = File(fn, data)
435eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  return out
436eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
437eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
438761e642d54eec743699c6c2ce1ea587853d08f33Doug ZongkerDIFF_PROGRAM_BY_EXT = {
439761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    ".gz" : "imgdiff",
440761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    ".zip" : ["imgdiff", "-z"],
441761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    ".jar" : ["imgdiff", "-z"],
442761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    ".apk" : ["imgdiff", "-z"],
443761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    ".img" : "imgdiff",
444761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    }
445eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
446eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
447761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongkerclass Difference(object):
448761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  def __init__(self, tf, sf):
449761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    self.tf = tf
450761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    self.sf = sf
451761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    self.patch = None
452eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
453761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  def ComputePatch(self):
454761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    """Compute the patch (as a string of data) needed to turn sf into
455761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    tf.  Returns the same tuple as GetPatch()."""
456761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
457761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    tf = self.tf
458761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    sf = self.sf
459761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
460761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    ext = os.path.splitext(tf.name)[1]
461761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    diff_program = DIFF_PROGRAM_BY_EXT.get(ext, "bsdiff")
462761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
463761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    ttemp = tf.WriteToTemp()
464761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    stemp = sf.WriteToTemp()
465761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
466761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    ext = os.path.splitext(tf.name)[1]
467761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
468761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    try:
469761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      ptemp = tempfile.NamedTemporaryFile()
470761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      if isinstance(diff_program, list):
471761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker        cmd = copy.copy(diff_program)
472761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      else:
473761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker        cmd = [diff_program]
474761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      cmd.append(stemp.name)
475761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      cmd.append(ttemp.name)
476761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      cmd.append(ptemp.name)
477761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      p = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
478761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      _, err = p.communicate()
479761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      if err or p.returncode != 0:
480761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker        print "WARNING: failure running %s:\n%s\n" % (diff_program, err)
481761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker        return None
482761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      diff = ptemp.read()
483761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    finally:
484761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      ptemp.close()
485761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      stemp.close()
486761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      ttemp.close()
487761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
488761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    self.patch = diff
489761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    return self.tf, self.sf, self.patch
490761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
491761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
492761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  def GetPatch(self):
493761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    """Return a tuple (target_file, source_file, patch_data).
494761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    patch_data may be None if ComputePatch hasn't been called, or if
495761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    computing the patch failed."""
496761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    return self.tf, self.sf, self.patch
497761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
498761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
499761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongkerdef ComputeDifferences(diffs):
500761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  """Call ComputePatch on all the Difference objects in 'diffs'."""
501761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  print len(diffs), "diffs to compute"
502761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
503761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  # Do the largest files first, to try and reduce the long-pole effect.
504761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  by_size = [(i.tf.size, i) for i in diffs]
505761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  by_size.sort(reverse=True)
506761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  by_size = [i[1] for i in by_size]
507761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
508761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  lock = threading.Lock()
509761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  diff_iter = iter(by_size)   # accessed under lock
510761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
511761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  def worker():
512761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    try:
513761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      lock.acquire()
514761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      for d in diff_iter:
515761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker        lock.release()
516761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker        start = time.time()
517761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker        d.ComputePatch()
518761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker        dur = time.time() - start
519761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker        lock.acquire()
520761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
521761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker        tf, sf, patch = d.GetPatch()
522761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker        if sf.name == tf.name:
523761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker          name = tf.name
524761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker        else:
525761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker          name = "%s (%s)" % (tf.name, sf.name)
526761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker        if patch is None:
527761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker          print "patching failed!                                  %s" % (name,)
528761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker        else:
529761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker          print "%8.2f sec %8d / %8d bytes (%6.2f%%) %s" % (
530761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker              dur, len(patch), tf.size, 100.0 * len(patch) / tf.size, name)
531761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      lock.release()
532481c4e650fe6cdb9c63ad04b35d221a2856ca48dDoug Zongker    except Exception, e:
533761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      print e
534761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      raise
535761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
536761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  # start worker threads; wait for them all to finish.
537761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  threads = [threading.Thread(target=worker)
538761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker             for i in range(OPTIONS.worker_threads)]
539761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  for th in threads:
540761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    th.start()
541761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  while threads:
542761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    threads.pop().join()
543eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
544eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
545eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef GetBuildProp(property, z):
546eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  """Return the fingerprint of the build of a given target-files
547eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  ZipFile object."""
548eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  bp = z.read("SYSTEM/build.prop")
549eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if not property:
550eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    return bp
551eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  m = re.search(re.escape(property) + r"=(.*)\n", bp)
552eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if not m:
5539fc74c7823182c4121f32114a7f83ae3fa7e4346Doug Zongker    raise common.ExternalError("couldn't find %s in build.prop" % (property,))
554eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  return m.group(1).strip()
555eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
556eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
557c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongkerdef GetRecoveryAPIVersion(zip):
558c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  """Returns the version of the recovery API.  Version 0 is the older
559c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  amend code (no separate binary)."""
560c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  try:
561c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    version = zip.read("META/recovery-api-version.txt")
562c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    return int(version)
563c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  except KeyError:
564c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    try:
565c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      # version one didn't have the recovery-api-version.txt file, but
566c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      # it did include an updater binary.
567c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      zip.getinfo("OTA/bin/updater")
568c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      return 1
569c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    except KeyError:
570c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      return 0
571c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker
57215604b84e246514da6c9721266919003f734380bDoug Zongker
573eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
574c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  source_version = GetRecoveryAPIVersion(source_zip)
57514833605d26bf970cd5335c02af4354b68d93348Doug Zongker  target_version = GetRecoveryAPIVersion(target_zip)
576c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker
577c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  if OPTIONS.script_mode == 'amend':
578c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script = amend_generator.AmendGenerator()
579c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  elif OPTIONS.script_mode == 'edify':
580c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    if source_version == 0:
581c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      print ("WARNING: generating edify script for a source that "
582c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker             "can't install it.")
583c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script = edify_generator.EdifyGenerator(source_version)
584c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  elif OPTIONS.script_mode == 'auto':
585c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    if source_version > 0:
586c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      script = edify_generator.EdifyGenerator(source_version)
587c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    else:
588c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      script = amend_generator.AmendGenerator()
589c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  else:
590c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
591eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
59205d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  device_specific = common.DeviceSpecificParams(
59305d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker      source_zip=source_zip,
59414833605d26bf970cd5335c02af4354b68d93348Doug Zongker      source_version=source_version,
59505d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker      target_zip=target_zip,
59614833605d26bf970cd5335c02af4354b68d93348Doug Zongker      target_version=target_version,
59705d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker      output_zip=output_zip,
59805d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker      script=script)
59905d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker
600eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  print "Loading target..."
601eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  target_data = LoadSystemFiles(target_zip)
602eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  print "Loading source..."
603eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  source_data = LoadSystemFiles(source_zip)
604eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
605eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  verbatim_targets = []
606eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  patch_list = []
607761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  diffs = []
608eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  largest_source_size = 0
609eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  for fn in sorted(target_data.keys()):
610eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    tf = target_data[fn]
611761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    assert fn == tf.name
612eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    sf = source_data.get(fn, None)
613eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
614eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if sf is None or fn in OPTIONS.require_verbatim:
615eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      # This file should be included verbatim
616eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if fn in OPTIONS.prohibit_verbatim:
6179fc74c7823182c4121f32114a7f83ae3fa7e4346Doug Zongker        raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
618eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      print "send", fn, "verbatim"
619eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      tf.AddToZip(output_zip)
620eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      verbatim_targets.append((fn, tf.size))
621eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    elif tf.sha1 != sf.sha1:
622eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      # File is different; consider sending as a patch
623761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      diffs.append(Difference(tf, sf))
624eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    else:
625eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      # Target file identical to source.
626eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      pass
627eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
628761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  ComputeDifferences(diffs)
629761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
630761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  for diff in diffs:
631761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    tf, sf, d = diff.GetPatch()
632761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
633761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      # patch is almost as big as the file; don't bother patching
634761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      tf.AddToZip(output_zip)
635761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      verbatim_targets.append((tf.name, tf.size))
636761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    else:
637761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      common.ZipWriteStr(output_zip, "patch/" + tf.name + ".p", d)
6385a48209541d5eed602bfb8e2c4ff51e31443daf2Doug Zongker      patch_list.append((tf.name, tf, sf, tf.size, sha.sha(d).hexdigest()))
639761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      largest_source_size = max(largest_source_size, sf.size)
640eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
641eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
642eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  target_fp = GetBuildProp("ro.build.fingerprint", target_zip)
643eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
644c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.Mount("MTD", "system", "/system")
645c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.AssertSomeFingerprint(source_fp, target_fp)
646eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
6475da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker  source_boot = File("/tmp/boot.img",
6485da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker                     common.BuildBootableImage(
6495da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker      os.path.join(OPTIONS.source_tmp, "BOOT")))
6505da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker  target_boot = File("/tmp/boot.img",
6515da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker                     common.BuildBootableImage(
6525da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker      os.path.join(OPTIONS.target_tmp, "BOOT")))
6535da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker  updating_boot = (source_boot.data != target_boot.data)
654eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
655f6a8bada5f0966762eadaec96de6430d0cd577e3Doug Zongker  source_recovery = File("system/recovery.img",
656f6a8bada5f0966762eadaec96de6430d0cd577e3Doug Zongker                         common.BuildBootableImage(
657f6a8bada5f0966762eadaec96de6430d0cd577e3Doug Zongker      os.path.join(OPTIONS.source_tmp, "RECOVERY")))
658f6a8bada5f0966762eadaec96de6430d0cd577e3Doug Zongker  target_recovery = File("system/recovery.img",
659f6a8bada5f0966762eadaec96de6430d0cd577e3Doug Zongker                         common.BuildBootableImage(
660f6a8bada5f0966762eadaec96de6430d0cd577e3Doug Zongker      os.path.join(OPTIONS.target_tmp, "RECOVERY")))
661f6a8bada5f0966762eadaec96de6430d0cd577e3Doug Zongker  updating_recovery = (source_recovery.data != target_recovery.data)
662eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
663881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  # Here's how we divide up the progress bar:
664881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  #  0.1 for verifying the start state (PatchCheck calls)
665881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  #  0.8 for applying patches (ApplyPatch calls)
666881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  #  0.1 for unpacking verbatim files, symlinking, and doing the
667881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  #      device-specific commands.
668eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
669eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  AppendAssertions(script, target_zip)
67005d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  device_specific.IncrementalOTA_Assertions()
671eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
672c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.Print("Verifying current system...")
673c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker
674881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  script.ShowProgress(0.1, 0)
675881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  total_verify_size = float(sum([i[2].size for i in patch_list]) + 1)
676881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  if updating_boot:
677881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    total_verify_size += source_boot.size
678881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  so_far = 0
679c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker
6805a48209541d5eed602bfb8e2c4ff51e31443daf2Doug Zongker  for fn, tf, sf, size, patch_sha in patch_list:
681c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
682881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    so_far += sf.size
683881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    script.SetProgress(so_far / total_verify_size)
684eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
6855da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker  if updating_boot:
686761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    d = Difference(target_boot, source_boot)
687761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    _, _, d = d.ComputePatch()
6885da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker    print "boot      target: %d  source: %d  diff: %d" % (
6895da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker        target_boot.size, source_boot.size, len(d))
6905da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker
691048e7ca15f6391681490ce564bc71194adf146aaDoug Zongker    common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
6925da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker
693c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.PatchCheck("MTD:boot:%d:%s:%d:%s" %
694c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                      (source_boot.size, source_boot.sha1,
695c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                       target_boot.size, target_boot.sha1))
696881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    so_far += source_boot.size
697881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    script.SetProgress(so_far / total_verify_size)
6985da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker
6995da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker  if patch_list or updating_recovery or updating_boot:
700c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.CacheFreeSpaceCheck(largest_source_size)
7015a48209541d5eed602bfb8e2c4ff51e31443daf2Doug Zongker
70205d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  device_specific.IncrementalOTA_VerifyEnd()
70305d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker
704c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.Comment("---- start making changes here ----")
705eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
706dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker  if OPTIONS.wipe_user_data:
707c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.Print("Erasing user data...")
708c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.FormatPartition("userdata")
709dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker
710c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.Print("Removing unneeded files...")
7110f3298a497e32f6c2325a0071124a62d031fae6fDoug Zongker  script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
7120f3298a497e32f6c2325a0071124a62d031fae6fDoug Zongker                     ["/"+i for i in sorted(source_data)
7133b949f07259ee8d67b4454627aceab5e6f44bd39Doug Zongker                            if i not in target_data] +
7143b949f07259ee8d67b4454627aceab5e6f44bd39Doug Zongker                     ["/system/recovery.img"])
715eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
716881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  script.ShowProgress(0.8, 0)
717881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
718881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  if updating_boot:
719881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    total_patch_size += target_boot.size
720881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  so_far = 0
721881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker
722881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  script.Print("Patching system files...")
7235a48209541d5eed602bfb8e2c4ff51e31443daf2Doug Zongker  for fn, tf, sf, size, _ in patch_list:
724c8d446bcde877ec94f8e68dd5af68fe34eb1b1f9Doug Zongker    script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
725881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    so_far += tf.size
726881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    script.SetProgress(so_far / total_patch_size)
727881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker
728eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if updating_boot:
7295da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker    # Produce the boot image by applying a patch to the current
7305da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker    # contents of the boot partition, and write it back to the
7315da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker    # partition.
732c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.Print("Patching boot image...")
733c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.ApplyPatch("MTD:boot:%d:%s:%d:%s"
734c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                      % (source_boot.size, source_boot.sha1,
735c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                         target_boot.size, target_boot.sha1),
736c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                      "-",
737c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                      target_boot.size, target_boot.sha1,
738c8d446bcde877ec94f8e68dd5af68fe34eb1b1f9Doug Zongker                      source_boot.sha1, "patch/boot.img.p")
739881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    so_far += target_boot.size
740881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    script.SetProgress(so_far / total_patch_size)
741eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    print "boot image changed; including."
742eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  else:
743eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    print "boot image unchanged; skipping."
744eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
745eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if updating_recovery:
74673ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # Is it better to generate recovery as a patch from the current
74773ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # boot image, or from the previous recovery image?  For large
74873ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # updates with significant kernel changes, probably the former.
74973ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # For small updates where the kernel hasn't changed, almost
75073ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # certainly the latter.  We pick the first option.  Future
75173ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # complicated schemes may let us effectively use both.
75273ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    #
75373ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # A wacky possibility: as long as there is room in the boot
75473ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # partition, include the binaries and image files from recovery in
75573ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # the boot image (though not in the ramdisk) so they can be used
75673ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # as fodder for constructing the recovery image.
757283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    MakeRecoveryPatch(output_zip, target_recovery, target_boot)
75842265390d993664e7797abc12d7e6bd1c2a6dc6bDoug Zongker    script.DeleteFiles(["/system/recovery-from-boot.p",
75942265390d993664e7797abc12d7e6bd1c2a6dc6bDoug Zongker                        "/system/etc/install-recovery.sh"])
76073ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    print "recovery image changed; including as patch from boot."
761eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  else:
762eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    print "recovery image unchanged; skipping."
763eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
764881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  script.ShowProgress(0.1, 10)
765eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
766eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  target_symlinks = CopySystemFiles(target_zip, None)
767eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
768eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
769c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  temp_script = script.MakeTemporary()
770283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker  Item.GetMetadata(target_zip)
77173ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  Item.Get("system").SetPermissions(temp_script)
772eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
773eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # Note that this call will mess up the tree of Items, so make sure
774eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # we're done with it.
775eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  source_symlinks = CopySystemFiles(source_zip, None)
776eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
777eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
778eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # Delete all the symlinks in source that aren't in target.  This
779eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # needs to happen before verbatim files are unpacked, in case a
780eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # symlink in the source is replaced by a real file in the target.
781eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  to_delete = []
782eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  for dest, link in source_symlinks:
783eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if link not in target_symlinks_d:
784eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      to_delete.append(link)
785c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.DeleteFiles(to_delete)
786eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
787eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if verbatim_targets:
788c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.Print("Unpacking new files...")
789c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.UnpackPackageDir("system", "/system")
790c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker
79142265390d993664e7797abc12d7e6bd1c2a6dc6bDoug Zongker  if updating_recovery:
79242265390d993664e7797abc12d7e6bd1c2a6dc6bDoug Zongker    script.Print("Unpacking new recovery...")
79342265390d993664e7797abc12d7e6bd1c2a6dc6bDoug Zongker    script.UnpackPackageDir("recovery", "/system")
79442265390d993664e7797abc12d7e6bd1c2a6dc6bDoug Zongker
79505d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  script.Print("Symlinks and permissions...")
796eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
797eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # Create all the symlinks that don't already exist, or point to
798eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # somewhere different than what we want.  Delete each symlink before
799eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # creating it, since the 'symlink' command won't overwrite.
800eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  to_create = []
801eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  for dest, link in target_symlinks:
802eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if link in source_symlinks_d:
803eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if dest != source_symlinks_d[link]:
804eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        to_create.append((dest, link))
805eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    else:
806eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      to_create.append((dest, link))
807c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.DeleteFiles([i[1] for i in to_create])
808c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.MakeSymlinks(to_create)
809eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
810eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # Now that the symlinks are created, we can set all the
811eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # permissions.
812c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.AppendScript(temp_script)
813eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
814881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  # Do device-specific installation (eg, write radio image).
81505d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  device_specific.IncrementalOTA_InstallEnd()
81605d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker
8171c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker  if OPTIONS.extra_script is not None:
818c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    scirpt.AppendExtra(OPTIONS.extra_script)
8191c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker
820c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.AddToZip(target_zip, output_zip)
821eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
822eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
823eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef main(argv):
824eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
825eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  def option_handler(o, a):
826eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if o in ("-b", "--board_config"):
827fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker      pass   # deprecated
828eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    elif o in ("-k", "--package_key"):
829eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      OPTIONS.package_key = a
830eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    elif o in ("-i", "--incremental_from"):
831eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      OPTIONS.incremental_source = a
832dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker    elif o in ("-w", "--wipe_user_data"):
833dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker      OPTIONS.wipe_user_data = True
834962069ce59c85949d147874df2728a5ffd9193beDoug Zongker    elif o in ("-n", "--no_prereq"):
835962069ce59c85949d147874df2728a5ffd9193beDoug Zongker      OPTIONS.omit_prereq = True
8361c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker    elif o in ("-e", "--extra_script"):
8371c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker      OPTIONS.extra_script = a
838c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    elif o in ("-m", "--script_mode"):
839c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      OPTIONS.script_mode = a
840761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    elif o in ("--worker_threads"):
841761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      OPTIONS.worker_threads = int(a)
842eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    else:
843eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      return False
844dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker    return True
845eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
846eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  args = common.ParseOptions(argv, __doc__,
847c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                             extra_opts="b:k:i:d:wne:m:",
848eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker                             extra_long_opts=["board_config=",
849eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker                                              "package_key=",
850dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker                                              "incremental_from=",
851962069ce59c85949d147874df2728a5ffd9193beDoug Zongker                                              "wipe_user_data",
8521c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker                                              "no_prereq",
853c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                                              "extra_script=",
854761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker                                              "script_mode=",
855761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker                                              "worker_threads="],
856eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker                             extra_option_handler=option_handler)
857eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
858eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if len(args) != 2:
859eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    common.Usage(__doc__)
860eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    sys.exit(1)
861eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
862c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  if OPTIONS.script_mode not in ("amend", "edify", "auto"):
863c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
864c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker
8651c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker  if OPTIONS.extra_script is not None:
8661c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker    OPTIONS.extra_script = open(OPTIONS.extra_script).read()
8671c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker
868eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  print "unzipping target target-files..."
869eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  OPTIONS.input_tmp = common.UnzipTemp(args[0])
870fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker
871c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker  if OPTIONS.device_specific is None:
872c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker    # look for the device-specific tools extension location in the input
873c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker    try:
874c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker      f = open(os.path.join(OPTIONS.input_tmp, "META", "tool-extensions.txt"))
875c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker      ds = f.read().strip()
876c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker      f.close()
877c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker      if ds:
878c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker        ds = os.path.normpath(ds)
879c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker        print "using device-specific extensions in", ds
880c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker        OPTIONS.device_specific = ds
881c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker    except IOError, e:
882c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker      if e.errno == errno.ENOENT:
883c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker        # nothing specified in the file
884c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker        pass
885c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker      else:
886c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker        raise
887c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker
888fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker  common.LoadMaxSizes()
889fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker  if not OPTIONS.max_image_size:
890fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker    print
891fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker    print "  WARNING:  Failed to load max image sizes; will not enforce"
892fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker    print "  image size limits."
893fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker    print
894fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker
895eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  OPTIONS.target_tmp = OPTIONS.input_tmp
896eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  input_zip = zipfile.ZipFile(args[0], "r")
897eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if OPTIONS.package_key:
898eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    temp_zip_file = tempfile.NamedTemporaryFile()
899eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    output_zip = zipfile.ZipFile(temp_zip_file, "w",
900eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker                                 compression=zipfile.ZIP_DEFLATED)
901eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  else:
902eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    output_zip = zipfile.ZipFile(args[1], "w",
903eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker                                 compression=zipfile.ZIP_DEFLATED)
904eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
905eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if OPTIONS.incremental_source is None:
906eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    WriteFullOTAPackage(input_zip, output_zip)
907eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  else:
908eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    print "unzipping source target-files..."
909eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    OPTIONS.source_tmp = common.UnzipTemp(OPTIONS.incremental_source)
910eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
911eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
912eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
913eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  output_zip.close()
914eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if OPTIONS.package_key:
915eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    SignOutput(temp_zip_file.name, args[1])
916eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    temp_zip_file.close()
917eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
918eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  common.Cleanup()
919eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
920eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  print "done."
921eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
922eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
923eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerif __name__ == '__main__':
924eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  try:
925eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    main(sys.argv[1:])
926eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  except common.ExternalError, e:
927eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    print
928eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    print "   ERROR: %s" % (e,)
929eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    print
930eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    sys.exit(1)
931