Merge "sdm: Fix topology after adding new mode"
diff --git a/libdrmutils/drm_interface.h b/libdrmutils/drm_interface.h
index 1727818..d64d904 100644
--- a/libdrmutils/drm_interface.h
+++ b/libdrmutils/drm_interface.h
@@ -287,6 +287,12 @@
    */
   CONNECTOR_SET_ROI,
   /*
+   * Op: Sets the connector to autorefresh mode.
+   * Arg: uint32_t - Connector ID
+   *      uint32_t - Enable-1, Disable-0
+   */
+  CONNECTOR_SET_AUTOREFRESH,
+  /*
    * Op: Set FB secure mode for Writeback connector.
    * Arg: uint32_t - Connector ID
    *      uint32_t - FB Secure mode
diff --git a/liblight/lights.c b/liblight/lights.c
index 15db827..115b98c 100644
--- a/liblight/lights.c
+++ b/liblight/lights.c
@@ -48,6 +48,7 @@
 static int g_last_backlight_mode = BRIGHTNESS_MODE_USER;
 static int g_attention = 0;
 static int g_brightness_max = 0;
+static bool g_has_persistence_node = false;
 
 char const*const RED_LED_FILE
         = "/sys/class/leds/red/brightness";
@@ -139,20 +140,23 @@
 
     pthread_mutex_lock(&g_lock);
     // Toggle low persistence mode state
-    if ((g_last_backlight_mode != state->brightnessMode && lpEnabled) ||
-        (!lpEnabled &&
-         g_last_backlight_mode == BRIGHTNESS_MODE_LOW_PERSISTENCE)) {
-        if ((err = write_int(PERSISTENCE_FILE, lpEnabled)) != 0) {
-            ALOGE("%s: Failed to write to %s: %s\n", __FUNCTION__,
-                   PERSISTENCE_FILE, strerror(errno));
+    bool persistence_mode = ((g_last_backlight_mode != state->brightnessMode && lpEnabled) ||
+                            (!lpEnabled &&
+                            g_last_backlight_mode == BRIGHTNESS_MODE_LOW_PERSISTENCE));
+    bool cannot_handle_persistence = !g_has_persistence_node && persistence_mode;
+    if (g_has_persistence_node) {
+        if (persistence_mode) {
+            if ((err = write_int(PERSISTENCE_FILE, lpEnabled)) != 0) {
+                ALOGE("%s: Failed to write to %s: %s\n", __FUNCTION__,
+                       PERSISTENCE_FILE, strerror(errno));
+            }
+            if (lpEnabled != 0) {
+                brightness = DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS;
+            }
         }
-        if (lpEnabled != 0) {
-            brightness = DEFAULT_LOW_PERSISTENCE_MODE_BRIGHTNESS;
-        }
+        g_last_backlight_mode = state->brightnessMode;
     }
 
-    g_last_backlight_mode = state->brightnessMode;
-
     if (!err) {
         if (!access(LCD_FILE, F_OK)) {
             err = write_int(LCD_FILE, brightness);
@@ -162,7 +166,7 @@
     }
 
     pthread_mutex_unlock(&g_lock);
-    return err;
+    return cannot_handle_persistence ? -ENOSYS : err;
 }
 
 static int
@@ -355,8 +359,10 @@
             g_brightness_max = atoi(property);
             set_brightness_ext_init();
             set_light = set_light_backlight_ext;
-        } else
+        } else {
+            g_has_persistence_node = !access(PERSISTENCE_FILE, F_OK);
             set_light = set_light_backlight;
+        }
     } else if (0 == strcmp(LIGHT_ID_BATTERY, name))
         set_light = set_light_battery;
     else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))
diff --git a/sdm/libs/core/drm/hw_device_drm.cpp b/sdm/libs/core/drm/hw_device_drm.cpp
index 65498be..96ad8a3 100644
--- a/sdm/libs/core/drm/hw_device_drm.cpp
+++ b/sdm/libs/core/drm/hw_device_drm.cpp
@@ -360,14 +360,24 @@
 }
 
 DisplayError HWDeviceDRM::Deinit() {
-  PowerOff();
+  DisplayError err = kErrorNone;
+  drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_CRTC, token_.conn_id, 0);
+  drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_POWER_MODE, token_.conn_id, DRMPowerMode::OFF);
+  drm_atomic_intf_->Perform(DRMOps::CRTC_SET_MODE, token_.crtc_id, nullptr);
+  drm_atomic_intf_->Perform(DRMOps::CRTC_SET_ACTIVE, token_.crtc_id, 0);
+  int ret = drm_atomic_intf_->Commit(true /* synchronous */, false /* retain_planes */);
+  if (ret) {
+    DLOGE("Commit failed with error: %d", ret);
+    err = kErrorHardware;
+  }
+
   delete hw_scale_;
   registry_.Clear();
   display_attributes_ = {};
   drm_mgr_intf_->DestroyAtomicReq(drm_atomic_intf_);
   drm_atomic_intf_ = {};
   drm_mgr_intf_->UnregisterDisplay(token_);
