Merge "releasetools: Allow skipping postinstall hooks when generating A/B OTAs." am: 6c0ddf2480
am: ff1288821d
am: a5baf56e6c
Change-Id: I59bf83ba415351ba07488086d8b812453b0ae61b
This commit is contained in:
commit
cf9c445ef9
2 changed files with 94 additions and 3 deletions
|
@ -144,6 +144,13 @@ Usage: ota_from_target_files [flags] input_target_files output_ota_package
|
||||||
|
|
||||||
--payload_signer_args <args>
|
--payload_signer_args <args>
|
||||||
Specify the arguments needed for payload signer.
|
Specify the arguments needed for payload signer.
|
||||||
|
|
||||||
|
--skip_postinstall
|
||||||
|
Skip the postinstall hooks when generating an A/B OTA package (default:
|
||||||
|
False). Note that this discards ALL the hooks, including non-optional
|
||||||
|
ones. Should only be used if caller knows it's safe to do so (e.g. all the
|
||||||
|
postinstall work is to dexopt apps and a data wipe will happen immediately
|
||||||
|
after). Only meaningful when generating A/B OTAs.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
@ -151,6 +158,7 @@ from __future__ import print_function
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os.path
|
import os.path
|
||||||
import shlex
|
import shlex
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
@ -193,8 +201,11 @@ OPTIONS.payload_signer = None
|
||||||
OPTIONS.payload_signer_args = []
|
OPTIONS.payload_signer_args = []
|
||||||
OPTIONS.extracted_input = None
|
OPTIONS.extracted_input = None
|
||||||
OPTIONS.key_passwords = []
|
OPTIONS.key_passwords = []
|
||||||
|
OPTIONS.skip_postinstall = False
|
||||||
|
|
||||||
|
|
||||||
METADATA_NAME = 'META-INF/com/android/metadata'
|
METADATA_NAME = 'META-INF/com/android/metadata'
|
||||||
|
POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
|
||||||
UNZIP_PATTERN = ['IMAGES/*', 'META/*']
|
UNZIP_PATTERN = ['IMAGES/*', 'META/*']
|
||||||
|
|
||||||
|
|
||||||
|
@ -1215,7 +1226,7 @@ endif;
|
||||||
WriteMetadata(metadata, output_zip)
|
WriteMetadata(metadata, output_zip)
|
||||||
|
|
||||||
|
|
||||||
def GetTargetFilesZipForSecondaryImages(input_file):
|
def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
|
||||||
"""Returns a target-files.zip file for generating secondary payload.
|
"""Returns a target-files.zip file for generating secondary payload.
|
||||||
|
|
||||||
Although the original target-files.zip already contains secondary slot
|
Although the original target-files.zip already contains secondary slot
|
||||||
|
@ -1229,6 +1240,7 @@ def GetTargetFilesZipForSecondaryImages(input_file):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
input_file: The input target-files.zip file.
|
input_file: The input target-files.zip file.
|
||||||
|
skip_postinstall: Whether to skip copying the postinstall config file.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The filename of the target-files.zip for generating secondary payload.
|
The filename of the target-files.zip for generating secondary payload.
|
||||||
|
@ -1247,6 +1259,10 @@ def GetTargetFilesZipForSecondaryImages(input_file):
|
||||||
'IMAGES/system.map'):
|
'IMAGES/system.map'):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Skip copying the postinstall config if requested.
|
||||||
|
elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
|
||||||
|
pass
|
||||||
|
|
||||||
elif info.filename.startswith(('META/', 'IMAGES/')):
|
elif info.filename.startswith(('META/', 'IMAGES/')):
|
||||||
common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
|
common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
|
||||||
|
|
||||||
|
@ -1256,6 +1272,31 @@ def GetTargetFilesZipForSecondaryImages(input_file):
|
||||||
return target_file
|
return target_file
|
||||||
|
|
||||||
|
|
||||||
|
def GetTargetFilesZipWithoutPostinstallConfig(input_file):
|
||||||
|
"""Returns a target-files.zip that's not containing postinstall_config.txt.
|
||||||
|
|
||||||
|
This allows brillo_update_payload script to skip writing all the postinstall
|
||||||
|
hooks in the generated payload. The input target-files.zip file will be
|
||||||
|
duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
|
||||||
|
contain the postinstall_config.txt entry, the input file will be returned.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input_file: The input target-files.zip filename.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The filename of target-files.zip that doesn't contain postinstall config.
|
||||||
|
"""
|
||||||
|
# We should only make a copy if postinstall_config entry exists.
|
||||||
|
with zipfile.ZipFile(input_file, 'r') as input_zip:
|
||||||
|
if POSTINSTALL_CONFIG not in input_zip.namelist():
|
||||||
|
return input_file
|
||||||
|
|
||||||
|
target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
|
||||||
|
shutil.copyfile(input_file, target_file)
|
||||||
|
common.ZipDelete(target_file, POSTINSTALL_CONFIG)
|
||||||
|
return target_file
|
||||||
|
|
||||||
|
|
||||||
def WriteABOTAPackageWithBrilloScript(target_file, output_file,
|
def WriteABOTAPackageWithBrilloScript(target_file, output_file,
|
||||||
source_file=None):
|
source_file=None):
|
||||||
"""Generate an Android OTA package that has A/B update payload."""
|
"""Generate an Android OTA package that has A/B update payload."""
|
||||||
|
@ -1325,6 +1366,9 @@ def WriteABOTAPackageWithBrilloScript(target_file, output_file,
|
||||||
# Metadata to comply with Android OTA package format.
|
# Metadata to comply with Android OTA package format.
|
||||||
metadata = GetPackageMetadata(target_info, source_info)
|
metadata = GetPackageMetadata(target_info, source_info)
|
||||||
|
|
||||||
|
if OPTIONS.skip_postinstall:
|
||||||
|
target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
|
||||||
|
|
||||||
# Generate payload.
|
# Generate payload.
|
||||||
payload = Payload()
|
payload = Payload()
|
||||||
|
|
||||||
|
@ -1349,7 +1393,8 @@ def WriteABOTAPackageWithBrilloScript(target_file, output_file,
|
||||||
if OPTIONS.include_secondary:
|
if OPTIONS.include_secondary:
|
||||||
# We always include a full payload for the secondary slot, even when
|
# We always include a full payload for the secondary slot, even when
|
||||||
# building an incremental OTA. See the comments for "--include_secondary".
|
# building an incremental OTA. See the comments for "--include_secondary".
|
||||||
secondary_target_file = GetTargetFilesZipForSecondaryImages(target_file)
|
secondary_target_file = GetTargetFilesZipForSecondaryImages(
|
||||||
|
target_file, OPTIONS.skip_postinstall)
|
||||||
secondary_payload = Payload(secondary=True)
|
secondary_payload = Payload(secondary=True)
|
||||||
secondary_payload.Generate(secondary_target_file,
|
secondary_payload.Generate(secondary_target_file,
|
||||||
additional_args=additional_args)
|
additional_args=additional_args)
|
||||||
|
@ -1478,6 +1523,8 @@ def main(argv):
|
||||||
OPTIONS.payload_signer_args = shlex.split(a)
|
OPTIONS.payload_signer_args = shlex.split(a)
|
||||||
elif o == "--extracted_input_target_files":
|
elif o == "--extracted_input_target_files":
|
||||||
OPTIONS.extracted_input = a
|
OPTIONS.extracted_input = a
|
||||||
|
elif o == "--skip_postinstall":
|
||||||
|
OPTIONS.skip_postinstall = True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
@ -1507,6 +1554,7 @@ def main(argv):
|
||||||
"payload_signer=",
|
"payload_signer=",
|
||||||
"payload_signer_args=",
|
"payload_signer_args=",
|
||||||
"extracted_input_target_files=",
|
"extracted_input_target_files=",
|
||||||
|
"skip_postinstall",
|
||||||
], extra_option_handler=option_handler)
|
], extra_option_handler=option_handler)
|
||||||
|
|
||||||
if len(args) != 2:
|
if len(args) != 2:
|
||||||
|
|
|
@ -24,7 +24,9 @@ import common
|
||||||
import test_utils
|
import test_utils
|
||||||
from ota_from_target_files import (
|
from ota_from_target_files import (
|
||||||
_LoadOemDicts, BuildInfo, GetPackageMetadata,
|
_LoadOemDicts, BuildInfo, GetPackageMetadata,
|
||||||
GetTargetFilesZipForSecondaryImages, Payload, PayloadSigner,
|
GetTargetFilesZipForSecondaryImages,
|
||||||
|
GetTargetFilesZipWithoutPostinstallConfig,
|
||||||
|
Payload, PayloadSigner, POSTINSTALL_CONFIG,
|
||||||
WriteFingerprintAssertion)
|
WriteFingerprintAssertion)
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,6 +39,16 @@ def construct_target_files(secondary=False):
|
||||||
'META/update_engine_config.txt',
|
'META/update_engine_config.txt',
|
||||||
"PAYLOAD_MAJOR_VERSION=2\nPAYLOAD_MINOR_VERSION=4\n")
|
"PAYLOAD_MAJOR_VERSION=2\nPAYLOAD_MINOR_VERSION=4\n")
|
||||||
|
|
||||||
|
# META/postinstall_config.txt
|
||||||
|
target_files_zip.writestr(
|
||||||
|
POSTINSTALL_CONFIG,
|
||||||
|
'\n'.join([
|
||||||
|
"RUN_POSTINSTALL_system=true",
|
||||||
|
"POSTINSTALL_PATH_system=system/bin/otapreopt_script",
|
||||||
|
"FILESYSTEM_TYPE_system=ext4",
|
||||||
|
"POSTINSTALL_OPTIONAL_system=true",
|
||||||
|
]))
|
||||||
|
|
||||||
# META/ab_partitions.txt
|
# META/ab_partitions.txt
|
||||||
ab_partitions = ['boot', 'system', 'vendor']
|
ab_partitions = ['boot', 'system', 'vendor']
|
||||||
target_files_zip.writestr(
|
target_files_zip.writestr(
|
||||||
|
@ -539,10 +551,41 @@ class OtaFromTargetFilesTest(unittest.TestCase):
|
||||||
self.assertIn('IMAGES/boot.img', namelist)
|
self.assertIn('IMAGES/boot.img', namelist)
|
||||||
self.assertIn('IMAGES/system.img', namelist)
|
self.assertIn('IMAGES/system.img', namelist)
|
||||||
self.assertIn('IMAGES/vendor.img', namelist)
|
self.assertIn('IMAGES/vendor.img', namelist)
|
||||||
|
self.assertIn(POSTINSTALL_CONFIG, namelist)
|
||||||
|
|
||||||
self.assertNotIn('IMAGES/system_other.img', namelist)
|
self.assertNotIn('IMAGES/system_other.img', namelist)
|
||||||
self.assertNotIn('IMAGES/system.map', namelist)
|
self.assertNotIn('IMAGES/system.map', namelist)
|
||||||
|
|
||||||
|
def test_GetTargetFilesZipForSecondaryImages_skipPostinstall(self):
|
||||||
|
input_file = construct_target_files(secondary=True)
|
||||||
|
target_file = GetTargetFilesZipForSecondaryImages(
|
||||||
|
input_file, skip_postinstall=True)
|
||||||
|
|
||||||
|
with zipfile.ZipFile(target_file) as verify_zip:
|
||||||
|
namelist = verify_zip.namelist()
|
||||||
|
|
||||||
|
self.assertIn('META/ab_partitions.txt', namelist)
|
||||||
|
self.assertIn('IMAGES/boot.img', namelist)
|
||||||
|
self.assertIn('IMAGES/system.img', namelist)
|
||||||
|
self.assertIn('IMAGES/vendor.img', namelist)
|
||||||
|
|
||||||
|
self.assertNotIn('IMAGES/system_other.img', namelist)
|
||||||
|
self.assertNotIn('IMAGES/system.map', namelist)
|
||||||
|
self.assertNotIn(POSTINSTALL_CONFIG, namelist)
|
||||||
|
|
||||||
|
def test_GetTargetFilesZipWithoutPostinstallConfig(self):
|
||||||
|
input_file = construct_target_files()
|
||||||
|
target_file = GetTargetFilesZipWithoutPostinstallConfig(input_file)
|
||||||
|
with zipfile.ZipFile(target_file) as verify_zip:
|
||||||
|
self.assertNotIn(POSTINSTALL_CONFIG, verify_zip.namelist())
|
||||||
|
|
||||||
|
def test_GetTargetFilesZipWithoutPostinstallConfig_missingEntry(self):
|
||||||
|
input_file = construct_target_files()
|
||||||
|
common.ZipDelete(input_file, POSTINSTALL_CONFIG)
|
||||||
|
target_file = GetTargetFilesZipWithoutPostinstallConfig(input_file)
|
||||||
|
with zipfile.ZipFile(target_file) as verify_zip:
|
||||||
|
self.assertNotIn(POSTINSTALL_CONFIG, verify_zip.namelist())
|
||||||
|
|
||||||
|
|
||||||
class PayloadSignerTest(unittest.TestCase):
|
class PayloadSignerTest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue