[SurfaceFlinger] add getDisplayedContentSample i/f

Add interface to ISurfaceComposer that can query the
graphics.composer for statistics on the displayed pixel content.

Bug: 116028618
Test: Boot
Test: ran test client, see data collected
Change-Id: Ide9b81b80c3399e7d648c7b611514e0d699120de
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 7d26151..f1fefcc 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -651,6 +651,49 @@
                                    &reply);
         return result;
     }
+
+    virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
+                                               uint64_t timestamp,
+                                               DisplayedFrameStats* outStats) const {
+        if (!outStats) return BAD_VALUE;
+
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        data.writeStrongBinder(display);
+        data.writeUint64(maxFrames);
+        data.writeUint64(timestamp);
+
+        status_t result =
+                remote()->transact(BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLE, data, &reply);
+
+        if (result != NO_ERROR) {
+            return result;
+        }
+
+        result = reply.readUint64(&outStats->numFrames);
+        if (result != NO_ERROR) {
+            return result;
+        }
+
+        result = reply.readInt64Vector(
+                reinterpret_cast<std::vector<int64_t>*>(&outStats->component_0_sample));
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.readInt64Vector(
+                reinterpret_cast<std::vector<int64_t>*>(&outStats->component_1_sample));
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.readInt64Vector(
+                reinterpret_cast<std::vector<int64_t>*>(&outStats->component_2_sample));
+        if (result != NO_ERROR) {
+            return result;
+        }
+        result = reply.readInt64Vector(
+                reinterpret_cast<std::vector<int64_t>*>(&outStats->component_3_sample));
+        return result;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -1055,6 +1098,40 @@
             return setDisplayContentSamplingEnabled(display, enable,
                                                     static_cast<uint8_t>(componentMask), maxFrames);
         }
+        case GET_DISPLAYED_CONTENT_SAMPLE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+            sp<IBinder> display = data.readStrongBinder();
+            uint64_t maxFrames = 0;
+            uint64_t timestamp = 0;
+
+            status_t result = data.readUint64(&maxFrames);
+            if (result != NO_ERROR) {
+                ALOGE("getDisplayedContentSample failure in reading max frames: %d", result);
+                return result;
+            }
+
+            result = data.readUint64(&timestamp);
+            if (result != NO_ERROR) {
+                ALOGE("getDisplayedContentSample failure in reading timestamp: %d", result);
+                return result;
+            }
+
+            DisplayedFrameStats stats;
+            result = getDisplayedContentSample(display, maxFrames, timestamp, &stats);
+            if (result == NO_ERROR) {
+                reply->writeUint64(stats.numFrames);
+                reply->writeInt64Vector(
+                        *reinterpret_cast<std::vector<int64_t>*>(&stats.component_0_sample));
+                reply->writeInt64Vector(
+                        *reinterpret_cast<std::vector<int64_t>*>(&stats.component_1_sample));
+                reply->writeInt64Vector(
+                        *reinterpret_cast<std::vector<int64_t>*>(&stats.component_2_sample));
+                reply->writeInt64Vector(
+                        *reinterpret_cast<std::vector<int64_t>*>(&stats.component_3_sample));
+            }
+            return result;
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 405d228..070b253 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1106,6 +1106,12 @@
                                                                                    maxFrames);
 }
 
+status_t SurfaceComposerClient::getDisplayedContentSample(const sp<IBinder>& display,
+                                                          uint64_t maxFrames, uint64_t timestamp,
+                                                          DisplayedFrameStats* outStats) {
+    return ComposerService::getComposerService()->getDisplayedContentSample(display, maxFrames,
+                                                                            timestamp, outStats);
+}
 // ----------------------------------------------------------------------------
 
 status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 41369c8..3052c0b 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -27,10 +27,11 @@
 
 #include <binder/IInterface.h>
 
+#include <ui/DisplayedFrameStats.h>
 #include <ui/FrameStats.h>
-#include <ui/PixelFormat.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicTypes.h>
+#include <ui/PixelFormat.h>
 
 #include <vector>
 
@@ -308,6 +309,14 @@
     virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
                                                       uint8_t componentMask,
                                                       uint64_t maxFrames) const = 0;
