Merge "Revert "Add NeuralNetworks HAL v1.3""
diff --git a/core/Makefile b/core/Makefile
index c9b478e..1b6c532 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -2154,6 +2154,7 @@
 $(error MTD device is no longer supported and thus BOARD_NAND_SPARE_SIZE is deprecated.)
 endif
 
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
 # -----------------------------------------------------------------
 # the debug ramdisk, which is the original ramdisk plus additional
 # files: force_debuggable, adb_debug.prop and userdebug sepolicy.
@@ -2250,41 +2251,116 @@
 # Using a test key to sign boot-debug.img to continue booting with the mismatched
 # public key, if the device is unlocked.
 ifneq ($(BOARD_AVB_BOOT_KEY_PATH),)
-BOARD_AVB_DEBUG_BOOT_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
-$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): PRIVATE_AVB_DEBUG_BOOT_SIGNING_ARGS := \
-  --algorithm SHA256_RSA2048 --key $(BOARD_AVB_DEBUG_BOOT_KEY_PATH)
-$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_DEBUG_BOOT_KEY_PATH)
+$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_BOOT_TEST_KEY_PATH)
 endif
 
+BOARD_AVB_BOOT_TEST_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
+INTERNAL_AVB_BOOT_TEST_SIGNING_ARGS := --algorithm SHA256_RSA2048 --key $(BOARD_AVB_BOOT_TEST_KEY_PATH)
+# $(1): the bootimage to sign
+define test-key-sign-bootimage
+$(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(BOARD_BOOTIMAGE_PARTITION_SIZE)))
+$(AVBTOOL) add_hash_footer \
+  --image $(1) \
+  --partition_size $(BOARD_BOOTIMAGE_PARTITION_SIZE) \
+  --partition_name boot $(INTERNAL_AVB_BOOT_TEST_SIGNING_ARGS) \
+  $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)
+$(call assert-max-image-size,$(1),$(BOARD_BOOTIMAGE_PARTITION_SIZE))
+endef
+
 # Depends on original boot.img and ramdisk-debug.img, to build the new boot-debug.img
 $(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_BOOTIMAGE_TARGET) $(INSTALLED_DEBUG_RAMDISK_TARGET)
 	$(call pretty,"Target boot debug image: $@")
 	$(MKBOOTIMG) $(INTERNAL_DEBUG_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@
-	$(if $(BOARD_AVB_BOOT_KEY_PATH),\
-	  $(call assert-max-image-size,$@,$(call get-hash-image-max-size,$(BOARD_BOOTIMAGE_PARTITION_SIZE))); \
-	  $(AVBTOOL) add_hash_footer \
-	    --image $@ \
-	    --partition_size $(BOARD_BOOTIMAGE_PARTITION_SIZE) \
-	    --partition_name boot $(PRIVATE_AVB_DEBUG_BOOT_SIGNING_ARGS) \
-	    $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS), \
-	  $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE)))
+	$(if $(BOARD_AVB_BOOT_KEY_PATH),$(call test-key-sign-bootimage,$@))
 
 .PHONY: bootimage_debug-nodeps
 bootimage_debug-nodeps: $(MKBOOTIMG)
 	echo "make $@: ignoring dependencies"
 	$(MKBOOTIMG) $(INTERNAL_DEBUG_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_DEBUG_BOOTIMAGE_TARGET)
-	$(if $(BOARD_AVB_BOOT_KEY_PATH),\
-	  $(call assert-max-image-size,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(call get-hash-image-max-size,$(BOARD_BOOTIMAGE_PARTITION_SIZE))); \
-	  $(AVBTOOL) add_hash_footer \
-	    --image $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \
-	    --partition_size $(BOARD_BOOTIMAGE_PARTITION_SIZE) \
-	    --partition_name boot $(PRIVATE_AVB_DEBUG_BOOT_SIGNING_ARGS) \
-	    $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS), \
-	  $(call assert-max-image-size,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(BOARD_BOOTIMAGE_PARTITION_SIZE)))
+	$(if $(BOARD_AVB_BOOT_KEY_PATH),$(call test-key-sign-bootimage,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET)))
 
 endif # TARGET_NO_KERNEL
 
 # -----------------------------------------------------------------
+# The test harness ramdisk, which is based off debug_ramdisk, plus a
+# few additional test-harness-specific properties in adb_debug.prop.
+
+ifdef BUILDING_RAMDISK_IMAGE
+BUILT_TEST_HARNESS_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk-test-harness.img
+INSTALLED_TEST_HARNESS_RAMDISK_TARGET := $(BUILT_TEST_HARNESS_RAMDISK_TARGET)
+
+# rsync the content from ramdisk-debug.img to ramdisk-test-harness.img, then
+# appends a few test harness specific properties into the adb_debug.prop.
+TEST_HARNESS_RAMDISK_SYNC_DIR := $(PRODUCT_OUT)/debug_ramdisk
+TEST_HARNESS_RAMDISK_ROOT_DIR := $(PRODUCT_OUT)/test_harness_ramdisk
+
+# The following TARGET_TEST_HARNESS_RAMDISK_OUT will be $(PRODUCT_OUT)/test_harness_ramdisk/first_stage_ramdisk,
+# if BOARD_USES_RECOVERY_AS_BOOT is true. Otherwise, it will be $(PRODUCT_OUT)/test_harness_ramdisk.
+TEST_HARNESS_PROP_TARGET := $(TARGET_TEST_HARNESS_RAMDISK_OUT)/adb_debug.prop
+ADDITIONAL_TEST_HARNESS_PROPERTIES := ro.audio.silent=1
+ADDITIONAL_TEST_HARNESS_PROPERTIES += ro.test_harness=1
+
+# $(1): a list of key=value pairs for additional property assignments
+# $(2): the target .prop file to append the properties from $(1)
+define append-test-harness-props
+  echo "#" >> $(2); \
+  echo "# ADDITIONAL TEST HARNESS_PROPERTIES" >> $(2); \
+  echo "#" >> $(2);
+  $(foreach line,$(1), echo "$(line)" >> $(2);)
+endef
+
+$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(INSTALLED_DEBUG_RAMDISK_TARGET)
+$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_TEST_HARNESS_RAMDISK_FILES) | $(MINIGZIP)
+	$(call pretty,"Target test harness ram disk: $@")
+	rsync -a $(TEST_HARNESS_RAMDISK_SYNC_DIR)/ $(TEST_HARNESS_RAMDISK_ROOT_DIR)
+	$(call append-test-harness-props,$(ADDITIONAL_TEST_HARNESS_PROPERTIES),$(TEST_HARNESS_PROP_TARGET))
+	$(MKBOOTFS) -d $(TARGET_OUT) $(TEST_HARNESS_RAMDISK_ROOT_DIR) | $(MINIGZIP) > $@
+
+.PHONY: ramdisk_test_harness-nodeps
+ramdisk_test_harness-nodeps: $(MKBOOTFS) | $(MINIGZIP)
+	echo "make $@: ignoring dependencies"
+	rsync -a $(TEST_HARNESS_RAMDISK_SYNC_DIR)/ $(TEST_HARNESS_RAMDISK_ROOT_DIR)
+	$(call append-test-harness-props,$(ADDITIONAL_TEST_HARNESS_PROPERTIES),$(TEST_HARNESS_PROP_TARGET))
+	$(MKBOOTFS) -d $(TARGET_OUT) $(TEST_HARNESS_RAMDISK_ROOT_DIR) | $(MINIGZIP) > $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)
+
+endif # BUILDING_RAMDISK_IMAGE
+
+# -----------------------------------------------------------------
+# the boot-test-harness.img, which is the kernel plus ramdisk-test-harness.img
+#
+# Note: it's intentional to skip signing for boot-test-harness.img, because it
+# can only be used if the device is unlocked with verification error.
+ifneq ($(strip $(TARGET_NO_KERNEL)),true)
+
+INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot-test-harness.img
+
+# Replace ramdisk-debug.img in $(MKBOOTIMG) ARGS with ramdisk-test-harness.img to build boot-test-harness.img
+INTERNAL_TEST_HARNESS_BOOTIMAGE_ARGS := $(subst $(INSTALLED_DEBUG_RAMDISK_TARGET),$(INSTALLED_TEST_HARNESS_RAMDISK_TARGET),$(INTERNAL_DEBUG_BOOTIMAGE_ARGS))
+
+# If boot.img is chained but boot-test-harness.img is not signed, libavb in bootloader
+# will fail to find valid AVB metadata from the end of /boot, thus stop booting.
+# Using a test key to sign boot-test-harness.img to continue booting with the mismatched
+# public key, if the device is unlocked.
+ifneq ($(BOARD_AVB_BOOT_KEY_PATH),)
+$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_BOOT_TEST_KEY_PATH)
+endif
+
+# Build the new boot-test-harness.img, based on boot-debug.img and ramdisk-test-harness.img.
+$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)
+	$(call pretty,"Target boot test harness image: $@")
+	$(MKBOOTIMG) $(INTERNAL_TEST_HARNESS_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@
+	$(if $(BOARD_AVB_BOOT_KEY_PATH),$(call test-key-sign-bootimage,$@))
+
+.PHONY: bootimage_test_harness-nodeps
+bootimage_test_harness-nodeps: $(MKBOOTIMG)
+	echo "make $@: ignoring dependencies"
+	$(MKBOOTIMG) $(INTERNAL_TEST_HARNESS_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET)
+	$(if $(BOARD_AVB_BOOT_KEY_PATH),$(call test-key-sign-bootimage,$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET)))
+
+endif # TARGET_NO_KERNEL
+endif # BOARD_BUILD_SYSTEM_ROOT_IMAGE is not true
+
+# -----------------------------------------------------------------
 # system image
 #
 # Remove overridden packages from $(ALL_PDK_FUSION_FILES)
