Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a28c5266 authored by Tianjie's avatar Tianjie Committed by Tianjie Xu
Browse files

Build the payload image only during apk-in-apex signing

When doing apk-in-apex signing, the package name is not reserved
during the apex repacking. As a result, the name accidentally
reverts to 'com.android.wifi' from 'com.google.android.wifi'.

This cl changes the behavior to call 'apexer' by passing the
'--payload_only' argument. So we don't build the apex file from
scratch and the old AndroidManifest.xml will be reused.

BUG: 152084536
Test: unit tests pass
Change-Id: I8332b2ee84832fb196f2e1c4309abac5ab92e153
Merged-In: I8332b2ee84832fb196f2e1c4309abac5ab92e153
parent c282ece0
Loading
Loading
Loading
Loading
+25 −42
Original line number Original line Diff line number Diff line
@@ -27,6 +27,8 @@ logger = logging.getLogger(__name__)


OPTIONS = common.OPTIONS
OPTIONS = common.OPTIONS


APEX_PAYLOAD_IMAGE = 'apex_payload.img'



class ApexInfoError(Exception):
class ApexInfoError(Exception):
  """An Exception raised during Apex Information command."""
  """An Exception raised during Apex Information command."""
@@ -50,15 +52,11 @@ class ApexApkSigner(object):
    self.key_passwords = key_passwords
    self.key_passwords = key_passwords
    self.codename_to_api_level_map = codename_to_api_level_map
    self.codename_to_api_level_map = codename_to_api_level_map


  def ProcessApexFile(self, apk_keys, payload_key, payload_public_key,
  def ProcessApexFile(self, apk_keys, payload_key):
                      signing_args=None):
    """Scans and signs the apk files and repack the apex
    """Scans and signs the apk files and repack the apex


    Args:
    Args:
      apk_keys: A dict that holds the signing keys for apk files.
      apk_keys: A dict that holds the signing keys for apk files.
      payload_key: The path to the apex payload signing key.
      payload_public_key: The path to the public key corresponding to the
       payload signing key.


    Returns:
    Returns:
      The repacked apex file containing the signed apk files.
      The repacked apex file containing the signed apk files.
@@ -89,8 +87,7 @@ class ApexApkSigner(object):
      logger.info('No apk file has been signed in %s', self.apex_path)
      logger.info('No apk file has been signed in %s', self.apex_path)
      return self.apex_path
      return self.apex_path


    return self.RepackApexPayload(payload_dir, payload_key, payload_public_key,
    return self.RepackApexPayload(payload_dir, payload_key)
                                  signing_args)


  def ExtractApexPayloadAndSignApks(self, apk_entries, apk_keys):
  def ExtractApexPayloadAndSignApks(self, apk_entries, apk_keys):
    """Extracts the payload image and signs the containing apk files."""
    """Extracts the payload image and signs the containing apk files."""
@@ -118,27 +115,15 @@ class ApexApkSigner(object):
      has_signed_apk = True
      has_signed_apk = True
    return payload_dir, has_signed_apk
    return payload_dir, has_signed_apk


  def RepackApexPayload(self, payload_dir, payload_key, payload_public_key,
  def RepackApexPayload(self, payload_dir, payload_key):
                        signing_args=None):
    """Rebuilds the apex file with the updated payload directory."""
    """Rebuilds the apex file with the updated payload directory."""
    apex_dir = common.MakeTempDir()
    apex_dir = common.MakeTempDir()
    # Extract the apex file and reuse its meta files as repack parameters.
    # Extract the apex file and reuse its meta files as repack parameters.
    common.UnzipToDir(self.apex_path, apex_dir)
    common.UnzipToDir(self.apex_path, apex_dir)

    android_jar_path = common.OPTIONS.android_jar_path
    if not android_jar_path:
      android_jar_path = os.path.join(os.environ.get('ANDROID_BUILD_TOP', ''),
                                      'prebuilts', 'sdk', 'current', 'public',
                                      'android.jar')
      logger.warning('android_jar_path not found in options, falling back to'
                     ' use %s', android_jar_path)

    arguments_dict = {
    arguments_dict = {
        'manifest': os.path.join(apex_dir, 'apex_manifest.pb'),
        'manifest': os.path.join(apex_dir, 'apex_manifest.pb'),
        'build_info': os.path.join(apex_dir, 'apex_build_info.pb'),
        'build_info': os.path.join(apex_dir, 'apex_build_info.pb'),
        'android_jar_path': android_jar_path,
        'key': payload_key,
        'key': payload_key,
        'pubkey': payload_public_key,
    }
    }
    for filename in arguments_dict.values():
    for filename in arguments_dict.values():
      assert os.path.exists(filename), 'file {} not found'.format(filename)
      assert os.path.exists(filename), 'file {} not found'.format(filename)
