edify_generator.py revision c494d7cee85d980647ca915ea64355b71fe817eb
1c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker# Copyright (C) 2009 The Android Open Source Project 2c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker# 3c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker# Licensed under the Apache License, Version 2.0 (the "License"); 4c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker# you may not use this file except in compliance with the License. 5c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker# You may obtain a copy of the License at 6c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker# 7c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker# http://www.apache.org/licenses/LICENSE-2.0 8c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker# 9c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker# Unless required by applicable law or agreed to in writing, software 10c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker# distributed under the License is distributed on an "AS IS" BASIS, 11c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker# See the License for the specific language governing permissions and 13c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker# limitations under the License. 14c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 15c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongkerimport os 16c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongkerimport re 17c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 18c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongkerimport common 19c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 20c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongkerclass EdifyGenerator(object): 21c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Class to generate scripts in the 'edify' recovery script language 22c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker used from donut onwards.""" 23c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 24c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def __init__(self, version): 25c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script = [] 26c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.mounts = set() 27c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.version = version 28c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 29c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def MakeTemporary(self): 30c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Make a temporary script object whose commands can latter be 31c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker appended to the parent script with AppendScript(). Used when the 32c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker caller wants to generate script commands out-of-order.""" 33c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker x = EdifyGenerator(self.version) 34c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker x.mounts = self.mounts 35c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker return x 36c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 37c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker @staticmethod 38c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def _WordWrap(cmd, linelen=80): 39c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """'cmd' should be a function call with null characters after each 40c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker parameter (eg, "somefun(foo,\0bar,\0baz)"). This function wraps cmd 41c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker to a given line length, replacing nulls with spaces and/or newlines 42c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker to format it nicely.""" 43c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker indent = cmd.index("(")+1 44c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker out = [] 45c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker first = True 46c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker x = re.compile("^(.{,%d})\0" % (linelen-indent,)) 47c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker while True: 48c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker if not first: 49c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker out.append(" " * indent) 50c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker first = False 51c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker m = x.search(cmd) 52c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker if not m: 53c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker parts = cmd.split("\0", 1) 54c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker out.append(parts[0]+"\n") 55c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker if len(parts) == 1: 56c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker break 57c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker else: 58c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker cmd = parts[1] 59c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker continue 60c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker out.append(m.group(1)+"\n") 61c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker cmd = cmd[m.end():] 62c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 63c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker return "".join(out).replace("\0", " ").rstrip("\n") 64c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 65c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def AppendScript(self, other): 66c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Append the contents of another script (which should be created 67c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker with temporary=True) to this one.""" 68c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.extend(other.script) 69c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 70c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def AssertSomeFingerprint(self, *fp): 71c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Assert that the current system build fingerprint is one of *fp.""" 72c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker if not fp: 73c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker raise ValueError("must specify some fingerprints") 74c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker cmd = ('assert(' + 75c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker ' ||\0'.join([('file_getprop("/system/build.prop", ' 76c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker '"ro.build.fingerprint") == "%s"') 77c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker % i for i in fp]) + 78c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker ');') 79c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append(self._WordWrap(cmd)) 80c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 81c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def AssertOlderBuild(self, timestamp): 82c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Assert that the build on the device is older (or the same as) 83c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker the given timestamp.""" 84c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append(('assert(!less_than_int(%s, ' 85c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 'getprop("ro.build.date.utc")));') % (timestamp,)) 86c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 87c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def AssertDevice(self, device): 88c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Assert that the device identifier is the given string.""" 89c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker cmd = ('assert(getprop("ro.product.device") == "%s" ||\0' 90c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 'getprop("ro.build.product") == "%s");' % (device, device)) 91c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append(self._WordWrap(cmd)) 92c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 93c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def AssertSomeBootloader(self, *bootloaders): 94c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Asert that the bootloader version is one of *bootloaders.""" 95c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker cmd = ("assert(" + 96c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker " ||\0".join(['getprop("ro.bootloader") == "%s"' % (b,) 97c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker for b in bootloaders]) + 98c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker ");") 99c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append(self._WordWrap(cmd)) 100c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 101c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def ShowProgress(self, frac, dur): 102c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Update the progress bar, advancing it over 'frac' over the next 103c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 'dur' seconds.""" 104c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append("show_progress(%f, %d);" % (frac, int(dur))) 105c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 106c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def PatchCheck(self, filename, *sha1): 107c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Check that the given file (or MTD reference) has one of the 108c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker given *sha1 hashes.""" 109c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append('assert(apply_patch_check("%s"' % (filename,) + 110c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker "".join([', "%s"' % (i,) for i in sha1]) + 111c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker '));') 112c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 113c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def CacheFreeSpaceCheck(self, amount): 114c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Check that there's at least 'amount' space that can be made 115c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker available on /cache.""" 116c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append("assert(apply_patch_space(%d));" % (amount,)) 117c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 118c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def Mount(self, kind, what, path): 119c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Mount the given 'what' at the given path. 'what' should be a 120c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker partition name if kind is "MTD", or a block device if kind is 121c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker "vfat". No other values of 'kind' are supported.""" 122c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append('mount("%s", "%s", "%s");' % (kind, what, path)) 123c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.mounts.add(path) 124c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 125c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def UnpackPackageDir(self, src, dst): 126c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Unpack a given directory from the OTA package into the given 127c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker destination directory.""" 128c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append('package_extract_dir("%s", "%s");' % (src, dst)) 129c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 130c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def Comment(self, comment): 131c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Write a comment into the update script.""" 132c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append("") 133c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker for i in comment.split("\n"): 134c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append("# " + i) 135c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append("") 136c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 137c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def Print(self, message): 138c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Log a message to the screen (if the logs are visible).""" 139c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append('ui_print("%s");' % (message,)) 140c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 141c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def FormatPartition(self, partition): 142c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Format the given MTD partition.""" 143c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append('format("MTD", "%s");' % (partition,)) 144c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 145c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def DeleteFiles(self, file_list): 146c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Delete all files in file_list.""" 147c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker if not file_list: return 148c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker cmd = "delete(" + ",\0".join(['"%s"' % (i,) for i in file_list]) + ");" 149c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append(self._WordWrap(cmd)) 150c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 151c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def ApplyPatch(self, srcfile, tgtfile, tgtsize, tgtsha1, *patchpairs): 152c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Apply binary patches (in *patchpairs) to the given srcfile to 153c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker produce tgtfile (which may be "-" to indicate overwriting the 154c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker source file.""" 155c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker if len(patchpairs) % 2 != 0 or len(patchpairs) == 0: 156c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker raise ValueError("bad patches given to ApplyPatch") 157c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker cmd = ['apply_patch("%s",\0"%s",\0%s,\0%d' 158c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker % (srcfile, tgtfile, tgtsha1, tgtsize)] 159c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker for i in range(0, len(patchpairs), 2): 160c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker cmd.append(',\0"%s:%s"' % patchpairs[i:i+2]) 161c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker cmd.append(');') 162c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker cmd = "".join(cmd) 163c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append(self._WordWrap(cmd)) 164c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 165c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def WriteFirmwareImage(self, kind, fn): 166c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Arrange to update the given firmware image (kind must be 167c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker "hboot" or "radio") when recovery finishes.""" 168c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker if self.version == 1: 169c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append( 170c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker ('assert(package_extract_file("%(fn)s", "/tmp/%(kind)s.img"),\n' 171c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker ' write_firmware_image("/tmp/%(kind)s.img", "%(kind)s"));') 172c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker % {'kind': kind, 'fn': fn}) 173c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker else: 174c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append( 175c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 'write_firmware_image("PACKAGE:%s", "%s");' % (fn, kind)) 176c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 177c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def WriteRawImage(self, partition, fn): 178c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Write the given package file into the given MTD partition.""" 179c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append( 180c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker ('assert(package_extract_file("%(fn)s", "/tmp/%(partition)s.img"),\n' 181c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker ' write_raw_image("/tmp/%(partition)s.img", "%(partition)s"),\n' 182c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker ' delete("/tmp/%(partition)s.img"));') 183c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker % {'partition': partition, 'fn': fn}) 184c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 185c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def SetPermissions(self, fn, uid, gid, mode): 186c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Set file ownership and permissions.""" 187c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append('set_perm(%d, %d, 0%o, "%s");' % (uid, gid, mode, fn)) 188c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 189c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def SetPermissionsRecursive(self, fn, uid, gid, dmode, fmode): 190c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Recursively set path ownership and permissions.""" 191c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append('set_perm_recursive(%d, %d, 0%o, 0%o, "%s");' 192c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker % (uid, gid, dmode, fmode, fn)) 193c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 194c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def MakeSymlinks(self, symlink_list): 195c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Create symlinks, given a list of (dest, link) pairs.""" 196c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker by_dest = {} 197c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker for d, l in symlink_list: 198c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker by_dest.setdefault(d, []).append(l) 199c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 200c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker for dest, links in sorted(by_dest.iteritems()): 201c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker cmd = ('symlink("%s", ' % (dest,) + 202c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker ",\0".join(['"' + i + '"' for i in sorted(links)]) + ");") 203c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append(self._WordWrap(cmd)) 204c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 205c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def AppendExtra(self, extra): 206c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Append text verbatim to the output script.""" 207c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append(extra) 208c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 209c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker def AddToZip(self, input_zip, output_zip, input_path=None): 210c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker """Write the accumulated script to the output_zip file. input_zip 211c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker is used as the source for the 'updater' binary needed to run 212c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker script. If input_path is not None, it will be used as a local 213c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker path for the binary instead of input_zip.""" 214c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 215c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker for p in sorted(self.mounts): 216c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker self.script.append('unmount("%s");' % (p,)) 217c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 218c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker common.ZipWriteStr(output_zip, "META-INF/com/google/android/updater-script", 219c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker "\n".join(self.script) + "\n") 220c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker 221c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker if input_path is None: 222c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker data = input_zip.read("OTA/bin/updater") 223c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker else: 224c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker data = open(os.path.join(input_path, "updater")).read() 225c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker common.ZipWriteStr(output_zip, "META-INF/com/google/android/update-binary", 226c494d7cee85d980647ca915ea64355b71fe817ebDoug Zongker data, perms=0755) 227