Merge "Switch ramdisk.img to the LZ4 format"
diff --git a/core/main.mk b/core/main.mk
index 9ba43f6..6a35417 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -1899,6 +1899,11 @@
 ndk: $(SOONG_OUT_DIR)/ndk.timestamp
 .PHONY: ndk
 
+# Checks that build/soong/apex/allowed_deps.txt remains up to date
+ifneq ($(UNSAFE_DISABLE_APEX_ALLOWED_DEPS_CHECK),true)
+  droidcore: ${APEX_ALLOWED_DEPS_CHECK}
+endif
+
 $(call dist-write-file,$(KATI_PACKAGE_MK_DIR)/dist.mk)
 
 $(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] writing build rules ...)
diff --git a/core/tasks/find-shareduid-violation.mk b/core/tasks/find-shareduid-violation.mk
index 972b1ec..d6885eb 100644
--- a/core/tasks/find-shareduid-violation.mk
+++ b/core/tasks/find-shareduid-violation.mk
@@ -16,8 +16,6 @@
 
 shareduid_violation_modules_filename := $(PRODUCT_OUT)/shareduid_violation_modules.json
 
-find_shareduid_script := $(BUILD_SYSTEM)/tasks/find-shareduid-violation.py
-
 $(shareduid_violation_modules_filename): $(INSTALLED_SYSTEMIMAGE_TARGET) \
     $(INSTALLED_RAMDISK_TARGET) \
     $(INSTALLED_BOOTIMAGE_TARGET) \
@@ -26,9 +24,9 @@
     $(INSTALLED_PRODUCTIMAGE_TARGET) \
     $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)
 
-$(shareduid_violation_modules_filename): $(find_shareduid_script)
+$(shareduid_violation_modules_filename): $(HOST_OUT_EXECUTABLES)/find_shareduid_violation
 $(shareduid_violation_modules_filename): $(AAPT2)
-	$(find_shareduid_script) \
+	$(HOST_OUT_EXECUTABLES)/find_shareduid_violation \
 		--product_out $(PRODUCT_OUT) \
 		--aapt $(AAPT2) \
 		--copy_out_system $(TARGET_COPY_OUT_SYSTEM) \
diff --git a/core/tasks/find-shareduid-violation.py b/core/tasks/find-shareduid-violation.py
deleted file mode 100755
index 8dba5a1..0000000
--- a/core/tasks/find-shareduid-violation.py
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2019 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 argparse
-import json
-import os
-import subprocess
-import sys
-
-from collections import defaultdict
-from glob import glob
-
-def parse_args():
-    """Parse commandline arguments."""
-    parser = argparse.ArgumentParser(description='Find sharedUserId violators')
-    parser.add_argument('--product_out', help='PRODUCT_OUT directory',
-                        default=os.environ.get("PRODUCT_OUT"))
-    parser.add_argument('--aapt', help='Path to aapt or aapt2',
-                        default="aapt2")
-    parser.add_argument('--copy_out_system', help='TARGET_COPY_OUT_SYSTEM',
-                        default="system")
-    parser.add_argument('--copy_out_vendor', help='TARGET_COPY_OUT_VENDOR',
-                        default="vendor")
-    parser.add_argument('--copy_out_product', help='TARGET_COPY_OUT_PRODUCT',
-                        default="product")
-    parser.add_argument('--copy_out_system_ext', help='TARGET_COPY_OUT_SYSTEM_EXT',
-                        default="system_ext")
-    return parser.parse_args()
-
-def execute(cmd):
-    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-    out, err = map(lambda b: b.decode('utf-8'), p.communicate())
-    return p.returncode == 0, out, err
-
-def make_aapt_cmds(file):
-    return [aapt + ' dump ' + file + ' --file AndroidManifest.xml',
-            aapt + ' dump xmltree ' + file + ' --file AndroidManifest.xml']
-
-def extract_shared_uid(file):
-    for cmd in make_aapt_cmds(file):
-        success, manifest, error_msg = execute(cmd)
-        if success:
-            break
-    else:
-        print(error_msg, file=sys.stderr)
-        sys.exit()
-
-    for l in manifest.split('\n'):
-        if "sharedUserId" in l:
-            return l.split('"')[-2]
-    return None
-
-
-args = parse_args()
-
-product_out = args.product_out
-aapt = args.aapt
-
-partitions = (
-        ("system", args.copy_out_system),
-        ("vendor", args.copy_out_vendor),
-        ("product", args.copy_out_product),
-        ("system_ext", args.copy_out_system_ext),
-)
-
-shareduid_app_dict = defaultdict(list)
-
-for part, location in partitions:
-    for f in glob(os.path.join(product_out, location, "*", "*", "*.apk")):
-        apk_file = os.path.basename(f)
-        shared_uid = extract_shared_uid(f)
-
-        if shared_uid is None:
-            continue
-        shareduid_app_dict[shared_uid].append((part, apk_file))
-
-
-output = defaultdict(lambda: defaultdict(list))
-
-for uid, app_infos in shareduid_app_dict.items():
-    partitions = {p for p, _ in app_infos}
-    if len(partitions) > 1:
-        for part in partitions:
-            output[uid][part].extend([a for p, a in app_infos if p == part])
-
-print(json.dumps(output, indent=2, sort_keys=True))
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index 41696e8..7362275 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -240,7 +240,7 @@
     #  It must be of the form "YYYY-MM-DD" on production devices.
     #  It must match one of the Android Security Patch Level strings of the Public Security Bulletins.
     #  If there is no $PLATFORM_SECURITY_PATCH set, keep it empty.
