Add external physical display emulation
Use a system property, hwservicemanager.external.displays, to configure
external physical displays. Serveral displays can be specified with
comma (,) as separator, each display has the following info:
physicalId,width,height,dpi,flags
For example, following specifies 2 displays:
PRODUCT_PRODUCT_PROPERTIES += \
hwservicemanager.external.displays=1,1200,800,120,0,2,1200,800,120,0
Test: manual
Change-Id: I4cc5b20226333971bfd0cafde743ed7b4d2232b0
diff --git a/system/hwc2/EmuHWC2.cpp b/system/hwc2/EmuHWC2.cpp
index 31e0789..3bda1a1 100644
--- a/system/hwc2/EmuHWC2.cpp
+++ b/system/hwc2/EmuHWC2.cpp
@@ -21,6 +21,7 @@
#define LOG_TAG "EmuHWC2"
#include <errno.h>
+#include <cutils/properties.h>
#include <log/log.h>
#include <sync/sync.h>
@@ -350,6 +351,13 @@
lock.unlock();
auto hotplug = reinterpret_cast<HWC2_PFN_VSYNC>(pointer);
hotplug(callbackData, 0, static_cast<int32_t>(Connection::Connected));
+
+ int ret = populateSecondaryDisplays();
+ if (ret < 0) {
+ ALOGE("Failed to populate secondary displays");
+ } else if (ret == 0) {
+ hotplug(callbackData, 1, static_cast<int32_t>(Connection::Connected));
+ }
}
return Error::None;
@@ -1138,6 +1146,42 @@
return 0;
}
+HWC2::Error EmuHWC2::Display::populateSecondaryConfigs(uint32_t width, uint32_t height,
+ uint32_t dpi) {
+ ALOGVV("%s DisplayId %u, width %u, height %u, dpi %u",
+ __FUNCTION__, (uint32_t)mId, width, height, dpi);
+ std::unique_lock<std::mutex> lock(mStateMutex);
+
+ mGralloc.reset(new GrallocModule());
+ auto newConfig = std::make_shared<Config>(*this);
+ // vsync is 60 hz;
+ newConfig->setAttribute(Attribute::VsyncPeriod, mVsyncPeriod);
+ newConfig->setAttribute(Attribute::Width, width);
+ newConfig->setAttribute(Attribute::Height, height);
+ newConfig->setAttribute(Attribute::DpiX, dpi*1000);
+ newConfig->setAttribute(Attribute::DpiY, dpi*1000);
+
+ newConfig->setId(static_cast<hwc2_config_t>(mConfigs.size()));
+ ALOGV("Found new secondary config %d: %s", (uint32_t)newConfig->getId(),
+ newConfig->toString().c_str());
+ mConfigs.emplace_back(std::move(newConfig));
+
+ // we need to reset these values after populatePrimaryConfigs()
+ mActiveConfig = mConfigs[0];
+ mActiveColorMode = HAL_COLOR_MODE_NATIVE;
+ mColorModes.emplace((android_color_mode_t)HAL_COLOR_MODE_NATIVE);
+
+ uint32_t displayId = 0;
+ DEFINE_AND_VALIDATE_HOST_CONNECTION
+
+ hostCon->lock();
+ rcEnc->rcCreateDisplay(rcEnc, &displayId);
+ rcEnc->rcSetDisplayPose(rcEnc, displayId, -1, -1, width, height);
+ hostCon->unlock();
+
+ return HWC2::Error::None;
+}
+
// Config functions
@@ -1397,6 +1441,70 @@
return ret;
}
+// Note "hwservicemanager." is used to avoid selinux issue
+#define EXTERANL_DISPLAY_PROP "hwservicemanager.external.displays"
+
+// return 0 for successful, 1 if no external displays are specified
+// return < 0 if failed
+int EmuHWC2::populateSecondaryDisplays() {
+ // this guest property, hwservicemanager.external.displays,
+ // specifies multi-display info, with comma (,) as separator
+ // each display has the following info:
+ // physicalId,width,height,dpi,flags
+ // serveral displays can be provided, e.g., following has 2 displays:
+ // setprop hwservicemanager.external.displays 1,1200,800,120,0,2,1200,800,120,0
+ std::vector<uint64_t> values;
+ char displaysValue[PROPERTY_VALUE_MAX] = "";
+ property_get(EXTERANL_DISPLAY_PROP, displaysValue, "");
+ bool isValid = displaysValue[0] != '\0';
+ if (isValid) {
+ char *p = displaysValue;
+ while (*p) {
+ if (!isdigit(*p) && *p != ',' && *p != ' ') {
+ isValid = false;
+ break;
+ }
+ p ++;
+ }
+ if (!isValid) {
+ ALOGE("Invalid syntax for the value of system prop: %s", EXTERANL_DISPLAY_PROP);
+ }
+ }
+ if (!isValid) {
+ // no external displays are specified
+ return 1;
+ }
+ // parse all int values to a vector
+ std::istringstream stream(displaysValue);
+ for (uint64_t id; stream >> id;) {
+ values.push_back(id);
+ if (stream.peek() == ',')
+ stream.ignore();
+ }
+ // each display has 5 values
+ if ((values.size() % 5) != 0) {
+ ALOGE("%s: invalid value for system property: %s", __FUNCTION__, EXTERANL_DISPLAY_PROP);
+ return -1;
+ }
+ while (!values.empty()) {
+ uint64_t physicalId = values[0];
+ uint32_t width = values[1];
+ uint32_t height = values[2];
+ uint32_t dpi = values[3];
+ uint32_t flags = values[4];
+ values.erase(values.begin(), values.begin() + 5);
+
+ Error ret = Error::None;
+ auto display = std::make_shared<Display>(*this, HWC2::DisplayType::Physical);
+ ret = display->populateSecondaryConfigs(width, height, dpi);
+ if (ret != Error::None) {
+ return -2;
+ }
+ mDisplays.emplace(display->getId(), std::move(display));
+ }
+ return 0;
+}
+
EmuHWC2::Display* EmuHWC2::getDisplay(hwc2_display_t id) {
auto display = mDisplays.find(id);
if (display == mDisplays.end()) {
diff --git a/system/hwc2/EmuHWC2.h b/system/hwc2/EmuHWC2.h
index 899d734..1c0dd51 100644
--- a/system/hwc2/EmuHWC2.h
+++ b/system/hwc2/EmuHWC2.h
@@ -27,6 +27,7 @@
#include <atomic>
#include <map>
#include <mutex>
+#include <sstream>
#include <vector>
#include <unordered_set>
#include <unordered_map>
@@ -42,6 +43,7 @@
public:
EmuHWC2();
int populatePrimary();
+ int populateSecondaryDisplays();
private:
static inline EmuHWC2* getHWC2(hwc2_device_t* device) {
@@ -258,6 +260,8 @@
// Read configs from PRIMARY Display
int populatePrimaryConfigs();
+ HWC2::Error populateSecondaryConfigs(uint32_t width, uint32_t height,
+ uint32_t dpi);
private:
class Config {