-  return kErrorNone;
+  return err;
 }
 
 void HWDeviceDRM::InitializeConfigs() {
@@ -915,6 +925,10 @@
     drm_atomic_intf_->Perform(DRMOps::CRTC_SET_IDLE_TIMEOUT, token_.crtc_id,
                               hw_layer_info.set_idle_time_ms);
   }
+
+  if (hw_panel_info_.mode == kModeCommand) {
+    drm_atomic_intf_->Perform(DRMOps::CONNECTOR_SET_AUTOREFRESH, token_.conn_id, autorefresh_);
+  }
 }
 
 void HWDeviceDRM::AddSolidfillStage(const HWSolidfillStage &sf, uint32_t plane_alpha) {
diff --git a/sdm/libs/core/drm/hw_device_drm.h b/sdm/libs/core/drm/hw_device_drm.h
index 0a8b722..e0b7fc1 100644
--- a/sdm/libs/core/drm/hw_device_drm.h
+++ b/sdm/libs/core/drm/hw_device_drm.h
@@ -90,7 +90,7 @@
   virtual DisplayError SetCursorPosition(HWLayers *hw_layers, int x, int y);
   virtual DisplayError OnMinHdcpEncryptionLevelChange(uint32_t min_enc_level);
   virtual DisplayError GetPanelBrightness(int *level);
-  virtual DisplayError SetAutoRefresh(bool enable) { return kErrorNone; }
+  virtual DisplayError SetAutoRefresh(bool enable) { autorefresh_ = enable; return kErrorNone; }
   virtual DisplayError SetS3DMode(HWS3DMode s3d_mode);
   virtual DisplayError SetScaleLutConfig(HWScaleLutInfo *lut_info);
   virtual DisplayError SetMixerAttributes(const HWMixerAttributes &mixer_attributes);
@@ -184,6 +184,7 @@
   bool resolution_switch_enabled_ = false;
   uint32_t vrefresh_ = 0;
   bool switch_mode_ = false;
+  bool autorefresh_ = false;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/core/drm/hw_tv_drm.cpp b/sdm/libs/core/drm/hw_tv_drm.cpp
index 08e502e..b2075a8 100644
--- a/sdm/libs/core/drm/hw_tv_drm.cpp
+++ b/sdm/libs/core/drm/hw_tv_drm.cpp
@@ -152,13 +152,6 @@
   return kErrorNone;
 }
 
