Merge "sdm: Clear old content metadata when gralloc buffer metadata is dropped"
diff --git a/composer/hwc_display.cpp b/composer/hwc_display.cpp
index dbf061b..1db3c93 100644
--- a/composer/hwc_display.cpp
+++ b/composer/hwc_display.cpp
@@ -1426,6 +1426,7 @@
     DLOGE("mmap failed with err %d", errno);
     buffer_allocator_->FreeBuffer(&output_buffer_info_);
     output_buffer_info_ = {};
+    dump_frame_count_ = 0;
     return HWC2::Error::NoResources;
   }
 
@@ -1435,6 +1436,7 @@
     munmap(output_buffer_base_, output_buffer_info_.alloc_buffer_info.size);
     buffer_allocator_->FreeBuffer(&output_buffer_info_);
     output_buffer_info_ = {};
+    dump_frame_count_ = 0;
     return err;
   }
   dump_output_to_file_ = dump_output_to_file;
@@ -1907,11 +1909,6 @@
     display_pause_pending_ = false;
   }
 
-  if (dump_frame_count_) {
-    dump_frame_count_--;
-    dump_frame_index_++;
-  }
-
   layer_stack_.flags.geometry_changed = false;
   geometry_changes_ = GeometryChanges::kNone;
   flush_ = false;
@@ -2114,7 +2111,8 @@
       result = fwrite(base, buffer_info.alloc_buffer_info.size, 1, fp);
       fclose(fp);
     }
-
+    // Need to clear buffer after dumping of current frame to provide empty buffer for next frame.
+    memset(base, 0, buffer_info.alloc_buffer_info.size);
     DLOGI("Frame Dump of %s is %s", dump_file_name, result ? "Successful" : "Failed");
   }
 }
