3 # Copyright (C) 2008 The Android Open Source Project
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
18 Given a target-files zipfile, produces an OTA package that installs
19 that build. An incremental OTA is produced if -i is given, otherwise
20 a full OTA is produced.
22 Usage: ota_from_target_files [flags] input_target_files output_ota_package
24 -b (--board_config) <file>
27 -k (--package_key) <key> Key to use to sign the package (default is
28 the value of default_system_dev_certificate from the input
29 target-files's META/misc_info.txt, or
30 "build/target/product/security/testkey" if that value is not
33 For incremental OTAs, the default value is based on the source
34 target-file, not the target build.
36 -i (--incremental_from) <file>
37 Generate an incremental OTA using the given target-files zip as
41 Generate an OTA package that will wipe the user data partition
45 Omit the timestamp prereq check normally included at the top of
46 the build scripts (used for developer OTA packages which
47 legitimately need to go back and forth).
49 -e (--extra_script) <file>
50 Insert the contents of file at the end of the update script.
52 -a (--aslr_mode) <on|off>
53 Specify whether to turn on ASLR for the package (on by default).
59 if sys.hexversion < 0x02040000:
60 print >> sys.stderr, "Python 2.4 or newer is required."
73 from hashlib import sha1 as sha1
75 from sha import sha as sha1
78 import edify_generator
80 OPTIONS = common.OPTIONS
81 OPTIONS.package_key = None
82 OPTIONS.incremental_source = None
83 OPTIONS.require_verbatim = set()
84 OPTIONS.prohibit_verbatim = set(("system/build.prop",))
85 OPTIONS.patch_threshold = 0.95
86 OPTIONS.wipe_user_data = False
87 OPTIONS.omit_prereq = False
88 OPTIONS.extra_script = None
89 OPTIONS.aslr_mode = True
90 OPTIONS.worker_threads = 3
92 def MostPopularKey(d, default):
93 """Given a dict, return the key corresponding to the largest
94 value. Returns 'default' if the dict is empty."""
95 x = [(v, k) for (k, v) in d.iteritems()]
96 if not x: return default
102 """Return true if the zipfile.ZipInfo object passed in represents a
104 return (info.external_attr >> 16) == 0120777
107 """Return true if the zipfile.ZipInfo object passed in represents a
109 return (info.external_attr >> 28) == 010
112 """Items represent the metadata (user, group, mode) of files and
113 directories in the system image."""
115 def __init__(self, name, dir=False):
123 self.parent = Item.Get(os.path.dirname(name), dir=True)
124 self.parent.children.append(self)
130 def Dump(self, indent=0):
131 if self.uid is not None:
132 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
134 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
136 print "%s%s" % (" "*indent, self.descendants)
137 print "%s%s" % (" "*indent, self.best_subtree)
138 for i in self.children:
139 i.Dump(indent=indent+1)
142 def Get(cls, name, dir=False):
143 if name not in cls.ITEMS:
144 cls.ITEMS[name] = Item(name, dir=dir)
145 return cls.ITEMS[name]
148 def GetMetadata(cls, input_zip):
151 # See if the target_files contains a record of what the uid,
152 # gid, and mode is supposed to be.
153 output = input_zip.read("META/filesystem_config.txt")
155 # Run the external 'fs_config' program to determine the desired
156 # uid, gid, and mode for every Item object. Note this uses the
157 # one in the client now, which might not be the same as the one
158 # used when this target_files was built.
159 p = common.Run(["fs_config"], stdin=subprocess.PIPE,
160 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
161 suffix = { False: "", True: "/" }
162 input = "".join(["%s%s\n" % (i.name, suffix[i.dir])
163 for i in cls.ITEMS.itervalues() if i.name])
164 output, error = p.communicate(input)
167 for line in output.split("\n"):
168 if not line: continue
169 name, uid, gid, mode = line.split()
170 i = cls.ITEMS.get(name, None)
174 i.mode = int(mode, 8)
176 i.children.sort(key=lambda i: i.name)
178 # set metadata for the files generated by this script.
179 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
180 if i: i.uid, i.gid, i.mode = 0, 0, 0644
181 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
182 if i: i.uid, i.gid, i.mode = 0, 0, 0544
184 def CountChildMetadata(self):
185 """Count up the (uid, gid, mode) tuples for all children and
186 determine the best strategy for using set_perm_recursive and
187 set_perm to correctly chown/chmod all the files to their desired
188 values. Recursively calls itself for all descendants.
190 Returns a dict of {(uid, gid, dmode, fmode): count} counting up
191 all descendants of this node. (dmode or fmode may be None.) Also
192 sets the best_subtree of each directory Item to the (uid, gid,
193 dmode, fmode) tuple that will match the most descendants of that
198 d = self.descendants = {(self.uid, self.gid, self.mode, None): 1}
199 for i in self.children:
201 for k, v in i.CountChildMetadata().iteritems():
202 d[k] = d.get(k, 0) + v
204 k = (i.uid, i.gid, None, i.mode)
205 d[k] = d.get(k, 0) + 1
207 # Find the (uid, gid, dmode, fmode) tuple that matches the most
210 # First, find the (uid, gid) pair that matches the most
213 for (uid, gid, _, _), count in d.iteritems():
214 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
215 ug = MostPopularKey(ug, (0, 0))
217 # Now find the dmode and fmode that match the most descendants
218 # with that (uid, gid), and choose those.
219 best_dmode = (0, 0755)
220 best_fmode = (0, 0644)
221 for k, count in d.iteritems():
222 if k[:2] != ug: continue
223 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
224 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
225 self.best_subtree = ug + (best_dmode[1], best_fmode[1])
229 def SetPermissions(self, script):
230 """Append set_perm/set_perm_recursive commands to 'script' to
231 set all permissions, users, and groups for the tree of files
234 self.CountChildMetadata()
236 def recurse(item, current):
237 # current is the (uid, gid, dmode, fmode) tuple that the current
238 # item (and all its children) have already been set to. We only
239 # need to issue set_perm/set_perm_recursive commands if we're
240 # supposed to be something different.
242 if current != item.best_subtree:
243 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
244 current = item.best_subtree
246 if item.uid != current[0] or item.gid != current[1] or \
247 item.mode != current[2]:
248 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
250 for i in item.children:
253 if item.uid != current[0] or item.gid != current[1] or \
254 item.mode != current[3]:
255 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
257 recurse(self, (-1, -1, -1, -1))
260 def CopySystemFiles(input_zip, output_zip=None,
262 """Copies files underneath system/ in the input zip to the output
263 zip. Populates the Item class with their metadata, and returns a
264 list of symlinks. output_zip may be None, in which case the copy is
265 skipped (but the other side effects still happen). substitute is an
266 optional dict of {output filename: contents} to be output instead of
272 for info in input_zip.infolist():
273 if info.filename.startswith("SYSTEM/"):
274 basefilename = info.filename[7:]
276 symlinks.append((input_zip.read(info.filename),
277 "/system/" + basefilename))
279 info2 = copy.copy(info)
280 fn = info2.filename = "system/" + basefilename
281 if substitute and fn in substitute and substitute[fn] is None:
283 if output_zip is not None:
284 if substitute and fn in substitute:
285 data = substitute[fn]
287 data = input_zip.read(info.filename)
288 output_zip.writestr(info2, data)
290 Item.Get(fn[:-1], dir=True)
292 Item.Get(fn, dir=False)
298 def SignOutput(temp_zip_name, output_zip_name):
299 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
300 pw = key_passwords[OPTIONS.package_key]
302 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
306 def AppendAssertions(script, info_dict):
307 device = GetBuildProp("ro.product.device", info_dict)
308 script.AssertDevice(device)
311 def MakeRecoveryPatch(input_tmp, output_zip, recovery_img, boot_img):
312 """Generate a binary patch that creates the recovery image starting
313 with the boot image. (Most of the space in these images is just the
314 kernel, which is identical for the two, so the resulting patch
315 should be efficient.) Add it to the output zip, along with a shell
316 script that is run from init.rc on first boot to actually do the
317 patching and install the new recovery image.
319 recovery_img and boot_img should be File objects for the
320 corresponding images. info should be the dictionary returned by
321 common.LoadInfoDict() on the input target_files.
323 Returns an Item for the shell script, which must be made
327 diff_program = ["imgdiff"]
328 path = os.path.join(input_tmp, "SYSTEM", "etc", "recovery-resource.dat")
329 if os.path.exists(path):
330 diff_program.append("-b")
331 diff_program.append(path)
332 bonus_args = "-b /system/etc/recovery-resource.dat"
336 d = common.Difference(recovery_img, boot_img, diff_program=diff_program)
337 _, _, patch = d.ComputePatch()
338 common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
339 Item.Get("system/recovery-from-boot.p", dir=False)
341 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
342 recovery_type, recovery_device = common.GetTypeAndDevice("/recovery", OPTIONS.info_dict)
344 sh = """#!/system/bin/sh
345 if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
346 log -t recovery "Installing new recovery image"
347 applypatch %(bonus_args)s %(boot_type)s:%(boot_device)s:%(boot_size)d:%(boot_sha1)s %(recovery_type)s:%(recovery_device)s %(recovery_sha1)s %(recovery_size)d %(boot_sha1)s:/system/recovery-from-boot.p
349 log -t recovery "Recovery image already installed"
351 """ % { 'boot_size': boot_img.size,
352 'boot_sha1': boot_img.sha1,
353 'recovery_size': recovery_img.size,
354 'recovery_sha1': recovery_img.sha1,
355 'boot_type': boot_type,
356 'boot_device': boot_device,
357 'recovery_type': recovery_type,
358 'recovery_device': recovery_device,
359 'bonus_args': bonus_args,
361 common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
362 return Item.Get("system/etc/install-recovery.sh", dir=False)
365 def WriteFullOTAPackage(input_zip, output_zip):
366 # TODO: how to determine this? We don't know what version it will
367 # be installed on top of. For now, we expect the API just won't
369 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
371 metadata = {"post-build": GetBuildProp("ro.build.fingerprint",
373 "pre-device": GetBuildProp("ro.product.device",
375 "post-timestamp": GetBuildProp("ro.build.date.utc",
379 device_specific = common.DeviceSpecificParams(
381 input_version=OPTIONS.info_dict["recovery_api_version"],
382 output_zip=output_zip,
384 input_tmp=OPTIONS.input_tmp,
386 info_dict=OPTIONS.info_dict)
388 if not OPTIONS.omit_prereq:
389 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
390 script.AssertOlderBuild(ts)
392 AppendAssertions(script, OPTIONS.info_dict)
393 device_specific.FullOTA_Assertions()
394 device_specific.FullOTA_InstallBegin()
396 script.ShowProgress(0.5, 0)
398 if OPTIONS.wipe_user_data:
399 script.FormatPartition("/data")
401 if "selinux_fc" in OPTIONS.info_dict:
402 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
404 script.FormatPartition("/system")
405 script.Mount("/system")
406 script.UnpackPackageDir("recovery", "/system")
407 script.UnpackPackageDir("system", "/system")
409 symlinks = CopySystemFiles(input_zip, output_zip)
410 script.MakeSymlinks(symlinks)
412 boot_img = common.GetBootableImage("boot.img", "boot.img",
413 OPTIONS.input_tmp, "BOOT")
414 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
415 OPTIONS.input_tmp, "RECOVERY")
416 MakeRecoveryPatch(OPTIONS.input_tmp, output_zip, recovery_img, boot_img)
418 Item.GetMetadata(input_zip)
419 Item.Get("system").SetPermissions(script)
421 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
422 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
423 script.ShowProgress(0.2, 0)
425 script.ShowProgress(0.2, 10)
426 script.WriteRawImage("/boot", "boot.img")
428 script.ShowProgress(0.1, 0)
429 device_specific.FullOTA_InstallEnd()
431 if OPTIONS.extra_script is not None:
432 script.AppendExtra(OPTIONS.extra_script)
435 script.AddToZip(input_zip, output_zip)
436 WriteMetadata(metadata, output_zip)
438 def WritePolicyConfig(file_context, output_zip):
439 f = open(file_context, 'r');
440 basename = os.path.basename(file_context)
441 common.ZipWriteStr(output_zip, basename, f.read())
444 def WriteMetadata(metadata, output_zip):
445 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
446 "".join(["%s=%s\n" % kv
447 for kv in sorted(metadata.iteritems())]))
449 def LoadSystemFiles(z):
450 """Load all the files from SYSTEM/... in a given target-files
451 ZipFile, and return a dict of {filename: File object}."""
453 for info in z.infolist():
454 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
455 basefilename = info.filename[7:]
456 fn = "system/" + basefilename
457 data = z.read(info.filename)
458 out[fn] = common.File(fn, data)
462 def GetBuildProp(prop, info_dict):
463 """Return the fingerprint of the build of a given target-files info_dict."""
465 return info_dict.get("build.prop", {})[prop]
467 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
470 def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
471 source_version = OPTIONS.source_info_dict["recovery_api_version"]
472 target_version = OPTIONS.target_info_dict["recovery_api_version"]
474 if source_version == 0:
475 print ("WARNING: generating edify script for a source that "
477 script = edify_generator.EdifyGenerator(source_version,
478 OPTIONS.target_info_dict)
480 metadata = {"pre-device": GetBuildProp("ro.product.device",
481 OPTIONS.source_info_dict),
482 "post-timestamp": GetBuildProp("ro.build.date.utc",
483 OPTIONS.target_info_dict),
486 device_specific = common.DeviceSpecificParams(
487 source_zip=source_zip,
488 source_version=source_version,
489 target_zip=target_zip,
490 target_version=target_version,
491 output_zip=output_zip,
494 info_dict=OPTIONS.info_dict)
496 print "Loading target..."
497 target_data = LoadSystemFiles(target_zip)
498 print "Loading source..."
499 source_data = LoadSystemFiles(source_zip)
501 verbatim_targets = []
504 largest_source_size = 0
505 for fn in sorted(target_data.keys()):
508 sf = source_data.get(fn, None)
510 if sf is None or fn in OPTIONS.require_verbatim:
511 # This file should be included verbatim
512 if fn in OPTIONS.prohibit_verbatim:
513 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
514 print "send", fn, "verbatim"
515 tf.AddToZip(output_zip)
516 verbatim_targets.append((fn, tf.size))
517 elif tf.sha1 != sf.sha1:
518 # File is different; consider sending as a patch
519 diffs.append(common.Difference(tf, sf))
521 # Target file identical to source.
524 common.ComputeDifferences(diffs)
527 tf, sf, d = diff.GetPatch()
528 if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
529 # patch is almost as big as the file; don't bother patching
530 tf.AddToZip(output_zip)
531 verbatim_targets.append((tf.name, tf.size))
533 common.ZipWriteStr(output_zip, "patch/" + tf.name + ".p", d)
534 patch_list.append((tf.name, tf, sf, tf.size, common.sha1(d).hexdigest()))
535 largest_source_size = max(largest_source_size, sf.size)
537 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
538 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
539 metadata["pre-build"] = source_fp
540 metadata["post-build"] = target_fp
542 script.Mount("/system")
543 script.AssertSomeFingerprint(source_fp, target_fp)
545 source_boot = common.GetBootableImage(
546 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
547 OPTIONS.source_info_dict)
548 target_boot = common.GetBootableImage(
549 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
550 updating_boot = (source_boot.data != target_boot.data)
552 source_recovery = common.GetBootableImage(
553 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
554 OPTIONS.source_info_dict)
555 target_recovery = common.GetBootableImage(
556 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
557 updating_recovery = (source_recovery.data != target_recovery.data)
559 # Here's how we divide up the progress bar:
560 # 0.1 for verifying the start state (PatchCheck calls)
561 # 0.8 for applying patches (ApplyPatch calls)
562 # 0.1 for unpacking verbatim files, symlinking, and doing the
563 # device-specific commands.
565 AppendAssertions(script, OPTIONS.target_info_dict)
566 device_specific.IncrementalOTA_Assertions()
568 script.Print("Verifying current system...")
570 device_specific.IncrementalOTA_VerifyBegin()
572 script.ShowProgress(0.1, 0)
573 total_verify_size = float(sum([i[2].size for i in patch_list]) + 1)
575 total_verify_size += source_boot.size
578 for fn, tf, sf, size, patch_sha in patch_list:
579 script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
581 script.SetProgress(so_far / total_verify_size)
584 d = common.Difference(target_boot, source_boot)
585 _, _, d = d.ComputePatch()
586 print "boot target: %d source: %d diff: %d" % (
587 target_boot.size, source_boot.size, len(d))
589 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
591 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
593 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
594 (boot_type, boot_device,
595 source_boot.size, source_boot.sha1,
596 target_boot.size, target_boot.sha1))
597 so_far += source_boot.size
598 script.SetProgress(so_far / total_verify_size)
600 if patch_list or updating_recovery or updating_boot:
601 script.CacheFreeSpaceCheck(largest_source_size)
603 device_specific.IncrementalOTA_VerifyEnd()
605 script.Comment("---- start making changes here ----")
607 device_specific.IncrementalOTA_InstallBegin()
609 if OPTIONS.wipe_user_data:
610 script.Print("Erasing user data...")
611 script.FormatPartition("/data")
613 script.Print("Removing unneeded files...")
614 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
615 ["/"+i for i in sorted(source_data)
616 if i not in target_data] +
617 ["/system/recovery.img"])
619 script.ShowProgress(0.8, 0)
620 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
622 total_patch_size += target_boot.size
625 script.Print("Patching system files...")
626 deferred_patch_list = []
627 for item in patch_list:
628 fn, tf, sf, size, _ = item
629 if tf.name == "system/build.prop":
630 deferred_patch_list.append(item)
632 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
634 script.SetProgress(so_far / total_patch_size)
637 # Produce the boot image by applying a patch to the current
638 # contents of the boot partition, and write it back to the
640 script.Print("Patching boot image...")
641 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
642 % (boot_type, boot_device,
643 source_boot.size, source_boot.sha1,
644 target_boot.size, target_boot.sha1),
646 target_boot.size, target_boot.sha1,
647 source_boot.sha1, "patch/boot.img.p")
648 so_far += target_boot.size
649 script.SetProgress(so_far / total_patch_size)
650 print "boot image changed; including."
652 print "boot image unchanged; skipping."
654 if updating_recovery:
655 # Recovery is generated as a patch using both the boot image
656 # (which contains the same linux kernel as recovery) and the file
657 # /system/etc/recovery-resource.dat (which contains all the images
658 # used in the recovery UI) as sources. This lets us minimize the
659 # size of the patch, which must be included in every OTA package.
661 # For older builds where recovery-resource.dat is not present, we
662 # use only the boot image as the source.
664 MakeRecoveryPatch(OPTIONS.target_tmp, output_zip,
665 target_recovery, target_boot)
666 script.DeleteFiles(["/system/recovery-from-boot.p",
667 "/system/etc/install-recovery.sh"])
668 print "recovery image changed; including as patch from boot."
670 print "recovery image unchanged; skipping."
672 script.ShowProgress(0.1, 10)
674 target_symlinks = CopySystemFiles(target_zip, None)
676 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
677 temp_script = script.MakeTemporary()
678 Item.GetMetadata(target_zip)
679 Item.Get("system").SetPermissions(temp_script)
681 # Note that this call will mess up the tree of Items, so make sure
682 # we're done with it.
683 source_symlinks = CopySystemFiles(source_zip, None)
684 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
686 # Delete all the symlinks in source that aren't in target. This
687 # needs to happen before verbatim files are unpacked, in case a
688 # symlink in the source is replaced by a real file in the target.
690 for dest, link in source_symlinks:
691 if link not in target_symlinks_d:
692 to_delete.append(link)
693 script.DeleteFiles(to_delete)
696 script.Print("Unpacking new files...")
697 script.UnpackPackageDir("system", "/system")
699 if updating_recovery:
700 script.Print("Unpacking new recovery...")
701 script.UnpackPackageDir("recovery", "/system")
703 script.Print("Symlinks and permissions...")
705 # Create all the symlinks that don't already exist, or point to
706 # somewhere different than what we want. Delete each symlink before
707 # creating it, since the 'symlink' command won't overwrite.
709 for dest, link in target_symlinks:
710 if link in source_symlinks_d:
711 if dest != source_symlinks_d[link]:
712 to_create.append((dest, link))
714 to_create.append((dest, link))
715 script.DeleteFiles([i[1] for i in to_create])
716 script.MakeSymlinks(to_create)
718 # Now that the symlinks are created, we can set all the
720 script.AppendScript(temp_script)
722 # Do device-specific installation (eg, write radio image).
723 device_specific.IncrementalOTA_InstallEnd()
725 if OPTIONS.extra_script is not None:
726 script.AppendExtra(OPTIONS.extra_script)
728 # Patch the build.prop file last, so if something fails but the
729 # device can still come up, it appears to be the old build and will
730 # get set the OTA package again to retry.
731 script.Print("Patching remaining system files...")
732 for item in deferred_patch_list:
733 fn, tf, sf, size, _ = item
734 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
735 script.SetPermissions("/system/build.prop", 0, 0, 0644)
737 script.AddToZip(target_zip, output_zip)
738 WriteMetadata(metadata, output_zip)
743 def option_handler(o, a):
744 if o in ("-b", "--board_config"):
746 elif o in ("-k", "--package_key"):
747 OPTIONS.package_key = a
748 elif o in ("-i", "--incremental_from"):
749 OPTIONS.incremental_source = a
750 elif o in ("-w", "--wipe_user_data"):
751 OPTIONS.wipe_user_data = True
752 elif o in ("-n", "--no_prereq"):
753 OPTIONS.omit_prereq = True
754 elif o in ("-e", "--extra_script"):
755 OPTIONS.extra_script = a
756 elif o in ("-a", "--aslr_mode"):
757 if a in ("on", "On", "true", "True", "yes", "Yes"):
758 OPTIONS.aslr_mode = True
760 OPTIONS.aslr_mode = False
761 elif o in ("--worker_threads"):
762 OPTIONS.worker_threads = int(a)
767 args = common.ParseOptions(argv, __doc__,
768 extra_opts="b:k:i:d:wne:a:",
769 extra_long_opts=["board_config=",
778 extra_option_handler=option_handler)
781 common.Usage(__doc__)
784 if OPTIONS.extra_script is not None:
785 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
787 print "unzipping target target-files..."
788 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
790 OPTIONS.target_tmp = OPTIONS.input_tmp
791 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
793 print "--- target info ---"
794 common.DumpInfoDict(OPTIONS.info_dict)
796 if OPTIONS.device_specific is None:
797 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
798 if OPTIONS.device_specific is not None:
799 OPTIONS.device_specific = os.path.normpath(OPTIONS.device_specific)
800 print "using device-specific extensions in", OPTIONS.device_specific
802 temp_zip_file = tempfile.NamedTemporaryFile()
803 output_zip = zipfile.ZipFile(temp_zip_file, "w",
804 compression=zipfile.ZIP_DEFLATED)
806 if OPTIONS.incremental_source is None:
807 WriteFullOTAPackage(input_zip, output_zip)
808 if OPTIONS.package_key is None:
809 OPTIONS.package_key = OPTIONS.info_dict.get(
810 "default_system_dev_certificate",
811 "build/target/product/security/testkey")
813 print "unzipping source target-files..."
814 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
815 OPTIONS.target_info_dict = OPTIONS.info_dict
816 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
817 if OPTIONS.package_key is None:
818 OPTIONS.package_key = OPTIONS.source_info_dict.get(
819 "default_system_dev_certificate",
820 "build/target/product/security/testkey")
822 print "--- source info ---"
823 common.DumpInfoDict(OPTIONS.source_info_dict)
824 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
828 SignOutput(temp_zip_file.name, args[1])
829 temp_zip_file.close()
836 if __name__ == '__main__':
838 common.CloseInheritedPipes()
840 except common.ExternalError, e:
842 print " ERROR: %s" % (e,)