@@ -4433,9 +4509,8 @@
 ifeq ($(PRODUCT_VIRTUAL_AB_OTA),true)
 	echo "virtual_ab=true" >> $(zip_root)/META/dynamic_partitions_info.txt
 endif # PRODUCT_VIRTUAL_AB_OTA
-	@# TODO(b/134525174): Remove `-r` after addressing the issue with recovery patch generation.
-	$(hide) PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH MKBOOTIMG=$(MKBOOTIMG) \
-	    $(ADD_IMG_TO_TARGET_FILES) -a -r -v -p $(HOST_OUT) $(zip_root)
+	PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH MKBOOTIMG=$(MKBOOTIMG) \
+	    $(ADD_IMG_TO_TARGET_FILES) -a -v -p $(HOST_OUT) $(zip_root)
 ifeq ($(BUILD_QEMU_IMAGES),true)
 	$(hide) AVBTOOL=$(AVBTOOL) $(MK_VBMETA_BOOT_KERNEL_CMDLINE_SH) $(zip_root)/IMAGES/vbmeta.img \
 	    $(zip_root)/IMAGES/system.img $(zip_root)/IMAGES/VerifiedBootParams.textproto
diff --git a/core/board_config.mk b/core/board_config.mk
index 3127c19..9032aaf 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -263,6 +263,7 @@
 # Now we can substitute with the real value of TARGET_COPY_OUT_DEBUG_RAMDISK
 ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
 TARGET_COPY_OUT_DEBUG_RAMDISK := debug_ramdisk/first_stage_ramdisk
+TARGET_COPY_OUT_TEST_HARNESS_RAMDISK := test_harness_ramdisk/first_stage_ramdisk
 endif
 
 ###########################################
diff --git a/core/config.mk b/core/config.mk
index d120d61..49b9329 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -1171,8 +1171,10 @@
     systemotherimage-nodeps \
     ramdisk-nodeps \
     ramdisk_debug-nodeps \
+    ramdisk_test_harness-nodeps \
     bootimage-nodeps \
     bootimage_debug-nodeps \
+    bootimage_test_harness-nodeps \
     recoveryimage-nodeps \
     vbmetaimage-nodeps \
     product-graph dump-products
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 5a615ac..0c58cd6 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -228,6 +228,7 @@
 TARGET_COPY_OUT_OEM := oem
 TARGET_COPY_OUT_RAMDISK := ramdisk
 TARGET_COPY_OUT_DEBUG_RAMDISK := debug_ramdisk
+TARGET_COPY_OUT_TEST_HARNESS_RAMDISK := test_harness_ramdisk
 TARGET_COPY_OUT_ROOT := root
 TARGET_COPY_OUT_RECOVERY := recovery
 # The directory used for optional partitions depend on the BoardConfig, so
@@ -825,6 +826,7 @@
 TARGET_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_RAMDISK)
 TARGET_RAMDISK_OUT_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)
 TARGET_DEBUG_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_DEBUG_RAMDISK)
+TARGET_TEST_HARNESS_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_TEST_HARNESS_RAMDISK)
 
 TARGET_VENDOR_RAMDISK_OUT := $(PRODUCT_OUT)/$(TARGET_COPY_OUT_VENDOR_RAMDISK)
 
diff --git a/core/main.mk b/core/main.mk
index fa203ca..e9d4390 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -1490,6 +1490,9 @@
 .PHONY: ramdisk_debug
 ramdisk_debug: $(INSTALLED_DEBUG_RAMDISK_TARGET)
 
+.PHONY: ramdisk_test_harness
+ramdisk_test_harness: $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET)
+
 .PHONY: userdataimage
 userdataimage: $(INSTALLED_USERDATAIMAGE_TARGET)
 
@@ -1530,6 +1533,9 @@
 .PHONY: bootimage_debug
 bootimage_debug: $(INSTALLED_DEBUG_BOOTIMAGE_TARGET)
 
+.PHONY: bootimage_test_harness
+bootimage_test_harness: $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET)
+
 .PHONY: vbmetaimage
 vbmetaimage: $(INSTALLED_VBMETAIMAGE_TARGET)
 
@@ -1697,6 +1703,10 @@
       $(INSTALLED_DEBUG_RAMDISK_TARGET) \
       $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \
     )
+    $(call dist-for-goals, bootimage_test_harness, \
+      $(INSTALLED_TEST_HARNESS_RAMDISK_TARGET) \
+      $(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET) \
+    )
   endif
 
   ifeq ($(EMMA_INSTRUMENT),true)
diff --git a/target/product/gsi/current.txt b/target/product/gsi/current.txt
index aaa6621..6649f28 100644
--- a/target/product/gsi/current.txt
+++ b/target/product/gsi/current.txt
@@ -77,6 +77,7 @@
 VNDK-core: android.hardware.bluetooth.audio@2.0.so
 VNDK-core: android.hardware.bluetooth@1.0.so
 VNDK-core: android.hardware.boot@1.0.so