@@ -3128,6 +3126,10 @@
   if (secure_event == kTUITransitionEnd || secure_event == kTUITransitionUnPrepare) {
     DLOGI("Resume display %d-%d",  sdm_id_, type_);
     display_paused_ = false;
+    if (*needs_refresh == false) {
+      secure_event_ = kSecureEventMax;
+      return kErrorNone;
+    }
   } else if (secure_event == kTUITransitionPrepare || secure_event == kTUITransitionStart) {
     if (*needs_refresh) {
       display_pause_pending_ = true;
@@ -3394,6 +3396,9 @@
   uint64_t handle_id = 0;
 
   *release_fence = nullptr;
+  // If release fence is available, then try to get it first and keep it out of lock to avoid
+  // deadlock with GetOutputBufferAcquireFence call, and validate it later with handle id.
+  display_intf_->GetOutputBufferAcquireFence(release_fence);
   {
     std::unique_lock<std::mutex> lock(cwb_mutex_);
     auto &cwb_resp = cwb_capture_status_map_[client];
@@ -3402,22 +3407,17 @@
       // If this function is called after either PostCommitLayerStack or NotifyCwbDone call,
       // then release fence can be successfully retrieved from cwb_capture_status_map_.
       handle_id = cwb_resp.handle_id;
-      if (cwb_resp.status == kCWBReleaseFenceNotChecked) {
-        // if status is updated as kCWBReleaseFenceNotChecked, which means the commit is over
-        // and status got updated in PostCommitLayerStack->HandleFrameOutput. It indicates
-        // that release fence is available.
-        display_intf_->GetOutputBufferAcquireFence(release_fence);
-      } else {
+      if (cwb_resp.status != kCWBReleaseFenceNotChecked) {
         *release_fence = cwb_resp.release_fence;
       }
       status = cwb_resp.status;
     } else if (layer_stack_.output_buffer != nullptr) {
       // If this function is called before both PostCommitLayerStack and NotifyCwbDone call,
-      // then release fence may be retrieved directly from layer_stack_.output_buffer.
+      // then handle_id may be retrieved directly from layer_stack_.output_buffer corresponding
+      // to available release fence.
       const auto map_cwb_buffer = cwb_buffer_map_.find(layer_stack_.output_buffer->handle_id);
       if (map_cwb_buffer != cwb_buffer_map_.end() && client == map_cwb_buffer->second) {
         handle_id = layer_stack_.output_buffer->handle_id;
-        display_intf_->GetOutputBufferAcquireFence(release_fence);
         status = kCWBReleaseFenceNotChecked;
       }
     } else {
@@ -3429,6 +3429,8 @@
           break;
         }
       }
+      // Avoid to return old release fence, in case of too early call of this function.
+      *release_fence = nullptr;
     }
 
     if (*release_fence != nullptr) {
@@ -3477,7 +3479,7 @@
   auto error = GetReadbackBufferFenceForClient(kCWBClientComposer, release_fence);
 
   // if release fence is null pointer, then just return with error.
-  if (release_fence && *release_fence == nullptr) {
+  if (!release_fence || *release_fence == nullptr) {
     status = HWC2::Error::Unsupported;
     DLOGW("Readback buffer fence is not available! CWBReleaseFenceError: %d", error);
   }
@@ -3542,29 +3544,59 @@
     // If CWB request status is not notified, then need to wait for the notification.
     if (cwb_resp.status == kCWBReleaseFenceNotChecked) {
       if (cwb_cv_.wait_until(
-              lock, std::chrono::system_clock::now() + std::chrono::milliseconds(cwb_wait_ms)) ==
+              lock, std::chrono::system_clock::now() + std::chrono::milliseconds(kCwbWaitMs)) ==
         std::cv_status::timeout) {
-        DLOGE("CWB wait timed out.");
-        cwb_resp.status = kCWBReleaseFenceWaitTimedOut;
+        DLOGW("CWB notification wait timed out, it would be handled in next cycle.");
+        // Return to handle the notification in next cycle, if release fence is not yet notified.
+        return;
       }
     }
     ret = cwb_resp.status;
+    cwb_capture_status_map_.erase(kCWBClientFrameDump);
   }
 
-  if (ret != kCWBReleaseFenceErrorNone) {
-    DLOGE("sync_wait error errno = %d, desc = %s", errno, strerror(errno));
-  }
-
-  if (!ret) {
+  if (!ret || ret == kCWBReleaseFenceWaitTimedOut) {
+    // On fence wait timeout, we could dump the frame, because timeout means it waited for
+    // one second for signal, and which might got delayed due to some flushing and resource
+    // releasing operations during certain power glitch event. So, we can assume that buffer
+    // writing operation is over after timeout.
     DumpOutputBuffer(output_buffer_info_, output_buffer_base_, layer_stack_.retire_fence);
+    if (ret == kCWBReleaseFenceWaitTimedOut) {
+      DLOGW("CWB frame-%d dump may be empty due to fence timeout on any unexpected event!",
+            dump_frame_index_);
+    }
+  } else {
+    // CwbManager notifies -1 (unknown error) on power down or tear down, where -1 means, last
+    // request is not processed by CwbManager and tried to flush the CWB pending requests from
+    // CWB request queue.
+    DLOGW("Probably, power/tear down occured during the cwb request. So, dropped off frame-%d.",
+          dump_frame_index_);
   }
 
+  bool stop_frame_dump = false;
   if (0 == (dump_frame_count_ - 1)) {
+    stop_frame_dump = true;
+  } else {
+    const native_handle_t *hnd = static_cast<native_handle_t *>(output_buffer_info_.private_data);
+    HWC2::Error err = SetReadbackBuffer(hnd, nullptr, output_buffer_cwb_config_,
+                                        kCWBClientFrameDump);
+    if (err != HWC2::Error::None) {
+      stop_frame_dump = true;
+      DLOGE("Unexpectedly stopped dumping of remaining %d frames for frame indices %d onwards!",
+            dump_frame_count_, dump_frame_index_);
+    } else {
+      dump_frame_count_--;
+      dump_frame_index_++;
+    }
+  }
+
+  if (stop_frame_dump) {
     dump_output_to_file_ = false;
     // Unmap and Free buffer
     if (munmap(output_buffer_base_, output_buffer_info_.alloc_buffer_info.size) != 0) {
       DLOGE("unmap failed with err %d", errno);
     }
+
     if (buffer_allocator_->FreeBuffer(&output_buffer_info_) != 0) {
       DLOGE("FreeBuffer failed");
     }
@@ -3572,15 +3604,8 @@
     output_buffer_info_ = {};
     output_buffer_base_ = nullptr;
     output_buffer_cwb_config_ = {};
-  } else {
-    const native_handle_t *handle = static_cast<native_handle_t *>(output_buffer_info_.private_data);
-    HWC2::Error err = SetReadbackBuffer(handle, nullptr, output_buffer_cwb_config_,
-                                        kCWBClientFrameDump);
-    if (err != HWC2::Error::None) {
-      munmap(output_buffer_base_, output_buffer_info_.alloc_buffer_info.size);
-      buffer_allocator_->FreeBuffer(&output_buffer_info_);
-      output_buffer_info_ = {};
-    }
+    dump_frame_count_ = 0;
+    dump_frame_index_ = 0;
   }
 }
 
diff --git a/composer/hwc_display.h b/composer/hwc_display.h
index d49566a..c38859e 100644
--- a/composer/hwc_display.h
+++ b/composer/hwc_display.h
@@ -683,6 +683,7 @@
   std::mutex cwb_mutex_;
   std::condition_variable cwb_cv_;
   std::map<CWBClient, CWBCaptureResponse> cwb_capture_status_map_;
+  static constexpr unsigned int kCwbWaitMs = 100;
 
  private:
   bool CanSkipSdmPrepare(uint32_t *num_types, uint32_t *num_requests);
@@ -702,7 +703,6 @@
   bool draw_method_set_ = false;
   bool validate_done_ = false;
   bool client_target_3_1_set_ = false;
-  static constexpr unsigned int cwb_wait_ms = 100;
 };
 
 inline int HWCDisplay::Perform(uint32_t operation, ...) {
diff --git a/composer/hwc_session.cpp b/composer/hwc_session.cpp
index 17e4c78..a5b1c1b 100644
--- a/composer/hwc_session.cpp
+++ b/composer/hwc_session.cpp
@@ -3234,10 +3234,6 @@
     available_mixer_count = hwc_display_[active_builtin]->GetAvailableMixerCount();
   }
 