@@ -151,29 +136,30 @@ class ApexApkSigner(object):
      elif os.path.isdir(path):
      elif os.path.isdir(path):
        shutil.rmtree(path)
        shutil.rmtree(path)


    repacked_apex = common.MakeTempFile(suffix='.apex')
    # TODO(xunchang) the signing process can be improved by using
    repack_cmd = ['apexer', '--force', '--include_build_info',
    # '--unsigned_payload_only'. But we need to parse the vbmeta earlier for
    # the signing arguments, e.g. algorithm, salt, etc.
    payload_img = os.path.join(apex_dir, APEX_PAYLOAD_IMAGE)
    generate_image_cmd = ['apexer', '--force', '--payload_only',
                          '--do_not_check_keyname', '--apexer_tool_path',
                          '--do_not_check_keyname', '--apexer_tool_path',
                          os.getenv('PATH')]
                          os.getenv('PATH')]
    for key, val in arguments_dict.items():
    for key, val in arguments_dict.items():
      repack_cmd.extend(['--' + key, val])
      generate_image_cmd.extend(['--' + key, val])
    # Add quote to the signing_args as we will pass
    # --signing_args "--signing_helper_with_files=%path" to apexer
    if signing_args:
      repack_cmd.extend(['--signing_args', '"{}"'.format(signing_args)])
    # optional arguments for apex repacking
    # optional arguments for apex repacking
    manifest_json = os.path.join(apex_dir, 'apex_manifest.json')
    manifest_json = os.path.join(apex_dir, 'apex_manifest.json')
    if os.path.exists(manifest_json):
    if os.path.exists(manifest_json):
      repack_cmd.extend(['--manifest_json', manifest_json])
      generate_image_cmd.extend(['--manifest_json', manifest_json])
    assets_dir = os.path.join(apex_dir, 'assets')
    generate_image_cmd.extend([payload_dir, payload_img])
    if os.path.isdir(assets_dir):
      repack_cmd.extend(['--assets_dir', assets_dir])
    repack_cmd.extend([payload_dir, repacked_apex])
    if OPTIONS.verbose:
    if OPTIONS.verbose:
      repack_cmd.append('-v')
      generate_image_cmd.append('-v')
    common.RunAndCheckOutput(repack_cmd)
    common.RunAndCheckOutput(generate_image_cmd)


    return repacked_apex
    # Add the payload image back to the apex file.
    common.ZipDelete(self.apex_path, APEX_PAYLOAD_IMAGE)
    with zipfile.ZipFile(self.apex_path, 'a') as output_apex:
      common.ZipWrite(output_apex, payload_img, APEX_PAYLOAD_IMAGE,
                      compress_type=zipfile.ZIP_STORED)
    return self.apex_path