+
+    /* Returns statistics on the color profile of the last frame displayed for a given display
+     *
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
+                                               uint64_t timestamp,
+                                               DisplayedFrameStats* outStats) const = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -349,6 +358,7 @@
         GET_COLOR_MANAGEMENT,
         GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES,
         SET_DISPLAY_CONTENT_SAMPLING_ENABLED,
+        GET_DISPLAYED_CONTENT_SAMPLE,
     };
 
     virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index ba943a0..2b107aa 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -30,6 +30,7 @@
 #include <utils/SortedVector.h>
 #include <utils/threads.h>
 
+#include <ui/DisplayedFrameStats.h>
 #include <ui/FrameStats.h>
 #include <ui/GraphicTypes.h>
 #include <ui/PixelFormat.h>
@@ -389,6 +390,9 @@
     static status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
                                                      uint8_t componentMask, uint64_t maxFrames);
 
+    static status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
+                                              uint64_t timestamp, DisplayedFrameStats* outStats);
+
 private:
     virtual void onFirstRef();
 
diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp
index f9d5dd6..5443812 100644
--- a/libs/gui/tests/DisplayedContentSampling_test.cpp
+++ b/libs/gui/tests/DisplayedContentSampling_test.cpp
@@ -104,4 +104,19 @@
                                                                0);
     EXPECT_EQ(OK, status);
 }
+
+TEST_F(DisplayedContentSamplingTest, SampleCollectionCoherentWithSupportMask) {
+    if (shouldSkipTest()) return;
+
+    DisplayedFrameStats stats;
+    status_t status = mComposerClient->getDisplayedContentSample(mDisplayToken, 0, 0, &stats);
+    EXPECT_EQ(OK, status);
+    if (stats.numFrames <= 0) return;
+
+    if (componentMask & (0x1 << 0)) EXPECT_NE(0, stats.component_0_sample.size());
+    if (componentMask & (0x1 << 1)) EXPECT_NE(0, stats.component_1_sample.size());
+    if (componentMask & (0x1 << 2)) EXPECT_NE(0, stats.component_2_sample.size());
+    if (componentMask & (0x1 << 3)) EXPECT_NE(0, stats.component_3_sample.size());
+}
+
 } // namespace android
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index cb1756f..d37b810 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -652,6 +652,11 @@
                                               uint64_t /*maxFrames*/) const override {
         return NO_ERROR;
     }
+    status_t getDisplayedContentSample(const sp<IBinder>& /*display*/, uint64_t /*maxFrames*/,
+                                       uint64_t /*timestamp*/,
+                                       DisplayedFrameStats* /*outStats*/) const override {
+        return NO_ERROR;
+    }
 
     virtual status_t getColorManagement(bool* /*outGetColorManagement*/) const { return NO_ERROR; }
 
diff --git a/libs/ui/include/ui/DisplayedFrameStats.h b/libs/ui/include/ui/DisplayedFrameStats.h
new file mode 100644
index 0000000..7a70ea1
--- /dev/null
+++ b/libs/ui/include/ui/DisplayedFrameStats.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+namespace android {
+
+struct DisplayedFrameStats {
+    /* The number of frames represented by this sample. */
+    uint64_t numFrames = 0;
+    /* A histogram counting how many times a pixel of a given value was displayed onscreen for
+     * FORMAT_COMPONENT_0. The buckets of the histogram are evenly weighted, the number of buckets
+     * is device specific. eg, for RGBA_8888, if sampleComponent0 is {10, 6, 4, 1} this means that
+     * 10 red pixels were displayed onscreen in range 0x00->0x3F, 6 red pixels
+     * were displayed onscreen in range 0x40->0x7F, etc.
+     */
+    std::vector<uint64_t> component_0_sample = {};
+    /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_1. */
+    std::vector<uint64_t> component_1_sample = {};
+    /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_2. */
+    std::vector<uint64_t> component_2_sample = {};
+    /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_3. */
+    std::vector<uint64_t> component_3_sample = {};
+};
+
+} // namespace android
diff --git a/libs/ui/include_vndk/ui/DisplayedFrameStats.h b/libs/ui/include_vndk/ui/DisplayedFrameStats.h
new file mode 120000
index 0000000..6014e19
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayedFrameStats.h
@@ -0,0 +1 @@
+../../include/ui/DisplayedFrameStats.h
\ No newline at end of file
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 3b7ed15..d6237cb 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -1079,6 +1079,31 @@
                                                            maxFrames);
 }
 
+Error Composer::getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
+                                          DisplayedFrameStats* outStats) {
+    if (!outStats) {
+        return Error::BAD_PARAMETER;
+    }
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+    Error error = kDefaultError;
+    mClient_2_3->getDisplayedContentSample(display, maxFrames, timestamp,
+                                           [&](const auto tmpError, auto tmpNumFrames,
+                                               const auto& tmpSamples0, const auto& tmpSamples1,
+                                               const auto& tmpSamples2, const auto& tmpSamples3) {
+                                               error = tmpError;
+                                               if (error == Error::NONE) {
+                                                   outStats->numFrames = tmpNumFrames;
+                                                   outStats->component_0_sample = tmpSamples0;
+                                                   outStats->component_1_sample = tmpSamples1;
+                                                   outStats->component_2_sample = tmpSamples2;
+                                                   outStats->component_3_sample = tmpSamples3;
+                                               }
+                                           });
+    return error;
+}
+
 CommandReader::~CommandReader()
 {
     resetData();
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 0db12a1..38ee7ad 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -30,6 +30,7 @@
 #include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
+#include <ui/DisplayedFrameStats.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/StrongPointer.h>
 
@@ -194,6 +195,8 @@
                                                         uint8_t* outComponentMask) = 0;
     virtual Error setDisplayContentSamplingEnabled(Display display, bool enabled,
                                                    uint8_t componentMask, uint64_t maxFrames) = 0;
+    virtual Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
+                                            DisplayedFrameStats* outStats) = 0;
     virtual Error getDisplayCapabilities(Display display,
                                          std::vector<DisplayCapability>* outCapabilities) = 0;
 };