-  if (available_mixer_count < min_mixer_count) {
-    return -EAGAIN;
-  }
-
   for (auto &iter : *hw_displays_info) {
     auto &info = iter.second;
 
@@ -3256,6 +3252,12 @@
       continue;
     }
 
+    if (available_mixer_count < min_mixer_count) {
+      DLOGI("mixers not available: available: %d, min: %d",
+            available_mixer_count, min_mixer_count);
+      return -EAGAIN;
+    }
+
     // Count active pluggable display slots and slots with no commits.
     bool first_commit_pending = false;
     std::for_each(map_info_pluggable_.begin(), map_info_pluggable_.end(),
@@ -3734,13 +3736,17 @@
     return;
   }
 
+  static constexpr uint32_t min_mixer_count = 2;
+  uint32_t available_mixer_count = 0;
   std :: bitset < kSecureMax > secure_sessions = 0;
   if (active_builtin_disp_id < HWCCallbacks::kNumDisplays) {
     Locker::ScopeLock lock_d(locker_[active_builtin_disp_id]);
     hwc_display_[active_builtin_disp_id]->GetActiveSecureSession(&secure_sessions);
+    available_mixer_count = hwc_display_[active_builtin_disp_id]->GetAvailableMixerCount();
   }
 
-  if (secure_sessions.any() || active_builtin_disp_id >= HWCCallbacks::kNumDisplays) {
+  if (secure_sessions.any() || active_builtin_disp_id >= HWCCallbacks::kNumDisplays ||
+      available_mixer_count < min_mixer_count) {
     return;
   }
 
diff --git a/composer/hwc_session.h b/composer/hwc_session.h
index 0702267..96bd91c 100644
--- a/composer/hwc_session.h
+++ b/composer/hwc_session.h
@@ -434,7 +434,7 @@
     };
 
     struct DisplayCWBSession{
-      std::deque<QueueNode *> queue;
+      std::deque<std::shared_ptr<QueueNode>> queue;
       std::mutex lock;
       std::condition_variable cv;
       std::future<void> future;
@@ -443,7 +443,7 @@
 
     static void AsyncTaskToProcessCWBStatus(CWB *cwb, hwc2_display_t display_type);
     void ProcessCWBStatus(hwc2_display_t display_type);
-    void NotifyCWBStatus(int status, QueueNode *cwb_node);
+    void NotifyCWBStatus(int status, std::shared_ptr<QueueNode> cwb_node);
 
     std::map<hwc2_display_t, DisplayCWBSession> display_cwb_session_map_;
     HWCSession *hwc_session_ = nullptr;
diff --git a/composer/hwc_session_services.cpp b/composer/hwc_session_services.cpp
index e507be3..12ad1ad 100644
--- a/composer/hwc_session_services.cpp
+++ b/composer/hwc_session_services.cpp
@@ -1149,7 +1149,7 @@
                                     hwc2_display_t display_type) {
   HWC2::Error error = HWC2::Error::None;
   auto& session_map = display_cwb_session_map_[display_type];
-  QueueNode *node = nullptr;
+  std::shared_ptr<QueueNode> node = nullptr;
   uint64_t node_handle_id = 0;
   void *hdl = const_cast<native_handle_t *>(buffer);
   auto err = gralloc::GetMetaDataValue(hdl, (int64_t)StandardMetadataType::BUFFER_ID,
@@ -1160,15 +1160,29 @@
   }
 
   if (error == HWC2::Error::None) {
-    node = new QueueNode(callback, cwb_config, buffer, display_type, node_handle_id);
+    node = std::make_shared<QueueNode>(callback, cwb_config, buffer, display_type, node_handle_id);
     if (node) {
       // Keep CWB request handling related resources in a requested display context.
       std::unique_lock<std::mutex> lock(session_map.lock);
+
+      // Iterate over the queue to avoid duplicate node of same buffer, because that
+      // buffer is already present in queue.
+      for (auto& qnode : session_map.queue) {
+        if (qnode->handle_id == node_handle_id) {
+          error = HWC2::Error::BadParameter;
+          DLOGW("CWB Buffer with handle id %lu is already available in Queue for processing!",
+                node_handle_id);
+          break;
+        }
+      }
+
       // Ensure that async task runs only until all queued CWB requests have been fulfilled.
       // If cwb queue is empty, async task has not either started or async task has finished
       // processing previously queued cwb requests. Start new async task on such a case as
       // currently running async task will automatically desolve without processing more requests.
-      session_map.queue.push_back(node);
+      if (error == HWC2::Error::None) {
+        session_map.queue.push_back(node);
+      }
     } else {
       error = HWC2::Error::BadParameter;
       DLOGE("Unable to allocate node for CWB request(handle id: %lu)!", node_handle_id);
@@ -1242,8 +1256,10 @@
           session_map.cv.notify_one();
           return 0;
         } else {
-          //Just skip notifying to client on not matching handle_id.
-          return -1;
+          // Continue to check on not matching handle_id, to update the status of any matching
+          // node, because if notification for particular handle_id skip, then it will not update
+          // again and notification thread will wait for skipped node forever.
+          continue;
         }
       }
     }
