ota_from_target_files revision c77a9ad444d49e2ad777678cf5671f0a94f44ffb
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
47eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker"""
48eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
49eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport sys
50eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
51eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerif sys.hexversion < 0x02040000:
52eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  print >> sys.stderr, "Python 2.4 or newer is required."
53eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  sys.exit(1)
54eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
55eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport copy
56c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongkerimport errno
57eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport os
58eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport re
59eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport sha
60eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport subprocess
61eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport tempfile
62eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport time
63eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport zipfile
64eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
65eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerimport common
66c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongkerimport edify_generator
67eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
68eef3944eb3673329b5e89cf188ac592805a0b08dDoug ZongkerOPTIONS = common.OPTIONS
69eef3944eb3673329b5e89cf188ac592805a0b08dDoug ZongkerOPTIONS.package_key = "build/target/product/security/testkey"
70eef3944eb3673329b5e89cf188ac592805a0b08dDoug ZongkerOPTIONS.incremental_source = None
71eef3944eb3673329b5e89cf188ac592805a0b08dDoug ZongkerOPTIONS.require_verbatim = set()
72eef3944eb3673329b5e89cf188ac592805a0b08dDoug ZongkerOPTIONS.prohibit_verbatim = set(("system/build.prop",))
73eef3944eb3673329b5e89cf188ac592805a0b08dDoug ZongkerOPTIONS.patch_threshold = 0.95
74dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug ZongkerOPTIONS.wipe_user_data = False
75962069ce59c85949d147874df2728a5ffd9193beDoug ZongkerOPTIONS.omit_prereq = False
761c390a2aa97127ef8af8b0df1d4028f501fdce64Doug ZongkerOPTIONS.extra_script = None
77761e642d54eec743699c6c2ce1ea587853d08f33Doug ZongkerOPTIONS.worker_threads = 3
78eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
79eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef MostPopularKey(d, default):
80eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  """Given a dict, return the key corresponding to the largest
81eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  value.  Returns 'default' if the dict is empty."""
82eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  x = [(v, k) for (k, v) in d.iteritems()]
83eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if not x: return default
84eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  x.sort()
85eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  return x[-1][1]
86eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
87eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
88eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef IsSymlink(info):
89eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  """Return true if the zipfile.ZipInfo object passed in represents a
90eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  symlink."""
91eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  return (info.external_attr >> 16) == 0120777
92eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
93eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
94eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
95eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerclass Item:
96eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  """Items represent the metadata (user, group, mode) of files and
97eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  directories in the system image."""
98eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  ITEMS = {}
99eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  def __init__(self, name, dir=False):
100eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.name = name
101eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.uid = None
102eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.gid = None
103eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.mode = None
104eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.dir = dir
105eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
106eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if name:
107eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      self.parent = Item.Get(os.path.dirname(name), dir=True)
108eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      self.parent.children.append(self)
109eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    else:
110eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      self.parent = None
111eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if dir:
112eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      self.children = []
113eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
114eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  def Dump(self, indent=0):
115eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if self.uid is not None:
116eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      print "%s%s %d %d %o" % ("  "*indent, self.name, self.uid, self.gid, self.mode)
117eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    else:
118eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      print "%s%s %s %s %s" % ("  "*indent, self.name, self.uid, self.gid, self.mode)
119eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if self.dir:
120eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      print "%s%s" % ("  "*indent, self.descendants)
121eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      print "%s%s" % ("  "*indent, self.best_subtree)
122eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      for i in self.children:
123eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        i.Dump(indent=indent+1)
124eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
125eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  @classmethod
126eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  def Get(cls, name, dir=False):
127eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if name not in cls.ITEMS:
128eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      cls.ITEMS[name] = Item(name, dir=dir)
129eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    return cls.ITEMS[name]
130eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
131eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  @classmethod
132283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker  def GetMetadata(cls, input_zip):
133283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker
134283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    try:
135283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      # See if the target_files contains a record of what the uid,
136283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      # gid, and mode is supposed to be.
137283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      output = input_zip.read("META/filesystem_config.txt")
138283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    except KeyError:
139283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      # Run the external 'fs_config' program to determine the desired
140283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      # uid, gid, and mode for every Item object.  Note this uses the
141283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      # one in the client now, which might not be the same as the one
142283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      # used when this target_files was built.
143283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      p = common.Run(["fs_config"], stdin=subprocess.PIPE,
144283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker                     stdout=subprocess.PIPE, stderr=subprocess.PIPE)
145283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      suffix = { False: "", True: "/" }
146283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      input = "".join(["%s%s\n" % (i.name, suffix[i.dir])
147283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker                       for i in cls.ITEMS.itervalues() if i.name])
1483475d362a771bcbbca23db527c399c0e5a67f576Doug Zongker      output, error = p.communicate(input)
149283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      assert not error
150eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
151eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    for line in output.split("\n"):
152eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if not line: continue
153eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      name, uid, gid, mode = line.split()
154283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      i = cls.ITEMS.get(name, None)
155283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker      if i is not None:
156283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker        i.uid = int(uid)
157283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker        i.gid = int(gid)
158283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker        i.mode = int(mode, 8)
159283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker        if i.dir:
160283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker          i.children.sort(key=lambda i: i.name)
161283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker
162283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    # set metadata for the files generated by this script.
163283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    i = cls.ITEMS.get("system/recovery-from-boot.p", None)
164283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    if i: i.uid, i.gid, i.mode = 0, 0, 0644
165283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
166283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    if i: i.uid, i.gid, i.mode = 0, 0, 0544
167eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
168eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  def CountChildMetadata(self):
169eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    """Count up the (uid, gid, mode) tuples for all children and
170eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    determine the best strategy for using set_perm_recursive and
171eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    set_perm to correctly chown/chmod all the files to their desired
172eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    values.  Recursively calls itself for all descendants.
173eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
174eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    Returns a dict of {(uid, gid, dmode, fmode): count} counting up
175eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    all descendants of this node.  (dmode or fmode may be None.)  Also
176eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    sets the best_subtree of each directory Item to the (uid, gid,
177eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    dmode, fmode) tuple that will match the most descendants of that
178eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    Item.
179eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    """
180eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
181eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    assert self.dir
182eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    d = self.descendants = {(self.uid, self.gid, self.mode, None): 1}
183eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    for i in self.children:
184eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if i.dir:
185eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        for k, v in i.CountChildMetadata().iteritems():
186eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          d[k] = d.get(k, 0) + v
187eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      else:
188eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        k = (i.uid, i.gid, None, i.mode)
189eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        d[k] = d.get(k, 0) + 1
190eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
191eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    # Find the (uid, gid, dmode, fmode) tuple that matches the most
192eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    # descendants.
193eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
194eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    # First, find the (uid, gid) pair that matches the most
195eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    # descendants.
196eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    ug = {}
197eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    for (uid, gid, _, _), count in d.iteritems():
198eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      ug[(uid, gid)] = ug.get((uid, gid), 0) + count
199eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    ug = MostPopularKey(ug, (0, 0))
200eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
201eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    # Now find the dmode and fmode that match the most descendants
202eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    # with that (uid, gid), and choose those.
203eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    best_dmode = (0, 0755)
204eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    best_fmode = (0, 0644)
205eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    for k, count in d.iteritems():
206eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if k[:2] != ug: continue
207eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
208eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
209eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.best_subtree = ug + (best_dmode[1], best_fmode[1])
210eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
211eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    return d
212eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
213c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  def SetPermissions(self, script):
214eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    """Append set_perm/set_perm_recursive commands to 'script' to
215eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    set all permissions, users, and groups for the tree of files
216c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    rooted at 'self'."""
217eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
218eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    self.CountChildMetadata()
219eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
220eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    def recurse(item, current):
221eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      # current is the (uid, gid, dmode, fmode) tuple that the current
222eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      # item (and all its children) have already been set to.  We only
223eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      # need to issue set_perm/set_perm_recursive commands if we're
224eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      # supposed to be something different.
225eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if item.dir:
226eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        if current != item.best_subtree:
227c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker          script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
228eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          current = item.best_subtree
229eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
230eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        if item.uid != current[0] or item.gid != current[1] or \
231eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker           item.mode != current[2]:
232c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker          script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
233eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
234eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        for i in item.children:
235eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          recurse(i, current)
236eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      else:
237eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        if item.uid != current[0] or item.gid != current[1] or \
238eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker               item.mode != current[3]:
239c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker          script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
240eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
241eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    recurse(self, (-1, -1, -1, -1))
242eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
243eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
244eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef CopySystemFiles(input_zip, output_zip=None,
245eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker                    substitute=None):
246eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  """Copies files underneath system/ in the input zip to the output
247eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  zip.  Populates the Item class with their metadata, and returns a
248eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  list of symlinks.  output_zip may be None, in which case the copy is
249eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  skipped (but the other side effects still happen).  substitute is an
250eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  optional dict of {output filename: contents} to be output instead of
251eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  certain input files.
252eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  """
253eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
254eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  symlinks = []
255eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
256eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  for info in input_zip.infolist():
257eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if info.filename.startswith("SYSTEM/"):
258eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      basefilename = info.filename[7:]
259eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if IsSymlink(info):
260eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        symlinks.append((input_zip.read(info.filename),
261c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                         "/system/" + basefilename))
262eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      else:
263eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        info2 = copy.copy(info)
264eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        fn = info2.filename = "system/" + basefilename
265eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        if substitute and fn in substitute and substitute[fn] is None:
266eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          continue
267eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        if output_zip is not None:
268eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          if substitute and fn in substitute:
269eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker            data = substitute[fn]
270eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          else:
271eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker            data = input_zip.read(info.filename)
272eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          output_zip.writestr(info2, data)
273eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        if fn.endswith("/"):
274eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          Item.Get(fn[:-1], dir=True)
275eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        else:
276eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker          Item.Get(fn, dir=False)
277eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
278eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  symlinks.sort()
279eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  return symlinks
280eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
281eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
282eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef SignOutput(temp_zip_name, output_zip_name):
283eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
284eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  pw = key_passwords[OPTIONS.package_key]
285eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
286951495fc4802a3603f654c02c7acceda4859f5e1Doug Zongker  common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
287951495fc4802a3603f654c02c7acceda4859f5e1Doug Zongker                  whole_file=True)
288eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
289eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
290c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongkerdef AppendAssertions(script, input_zip):
291eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  device = GetBuildProp("ro.product.device", input_zip)
292c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.AssertDevice(device)
293eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
294eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
29573ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongkerdef MakeRecoveryPatch(output_zip, recovery_img, boot_img):
29673ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  """Generate a binary patch that creates the recovery image starting
29773ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  with the boot image.  (Most of the space in these images is just the
29873ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  kernel, which is identical for the two, so the resulting patch
29973ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  should be efficient.)  Add it to the output zip, along with a shell
30073ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  script that is run from init.rc on first boot to actually do the
30173ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  patching and install the new recovery image.
30273ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker
30373ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  recovery_img and boot_img should be File objects for the
30473ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  corresponding images.
30573ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker
30673ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  Returns an Item for the shell script, which must be made
30773ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  executable.
30873ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  """
30973ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker
310ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker  d = common.Difference(recovery_img, boot_img)
311761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  _, _, patch = d.ComputePatch()
312cfd7db6d8494c7d3169a4eac0dc63737a24ff1d1Doug Zongker  common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
31373ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  Item.Get("system/recovery-from-boot.p", dir=False)
31473ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker
31573ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  # Images with different content will have a different first page, so
31673ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  # we check to see if this recovery has already been installed by
31773ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  # testing just the first 2k.
31873ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  HEADER_SIZE = 2048
31973ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  header_sha1 = sha.sha(recovery_img.data[:HEADER_SIZE]).hexdigest()
32073ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  sh = """#!/system/bin/sh
32173ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongkerif ! applypatch -c MTD:recovery:%(header_size)d:%(header_sha1)s; then
32273ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  log -t recovery "Installing new recovery image"
32373ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug 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
32473ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongkerelse
32573ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  log -t recovery "Recovery image already installed"
32673ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongkerfi
32773ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker""" % { 'boot_size': boot_img.size,
32873ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker        'boot_sha1': boot_img.sha1,
32973ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker        'header_size': HEADER_SIZE,
33073ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker        'header_sha1': header_sha1,
33173ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker        'recovery_size': recovery_img.size,
33273ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker        'recovery_sha1': recovery_img.sha1 }
333cfd7db6d8494c7d3169a4eac0dc63737a24ff1d1Doug Zongker  common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
33473ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  return Item.Get("system/etc/install-recovery.sh", dir=False)
33573ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker
33673ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker
337c77a9ad444d49e2ad777678cf5671f0a94f44ffbDoug Zongkerdef WriteFullOTAPackage(input_zip, output_zip):
338c637db16d83b2c248b1cf0122e2ba558ed95762cDoug Zongker  # TODO: how to determine this?  We don't know what version it will
339c637db16d83b2c248b1cf0122e2ba558ed95762cDoug Zongker  # be installed on top of.  For now, we expect the API just won't
340c637db16d83b2c248b1cf0122e2ba558ed95762cDoug Zongker  # change very often.
341c77a9ad444d49e2ad777678cf5671f0a94f44ffbDoug Zongker  script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
342eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
3432ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker  metadata = {"post-build": GetBuildProp("ro.build.fingerprint", input_zip),
3442ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker              "pre-device": GetBuildProp("ro.product.device", input_zip),
3453b85269cfe10ec15e9b544dd6964b8c9f5f761f9Doug Zongker              "post-timestamp": GetBuildProp("ro.build.date.utc", input_zip),
3462ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker              }
3472ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker
34805d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  device_specific = common.DeviceSpecificParams(
34905d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker      input_zip=input_zip,
35014833605d26bf970cd5335c02af4354b68d93348Doug Zongker      input_version=GetRecoveryAPIVersion(input_zip),
35105d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker      output_zip=output_zip,
35205d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker      script=script,
3532ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker      input_tmp=OPTIONS.input_tmp,
3542ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker      metadata=metadata)
35505d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker
356962069ce59c85949d147874df2728a5ffd9193beDoug Zongker  if not OPTIONS.omit_prereq:
357962069ce59c85949d147874df2728a5ffd9193beDoug Zongker    ts = GetBuildProp("ro.build.date.utc", input_zip)
358c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.AssertOlderBuild(ts)
359eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
360eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  AppendAssertions(script, input_zip)
36105d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  device_specific.FullOTA_Assertions()
362171f1cde104891840b0c3c271935fae5433f1b25Doug Zongker
363c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.ShowProgress(0.5, 0)
364eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
365dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker  if OPTIONS.wipe_user_data:
366c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.FormatPartition("userdata")
367dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker
368c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.FormatPartition("system")
369c19a8d5590a4ffd42b37ceaca2d779b48e481f99Doug Zongker  script.Mount("system", "/system")
370cfd7db6d8494c7d3169a4eac0dc63737a24ff1d1Doug Zongker  script.UnpackPackageDir("recovery", "/system")
371c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.UnpackPackageDir("system", "/system")
372eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
373eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  symlinks = CopySystemFiles(input_zip, output_zip)
374c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.MakeSymlinks(symlinks)
375eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
376ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker  boot_img = common.File("boot.img", common.BuildBootableImage(
37773ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker      os.path.join(OPTIONS.input_tmp, "BOOT")))
378ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker  recovery_img = common.File("recovery.img", common.BuildBootableImage(
37973ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker      os.path.join(OPTIONS.input_tmp, "RECOVERY")))
380283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker  MakeRecoveryPatch(output_zip, recovery_img, boot_img)
38173ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker
382283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker  Item.GetMetadata(input_zip)
38373ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  Item.Get("system").SetPermissions(script)
384eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
38573ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  common.CheckSize(boot_img.data, "boot.img")
38673ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
387c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.ShowProgress(0.2, 0)
388c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker
389c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.ShowProgress(0.2, 10)
39005d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  script.WriteRawImage("boot", "boot.img")
39105d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker
39205d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  script.ShowProgress(0.1, 0)
39305d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  device_specific.FullOTA_InstallEnd()
394eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
3951c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker  if OPTIONS.extra_script is not None:
396c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.AppendExtra(OPTIONS.extra_script)
3971c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker
39814833605d26bf970cd5335c02af4354b68d93348Doug Zongker  script.UnmountAll()
399c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.AddToZip(input_zip, output_zip)
4002ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker  WriteMetadata(metadata, output_zip)
4012ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker
4022ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker
4032ea21065b66da9819df92b37a79f0f87552ee331Doug Zongkerdef WriteMetadata(metadata, output_zip):
4042ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker  common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
4052ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker                     "".join(["%s=%s\n" % kv
4062ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker                              for kv in sorted(metadata.iteritems())]))
407eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
408eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
409eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
410eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
411eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef LoadSystemFiles(z):
412eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  """Load all the files from SYSTEM/... in a given target-files
413eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  ZipFile, and return a dict of {filename: File object}."""
414eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  out = {}
415eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  for info in z.infolist():
416eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
417eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      fn = "system/" + info.filename[7:]
418eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      data = z.read(info.filename)
419ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker      out[fn] = common.File(fn, data)
420eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  return out
421eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
422eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
423eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef GetBuildProp(property, z):
424eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  """Return the fingerprint of the build of a given target-files
425eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  ZipFile object."""
426eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  bp = z.read("SYSTEM/build.prop")
427eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if not property:
428eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    return bp
429eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  m = re.search(re.escape(property) + r"=(.*)\n", bp)
430eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if not m:
4319fc74c7823182c4121f32114a7f83ae3fa7e4346Doug Zongker    raise common.ExternalError("couldn't find %s in build.prop" % (property,))
432eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  return m.group(1).strip()
433eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
434eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
435c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongkerdef GetRecoveryAPIVersion(zip):
436c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  """Returns the version of the recovery API.  Version 0 is the older
437c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  amend code (no separate binary)."""
438c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  try:
439c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    version = zip.read("META/recovery-api-version.txt")
440c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    return int(version)
441c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  except KeyError:
442c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    try:
443c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      # version one didn't have the recovery-api-version.txt file, but
444c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      # it did include an updater binary.
445c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      zip.getinfo("OTA/bin/updater")
446c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      return 1
447c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    except KeyError:
448c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker      return 0
449c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker
45015604b84e246514da6c9721266919003f734380bDoug Zongker
451c77a9ad444d49e2ad777678cf5671f0a94f44ffbDoug Zongkerdef WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
452c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  source_version = GetRecoveryAPIVersion(source_zip)
45314833605d26bf970cd5335c02af4354b68d93348Doug Zongker  target_version = GetRecoveryAPIVersion(target_zip)
454c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker
455c637db16d83b2c248b1cf0122e2ba558ed95762cDoug Zongker  if source_version == 0:
456c637db16d83b2c248b1cf0122e2ba558ed95762cDoug Zongker    print ("WARNING: generating edify script for a source that "
457c637db16d83b2c248b1cf0122e2ba558ed95762cDoug Zongker           "can't install it.")
458c77a9ad444d49e2ad777678cf5671f0a94f44ffbDoug Zongker  script = edify_generator.EdifyGenerator(source_version, OPTIONS.info_dict)
459eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
4602ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker  metadata = {"pre-device": GetBuildProp("ro.product.device", source_zip),
4613b85269cfe10ec15e9b544dd6964b8c9f5f761f9Doug Zongker              "post-timestamp": GetBuildProp("ro.build.date.utc", target_zip),
4622ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker              }
4632ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker
46405d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  device_specific = common.DeviceSpecificParams(
46505d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker      source_zip=source_zip,
46614833605d26bf970cd5335c02af4354b68d93348Doug Zongker      source_version=source_version,
46705d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker      target_zip=target_zip,
46814833605d26bf970cd5335c02af4354b68d93348Doug Zongker      target_version=target_version,
46905d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker      output_zip=output_zip,
4702ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker      script=script,
4712ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker      metadata=metadata)
47205d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker
473eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  print "Loading target..."
474eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  target_data = LoadSystemFiles(target_zip)
475eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  print "Loading source..."
476eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  source_data = LoadSystemFiles(source_zip)
477eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
478eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  verbatim_targets = []
479eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  patch_list = []
480761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  diffs = []
481eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  largest_source_size = 0
482eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  for fn in sorted(target_data.keys()):
483eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    tf = target_data[fn]
484761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    assert fn == tf.name
485eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    sf = source_data.get(fn, None)
486eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
487eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if sf is None or fn in OPTIONS.require_verbatim:
488eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      # This file should be included verbatim
489eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if fn in OPTIONS.prohibit_verbatim:
4909fc74c7823182c4121f32114a7f83ae3fa7e4346Doug Zongker        raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
491eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      print "send", fn, "verbatim"
492eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      tf.AddToZip(output_zip)
493eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      verbatim_targets.append((fn, tf.size))
494eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    elif tf.sha1 != sf.sha1:
495eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      # File is different; consider sending as a patch
496ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker      diffs.append(common.Difference(tf, sf))
497eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    else:
498eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      # Target file identical to source.
499eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      pass
500eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
501ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker  common.ComputeDifferences(diffs)
502761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker
503761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker  for diff in diffs:
504761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    tf, sf, d = diff.GetPatch()
505761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
506761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      # patch is almost as big as the file; don't bother patching
507761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      tf.AddToZip(output_zip)
508761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      verbatim_targets.append((tf.name, tf.size))
509761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    else:
510761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      common.ZipWriteStr(output_zip, "patch/" + tf.name + ".p", d)
5115a48209541d5eed602bfb8e2c4ff51e31443daf2Doug Zongker      patch_list.append((tf.name, tf, sf, tf.size, sha.sha(d).hexdigest()))
512761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      largest_source_size = max(largest_source_size, sf.size)
513eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
514eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
515eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  target_fp = GetBuildProp("ro.build.fingerprint", target_zip)
5162ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker  metadata["pre-build"] = source_fp
5172ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker  metadata["post-build"] = target_fp
518eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
519c19a8d5590a4ffd42b37ceaca2d779b48e481f99Doug Zongker  script.Mount("system", "/system")
520c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.AssertSomeFingerprint(source_fp, target_fp)
521eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
522ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker  source_boot = common.File("/tmp/boot.img",
523ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker                            common.BuildBootableImage(
524ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker                                os.path.join(OPTIONS.source_tmp, "BOOT")))
525ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker  target_boot = common.File("/tmp/boot.img",
526ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker                            common.BuildBootableImage(
527ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker                                os.path.join(OPTIONS.target_tmp, "BOOT")))
5285da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker  updating_boot = (source_boot.data != target_boot.data)
529eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
530ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker  source_recovery = common.File("system/recovery.img",
531ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker                                common.BuildBootableImage(
532ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker                                    os.path.join(OPTIONS.source_tmp, "RECOVERY")))
533ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker  target_recovery = common.File("system/recovery.img",
534ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker                                common.BuildBootableImage(
535ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker                                    os.path.join(OPTIONS.target_tmp, "RECOVERY")))
536f6a8bada5f0966762eadaec96de6430d0cd577e3Doug Zongker  updating_recovery = (source_recovery.data != target_recovery.data)
537eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
538881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  # Here's how we divide up the progress bar:
539881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  #  0.1 for verifying the start state (PatchCheck calls)
540881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  #  0.8 for applying patches (ApplyPatch calls)
541881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  #  0.1 for unpacking verbatim files, symlinking, and doing the
542881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  #      device-specific commands.
543eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
544eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  AppendAssertions(script, target_zip)
54505d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  device_specific.IncrementalOTA_Assertions()
546eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
547c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.Print("Verifying current system...")
548c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker
549881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  script.ShowProgress(0.1, 0)
550881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  total_verify_size = float(sum([i[2].size for i in patch_list]) + 1)
551881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  if updating_boot:
552881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    total_verify_size += source_boot.size
553881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  so_far = 0
554c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker
5555a48209541d5eed602bfb8e2c4ff51e31443daf2Doug Zongker  for fn, tf, sf, size, patch_sha in patch_list:
556c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
557881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    so_far += sf.size
558881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    script.SetProgress(so_far / total_verify_size)
559eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
5605da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker  if updating_boot:
561ea5d7a9de7660bef5b9c68f6372a92d4b2f2f1f6Doug Zongker    d = common.Difference(target_boot, source_boot)
562761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    _, _, d = d.ComputePatch()
5635da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker    print "boot      target: %d  source: %d  diff: %d" % (
5645da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker        target_boot.size, source_boot.size, len(d))
5655da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker
566048e7ca15f6391681490ce564bc71194adf146aaDoug Zongker    common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
5675da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker
568c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.PatchCheck("MTD:boot:%d:%s:%d:%s" %
569c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                      (source_boot.size, source_boot.sha1,
570c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                       target_boot.size, target_boot.sha1))
571881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    so_far += source_boot.size
572881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    script.SetProgress(so_far / total_verify_size)
5735da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker
5745da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker  if patch_list or updating_recovery or updating_boot:
575c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.CacheFreeSpaceCheck(largest_source_size)
5765a48209541d5eed602bfb8e2c4ff51e31443daf2Doug Zongker
57705d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  device_specific.IncrementalOTA_VerifyEnd()
57805d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker
579c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.Comment("---- start making changes here ----")
580eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
581dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker  if OPTIONS.wipe_user_data:
582c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.Print("Erasing user data...")
583c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.FormatPartition("userdata")
584dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker
585c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.Print("Removing unneeded files...")
5860f3298a497e32f6c2325a0071124a62d031fae6fDoug Zongker  script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
5870f3298a497e32f6c2325a0071124a62d031fae6fDoug Zongker                     ["/"+i for i in sorted(source_data)
5883b949f07259ee8d67b4454627aceab5e6f44bd39Doug Zongker                            if i not in target_data] +
5893b949f07259ee8d67b4454627aceab5e6f44bd39Doug Zongker                     ["/system/recovery.img"])
590eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
591881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  script.ShowProgress(0.8, 0)
592881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
593881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  if updating_boot:
594881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    total_patch_size += target_boot.size
595881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  so_far = 0
596881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker
597881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  script.Print("Patching system files...")
5985a48209541d5eed602bfb8e2c4ff51e31443daf2Doug Zongker  for fn, tf, sf, size, _ in patch_list:
599c8d446bcde877ec94f8e68dd5af68fe34eb1b1f9Doug Zongker    script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
600881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    so_far += tf.size
601881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    script.SetProgress(so_far / total_patch_size)
602881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker
603eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if updating_boot:
6045da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker    # Produce the boot image by applying a patch to the current
6055da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker    # contents of the boot partition, and write it back to the
6065da317e51d1832cb1ec67dd20fbcff7708bbadb5Doug Zongker    # partition.
607c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.Print("Patching boot image...")
608c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.ApplyPatch("MTD:boot:%d:%s:%d:%s"
609c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                      % (source_boot.size, source_boot.sha1,
610c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                         target_boot.size, target_boot.sha1),
611c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                      "-",
612c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                      target_boot.size, target_boot.sha1,
613c8d446bcde877ec94f8e68dd5af68fe34eb1b1f9Doug Zongker                      source_boot.sha1, "patch/boot.img.p")
614881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    so_far += target_boot.size
615881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker    script.SetProgress(so_far / total_patch_size)
616eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    print "boot image changed; including."
617eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  else:
618eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    print "boot image unchanged; skipping."
619eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
620eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if updating_recovery:
62173ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # Is it better to generate recovery as a patch from the current
62273ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # boot image, or from the previous recovery image?  For large
62373ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # updates with significant kernel changes, probably the former.
62473ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # For small updates where the kernel hasn't changed, almost
62573ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # certainly the latter.  We pick the first option.  Future
62673ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # complicated schemes may let us effectively use both.
62773ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    #
62873ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # A wacky possibility: as long as there is room in the boot
62973ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # partition, include the binaries and image files from recovery in
63073ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # the boot image (though not in the ramdisk) so they can be used
63173ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    # as fodder for constructing the recovery image.
632283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker    MakeRecoveryPatch(output_zip, target_recovery, target_boot)
63342265390d993664e7797abc12d7e6bd1c2a6dc6bDoug Zongker    script.DeleteFiles(["/system/recovery-from-boot.p",
63442265390d993664e7797abc12d7e6bd1c2a6dc6bDoug Zongker                        "/system/etc/install-recovery.sh"])
63573ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker    print "recovery image changed; including as patch from boot."
636eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  else:
637eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    print "recovery image unchanged; skipping."
638eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
639881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  script.ShowProgress(0.1, 10)
640eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
641eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  target_symlinks = CopySystemFiles(target_zip, None)
642eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
643eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
644c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  temp_script = script.MakeTemporary()
645283e2a1e1bae4e21824969a15da6420204633dddDoug Zongker  Item.GetMetadata(target_zip)
64673ef8257ce54ce1ddc2d4cfc3b3814ca734c0e11Doug Zongker  Item.Get("system").SetPermissions(temp_script)
647eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
648eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # Note that this call will mess up the tree of Items, so make sure
649eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # we're done with it.
650eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  source_symlinks = CopySystemFiles(source_zip, None)
651eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
652eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
653eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # Delete all the symlinks in source that aren't in target.  This
654eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # needs to happen before verbatim files are unpacked, in case a
655eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # symlink in the source is replaced by a real file in the target.
656eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  to_delete = []
657eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  for dest, link in source_symlinks:
658eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if link not in target_symlinks_d:
659eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      to_delete.append(link)
660c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.DeleteFiles(to_delete)
661eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
662eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if verbatim_targets:
663c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.Print("Unpacking new files...")
664c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    script.UnpackPackageDir("system", "/system")
665c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker
66642265390d993664e7797abc12d7e6bd1c2a6dc6bDoug Zongker  if updating_recovery:
66742265390d993664e7797abc12d7e6bd1c2a6dc6bDoug Zongker    script.Print("Unpacking new recovery...")
66842265390d993664e7797abc12d7e6bd1c2a6dc6bDoug Zongker    script.UnpackPackageDir("recovery", "/system")
66942265390d993664e7797abc12d7e6bd1c2a6dc6bDoug Zongker
67005d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  script.Print("Symlinks and permissions...")
671eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
672eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # Create all the symlinks that don't already exist, or point to
673eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # somewhere different than what we want.  Delete each symlink before
674eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # creating it, since the 'symlink' command won't overwrite.
675eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  to_create = []
676eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  for dest, link in target_symlinks:
677eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if link in source_symlinks_d:
678eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      if dest != source_symlinks_d[link]:
679eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker        to_create.append((dest, link))
680eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    else:
681eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      to_create.append((dest, link))
682c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.DeleteFiles([i[1] for i in to_create])
683c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.MakeSymlinks(to_create)
684eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
685eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # Now that the symlinks are created, we can set all the
686eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  # permissions.
687c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.AppendScript(temp_script)
688eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
689881dd40ffb683fed465df955f3fd21812fae59aaDoug Zongker  # Do device-specific installation (eg, write radio image).
69005d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker  device_specific.IncrementalOTA_InstallEnd()
69105d3dea519688b61d86e30c2d4b99ff494aeca73Doug Zongker
6921c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker  if OPTIONS.extra_script is not None:
693c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker    scirpt.AppendExtra(OPTIONS.extra_script)
6941c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker
695c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker  script.AddToZip(target_zip, output_zip)
6962ea21065b66da9819df92b37a79f0f87552ee331Doug Zongker  WriteMetadata(metadata, output_zip)
697eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
698eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
699eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerdef main(argv):
700eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
701eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  def option_handler(o, a):
702eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    if o in ("-b", "--board_config"):
703fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker      pass   # deprecated
704eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    elif o in ("-k", "--package_key"):
705eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      OPTIONS.package_key = a
706eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    elif o in ("-i", "--incremental_from"):
707eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      OPTIONS.incremental_source = a
708dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker    elif o in ("-w", "--wipe_user_data"):
709dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker      OPTIONS.wipe_user_data = True
710962069ce59c85949d147874df2728a5ffd9193beDoug Zongker    elif o in ("-n", "--no_prereq"):
711962069ce59c85949d147874df2728a5ffd9193beDoug Zongker      OPTIONS.omit_prereq = True
7121c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker    elif o in ("-e", "--extra_script"):
7131c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker      OPTIONS.extra_script = a
714761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker    elif o in ("--worker_threads"):
715761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker      OPTIONS.worker_threads = int(a)
716eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    else:
717eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker      return False
718dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker    return True
719eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
720eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  args = common.ParseOptions(argv, __doc__,
721c637db16d83b2c248b1cf0122e2ba558ed95762cDoug Zongker                             extra_opts="b:k:i:d:wne:",
722eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker                             extra_long_opts=["board_config=",
723eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker                                              "package_key=",
724dbfaae5567dbbfcc17e15b31e65178ad367e6d13Doug Zongker                                              "incremental_from=",
725962069ce59c85949d147874df2728a5ffd9193beDoug Zongker                                              "wipe_user_data",
7261c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker                                              "no_prereq",
727c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker                                              "extra_script=",
728761e642d54eec743699c6c2ce1ea587853d08f33Doug Zongker                                              "worker_threads="],
729eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker                             extra_option_handler=option_handler)
730eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
731eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if len(args) != 2:
732eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    common.Usage(__doc__)
733eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    sys.exit(1)
734eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
7351c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker  if OPTIONS.extra_script is not None:
7361c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker    OPTIONS.extra_script = open(OPTIONS.extra_script).read()
7371c390a2aa97127ef8af8b0df1d4028f501fdce64Doug Zongker
738eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  print "unzipping target target-files..."
739eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  OPTIONS.input_tmp = common.UnzipTemp(args[0])
740fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker
741c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker  if OPTIONS.device_specific is None:
742c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker    # look for the device-specific tools extension location in the input
743c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker    try:
744c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker      f = open(os.path.join(OPTIONS.input_tmp, "META", "tool-extensions.txt"))
745c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker      ds = f.read().strip()
746c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker      f.close()
747c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker      if ds:
748c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker        ds = os.path.normpath(ds)
749c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker        print "using device-specific extensions in", ds
750c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker        OPTIONS.device_specific = ds
751c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker    except IOError, e:
752c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker      if e.errno == errno.ENOENT:
753c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker        # nothing specified in the file
754c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker        pass
755c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker      else:
756c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker        raise
757c18736b1a777c386dc3c5e3f878249770b5edd78Doug Zongker
758c77a9ad444d49e2ad777678cf5671f0a94f44ffbDoug Zongker  OPTIONS.info_dict = common.LoadInfoDict()
759c77a9ad444d49e2ad777678cf5671f0a94f44ffbDoug Zongker  common.LoadMaxSizes(OPTIONS.info_dict)
760fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker  if not OPTIONS.max_image_size:
761fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker    print
762fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker    print "  WARNING:  Failed to load max image sizes; will not enforce"
763fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker    print "  image size limits."
764fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker    print
765fdd8e69c42e66fb70384bcaca1747f504f2c021cDoug Zongker
766eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  OPTIONS.target_tmp = OPTIONS.input_tmp
767eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  input_zip = zipfile.ZipFile(args[0], "r")
768eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if OPTIONS.package_key:
769eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    temp_zip_file = tempfile.NamedTemporaryFile()
770eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    output_zip = zipfile.ZipFile(temp_zip_file, "w",
771eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker                                 compression=zipfile.ZIP_DEFLATED)
772eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  else:
773eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    output_zip = zipfile.ZipFile(args[1], "w",
774eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker                                 compression=zipfile.ZIP_DEFLATED)
775eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
776eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if OPTIONS.incremental_source is None:
777c77a9ad444d49e2ad777678cf5671f0a94f44ffbDoug Zongker    WriteFullOTAPackage(input_zip, output_zip)
778eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  else:
779eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    print "unzipping source target-files..."
780eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    OPTIONS.source_tmp = common.UnzipTemp(OPTIONS.incremental_source)
781eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
782c77a9ad444d49e2ad777678cf5671f0a94f44ffbDoug Zongker    WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
783eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
784eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  output_zip.close()
785eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  if OPTIONS.package_key:
786eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    SignOutput(temp_zip_file.name, args[1])
787eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    temp_zip_file.close()
788eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
789eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  common.Cleanup()
790eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
791eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  print "done."
792eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
793eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker
794eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongkerif __name__ == '__main__':
795eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  try:
796eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    main(sys.argv[1:])
797eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker  except common.ExternalError, e:
798eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    print
799eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    print "   ERROR: %s" % (e,)
800eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    print
801eef3944eb3673329b5e89cf188ac592805a0b08dDoug Zongker    sys.exit(1)
802