+VNDK-core: android.hardware.boot@1.1.so
 VNDK-core: android.hardware.broadcastradio@1.0.so
 VNDK-core: android.hardware.broadcastradio@1.1.so
 VNDK-core: android.hardware.broadcastradio@2.0.so
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index 4ba8643..8249915 100755
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -337,13 +337,8 @@
 
   # Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and
   # build fingerprint).
-  uuid_seed = what + "-"
-  if "build.prop" in info_dict:
-    build_prop = info_dict["build.prop"]
-    if "ro.build.fingerprint" in build_prop:
-      uuid_seed += build_prop["ro.build.fingerprint"]
-    elif "ro.build.thumbprint" in build_prop:
-      uuid_seed += build_prop["ro.build.thumbprint"]
+  build_info = common.BuildInfo(info_dict)
+  uuid_seed = what + "-" + build_info.fingerprint
   image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed))
   hash_seed = "hash_seed-" + uuid_seed
   image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed))
@@ -728,7 +723,7 @@
 
   # A map between partition names and their paths, which could be used when
   # generating AVB vbmeta image.
-  partitions = dict()
+  partitions = {}
 
   def banner(s):
     logger.info("\n\n++++ %s  ++++\n\n", s)
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 37c03ab..d73628f 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -284,6 +284,225 @@
       pass
 
 
