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, input_zip):
307 device = GetBuildProp("ro.product.device", input_zip)
308 script.AssertDevice(device)
311 def MakeRecoveryPatch(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 d = common.Difference(recovery_img, boot_img)
328 _, _, patch = d.ComputePatch()
329 common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
330 Item.Get("system/recovery-from-boot.p", dir=False)
332 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
333 recovery_type, recovery_device = common.GetTypeAndDevice("/recovery", OPTIONS.info_dict)
335 sh = """#!/system/bin/sh
336 if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
337 log -t recovery "Installing new recovery image"
338 applypatch %(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
340 log -t recovery "Recovery image already installed"
342 """ % { 'boot_size': boot_img.size,
343 'boot_sha1': boot_img.sha1,
344 'recovery_size': recovery_img.size,
345 'recovery_sha1': recovery_img.sha1,
346 'boot_type': boot_type,
347 'boot_device': boot_device,
348 'recovery_type': recovery_type,
349 'recovery_device': recovery_device,
351 common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
352 return Item.Get("system/etc/install-recovery.sh", dir=False)
355 def WriteFullOTAPackage(input_zip, output_zip):
356 # TODO: how to determine this? We don't know what version it will
357 # be installed on top of. For now, we expect the API just won't
359 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
361 metadata = {"post-build": GetBuildProp("ro.build.fingerprint", input_zip),
362 "pre-device": GetBuildProp("ro.product.device", input_zip),
363 "post-timestamp": GetBuildProp("ro.build.date.utc", input_zip),
366 device_specific = common.DeviceSpecificParams(
368 input_version=OPTIONS.info_dict["recovery_api_version"],
369 output_zip=output_zip,
371 input_tmp=OPTIONS.input_tmp,
373 info_dict=OPTIONS.info_dict)
375 if not OPTIONS.omit_prereq:
376 ts = GetBuildProp("ro.build.date.utc", input_zip)
377 script.AssertOlderBuild(ts)
379 AppendAssertions(script, input_zip)
380 device_specific.FullOTA_Assertions()
381 device_specific.FullOTA_InstallBegin()
383 script.ShowProgress(0.5, 0)
385 if OPTIONS.wipe_user_data:
386 script.FormatPartition("/data")
388 if "selinux_fc" in OPTIONS.info_dict:
389 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
391 script.FormatPartition("/system")
392 script.Mount("/system")
393 script.UnpackPackageDir("recovery", "/system")
394 script.UnpackPackageDir("system", "/system")
396 symlinks = CopySystemFiles(input_zip, output_zip)
397 script.MakeSymlinks(symlinks)
399 boot_img = common.GetBootableImage("boot.img", "boot.img",
400 OPTIONS.input_tmp, "BOOT")
401 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
402 OPTIONS.input_tmp, "RECOVERY")
403 MakeRecoveryPatch(output_zip, recovery_img, boot_img)
405 Item.GetMetadata(input_zip)
406 Item.Get("system").SetPermissions(script)
408 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
409 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
410 script.ShowProgress(0.2, 0)
412 script.ShowProgress(0.2, 10)
413 script.WriteRawImage("/boot", "boot.img")
415 script.ShowProgress(0.1, 0)
416 device_specific.FullOTA_InstallEnd()
418 if OPTIONS.extra_script is not None:
419 script.AppendExtra(OPTIONS.extra_script)
422 script.AddToZip(input_zip, output_zip)
423 WriteMetadata(metadata, output_zip)
425 def WritePolicyConfig(file_context, output_zip):
426 f = open(file_context, 'r');
427 basename = os.path.basename(file_context)
428 common.ZipWriteStr(output_zip, basename, f.read())
431 def WriteMetadata(metadata, output_zip):
432 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
433 "".join(["%s=%s\n" % kv
434 for kv in sorted(metadata.iteritems())]))
436 def LoadSystemFiles(z):
437 """Load all the files from SYSTEM/... in a given target-files
438 ZipFile, and return a dict of {filename: File object}."""
440 for info in z.infolist():
441 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
442 basefilename = info.filename[7:]
443 fn = "system/" + basefilename
444 data = z.read(info.filename)
445 out[fn] = common.File(fn, data)
449 def GetBuildProp(property, z):
450 """Return the fingerprint of the build of a given target-files
452 bp = z.read("SYSTEM/build.prop")
455 m = re.search(re.escape(property) + r"=(.*)\n", bp)
457 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
458 return m.group(1).strip()
461 def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
462 source_version = OPTIONS.source_info_dict["recovery_api_version"]
463 target_version = OPTIONS.target_info_dict["recovery_api_version"]
465 if source_version == 0:
466 print ("WARNING: generating edify script for a source that "
468 script = edify_generator.EdifyGenerator(source_version, OPTIONS.target_info_dict)
470 metadata = {"pre-device": GetBuildProp("ro.product.device", source_zip),
471 "post-timestamp": GetBuildProp("ro.build.date.utc", target_zip),
474 device_specific = common.DeviceSpecificParams(
475 source_zip=source_zip,
476 source_version=source_version,
477 target_zip=target_zip,
478 target_version=target_version,
479 output_zip=output_zip,
482 info_dict=OPTIONS.info_dict)
484 print "Loading target..."
485 target_data = LoadSystemFiles(target_zip)
486 print "Loading source..."
487 source_data = LoadSystemFiles(source_zip)
489 verbatim_targets = []
492 largest_source_size = 0
493 for fn in sorted(target_data.keys()):
496 sf = source_data.get(fn, None)
498 if sf is None or fn in OPTIONS.require_verbatim:
499 # This file should be included verbatim
500 if fn in OPTIONS.prohibit_verbatim:
501 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
502 print "send", fn, "verbatim"
503 tf.AddToZip(output_zip)
504 verbatim_targets.append((fn, tf.size))
505 elif tf.sha1 != sf.sha1:
506 # File is different; consider sending as a patch
507 diffs.append(common.Difference(tf, sf))
509 # Target file identical to source.
512 common.ComputeDifferences(diffs)
515 tf, sf, d = diff.GetPatch()
516 if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
517 # patch is almost as big as the file; don't bother patching
518 tf.AddToZip(output_zip)
519 verbatim_targets.append((tf.name, tf.size))
521 common.ZipWriteStr(output_zip, "patch/" + tf.name + ".p", d)
522 patch_list.append((tf.name, tf, sf, tf.size, common.sha1(d).hexdigest()))
523 largest_source_size = max(largest_source_size, sf.size)
525 source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
526 target_fp = GetBuildProp("ro.build.fingerprint", target_zip)
527 metadata["pre-build"] = source_fp
528 metadata["post-build"] = target_fp
530 script.Mount("/system")
531 script.AssertSomeFingerprint(source_fp, target_fp)
533 source_boot = common.GetBootableImage(
534 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
535 OPTIONS.source_info_dict)
536 target_boot = common.GetBootableImage(
537 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
538 updating_boot = (source_boot.data != target_boot.data)
540 source_recovery = common.GetBootableImage(
541 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
542 OPTIONS.source_info_dict)
543 target_recovery = common.GetBootableImage(
544 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
545 updating_recovery = (source_recovery.data != target_recovery.data)
547 # Here's how we divide up the progress bar:
548 # 0.1 for verifying the start state (PatchCheck calls)
549 # 0.8 for applying patches (ApplyPatch calls)
550 # 0.1 for unpacking verbatim files, symlinking, and doing the
551 # device-specific commands.
553 AppendAssertions(script, target_zip)
554 device_specific.IncrementalOTA_Assertions()
556 script.Print("Verifying current system...")
558 device_specific.IncrementalOTA_VerifyBegin()
560 script.ShowProgress(0.1, 0)
561 total_verify_size = float(sum([i[2].size for i in patch_list]) + 1)
563 total_verify_size += source_boot.size
566 for fn, tf, sf, size, patch_sha in patch_list:
567 script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
569 script.SetProgress(so_far / total_verify_size)
572 d = common.Difference(target_boot, source_boot)
573 _, _, d = d.ComputePatch()
574 print "boot target: %d source: %d diff: %d" % (
575 target_boot.size, source_boot.size, len(d))
577 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
579 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
581 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
582 (boot_type, boot_device,
583 source_boot.size, source_boot.sha1,
584 target_boot.size, target_boot.sha1))
585 so_far += source_boot.size
586 script.SetProgress(so_far / total_verify_size)
588 if patch_list or updating_recovery or updating_boot:
589 script.CacheFreeSpaceCheck(largest_source_size)
591 device_specific.IncrementalOTA_VerifyEnd()
593 script.Comment("---- start making changes here ----")
595 device_specific.IncrementalOTA_InstallBegin()
597 if OPTIONS.wipe_user_data:
598 script.Print("Erasing user data...")
599 script.FormatPartition("/data")
601 script.Print("Removing unneeded files...")
602 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
603 ["/"+i for i in sorted(source_data)
604 if i not in target_data] +
605 ["/system/recovery.img"])
607 script.ShowProgress(0.8, 0)
608 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
610 total_patch_size += target_boot.size
613 script.Print("Patching system files...")
614 deferred_patch_list = []
615 for item in patch_list:
616 fn, tf, sf, size, _ = item
617 if tf.name == "system/build.prop":
618 deferred_patch_list.append(item)
620 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
622 script.SetProgress(so_far / total_patch_size)
625 # Produce the boot image by applying a patch to the current
626 # contents of the boot partition, and write it back to the
628 script.Print("Patching boot image...")
629 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
630 % (boot_type, boot_device,
631 source_boot.size, source_boot.sha1,
632 target_boot.size, target_boot.sha1),
634 target_boot.size, target_boot.sha1,
635 source_boot.sha1, "patch/boot.img.p")
636 so_far += target_boot.size
637 script.SetProgress(so_far / total_patch_size)
638 print "boot image changed; including."
640 print "boot image unchanged; skipping."
642 if updating_recovery:
643 # Is it better to generate recovery as a patch from the current
644 # boot image, or from the previous recovery image? For large
645 # updates with significant kernel changes, probably the former.
646 # For small updates where the kernel hasn't changed, almost
647 # certainly the latter. We pick the first option. Future
648 # complicated schemes may let us effectively use both.
650 # A wacky possibility: as long as there is room in the boot
651 # partition, include the binaries and image files from recovery in
652 # the boot image (though not in the ramdisk) so they can be used
653 # as fodder for constructing the recovery image.
654 MakeRecoveryPatch(output_zip, target_recovery, target_boot)
655 script.DeleteFiles(["/system/recovery-from-boot.p",
656 "/system/etc/install-recovery.sh"])
657 print "recovery image changed; including as patch from boot."
659 print "recovery image unchanged; skipping."
661 script.ShowProgress(0.1, 10)
663 target_symlinks = CopySystemFiles(target_zip, None)
665 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
666 temp_script = script.MakeTemporary()
667 Item.GetMetadata(target_zip)
668 Item.Get("system").SetPermissions(temp_script)
670 # Note that this call will mess up the tree of Items, so make sure
671 # we're done with it.
672 source_symlinks = CopySystemFiles(source_zip, None)
673 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
675 # Delete all the symlinks in source that aren't in target. This
676 # needs to happen before verbatim files are unpacked, in case a
677 # symlink in the source is replaced by a real file in the target.
679 for dest, link in source_symlinks:
680 if link not in target_symlinks_d:
681 to_delete.append(link)
682 script.DeleteFiles(to_delete)
685 script.Print("Unpacking new files...")
686 script.UnpackPackageDir("system", "/system")
688 if updating_recovery:
689 script.Print("Unpacking new recovery...")
690 script.UnpackPackageDir("recovery", "/system")
692 script.Print("Symlinks and permissions...")
694 # Create all the symlinks that don't already exist, or point to
695 # somewhere different than what we want. Delete each symlink before
696 # creating it, since the 'symlink' command won't overwrite.
698 for dest, link in target_symlinks:
699 if link in source_symlinks_d:
700 if dest != source_symlinks_d[link]:
701 to_create.append((dest, link))
703 to_create.append((dest, link))
704 script.DeleteFiles([i[1] for i in to_create])
705 script.MakeSymlinks(to_create)
707 # Now that the symlinks are created, we can set all the
709 script.AppendScript(temp_script)
711 # Do device-specific installation (eg, write radio image).
712 device_specific.IncrementalOTA_InstallEnd()
714 if OPTIONS.extra_script is not None:
715 script.AppendExtra(OPTIONS.extra_script)
717 # Patch the build.prop file last, so if something fails but the
718 # device can still come up, it appears to be the old build and will
719 # get set the OTA package again to retry.
720 script.Print("Patching remaining system files...")
721 for item in deferred_patch_list:
722 fn, tf, sf, size, _ = item
723 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
724 script.SetPermissions("/system/build.prop", 0, 0, 0644)
726 script.AddToZip(target_zip, output_zip)
727 WriteMetadata(metadata, output_zip)
732 def option_handler(o, a):
733 if o in ("-b", "--board_config"):
735 elif o in ("-k", "--package_key"):
736 OPTIONS.package_key = a
737 elif o in ("-i", "--incremental_from"):
738 OPTIONS.incremental_source = a
739 elif o in ("-w", "--wipe_user_data"):
740 OPTIONS.wipe_user_data = True
741 elif o in ("-n", "--no_prereq"):
742 OPTIONS.omit_prereq = True
743 elif o in ("-e", "--extra_script"):
744 OPTIONS.extra_script = a
745 elif o in ("-a", "--aslr_mode"):
746 if a in ("on", "On", "true", "True", "yes", "Yes"):
747 OPTIONS.aslr_mode = True
749 OPTIONS.aslr_mode = False
750 elif o in ("--worker_threads"):
751 OPTIONS.worker_threads = int(a)
756 args = common.ParseOptions(argv, __doc__,
757 extra_opts="b:k:i:d:wne:a:",
758 extra_long_opts=["board_config=",
767 extra_option_handler=option_handler)
770 common.Usage(__doc__)
773 if OPTIONS.extra_script is not None:
774 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
776 print "unzipping target target-files..."
777 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
779 OPTIONS.target_tmp = OPTIONS.input_tmp
780 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
782 print "--- target info ---"
783 common.DumpInfoDict(OPTIONS.info_dict)
785 if OPTIONS.device_specific is None:
786 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
787 if OPTIONS.device_specific is not None:
788 OPTIONS.device_specific = os.path.normpath(OPTIONS.device_specific)
789 print "using device-specific extensions in", OPTIONS.device_specific
791 temp_zip_file = tempfile.NamedTemporaryFile()
792 output_zip = zipfile.ZipFile(temp_zip_file, "w",
793 compression=zipfile.ZIP_DEFLATED)
795 if OPTIONS.incremental_source is None:
796 WriteFullOTAPackage(input_zip, output_zip)
797 if OPTIONS.package_key is None:
798 OPTIONS.package_key = OPTIONS.info_dict.get(
799 "default_system_dev_certificate",
800 "build/target/product/security/testkey")
802 print "unzipping source target-files..."
803 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
804 OPTIONS.target_info_dict = OPTIONS.info_dict
805 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
806 if OPTIONS.package_key is None:
807 OPTIONS.package_key = OPTIONS.source_info_dict.get(
808 "default_system_dev_certificate",
809 "build/target/product/security/testkey")
811 print "--- source info ---"
812 common.DumpInfoDict(OPTIONS.source_info_dict)
813 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
817 SignOutput(temp_zip_file.name, args[1])
818 temp_zip_file.close()
825 if __name__ == '__main__':
827 common.CloseInheritedPipes()
829 except common.ExternalError, e:
831 print " ERROR: %s" % (e,)