-      PLATFORM_SECURITY_PATCH := 2020-10-05
+      PLATFORM_SECURITY_PATCH := 2020-11-05
 endif
 .KATI_READONLY := PLATFORM_SECURITY_PATCH
 
diff --git a/envsetup.sh b/envsetup.sh
index e4afdb9..a2d5d1d 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -307,6 +307,9 @@
     unset ANDROID_HOST_OUT
     export ANDROID_HOST_OUT=$(get_abs_build_var HOST_OUT)
 
+    unset ANDROID_SOONG_HOST_OUT
+    export ANDROID_SOONG_HOST_OUT=$(get_abs_build_var SOONG_HOST_OUT)
+
     unset ANDROID_HOST_OUT_TESTCASES
     export ANDROID_HOST_OUT_TESTCASES=$(get_abs_build_var HOST_OUT_TESTCASES)
 
diff --git a/target/board/generic_arm64/BoardConfig.mk b/target/board/generic_arm64/BoardConfig.mk
index 42660e5..3d3eb2e 100644
--- a/target/board/generic_arm64/BoardConfig.mk
+++ b/target/board/generic_arm64/BoardConfig.mk
@@ -54,9 +54,6 @@
 
 include build/make/target/board/BoardConfigGsiCommon.mk
 
-TARGET_NO_KERNEL := false
-BOARD_USES_RECOVERY_AS_BOOT := true
-
 BOARD_KERNEL-4.19-GZ_BOOTIMAGE_PARTITION_SIZE := 47185920
 BOARD_KERNEL-5.4_BOOTIMAGE_PARTITION_SIZE := 67108864
 BOARD_KERNEL-5.4-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 67108864
@@ -79,8 +76,19 @@
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
 BOARD_KERNEL_BINARIES += kernel-5.4-allsyms kernel-5.4-gz-allsyms kernel-5.4-lz4-allsyms
 endif
+
+# Boot image
+BOARD_USES_RECOVERY_AS_BOOT :=
+TARGET_NO_KERNEL := false
+BOARD_USES_GENERIC_KERNEL_IMAGE := true
 BOARD_KERNEL_MODULE_INTERFACE_VERSIONS := 5.4-android12-0
 
+# No vendor_boot
+BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT :=
+
+# No recovery
+BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE :=
+
 # Some vendors still haven't cleaned up all device specific directories under
 # root!
 
diff --git a/target/board/generic_arm64/device.mk b/target/board/generic_arm64/device.mk
index 866d7c7..7c19279 100644
--- a/target/board/generic_arm64/device.mk
+++ b/target/board/generic_arm64/device.mk
@@ -31,3 +31,4 @@
 endif
 
 PRODUCT_BUILD_VENDOR_BOOT_IMAGE := false
+PRODUCT_BUILD_RECOVERY_IMAGE := false
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 1a28cf0..8b7a9aa 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -117,6 +117,7 @@
     ip-up-vpn \
     javax.obex \
     keystore \
+    keystore2 \
     credstore \
     ld.mc \
     libaaudio \
@@ -381,6 +382,8 @@
     logpersist.start \
     logtagd.rc \
     procrank \
+    profcollectd \
+    profcollectctl \
     remount \
     showmap \
     sqlite3 \
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index ca6c2b2..11fb584 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -369,6 +369,32 @@
     ],
 }
 
