sdm: Add support for HDMI minimum HDCP encryption level change.

- Enable HDCP libraries to communicate with SDM via a qdutils API to
  indicate change in encryption level.
- Write this change on HDMI file node to trigger driver.

Change-Id: I226d4e986081b97243c80ea30c16b05ea34499c4
diff --git a/libqdutils/display_config.cpp b/libqdutils/display_config.cpp
index 7849d7f..8f77c66 100644
--- a/libqdutils/display_config.cpp
+++ b/libqdutils/display_config.cpp
@@ -280,6 +280,26 @@
     return err;
 }
 
+int minHdcpEncryptionLevelChanged(int dpy) {
+    status_t err = (status_t) FAILED_TRANSACTION;
+    sp<IQService> binder = getBinder();
+    Parcel inParcel, outParcel;
+    inParcel.writeInt32(dpy);
+
+    if(binder != NULL) {
+        err = binder->dispatch(IQService::MIN_HDCP_ENCRYPTION_LEVEL_CHANGED,
+                &inParcel, &outParcel);
+    }
+
+    if(err) {
+        ALOGE("%s: Failed for dpy %d err=%d", __FUNCTION__, dpy, err);
+    } else {
+        err = outParcel.readInt32();
+    }
+
+    return err;
+}
+
 }// namespace
 
 // ----------------------------------------------------------------------------
diff --git a/libqdutils/display_config.h b/libqdutils/display_config.h
index 768fe6c..c5e7340 100644
--- a/libqdutils/display_config.h
+++ b/libqdutils/display_config.h
@@ -136,6 +136,11 @@
 // Set the primary display mode to command or video mode
 int setDisplayMode(int mode);
 
+// Notify change in minimum encryption level for HDCP
+// Return -1 on error.
+// Only HDMI display is supported as dpy for now
+int minHdcpEncryptionLevelChanged(int dpy);
+
 //=============================================================================
 // The functions and methods below run in the context of HWC and
 // are called in response to binder calls from clients
diff --git a/libqservice/IQService.h b/libqservice/IQService.h
index 4c0271a..df8e235 100644
--- a/libqservice/IQService.h
+++ b/libqservice/IQService.h
@@ -70,6 +70,7 @@
         GET_DISPLAY_ATTRIBUTES_FOR_CONFIG = 28, //Get attr for specified config
         SET_DISPLAY_MODE = 29, // Set display mode to command or video mode
         SET_CAMERA_STATUS = 30, // To notify display when camera is on and off
+        MIN_HDCP_ENCRYPTION_LEVEL_CHANGED = 31,
         COMMAND_LIST_END = 400,
     };
 
diff --git a/sdm/include/core/display_interface.h b/sdm/include/core/display_interface.h
index 0c6f4fd..140ac77 100644
--- a/sdm/include/core/display_interface.h
+++ b/sdm/include/core/display_interface.h
@@ -373,6 +373,12 @@
   */
   virtual DisplayError SetPanelBrightness(int level) = 0;
 