+class BuildInfo(object):
+  """A class that holds the information for a given build.
+
+  This class wraps up the property querying for a given source or target build.
+  It abstracts away the logic of handling OEM-specific properties, and caches
+  the commonly used properties such as fingerprint.
+
+  There are two types of info dicts: a) build-time info dict, which is generated
+  at build time (i.e. included in a target_files zip); b) OEM info dict that is
+  specified at package generation time (via command line argument
+  '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
+  having "oem_fingerprint_properties" in build-time info dict), all the queries
+  would be answered based on build-time info dict only. Otherwise if using
+  OEM-specific properties, some of them will be calculated from two info dicts.
+
+  Users can query properties similarly as using a dict() (e.g. info['fstab']),
+  or to query build properties via GetBuildProp() or GetVendorBuildProp().
+
+  Attributes:
+    info_dict: The build-time info dict.
+    is_ab: Whether it's a build that uses A/B OTA.
+    oem_dicts: A list of OEM dicts.
+    oem_props: A list of OEM properties that should be read from OEM dicts; None
+        if the build doesn't use any OEM-specific property.
+    fingerprint: The fingerprint of the build, which would be calculated based
+        on OEM properties if applicable.
+    device: The device name, which could come from OEM dicts if applicable.
+  """
+
+  _RO_PRODUCT_RESOLVE_PROPS = ["ro.product.brand", "ro.product.device",
+                               "ro.product.manufacturer", "ro.product.model",
+                               "ro.product.name"]
+  _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = ["product", "odm", "vendor",
+                                            "system_ext", "system"]
+
+  def __init__(self, info_dict, oem_dicts=None):
+    """Initializes a BuildInfo instance with the given dicts.
+
+    Note that it only wraps up the given dicts, without making copies.
+
+    Arguments:
+      info_dict: The build-time info dict.
+      oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
+          that it always uses the first dict to calculate the fingerprint or the
+          device name. The rest would be used for asserting OEM properties only
+          (e.g. one package can be installed on one of these devices).
+
+    Raises:
+      ValueError: On invalid inputs.
+    """
+    self.info_dict = info_dict
+    self.oem_dicts = oem_dicts
+
+    self._is_ab = info_dict.get("ab_update") == "true"
+    self._oem_props = info_dict.get("oem_fingerprint_properties")
+
+    if self._oem_props:
+      assert oem_dicts, "OEM source required for this build"
+
+    # These two should be computed only after setting self._oem_props.
+    self._device = self.GetOemProperty("ro.product.device")
+    self._fingerprint = self.CalculateFingerprint()
+
+    # Sanity check the build fingerprint.
+    if (' ' in self._fingerprint or
+        any(ord(ch) > 127 for ch in self._fingerprint)):
+      raise ValueError(
+          'Invalid build fingerprint: "{}". See the requirement in Android CDD '
+          '3.2.2. Build Parameters.'.format(self._fingerprint))
+
+  @property
+  def is_ab(self):
+    return self._is_ab
+
+  @property
+  def device(self):
+    return self._device
+
+  @property
+  def fingerprint(self):
+    return self._fingerprint
+
+  @property
+  def vendor_fingerprint(self):
+    return self._fingerprint_of("vendor")
+
+  @property
+  def product_fingerprint(self):
+    return self._fingerprint_of("product")
+
+  @property
+  def odm_fingerprint(self):
+    return self._fingerprint_of("odm")
+
+  def _fingerprint_of(self, partition):
+    if partition + ".build.prop" not in self.info_dict:
+      return None
+    build_prop = self.info_dict[partition + ".build.prop"]
+    if "ro." + partition + ".build.fingerprint" in build_prop:
+      return build_prop["ro." + partition + ".build.fingerprint"]
+    if "ro." + partition + ".build.thumbprint" in build_prop:
+      return build_prop["ro." + partition + ".build.thumbprint"]
+    return None
+
+  @property
+  def oem_props(self):
+    return self._oem_props
+
+  def __getitem__(self, key):
+    return self.info_dict[key]
+
+  def __setitem__(self, key, value):
+    self.info_dict[key] = value
+
+  def get(self, key, default=None):
+    return self.info_dict.get(key, default)
+
+  def items(self):
+    return self.info_dict.items()
+
+  def GetBuildProp(self, prop):
+    """Returns the inquired build property."""
+    if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
+      return self._ResolveRoProductBuildProp(prop)
+
+    try:
+      return self.info_dict.get("build.prop", {})[prop]
+    except KeyError:
+      raise ExternalError("couldn't find %s in build.prop" % (prop,))
+
+  def _ResolveRoProductBuildProp(self, prop):
+    """Resolves the inquired ro.product.* build property"""
+    prop_val = self.info_dict.get("build.prop", {}).get(prop)
+    if prop_val:
+      return prop_val
+
+    source_order_val = self.info_dict.get("build.prop", {}).get(
+        "ro.product.property_source_order")
+    if source_order_val:
+      source_order = source_order_val.split(",")
+    else:
+      source_order = BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
+
+    # Check that all sources in ro.product.property_source_order are valid
+    if any([x not in BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
+            for x in source_order]):
+      raise ExternalError(
+          "Invalid ro.product.property_source_order '{}'".format(source_order))
+
+    for source in source_order:
+      source_prop = prop.replace(
+          "ro.product", "ro.product.{}".format(source), 1)
+      prop_val = self.info_dict.get(
+          "{}.build.prop".format(source), {}).get(source_prop)
+      if prop_val:
+        return prop_val
+
+    raise ExternalError("couldn't resolve {}".format(prop))
+
+  def GetVendorBuildProp(self, prop):
+    """Returns the inquired vendor build property."""
+    try:
+      return self.info_dict.get("vendor.build.prop", {})[prop]
+    except KeyError:
+      raise ExternalError(
+          "couldn't find %s in vendor.build.prop" % (prop,))
+
+  def GetOemProperty(self, key):
+    if self.oem_props is not None and key in self.oem_props:
+      return self.oem_dicts[0][key]
+    return self.GetBuildProp(key)
+
+  def CalculateFingerprint(self):
+    if self.oem_props is None:
+      try:
+        return self.GetBuildProp("ro.build.fingerprint")
+      except ExternalError:
+        return "{}/{}/{}:{}/{}/{}:{}/{}".format(
+            self.GetBuildProp("ro.product.brand"),
+            self.GetBuildProp("ro.product.name"),
+            self.GetBuildProp("ro.product.device"),
+            self.GetBuildProp("ro.build.version.release"),
+            self.GetBuildProp("ro.build.id"),
+            self.GetBuildProp("ro.build.version.incremental"),
+            self.GetBuildProp("ro.build.type"),
+            self.GetBuildProp("ro.build.tags"))
+    return "%s/%s/%s:%s" % (
+        self.GetOemProperty("ro.product.brand"),
+        self.GetOemProperty("ro.product.name"),
+        self.GetOemProperty("ro.product.device"),
+        self.GetBuildProp("ro.build.thumbprint"))
+
+  def WriteMountOemScript(self, script):
+    assert self.oem_props is not None
+    recovery_mount_options = self.info_dict.get("recovery_mount_options")
+    script.Mount("/oem", recovery_mount_options)
+
+  def WriteDeviceAssertions(self, script, oem_no_mount):
+    # Read the property directly if not using OEM properties.
+    if not self.oem_props:
+      script.AssertDevice(self.device)
+      return
+
+    # Otherwise assert OEM properties.
+    if not self.oem_dicts:
+      raise ExternalError(
+          "No OEM file provided to answer expected assertions")
+
+    for prop in self.oem_props.split():
+      values = []
+      for oem_dict in self.oem_dicts:
+        if prop in oem_dict:
+          values.append(oem_dict[prop])
+      if not values:
+        raise ExternalError(
+            "The OEM file is missing the property %s" % (prop,))
+      script.AssertOemProperty(prop, values, oem_no_mount)
+
+
 def LoadInfoDict(input_file, repacking=False):
   """Loads the key/value pairs from the given input target_files.
 
@@ -409,18 +628,11 @@
           read_helper, "{}/etc/build.prop".format(partition.upper()))
   d["build.prop"] = d["system.build.prop"]
 
-  # Set up the salt (based on fingerprint or thumbprint) that will be used when
-  # adding AVB footer.
+  # Set up the salt (based on fingerprint) that will be used when adding AVB
+  # hash / hashtree footers.
   if d.get("avb_enable") == "true":
-    fp = None
-    if "build.prop" in d:
-      build_prop = d["build.prop"]
-      if "ro.build.fingerprint" in build_prop:
-        fp = build_prop["ro.build.fingerprint"]
-      elif "ro.build.thumbprint" in build_prop:
-        fp = build_prop["ro.build.thumbprint"]
-    if fp:
-      d["avb_salt"] = sha256(fp).hexdigest()
+    build_info = BuildInfo(d)
+    d["avb_salt"] = sha256(build_info.fingerprint).hexdigest()
 
   return d
 
@@ -664,8 +876,7 @@
   if key_path:
     chained_partition_arg = GetAvbChainedPartitionArg(partition, info_dict)
     return ["--chain_partition", chained_partition_arg]
-  else:
-    return ["--include_descriptors_from_image", image]
+  return ["--include_descriptors_from_image", image]
 
 
 def GetAvbChainedPartitionArg(partition, info_dict, key=None):
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 3fc3c22..1e7bb3a 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -258,225 +258,6 @@
     'system_ext', 'vbmeta', 'vbmeta_system', 'vbmeta_vendor', 'vendor']
 
 
-class BuildInfo(object):
-  """A class that holds the information for a given build.
-
-  This class wraps up the property querying for a given source or target build.
-  It abstracts away the logic of handling OEM-specific properties, and caches
-  the commonly used properties such as fingerprint.
-
-  There are two types of info dicts: a) build-time info dict, which is generated
-  at build time (i.e. included in a target_files zip); b) OEM info dict that is
-  specified at package generation time (via command line argument
-  '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
-  having "oem_fingerprint_properties" in build-time info dict), all the queries
-  would be answered based on build-time info dict only. Otherwise if using
-  OEM-specific properties, some of them will be calculated from two info dicts.
-
-  Users can query properties similarly as using a dict() (e.g. info['fstab']),
-  or to query build properties via GetBuildProp() or GetVendorBuildProp().
-
-  Attributes:
-    info_dict: The build-time info dict.
-    is_ab: Whether it's a build that uses A/B OTA.
-    oem_dicts: A list of OEM dicts.
-    oem_props: A list of OEM properties that should be read from OEM dicts; None
-        if the build doesn't use any OEM-specific property.
-    fingerprint: The fingerprint of the build, which would be calculated based
-        on OEM properties if applicable.
-    device: The device name, which could come from OEM dicts if applicable.
-  """
-
-  _RO_PRODUCT_RESOLVE_PROPS = ["ro.product.brand", "ro.product.device",
-                               "ro.product.manufacturer", "ro.product.model",
-                               "ro.product.name"]
-  _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = ["product", "odm", "vendor",
-                                            "system_ext", "system"]
-
-  def __init__(self, info_dict, oem_dicts):
-    """Initializes a BuildInfo instance with the given dicts.
-
-    Note that it only wraps up the given dicts, without making copies.
-
-    Arguments:
-      info_dict: The build-time info dict.
-      oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
-          that it always uses the first dict to calculate the fingerprint or the
-          device name. The rest would be used for asserting OEM properties only
-          (e.g. one package can be installed on one of these devices).
-
-    Raises:
-      ValueError: On invalid inputs.
-    """
-    self.info_dict = info_dict
-    self.oem_dicts = oem_dicts
-
-    self._is_ab = info_dict.get("ab_update") == "true"
-    self._oem_props = info_dict.get("oem_fingerprint_properties")
-
-    if self._oem_props:
-      assert oem_dicts, "OEM source required for this build"
-
-    # These two should be computed only after setting self._oem_props.
-    self._device = self.GetOemProperty("ro.product.device")
-    self._fingerprint = self.CalculateFingerprint()
-
-    # Sanity check the build fingerprint.
-    if (' ' in self._fingerprint or
-        any(ord(ch) > 127 for ch in self._fingerprint)):
-      raise ValueError(
-          'Invalid build fingerprint: "{}". See the requirement in Android CDD '
-          '3.2.2. Build Parameters.'.format(self._fingerprint))
-
-  @property
-  def is_ab(self):
-    return self._is_ab
-
-  @property
-  def device(self):
-    return self._device
-
-  @property
-  def fingerprint(self):
-    return self._fingerprint
-
-  @property
-  def vendor_fingerprint(self):
-    return self._fingerprint_of("vendor")
-
-  @property
-  def product_fingerprint(self):
-    return self._fingerprint_of("product")
-
-  @property
-  def odm_fingerprint(self):
-    return self._fingerprint_of("odm")
-
-  def _fingerprint_of(self, partition):
-    if partition + ".build.prop" not in self.info_dict:
-      return None
-    build_prop = self.info_dict[partition + ".build.prop"]
-    if "ro." + partition + ".build.fingerprint" in build_prop:
-      return build_prop["ro." + partition + ".build.fingerprint"]
-    if "ro." + partition + ".build.thumbprint" in build_prop:
-      return build_prop["ro." + partition + ".build.thumbprint"]
-    return None
-
-  @property
-  def oem_props(self):
-    return self._oem_props
-
-  def __getitem__(self, key):
-    return self.info_dict[key]
-
-  def __setitem__(self, key, value):
-    self.info_dict[key] = value
-
-  def get(self, key, default=None):
-    return self.info_dict.get(key, default)
-
-  def items(self):
-    return self.info_dict.items()
-
-  def GetBuildProp(self, prop):
-    """Returns the inquired build property."""
-    if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
-      return self._ResolveRoProductBuildProp(prop)
-
-    try:
-      return self.info_dict.get("build.prop", {})[prop]
-    except KeyError:
-      raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
-
-  def _ResolveRoProductBuildProp(self, prop):
-    """Resolves the inquired ro.product.* build property"""
-    prop_val = self.info_dict.get("build.prop", {}).get(prop)
-    if prop_val:
-      return prop_val
-
-    source_order_val = self.info_dict.get("build.prop", {}).get(
-        "ro.product.property_source_order")
-    if source_order_val:
-      source_order = source_order_val.split(",")
-    else:
-      source_order = BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
-
-    # Check that all sources in ro.product.property_source_order are valid
-    if any([x not in BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
-            for x in source_order]):
-      raise common.ExternalError(
-          "Invalid ro.product.property_source_order '{}'".format(source_order))
-
-    for source in source_order:
-      source_prop = prop.replace(
-          "ro.product", "ro.product.{}".format(source), 1)
-      prop_val = self.info_dict.get(
-          "{}.build.prop".format(source), {}).get(source_prop)
-      if prop_val:
-        return prop_val
-
-    raise common.ExternalError("couldn't resolve {}".format(prop))
-
-  def GetVendorBuildProp(self, prop):
-    """Returns the inquired vendor build property."""
-    try:
-      return self.info_dict.get("vendor.build.prop", {})[prop]
-    except KeyError:
-      raise common.ExternalError(
-          "couldn't find %s in vendor.build.prop" % (prop,))
-
-  def GetOemProperty(self, key):
-    if self.oem_props is not None and key in self.oem_props:
-      return self.oem_dicts[0][key]
-    return self.GetBuildProp(key)
-
-  def CalculateFingerprint(self):
-    if self.oem_props is None:
-      try:
-        return self.GetBuildProp("ro.build.fingerprint")
-      except common.ExternalError:
-        return "{}/{}/{}:{}/{}/{}:{}/{}".format(
-            self.GetBuildProp("ro.product.brand"),
-            self.GetBuildProp("ro.product.name"),
-            self.GetBuildProp("ro.product.device"),
-            self.GetBuildProp("ro.build.version.release"),
-            self.GetBuildProp("ro.build.id"),
-            self.GetBuildProp("ro.build.version.incremental"),
-            self.GetBuildProp("ro.build.type"),
-            self.GetBuildProp("ro.build.tags"))
-    return "%s/%s/%s:%s" % (
-        self.GetOemProperty("ro.product.brand"),
-        self.GetOemProperty("ro.product.name"),
-        self.GetOemProperty("ro.product.device"),
-        self.GetBuildProp("ro.build.thumbprint"))
-
-  def WriteMountOemScript(self, script):
-    assert self.oem_props is not None
-    recovery_mount_options = self.info_dict.get("recovery_mount_options")
-    script.Mount("/oem", recovery_mount_options)
-
-  def WriteDeviceAssertions(self, script, oem_no_mount):
-    # Read the property directly if not using OEM properties.
-    if not self.oem_props:
-      script.AssertDevice(self.device)
-      return
-
-    # Otherwise assert OEM properties.
-    if not self.oem_dicts:
-      raise common.ExternalError(
-          "No OEM file provided to answer expected assertions")
-
-    for prop in self.oem_props.split():
-      values = []
-      for oem_dict in self.oem_dicts:
-        if prop in oem_dict:
-          values.append(oem_dict[prop])
-      if not values:
-        raise common.ExternalError(
-            "The OEM file is missing the property %s" % (prop,))
-      script.AssertOemProperty(prop, values, oem_no_mount)
-
-
 class PayloadSigner(object):
   """A class that wraps the payload signing works.
 
