support hooks for device-specific code in OTA package generation
Replace the installation of the "radio image", which is an
HTC-specific notion, with calls to device-specific python modules that
can add whatever additional OTA script commands are necessary. Add
the -s flag to specify the location of the device-specific script
(replacing the unused -s flag in sign_target_files_apks).
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 0b3803f..9ba85c6 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -15,6 +15,7 @@
import errno
import getopt
import getpass
+import imp
import os
import re
import shutil
@@ -33,7 +34,7 @@
OPTIONS.max_image_size = {}
OPTIONS.verbose = False
OPTIONS.tempfiles = []
-
+OPTIONS.device_specific = None
class ExternalError(RuntimeError): pass
@@ -233,6 +234,10 @@
Prepend <dir>/bin to the list of places to search for binaries
run by this script, and expect to find jars in <dir>/framework.
+ -s (--device_specific) <file>
+ Path to the python module containing device-specific
+ releasetools code.
+
-v (--verbose)
Show command lines being executed.
@@ -257,8 +262,9 @@
try:
opts, args = getopt.getopt(
- argv, "hvp:" + extra_opts,
- ["help", "verbose", "path="] + list(extra_long_opts))
+ argv, "hvp:s:" + extra_opts,
+ ["help", "verbose", "path=", "device_specific="] +
+ list(extra_long_opts))
except getopt.GetoptError, err:
Usage(docstring)
print "**", str(err), "**"
@@ -274,6 +280,8 @@
OPTIONS.verbose = True
elif o in ("-p", "--path"):
OPTIONS.search_path = a
+ elif o in ("-s", "--device_specific"):
+ OPTIONS.device_specific = a
else:
if extra_option_handler is None or not extra_option_handler(o, a):
assert False, "unknown option \"%s\"" % (o,)
@@ -398,3 +406,67 @@
zinfo.compress_type = zip.compression
zinfo.external_attr = perms << 16
zip.writestr(zinfo, data)
+
+
+class DeviceSpecificParams(object):
+ module = None
+ def __init__(self, **kwargs):
+ """Keyword arguments to the constructor become attributes of this
+ object, which is passed to all functions in the device-specific
+ module."""
+ for k, v in kwargs.iteritems():
+ setattr(self, k, v)
+
+ if self.module is None:
+ path = OPTIONS.device_specific
+ if path is None: return
+ if os.path.isdir(path):
+ info = imp.find_module("releasetools", [path])
+ else:
+ d, f = os.path.split(path)
+ b, x = os.path.splitext(f)
+ if x == ".py":
+ f = b
+ info = imp.find_module(f, [d])
+ if not info or info[0] is None:
+ raise ValueError("unable to find device-specific module")
+ self.module = imp.load_module("device_specific", *info)
+
+ def _DoCall(self, function_name, *args, **kwargs):
+ """Call the named function in the device-specific module, passing
+ the given args and kwargs. The first argument to the call will be
+ the DeviceSpecific object itself. If there is no module, or the
+ module does not define the function, return the value of the
+ 'default' kwarg (which itself defaults to None)."""
+ if self.module is None or not hasattr(self.module, function_name):
+ return kwargs.get("default", None)
+ return getattr(self.module, function_name)(*((self,) + args), **kwargs)
+
+ def FullOTA_Assertions(self):
+ """Called after emitting the block of assertions at the top of a
+ full OTA package. Implementations can add whatever additional
+ assertions they like."""
+ return self._DoCall("FullOTA_Assertions")
+
+ def FullOTA_InstallEnd(self):
+ """Called at the end of full OTA installation; typically this is
+ used to install the image for the device's baseband processor."""
+ return self._DoCall("FullOTA_InstallEnd")
+
+ def IncrementalOTA_Assertions(self):
+ """Called after emitting the block of assertions at the top of an
+ incremental OTA package. Implementations can add whatever
+ additional assertions they like."""
+ return self._DoCall("IncrementalOTA_Assertions")
+
+ def IncrementalOTA_VerifyEnd(self):
+ """Called at the end of the verification phase of incremental OTA
+ installation; additional checks can be placed here to abort the
+ script before any changes are made."""
+ return self._DoCall("IncrementalOTA_VerifyEnd")
+
+ def IncrementalOTA_InstallEnd(self):
+ """Called at the end of incremental OTA installation; typically
+ this is used to install the image for the device's baseband
+ processor."""
+ return self._DoCall("IncrementalOTA_InstallEnd")
diff --git a/tools/releasetools/ota_from_target_files b/tools/releasetools/ota_from_target_files
index 11e6695..7261b1b 100755
--- a/tools/releasetools/ota_from_target_files
+++ b/tools/releasetools/ota_from_target_files
@@ -302,19 +302,18 @@
# change very often.
script = edify_generator.EdifyGenerator(1)
+ device_specific = common.DeviceSpecificParams(
+ input_zip=input_zip,
+ output_zip=output_zip,
+ script=script,
+ input_tmp=OPTIONS.input_tmp)
+
if not OPTIONS.omit_prereq:
ts = GetBuildProp("ro.build.date.utc", input_zip)
script.AssertOlderBuild(ts)
AppendAssertions(script, input_zip)
-
- script.ShowProgress(0.1, 0)
-
- try:
- common.ZipWriteStr(output_zip, "radio.img", input_zip.read("RADIO/image"))
- script.WriteFirmwareImage("radio", "radio.img")
- except KeyError:
- pass
+ device_specific.FullOTA_Assertions()
script.ShowProgress(0.5, 0)
@@ -335,10 +334,12 @@
FixPermissions(script)
common.AddBoot(output_zip)
- script.ShowProgress(0.2, 0)
- script.WriteRawImage("boot", "boot.img")
script.ShowProgress(0.2, 10)
+ script.WriteRawImage("boot", "boot.img")
+
+ script.ShowProgress(0.1, 0)
+ device_specific.FullOTA_InstallEnd()
if OPTIONS.extra_script is not None:
script.AppendExtra(OPTIONS.extra_script)
@@ -454,6 +455,12 @@
else:
raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
+ device_specific = common.DeviceSpecificParams(
+ source_zip=source_zip,
+ target_zip=target_zip,
+ output_zip=output_zip,
+ script=script)
+
print "Loading target..."
target_data = LoadSystemFiles(target_zip)
print "Loading source..."
@@ -518,19 +525,15 @@
os.path.join(OPTIONS.target_tmp, "RECOVERY")))
updating_recovery = (source_recovery.data != target_recovery.data)
- source_radio = source_zip.read("RADIO/image")
- target_radio = target_zip.read("RADIO/image")
- updating_radio = (source_radio != target_radio)
-
- # The last 0.1 is reserved for creating symlinks, fixing
- # permissions, and writing the boot image (if necessary).
- progress_bar_total = 1.0
+ # We reserve the last 0.3 of the progress bar for the
+ # device-specific IncrementalOTA_InstallEnd() call at the end, which
+ # will typically install a radio image.
+ progress_bar_total = 0.7
if updating_boot:
progress_bar_total -= 0.1
- if updating_radio:
- progress_bar_total -= 0.3
AppendAssertions(script, target_zip)
+ device_specific.IncrementalOTA_Assertions()
script.Print("Verifying current system...")
@@ -572,6 +575,8 @@
script.Print("Unpacking patches...")
script.UnpackPackageDir("patch", "/tmp/patchtmp")
+ device_specific.IncrementalOTA_VerifyEnd()
+
script.Comment("---- start making changes here ----")
if OPTIONS.wipe_user_data:
@@ -610,15 +615,6 @@
else:
print "recovery image unchanged; skipping."
- if updating_radio:
- script.ShowProgress(0.3, 10)
- script.Print("Writing radio image...")
- script.WriteFirmwareImage("radio", "radio.img")
- common.ZipWriteStr(output_zip, "radio.img", target_radio)
- print "radio image changed; including."
- else:
- print "radio image unchanged; skipping."
-
script.Print("Patching system files...")
pb_apply = progress_bar_total * 0.7 * \
(total_patched_size /
@@ -658,7 +654,7 @@
script.Print("Unpacking new files...")
script.UnpackPackageDir("system", "/system")
- script.Print("Finishing up...")
+ script.Print("Symlinks and permissions...")
# Create all the symlinks that don't already exist, or point to
# somewhere different than what we want. Delete each symlink before
@@ -677,6 +673,10 @@
# permissions.
script.AppendScript(temp_script)
+ # Write the radio image, if necessary.
+ script.ShowProgress(0.3, 10)
+ device_specific.IncrementalOTA_InstallEnd()
+
if OPTIONS.extra_script is not None:
scirpt.AppendExtra(OPTIONS.extra_script)
diff --git a/tools/releasetools/sign_target_files_apks b/tools/releasetools/sign_target_files_apks
index 6dd8ede..5153398 100755
--- a/tools/releasetools/sign_target_files_apks
+++ b/tools/releasetools/sign_target_files_apks
@@ -20,10 +20,6 @@
Usage: sign_target_files_apks [flags] input_target_files output_target_files
- -s (--signapk_jar) <path>
- Path of the signapks.jar file used to sign an individual APK
- file.
-
-e (--extra_apks) <name,name,...=key>
Add extra APK name/key pairs as though they appeared in
apkcerts.txt (so mappings specified by -k and -d are applied).
@@ -302,9 +298,7 @@
def main(argv):
def option_handler(o, a):
- if o in ("-s", "--signapk_jar"):
- OPTIONS.signapk_jar = a
- elif o in ("-e", "--extra_apks"):
+ if o in ("-e", "--extra_apks"):
names, key = a.split("=")
names = names.split(",")
for n in names:
@@ -334,9 +328,8 @@
return True
args = common.ParseOptions(argv, __doc__,
- extra_opts="s:e:d:k:ot:",
- extra_long_opts=["signapk_jar=",
- "extra_apks=",
+ extra_opts="e:d:k:ot:",
+ extra_long_opts=["extra_apks=",
"default_key_mappings=",
"key_mapping=",
"replace_ota_keys",