+  /*! @brief Method to notify display about change in min HDCP encryption level.
+
+    @return \link DisplayError \endlink
+  */
+  virtual DisplayError OnMinHdcpEncryptionLevelChange() = 0;
+
   /*! @brief Method to route display API requests to color service.
 
     @param[in] in_payload \link PPDisplayAPIPayload \endlink
diff --git a/sdm/libs/core/display_base.cpp b/sdm/libs/core/display_base.cpp
index ca81fdc..118c036 100644
--- a/sdm/libs/core/display_base.cpp
+++ b/sdm/libs/core/display_base.cpp
@@ -471,6 +471,10 @@
   return kErrorNotSupported;
 }
 
+DisplayError DisplayBase::OnMinHdcpEncryptionLevelChange() {
+  return kErrorNotSupported;
+}
+
 void DisplayBase::AppendDump(char *buffer, uint32_t length) {
   DumpImpl::AppendString(buffer, length, "\n-----------------------");
   DumpImpl::AppendString(buffer, length, "\ndevice type: %u", display_type_);
diff --git a/sdm/libs/core/display_base.h b/sdm/libs/core/display_base.h
index 6f75a27..75c1f8c 100644
--- a/sdm/libs/core/display_base.h
+++ b/sdm/libs/core/display_base.h
@@ -66,6 +66,7 @@
   virtual DisplayError IsScalingValid(const LayerRect &crop, const LayerRect &dst, bool rotate90);
   virtual bool IsUnderscanSupported();
   virtual DisplayError SetPanelBrightness(int level);
+  virtual DisplayError OnMinHdcpEncryptionLevelChange();
   virtual DisplayError ColorSVCRequestRoute(const PPDisplayAPIPayload &in_payload,
                                             PPDisplayAPIPayload *out_payload,
                                             PPPendingParams *pending_action);
diff --git a/sdm/libs/core/display_hdmi.cpp b/sdm/libs/core/display_hdmi.cpp
index c1ad8cd..dcabb7b 100644
--- a/sdm/libs/core/display_hdmi.cpp
+++ b/sdm/libs/core/display_hdmi.cpp
@@ -176,6 +176,11 @@
   return DisplayBase::SetPanelBrightness(level);
 }
 
+DisplayError DisplayHDMI::OnMinHdcpEncryptionLevelChange() {
+  SCOPE_LOCK(locker_);
+  return hw_hdmi_intf_->OnMinHdcpEncryptionLevelChange();
+}
+
 int DisplayHDMI::GetBestConfig() {
   uint32_t best_config_mode = 0;
   HWDisplayAttributes *best = &display_attributes_[0];
diff --git a/sdm/libs/core/display_hdmi.h b/sdm/libs/core/display_hdmi.h
index f22eca4..8e96828 100644
--- a/sdm/libs/core/display_hdmi.h
+++ b/sdm/libs/core/display_hdmi.h
@@ -59,6 +59,7 @@
   virtual DisplayError SetRefreshRate(uint32_t refresh_rate);
   virtual bool IsUnderscanSupported();
   virtual DisplayError SetPanelBrightness(int level);
+  virtual DisplayError OnMinHdcpEncryptionLevelChange();
   virtual void AppendDump(char *buffer, uint32_t length);
   virtual DisplayError SetCursorPosition(int x, int y);
 
diff --git a/sdm/libs/core/fb/hw_hdmi.cpp b/sdm/libs/core/fb/hw_hdmi.cpp
index 6ab3980..56b329a 100644
--- a/sdm/libs/core/fb/hw_hdmi.cpp
+++ b/sdm/libs/core/fb/hw_hdmi.cpp
@@ -376,6 +376,33 @@
   return kErrorNone;
 }
 
+DisplayError HWHDMI::OnMinHdcpEncryptionLevelChange() {
+  DisplayError error = kErrorNone;
+  int fd = -1;
+  char data[kMaxStringLength] = {'\0'};
+
+  snprintf(data, sizeof(data), "%s%d/hdcp2p2/min_level_change", fb_path_, fb_node_index_);
+
+  fd = Sys::open_(data, O_WRONLY);
+  if (fd < 0) {
+    DLOGW("File '%s' could not be opened.", data);
+    return kErrorHardware;
+  }
+
+  // write any value (1 here) on this fd to trigger level change.
+  snprintf(data, sizeof(data), "%d", 1);
+
+  ssize_t err = Sys::pwrite_(fd, data, strlen(data), 0);
+  if (err <= 0) {
+    DLOGE("Write failed, Error = %s", strerror(errno));
+    error = kErrorHardware;
+  }
+
+  Sys::close_(fd);
+
+  return error;
+}
+
 HWScanSupport HWHDMI::MapHWScanSupport(uint32_t value) {
   switch (value) {
   // TODO(user): Read the scan type from driver defined values instead of hardcoding
@@ -396,7 +423,7 @@
 void HWHDMI::ReadScanInfo() {
   int scan_info_file = -1;
   ssize_t len = -1;
-  char data[4096] = {'\0'};
+  char data[kPageSize] = {'\0'};
 
   snprintf(data, sizeof(data), "%s%d/scan_info", fb_path_, fb_node_index_);
   scan_info_file = Sys::open_(data, O_RDONLY);
@@ -406,7 +433,7 @@
   }
 
   memset(&data[0], 0, sizeof(data));
-  len = read(scan_info_file, data, sizeof(data) - 1);
+  len = Sys::pread_(scan_info_file, data, sizeof(data) - 1, 0);
   if (len <= 0) {
     Sys::close_(scan_info_file);
     DLOGW("File %s%d/scan_info is empty.", fb_path_, fb_node_index_);
@@ -469,7 +496,8 @@
   if (err <= 0) {
     DLOGE("Write to res_info failed (%s)", strerror(errno));
   }
-  close(fd);
+
+  Sys::close_(fd);
 }
 
 // Reads the contents of res_info node into a buffer if the file is not empty
@@ -484,7 +512,8 @@
   if ((bytes_read = Sys::pread_(fd, config_buffer, kPageSize, 0)) != 0) {
     is_file_read = true;
   }
-  close(fd);
+
+  Sys::close_(fd);
 
   DLOGI_IF(kTagDriverConfig, "bytes_read=%d is_file_read=%d", bytes_read, is_file_read);
 
diff --git a/sdm/libs/core/fb/hw_hdmi.h b/sdm/libs/core/fb/hw_hdmi.h
index 50d793d..2d2d557 100644
--- a/sdm/libs/core/fb/hw_hdmi.h
+++ b/sdm/libs/core/fb/hw_hdmi.h
@@ -45,6 +45,7 @@
   virtual DisplayError GetHWScanInfo(HWScanInfo *scan_info);
   virtual DisplayError GetVideoFormat(uint32_t config_index, uint32_t *video_format);
   virtual DisplayError GetMaxCEAFormat(uint32_t *max_cea_format);
+  virtual DisplayError OnMinHdcpEncryptionLevelChange();
   virtual DisplayError SetDisplayAttributes(uint32_t index);
   virtual DisplayError GetConfigIndex(uint32_t mode, uint32_t *index);
   virtual DisplayError PowerOn();
diff --git a/sdm/libs/core/hw_hdmi_interface.h b/sdm/libs/core/hw_hdmi_interface.h
index 0d3d800..3b177b4 100644
--- a/sdm/libs/core/hw_hdmi_interface.h
+++ b/sdm/libs/core/hw_hdmi_interface.h
@@ -39,6 +39,7 @@
   virtual DisplayError GetHWScanInfo(HWScanInfo *scan_info) = 0;
   virtual DisplayError GetVideoFormat(uint32_t config_index, uint32_t *video_format) = 0;
   virtual DisplayError GetMaxCEAFormat(uint32_t *max_cea_format) = 0;
+  virtual DisplayError OnMinHdcpEncryptionLevelChange() = 0;
 
  protected:
   virtual ~HWHDMIInterface() { }
diff --git a/sdm/libs/hwc/hwc_display.cpp b/sdm/libs/hwc/hwc_display.cpp
index 14b4f3f..3f73064 100644
--- a/sdm/libs/hwc/hwc_display.cpp
+++ b/sdm/libs/hwc/hwc_display.cpp
@@ -1192,6 +1192,15 @@
   return 0;
 }
 
+int HWCDisplay::OnMinHdcpEncryptionLevelChange() {
+  DisplayError error = display_intf_->OnMinHdcpEncryptionLevelChange();
+  if (error != kErrorNone) {
+    DLOGE("Failed. Error = %d", error);
+    return -1;
+  }
+
+  return 0;
+}
 
 void HWCDisplay::MarkLayersForGPUBypass(hwc_display_contents_1_t *content_list) {
   for (size_t i = 0 ; i < (content_list->numHwLayers - 1); i++) {
diff --git a/sdm/libs/hwc/hwc_display.h b/sdm/libs/hwc/hwc_display.h
index b546ee2..a7386a4 100644
--- a/sdm/libs/hwc/hwc_display.h
+++ b/sdm/libs/hwc/hwc_display.h
@@ -55,6 +55,7 @@
   virtual void GetFrameBufferResolution(uint32_t *x_pixels, uint32_t *y_pixels);
   virtual void GetPanelResolution(uint32_t *x_pixels, uint32_t *y_pixels);
   virtual int SetDisplayStatus(uint32_t display_status);
+  virtual int OnMinHdcpEncryptionLevelChange();
   virtual int Perform(uint32_t operation, ...);
   virtual int SetCursorPosition(int x, int y);
   virtual ~HWCDisplay() { }
diff --git a/sdm/libs/hwc/hwc_session.cpp b/sdm/libs/hwc/hwc_session.cpp
index 38b1eb8..af2d6f1 100644
--- a/sdm/libs/hwc/hwc_session.cpp
+++ b/sdm/libs/hwc/hwc_session.cpp
@@ -541,7 +541,11 @@
     break;
 
   case qService::IQService::QDCM_SVC_CMDS:
-    status = QdcmCMDHandler(*input_parcel, output_parcel);
+    status = QdcmCMDHandler(input_parcel, output_parcel);
+    break;
+
+  case qService::IQService::MIN_HDCP_ENCRYPTION_LEVEL_CHANGED:
+    status = OnMinHdcpEncryptionLevelChange(input_parcel, output_parcel);
     break;
 
   case qService::IQService::CONTROL_PARTIAL_UPDATE:
@@ -784,7 +788,8 @@
   }
 }
 
-android::status_t HWCSession::QdcmCMDHandler(const android::Parcel &in, android::Parcel *out) {
+android::status_t HWCSession::QdcmCMDHandler(const android::Parcel *input_parcel,
+                                             android::Parcel *output_parcel) {
   int ret = 0;
   int32_t *brightness_value = NULL;
   uint32_t display_id(0);
@@ -792,7 +797,7 @@
   PPDisplayAPIPayload resp_payload, req_payload;
 
   // Read display_id, payload_size and payload from in_parcel.
-  ret = HWCColorManager::CreatePayloadFromParcel(in, &display_id, &req_payload);
+  ret = HWCColorManager::CreatePayloadFromParcel(*input_parcel, &display_id, &req_payload);
   if (!ret) {
     if (HWC_DISPLAY_PRIMARY == display_id && hwc_display_[HWC_DISPLAY_PRIMARY])
       ret = hwc_display_[HWC_DISPLAY_PRIMARY]->ColorSVCRequestRoute(req_payload,
@@ -804,7 +809,7 @@
   }
 
   if (ret) {
-    out->writeInt32(ret);  // first field in out parcel indicates return code.
+    output_parcel->writeInt32(ret);  // first field in out parcel indicates return code.
     req_payload.DestroyPayload();
     resp_payload.DestroyPayload();
     return ret;
@@ -847,14 +852,33 @@
   }
 
   // for display API getter case, marshall returned params into out_parcel.
-  out->writeInt32(ret);
-  HWCColorManager::MarshallStructIntoParcel(resp_payload, out);
+  output_parcel->writeInt32(ret);
+  HWCColorManager::MarshallStructIntoParcel(resp_payload, output_parcel);
   req_payload.DestroyPayload();
   resp_payload.DestroyPayload();
 
   return (ret? -EINVAL : 0);
 }
 
+android::status_t HWCSession::OnMinHdcpEncryptionLevelChange(const android::Parcel *input_parcel,
+                                                             android::Parcel *output_parcel) {
+  int ret = -EINVAL;
+  uint32_t display_id = UINT32(input_parcel->readInt32());
+
+  DLOGI("Display %d", display_id);
+  if (display_id != HWC_DISPLAY_EXTERNAL) {
+    DLOGW("Not supported for display %d", display_id);
+  } else if (!hwc_display_[display_id]) {
+    DLOGE("Display %d is not connected", display_id);
+  } else {
+    ret = hwc_display_[display_id]->OnMinHdcpEncryptionLevelChange();
+  }
+
+  output_parcel->writeInt32(ret);
+
+  return ret;
+}
+
 void* HWCSession::HWCUeventThread(void *context) {
   if (context) {
     return reinterpret_cast<HWCSession *>(context)->HWCUeventThreadHandler();
diff --git a/sdm/libs/hwc/hwc_session.h b/sdm/libs/hwc/hwc_session.h
index a3ac07c..def7616 100644
--- a/sdm/libs/hwc/hwc_session.h
+++ b/sdm/libs/hwc/hwc_session.h
@@ -91,8 +91,11 @@
   android::status_t SetSecondaryDisplayStatus(const android::Parcel *input_parcel);
   android::status_t ControlBackLight(const android::Parcel *input_parcel);
   android::status_t ConfigureRefreshRate(const android::Parcel *input_parcel);
-  android::status_t QdcmCMDHandler(const android::Parcel &in, android::Parcel *out);
+  android::status_t QdcmCMDHandler(const android::Parcel *input_parcel,
+                                   android::Parcel *output_parcel);
   android::status_t ControlPartialUpdate(const android::Parcel *input_parcel, android::Parcel *out);
+  android::status_t OnMinHdcpEncryptionLevelChange(const android::Parcel *input_parcel,
+                                   android::Parcel *output_parcel);
 
   static Locker locker_;
   CoreInterface *core_intf_;