+python_defaults {
+    name: "releasetools_find_shareduid_violation_defaults",
+    srcs: [
+        "find_shareduid_violation.py",
+    ],
+    libs: [
+        "releasetools_common",
+    ],
+}
+
+python_binary_host {
+    name: "find_shareduid_violation",
+    defaults: [
+        "releasetools_binary_defaults",
+        "releasetools_find_shareduid_violation_defaults",
+    ],
+}
+
+python_library_host {
+    name: "releasetools_find_shareduid_violation",
+    defaults: [
+        "releasetools_find_shareduid_violation_defaults",
+        "releasetools_library_defaults",
+    ],
+}
+
 python_binary_host {
     name: "make_recovery_patch",
     defaults: ["releasetools_binary_defaults"],
@@ -403,6 +429,7 @@
         "releasetools_build_super_image",
         "releasetools_check_target_files_vintf",
         "releasetools_common",
+        "releasetools_find_shareduid_violation",
         "releasetools_img_from_target_files",
         "releasetools_ota_from_target_files",
     ],
@@ -505,6 +532,7 @@
         "releasetools_build_super_image",
         "releasetools_check_target_files_vintf",
         "releasetools_common",
+        "releasetools_find_shareduid_violation",
         "releasetools_img_from_target_files",
         "releasetools_ota_from_target_files",
         "releasetools_verity_utils",