-/* overriding display state funcs to have special or NO OP implementation for TVs */
-DisplayError HWTVDRM::Deinit() {
-  drm_atomic_intf_->Perform(DRMOps::CRTC_SET_ACTIVE, token_.crtc_id, 0);
-
-  return HWDeviceDRM::Deinit();
-}
-
 DisplayError HWTVDRM::PowerOff() {
   DTRACE_SCOPED();
 
diff --git a/sdm/libs/core/drm/hw_tv_drm.h b/sdm/libs/core/drm/hw_tv_drm.h
index 985499e..ff07ac2 100644
--- a/sdm/libs/core/drm/hw_tv_drm.h
+++ b/sdm/libs/core/drm/hw_tv_drm.h
@@ -41,7 +41,6 @@
 
  protected:
   virtual DisplayError Init();
-  virtual DisplayError Deinit();
   virtual DisplayError SetDisplayAttributes(uint32_t index);
   virtual DisplayError GetConfigIndex(char *mode, uint32_t *index);
   virtual DisplayError PowerOff();
diff --git a/sdm/libs/core/fb/hw_hdmi.cpp b/sdm/libs/core/fb/hw_hdmi.cpp
index c69e615..e1d7ba1 100644
--- a/sdm/libs/core/fb/hw_hdmi.cpp
+++ b/sdm/libs/core/fb/hw_hdmi.cpp
@@ -190,6 +190,7 @@
   (void)hdr_reset_start_;
   (void)hdr_reset_end_;
   (void)reset_hdr_flag_;
+  (void)cdm_color_space_;
 }
 
 DisplayError HWHDMI::Init() {
@@ -440,8 +441,19 @@
   if (error != kErrorNone) {
     return error;
   }
+  if (cdm_color_space_commit_) {
+#ifdef MDP_COMMIT_UPDATE_CDM_COLOR_SPACE
+    mdp_layer_commit_v1 &mdp_commit = mdp_disp_commit_.commit_v1;
+    mdp_commit.cdm_color_space = cdm_color_space_;
+    mdp_commit.flags |= MDP_COMMIT_UPDATE_CDM_COLOR_SPACE;
+#endif
+  }
 
-  return HWDevice::Commit(hw_layers);
+  error = HWDevice::Commit(hw_layers);
+  if (cdm_color_space_commit_)
+    cdm_color_space_commit_ = false;
+
+  return error;
 }
 
 DisplayError HWHDMI::GetHWScanInfo(HWScanInfo *scan_info) {
@@ -1032,6 +1044,10 @@
     hdr_ctrl.hdr_stream.max_average_light_level = light_level.minPicAverageLightLevel;
     hdr_ctrl.hdr_state = HDR_ENABLE;
     reset_hdr_flag_ = false;
+#ifdef MDP_COMMIT_UPDATE_CDM_COLOR_SPACE
+    HWDevice::SetCSC(layer_buffer->color_metadata, &cdm_color_space_);
+    cdm_color_space_commit_ = true;
+#endif
     // DP related
     int32_t pixel_encoding = GetPixelEncoding(hdr_layer.input_buffer);
     hdr_ctrl.hdr_stream.pixel_encoding = (pixel_encoding < 0) ? 0 : UINT32(pixel_encoding);
@@ -1063,6 +1079,10 @@
     hdr_ctrl.hdr_state = HDR_RESET;
     reset_hdr_flag_ = true;
     hdr_reset_start_ = time(NULL);
+#ifdef MDP_COMMIT_UPDATE_CDM_COLOR_SPACE
+    cdm_color_space_ = (mdp_color_space) MDP_CSC_DEFAULT;
+    cdm_color_space_commit_ = true;
+#endif
     DLOGV_IF(kTagDriverConfig, "kReset: HDR Stream: HDR_RESET");
   } else if (hdr_layer_info.operation == HWHDRLayerInfo::kNoOp) {
      if (reset_hdr_flag_) {
diff --git a/sdm/libs/core/fb/hw_hdmi.h b/sdm/libs/core/fb/hw_hdmi.h
index 80d289a..183e9db 100644
--- a/sdm/libs/core/fb/hw_hdmi.h
+++ b/sdm/libs/core/fb/hw_hdmi.h
@@ -121,6 +121,8 @@
   uint32_t frame_rate_ = 0;
   time_t hdr_reset_start_ = 0, hdr_reset_end_ = 0;
   bool reset_hdr_flag_ = false;
+  mdp_color_space cdm_color_space_ = {};
+  bool cdm_color_space_commit_ = false;
 };
 
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_buffer_allocator.cpp b/sdm/libs/hwc2/hwc_buffer_allocator.cpp
index 1c264aa..e41a267 100644
--- a/sdm/libs/hwc2/hwc_buffer_allocator.cpp
+++ b/sdm/libs/hwc2/hwc_buffer_allocator.cpp
@@ -77,6 +77,8 @@
       gralloc_device_->getFunction(gralloc_device_, GRALLOC1_FUNCTION_SET_PRODUCER_USAGE));
   LockBuffer_ = reinterpret_cast<GRALLOC1_PFN_LOCK>(
       gralloc_device_->getFunction(gralloc_device_, GRALLOC1_FUNCTION_LOCK));
+  UnlockBuffer_ = reinterpret_cast<GRALLOC1_PFN_UNLOCK>(
+      gralloc_device_->getFunction(gralloc_device_, GRALLOC1_FUNCTION_UNLOCK));
   Perform_ = reinterpret_cast<GRALLOC1_PFN_PERFORM>(
       gralloc_device_->getFunction(gralloc_device_, GRALLOC1_FUNCTION_PERFORM));
 
@@ -444,4 +446,8 @@
   return kErrorNone;
 }
 
+DisplayError HWCBufferAllocator::UnmapBuffer(const private_handle_t *handle, int* release_fence) {
+  return (DisplayError)(UnlockBuffer_(gralloc_device_, handle, release_fence));
+}
+
 }  // namespace sdm
