forked from openkylin/platform_build
generate OTA packages that use edify
Split the details of generating script syntax into a generator class: one for amend (whose output should be equivalent to the current output), and one for edify. Fix 'otatools' build rule to build imgdiff.
This commit is contained in:
parent
602a84e0bb
commit
c494d7cee8
|
@ -754,6 +754,7 @@ otatools: $(HOST_OUT_EXECUTABLES)/minigzip \
|
|||
$(HOST_OUT_EXECUTABLES)/zipalign \
|
||||
$(HOST_OUT_EXECUTABLES)/aapt \
|
||||
$(HOST_OUT_EXECUTABLES)/bsdiff \
|
||||
$(HOST_OUT_EXECUTABLES)/imgdiff \
|
||||
$(HOST_OUT_JAVA_LIBRARIES)/dumpkey.jar \
|
||||
$(HOST_OUT_JAVA_LIBRARIES)/signapk.jar
|
||||
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
# Copyright (C) 2009 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
|
||||
import common
|
||||
|
||||
class AmendGenerator(object):
|
||||
"""Class to generate scripts in the 'amend' recovery script language
|
||||
used up through cupcake."""
|
||||
|
||||
def __init__(self):
|
||||
self.script = ['assert compatible_with("0.2") == "true"']
|
||||
self.included_files = set()
|
||||
|
||||
def MakeTemporary(self):
|
||||
"""Make a temporary script object whose commands can latter be
|
||||
appended to the parent script with AppendScript(). Used when the
|
||||
caller wants to generate script commands out-of-order."""
|
||||
x = AmendGenerator()
|
||||
x.script = []
|
||||
x.included_files = self.included_files
|
||||
return x
|
||||
|
||||
@staticmethod
|
||||
def _FileRoot(fn):
|
||||
"""Convert a file path to the 'root' notation used by amend."""
|
||||
if fn.startswith("/system/"):
|
||||
return "SYSTEM:" + fn[8:]
|
||||
elif fn == "/system":
|
||||
return "SYSTEM:"
|
||||
elif fn.startswith("/tmp/"):
|
||||
return "CACHE:.." + fn
|
||||
else:
|
||||
raise ValueError("don't know root for \"%s\"" % (fn,))
|
||||
|
||||
@staticmethod
|
||||
def _PartitionRoot(partition):
|
||||
"""Convert a partition name to the 'root' notation used by amend."""
|
||||
if partition == "userdata":
|
||||
return "DATA:"
|
||||
else:
|
||||
return partition.upper() + ":"
|
||||
|
||||
def AppendScript(self, other):
|
||||
"""Append the contents of another script (which should be created
|
||||
with temporary=True) to this one."""
|
||||
self.script.extend(other.script)
|
||||
self.included_files.update(other.included_files)
|
||||
|
||||
def AssertSomeFingerprint(self, *fp):
|
||||
"""Assert that the current fingerprint is one of *fp."""
|
||||
x = [('file_contains("SYSTEM:build.prop", '
|
||||
'"ro.build.fingerprint=%s") == "true"') % i for i in fp]
|
||||
self.script.append("assert %s" % (" || ".join(x),))
|
||||
|
||||
def AssertOlderBuild(self, timestamp):
|
||||
"""Assert that the build on the device is older (or the same as)
|
||||
the given timestamp."""
|
||||
self.script.append("run_program PACKAGE:check_prereq %s" % (timestamp,))
|
||||
self.included_files.add("check_prereq")
|
||||
|
||||
def AssertDevice(self, device):
|
||||
"""Assert that the device identifier is the given string."""
|
||||
self.script.append('assert getprop("ro.product.device") == "%s" || '
|
||||
'getprop("ro.build.product") == "%s"' % (device, device))
|
||||
|
||||
def AssertSomeBootloader(self, *bootloaders):
|
||||
"""Asert that the bootloader version is one of *bootloaders."""
|
||||
self.script.append("assert " +
|
||||
" || ".join(['getprop("ro.bootloader") == "%s"' % (b,)
|
||||
for b in bootloaders]))
|
||||
|
||||
def ShowProgress(self, frac, dur):
|
||||
"""Update the progress bar, advancing it over 'frac' over the next
|
||||
'dur' seconds."""
|
||||
self.script.append("show_progress %f %d" % (frac, int(dur)))
|
||||
|
||||
def PatchCheck(self, filename, *sha1):
|
||||
"""Check that the given file (or MTD reference) has one of the
|
||||
given *sha1 hashes."""
|
||||
out = ["run_program PACKAGE:applypatch -c %s" % (filename,)]
|
||||
for i in sha1:
|
||||
out.append(" " + i)
|
||||
self.script.append("".join(out))
|
||||
self.included_files.add("applypatch")
|
||||
|
||||
def CacheFreeSpaceCheck(self, amount):
|
||||
"""Check that there's at least 'amount' space that can be made
|
||||
available on /cache."""
|
||||
self.script.append("run_program PACKAGE:applypatch -s %d" % (amount,))
|
||||
self.included_files.add("applypatch")
|
||||
|
||||
def Mount(self, kind, what, path):
|
||||
# no-op; amend uses it's 'roots' system to automatically mount
|
||||
# things when they're referred to
|
||||
pass
|
||||
|
||||
def UnpackPackageDir(self, src, dst):
|
||||
"""Unpack a given directory from the OTA package into the given
|
||||
destination directory."""
|
||||
dst = self._FileRoot(dst)
|
||||
self.script.append("copy_dir PACKAGE:%s %s" % (src, dst))
|
||||
|
||||
def Comment(self, comment):
|
||||
"""Write a comment into the update script."""
|
||||
self.script.append("")
|
||||
for i in comment.split("\n"):
|
||||
self.script.append("# " + i)
|
||||
self.script.append("")
|
||||
|
||||
def Print(self, message):
|
||||
"""Log a message to the screen (if the logs are visible)."""
|
||||
# no way to do this from amend; substitute a script comment instead
|
||||
self.Comment(message)
|
||||
|
||||
def FormatPartition(self, partition):
|
||||
"""Format the given MTD partition."""
|
||||
self.script.append("format %s" % (self._PartitionRoot(partition),))
|
||||
|
||||
def DeleteFiles(self, file_list):
|
||||
"""Delete all files in file_list."""
|
||||
line = []
|
||||
t = 0
|
||||
for i in file_list:
|
||||
i = self._FileRoot(i)
|
||||
line.append(i)
|
||||
t += len(i) + 1
|
||||
if t > 80:
|
||||
self.script.append("delete " + " ".join(line))
|
||||
line = []
|
||||
t = 0
|
||||
if line:
|
||||
self.script.append("delete " + " ".join(line))
|
||||
|
||||
def ApplyPatch(self, srcfile, tgtfile, tgtsize, tgtsha1, *patchpairs):
|
||||
"""Apply binary patches (in *patchpairs) to the given srcfile to
|
||||
produce tgtfile (which may be "-" to indicate overwriting the
|
||||
source file."""
|
||||
if len(patchpairs) % 2 != 0:
|
||||
raise ValueError("bad patches given to ApplyPatch")
|
||||
self.script.append(
|
||||
("run_program PACKAGE:applypatch %s %s %s %d " %
|
||||
(srcfile, tgtfile, tgtsha1, tgtsize)) +
|
||||
" ".join(["%s:%s" % patchpairs[i:i+2]
|
||||
for i in range(0, len(patchpairs), 2)]))
|
||||
self.included_files.add("applypatch")
|
||||
|
||||
def WriteFirmwareImage(self, kind, fn):
|
||||
"""Arrange to update the given firmware image (kind must be
|
||||
"hboot" or "radio") when recovery finishes."""
|
||||
self.script.append("write_%s_image PACKAGE:%s" % (kind, fn))
|
||||
|
||||
def WriteRawImage(self, partition, fn):
|
||||
"""Write the given file into the given MTD partition."""
|
||||
self.script.append("write_raw_image PACKAGE:%s %s" %
|
||||
(fn, self._PartitionRoot(partition)))
|
||||
|
||||
def SetPermissions(self, fn, uid, gid, mode):
|
||||
"""Set file ownership and permissions."""
|
||||
fn = self._FileRoot(fn)
|
||||
self.script.append("set_perm %d %d 0%o %s" % (uid, gid, mode, fn))
|
||||
|
||||
def SetPermissionsRecursive(self, fn, uid, gid, dmode, fmode):
|
||||
"""Recursively set path ownership and permissions."""
|
||||
fn = self._FileRoot(fn)
|
||||
self.script.append("set_perm_recursive %d %d 0%o 0%o %s" %
|
||||
(uid, gid, dmode, fmode, fn))
|
||||
|
||||
def MakeSymlinks(self, symlink_list):
|
||||
"""Create symlinks, given a list of (dest, link) pairs."""
|
||||
self.script.extend(["symlink %s %s" % (i[0], self._FileRoot(i[1]))
|
||||
for i in sorted(symlink_list)])
|
||||
|
||||
def AppendExtra(self, extra):
|
||||
"""Append text verbatim to the output script."""
|
||||
self.script.append(extra)
|
||||
|
||||
def AddToZip(self, input_zip, output_zip, input_path=None):
|
||||
"""Write the accumulated script to the output_zip file. input_zip
|
||||
is used as the source for any ancillary binaries needed by the
|
||||
script. If input_path is not None, it will be used as a local
|
||||
path for binaries instead of input_zip."""
|
||||
common.ZipWriteStr(output_zip, "META-INF/com/google/android/update-script",
|
||||
"\n".join(self.script) + "\n")
|
||||
for i in self.included_files:
|
||||
try:
|
||||
if input_path is None:
|
||||
data = input_zip.read(os.path.join("OTA/bin", i))
|
||||
else:
|
||||
data = open(os.path.join(input_path, i)).read()
|
||||
common.ZipWriteStr(output_zip, i, data, perms=0755)
|
||||
except (IOError, KeyError), e:
|
||||
raise ExternalError("unable to include binary %s: %s" % (i, e))
|
|
@ -0,0 +1,226 @@
|
|||
# Copyright (C) 2009 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
import common
|
||||
|
||||
class EdifyGenerator(object):
|
||||
"""Class to generate scripts in the 'edify' recovery script language
|
||||
used from donut onwards."""
|
||||
|
||||
def __init__(self, version):
|
||||
self.script = []
|
||||
self.mounts = set()
|
||||
self.version = version
|
||||
|
||||
def MakeTemporary(self):
|
||||
"""Make a temporary script object whose commands can latter be
|
||||
appended to the parent script with AppendScript(). Used when the
|
||||
caller wants to generate script commands out-of-order."""
|
||||
x = EdifyGenerator(self.version)
|
||||
x.mounts = self.mounts
|
||||
return x
|
||||
|
||||
@staticmethod
|
||||
def _WordWrap(cmd, linelen=80):
|
||||
"""'cmd' should be a function call with null characters after each
|
||||
parameter (eg, "somefun(foo,\0bar,\0baz)"). This function wraps cmd
|
||||
to a given line length, replacing nulls with spaces and/or newlines
|
||||
to format it nicely."""
|
||||
indent = cmd.index("(")+1
|
||||
out = []
|
||||
first = True
|
||||
x = re.compile("^(.{,%d})\0" % (linelen-indent,))
|
||||
while True:
|
||||
if not first:
|
||||
out.append(" " * indent)
|
||||
first = False
|
||||
m = x.search(cmd)
|
||||
if not m:
|
||||
parts = cmd.split("\0", 1)
|
||||
out.append(parts[0]+"\n")
|
||||
if len(parts) == 1:
|
||||
break
|
||||
else:
|
||||
cmd = parts[1]
|
||||
continue
|
||||
out.append(m.group(1)+"\n")
|
||||
cmd = cmd[m.end():]
|
||||
|
||||
return "".join(out).replace("\0", " ").rstrip("\n")
|
||||
|
||||
def AppendScript(self, other):
|
||||
"""Append the contents of another script (which should be created
|
||||
with temporary=True) to this one."""
|
||||
self.script.extend(other.script)
|
||||
|
||||
def AssertSomeFingerprint(self, *fp):
|
||||
"""Assert that the current system build fingerprint is one of *fp."""
|
||||
if not fp:
|
||||
raise ValueError("must specify some fingerprints")
|
||||
cmd = ('assert(' +
|
||||
' ||\0'.join([('file_getprop("/system/build.prop", '
|
||||
'"ro.build.fingerprint") == "%s"')
|
||||
% i for i in fp]) +
|
||||
');')
|
||||
self.script.append(self._WordWrap(cmd))
|
||||
|
||||
def AssertOlderBuild(self, timestamp):
|
||||
"""Assert that the build on the device is older (or the same as)
|
||||
the given timestamp."""
|
||||
self.script.append(('assert(!less_than_int(%s, '
|
||||
'getprop("ro.build.date.utc")));') % (timestamp,))
|
||||
|
||||
def AssertDevice(self, device):
|
||||
"""Assert that the device identifier is the given string."""
|
||||
cmd = ('assert(getprop("ro.product.device") == "%s" ||\0'
|
||||
'getprop("ro.build.product") == "%s");' % (device, device))
|
||||
self.script.append(self._WordWrap(cmd))
|
||||
|
||||
def AssertSomeBootloader(self, *bootloaders):
|
||||
"""Asert that the bootloader version is one of *bootloaders."""
|
||||
cmd = ("assert(" +
|
||||
" ||\0".join(['getprop("ro.bootloader") == "%s"' % (b,)
|
||||
for b in bootloaders]) +
|
||||
");")
|
||||
self.script.append(self._WordWrap(cmd))
|
||||
|
||||
def ShowProgress(self, frac, dur):
|
||||
"""Update the progress bar, advancing it over 'frac' over the next
|
||||
'dur' seconds."""
|
||||
self.script.append("show_progress(%f, %d);" % (frac, int(dur)))
|
||||
|
||||
def PatchCheck(self, filename, *sha1):
|
||||
"""Check that the given file (or MTD reference) has one of the
|
||||
given *sha1 hashes."""
|
||||
self.script.append('assert(apply_patch_check("%s"' % (filename,) +
|
||||
"".join([', "%s"' % (i,) for i in sha1]) +
|
||||
'));')
|
||||
|
||||
def CacheFreeSpaceCheck(self, amount):
|
||||
"""Check that there's at least 'amount' space that can be made
|
||||
available on /cache."""
|
||||
self.script.append("assert(apply_patch_space(%d));" % (amount,))
|
||||
|
||||
def Mount(self, kind, what, path):
|
||||
"""Mount the given 'what' at the given path. 'what' should be a
|
||||
partition name if kind is "MTD", or a block device if kind is
|
||||
"vfat". No other values of 'kind' are supported."""
|
||||
self.script.append('mount("%s", "%s", "%s");' % (kind, what, path))
|
||||
self.mounts.add(path)
|
||||
|
||||
def UnpackPackageDir(self, src, dst):
|
||||
"""Unpack a given directory from the OTA package into the given
|
||||
destination directory."""
|
||||
self.script.append('package_extract_dir("%s", "%s");' % (src, dst))
|
||||
|
||||
def Comment(self, comment):
|
||||
"""Write a comment into the update script."""
|
||||
self.script.append("")
|
||||
for i in comment.split("\n"):
|
||||
self.script.append("# " + i)
|
||||
self.script.append("")
|
||||
|
||||
def Print(self, message):
|
||||
"""Log a message to the screen (if the logs are visible)."""
|
||||
self.script.append('ui_print("%s");' % (message,))
|
||||
|
||||
def FormatPartition(self, partition):
|
||||
"""Format the given MTD partition."""
|
||||
self.script.append('format("MTD", "%s");' % (partition,))
|
||||
|
||||
def DeleteFiles(self, file_list):
|
||||
"""Delete all files in file_list."""
|
||||
if not file_list: return
|
||||
cmd = "delete(" + ",\0".join(['"%s"' % (i,) for i in file_list]) + ");"
|
||||
self.script.append(self._WordWrap(cmd))
|
||||
|
||||
def ApplyPatch(self, srcfile, tgtfile, tgtsize, tgtsha1, *patchpairs):
|
||||
"""Apply binary patches (in *patchpairs) to the given srcfile to
|
||||
produce tgtfile (which may be "-" to indicate overwriting the
|
||||
source file."""
|
||||
if len(patchpairs) % 2 != 0 or len(patchpairs) == 0:
|
||||
raise ValueError("bad patches given to ApplyPatch")
|
||||
cmd = ['apply_patch("%s",\0"%s",\0%s,\0%d'
|
||||
% (srcfile, tgtfile, tgtsha1, tgtsize)]
|
||||
for i in range(0, len(patchpairs), 2):
|
||||
cmd.append(',\0"%s:%s"' % patchpairs[i:i+2])
|
||||
cmd.append(');')
|
||||
cmd = "".join(cmd)
|
||||
self.script.append(self._WordWrap(cmd))
|
||||
|
||||
def WriteFirmwareImage(self, kind, fn):
|
||||
"""Arrange to update the given firmware image (kind must be
|
||||
"hboot" or "radio") when recovery finishes."""
|
||||
if self.version == 1:
|
||||
self.script.append(
|
||||
('assert(package_extract_file("%(fn)s", "/tmp/%(kind)s.img"),\n'
|
||||
' write_firmware_image("/tmp/%(kind)s.img", "%(kind)s"));')
|
||||
% {'kind': kind, 'fn': fn})
|
||||
else:
|
||||
self.script.append(
|
||||
'write_firmware_image("PACKAGE:%s", "%s");' % (fn, kind))
|
||||
|
||||
def WriteRawImage(self, partition, fn):
|
||||
"""Write the given package file into the given MTD partition."""
|
||||
self.script.append(
|
||||
('assert(package_extract_file("%(fn)s", "/tmp/%(partition)s.img"),\n'
|
||||
' write_raw_image("/tmp/%(partition)s.img", "%(partition)s"),\n'
|
||||
' delete("/tmp/%(partition)s.img"));')
|
||||
% {'partition': partition, 'fn': fn})
|
||||
|
||||
def SetPermissions(self, fn, uid, gid, mode):
|
||||
"""Set file ownership and permissions."""
|
||||
self.script.append('set_perm(%d, %d, 0%o, "%s");' % (uid, gid, mode, fn))
|
||||
|
||||
def SetPermissionsRecursive(self, fn, uid, gid, dmode, fmode):
|
||||
"""Recursively set path ownership and permissions."""
|
||||
self.script.append('set_perm_recursive(%d, %d, 0%o, 0%o, "%s");'
|
||||
% (uid, gid, dmode, fmode, fn))
|
||||
|
||||
def MakeSymlinks(self, symlink_list):
|
||||
"""Create symlinks, given a list of (dest, link) pairs."""
|
||||
by_dest = {}
|
||||
for d, l in symlink_list:
|
||||
by_dest.setdefault(d, []).append(l)
|
||||
|
||||
for dest, links in sorted(by_dest.iteritems()):
|
||||
cmd = ('symlink("%s", ' % (dest,) +
|
||||
",\0".join(['"' + i + '"' for i in sorted(links)]) + ");")
|
||||
self.script.append(self._WordWrap(cmd))
|
||||
|
||||
def AppendExtra(self, extra):
|
||||
"""Append text verbatim to the output script."""
|
||||
self.script.append(extra)
|
||||
|
||||
def AddToZip(self, input_zip, output_zip, input_path=None):
|
||||
"""Write the accumulated script to the output_zip file. input_zip
|
||||
is used as the source for the 'updater' binary needed to run
|
||||
script. If input_path is not None, it will be used as a local
|
||||
path for the binary instead of input_zip."""
|
||||
|
||||
for p in sorted(self.mounts):
|
||||
self.script.append('unmount("%s");' % (p,))
|
||||
|
||||
common.ZipWriteStr(output_zip, "META-INF/com/google/android/updater-script",
|
||||
"\n".join(self.script) + "\n")
|
||||
|
||||
if input_path is None:
|
||||
data = input_zip.read("OTA/bin/updater")
|
||||
else:
|
||||
data = open(os.path.join(input_path, "updater")).read()
|
||||
common.ZipWriteStr(output_zip, "META-INF/com/google/android/update-binary",
|
||||
data, perms=0755)
|
|
@ -45,6 +45,10 @@ Usage: ota_from_target_files [flags] input_target_files output_ota_package
|
|||
-e (--extra_script) <file>
|
||||
Insert the contents of file at the end of the update script.
|
||||
|
||||
-m (--script_mode) <mode>
|
||||
Specify 'amend' or 'edify' scripts, or 'auto' to pick
|
||||
automatically (this is the default).
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
@ -63,6 +67,8 @@ import time
|
|||
import zipfile
|
||||
|
||||
import common
|
||||
import amend_generator
|
||||
import edify_generator
|
||||
|
||||
OPTIONS = common.OPTIONS
|
||||
OPTIONS.package_key = "build/target/product/security/testkey"
|
||||
|
@ -73,6 +79,7 @@ OPTIONS.patch_threshold = 0.95
|
|||
OPTIONS.wipe_user_data = False
|
||||
OPTIONS.omit_prereq = False
|
||||
OPTIONS.extra_script = None
|
||||
OPTIONS.script_mode = 'auto'
|
||||
|
||||
def MostPopularKey(d, default):
|
||||
"""Given a dict, return the key corresponding to the largest
|
||||
|
@ -193,11 +200,10 @@ class Item:
|
|||
|
||||
return d
|
||||
|
||||
def SetPermissions(self, script, renamer=lambda x: x):
|
||||
def SetPermissions(self, script):
|
||||
"""Append set_perm/set_perm_recursive commands to 'script' to
|
||||
set all permissions, users, and groups for the tree of files
|
||||
rooted at 'self'. 'renamer' turns the filenames stored in the
|
||||
tree of Items into the strings used in the script."""
|
||||
rooted at 'self'."""
|
||||
|
||||
self.CountChildMetadata()
|
||||
|
||||
|
@ -208,22 +214,19 @@ class Item:
|
|||
# supposed to be something different.
|
||||
if item.dir:
|
||||
if current != item.best_subtree:
|
||||
script.append("set_perm_recursive %d %d 0%o 0%o %s" %
|
||||
(item.best_subtree + (renamer(item.name),)))
|
||||
script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
|
||||
current = item.best_subtree
|
||||
|
||||
if item.uid != current[0] or item.gid != current[1] or \
|
||||
item.mode != current[2]:
|
||||
script.append("set_perm %d %d 0%o %s" %
|
||||
(item.uid, item.gid, item.mode, renamer(item.name)))
|
||||
script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
|
||||
|
||||
for i in item.children:
|
||||
recurse(i, current)
|
||||
else:
|
||||
if item.uid != current[0] or item.gid != current[1] or \
|
||||
item.mode != current[3]:
|
||||
script.append("set_perm %d %d 0%o %s" %
|
||||
(item.uid, item.gid, item.mode, renamer(item.name)))
|
||||
script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
|
||||
|
||||
recurse(self, (-1, -1, -1, -1))
|
||||
|
||||
|
@ -245,7 +248,7 @@ def CopySystemFiles(input_zip, output_zip=None,
|
|||
basefilename = info.filename[7:]
|
||||
if IsSymlink(info):
|
||||
symlinks.append((input_zip.read(info.filename),
|
||||
"SYSTEM:" + basefilename))
|
||||
"/system/" + basefilename))
|
||||
else:
|
||||
info2 = copy.copy(info)
|
||||
fn = info2.filename = "system/" + basefilename
|
||||
|
@ -266,11 +269,6 @@ def CopySystemFiles(input_zip, output_zip=None,
|
|||
return symlinks
|
||||
|
||||
|
||||
def AddScript(script, output_zip):
|
||||
common.ZipWriteStr(output_zip, "META-INF/com/google/android/update-script",
|
||||
"\n".join(script) + "\n")
|
||||
|
||||
|
||||
def SignOutput(temp_zip_name, output_zip_name):
|
||||
key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
|
||||
pw = key_passwords[OPTIONS.package_key]
|
||||
|
@ -278,35 +276,15 @@ def SignOutput(temp_zip_name, output_zip_name):
|
|||
common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw)
|
||||
|
||||
|
||||
def SubstituteRoot(s):
|
||||
if s == "system": return "SYSTEM:"
|
||||
assert s.startswith("system/")
|
||||
return "SYSTEM:" + s[7:]
|
||||
|
||||
def FixPermissions(script):
|
||||
Item.GetMetadata()
|
||||
root = Item.Get("system")
|
||||
root.SetPermissions(script, renamer=SubstituteRoot)
|
||||
root.SetPermissions(script)
|
||||
|
||||
def DeleteFiles(script, to_delete):
|
||||
line = []
|
||||
t = 0
|
||||
for i in to_delete:
|
||||
line.append(i)
|
||||
t += len(i) + 1
|
||||
if t > 80:
|
||||
script.append("delete " + " ".join(line))
|
||||
line = []
|
||||
t = 0
|
||||
if line:
|
||||
script.append("delete " + " ".join(line))
|
||||
|
||||
def AppendAssertions(script, input_zip):
|
||||
script.append('assert compatible_with("0.2") == "true"')
|
||||
|
||||
device = GetBuildProp("ro.product.device", input_zip)
|
||||
script.append('assert getprop("ro.product.device") == "%s" || '
|
||||
'getprop("ro.build.product") == "%s"' % (device, device))
|
||||
script.AssertDevice(device)
|
||||
|
||||
info = input_zip.read("OTA/android-info.txt")
|
||||
m = re.search(r"require\s+version-bootloader\s*=\s*(\S+)", info)
|
||||
|
@ -314,47 +292,39 @@ def AppendAssertions(script, input_zip):
|
|||
raise ExternalError("failed to find required bootloaders in "
|
||||
"android-info.txt")
|
||||
bootloaders = m.group(1).split("|")
|
||||
script.append("assert " +
|
||||
" || ".join(['getprop("ro.bootloader") == "%s"' % (b,)
|
||||
for b in bootloaders]))
|
||||
|
||||
|
||||
def IncludeBinary(name, input_zip, output_zip, input_path=None):
|
||||
try:
|
||||
if input_path is not None:
|
||||
data = open(input_path).read()
|
||||
else:
|
||||
data = input_zip.read(os.path.join("OTA/bin", name))
|
||||
common.ZipWriteStr(output_zip, name, data, perms=0755)
|
||||
except IOError:
|
||||
raise ExternalError('unable to include device binary "%s"' % (name,))
|
||||
script.AssertSomeBootloader(*bootloaders)
|
||||
|
||||
|
||||
def WriteFullOTAPackage(input_zip, output_zip):
|
||||
script = []
|
||||
if OPTIONS.script_mode in ("amend", "auto"):
|
||||
script = amend_generator.AmendGenerator()
|
||||
else:
|
||||
# TODO: how to determine this? We don't know what version it will
|
||||
# be installed on top of. For now, we expect the API just won't
|
||||
# change very often.
|
||||
script = edify_generator.EdifyGenerator(1)
|
||||
|
||||
if not OPTIONS.omit_prereq:
|
||||
ts = GetBuildProp("ro.build.date.utc", input_zip)
|
||||
script.append("run_program PACKAGE:check_prereq %s" % (ts,))
|
||||
IncludeBinary("check_prereq", input_zip, output_zip)
|
||||
script.AssertOlderBuild(ts)
|
||||
|
||||
AppendAssertions(script, input_zip)
|
||||
|
||||
script.append("format BOOT:")
|
||||
script.append("show_progress 0.1 0")
|
||||
script.ShowProgress(0.1, 0)
|
||||
|
||||
common.ZipWriteStr(output_zip, "radio.img", input_zip.read("RADIO/image"))
|
||||
script.append("write_radio_image PACKAGE:radio.img")
|
||||
script.append("show_progress 0.5 0")
|
||||
script.WriteFirmwareImage("radio", "radio.img")
|
||||
script.ShowProgress(0.5, 0)
|
||||
|
||||
if OPTIONS.wipe_user_data:
|
||||
script.append("format DATA:")
|
||||
script.FormatPartition("userdata")
|
||||
|
||||
script.append("format SYSTEM:")
|
||||
script.append("copy_dir PACKAGE:system SYSTEM:")
|
||||
script.FormatPartition("system")
|
||||
script.Mount("MTD", "system", "/system")
|
||||
script.UnpackPackageDir("system", "/system")
|
||||
|
||||
symlinks = CopySystemFiles(input_zip, output_zip)
|
||||
script.extend(["symlink %s %s" % s for s in symlinks])
|
||||
script.MakeSymlinks(symlinks)
|
||||
|
||||
common.BuildAndAddBootableImage(os.path.join(OPTIONS.input_tmp, "RECOVERY"),
|
||||
"system/recovery.img", output_zip)
|
||||
|
@ -363,14 +333,15 @@ def WriteFullOTAPackage(input_zip, output_zip):
|
|||
FixPermissions(script)
|
||||
|
||||
common.AddBoot(output_zip)
|
||||
script.append("show_progress 0.2 0")
|
||||
script.append("write_raw_image PACKAGE:boot.img BOOT:")
|
||||
script.append("show_progress 0.2 10")
|
||||
script.ShowProgress(0.2, 0)
|
||||
|
||||
script.WriteRawImage("boot", "boot.img")
|
||||
script.ShowProgress(0.2, 10)
|
||||
|
||||
if OPTIONS.extra_script is not None:
|
||||
script.append(OPTIONS.extra_script)
|
||||
script.AppendExtra(OPTIONS.extra_script)
|
||||
|
||||
AddScript(script, output_zip)
|
||||
script.AddToZip(input_zip, output_zip)
|
||||
|
||||
|
||||
class File(object):
|
||||
|
@ -448,8 +419,38 @@ def GetBuildProp(property, z):
|
|||
return m.group(1).strip()
|
||||
|
||||
|
||||
def GetRecoveryAPIVersion(zip):
|
||||
"""Returns the version of the recovery API. Version 0 is the older
|
||||
amend code (no separate binary)."""
|
||||
try:
|
||||
version = zip.read("META/recovery-api-version.txt")
|
||||
return int(version)
|
||||
except KeyError:
|
||||
try:
|
||||
# version one didn't have the recovery-api-version.txt file, but
|
||||
# it did include an updater binary.
|
||||
zip.getinfo("OTA/bin/updater")
|
||||
return 1
|
||||
except KeyError:
|
||||
return 0
|
||||
|
||||
def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
||||
script = []
|
||||
source_version = GetRecoveryAPIVersion(source_zip)
|
||||
|
||||
if OPTIONS.script_mode == 'amend':
|
||||
script = amend_generator.AmendGenerator()
|
||||
elif OPTIONS.script_mode == 'edify':
|
||||
if source_version == 0:
|
||||
print ("WARNING: generating edify script for a source that "
|
||||
"can't install it.")
|
||||
script = edify_generator.EdifyGenerator(source_version)
|
||||
elif OPTIONS.script_mode == 'auto':
|
||||
if source_version > 0:
|
||||
script = edify_generator.EdifyGenerator(source_version)
|
||||
else:
|
||||
script = amend_generator.AmendGenerator()
|
||||
else:
|
||||
raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
|
||||
|
||||
print "Loading target..."
|
||||
target_data = LoadSystemFiles(target_zip)
|
||||
|
@ -496,11 +497,8 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
|||
source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
|
||||
target_fp = GetBuildProp("ro.build.fingerprint", target_zip)
|
||||
|
||||
script.append(('assert file_contains("SYSTEM:build.prop", '
|
||||
'"ro.build.fingerprint=%s") == "true" || '
|
||||
'file_contains("SYSTEM:build.prop", '
|
||||
'"ro.build.fingerprint=%s") == "true"') %
|
||||
(source_fp, target_fp))
|
||||
script.Mount("MTD", "system", "/system")
|
||||
script.AssertSomeFingerprint(source_fp, target_fp)
|
||||
|
||||
source_boot = File("/tmp/boot.img",
|
||||
common.BuildBootableImage(
|
||||
|
@ -532,6 +530,8 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
|||
|
||||
AppendAssertions(script, target_zip)
|
||||
|
||||
script.Print("Verifying current system...")
|
||||
|
||||
pb_verify = progress_bar_total * 0.3 * \
|
||||
(total_patched_size /
|
||||
float(total_patched_size+total_verbatim_size+1))
|
||||
|
@ -539,10 +539,9 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
|||
for i, (fn, tf, sf, size) in enumerate(patch_list):
|
||||
if i % 5 == 0:
|
||||
next_sizes = sum([i[3] for i in patch_list[i:i+5]])
|
||||
script.append("show_progress %f 1" %
|
||||
(next_sizes * pb_verify / (total_patched_size+1),))
|
||||
script.append("run_program PACKAGE:applypatch -c /%s %s %s" %
|
||||
(fn, tf.sha1, sf.sha1))
|
||||
script.ShowProgress(next_sizes * pb_verify / (total_patched_size+1), 1)
|
||||
|
||||
script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
|
||||
|
||||
if updating_recovery:
|
||||
d = Difference(target_recovery, source_recovery, "imgdiff")
|
||||
|
@ -551,10 +550,9 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
|||
|
||||
common.ZipWriteStr(output_zip, "patch/recovery.img.p", d)
|
||||
|
||||
script.append(("run_program PACKAGE:applypatch -c "
|
||||
"MTD:recovery:%d:%s:%d:%s") %
|
||||
(source_recovery.size, source_recovery.sha1,
|
||||
target_recovery.size, target_recovery.sha1))
|
||||
script.PatchCheck("MTD:recovery:%d:%s:%d:%s" %
|
||||
(source_recovery.size, source_recovery.sha1,
|
||||
target_recovery.size, target_recovery.sha1))
|
||||
|
||||
if updating_boot:
|
||||
d = Difference(target_boot, source_boot, "imgdiff")
|
||||
|
@ -563,36 +561,35 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
|||
|
||||
common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
|
||||
|
||||
script.append(("run_program PACKAGE:applypatch -c "
|
||||
"MTD:boot:%d:%s:%d:%s") %
|
||||
(source_boot.size, source_boot.sha1,
|
||||
target_boot.size, target_boot.sha1))
|
||||
script.PatchCheck("MTD:boot:%d:%s:%d:%s" %
|
||||
(source_boot.size, source_boot.sha1,
|
||||
target_boot.size, target_boot.sha1))
|
||||
|
||||
if patch_list or updating_recovery or updating_boot:
|
||||
script.append("run_program PACKAGE:applypatch -s %d" %
|
||||
(largest_source_size,))
|
||||
script.append("copy_dir PACKAGE:patch CACHE:../tmp/patchtmp")
|
||||
IncludeBinary("applypatch", target_zip, output_zip)
|
||||
script.CacheFreeSpaceCheck(largest_source_size)
|
||||
script.Print("Unpacking patches...")
|
||||
script.UnpackPackageDir("patch", "/tmp/patchtmp")
|
||||
|
||||
script.append("\n# ---- start making changes here\n")
|
||||
script.Comment("---- start making changes here ----")
|
||||
|
||||
if OPTIONS.wipe_user_data:
|
||||
script.append("format DATA:")
|
||||
script.Print("Erasing user data...")
|
||||
script.FormatPartition("userdata")
|
||||
|
||||
DeleteFiles(script, [SubstituteRoot(i[0]) for i in verbatim_targets])
|
||||
script.Print("Removing unneeded files...")
|
||||
script.DeleteFiles(["/"+i[0] for i in verbatim_targets])
|
||||
|
||||
if updating_boot:
|
||||
# Produce the boot image by applying a patch to the current
|
||||
# contents of the boot partition, and write it back to the
|
||||
# partition.
|
||||
script.append(("run_program PACKAGE:applypatch "
|
||||
"MTD:boot:%d:%s:%d:%s - "
|
||||
"%s %d %s:/tmp/patchtmp/boot.img.p")
|
||||
% (source_boot.size, source_boot.sha1,
|
||||
target_boot.size, target_boot.sha1,
|
||||
target_boot.sha1,
|
||||
target_boot.size,
|
||||
source_boot.sha1))
|
||||
script.Print("Patching boot image...")
|
||||
script.ApplyPatch("MTD:boot:%d:%s:%d:%s"
|
||||
% (source_boot.size, source_boot.sha1,
|
||||
target_boot.size, target_boot.sha1),
|
||||
"-",
|
||||
target_boot.size, target_boot.sha1,
|
||||
source_boot.sha1, "/tmp/patchtmp/boot.img.p")
|
||||
print "boot image changed; including."
|
||||
else:
|
||||
print "boot image unchanged; skipping."
|
||||
|
@ -600,41 +597,41 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
|||
if updating_recovery:
|
||||
# Produce /system/recovery.img by applying a patch to the current
|
||||
# contents of the recovery partition.
|
||||
script.append(("run_program PACKAGE:applypatch MTD:recovery:%d:%s:%d:%s "
|
||||
"/system/recovery.img %s %d %s:/tmp/patchtmp/recovery.img.p")
|
||||
% (source_recovery.size, source_recovery.sha1,
|
||||
target_recovery.size, target_recovery.sha1,
|
||||
target_recovery.sha1,
|
||||
target_recovery.size,
|
||||
source_recovery.sha1))
|
||||
script.Print("Patching recovery image...")
|
||||
script.ApplyPatch("MTD:recovery:%d:%s:%d:%s"
|
||||
% (source_recovery.size, source_recovery.sha1,
|
||||
target_recovery.size, target_recovery.sha1),
|
||||
"/system/recovery.img",
|
||||
target_recovery.size, target_recovery.sha1,
|
||||
source_recovery.sha1, "/tmp/patchtmp/recovery.img.p")
|
||||
print "recovery image changed; including."
|
||||
else:
|
||||
print "recovery image unchanged; skipping."
|
||||
|
||||
if updating_radio:
|
||||
script.append("show_progress 0.3 10")
|
||||
script.append("write_radio_image PACKAGE:radio.img")
|
||||
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 /
|
||||
float(total_patched_size+total_verbatim_size+1))
|
||||
for i, (fn, tf, sf, size) in enumerate(patch_list):
|
||||
if i % 5 == 0:
|
||||
next_sizes = sum([i[3] for i in patch_list[i:i+5]])
|
||||
script.append("show_progress %f 1" %
|
||||
(next_sizes * pb_apply / (total_patched_size+1),))
|
||||
script.append(("run_program PACKAGE:applypatch "
|
||||
"/%s - %s %d %s:/tmp/patchtmp/%s.p") %
|
||||
(fn, tf.sha1, tf.size, sf.sha1, fn))
|
||||
script.ShowProgress(next_sizes * pb_apply / (total_patched_size+1), 1)
|
||||
script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1,
|
||||
sf.sha1, "/tmp/patchtmp/"+fn+".p")
|
||||
|
||||
target_symlinks = CopySystemFiles(target_zip, None)
|
||||
|
||||
target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
|
||||
temp_script = []
|
||||
temp_script = script.MakeTemporary()
|
||||
FixPermissions(temp_script)
|
||||
|
||||
# Note that this call will mess up the tree of Items, so make sure
|
||||
|
@ -649,14 +646,17 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
|||
for dest, link in source_symlinks:
|
||||
if link not in target_symlinks_d:
|
||||
to_delete.append(link)
|
||||
DeleteFiles(script, to_delete)
|
||||
script.DeleteFiles(to_delete)
|
||||
|
||||
if verbatim_targets:
|
||||
pb_verbatim = progress_bar_total * \
|
||||
(total_verbatim_size /
|
||||
float(total_patched_size+total_verbatim_size+1))
|
||||
script.append("show_progress %f 5" % (pb_verbatim,))
|
||||
script.append("copy_dir PACKAGE:system SYSTEM:")
|
||||
script.ShowProgress(pb_verbatim, 5)
|
||||
script.Print("Unpacking new files...")
|
||||
script.UnpackPackageDir("system", "/system")
|
||||
|
||||
script.Print("Finishing up...")
|
||||
|
||||
# Create all the symlinks that don't already exist, or point to
|
||||
# somewhere different than what we want. Delete each symlink before
|
||||
|
@ -668,17 +668,17 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
|||
to_create.append((dest, link))
|
||||
else:
|
||||
to_create.append((dest, link))
|
||||
DeleteFiles(script, [i[1] for i in to_create])
|
||||
script.extend(["symlink %s %s" % s for s in to_create])
|
||||
script.DeleteFiles([i[1] for i in to_create])
|
||||
script.MakeSymlinks(to_create)
|
||||
|
||||
# Now that the symlinks are created, we can set all the
|
||||
# permissions.
|
||||
script.extend(temp_script)
|
||||
script.AppendScript(temp_script)
|
||||
|
||||
if OPTIONS.extra_script is not None:
|
||||
script.append(OPTIONS.extra_script)
|
||||
scirpt.AppendExtra(OPTIONS.extra_script)
|
||||
|
||||
AddScript(script, output_zip)
|
||||
script.AddToZip(target_zip, output_zip)
|
||||
|
||||
|
||||
def main(argv):
|
||||
|
@ -696,18 +696,21 @@ def main(argv):
|
|||
OPTIONS.omit_prereq = True
|
||||
elif o in ("-e", "--extra_script"):
|
||||
OPTIONS.extra_script = a
|
||||
elif o in ("-m", "--script_mode"):
|
||||
OPTIONS.script_mode = a
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
args = common.ParseOptions(argv, __doc__,
|
||||
extra_opts="b:k:i:d:wne:",
|
||||
extra_opts="b:k:i:d:wne:m:",
|
||||
extra_long_opts=["board_config=",
|
||||
"package_key=",
|
||||
"incremental_from=",
|
||||
"wipe_user_data",
|
||||
"no_prereq",
|
||||
"extra_script="],
|
||||
"extra_script=",
|
||||
"script_mode="],
|
||||
extra_option_handler=option_handler)
|
||||
|
||||
if len(args) != 2:
|
||||
|
@ -721,6 +724,9 @@ def main(argv):
|
|||
print " images don't exceed partition sizes."
|
||||
print
|
||||
|
||||
if OPTIONS.script_mode not in ("amend", "edify", "auto"):
|
||||
raise ValueError('unknown script mode "%s"' % (OPTIONS.script_mode,))
|
||||
|
||||
if OPTIONS.extra_script is not None:
|
||||
OPTIONS.extra_script = open(OPTIONS.extra_script).read()
|
||||
|
||||
|
|
Loading…
Reference in New Issue