@@ -1259,7 +1275,7 @@
 void HWCSession::CWB::ProcessCWBStatus(hwc2_display_t display_type) {
   auto& session_map = display_cwb_session_map_[display_type];
   while(true) {
-    QueueNode *cwb_node = nullptr;
+    std::shared_ptr<QueueNode> cwb_node = nullptr;
     {
       std::unique_lock<std::mutex> lock(session_map.lock);
       // Exit thread in case of no pending CWB request in queue.
@@ -1276,6 +1292,13 @@
       } else if (cwb_node->notified_status == kCwbNotifiedNone) {
         // Wait for the signal for availability of CWB notified node.
         session_map.cv.wait(lock);
+        if (cwb_node->notified_status == kCwbNotifiedNone) {
+          // If any other node notified before front node, then need to continue to wait
+          // for front node, such that further client notification will be done in sequential
+          // manner.
+          DLOGW("CWB request is notified out of sequence.");
+          continue;
+        }
       }
       session_map.queue.pop_front();
     }
@@ -1283,9 +1306,10 @@
     // Notify to client, when notification is received successfully for expected input buffer.
     NotifyCWBStatus(cwb_node->notified_status , cwb_node);
   }
+  DLOGI("CWB queue is empty. Display: %d", display_type);
 }
 
-void HWCSession::CWB::NotifyCWBStatus(int status, QueueNode *cwb_node) {
+void HWCSession::CWB::NotifyCWBStatus(int status, std::shared_ptr<QueueNode> cwb_node) {
   // Notify client about buffer status and erase the node from pending request queue.
   std::shared_ptr<DisplayConfig::ConfigCallback> callback = cwb_node->callback.lock();
   if (callback) {
@@ -1295,7 +1319,6 @@
 
   native_handle_close(cwb_node->buffer);
   native_handle_delete(const_cast<native_handle_t *>(cwb_node->buffer));
-  delete cwb_node;
 }
 
 int HWCSession::NotifyCwbDone(hwc2_display_t display, int32_t status, uint64_t handle_id) {
diff --git a/sdm/libs/core/display_base.cpp b/sdm/libs/core/display_base.cpp
index 03dba0e..ee063c0 100644
--- a/sdm/libs/core/display_base.cpp
+++ b/sdm/libs/core/display_base.cpp
@@ -3837,6 +3837,9 @@
         return err;
       }
     }
+    if (state == kStateOff) {
+      *needs_refresh = false;
+    }
   }
   if (*needs_refresh) {
     validated_ = false;