diff --git a/sdm/libs/hwc2/hwc_buffer_allocator.h b/sdm/libs/hwc2/hwc_buffer_allocator.h
index 8a73ccb..7df88a4 100644
--- a/sdm/libs/hwc2/hwc_buffer_allocator.h
+++ b/sdm/libs/hwc2/hwc_buffer_allocator.h
@@ -61,6 +61,7 @@
                                uint32_t *num_planes);
   int SetBufferInfo(LayerBufferFormat format, int *target, uint64_t *flags);
   DisplayError MapBuffer(const private_handle_t *handle, int acquire_fence);
+  DisplayError UnmapBuffer(const private_handle_t *handle, int* release_fence);
 
  private:
   gralloc1_device_t *gralloc_device_ = nullptr;
@@ -74,6 +75,7 @@
   GRALLOC1_PFN_SET_CONSUMER_USAGE SetConsumerUsage_ = nullptr;
   GRALLOC1_PFN_SET_PRODUCER_USAGE SetProducerUsage_ = nullptr;
   GRALLOC1_PFN_LOCK LockBuffer_ = nullptr;
+  GRALLOC1_PFN_UNLOCK UnlockBuffer_ = nullptr;
   GRALLOC1_PFN_PERFORM Perform_ = nullptr;
 };
 
