Snap for 7901677 from a1d5a41dce57f5aa647d98ec26db92a0ba27df21 to sc-platform-release

Change-Id: I2d071f1d1a2751af9d2be9a07ffa77caafe36a31
diff --git a/common/hal/google_camera_hal/realtime_zsl_request_processor.cc b/common/hal/google_camera_hal/realtime_zsl_request_processor.cc
index 1250496..c5db47d 100644
--- a/common/hal/google_camera_hal/realtime_zsl_request_processor.cc
+++ b/common/hal/google_camera_hal/realtime_zsl_request_processor.cc
@@ -195,8 +195,7 @@
   // checked the size is supported in capture session.
   if (pixel_format_ == android_pixel_format_t::HAL_PIXEL_FORMAT_YCBCR_420_888) {
     for (const auto& stream : stream_config.streams) {
-      if (utils::IsJPEGSnapshotStream(stream) ||
-          utils::IsYUVSnapshotStream(stream)) {
+      if (utils::IsSoftwareDenoiseEligibleSnapshotStream(stream)) {
         if (SelectWidthAndHeight(stream.width, stream.height,
                                  *device_session_hwl_, active_array_width_,
                                  active_array_height_) != OK) {
diff --git a/common/hal/google_camera_hal/zsl_snapshot_capture_session.cc b/common/hal/google_camera_hal/zsl_snapshot_capture_session.cc
index 077a880..fa25403 100644
--- a/common/hal/google_camera_hal/zsl_snapshot_capture_session.cc
+++ b/common/hal/google_camera_hal/zsl_snapshot_capture_session.cc
@@ -163,28 +163,28 @@
     return false;
   }
 
-  bool has_jpeg_stream = false;
+  bool has_eligible_snapshot_stream = false;
   bool has_preview_stream = false;
-  bool has_yuv_stream = false;
   for (const auto& stream : stream_config.streams) {
     if (stream.is_physical_camera_stream) {
       ALOGE("%s: support logical camera only", __FUNCTION__);
       return false;
     }
-    if (utils::IsJPEGSnapshotStream(stream)) {
-      has_jpeg_stream = true;
+    if (utils::IsJPEGSnapshotStream(stream) ||
+        utils::IsYUVSnapshotStream(stream)) {
+      if (utils::IsSoftwareDenoiseEligibleSnapshotStream(stream)) {
+        has_eligible_snapshot_stream = true;
+      }
     } else if (utils::IsPreviewStream(stream)) {
       has_preview_stream = true;
-    } else if (utils::IsYUVSnapshotStream(stream)) {
-      has_yuv_stream = true;
     } else {
       ALOGE("%s: only support preview + (snapshot and/or YUV) streams",
             __FUNCTION__);
       return false;
     }
   }
-  if (!has_jpeg_stream && !has_yuv_stream) {
-    ALOGE("%s: no JPEG or YUV stream", __FUNCTION__);
+  if (!has_eligible_snapshot_stream) {
+    ALOGE("%s: no eligible JPEG or YUV stream", __FUNCTION__);
     return false;
   }
 
@@ -231,12 +231,16 @@
 }
 
 ZslSnapshotCaptureSession::~ZslSnapshotCaptureSession() {
+  auto release_thread = std::thread([this]() {
+    ATRACE_NAME("Release snapshot request processor");
+    snapshot_request_processor_ = nullptr;
+  });
   if (camera_device_session_hwl_ != nullptr) {
     camera_device_session_hwl_->DestroyPipelines();
   }
   // Need to explicitly release SnapshotProcessBlock by releasing
   // SnapshotRequestProcessor before the lib handle is released.
-  snapshot_request_processor_ = nullptr;
+  release_thread.join();
   dlclose(snapshot_process_block_lib_handle_);
 }
 
@@ -609,8 +613,8 @@
   if (res == OK) {
     partial_result_count_ = partial_result_entry.data.i32[0];
   }
-  result_dispatcher_ = ResultDispatcher::Create(partial_result_count_,
-                                                process_capture_result, notify);
+  result_dispatcher_ = ZslResultDispatcher::Create(
+      partial_result_count_, process_capture_result, notify);
   if (result_dispatcher_ == nullptr) {
     ALOGE("%s: Cannot create result dispatcher.", __FUNCTION__);
     return UNKNOWN_ERROR;
@@ -677,7 +681,15 @@
 
 status_t ZslSnapshotCaptureSession::ProcessRequest(const CaptureRequest& request) {
   ATRACE_CALL();
-  status_t res = result_dispatcher_->AddPendingRequest(request);
+  bool is_zsl_request = false;
+  camera_metadata_ro_entry entry;
+  if (request.settings != nullptr) {
+    if (request.settings->Get(ANDROID_CONTROL_ENABLE_ZSL, &entry) == OK &&
+        *entry.data.u8 == ANDROID_CONTROL_ENABLE_ZSL_TRUE) {
+      is_zsl_request = true;
+    }
+  }
+  status_t res = result_dispatcher_->AddPendingRequest(request, is_zsl_request);
   if (res != OK) {
     ALOGE("%s: frame(%d) fail to AddPendingRequest", __FUNCTION__,
           request.frame_number);
diff --git a/common/hal/google_camera_hal/zsl_snapshot_capture_session.h b/common/hal/google_camera_hal/zsl_snapshot_capture_session.h
index dab4515..74b3cc3 100644
--- a/common/hal/google_camera_hal/zsl_snapshot_capture_session.h
+++ b/common/hal/google_camera_hal/zsl_snapshot_capture_session.h
@@ -27,10 +27,10 @@
 #include "realtime_zsl_request_processor.h"
 #include "realtime_zsl_result_processor.h"
 #include "request_processor.h"
-#include "result_dispatcher.h"
 #include "result_processor.h"
 #include "snapshot_request_processor.h"
 #include "snapshot_result_processor.h"
+#include "zsl_result_dispatcher.h"
 
 namespace android {
 namespace google_camera_hal {
@@ -157,7 +157,7 @@
 
   int32_t additional_stream_id_ = -1;
 
-  std::unique_ptr<ResultDispatcher> result_dispatcher_;
+  std::unique_ptr<ZslResultDispatcher> result_dispatcher_;
 
   std::mutex callback_lock_;
   // The following callbacks must be protected by callback_lock_.
diff --git a/common/hal/hidl_service/Android.bp b/common/hal/hidl_service/Android.bp
index 29f7203..f10d6ef 100644
--- a/common/hal/hidl_service/Android.bp
+++ b/common/hal/hidl_service/Android.bp
@@ -46,21 +46,22 @@
     },
 }
 
-sh_binary_host {
+// Exported for use in vendor/google/services/LyricCameraHAL/src/
+python_binary_host {
     name: "camera_hal_version_script",
-    src: "version_script.sh",
-    filename_from_src: true,
+    main: "version_script.py",
+    srcs: ["version_script.py"],
 }
 
 cc_genrule {
-    name: "hidl_camera_build_version",
-    tools: [":camera_hal_version_script"],
-    cmd: "$(location :camera_hal_version_script) $(in) $(out)",
-    vendor: true,
-    srcs: [
-        "hidl_camera_build_version.inl",
-    ],
-    out: ["hidl_camera_build_version.h"],
+   name: "hidl_camera_build_version",
+   tool_files: ["version_script.py"],
+   cmd: "python3 $(location version_script.py) $(in) $(out)",
+   vendor: true,
+   srcs: [
+       "hidl_camera_build_version.inl",
+   ],
+   out: ["hidl_camera_build_version.h"],
 }
 
 cc_defaults {
diff --git a/common/hal/hidl_service/version_script.py b/common/hal/hidl_service/version_script.py
new file mode 100644
index 0000000..a5bb167
--- /dev/null
+++ b/common/hal/hidl_service/version_script.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python3
+#
+# See: go/camera-sideline#versions for more context
+#
+# This script generates the apex manifest version number (which is also used for
+# the outer aab/jar object's version available to PackageManager).
+#
+# That version is limited to INT_MAX
+# Strategy:
+#   if(local eng build)
+#      version = 2147480000
+#   else
+#      version = brach specific code + padding + numeric part of build number
+#
+# 2147480000 is chosen as being a value that can install over any expected build
+# server build number that is still a little smaller than INT_MAX to leave room
+# for maneuvering
+
+import os
+import re
+import sys
+
+BRANCH_SPECIFIC_VERSION_IDENTIFIER = 2  # sc-qpr1-dev
+DEFAULT_ENG_BUILD_NUMBER = 2147480000
+DEFAULT_BAD_BUILD_NUMBER = DEFAULT_ENG_BUILD_NUMBER - 1
+
+
+def tame_box(numeric_build_number):
+  # Box the regex extracted value to a min and a max, just in case. Should not
+  # be needed ever.
+  if numeric_build_number < 1:
+    numeric_build_number = 1
+  if numeric_build_number > DEFAULT_ENG_BUILD_NUMBER:
+    numeric_build_number = DEFAULT_BAD_BUILD_NUMBER
+  return numeric_build_number
+
+
+def get_version(input_path, output_path):
+
+  soong_build_number = DEFAULT_BAD_BUILD_NUMBER
+  numeric_build_number = DEFAULT_BAD_BUILD_NUMBER
+
+  # Extract the Android Build ID from the build products
+  out_dir = os.getenv('OUT_DIR')
+  with open('%s/soong/build_number.txt' % out_dir, 'r') as file:
+    soong_build_number = file.read().replace('\n', '')
+
+  # Eng builds all have a default very high version number to permit for local
+  # builds to ovewrite whatever else is installed on the OS.
+  if soong_build_number.startswith('eng.'):  # eng.bills.20210901.235403
+    numeric_build_number = DEFAULT_ENG_BUILD_NUMBER
+  else:
+    # We only want the numeric part of the Android Build ID
+    match = re.search(r'([0-9]+)', soong_build_number)
+    if match:
+      numeric_build_number = int(match.group(0))
+    else:
+      numeric_build_number = DEFAULT_BAD_BUILD_NUMBER
+
+  # Tame the inputs into a reasonable box, just in case
+  numeric_build_number = tame_box(numeric_build_number)
+  # print('numeric_build_number: %s' % str(numeric_build_number))
+
+  # With the numeric build number as a starting point, let's augment it with
+  # the BRANCH_SPECIFIC_VERSION_IDENTIFIER to differentiate build products from
+  # this branch according to: go/camera-sideline#versions
+  branched_build_number = numeric_build_number
+  if numeric_build_number == DEFAULT_ENG_BUILD_NUMBER:
+    # High default eng numbers can't really be multiplied so we add our branch
+    # specific number instead
+    branched_build_number = numeric_build_number + BRANCH_SPECIFIC_VERSION_IDENTIFIER
+  else:
+    # Two cases to consider:
+    #   1. A "regular" Android Build ID like: 7699287
+    #   2. A pending Android Build ID like: P25748464
+
+    # "It's just string concatenation"
+    string_build_number = '%s%s' % (BRANCH_SPECIFIC_VERSION_IDENTIFIER,
+                                    str(numeric_build_number).zfill(8))
+    # Ints in python3 are long
+    branched_build_number = int(string_build_number)
+    # Tame the result into a reasonable box, just in case
+    branched_build_number = tame_box(branched_build_number)
+
+  # print('soong_build_number: %s' % soong_build_number)
+  # print('branched_build_number: %s' % str(branched_build_number))
+
+  # Bash version:
+  # cat $1 | \
+  # sed -E "s/\{BUILD_NUMBER\}/$numeric_build_number/g" | \
+  # sed -E "s/\{BUILD_ID\}/$soong_build_number/g" > $2
+  try:
+    if os.path.exists(input_path):
+      input_fh = open(input_path, 'r')
+      contents = input_fh.readlines()
+      # print('Read: %s' % input_path)
+      new_contents = []
+      input_fh.close()
+      for line in contents:
+        line = line.replace('{BUILD_ID}', soong_build_number)
+        line = line.replace('{BUILD_NUMBER}', str(branched_build_number))
+        new_contents.append(line)
+      # print(new_contents)
+      output_fh = open(output_path, 'w')
+      output_fh.write(''.join(new_contents))
+      output_fh.close()
+      # print('Wrote: %s' % output_path)
+  except IOError:
+    print(f'error occurred trying to read the file {input_path}')
+    return 1
+
+  return 0
+
+
+def main():
+  return get_version(*sys.argv[1:])
+
+
+if __name__ == '__main__':
+  sys.exit(main())
+
diff --git a/common/hal/hidl_service/version_script.sh b/common/hal/hidl_service/version_script.sh
deleted file mode 100755
index 2ab4e2b..0000000
--- a/common/hal/hidl_service/version_script.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-#
-# This script generates the apex manifest version number (which is also used for
-# the outer aab/jar object's version available to PackageManager).
-#
-# That version is limited to INT_MAX
-# Strategy:
-#   if(local eng build)
-#      version = 2147480000
-#   else
-#      version = numeric part of build number
-#
-# 2147480000 is chosen as being a value that can install over any expected build
-# server build number that is still a little smaller than INT_MAX to leave room
-# for maneuvering
-
-default_eng_build_number=2147480000
-
-build_number=$(cat $OUT_DIR/soong/build_number.txt)
-if [[ "$build_number" == "eng."* ]]; then
-  numeric_build_number=$default_eng_build_number
-else
-  numeric_build_number=$(cat $OUT_DIR/soong/build_number.txt | tr -d -c 0-9)
-  if [[ -z "$numeric_build_number" ]]; then
-    numeric_build_number=$default_eng_build_number
-  fi
-  if ((numeric_build_number < 1)); then
-    numeric_build_number=1
-  fi
-  if ((numeric_build_number >= default_eng_build_number)); then
-    numeric_build_number=$((default_eng_build_number-1))
-  fi
-fi
-
-cat $1 | sed -E "s/\{BUILD_NUMBER\}/$numeric_build_number/g" | sed -E "s/\{BUILD_ID\}/$build_number/g" > $2
-
diff --git a/common/hal/utils/Android.bp b/common/hal/utils/Android.bp
index 6b2dc58..635a798 100644
--- a/common/hal/utils/Android.bp
+++ b/common/hal/utils/Android.bp
@@ -47,6 +47,7 @@
         "vendor_tag_utils.cc",
         "zoom_ratio_mapper.cc",
         "zsl_buffer_manager.cc",
+        "zsl_result_dispatcher.cc",
     ],
     shared_libs: [
         "lib_profiler",
diff --git a/common/hal/utils/result_dispatcher.cc b/common/hal/utils/result_dispatcher.cc
index 6443e8f..6c8b2de 100644
--- a/common/hal/utils/result_dispatcher.cc
+++ b/common/hal/utils/result_dispatcher.cc
@@ -273,11 +273,13 @@
   uint32_t frame_number = error.frame_number;
   // No need to deliver the shutter message on an error
   if (error.error_code == ErrorCode::kErrorDevice ||
-      error.error_code == ErrorCode::kErrorResult) {
+      error.error_code == ErrorCode::kErrorResult ||
+      error.error_code == ErrorCode::kErrorRequest) {
     pending_shutters_.erase(frame_number);
   }
   // No need to deliver the result metadata on a result metadata error
-  if (error.error_code == ErrorCode::kErrorResult) {
+  if (error.error_code == ErrorCode::kErrorResult ||
+      error.error_code == ErrorCode::kErrorRequest) {
     pending_final_metadata_.erase(frame_number);
   }
 
diff --git a/common/hal/utils/result_dispatcher.h b/common/hal/utils/result_dispatcher.h
index 74f4b4a..5acc90a 100644
--- a/common/hal/utils/result_dispatcher.h
+++ b/common/hal/utils/result_dispatcher.h
@@ -65,7 +65,6 @@
   // Remove a pending request.
   void RemovePendingRequest(uint32_t frame_number);
 
- protected:
   ResultDispatcher(uint32_t partial_result_count,
                    ProcessCaptureResultFunc process_capture_result,
                    NotifyFunc notify);
diff --git a/common/hal/utils/utils.cc b/common/hal/utils/utils.cc
index 54b382a..7a41889 100644
--- a/common/hal/utils/utils.cc
+++ b/common/hal/utils/utils.cc
@@ -15,16 +15,16 @@
  */
 
 //#define LOG_NDEBUG 0
+#include <cstdint>
 #define LOG_TAG "GCH_Utils"
 
-#include "utils.h"
-
 #include <cutils/properties.h>
 #include <dirent.h>
 #include <dlfcn.h>
 #include <hardware/gralloc.h>
 #include <sys/stat.h>
 
+#include "utils.h"
 #include "vendor_tag_defs.h"
 
 namespace android {
@@ -34,6 +34,8 @@
 constexpr char kRealtimeThreadSetProp[] =
     "persist.vendor.camera.realtimethread";
 
+constexpr uint32_t kMinSupportedSoftwareDenoiseDimension = 1000;
+
 bool IsDepthStream(const Stream& stream) {
   if (stream.stream_type == StreamType::kOutput &&
       stream.data_space == HAL_DATASPACE_DEPTH &&
@@ -121,6 +123,15 @@
   return false;
 }
 
+bool IsSoftwareDenoiseEligibleSnapshotStream(const Stream& stream) {
+  if (utils::IsYUVSnapshotStream(stream) ||
+      utils::IsJPEGSnapshotStream(stream)) {
+    return stream.width >= kMinSupportedSoftwareDenoiseDimension ||
+           stream.height >= kMinSupportedSoftwareDenoiseDimension;
+  }
+  return false;
+}
+
 status_t GetSensorPhysicalSize(const HalCameraMetadata* characteristics,
                                float* width, float* height) {
   if (characteristics == nullptr || width == nullptr || height == nullptr) {
diff --git a/common/hal/utils/utils.h b/common/hal/utils/utils.h
index 4412908..a43e614 100644
--- a/common/hal/utils/utils.h
+++ b/common/hal/utils/utils.h
@@ -36,6 +36,7 @@
 bool IsYUVSnapshotStream(const Stream& stream);
 bool IsDepthStream(const Stream& stream);
 bool IsOutputZslStream(const Stream& stream);
+bool IsSoftwareDenoiseEligibleSnapshotStream(const Stream& stream);
 
 bool HasCapability(const HalCameraMetadata* metadata, uint8_t capability);
 
diff --git a/common/hal/utils/zsl_result_dispatcher.cc b/common/hal/utils/zsl_result_dispatcher.cc
new file mode 100644
index 0000000..3b5baa4
--- /dev/null
+++ b/common/hal/utils/zsl_result_dispatcher.cc
@@ -0,0 +1,181 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "GCH_ZslResultDispatcher"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <inttypes.h>
+
+#include "utils.h"
+#include "zsl_result_dispatcher.h"
+
+namespace android {
+namespace google_camera_hal {
+
+std::unique_ptr<ZslResultDispatcher> ZslResultDispatcher::Create(
+    uint32_t partial_result_count,
+    ProcessCaptureResultFunc process_capture_result, NotifyFunc notify) {
+  ATRACE_CALL();
+  auto dispatcher = std::unique_ptr<ZslResultDispatcher>(
+      new ZslResultDispatcher(process_capture_result, notify));
+  if (dispatcher == nullptr) {
+    ALOGE("%s: Creating ZslResultDispatcher failed.", __FUNCTION__);
+    return nullptr;
+  }
+
+  status_t res = dispatcher->Initialize(partial_result_count);
+  if (res != OK) {
+    ALOGE("%s: Initialize failed.", __FUNCTION__);
+    return nullptr;
+  }
+
+  return dispatcher;
+}
+
+ZslResultDispatcher::ZslResultDispatcher(
+    ProcessCaptureResultFunc process_capture_result, NotifyFunc notify)
+    : device_session_process_capture_result_(process_capture_result),
+      device_session_notify_(notify) {
+}
+
+status_t ZslResultDispatcher::Initialize(uint32_t partial_result_count) {
+  ATRACE_CALL();
+  process_capture_result_ =
+      ProcessCaptureResultFunc([this](std::unique_ptr<CaptureResult> result) {
+        ProcessCaptureResult(std::move(result));
+      });
+  notify_ = NotifyFunc(
+      [this](const NotifyMessage& message) { NotifyHalMessage(message); });
+
+  normal_result_dispatcher_ =
+      std::unique_ptr<ResultDispatcher>(new ResultDispatcher(
+          partial_result_count, process_capture_result_, notify_));
+  if (normal_result_dispatcher_ == nullptr) {
+    ALOGE("%s: Creating normal_result_dispatcher_ failed.", __FUNCTION__);
+    return BAD_VALUE;
+  }
+
+  zsl_result_dispatcher_ =
+      std::unique_ptr<ResultDispatcher>(new ResultDispatcher(
+          partial_result_count, process_capture_result_, notify_));
+  if (zsl_result_dispatcher_ == nullptr) {
+    ALOGE("%s: Creating zsl_result_dispatcher_ failed.", __FUNCTION__);
+    return BAD_VALUE;
+  }
+
+  return OK;
+}
+
+void ZslResultDispatcher::ProcessCaptureResult(
+    std::unique_ptr<CaptureResult> result) {
+  std::lock_guard<std::mutex> lock(process_capture_result_lock_);
+  device_session_process_capture_result_(std::move(result));
+}
+
+bool ZslResultDispatcher::IsZslFrame(uint32_t frame_number) {
+  std::lock_guard<std::mutex> lock(zsl_frames_lock_);
+  if (zsl_frames_.empty()) {
+    return false;
+  }
+  if (std::find(zsl_frames_.begin(), zsl_frames_.end(), frame_number) ==
+      zsl_frames_.end()) {
+    return false;
+  }
+  return true;
+}
+
+void ZslResultDispatcher::NotifyHalMessage(const NotifyMessage& message) {
+  std::lock_guard<std::mutex> lock(result_lock_);
+  device_session_notify_(message);
+}
+
+status_t ZslResultDispatcher::AddPendingRequest(
+    const CaptureRequest& pending_request, bool is_zsl_request) {
+  ATRACE_CALL();
+  if (is_zsl_request) {
+    uint32_t frame_number = pending_request.frame_number;
+    {
+      std::lock_guard<std::mutex> lock(zsl_frames_lock_);
+      zsl_frames_.push_back(frame_number);
+    }
+
+    status_t res = zsl_result_dispatcher_->AddPendingRequest(pending_request);
+    if (res != OK) {
+      std::lock_guard<std::mutex> lock(zsl_frames_lock_);
+      zsl_frames_.erase(
+          std::find(zsl_frames_.begin(), zsl_frames_.end(), frame_number));
+    }
+    return res;
+  } else {
+    return normal_result_dispatcher_->AddPendingRequest(pending_request);
+  }
+}
+
+status_t ZslResultDispatcher::AddResult(std::unique_ptr<CaptureResult> result) {
+  ATRACE_CALL();
+  if (result == nullptr) {
+    ALOGE("%s: result is nullptr", __FUNCTION__);
+    return BAD_VALUE;
+  }
+  uint32_t frame_number = result->frame_number;
+  bool is_zsl_request = IsZslFrame(frame_number);
+  if (is_zsl_request) {
+    return zsl_result_dispatcher_->AddResult(std::move(result));
+  } else {
+    return normal_result_dispatcher_->AddResult(std::move(result));
+  }
+}
+
+status_t ZslResultDispatcher::AddShutter(uint32_t frame_number,
+                                         int64_t timestamp_ns) {
+  ATRACE_CALL();
+  bool is_zsl_request = IsZslFrame(frame_number);
+  if (is_zsl_request) {
+    return zsl_result_dispatcher_->AddShutter(frame_number, timestamp_ns);
+  } else {
+    return normal_result_dispatcher_->AddShutter(frame_number, timestamp_ns);
+  }
+}
+
+status_t ZslResultDispatcher::AddError(const ErrorMessage& error) {
+  ATRACE_CALL();
+  uint32_t frame_number = error.frame_number;
+  bool is_zsl_request = IsZslFrame(frame_number);
+  if (is_zsl_request) {
+    return zsl_result_dispatcher_->AddError(error);
+  } else {
+    return normal_result_dispatcher_->AddError(error);
+  }
+}
+
+void ZslResultDispatcher::RemovePendingRequest(uint32_t frame_number) {
+  ATRACE_CALL();
+  bool is_zsl_request = IsZslFrame(frame_number);
+  if (is_zsl_request) {
+    zsl_result_dispatcher_->RemovePendingRequest(frame_number);
+    std::lock_guard<std::mutex> lock(zsl_frames_lock_);
+    zsl_frames_.erase(
+        std::find(zsl_frames_.begin(), zsl_frames_.end(), frame_number));
+  } else {
+    normal_result_dispatcher_->RemovePendingRequest(frame_number);
+  }
+}
+
+}  // namespace google_camera_hal
+}  // namespace android
diff --git a/common/hal/utils/zsl_result_dispatcher.h b/common/hal/utils/zsl_result_dispatcher.h
new file mode 100644
index 0000000..6297b6b
--- /dev/null
+++ b/common/hal/utils/zsl_result_dispatcher.h
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#ifndef HARDWARE_GOOGLE_CAMERA_HAL_UTILS_ZSL_RESULT_DISPATCHER_H_
+#define HARDWARE_GOOGLE_CAMERA_HAL_UTILS_ZSL_RESULT_DISPATCHER_H_
+
+#include <map>
+#include <thread>
+
+#include "hal_types.h"
+#include "result_dispatcher.h"
+
+namespace android {
+namespace google_camera_hal {
+
+// ZslResultDispatcher dispatches capture results of zsl requests and none-zsl
+// requests in the order of frame numbers, including result metadata, shutters,
+// and stream buffers.
+//
+// The client can add results and shutters via AddResult() and AddShutter() in
+// any order. ZslResultDispatcher will invoke ProcessCaptureResultFunc and
+// NotifyFunc to notify result metadata, shutters, and stream buffers in the
+// in the order of increasing frame numbers.
+class ZslResultDispatcher {
+ public:
+  // Create a ZslResultDispatcher.
+  // partial_result_count is the partial result count.
+  // process_capture_result is the function to notify capture results.
+  // notify is the function to notify shutter messages.
+  // Treat ZSL requests and normal requests separately.
+  // For ZSL requests, it returns zsl shutter and zsl results in order
+  // and is not blocked by normal shutter and results.
+  static std::unique_ptr<ZslResultDispatcher> Create(
+      uint32_t partial_result_count,
+      ProcessCaptureResultFunc process_capture_result, NotifyFunc notify);
+
+  virtual ~ZslResultDispatcher() = default;
+
+  // Add a pending request. This tells ZslResultDispatcher to watch for
+  // the shutter, result metadata, and stream buffers for this request,
+  // that will be added later via AddResult() and AddShutter().
+  // Treat the request as zsl request if is_zsl_request is true
+  status_t AddPendingRequest(const CaptureRequest& pending_request,
+                             bool is_zsl_request);
+
+  // Add a ready result. If the result doesn't belong to a pending request that
+  // was previously added via AddPendingRequest(), an error will be returned.
+  status_t AddResult(std::unique_ptr<CaptureResult> result);
+
+  // Add a shutter for a frame number. If the frame number doesn't belong to a
+  // pending request that was previously added via AddPendingRequest(), an error
+  // will be returned.
+  status_t AddShutter(uint32_t frame_number, int64_t timestamp_ns);
+
+  // Add an error notification for a frame number. When this is called, we no
+  // longer wait for a shutter message or result metadata for the given frame.
+  status_t AddError(const ErrorMessage& error);
+
+  // Remove a pending request.
+  void RemovePendingRequest(uint32_t frame_number);
+
+ protected:
+  ZslResultDispatcher(ProcessCaptureResultFunc process_capture_result,
+                      NotifyFunc notify);
+
+ private:
+  status_t Initialize(uint32_t partial_result_count);
+
+  // Invoked when receiving a result from ResultDispatcher class.
+  void ProcessCaptureResult(std::unique_ptr<CaptureResult> result);
+
+  // Invoked when receiving a message from ResultDispatcher.
+  void NotifyHalMessage(const NotifyMessage& message);
+
+  // Return true if this frame is zsl request.
+  bool IsZslFrame(uint32_t frame_number);
+
+  std::unique_ptr<ResultDispatcher> normal_result_dispatcher_;
+  std::unique_ptr<ResultDispatcher> zsl_result_dispatcher_;
+
+  std::mutex process_capture_result_lock_;
+  // The following callbacks must be protected by process_capture_result_lock_.
+  // Pass this callback function to ResultDispatcher class
+  ProcessCaptureResultFunc process_capture_result_;
+
+  std::mutex result_lock_;
+  // The following callbacks must be protected by result_lock_.
+  // Pass this callback function to ResultDispatcher class
+  NotifyFunc notify_;
+
+  // Record the callback function for framework callback
+  ProcessCaptureResultFunc device_session_process_capture_result_;
+  NotifyFunc device_session_notify_;
+
+  std::mutex zsl_frames_lock_;
+  // Store the frame number of zsl requests
+  // Protected by zsl_frames_lock_.
+  std::vector<uint32_t> zsl_frames_;
+};
+
+}  // namespace google_camera_hal
+}  // namespace android
+
+#endif  // HARDWARE_GOOGLE_CAMERA_HAL_UTILS_ZSL_RESULT_DISPATCHER_H_
diff --git a/devices/EmulatedCamera/hwl/EmulatedRequestState.cpp b/devices/EmulatedCamera/hwl/EmulatedRequestState.cpp
index a1e553a..e7c62f9 100644
--- a/devices/EmulatedCamera/hwl/EmulatedRequestState.cpp
+++ b/devices/EmulatedCamera/hwl/EmulatedRequestState.cpp
@@ -969,8 +969,11 @@
   }
   if (zoom_ratio_supported_) {
     result->result_metadata->Set(ANDROID_CONTROL_ZOOM_RATIO, &zoom_ratio_, 1);
-    result->result_metadata->Set(ANDROID_SCALER_CROP_REGION,
-                                 scaler_crop_region_default_,
+    int32_t* chosen_crop_region = scaler_crop_region_default_;
+    if (sensor_pixel_mode_ == ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) {
+      chosen_crop_region = scaler_crop_region_max_resolution_;
+    }
+    result->result_metadata->Set(ANDROID_SCALER_CROP_REGION, chosen_crop_region,
                                  ARRAY_SIZE(scaler_crop_region_default_));
   }
   if (report_extended_scene_mode_) {
@@ -2285,6 +2288,23 @@
       return BAD_VALUE;
     }
 
+    if (SupportsCapability(
+            ANDROID_REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR)) {
+      ret = static_metadata_->Get(
+          ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION, &entry);
+      if ((ret == OK) && (entry.count == 4)) {
+        scaler_crop_region_max_resolution_[0] = entry.data.i32[0];
+        scaler_crop_region_max_resolution_[1] = entry.data.i32[1];
+        scaler_crop_region_max_resolution_[2] = entry.data.i32[2];
+        scaler_crop_region_max_resolution_[3] = entry.data.i32[3];
+      } else {
+        ALOGE(
+            "%s: Sensor pixel array size maximum resolution is not available!",
+            __FUNCTION__);
+        return BAD_VALUE;
+      }
+    }
+
     if (available_requests_.find(ANDROID_SCALER_CROP_REGION) ==
         available_requests_.end()) {
       ALOGE(
diff --git a/devices/EmulatedCamera/hwl/EmulatedRequestState.h b/devices/EmulatedCamera/hwl/EmulatedRequestState.h
index 1591422..1987bfa 100644
--- a/devices/EmulatedCamera/hwl/EmulatedRequestState.h
+++ b/devices/EmulatedCamera/hwl/EmulatedRequestState.h
@@ -262,6 +262,7 @@
   bool report_rotate_and_crop_ = false;
   uint8_t rotate_and_crop_ = ANDROID_SCALER_ROTATE_AND_CROP_NONE;
   int32_t scaler_crop_region_default_[4] = {0, 0, 0, 0};
+  int32_t scaler_crop_region_max_resolution_[4] = {0, 0, 0, 0};
   std::set<uint8_t> available_rotate_crop_modes_;
 
   // android.statistics.*