@@ -904,7 +685,7 @@
 
 
 def WriteFullOTAPackage(input_zip, output_file):
-  target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
+  target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
 
   # We don't know what version it will be installed on top of. We expect the API
   # just won't change very often. Similarly for fstab, it might have changed in
@@ -1130,8 +911,8 @@
   Returns:
     A dict to be written into package metadata entry.
   """
-  assert isinstance(target_info, BuildInfo)
-  assert source_info is None or isinstance(source_info, BuildInfo)
+  assert isinstance(target_info, common.BuildInfo)
+  assert source_info is None or isinstance(source_info, common.BuildInfo)
 
   metadata = {
       'post-build' : target_info.fingerprint,
@@ -1544,8 +1325,8 @@
 
 
 def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
-  target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
-  source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
+  target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
+  source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
 
   target_api_version = target_info["recovery_api_version"]
   source_api_version = source_info["recovery_api_version"]
@@ -2024,10 +1805,10 @@
                                compression=zipfile.ZIP_DEFLATED)
 
   if source_file is not None:
-    target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
-    source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
+    target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
+    source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
   else:
-    target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
+    target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
     source_info = None
 
   # Metadata to comply with Android OTA package format.
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index c1e9d7a..59b05e9 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -44,6 +44,210 @@
     yield b'\0' * (step_size - block_size)
 
 
+class BuildInfoTest(test_utils.ReleaseToolsTestCase):
+
+  TEST_INFO_DICT = {
+      'build.prop' : {
+          'ro.product.device' : 'product-device',
+          'ro.product.name' : 'product-name',
+          'ro.build.fingerprint' : 'build-fingerprint',
+          'ro.build.foo' : 'build-foo',
+      },
+      'vendor.build.prop' : {
+          'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
+      },
+      'property1' : 'value1',
+      'property2' : 4096,
+  }
+
+  TEST_INFO_DICT_USES_OEM_PROPS = {
+      'build.prop' : {
+          'ro.product.name' : 'product-name',
+          'ro.build.thumbprint' : 'build-thumbprint',
+          'ro.build.bar' : 'build-bar',
+      },
+      'vendor.build.prop' : {
+          'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
+      },
+      'property1' : 'value1',
+      'property2' : 4096,
+      'oem_fingerprint_properties' : 'ro.product.device ro.product.brand',
+  }
+
+  TEST_OEM_DICTS = [
+      {
+          'ro.product.brand' : 'brand1',
+          'ro.product.device' : 'device1',
+      },
+      {
+          'ro.product.brand' : 'brand2',
+          'ro.product.device' : 'device2',
+      },
+      {
+          'ro.product.brand' : 'brand3',
+          'ro.product.device' : 'device3',
+      },
+  ]
+
+  def test_init(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
+    self.assertEqual('product-device', target_info.device)
+    self.assertEqual('build-fingerprint', target_info.fingerprint)
+    self.assertFalse(target_info.is_ab)
+    self.assertIsNone(target_info.oem_props)
+
+  def test_init_with_oem_props(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
+                                   self.TEST_OEM_DICTS)
+    self.assertEqual('device1', target_info.device)
+    self.assertEqual('brand1/product-name/device1:build-thumbprint',
+                     target_info.fingerprint)
+
+    # Swap the order in oem_dicts, which would lead to different BuildInfo.
+    oem_dicts = copy.copy(self.TEST_OEM_DICTS)
+    oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
+    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
+                                   oem_dicts)
+    self.assertEqual('device3', target_info.device)
+    self.assertEqual('brand3/product-name/device3:build-thumbprint',
+                     target_info.fingerprint)
+
+    # Missing oem_dict should be rejected.
+    self.assertRaises(AssertionError, common.BuildInfo,
+                      self.TEST_INFO_DICT_USES_OEM_PROPS, None)
+
+  def test_init_badFingerprint(self):
+    info_dict = copy.deepcopy(self.TEST_INFO_DICT)
+    info_dict['build.prop']['ro.build.fingerprint'] = 'bad fingerprint'
+    self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
+
+    info_dict['build.prop']['ro.build.fingerprint'] = 'bad\x80fingerprint'
+    self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
+
+  def test___getitem__(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
+    self.assertEqual('value1', target_info['property1'])
+    self.assertEqual(4096, target_info['property2'])
+    self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
+
+  def test___getitem__with_oem_props(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
+                                   self.TEST_OEM_DICTS)
+    self.assertEqual('value1', target_info['property1'])
+    self.assertEqual(4096, target_info['property2'])
+    self.assertRaises(KeyError,
+                      lambda: target_info['build.prop']['ro.build.foo'])
+
+  def test___setitem__(self):
+    target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
+    self.assertEqual('value1', target_info['property1'])
+    target_info['property1'] = 'value2'
+    self.assertEqual('value2', target_info['property1'])
+
+    self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
+    target_info['build.prop']['ro.build.foo'] = 'build-bar'
+    self.assertEqual('build-bar', target_info['build.prop']['ro.build.foo'])
+
+  def test_get(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
+    self.assertEqual('value1', target_info.get('property1'))
+    self.assertEqual(4096, target_info.get('property2'))
+    self.assertEqual(4096, target_info.get('property2', 1024))
+    self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
+    self.assertEqual('build-foo', target_info.get('build.prop')['ro.build.foo'])
+
+  def test_get_with_oem_props(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
+                                   self.TEST_OEM_DICTS)
+    self.assertEqual('value1', target_info.get('property1'))
+    self.assertEqual(4096, target_info.get('property2'))
+    self.assertEqual(4096, target_info.get('property2', 1024))
+    self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
+    self.assertIsNone(target_info.get('build.prop').get('ro.build.foo'))
+    self.assertRaises(KeyError,
+                      lambda: target_info.get('build.prop')['ro.build.foo'])
+
+  def test_items(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
+    items = target_info.items()
+    self.assertIn(('property1', 'value1'), items)
+    self.assertIn(('property2', 4096), items)
+
+  def test_GetBuildProp(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
+    self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
+    self.assertRaises(common.ExternalError, target_info.GetBuildProp,
+                      'ro.build.nonexistent')
+
+  def test_GetBuildProp_with_oem_props(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
+                                   self.TEST_OEM_DICTS)
+    self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
+    self.assertRaises(common.ExternalError, target_info.GetBuildProp,
+                      'ro.build.nonexistent')
+
+  def test_GetVendorBuildProp(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
+    self.assertEqual('vendor-build-fingerprint',
+                     target_info.GetVendorBuildProp(
+                         'ro.vendor.build.fingerprint'))
+    self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
+                      'ro.build.nonexistent')
+
+  def test_GetVendorBuildProp_with_oem_props(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
+                                   self.TEST_OEM_DICTS)
+    self.assertEqual('vendor-build-fingerprint',
+                     target_info.GetVendorBuildProp(
+                         'ro.vendor.build.fingerprint'))
+    self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
+                      'ro.build.nonexistent')
+
+  def test_vendor_fingerprint(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
+    self.assertEqual('vendor-build-fingerprint',
+                     target_info.vendor_fingerprint)
+
+  def test_vendor_fingerprint_blacklisted(self):
+    target_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
+    del target_info_dict['vendor.build.prop']['ro.vendor.build.fingerprint']
+    target_info = common.BuildInfo(target_info_dict, self.TEST_OEM_DICTS)
+    self.assertIsNone(target_info.vendor_fingerprint)
+
+  def test_vendor_fingerprint_without_vendor_build_prop(self):
+    target_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
+    del target_info_dict['vendor.build.prop']
+    target_info = common.BuildInfo(target_info_dict, self.TEST_OEM_DICTS)
+    self.assertIsNone(target_info.vendor_fingerprint)
+
+  def test_WriteMountOemScript(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
+                                   self.TEST_OEM_DICTS)
+    script_writer = test_utils.MockScriptWriter()
+    target_info.WriteMountOemScript(script_writer)
+    self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
+
+  def test_WriteDeviceAssertions(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
+    script_writer = test_utils.MockScriptWriter()
+    target_info.WriteDeviceAssertions(script_writer, False)
+    self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
+
+  def test_WriteDeviceAssertions_with_oem_props(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
+                                   self.TEST_OEM_DICTS)
+    script_writer = test_utils.MockScriptWriter()
+    target_info.WriteDeviceAssertions(script_writer, False)
+    self.assertEqual(
+        [
+            ('AssertOemProperty', 'ro.product.device',
+             ['device1', 'device2', 'device3'], False),
+            ('AssertOemProperty', 'ro.product.brand',
+             ['brand1', 'brand2', 'brand3'], False),
+        ],
+        script_writer.lines)
+
+
 class CommonZipTest(test_utils.ReleaseToolsTestCase):
 
   def _verify(self, zip_file, zip_file_name, arcname, expected_hash,
diff --git a/tools/releasetools/test_ota_from_target_files.py b/tools/releasetools/test_ota_from_target_files.py
index dd42822..c3021a1 100644
--- a/tools/releasetools/test_ota_from_target_files.py
+++ b/tools/releasetools/test_ota_from_target_files.py
@@ -22,7 +22,7 @@
 import common
 import test_utils
 from ota_from_target_files import (
-    _LoadOemDicts, AbOtaPropertyFiles, BuildInfo, FinalizeMetadata,
+    _LoadOemDicts, AbOtaPropertyFiles, FinalizeMetadata,
     GetPackageMetadata, GetTargetFilesZipForSecondaryImages,
     GetTargetFilesZipWithoutPostinstallConfig, NonAbOtaPropertyFiles,
     Payload, PayloadSigner, POSTINSTALL_CONFIG, PropertyFiles,
@@ -74,262 +74,6 @@
   return target_files
 
 
-class BuildInfoTest(test_utils.ReleaseToolsTestCase):
-
-  TEST_INFO_DICT = {
-      'build.prop' : {
-          'ro.product.device' : 'product-device',
-          'ro.product.name' : 'product-name',
-          'ro.build.fingerprint' : 'build-fingerprint',
-          'ro.build.foo' : 'build-foo',
-      },
-      'vendor.build.prop' : {
-          'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
-      },
-      'property1' : 'value1',
-      'property2' : 4096,
-  }
-
-  TEST_INFO_DICT_USES_OEM_PROPS = {
-      'build.prop' : {
-          'ro.product.name' : 'product-name',
-          'ro.build.thumbprint' : 'build-thumbprint',
-          'ro.build.bar' : 'build-bar',
-      },
-      'vendor.build.prop' : {
-          'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
-      },
-      'property1' : 'value1',
-      'property2' : 4096,
-      'oem_fingerprint_properties' : 'ro.product.device ro.product.brand',
-  }
-
-  TEST_OEM_DICTS = [
-      {
-          'ro.product.brand' : 'brand1',
-          'ro.product.device' : 'device1',
-      },
-      {
-          'ro.product.brand' : 'brand2',
-          'ro.product.device' : 'device2',
-      },
-      {
-          'ro.product.brand' : 'brand3',
-          'ro.product.device' : 'device3',
-      },
-  ]
-
-  def test_init(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT, None)
-    self.assertEqual('product-device', target_info.device)
-    self.assertEqual('build-fingerprint', target_info.fingerprint)
-    self.assertFalse(target_info.is_ab)
-    self.assertIsNone(target_info.oem_props)
-
-  def test_init_with_oem_props(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
-                            self.TEST_OEM_DICTS)
-    self.assertEqual('device1', target_info.device)
-    self.assertEqual('brand1/product-name/device1:build-thumbprint',
-                     target_info.fingerprint)
-
-    # Swap the order in oem_dicts, which would lead to different BuildInfo.
-    oem_dicts = copy.copy(self.TEST_OEM_DICTS)
-    oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
-    target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS, oem_dicts)
-    self.assertEqual('device3', target_info.device)
-    self.assertEqual('brand3/product-name/device3:build-thumbprint',
-                     target_info.fingerprint)
-
-    # Missing oem_dict should be rejected.
-    self.assertRaises(AssertionError, BuildInfo,
-                      self.TEST_INFO_DICT_USES_OEM_PROPS, None)
-
-  def test_init_badFingerprint(self):
-    info_dict = copy.deepcopy(self.TEST_INFO_DICT)
-    info_dict['build.prop']['ro.build.fingerprint'] = 'bad fingerprint'
-    self.assertRaises(ValueError, BuildInfo, info_dict, None)
-
-    info_dict['build.prop']['ro.build.fingerprint'] = 'bad\x80fingerprint'
-    self.assertRaises(ValueError, BuildInfo, info_dict, None)
-
-  def test___getitem__(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT, None)
-    self.assertEqual('value1', target_info['property1'])
-    self.assertEqual(4096, target_info['property2'])
-    self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
-
-  def test___getitem__with_oem_props(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
-                            self.TEST_OEM_DICTS)
-    self.assertEqual('value1', target_info['property1'])
-    self.assertEqual(4096, target_info['property2'])
-    self.assertRaises(KeyError,
-                      lambda: target_info['build.prop']['ro.build.foo'])
-
-  def test___setitem__(self):
-    target_info = BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
-    self.assertEqual('value1', target_info['property1'])
-    target_info['property1'] = 'value2'
-    self.assertEqual('value2', target_info['property1'])
-
-    self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
-    target_info['build.prop']['ro.build.foo'] = 'build-bar'
-    self.assertEqual('build-bar', target_info['build.prop']['ro.build.foo'])
-
-  def test_get(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT, None)
-    self.assertEqual('value1', target_info.get('property1'))
-    self.assertEqual(4096, target_info.get('property2'))
-    self.assertEqual(4096, target_info.get('property2', 1024))
-    self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
-    self.assertEqual('build-foo', target_info.get('build.prop')['ro.build.foo'])
-
-  def test_get_with_oem_props(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
-                            self.TEST_OEM_DICTS)
-    self.assertEqual('value1', target_info.get('property1'))
-    self.assertEqual(4096, target_info.get('property2'))
-    self.assertEqual(4096, target_info.get('property2', 1024))
-    self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
-    self.assertIsNone(target_info.get('build.prop').get('ro.build.foo'))
-    self.assertRaises(KeyError,
-                      lambda: target_info.get('build.prop')['ro.build.foo'])
-
-  def test_items(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT, None)
-    items = target_info.items()
-    self.assertIn(('property1', 'value1'), items)
-    self.assertIn(('property2', 4096), items)
-
-  def test_GetBuildProp(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT, None)
-    self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
-    self.assertRaises(common.ExternalError, target_info.GetBuildProp,
-                      'ro.build.nonexistent')
-
-  def test_GetBuildProp_with_oem_props(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
-                            self.TEST_OEM_DICTS)
-    self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
-    self.assertRaises(common.ExternalError, target_info.GetBuildProp,
-                      'ro.build.nonexistent')
-
-  def test_GetVendorBuildProp(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT, None)
-    self.assertEqual('vendor-build-fingerprint',
-                     target_info.GetVendorBuildProp(
-                         'ro.vendor.build.fingerprint'))
-    self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
-                      'ro.build.nonexistent')
-
-  def test_GetVendorBuildProp_with_oem_props(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
-                            self.TEST_OEM_DICTS)
-    self.assertEqual('vendor-build-fingerprint',
-                     target_info.GetVendorBuildProp(
-                         'ro.vendor.build.fingerprint'))
-    self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
-                      'ro.build.nonexistent')
-
-  def test_vendor_fingerprint(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT, None)
-    self.assertEqual('vendor-build-fingerprint',
-                     target_info.vendor_fingerprint)
-
-  def test_vendor_fingerprint_blacklisted(self):
-    target_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
-    del target_info_dict['vendor.build.prop']['ro.vendor.build.fingerprint']
-    target_info = BuildInfo(target_info_dict, self.TEST_OEM_DICTS)
-    self.assertIsNone(target_info.vendor_fingerprint)
-
-  def test_vendor_fingerprint_without_vendor_build_prop(self):
-    target_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
-    del target_info_dict['vendor.build.prop']
-    target_info = BuildInfo(target_info_dict, self.TEST_OEM_DICTS)
-    self.assertIsNone(target_info.vendor_fingerprint)
-
-  def test_WriteMountOemScript(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
-                            self.TEST_OEM_DICTS)
-    script_writer = test_utils.MockScriptWriter()
-    target_info.WriteMountOemScript(script_writer)
-    self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
-
-  def test_WriteDeviceAssertions(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT, None)
-    script_writer = test_utils.MockScriptWriter()
-    target_info.WriteDeviceAssertions(script_writer, False)
-    self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
-
-  def test_WriteDeviceAssertions_with_oem_props(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
-                            self.TEST_OEM_DICTS)
-    script_writer = test_utils.MockScriptWriter()
-    target_info.WriteDeviceAssertions(script_writer, False)
-    self.assertEqual(
-        [
-            ('AssertOemProperty', 'ro.product.device',
-             ['device1', 'device2', 'device3'], False),
-            ('AssertOemProperty', 'ro.product.brand',
-             ['brand1', 'brand2', 'brand3'], False),
-        ],
-        script_writer.lines)
-
-  def test_WriteFingerprintAssertion_without_oem_props(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT, None)
-    source_info_dict = copy.deepcopy(self.TEST_INFO_DICT)
-    source_info_dict['build.prop']['ro.build.fingerprint'] = (
-        'source-build-fingerprint')
-    source_info = BuildInfo(source_info_dict, None)
-
-    script_writer = test_utils.MockScriptWriter()
-    WriteFingerprintAssertion(script_writer, target_info, source_info)
-    self.assertEqual(
-        [('AssertSomeFingerprint', 'source-build-fingerprint',
-          'build-fingerprint')],
-        script_writer.lines)
-
-  def test_WriteFingerprintAssertion_with_source_oem_props(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT, None)
-    source_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
-                            self.TEST_OEM_DICTS)
-
-    script_writer = test_utils.MockScriptWriter()
-    WriteFingerprintAssertion(script_writer, target_info, source_info)
-    self.assertEqual(
-        [('AssertFingerprintOrThumbprint', 'build-fingerprint',
-          'build-thumbprint')],
-        script_writer.lines)
-
-  def test_WriteFingerprintAssertion_with_target_oem_props(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
-                            self.TEST_OEM_DICTS)
-    source_info = BuildInfo(self.TEST_INFO_DICT, None)
-
-    script_writer = test_utils.MockScriptWriter()
-    WriteFingerprintAssertion(script_writer, target_info, source_info)
-    self.assertEqual(
-        [('AssertFingerprintOrThumbprint', 'build-fingerprint',
-          'build-thumbprint')],
-        script_writer.lines)
-
-  def test_WriteFingerprintAssertion_with_both_oem_props(self):
-    target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
-                            self.TEST_OEM_DICTS)
-    source_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
-    source_info_dict['build.prop']['ro.build.thumbprint'] = (
-        'source-build-thumbprint')
-    source_info = BuildInfo(source_info_dict, self.TEST_OEM_DICTS)
-
-    script_writer = test_utils.MockScriptWriter()
-    WriteFingerprintAssertion(script_writer, target_info, source_info)
-    self.assertEqual(
-        [('AssertSomeThumbprint', 'build-thumbprint',
-          'source-build-thumbprint')],
-        script_writer.lines)
-
-
 class LoadOemDictsTest(test_utils.ReleaseToolsTestCase):
 
   def test_NoneDict(self):
@@ -387,6 +131,35 @@
       },
   }
 
+  TEST_INFO_DICT_USES_OEM_PROPS = {
+      'build.prop' : {
+          'ro.product.name' : 'product-name',
+          'ro.build.thumbprint' : 'build-thumbprint',
+          'ro.build.bar' : 'build-bar',
+      },
+      'vendor.build.prop' : {
+          'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
+      },
+      'property1' : 'value1',
+      'property2' : 4096,
+      'oem_fingerprint_properties' : 'ro.product.device ro.product.brand',
+  }
+
+  TEST_OEM_DICTS = [
+      {
+          'ro.product.brand' : 'brand1',
+          'ro.product.device' : 'device1',
+      },
+      {
+          'ro.product.brand' : 'brand2',
+          'ro.product.device' : 'device2',
+      },
+      {
+          'ro.product.brand' : 'brand3',
+          'ro.product.device' : 'device3',
+      },
+  ]
+
   def setUp(self):
     self.testdata_dir = test_utils.get_testdata_dir()
     self.assertTrue(os.path.exists(self.testdata_dir))
@@ -408,7 +181,7 @@
   def test_GetPackageMetadata_abOta_full(self):
     target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
     target_info_dict['ab_update'] = 'true'
-    target_info = BuildInfo(target_info_dict, None)
+    target_info = common.BuildInfo(target_info_dict, None)
     metadata = GetPackageMetadata(target_info)
     self.assertDictEqual(
         {
@@ -426,8 +199,8 @@
   def test_GetPackageMetadata_abOta_incremental(self):
     target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
     target_info_dict['ab_update'] = 'true'
-    target_info = BuildInfo(target_info_dict, None)
-    source_info = BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
+    target_info = common.BuildInfo(target_info_dict, None)
+    source_info = common.BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
     common.OPTIONS.incremental_source = ''
     metadata = GetPackageMetadata(target_info, source_info)
     self.assertDictEqual(
@@ -446,7 +219,7 @@
         metadata)
 
   def test_GetPackageMetadata_nonAbOta_full(self):
-    target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
+    target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
     metadata = GetPackageMetadata(target_info)
     self.assertDictEqual(
         {
@@ -461,8 +234,8 @@
         metadata)
 
   def test_GetPackageMetadata_nonAbOta_incremental(self):
-    target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
-    source_info = BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
+    target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
+    source_info = common.BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
     common.OPTIONS.incremental_source = ''
     metadata = GetPackageMetadata(target_info, source_info)
     self.assertDictEqual(
@@ -480,7 +253,7 @@
         metadata)
 
   def test_GetPackageMetadata_wipe(self):
-    target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
+    target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
     common.OPTIONS.wipe_user_data = True
     metadata = GetPackageMetadata(target_info)
     self.assertDictEqual(
@@ -497,7 +270,7 @@
         metadata)
 
   def test_GetPackageMetadata_retrofitDynamicPartitions(self):
-    target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
+    target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
     common.OPTIONS.retrofit_dynamic_partitions = True
     metadata = GetPackageMetadata(target_info)
     self.assertDictEqual(
@@ -526,8 +299,8 @@
     self._test_GetPackageMetadata_swapBuildTimestamps(
         target_info_dict, source_info_dict)
 
-    target_info = BuildInfo(target_info_dict, None)
-    source_info = BuildInfo(source_info_dict, None)
+    target_info = common.BuildInfo(target_info_dict, None)
+    source_info = common.BuildInfo(source_info_dict, None)
     common.OPTIONS.incremental_source = ''
     self.assertRaises(RuntimeError, GetPackageMetadata, target_info,
                       source_info)
@@ -538,8 +311,8 @@
     self._test_GetPackageMetadata_swapBuildTimestamps(
         target_info_dict, source_info_dict)
 
-    target_info = BuildInfo(target_info_dict, None)
-    source_info = BuildInfo(source_info_dict, None)
+    target_info = common.BuildInfo(target_info_dict, None)
+    source_info = common.BuildInfo(source_info_dict, None)
     common.OPTIONS.incremental_source = ''
     common.OPTIONS.downgrade = True
     common.OPTIONS.wipe_user_data = True
@@ -752,6 +525,59 @@
     FinalizeMetadata(metadata, zip_file, output_file, needed_property_files)
     self.assertIn('ota-test-property-files', metadata)
 
+  def test_WriteFingerprintAssertion_without_oem_props(self):
+    target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
+    source_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
+    source_info_dict['build.prop']['ro.build.fingerprint'] = (
+        'source-build-fingerprint')
+    source_info = common.BuildInfo(source_info_dict, None)
+
+    script_writer = test_utils.MockScriptWriter()
+    WriteFingerprintAssertion(script_writer, target_info, source_info)
+    self.assertEqual(
+        [('AssertSomeFingerprint', 'source-build-fingerprint',
+          'build-fingerprint-target')],
+        script_writer.lines)
+
+  def test_WriteFingerprintAssertion_with_source_oem_props(self):
+    target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
+    source_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
+                                   self.TEST_OEM_DICTS)
+
+    script_writer = test_utils.MockScriptWriter()
+    WriteFingerprintAssertion(script_writer, target_info, source_info)
+    self.assertEqual(
+        [('AssertFingerprintOrThumbprint', 'build-fingerprint-target',
+          'build-thumbprint')],
+        script_writer.lines)
+
+  def test_WriteFingerprintAssertion_with_target_oem_props(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
+                                   self.TEST_OEM_DICTS)
+    source_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
+
+    script_writer = test_utils.MockScriptWriter()
+    WriteFingerprintAssertion(script_writer, target_info, source_info)
+    self.assertEqual(
+        [('AssertFingerprintOrThumbprint', 'build-fingerprint-target',
+          'build-thumbprint')],
+        script_writer.lines)
+
+  def test_WriteFingerprintAssertion_with_both_oem_props(self):
+    target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
+                                   self.TEST_OEM_DICTS)
+    source_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
+    source_info_dict['build.prop']['ro.build.thumbprint'] = (
+        'source-build-thumbprint')
+    source_info = common.BuildInfo(source_info_dict, self.TEST_OEM_DICTS)
+
+    script_writer = test_utils.MockScriptWriter()
+    WriteFingerprintAssertion(script_writer, target_info, source_info)
+    self.assertEqual(
+        [('AssertSomeThumbprint', 'build-thumbprint',
+          'source-build-thumbprint')],
+        script_writer.lines)
+
 
 class TestPropertyFiles(PropertyFiles):
   """A class that extends PropertyFiles for testing purpose."""