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 target_boot = common.GetBootableImage(
536 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
537 updating_boot = (source_boot.data != target_boot.data)
539 source_recovery = common.GetBootableImage(
540 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY")
541 target_recovery = common.GetBootableImage(
542 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
543 updating_recovery = (source_recovery.data != target_recovery.data)
545 # Here's how we divide up the progress bar:
546 # 0.1 for verifying the start state (PatchCheck calls)
547 # 0.8 for applying patches (ApplyPatch calls)
548 # 0.1 for unpacking verbatim files, symlinking, and doing the
549 # device-specific commands.
551 AppendAssertions(script, target_zip)
552 device_specific.IncrementalOTA_Assertions()
554 script.Print("Verifying current system...")
556 device_specific.IncrementalOTA_VerifyBegin()
558 script.ShowProgress(0.1, 0)
559 total_verify_size = float(sum([i[2].size for i in patch_list]) + 1)
561 total_verify_size += source_boot.size
564 for fn, tf, sf, size, patch_sha in patch_list:
565 script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
567 script.SetProgress(so_far / total_verify_size)
570 d = common.Difference(target_boot, source_boot)
571 _, _, d = d.ComputePatch()
572 print "boot target: %d source: %d diff: %d" % (
573 target_boot.size, source_boot.size, len(d))
575 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
577 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
579 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
580 (boot_type, boot_device,
581 source_boot.size, source_boot.sha1,
582 target_boot.size, target_boot.sha1))
583 so_far += source_boot.size
584 script.SetProgress(so_far / total_verify_size)
586 if patch_list or updating_recovery or updating_boot:
587 script.CacheFreeSpaceCheck(largest_source_size)
589 device_specific.IncrementalOTA_VerifyEnd()
591 script.Comment("---- start making changes here ----")
593 device_specific.IncrementalOTA_InstallBegin()
595 if OPTIONS.wipe_user_data:
596 script.Print("Erasing user data...")
597 script.FormatPartition("/data")
599 script.Print("Removing unneeded files...")
600 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
601 ["/"+i for i in sorted(source_data)
602 if i not in target_data] +
603 ["/system/recovery.img"])
605 script.ShowProgress(0.8, 0)
606 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
608 total_patch_size += target_boot.size
611 script.Print("Patching system files...")
612 deferred_patch_list = []
613 for item in patch_list:
614 fn, tf, sf, size, _ = item
615 if tf.name == "system/build.prop":
616 deferred_patch_list.append(item)
618 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
620 script.SetProgress(so_far / total_patch_size)
623 # Produce the boot image by applying a patch to the current
624 # contents of the boot partition, and write it back to the
626 script.Print("Patching boot image...")
627 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
628 % (boot_type, boot_device,
629 source_boot.size, source_boot.sha1,
630 target_boot.size, target_boot.sha1),
632 target_boot.size, target_boot.sha1,
633 source_boot.sha1, "patch/boot.img.p")
634 so_far += target_boot.size
635 script.SetProgress(so_far / total_patch_size)
636 print "boot image changed; including."
638 print "boot image unchanged; skipping."
640 if updating_recovery:
641 # Is it better to generate recovery as a patch from the current
642 # boot image, or from the previous recovery image? For large
643 # updates with significant kernel changes, probably the former.
644 # For small updates where the kernel hasn't changed, almost
645 # certainly the latter. We pick the first option. Future
646 # complicated schemes may let us effectively use both.
648 # A wacky possibility: as long as there is room in the boot
649 # partition, include the binaries and image files from recovery in
650 # the boot image (though not in the ramdisk) so they can be used
651 # as fodder for constructing the recovery image.
652 MakeRecoveryPatch(output_zip, target_recovery, target_boot)
653 script.DeleteFiles(["/system/recovery-from-boot.p",
654 "/system/etc/install-recovery.sh"])
655 print "recovery image changed; including as patch from boot."
657 print "recovery image unchanged; skipping."
659 script.ShowProgress(0.1, 10)
661 target_symlinks = CopySystemFiles(target_zip, None)
663 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
664 temp_script = script.MakeTemporary()
665 Item.GetMetadata(target_zip)
666 Item.Get("system").SetPermissions(temp_script)
668 # Note that this call will mess up the tree of Items, so make sure
669 # we're done with it.
670 source_symlinks = CopySystemFiles(source_zip, None)
671 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
673 # Delete all the symlinks in source that aren't in target. This
674 # needs to happen before verbatim files are unpacked, in case a
675 # symlink in the source is replaced by a real file in the target.
677 for dest, link in source_symlinks:
678 if link not in target_symlinks_d:
679 to_delete.append(link)
680 script.DeleteFiles(to_delete)
683 script.Print("Unpacking new files...")
684 script.UnpackPackageDir("system", "/system")
686 if updating_recovery:
687 script.Print("Unpacking new recovery...")
688 script.UnpackPackageDir("recovery", "/system")
690 script.Print("Symlinks and permissions...")
692 # Create all the symlinks that don't already exist, or point to
693 # somewhere different than what we want. Delete each symlink before
694 # creating it, since the 'symlink' command won't overwrite.
696 for dest, link in target_symlinks:
697 if link in source_symlinks_d:
698 if dest != source_symlinks_d[link]:
699 to_create.append((dest, link))
701 to_create.append((dest, link))
702 script.DeleteFiles([i[1] for i in to_create])
703 script.MakeSymlinks(to_create)
705 # Now that the symlinks are created, we can set all the
707 script.AppendScript(temp_script)
709 # Do device-specific installation (eg, write radio image).
710 device_specific.IncrementalOTA_InstallEnd()
712 if OPTIONS.extra_script is not None:
713 script.AppendExtra(OPTIONS.extra_script)
715 # Patch the build.prop file last, so if something fails but the
716 # device can still come up, it appears to be the old build and will
717 # get set the OTA package again to retry.
718 script.Print("Patching remaining system files...")
719 for item in deferred_patch_list:
720 fn, tf, sf, size, _ = item
721 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
722 script.SetPermissions("/system/build.prop", 0, 0, 0644)
724 script.AddToZip(target_zip, output_zip)
725 WriteMetadata(metadata, output_zip)
730 def option_handler(o, a):
731 if o in ("-b", "--board_config"):
733 elif o in ("-k", "--package_key"):
734 OPTIONS.package_key = a
735 elif o in ("-i", "--incremental_from"):
736 OPTIONS.incremental_source = a
737 elif o in ("-w", "--wipe_user_data"):
738 OPTIONS.wipe_user_data = True
739 elif o in ("-n", "--no_prereq"):
740 OPTIONS.omit_prereq = True
741 elif o in ("-e", "--extra_script"):
742 OPTIONS.extra_script = a
743 elif o in ("-a", "--aslr_mode"):
744 if a in ("on", "On", "true", "True", "yes", "Yes"):
745 OPTIONS.aslr_mode = True
747 OPTIONS.aslr_mode = False
748 elif o in ("--worker_threads"):
749 OPTIONS.worker_threads = int(a)
754 args = common.ParseOptions(argv, __doc__,
755 extra_opts="b:k:i:d:wne:a:",
756 extra_long_opts=["board_config=",
765 extra_option_handler=option_handler)
768 common.Usage(__doc__)
771 if OPTIONS.extra_script is not None:
772 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
774 print "unzipping target target-files..."
775 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
777 OPTIONS.target_tmp = OPTIONS.input_tmp
778 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
780 print "--- target info ---"
781 common.DumpInfoDict(OPTIONS.info_dict)
783 if OPTIONS.device_specific is None:
784 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
785 if OPTIONS.device_specific is not None:
786 OPTIONS.device_specific = os.path.normpath(OPTIONS.device_specific)
787 print "using device-specific extensions in", OPTIONS.device_specific
789 temp_zip_file = tempfile.NamedTemporaryFile()
790 output_zip = zipfile.ZipFile(temp_zip_file, "w",
791 compression=zipfile.ZIP_DEFLATED)
793 if OPTIONS.incremental_source is None:
794 WriteFullOTAPackage(input_zip, output_zip)
795 if OPTIONS.package_key is None:
796 OPTIONS.package_key = OPTIONS.info_dict.get(
797 "default_system_dev_certificate",
798 "build/target/product/security/testkey")
800 print "unzipping source target-files..."
801 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
802 OPTIONS.target_info_dict = OPTIONS.info_dict
803 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
804 if OPTIONS.package_key is None:
805 OPTIONS.package_key = OPTIONS.source_info_dict.get(
806 "default_system_dev_certificate",
807 "build/target/product/security/testkey")
809 print "--- source info ---"
810 common.DumpInfoDict(OPTIONS.source_info_dict)
811 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
815 SignOutput(temp_zip_file.name, args[1])
816 temp_zip_file.close()
823 if __name__ == '__main__':
825 common.CloseInheritedPipes()
827 except common.ExternalError, e:
829 print " ERROR: %s" % (e,)