diff --git a/tools/releasetools/find_shareduid_violation.py b/tools/releasetools/find_shareduid_violation.py
new file mode 100755
index 0000000..35acde3
--- /dev/null
+++ b/tools/releasetools/find_shareduid_violation.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2019 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.
+#
+"""Find APK sharedUserId violators.
+
+Usage: find_shareduid_violation [args]
+
+  --product_out
+    PRODUCT_OUT directory
+
+  --aapt
+    Path to aapt or aapt2
+
+  --copy_out_system
+    TARGET_COPY_OUT_SYSTEM
+
+  --copy_out_vendor_
+    TARGET_COPY_OUT_VENDOR
+
+  --copy_out_product
+    TARGET_COPY_OUT_PRODUCT
+
+  --copy_out_system_ext
+    TARGET_COPY_OUT_SYSTEM_EXT
+"""
+
+import json
+import logging
+import os
+import re
+import subprocess
+import sys
+
+from collections import defaultdict
+from glob import glob
+
+import common
+
+logger = logging.getLogger(__name__)
+
+OPTIONS = common.OPTIONS
+OPTIONS.product_out = os.environ.get("PRODUCT_OUT")
+OPTIONS.aapt = "aapt2"
+OPTIONS.copy_out_system = "system"
+OPTIONS.copy_out_vendor = "vendor"
+OPTIONS.copy_out_product = "product"
+OPTIONS.copy_out_system_ext = "system_ext"
+
+
+def execute(cmd):
+  p = subprocess.Popen(
+      cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+  out, err = map(lambda b: b.decode("utf-8"), p.communicate())
+  return p.returncode == 0, out, err
+
+
+def make_aapt_cmds(aapt, apk):
+  return [
+      aapt + " dump " + apk + " --file AndroidManifest.xml",
+      aapt + " dump xmltree " + apk + " --file AndroidManifest.xml"
+  ]
+
+
+def extract_shared_uid(aapt, apk):
+  for cmd in make_aapt_cmds(aapt, apk):
+    success, manifest, error_msg = execute(cmd)
+    if success:
+      break
+  else:
+    logger.error(error_msg)
+    sys.exit()
+
+  pattern = re.compile(r"sharedUserId.*=\"([^\"]*)")
+
+  for line in manifest.split("\n"):
+    match = pattern.search(line)
+    if match:
+      return match.group(1)
+  return None
+
+
+def FindShareduidViolation(product_out, partition_map, aapt="aapt2"):
+  """Find sharedUserId violators in the given partitions.
+
+  Args:
+    product_out: The base directory containing the partition directories.
+    partition_map: A map of partition name -> directory name.
+    aapt: The name of the aapt binary. Defaults to aapt2.
+
+  Returns:
+    A string containing a JSON object describing the shared UIDs.
+  """
+  shareduid_app_dict = defaultdict(lambda: defaultdict(list))
+
+  for part, location in partition_map.items():
+    for f in glob(os.path.join(product_out, location, "*", "*", "*.apk")):
+      apk_file = os.path.basename(f)
+      shared_uid = extract_shared_uid(aapt, f)
+
+      if shared_uid is None:
+        continue
+      shareduid_app_dict[shared_uid][part].append(apk_file)
+
+  # Only output sharedUserId values that appear in >1 partition.
+  output = {}
+  for uid, partitions in shareduid_app_dict.items():
+    if len(partitions) > 1:
+      output[uid] = shareduid_app_dict[uid]
+
+  return json.dumps(output, indent=2, sort_keys=True)
+
+
+def main():
+  common.InitLogging()
+
+  def option_handler(o, a):
+    if o == "--product_out":
+      OPTIONS.product_out = a
+    elif o == "--aapt":
+      OPTIONS.aapt = a
+    elif o == "--copy_out_system":
+      OPTIONS.copy_out_system = a
+    elif o == "--copy_out_vendor":
+      OPTIONS.copy_out_vendor = a
+    elif o == "--copy_out_product":
+      OPTIONS.copy_out_product = a
+    elif o == "--copy_out_system_ext":
+      OPTIONS.copy_out_system_ext = a
+    else:
+      return False
+    return True
+
+  args = common.ParseOptions(
+      sys.argv[1:],
+      __doc__,
+      extra_long_opts=[
+          "product_out=",
+          "aapt=",
+          "copy_out_system=",
+          "copy_out_vendor=",
+          "copy_out_product=",
+          "copy_out_system_ext=",
+      ],
+      extra_option_handler=option_handler)
+
+  if args:
+    common.Usage(__doc__)
+    sys.exit(1)
+
+  partition_map = {
+      "system": OPTIONS.copy_out_system,
+      "vendor": OPTIONS.copy_out_vendor,
+      "product": OPTIONS.copy_out_product,
+      "system_ext": OPTIONS.copy_out_system_ext,
+  }
+
+  print(
+      FindShareduidViolation(OPTIONS.product_out, partition_map, OPTIONS.aapt))
+
+
+if __name__ == "__main__":
+  main()
diff --git a/tools/releasetools/merge_target_files.py b/tools/releasetools/merge_target_files.py
index 2da5cc0..0d135d6 100755
--- a/tools/releasetools/merge_target_files.py
+++ b/tools/releasetools/merge_target_files.py
@@ -98,6 +98,7 @@
 import check_target_files_vintf
 import common
 import img_from_target_files
+import find_shareduid_violation
 import ota_from_target_files
 
 logger = logging.getLogger(__name__)
@@ -943,6 +944,21 @@
   if not check_target_files_vintf.CheckVintf(output_target_files_temp_dir):
     raise RuntimeError('Incompatible VINTF metadata')
 
+  shareduid_violation_modules = os.path.join(
+      output_target_files_temp_dir, 'META', 'shareduid_violation_modules.json')
+  with open(shareduid_violation_modules, 'w') as f:
+    partition_map = {
+        'system': 'SYSTEM',
+        'vendor': 'VENDOR',
+        'product': 'PRODUCT',
+        'system_ext': 'SYSTEM_EXT',
+    }
+    violation = find_shareduid_violation.FindShareduidViolation(
+        output_target_files_temp_dir, partition_map)
+    f.write(violation)
+    # TODO(b/171431774): Add a check to common.py to check if the
+    # shared UIDs cross the input build partition boundary.
+
   generate_images(output_target_files_temp_dir, rebuild_recovery)
 
   generate_super_empty_image(output_target_files_temp_dir, output_super_empty)
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index fba43e9..9a57c8a 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -751,6 +751,9 @@
   common.ZipDelete(target_file, POSTINSTALL_CONFIG)
   return target_file
 
+def ParseInfoDict(target_file_path):
+  with zipfile.ZipFile(target_file_path, 'r', allowZip64=True) as zfp:
+    return common.LoadInfoDict(zfp)
 
 def GetTargetFilesZipForPartialUpdates(input_file, ab_partitions):
   """Returns a target-files.zip for partial ota update package generation.
@@ -781,7 +784,8 @@
     raise ValueError("Cannot find {} in input zipfile".format(partition_name))
 
   with zipfile.ZipFile(input_file, allowZip64=True) as input_zip:
-    original_ab_partitions = input_zip.read(AB_PARTITIONS).decode().splitlines()
+    original_ab_partitions = input_zip.read(
+        AB_PARTITIONS).decode().splitlines()
     namelist = input_zip.namelist()
 
   unrecognized_partitions = [partition for partition in ab_partitions if
@@ -871,7 +875,7 @@
   with open(new_ab_partitions, 'w') as f:
     for partition in ab_partitions:
       if (partition in dynamic_partition_list and
-              partition not in super_block_devices):
+          partition not in super_block_devices):
         logger.info("Dropping %s from ab_partitions.txt", partition)
         continue
       f.write(partition + "\n")
@@ -906,6 +910,7 @@
 
   return target_file
 
+
 def GetTargetFilesZipForCustomImagesUpdates(input_file, custom_images):
   """Returns a target-files.zip for custom partitions update.
 
@@ -944,6 +949,12 @@
 
   return target_file
 
+def GeneratePartitionTimestampFlags(partition_state):
+  partition_timestamps = [
+      part.partition_name + ":" + part.version
+      for part in partition_state]
+  return ["--partition_timestamps", ",".join(partition_timestamps)]
+
 def GenerateAbOtaPackage(target_file, output_file, source_file=None):
   """Generates an Android OTA package that has A/B update payload."""
   # Stage the output zip package for package signing.
@@ -961,6 +972,12 @@
         "META/ab_partitions.txt is required for ab_update."
     target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
     source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
+    vendor_prop = source_info.info_dict.get("vendor.build.prop")
+    if vendor_prop and \
+        vendor_prop.GetProp("ro.virtual_ab.compression.enabled") == "true":
+      # TODO(zhangkelvin) Remove this once FEC on VABC is supported
+      logger.info("Virtual AB Compression enabled, disabling FEC")
+      OPTIONS.disable_fec_computation = True
   else:
     assert "ab_partitions" in OPTIONS.info_dict, \
         "META/ab_partitions.txt is required for ab_update."
@@ -987,30 +1004,29 @@
   # Target_file may have been modified, reparse ab_partitions
   with zipfile.ZipFile(target_file, allowZip64=True) as zfp:
     target_info.info_dict['ab_partitions'] = zfp.read(
-        AB_PARTITIONS).strip().split("\n")
+        AB_PARTITIONS).decode().strip().split("\n")
 
   # Metadata to comply with Android OTA package format.
   metadata = GetPackageMetadata(target_info, source_info)
   # Generate payload.
   payload = Payload()
 