def SignApexPayload(avbtool, payload_file, payload_key_path, payload_key_name,
def SignApexPayload(avbtool, payload_file, payload_key_path, payload_key_name,
@@ -311,16 +297,13 @@ def SignApex(avbtool, apex_data, payload_key, container_key, container_pw,
  with open(apex_file, 'wb') as apex_fp:
  with open(apex_file, 'wb') as apex_fp:
    apex_fp.write(apex_data)
    apex_fp.write(apex_data)


  APEX_PAYLOAD_IMAGE = 'apex_payload.img'
  APEX_PUBKEY = 'apex_pubkey'
  APEX_PUBKEY = 'apex_pubkey'


  # 1. Extract the apex payload image and sign the containing apk files. Repack
  # 1. Extract the apex payload image and sign the containing apk files. Repack
  # the apex file after signing.
  # the apex file after signing.
  payload_public_key = common.ExtractAvbPublicKey(avbtool, payload_key)
  apk_signer = ApexApkSigner(apex_file, container_pw,
  apk_signer = ApexApkSigner(apex_file, container_pw,
                             codename_to_api_level_map)
                             codename_to_api_level_map)
  apex_file = apk_signer.ProcessApexFile(apk_keys, payload_key,
  apex_file = apk_signer.ProcessApexFile(apk_keys, payload_key)
                                         payload_public_key, signing_args)


  # 2a. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given
  # 2a. Extract and sign the APEX_PAYLOAD_IMAGE entry with the given
  # payload_key.
  # payload_key.
@@ -341,7 +324,7 @@ def SignApex(avbtool, apex_data, payload_key, container_key, container_pw,
      signing_args)
      signing_args)


  # 2b. Update the embedded payload public key.
  # 2b. Update the embedded payload public key.

  payload_public_key = common.ExtractAvbPublicKey(avbtool, payload_key)
  common.ZipDelete(apex_file, APEX_PAYLOAD_IMAGE)
  common.ZipDelete(apex_file, APEX_PAYLOAD_IMAGE)
  if APEX_PUBKEY in zip_items:
  if APEX_PUBKEY in zip_items:
    common.ZipDelete(apex_file, APEX_PUBKEY)
    common.ZipDelete(apex_file, APEX_PUBKEY)
+23 −31
Original line number Original line Diff line number Diff line
@@ -14,8 +14,10 @@
# limitations under the License.
# limitations under the License.
#
#


import re
import os
import os
import os.path
import os.path
import shutil
import zipfile
import zipfile


import apex_utils
import apex_utils
@@ -32,6 +34,7 @@ class ApexUtilsTest(test_utils.ReleaseToolsTestCase):
    self.testdata_dir = test_utils.get_testdata_dir()
    self.testdata_dir = test_utils.get_testdata_dir()
    # The default payload signing key.
    # The default payload signing key.
    self.payload_key = os.path.join(self.testdata_dir, 'testkey.key')
    self.payload_key = os.path.join(self.testdata_dir, 'testkey.key')
    self.apex_with_apk = os.path.join(self.testdata_dir, 'has_apk.apex')


    common.OPTIONS.search_path = test_utils.get_search_path()
    common.OPTIONS.search_path = test_utils.get_search_path()


@@ -134,35 +137,43 @@ class ApexUtilsTest(test_utils.ReleaseToolsTestCase):
  def test_ApexApkSigner_noApkPresent(self):
  def test_ApexApkSigner_noApkPresent(self):
    apex_path = os.path.join(self.testdata_dir, 'foo.apex')
    apex_path = os.path.join(self.testdata_dir, 'foo.apex')
    signer = apex_utils.ApexApkSigner(apex_path, None, None)
    signer = apex_utils.ApexApkSigner(apex_path, None, None)
    processed_apex = signer.ProcessApexFile({}, self.payload_key,
    processed_apex = signer.ProcessApexFile({}, self.payload_key)
                                            None)
    self.assertEqual(apex_path, processed_apex)
    self.assertEqual(apex_path, processed_apex)


  @test_utils.SkipIfExternalToolsUnavailable()
  @test_utils.SkipIfExternalToolsUnavailable()
  def test_ApexApkSigner_apkKeyNotPresent(self):
  def test_ApexApkSigner_apkKeyNotPresent(self):
    apex_path = os.path.join(self.testdata_dir, 'has_apk.apex')
    apex_path = common.MakeTempFile(suffix='.apex')
    shutil.copy(self.apex_with_apk, apex_path)
    signer = apex_utils.ApexApkSigner(apex_path, None, None)
    signer = apex_utils.ApexApkSigner(apex_path, None, None)
    self.assertRaises(apex_utils.ApexSigningError, signer.ProcessApexFile, {},
    self.assertRaises(apex_utils.ApexSigningError, signer.ProcessApexFile,
                      self.payload_key, None)
                      {}, self.payload_key)


  @test_utils.SkipIfExternalToolsUnavailable()
  @test_utils.SkipIfExternalToolsUnavailable()
  def test_ApexApkSigner_signApk(self):
  def test_ApexApkSigner_signApk(self):
    apex_path = os.path.join(self.testdata_dir, 'has_apk.apex')
    apex_path = common.MakeTempFile(suffix='.apex')
    shutil.copy(self.apex_with_apk, apex_path)
    signer = apex_utils.ApexApkSigner(apex_path, None, None)
    signer = apex_utils.ApexApkSigner(apex_path, None, None)
    apk_keys = {'wifi-service-resources.apk': os.path.join(
    apk_keys = {'wifi-service-resources.apk': os.path.join(
        self.testdata_dir, 'testkey')}
        self.testdata_dir, 'testkey')}


    self.payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')
    self.payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')
    payload_pubkey = common.ExtractAvbPublicKey('avbtool',
    apex_file = signer.ProcessApexFile(apk_keys, self.payload_key)
                                                self.payload_key)
    package_name_extract_cmd = ['aapt', 'dump', 'badging', apex_file]
    signer.ProcessApexFile(apk_keys, self.payload_key, payload_pubkey)
    output = common.RunAndCheckOutput(package_name_extract_cmd)
    for line in output.splitlines():
      # Sample output from aapt: "package: name='com.google.android.wifi'
      # versionCode='1' versionName='' platformBuildVersionName='R'
      # compileSdkVersion='29' compileSdkVersionCodename='R'"
      match = re.search(r"^package:.* name='([\w|\.]+)'", line, re.IGNORECASE)
      if match:
        package_name = match.group(1)
    self.assertEquals('com.google.android.wifi', package_name)


  @test_utils.SkipIfExternalToolsUnavailable()
  @test_utils.SkipIfExternalToolsUnavailable()
  def test_ApexApkSigner_noAssetDir(self):
  def test_ApexApkSigner_noAssetDir(self):
    apex_path = os.path.join(self.testdata_dir, 'has_apk.apex')
    no_asset = common.MakeTempFile(suffix='.apex')
    no_asset = common.MakeTempFile(suffix='.apex')
    with zipfile.ZipFile(no_asset, 'w') as output_zip:
    with zipfile.ZipFile(no_asset, 'w') as output_zip:
      with zipfile.ZipFile(apex_path, 'r') as input_zip:
      with zipfile.ZipFile(self.apex_with_apk, 'r') as input_zip:
        name_list = input_zip.namelist()
        name_list = input_zip.namelist()
        for name in name_list:
        for name in name_list:
          if not name.startswith('assets'):
          if not name.startswith('assets'):
@@ -173,23 +184,4 @@ class ApexUtilsTest(test_utils.ReleaseToolsTestCase):
        self.testdata_dir, 'testkey')}
        self.testdata_dir, 'testkey')}


    self.payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')
    self.payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')
    payload_pubkey = common.ExtractAvbPublicKey('avbtool',
    signer.ProcessApexFile(apk_keys, self.payload_key)
                                                self.payload_key)
    signer.ProcessApexFile(apk_keys, self.payload_key, payload_pubkey)

  @test_utils.SkipIfExternalToolsUnavailable()
  def test_ApexApkSigner_withSignerHelper(self):
    apex_path = os.path.join(self.testdata_dir, 'has_apk.apex')
    signer = apex_utils.ApexApkSigner(apex_path, None, None)
    apk_keys = {'wifi-service-resources.apk': os.path.join(
        self.testdata_dir, 'testkey')}

    self.payload_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')
    payload_pubkey = common.ExtractAvbPublicKey('avbtool', self.payload_key)

    signing_helper = os.path.join(self.testdata_dir, 'signing_helper.sh')
    os.chmod(signing_helper, 0o700)
    payload_signer_args = '--signing_helper_with_files={}'.format(
        signing_helper)
    signer.ProcessApexFile(apk_keys, self.payload_key, payload_pubkey,
                           payload_signer_args)