diff --git a/sdm/libs/hwc2/hwc_display.cpp b/sdm/libs/hwc2/hwc_display.cpp
index 03e37aa..0c305be 100644
--- a/sdm/libs/hwc2/hwc_display.cpp
+++ b/sdm/libs/hwc2/hwc_display.cpp
@@ -94,11 +94,12 @@
 HWC2::Error HWCColorMode::GetColorModes(uint32_t *out_num_modes,
                                         android_color_mode_t *out_modes) {
   auto it = color_mode_transform_map_.begin();
-  for (auto i = 0; it != color_mode_transform_map_.end(); it++, i++) {
+  *out_num_modes = std::min(*out_num_modes, UINT32(color_mode_transform_map_.size()));
+  for (uint32_t i = 0; i < *out_num_modes; it++, i++) {
     out_modes[i] = it->first;
     DLOGI("Supports color mode[%d] = %d", i, it->first);
   }
-  *out_num_modes = UINT32(color_mode_transform_map_.size());
+
   return HWC2::Error::None;
 }
 
@@ -131,11 +132,6 @@
 }
 
 HWC2::Error HWCColorMode::SetColorTransform(const float *matrix, android_color_transform_t hint) {
-  if (!matrix || (hint < HAL_COLOR_TRANSFORM_IDENTITY ||
-      hint > HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA)) {
-    return HWC2::Error::BadParameter;
-  }
-
   DTRACE_SCOPED();
   double color_matrix[kColorTransformMatrixCount] = {0};
   CopyColorTransformMatrix(matrix, color_matrix);
@@ -750,23 +746,28 @@
 }
 
 HWC2::Error HWCDisplay::GetColorModes(uint32_t *out_num_modes, android_color_mode_t *out_modes) {
-  if (out_modes) {
+  if (out_modes == nullptr) {
+    *out_num_modes = 1;
+  } else if (out_modes && *out_num_modes > 0) {
+    *out_num_modes = 1;
     out_modes[0] = HAL_COLOR_MODE_NATIVE;
   }
-  *out_num_modes = 1;
 
   return HWC2::Error::None;
 }
 
 HWC2::Error HWCDisplay::GetDisplayConfigs(uint32_t *out_num_configs, hwc2_config_t *out_configs) {
+  if (out_num_configs == nullptr) {
+    return HWC2::Error::BadParameter;
+  }
+
   if (out_configs == nullptr) {
     *out_num_configs = num_configs_;
     return HWC2::Error::None;
   }
 
-  *out_num_configs = num_configs_;
-
-  for (uint32_t i = 0; i < num_configs_; i++) {
+  *out_num_configs = std::min(*out_num_configs, num_configs_);
+  for (uint32_t i = 0; i < *out_num_configs; i++) {
     out_configs[i] = i;
   }
 
@@ -817,27 +818,38 @@
 
 HWC2::Error HWCDisplay::GetDisplayName(uint32_t *out_size, char *out_name) {
   // TODO(user): Get panel name and EDID name and populate it here
-  if (out_name == nullptr) {
-    *out_size = 32;
-  } else {
-    std::string name;
-    switch (id_) {
-      case HWC_DISPLAY_PRIMARY:
-        name = "Primary Display";
-        break;
-      case HWC_DISPLAY_EXTERNAL:
-        name = "External Display";
-        break;
-      case HWC_DISPLAY_VIRTUAL:
-        name = "Virtual Display";
-        break;
-      default:
-        name = "Unknown";
-        break;
-    }
-    std::strncpy(out_name, name.c_str(), name.size());
-    *out_size = UINT32(name.size());
+  if (out_size == nullptr) {
+    return HWC2::Error::BadParameter;
   }
+
+  std::string name;
+  switch (id_) {
+    case HWC_DISPLAY_PRIMARY:
+      name = "Primary Display";
+      break;
+    case HWC_DISPLAY_EXTERNAL:
+      name = "External Display";
+      break;
+    case HWC_DISPLAY_VIRTUAL:
+      name = "Virtual Display";
+      break;
+    default:
+      name = "Unknown";
+      break;
+  }
+
+  if (out_name == nullptr) {
+    *out_size = UINT32(name.size()) + 1;
+  } else {
+    *out_size = std::min((UINT32(name.size()) + 1), *out_size);
+    if (*out_size > 0) {
+      std::strncpy(out_name, name.c_str(), *out_size);
+      out_name[*out_size - 1] = '\0';
+    } else {
+      DLOGW("Invalid size requested");
+    }
+  }
+
   return HWC2::Error::None;
 }
 
@@ -1073,15 +1085,22 @@
 
 HWC2::Error HWCDisplay::GetReleaseFences(uint32_t *out_num_elements, hwc2_layer_t *out_layers,
                                          int32_t *out_fences) {
+  if (out_num_elements == nullptr) {
+    return HWC2::Error::BadParameter;
+  }
+
   if (out_layers != nullptr && out_fences != nullptr) {
-    int i = 0;
-    for (auto hwc_layer : layer_set_) {
+    *out_num_elements = std::min(*out_num_elements, UINT32(layer_set_.size()));
+    auto it = layer_set_.begin();
+    for (uint32_t i = 0; i < *out_num_elements; i++, it++) {
+      auto hwc_layer = *it;
       out_layers[i] = hwc_layer->GetId();
       out_fences[i] = hwc_layer->PopReleaseFence();
-      i++;
     }
+  } else {
+    *out_num_elements = UINT32(layer_set_.size());
   }
-  *out_num_elements = UINT32(layer_set_.size());
+
   return HWC2::Error::None;
 }
 
@@ -1092,6 +1111,10 @@
     return HWC2::Error::None;
   }
 
+  if (out_display_requests == nullptr || out_num_elements == nullptr) {
+    return HWC2::Error::BadParameter;
+  }
+
   // No display requests for now
   // Use for sharing blit buffers and
   // writing wfd buffer directly to output if there is full GPU composition
@@ -1102,14 +1125,15 @@
   }
 
   *out_display_requests = 0;
-  *out_num_elements = UINT32(layer_requests_.size());
   if (out_layers != nullptr && out_layer_requests != nullptr) {
-    int i = 0;
-    for (auto &request : layer_requests_) {
-      out_layers[i] = request.first;
-      out_layer_requests[i] = INT32(request.second);
-      i++;
+    *out_num_elements = std::min(*out_num_elements, UINT32(layer_requests_.size()));
+    auto it = layer_requests_.begin();
+    for (uint32_t i = 0; i < *out_num_elements; i++, it++) {
+      out_layers[i] = it->first;
+      out_layer_requests[i] = INT32(it->second);
     }
+  } else {
+    *out_num_elements = UINT32(layer_requests_.size());
   }
 
   auto client_target_layer = client_target_->GetSDMLayer();
@@ -1124,6 +1148,11 @@
                                            float *out_max_luminance,
                                            float *out_max_average_luminance,
                                            float *out_min_luminance) {
+  if (out_num_types == nullptr || out_max_luminance == nullptr ||
+      out_max_average_luminance == nullptr || out_min_luminance == nullptr) {
+    return HWC2::Error::BadParameter;
+  }
+
   DisplayConfigFixedInfo fixed_info = {};
   display_intf_->GetConfig(&fixed_info);
 
@@ -1460,32 +1489,50 @@
     auto acquire_fence_fd = layer->input_buffer.acquire_fence_fd;
 
     if (acquire_fence_fd >= 0) {
-      DisplayError error = buffer_allocator_->MapBuffer(pvt_handle, acquire_fence_fd);
-      if (error != kErrorNone) {
-        continue;
+      int error = sync_wait(acquire_fence_fd, 1000);
+      if (error < 0) {
+        DLOGW("sync_wait error errno = %d, desc = %s", errno, strerror(errno));
+        return;
       }
     }
 
-
     DLOGI("Dump layer[%d] of %d pvt_handle %x pvt_handle->base %x", i, layer_stack_.layers.size(),
           pvt_handle, pvt_handle? pvt_handle->base : 0);
 
-    if (pvt_handle && pvt_handle->base) {
-      char dump_file_name[PATH_MAX];
-      size_t result = 0;
-
-      snprintf(dump_file_name, sizeof(dump_file_name), "%s/input_layer%d_%dx%d_%s_frame%d.raw",
-               dir_path, i, pvt_handle->width, pvt_handle->height,
-               qdutils::GetHALPixelFormatString(pvt_handle->format), dump_frame_index_);
-
-      FILE *fp = fopen(dump_file_name, "w+");
-      if (fp) {
-        result = fwrite(reinterpret_cast<void *>(pvt_handle->base), pvt_handle->size, 1, fp);
-        fclose(fp);
-      }
-
-      DLOGI("Frame Dump %s: is %s", dump_file_name, result ? "Successful" : "Failed");
+    if (!pvt_handle) {
+      DLOGE("Buffer handle is null");
+      return;
     }
+
+    if (!pvt_handle->base) {
+      DisplayError error = buffer_allocator_->MapBuffer(pvt_handle, -1);
+      if (error != kErrorNone) {
+        DLOGE("Failed to map buffer, error = %d", error);
+        return;
+      }
+    }
+
+    char dump_file_name[PATH_MAX];
+    size_t result = 0;
+
+    snprintf(dump_file_name, sizeof(dump_file_name), "%s/input_layer%d_%dx%d_%s_frame%d.raw",
+             dir_path, i, pvt_handle->width, pvt_handle->height,
+             qdutils::GetHALPixelFormatString(pvt_handle->format), dump_frame_index_);
+
+    FILE *fp = fopen(dump_file_name, "w+");
+    if (fp) {
+      result = fwrite(reinterpret_cast<void *>(pvt_handle->base), pvt_handle->size, 1, fp);
+      fclose(fp);
+    }
+
+    int release_fence = -1;
+    DisplayError error = buffer_allocator_->UnmapBuffer(pvt_handle, &release_fence);
+    if (error != kErrorNone) {
+      DLOGE("Failed to unmap buffer, error = %d", error);
+      return;
+    }
+
+    DLOGI("Frame Dump %s: is %s", dump_file_name, result ? "Successful" : "Failed");
   }
 }
 
diff --git a/sdm/libs/hwc2/hwc_display_virtual.cpp b/sdm/libs/hwc2/hwc_display_virtual.cpp
index 719167a..e1949ba 100644
--- a/sdm/libs/hwc2/hwc_display_virtual.cpp
+++ b/sdm/libs/hwc2/hwc_display_virtual.cpp
@@ -133,12 +133,22 @@
       }
     }
   }
+
+  if (layer_set_.empty()) {
+    DLOGI("Skipping Validate and Commit");
+    return status;
+  }
   status = PrepareLayerStack(out_num_types, out_num_requests);
   return status;
 }
 
 HWC2::Error HWCDisplayVirtual::Present(int32_t *out_retire_fence) {
   auto status = HWC2::Error::None;
+
+  if (!output_buffer_->buffer_id) {
+    return HWC2::Error::NoResources;
+  }
+
   if (display_paused_) {
     DisplayError error = display_intf_->Flush();
     validated_ = false;
@@ -149,10 +159,18 @@
     status = HWCDisplay::CommitLayerStack();
     if (status == HWC2::Error::None) {
       if (dump_frame_count_ && !flush_ && dump_output_layer_) {
-        if (output_handle_ && output_handle_->base) {
+        if (output_handle_) {
           BufferInfo buffer_info;
           const private_handle_t *output_handle =
               reinterpret_cast<const private_handle_t *>(output_buffer_->buffer_id);
+          DisplayError error = kErrorNone;
+          if (!output_handle->base) {
+            error = buffer_allocator_->MapBuffer(output_handle, -1);
+            if (error != kErrorNone) {
+              DLOGE("Failed to map output buffer, error = %d", error);
+              return HWC2::Error::BadParameter;
+            }
+          }
           buffer_info.buffer_config.width = static_cast<uint32_t>(output_handle->width);
           buffer_info.buffer_config.height = static_cast<uint32_t>(output_handle->height);
           buffer_info.buffer_config.format =
@@ -160,6 +178,13 @@
           buffer_info.alloc_buffer_info.size = static_cast<uint32_t>(output_handle->size);
           DumpOutputBuffer(buffer_info, reinterpret_cast<void *>(output_handle->base),
                            layer_stack_.retire_fence_fd);
+
+          int release_fence = -1;
+          error = buffer_allocator_->UnmapBuffer(output_handle, &release_fence);
+          if (error != kErrorNone) {
+            DLOGE("Failed to unmap buffer, error = %d", error);
+            return HWC2::Error::BadParameter;
+          }
         }
       }
 
diff --git a/sdm/libs/hwc2/hwc_session.cpp b/sdm/libs/hwc2/hwc_session.cpp
index 31e61f0..624105b 100644
--- a/sdm/libs/hwc2/hwc_session.cpp
+++ b/sdm/libs/hwc2/hwc_session.cpp
@@ -446,6 +446,9 @@
 static int32_t GetColorModes(hwc2_device_t *device, hwc2_display_t display, uint32_t *out_num_modes,
                              int32_t /*android_color_mode_t*/ *int_out_modes) {
   auto out_modes = reinterpret_cast<android_color_mode_t *>(int_out_modes);
+  if (out_num_modes == nullptr) {
+    return HWC2_ERROR_BAD_PARAMETER;
+  }
   return HWCSession::CallDisplayFunction(device, display, &HWCDisplay::GetColorModes, out_num_modes,
                                          out_modes);
 }
@@ -453,6 +456,10 @@
 static int32_t GetDisplayAttribute(hwc2_device_t *device, hwc2_display_t display,
                                    hwc2_config_t config, int32_t int_attribute,
                                    int32_t *out_value) {
+  if (out_value == nullptr || int_attribute < HWC2_ATTRIBUTE_INVALID ||
+      int_attribute > HWC2_ATTRIBUTE_DPI_Y) {
+    return HWC2_ERROR_BAD_PARAMETER;
+  }
   auto attribute = static_cast<HWC2::Attribute>(int_attribute);
   return HWCSession::CallDisplayFunction(device, display, &HWCDisplay::GetDisplayAttribute, config,
                                          attribute, out_value);
@@ -510,6 +517,10 @@
 }
 
 static uint32_t GetMaxVirtualDisplayCount(hwc2_device_t *device) {
+  if (device == nullptr) {
+    return HWC2_ERROR_BAD_PARAMETER;
+  }
+
   return 1;
 }
 
@@ -528,7 +539,9 @@
   if (!device) {
     return HWC2_ERROR_BAD_DISPLAY;
   }
-
+  if (out_retire_fence == nullptr) {
+    return HWC2_ERROR_BAD_PARAMETER;
+  }
   auto status = HWC2::Error::BadDisplay;
   // TODO(user): Handle virtual display/HDMI concurrency
   if (hwc_session->hwc_display_[display]) {
@@ -544,10 +557,10 @@
 int32_t HWCSession::RegisterCallback(hwc2_device_t *device, int32_t descriptor,
                                      hwc2_callback_data_t callback_data,
                                      hwc2_function_pointer_t pointer) {
-  HWCSession *hwc_session = static_cast<HWCSession *>(device);
-  if (!device) {
-    return HWC2_ERROR_BAD_DISPLAY;
+  if (!device || pointer == nullptr) {
+    return HWC2_ERROR_BAD_PARAMETER;
   }
+  HWCSession *hwc_session = static_cast<HWCSession *>(device);
   SCOPE_LOCK(hwc_session->callbacks_lock_);
   auto desc = static_cast<HWC2::Callback>(descriptor);
   auto error = hwc_session->callbacks_.Register(desc, callback_data, pointer);
@@ -575,6 +588,12 @@
 
 int32_t HWCSession::SetColorMode(hwc2_device_t *device, hwc2_display_t display,
                                  int32_t /*android_color_mode_t*/ int_mode) {
+  if (display >= HWC_NUM_DISPLAY_TYPES) {
+    return HWC2_ERROR_BAD_DISPLAY;
+  }
+  if (int_mode < HAL_COLOR_MODE_NATIVE || int_mode > HAL_COLOR_MODE_DISPLAY_P3) {
+    return HWC2_ERROR_BAD_PARAMETER;
+  }
   auto mode = static_cast<android_color_mode_t>(int_mode);
   SCOPE_LOCK(locker_[display]);
   return HWCSession::CallDisplayFunction(device, display, &HWCDisplay::SetColorMode, mode);
@@ -583,6 +602,13 @@
 int32_t HWCSession::SetColorTransform(hwc2_device_t *device, hwc2_display_t display,
                                       const float *matrix,
                                       int32_t /*android_color_transform_t*/ hint) {
+  if (display >= HWC_NUM_DISPLAY_TYPES) {
+    return HWC2_ERROR_BAD_DISPLAY;
+  }
+  if (!matrix || hint < HAL_COLOR_TRANSFORM_IDENTITY ||
+       hint > HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA) {
+    return HWC2_ERROR_BAD_PARAMETER;
+  }
   SCOPE_LOCK(locker_[display]);
   android_color_transform_t transform_hint = static_cast<android_color_transform_t>(hint);
   return HWCSession::CallDisplayFunction(device, display, &HWCDisplay::SetColorTransform, matrix,
@@ -603,6 +629,9 @@
 
 static int32_t SetLayerBlendMode(hwc2_device_t *device, hwc2_display_t display, hwc2_layer_t layer,
                                  int32_t int_mode) {
+  if (int_mode < HWC2_BLEND_MODE_INVALID || int_mode > HWC2_BLEND_MODE_COVERAGE) {
+    return HWC2_ERROR_BAD_PARAMETER;
+  }
   auto mode = static_cast<HWC2::BlendMode>(int_mode);
   return HWCSession::CallLayerFunction(device, display, layer, &HWCLayer::SetLayerBlendMode, mode);
 }
@@ -760,6 +789,7 @@
 
         if (hwc_session->need_invalidate_) {
           hwc_session->Refresh(display);
+          hwc_session->need_invalidate_ = false;
         }
 
         if (hwc_session->color_mgr_) {
@@ -1693,11 +1723,7 @@
       }
     } else {
       SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_EXTERNAL]);
-      // Do not return error if external display is not in connected status.
-      // Due to virtual display concurrency, external display connection might be still pending
-      // but hdmi got disconnected before pending connection could be processed.
       if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
-        status = DisconnectDisplay(HWC_DISPLAY_EXTERNAL);
         notify_hotplug = true;
       }
       external_pending_connect_ = false;
@@ -1705,6 +1731,7 @@
   } while (0);
 
   if (connected) {
+    // In connect case, we send hotplug after we create display
     Refresh(0);
 
     if (!hdmi_is_primary_) {
@@ -1713,13 +1740,35 @@
       uint32_t vsync_period = UINT32(GetVsyncPeriod(HWC_DISPLAY_PRIMARY));
       usleep(vsync_period * 2 / 1000);
     }
+    if (notify_hotplug) {
+      HotPlug(hdmi_is_primary_ ? HWC_DISPLAY_PRIMARY : HWC_DISPLAY_EXTERNAL,
+              HWC2::Connection::Connected);
+    }
+  } else {
+    // In disconnect case, we notify hotplug first to let the listener state update happen first
+    // Then we can destroy the underlying display object
+    if (notify_hotplug) {
+      HotPlug(hdmi_is_primary_ ? HWC_DISPLAY_PRIMARY : HWC_DISPLAY_EXTERNAL,
+              HWC2::Connection::Disconnected);
+    }
+    Refresh(0);
+    if (!hdmi_is_primary_) {
+      uint32_t vsync_period = UINT32(GetVsyncPeriod(HWC_DISPLAY_PRIMARY));
+      usleep(vsync_period * 2 / 1000);
+    }
+    // Now disconnect the display
+    {
+      SEQUENCE_WAIT_SCOPE_LOCK(locker_[HWC_DISPLAY_EXTERNAL]);
+      // Do not return error if external display is not in connected status.
+      // Due to virtual display concurrency, external display connection might be still pending
+      // but hdmi got disconnected before pending connection could be processed.
+      if (hwc_display_[HWC_DISPLAY_EXTERNAL]) {
+        status = DisconnectDisplay(HWC_DISPLAY_EXTERNAL);
+      }
+    }
   }
 
   // notify client
-  if (notify_hotplug) {
-    HotPlug(hdmi_is_primary_ ? HWC_DISPLAY_PRIMARY : HWC_DISPLAY_EXTERNAL,
-            connected ? HWC2::Connection::Connected : HWC2::Connection::Disconnected);
-  }
 
   qservice_->onHdmiHotplug(INT(connected));