-  partition_timestamps = []
+  partition_timestamps_flags = []
   # Enforce a max timestamp this payload can be applied on top of.
   if OPTIONS.downgrade:
     max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
   else:
     max_timestamp = str(metadata.postcondition.timestamp)
-    partition_timestamps = [
-        part.partition_name + ":" + part.version
-        for part in metadata.postcondition.partition_state]
-  additional_args += ["--max_timestamp", max_timestamp]
-  if partition_timestamps:
-    additional_args.extend(
-        ["--partition_timestamps", ",".join(
-            partition_timestamps)]
-    )
+    partition_timestamps_flags = GeneratePartitionTimestampFlags(
+        metadata.postcondition.partition_state)
 
-  payload.Generate(target_file, source_file, additional_args)
+  additional_args += ["--max_timestamp", max_timestamp]
+
+  payload.Generate(
+      target_file,
+      source_file,
+      additional_args + partition_timestamps_flags
+   )
 
   # Sign the payload.
   payload_signer = PayloadSigner()
@@ -1027,8 +1043,15 @@
     secondary_target_file = GetTargetFilesZipForSecondaryImages(
         target_file, OPTIONS.skip_postinstall)
     secondary_payload = Payload(secondary=True)
+    assert not OPTIONS.downgrade
+    partition_timestamps_flags = GeneratePartitionTimestampFlags(
+      [part
+       for part in metadata.postcondition.partition_state
+       if part.partition_name not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
+    )
     secondary_payload.Generate(secondary_target_file,
-                               additional_args=additional_args)
+                               additional_args=["--max_timestamp",
+                               max_timestamp]+partition_timestamps_flags)
     secondary_payload.Sign(payload_signer)
     secondary_payload.WriteToZip(output_zip)
 
@@ -1222,8 +1245,7 @@
   if OPTIONS.extracted_input is not None:
     OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
   else:
-    with zipfile.ZipFile(args[0], 'r', allowZip64=True) as input_zip:
-      OPTIONS.info_dict = common.LoadInfoDict(input_zip)
+    OPTIONS.info_dict = ParseInfoDict(args[0])
 
   # TODO(xunchang) for retrofit and partial updates, maybe we should rebuild the
   # target-file and reload the info_dict. So the info will be consistent with
@@ -1235,8 +1257,7 @@
   # Load the source build dict if applicable.
   if OPTIONS.incremental_source is not None:
     OPTIONS.target_info_dict = OPTIONS.info_dict
-    with zipfile.ZipFile(OPTIONS.incremental_source, 'r', allowZip64=True) as source_zip:
-      OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
+    OPTIONS.source_info_dict = ParseInfoDict(OPTIONS.incremental_source)
 
     logger.info("--- source info ---")
     common.DumpInfoDict(OPTIONS.source_info_dict)
diff --git a/tools/zipalign/Android.bp b/tools/zipalign/Android.bp
index 8e6196d..3eb660d 100644
--- a/tools/zipalign/Android.bp
+++ b/tools/zipalign/Android.bp
@@ -4,20 +4,31 @@
 // Zip alignment tool
 //
 
