[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(×tamp);
+ 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>*));
};