@@ -400,6 +403,8 @@
                                                 uint8_t* outComponentMask) override;
     Error setDisplayContentSamplingEnabled(Display display, bool enabled, uint8_t componentMask,
                                            uint64_t maxFrames) override;
+    Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
+                                    DisplayedFrameStats* outStats) override;
     Error getDisplayCapabilities(Display display,
                                  std::vector<DisplayCapability>* outCapabilities) override;
 
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 733a5da..d2aa4ad 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -562,6 +562,12 @@
     return static_cast<Error>(intError);
 }
 
+Error Display::getDisplayedContentSample(uint64_t maxFrames, uint64_t timestamp,
+                                         android::DisplayedFrameStats* outStats) const {
+    auto intError = mComposer.getDisplayedContentSample(mId, maxFrames, timestamp, outStats);
+    return static_cast<Error>(intError);
+}
+
 Error Display::getReleaseFences(
         std::unordered_map<Layer*, sp<Fence>>* outFences) const
 {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 2d65051..f5cb97e 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -40,6 +40,7 @@
 #include "PowerAdvisor.h"
 
 namespace android {
+    struct DisplayedFrameStats;
     class Fence;
     class FloatRect;
     class GraphicBuffer;
@@ -243,6 +244,8 @@
     [[clang::warn_unused_result]] Error setDisplayContentSamplingEnabled(bool enabled,
                                                                          uint8_t componentMask,
                                                                          uint64_t maxFrames) const;
+    [[clang::warn_unused_result]] Error getDisplayedContentSample(
+            uint64_t maxFrames, uint64_t timestamp, android::DisplayedFrameStats* outStats) const;
     [[clang::warn_unused_result]] Error getReleaseFences(
             std::unordered_map<Layer*,
                     android::sp<android::Fence>>* outFences) const;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index b27344d..dc6faad 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -770,6 +770,16 @@
     return NO_ERROR;
 }
 
+status_t HWComposer::getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames,
+                                               uint64_t timestamp, DisplayedFrameStats* outStats) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error =
+            mDisplayData[displayId].hwcDisplay->getDisplayedContentSample(maxFrames, timestamp,
+                                                                          outStats);
+    RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+    return NO_ERROR;
+}
+
 bool HWComposer::isUsingVrComposer() const {
     return getComposer()->isUsingVrComposer();
 }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 3f1328e..4d0694e 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -36,6 +36,7 @@
 
 namespace android {
 
+struct DisplayedFrameStats;
 class GraphicBuffer;
 class String8;
 class TestableSurfaceFlinger;
@@ -133,6 +134,8 @@
                                                    uint8_t* outComponentMask);
     status_t setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled,
                                               uint8_t componentMask, uint64_t maxFrames);
+    status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames, uint64_t timestamp,
+                                       DisplayedFrameStats* outStats);
 
     // Events handling ---------------------------------------------------------
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0bda020..90eccd2 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1145,6 +1145,19 @@
                                                             componentMask, maxFrames);
 }
 
+status_t SurfaceFlinger::getDisplayedContentSample(const sp<IBinder>& displayToken,
+                                                   uint64_t maxFrames, uint64_t timestamp,
+                                                   DisplayedFrameStats* outStats) const {
+    const auto display = getDisplayDevice(displayToken);
+    if (!display || !display->getId()) {
+        ALOGE("getDisplayContentSample: Bad display token: %p", displayToken.get());
+        return BAD_VALUE;
+    }
+
+    return getHwComposer().getDisplayedContentSample(*display->getId(), maxFrames, timestamp,
+                                                     outStats);
+}
+
 status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
     postMessageSync(new LambdaMessage([&] {
         Mutex::Autolock _l(mStateLock);
@@ -4791,7 +4804,8 @@
         case INJECT_VSYNC:
         case SET_POWER_MODE:
         case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
-        case SET_DISPLAY_CONTENT_SAMPLING_ENABLED: {
+        case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
+        case GET_DISPLAYED_CONTENT_SAMPLE: {
             if (!callingThreadHasUnscopedSurfaceFlingerAccess()) {
                 IPCThreadState* ipc = IPCThreadState::self();
                 ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index fe2f1c26..ba92a8f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -486,6 +486,9 @@
     virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
                                                       uint8_t componentMask,
                                                       uint64_t maxFrames) const override;
+    virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
+                                               uint64_t timestamp,
+                                               DisplayedFrameStats* outStats) const override;
 
     /* ------------------------------------------------------------------------
      * DeathRecipient interface
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 68fd8b4..dfdda09 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -116,6 +116,8 @@
     MOCK_METHOD4(getDisplayedContentSamplingAttributes,
                  Error(Display, PixelFormat*, Dataspace*, uint8_t*));
     MOCK_METHOD4(setDisplayContentSamplingEnabled, Error(Display, bool, uint8_t, uint64_t));
+    MOCK_METHOD4(getDisplayedContentSample,
+                 Error(Display, uint64_t, uint64_t, DisplayedFrameStats*));
     MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*));
 };