-cc_binary_host {
-    name: "zipalign",
+cc_defaults {
+    name: "zipalign_defaults",
+    target: {
+        windows: {
+            host_ldlibs: ["-lpthread"],
+            enabled: true,
+        },
+    },
+}
 
+cc_library_host_static {
+    name: "libzipalign",
     srcs: [
         "ZipAlign.cpp",
         "ZipEntry.cpp",
         "ZipFile.cpp",
     ],
-
+    export_include_dirs: [
+        "include",
+    ],
     cflags: ["-Wall", "-Werror"],
 
     // NOTE: Do not add any shared_libs dependencies because they will break the
     // static_sdk_tools target.
-    static_libs: [
+    whole_static_libs: [
         "libutils",
         "libcutils",
         "liblog",
@@ -26,11 +37,32 @@
         "libbase",
         "libzopfli",
     ],
+    defaults: ["zipalign_defaults"],
+}
 
-    target: {
-        windows: {
-            host_ldlibs: ["-lpthread"],
-            enabled: true,
-        },
-    },
+cc_binary_host {
+    name: "zipalign",
+    srcs: [
+        "ZipAlignMain.cpp",
+    ],
+    cflags: ["-Wall", "-Werror"],
+    static_libs: [
+        "libzipalign",
+    ],
+    defaults: ["zipalign_defaults"],
+}
+
+cc_test_host {
+    name: "zipalign_tests",
+    srcs: [
+        "tests/src/*_test.cpp",
+    ],
+    static_libs: [
+        "libzipalign",
+        "libgmock",
+    ],
+    data: [
+         "tests/data/unaligned.zip",
+    ],
+    defaults: ["zipalign_defaults"],
 }
diff --git a/tools/zipalign/ZipAlign.cpp b/tools/zipalign/ZipAlign.cpp
index eea1749..1851ac5 100644
--- a/tools/zipalign/ZipAlign.cpp
+++ b/tools/zipalign/ZipAlign.cpp
@@ -14,35 +14,13 @@
  * limitations under the License.
  */
 
-/*
- * Zip alignment tool
- */
 #include "ZipFile.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 
-using namespace android;
-
-/*
- * Show program usage.
- */
-void usage(void)
-{
-    fprintf(stderr, "Zip alignment utility\n");
-    fprintf(stderr, "Copyright (C) 2009 The Android Open Source Project\n\n");
-    fprintf(stderr,
-        "Usage: zipalign [-f] [-p] [-v] [-z] <align> infile.zip outfile.zip\n"
-        "       zipalign -c [-p] [-v] <align> infile.zip\n\n" );
-    fprintf(stderr,
-        "  <align>: alignment in bytes, e.g. '4' provides 32-bit alignment\n");
-    fprintf(stderr, "  -c: check alignment only (does not modify file)\n");
-    fprintf(stderr, "  -f: overwrite existing outfile.zip\n");
-    fprintf(stderr, "  -p: memory page alignment for stored shared object files\n");
-    fprintf(stderr, "  -v: verbose output\n");
-    fprintf(stderr, "  -z: recompress using Zopfli\n");
-}
+namespace android {
 
 static int getAlignment(bool pageAlignSharedLibs, int defaultAlignment,
     ZipEntry* pEntry) {
@@ -126,7 +104,7 @@
  * Process a file.  We open the input and output files, failing if the
  * output file exists and "force" wasn't specified.
  */
-static int process(const char* inFileName, const char* outFileName,
+int process(const char* inFileName, const char* outFileName,
     int alignment, bool force, bool zopfli, bool pageAlignSharedLibs)
 {
     ZipFile zin, zout;
@@ -169,7 +147,7 @@
 /*
  * Verify the alignment of a zip archive.
  */
-static int verify(const char* fileName, int alignment, bool verbose,
+int verify(const char* fileName, int alignment, bool verbose,
     bool pageAlignSharedLibs)
 {
     ZipFile zipFile;
@@ -218,92 +196,4 @@
     return foundBad ? 1 : 0;
 }
 
-/*
- * Parse args.
- */
-int main(int argc, char* const argv[])
-{
-    bool wantUsage = false;
-    bool check = false;
-    bool force = false;
-    bool verbose = false;
-    bool zopfli = false;
-    bool pageAlignSharedLibs = false;
-    int result = 1;
-    int alignment;
-    char* endp;
-
-    if (argc < 4) {
-        wantUsage = true;
-        goto bail;
-    }
-
-    argc--;
-    argv++;
-
-    while (argc && argv[0][0] == '-') {
-        const char* cp = argv[0] +1;
-
-        while (*cp != '\0') {
-            switch (*cp) {
-            case 'c':
-                check = true;
-                break;
-            case 'f':
-                force = true;
-                break;
-            case 'v':
-                verbose = true;
-                break;
-            case 'z':
-                zopfli = true;
-                break;
-            case 'p':
-                pageAlignSharedLibs = true;
-                break;
-            default:
-                fprintf(stderr, "ERROR: unknown flag -%c\n", *cp);
-                wantUsage = true;
-                goto bail;
-            }
-
-            cp++;
-        }
-
-        argc--;
-        argv++;
-    }
-
-    if (!((check && argc == 2) || (!check && argc == 3))) {
-        wantUsage = true;
-        goto bail;
-    }
-
-    alignment = strtol(argv[0], &endp, 10);
-    if (*endp != '\0' || alignment <= 0) {
-        fprintf(stderr, "Invalid value for alignment: %s\n", argv[0]);
-        wantUsage = true;
-        goto bail;
-    }
-
-    if (check) {
-        /* check existing archive for correct alignment */
-        result = verify(argv[1], alignment, verbose, pageAlignSharedLibs);
-    } else {
-        /* create the new archive */
-        result = process(argv[1], argv[2], alignment, force, zopfli, pageAlignSharedLibs);
-
-        /* trust, but verify */
-        if (result == 0) {
-            result = verify(argv[2], alignment, verbose, pageAlignSharedLibs);
-        }
-    }
-
-bail:
-    if (wantUsage) {
-        usage();
-        result = 2;
-    }
-
-    return result;
-}
+} // namespace android
diff --git a/tools/zipalign/ZipAlignMain.cpp b/tools/zipalign/ZipAlignMain.cpp
new file mode 100644
index 0000000..49be916
--- /dev/null
+++ b/tools/zipalign/ZipAlignMain.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Zip alignment tool
+ */
+
+#include "ZipAlign.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+using namespace android;
+
+/*
+ * Show program usage.
+ */
+void usage(void)
+{
+    fprintf(stderr, "Zip alignment utility\n");
+    fprintf(stderr, "Copyright (C) 2009 The Android Open Source Project\n\n");
+    fprintf(stderr,
+        "Usage: zipalign [-f] [-p] [-v] [-z] <align> infile.zip outfile.zip\n"
+        "       zipalign -c [-p] [-v] <align> infile.zip\n\n" );
+    fprintf(stderr,
+        "  <align>: alignment in bytes, e.g. '4' provides 32-bit alignment\n");
+    fprintf(stderr, "  -c: check alignment only (does not modify file)\n");
+    fprintf(stderr, "  -f: overwrite existing outfile.zip\n");
+    fprintf(stderr, "  -p: memory page alignment for stored shared object files\n");
+    fprintf(stderr, "  -v: verbose output\n");
+    fprintf(stderr, "  -z: recompress using Zopfli\n");
+}
+
+
+/*
+ * Parse args.
+ */
+int main(int argc, char* const argv[])
+{
+    bool wantUsage = false;
+    bool check = false;
+    bool force = false;
+    bool verbose = false;
+    bool zopfli = false;
+    bool pageAlignSharedLibs = false;
+    int result = 1;
+    int alignment;
+    char* endp;
+
+    if (argc < 4) {
+        wantUsage = true;
+        goto bail;
+    }
+
+    argc--;
+    argv++;
+
+    while (argc && argv[0][0] == '-') {
+        const char* cp = argv[0] +1;
+
+        while (*cp != '\0') {
+            switch (*cp) {
+            case 'c':
+                check = true;
+                break;
+            case 'f':
+                force = true;
+                break;
+            case 'v':
+                verbose = true;
+                break;
+            case 'z':
+                zopfli = true;
+                break;
+            case 'p':
+                pageAlignSharedLibs = true;
+                break;
+            default:
+                fprintf(stderr, "ERROR: unknown flag -%c\n", *cp);
+                wantUsage = true;
+                goto bail;
+            }
+
+            cp++;
+        }
+
+        argc--;
+        argv++;
+    }
+
+    if (!((check && argc == 2) || (!check && argc == 3))) {
+        wantUsage = true;
+        goto bail;
+    }
+
+    alignment = strtol(argv[0], &endp, 10);
+    if (*endp != '\0' || alignment <= 0) {
+        fprintf(stderr, "Invalid value for alignment: %s\n", argv[0]);
+        wantUsage = true;
+        goto bail;
+    }
+
+    if (check) {
+        /* check existing archive for correct alignment */
+        result = verify(argv[1], alignment, verbose, pageAlignSharedLibs);
+    } else {
+        /* create the new archive */
+        result = process(argv[1], argv[2], alignment, force, zopfli, pageAlignSharedLibs);
+
+        /* trust, but verify */
+        if (result == 0) {
+            result = verify(argv[2], alignment, verbose, pageAlignSharedLibs);
+        }
+    }
+
+bail:
+    if (wantUsage) {
+        usage();
+        result = 2;
+    }
+
+    return result;
+}
diff --git a/tools/zipalign/ZipEntry.cpp b/tools/zipalign/ZipEntry.cpp
index 810d74a..5233f0a 100644
--- a/tools/zipalign/ZipEntry.cpp
+++ b/tools/zipalign/ZipEntry.cpp
@@ -29,7 +29,7 @@
 #include <string.h>
 #include <time.h>
 
-using namespace android;
+namespace android {
 
 /*
  * Initialize a new ZipEntry structure from a FILE* positioned at a
@@ -696,3 +696,5 @@
         ALOGD("  comment: '%s'\n", mFileComment);
 }
 
+} // namespace android
+
diff --git a/tools/zipalign/ZipFile.cpp b/tools/zipalign/ZipFile.cpp
index 88505b7..29d1bc6 100644
--- a/tools/zipalign/ZipFile.cpp
+++ b/tools/zipalign/ZipFile.cpp
@@ -35,7 +35,7 @@
 #include <assert.h>
 #include <inttypes.h>
 
-using namespace android;
+namespace android {
 
 /*
  * Some environments require the "b", some choke on it.
@@ -134,7 +134,7 @@
 /*
  * Return the Nth entry in the archive.
  */
-android::ZipEntry* ZipFile::getEntryByIndex(int idx) const
+ZipEntry* ZipFile::getEntryByIndex(int idx) const
 {
     if (idx < 0 || idx >= (int) mEntries.size())
         return NULL;
@@ -145,7 +145,7 @@
 /*
  * Find an entry by name.
  */
-android::ZipEntry* ZipFile::getEntryByName(const char* fileName) const
+ZipEntry* ZipFile::getEntryByName(const char* fileName) const
 {
     /*
      * Do a stupid linear string-compare search.
@@ -1397,3 +1397,4 @@
         mCentralDirSize, mCentralDirOffset, mCommentLen);
 }
 
+} // namespace android
diff --git a/tools/zipalign/include/ZipAlign.h b/tools/zipalign/include/ZipAlign.h
new file mode 100644
index 0000000..ab36086
--- /dev/null
+++ b/tools/zipalign/include/ZipAlign.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ZIPALIGN_H
+#define ZIPALIGN_H
+
+namespace android {
+
+/*
+ * Generate a new, aligned, zip "output" from an "input" zip.
+ * - alignTo: Alignment (in bytes) for uncompressed entries.
+ * - force  : Overwrite output if it exists, fail otherwise.
+ * - zopfli : Recompress compressed entries with more efficient algorithm.
+ *            Copy compressed entries as-is, and unaligned, otherwise.
+ * - pageAlignSharedLibs: Align .so files to 4096 and other files to
+ *   alignTo, or all files to alignTo if false..
+ *
+ * Returns 0 on success.
+ */
+int process(const char* input, const char* output, int alignTo, bool force,
+    bool zopfli, bool pageAlignSharedLibs);
+
+/*
+ * Verify the alignment of a zip archive.
+ * - alignTo: Alignment (in bytes) for uncompressed entries.
+ * - pageAlignSharedLibs: Align .so files to 4096 and other files to
+ *   alignTo, or all files to alignTo if false..
+ *
+ * Returns 0 on success.
+ */
+int verify(const char* fileName, int alignTo, bool verbose,
+    bool pageAlignSharedLibs);
+
+} // namespace android
+
+#endif // ZIPALIGN_H
diff --git a/tools/zipalign/tests/data/unaligned.zip b/tools/zipalign/tests/data/unaligned.zip
new file mode 100644
index 0000000..d572b1a
--- /dev/null
+++ b/tools/zipalign/tests/data/unaligned.zip
Binary files differ
diff --git a/tools/zipalign/tests/src/align_test.cpp b/tools/zipalign/tests/src/align_test.cpp
new file mode 100644
index 0000000..b8f2e15
--- /dev/null
+++ b/tools/zipalign/tests/src/align_test.cpp
@@ -0,0 +1,15 @@
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "ZipAlign.h"
+
+#include <stdio.h>
+
+using namespace android;
+
+TEST(Align, Unaligned) {
+  const char* src = "tests/data/unaligned.zip";
+  const char* dst = "tests/data/unaligned_out.zip";
+  int result = process(src, dst, 4, true, false, 4096);
+  ASSERT_EQ(0, result);
+}