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 {