Merge "Add additional predictor unit tests" into sc-dev
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index c62d302..0712c0a 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -411,7 +411,6 @@
}
TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
- SectionExists("HIGH connectivity", /* bytes= */ 3000);
SectionExists("connectivity", /* bytes= */ 5000);
}
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index eb3b434..026b19a 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -84,6 +84,7 @@
name: "librenderengine_skia_sources",
srcs: [
"skia/AutoBackendTexture.cpp",
+ "skia/Cache.cpp",
"skia/ColorSpaces.cpp",
"skia/SkiaRenderEngine.cpp",
"skia/SkiaGLRenderEngine.cpp",
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 2b09c15..a2963a7 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -515,7 +515,7 @@
return mDrawingBuffer.get();
}
-void GLESRenderEngine::primeCache() const {
+void GLESRenderEngine::primeCache() {
ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
mUseColorManagement, mPrecacheToneMapperShaderOnly);
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 7496b74..06a1722 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -57,7 +57,7 @@
EGLSurface protectedStub);
~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
- void primeCache() const override;
+ void primeCache() override;
void genTextures(size_t count, uint32_t* names) override;
void deleteTextures(size_t count, uint32_t const* names) override;
void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex);
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index ddae34a..7c51f1b 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -97,7 +97,7 @@
// This interface, while still in use until a suitable replacement is built,
// should be considered deprecated, minus some methods which still may be
// used to support legacy behavior.
- virtual void primeCache() const = 0;
+ virtual void primeCache() = 0;
// dump the extension strings. always call the base class.
virtual void dump(std::string& result) = 0;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 895ba3f..5f75b81 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -35,7 +35,7 @@
RenderEngine();
~RenderEngine() override;
- MOCK_CONST_METHOD0(primeCache, void());
+ MOCK_METHOD0(primeCache, void());
MOCK_METHOD1(dump, void(std::string&));
MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
new file mode 100644
index 0000000..4fdae74
--- /dev/null
+++ b/libs/renderengine/skia/Cache.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#include "Cache.h"
+#include "AutoBackendTexture.h"
+#include "SkiaRenderEngine.h"
+#include "android-base/unique_fd.h"
+#include "renderengine/DisplaySettings.h"
+#include "renderengine/LayerSettings.h"
+#include "ui/GraphicBuffer.h"
+#include "ui/GraphicTypes.h"
+#include "ui/PixelFormat.h"
+#include "ui/Rect.h"
+#include "utils/Timers.h"
+
+namespace android::renderengine::skia {
+
+static void drawShadowLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ sp<GraphicBuffer> dstBuffer) {
+ // Somewhat arbitrary dimensions, but on screen and slightly shorter, based
+ // on actual use.
+ FloatRect rect(0, 0, display.physicalDisplay.width(), display.physicalDisplay.height() - 30);
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = rect,
+ .roundedCornersCrop = rect,
+ },
+ .shadow =
+ ShadowSettings{
+ .ambientColor = vec4(0, 0, 0, 0.00935997f),
+ .spotColor = vec4(0, 0, 0, 0.0455841f),
+ .lightPos = vec3(370.508f, -1527.03f, 1650.f),
+ .lightRadius = 2200.0f,
+ .length = 0.955342f,
+ },
+ };
+
+ auto layers = std::vector<const LayerSettings*>{&layer};
+ // The identity matrix will generate the fast shaders, and the second matrix
+ // (based on one seen while going from dialer to the home screen) will
+ // generate the slower (more general case) version. If we also need a
+ // slow version without color correction, we should use this matrix with
+ // display.outputDataspace set to SRGB.
+ for (const mat4 transform : { mat4(), mat4(0.728872f, 0.f, 0.f, 0.f,
+ 0.f, 0.727627f, 0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 167.355743f, 1852.257812f, 0.f, 1.f) }) {
+ layer.geometry.positionTransform = transform;
+ renderengine->drawLayers(display, layers, dstBuffer, false /* useFrameBufferCache*/,
+ base::unique_fd(), nullptr);
+ }
+}
+
+static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ sp<GraphicBuffer> dstBuffer, sp<GraphicBuffer> srcBuffer) {
+ const Rect& displayRect = display.physicalDisplay;
+ FloatRect rect(0, 0, displayRect.width(), displayRect.height());
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = rect,
+ // This matrix is based on actual data seen when opening the dialer.
+ // What seems to be important in matching the actual use cases are:
+ // - it is not identity
+ // - the layer will be drawn (not clipped out etc)
+ .positionTransform = mat4(.19f, .0f, .0f, .0f,
+ .0f, .19f, .0f, .0f,
+ .0f, .0f, 1.f, .0f,
+ 169.f, 1527.f, .0f, 1.f),
+ .roundedCornersCrop = rect,
+ },
+ .source = PixelSource{.buffer =
+ Buffer{
+ .buffer = srcBuffer,
+ .maxMasteringLuminance = 1000.f,
+ .maxContentLuminance = 1000.f,
+ }},
+ };
+
+ // Test both drawRect and drawRRect
+ auto layers = std::vector<const LayerSettings*>{&layer};
+ for (float roundedCornersRadius : {0.0f, 500.f}) {
+ layer.geometry.roundedCornersRadius = roundedCornersRadius;
+ // No need to check UNKNOWN, which is treated as SRGB.
+ for (auto dataspace : {ui::Dataspace::SRGB, ui::Dataspace::DISPLAY_P3}) {
+ layer.sourceDataspace = dataspace;
+ for (bool isOpaque : {true, false}) {
+ layer.source.buffer.isOpaque = isOpaque;
+ for (auto alpha : {half(.23999f), half(1.0f)}) {
+ layer.alpha = alpha;
+ renderengine->drawLayers(display, layers, dstBuffer,
+ false /* useFrameBufferCache*/, base::unique_fd(),
+ nullptr);
+ }
+ }
+ }
+ }
+}
+
+void Cache::primeShaderCache(SkiaRenderEngine* renderengine) {
+ const nsecs_t timeBefore = systemTime();
+ // The dimensions should not matter, so long as we draw inside them.
+ const Rect displayRect(0, 0, 1080, 2340);
+ DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .maxLuminance = 500,
+ .outputDataspace = ui::Dataspace::DISPLAY_P3,
+ };
+
+ const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+
+ sp<GraphicBuffer> dstBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
+ usage, "primeShaderCache_dst");
+ // This buffer will be the source for the call to drawImageLayers. Draw
+ // something to it as a placeholder for what an app draws. We should draw
+ // something, but the details are not important. We only need one version of
+ // the shadow's SkSL, so draw it here, giving us both a placeholder image
+ // and a chance to compile the shadow's SkSL.
+ sp<GraphicBuffer> srcBuffer =
+ new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1,
+ usage, "drawImageLayer_src");
+ drawShadowLayer(renderengine, display, srcBuffer);
+
+ drawImageLayers(renderengine, display, dstBuffer, srcBuffer);
+ const nsecs_t timeAfter = systemTime();
+ const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
+ ALOGD("shader cache generated in %f ms\n", compileTimeMs);
+}
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/Cache.h b/libs/renderengine/skia/Cache.h
new file mode 100644
index 0000000..437571e
--- /dev/null
+++ b/libs/renderengine/skia/Cache.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 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
+
+namespace android::renderengine::skia {
+
+class SkiaRenderEngine;
+
+class Cache {
+public:
+ static void primeShaderCache(SkiaRenderEngine*);
+
+private:
+ Cache() = default;
+};
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 91b163e..66efb09 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -40,6 +40,7 @@
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
#include <utils/Trace.h>
+#include "Cache.h"
#include <cmath>
#include <cstdint>
@@ -224,6 +225,10 @@
return engine;
}
+void SkiaGLRenderEngine::primeCache() {
+ Cache::primeShaderCache(this);
+}
+
EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
status_t err;
EGLConfig config;
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 1c3a633..15d834d 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -50,6 +50,7 @@
EGLSurface protectedPlaceholder);
~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
+ void primeCache() override;
void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
void unbindExternalTextureBuffer(uint64_t bufferId) override;
status_t drawLayers(const DisplaySettings& display,
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 79a1040..f403725 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -39,7 +39,7 @@
SkiaRenderEngine(RenderEngineType type) : RenderEngine(type) {}
~SkiaRenderEngine() override {}
- virtual void primeCache() const override{};
+ virtual void primeCache() override{};
virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{};
virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{};
virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/){};
@@ -64,4 +64,4 @@
} // namespace renderengine
} // namespace android
-#endif /* SF_GLESRENDERENGINE_H_ */
\ No newline at end of file
+#endif /* SF_GLESRENDERENGINE_H_ */
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 7c7d165..6a91c7c 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -86,7 +86,7 @@
}
}
-void RenderEngineThreaded::primeCache() const {
+void RenderEngineThreaded::primeCache() {
std::promise<void> resultPromise;
std::future<void> resultFuture = resultPromise.get_future();
{
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index d362e17..df0551d 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -42,7 +42,7 @@
RenderEngineThreaded(CreateInstanceFactory factory, RenderEngineType type);
~RenderEngineThreaded() override;
- void primeCache() const override;
+ void primeCache() override;
void dump(std::string& result) override;
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index a19b04f..5270b8a 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -130,6 +130,23 @@
pointerCaptureEnabled ? "true" : "false");
}
+// --- DragEntry ---
+
+// Drag notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries
+DragEntry::DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting,
+ float x, float y)
+ : EventEntry(id, Type::DRAG, eventTime, POLICY_FLAG_PASS_TO_USER),
+ connectionToken(connectionToken),
+ isExiting(isExiting),
+ x(x),
+ y(y) {}
+
+DragEntry::~DragEntry() {}
+
+std::string DragEntry::getDescription() const {
+ return StringPrintf("DragEntry(isExiting=%s, x=%f, y=%f)", isExiting ? "true" : "false", x, y);
+}
+
// --- KeyEntry ---
KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index ed17e68..e5fb26c 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -38,6 +38,7 @@
MOTION,
SENSOR,
POINTER_CAPTURE_CHANGED,
+ DRAG,
};
int32_t id;
@@ -111,6 +112,18 @@
virtual ~PointerCaptureChangedEntry();
};
+struct DragEntry : EventEntry {
+ sp<IBinder> connectionToken;
+ bool isExiting;
+ float x, y;
+
+ DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting, float x,
+ float y);
+ std::string getDescription() const override;
+
+ ~DragEntry() override;
+};
+
struct KeyEntry : EventEntry {
int32_t deviceId;
uint32_t source;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 3183a98..fe46d17 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -753,6 +753,14 @@
break;
}
+ case EventEntry::Type::DRAG: {
+ std::shared_ptr<DragEntry> typedEntry =
+ std::static_pointer_cast<DragEntry>(mPendingEvent);
+ dispatchDragLocked(currentTime, typedEntry);
+ done = true;
+ break;
+ }
+
case EventEntry::Type::KEY: {
std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
if (isAppSwitchDue) {
@@ -921,7 +929,8 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
// nothing to do
break;
}
@@ -943,7 +952,8 @@
sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
int32_t y, TouchState* touchState,
bool addOutsideTargets,
- bool addPortalWindows) {
+ bool addPortalWindows,
+ bool ignoreDragWindow) {
if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
LOG_ALWAYS_FATAL(
"Must provide a valid touch state if adding portal windows or outside targets");
@@ -951,6 +961,9 @@
// Traverse windows from front to back to find touched window.
const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
+ if (ignoreDragWindow && haveSameToken(windowHandle, touchState->dragWindow)) {
+ continue;
+ }
const InputWindowInfo* windowInfo = windowHandle->getInfo();
if (windowInfo->displayId == displayId) {
auto flags = windowInfo->flags;
@@ -1060,7 +1073,8 @@
case EventEntry::Type::SENSOR: {
break;
}
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
break;
}
case EventEntry::Type::FOCUS:
@@ -1554,6 +1568,35 @@
return true;
}
+void InputDispatcher::enqueueDragEventLocked(const sp<InputWindowHandle>& windowHandle,
+ bool isExiting, const MotionEntry& motionEntry) {
+ // If the window needs enqueue a drag event, the pointerCount should be 1 and the action should
+ // be AMOTION_EVENT_ACTION_MOVE, that could guarantee the first pointer is always valid.
+ LOG_ALWAYS_FATAL_IF(motionEntry.pointerCount != 1);
+ PointerCoords pointerCoords;
+ pointerCoords.copyFrom(motionEntry.pointerCoords[0]);
+ pointerCoords.transform(windowHandle->getInfo()->transform);
+
+ std::unique_ptr<DragEntry> dragEntry =
+ std::make_unique<DragEntry>(mIdGenerator.nextId(), motionEntry.eventTime,
+ windowHandle->getToken(), isExiting, pointerCoords.getX(),
+ pointerCoords.getY());
+
+ enqueueInboundEventLocked(std::move(dragEntry));
+}
+
+void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) {
+ std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
+ if (channel == nullptr) {
+ return; // Window has gone away
+ }
+ InputTarget target;
+ target.inputChannel = channel;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ entry->dispatchInProgress = true;
+ dispatchEventLocked(currentTime, entry, {target});
+}
+
void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
@@ -1660,7 +1703,8 @@
case EventEntry::Type::FOCUS:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
- case EventEntry::Type::SENSOR: {
+ case EventEntry::Type::SENSOR:
+ case EventEntry::Type::DRAG: {
ALOGE("%s events do not have a target display", NamedEnum::string(entry.type).c_str());
return ADISPLAY_ID_NONE;
}
@@ -2017,6 +2061,8 @@
goto Failed;
}
+ addDragEventLocked(entry, tempTouchState);
+
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
tempTouchState.isSlippery()) {
@@ -2272,6 +2318,38 @@
return injectionResult;
}
+void InputDispatcher::addDragEventLocked(const MotionEntry& entry, TouchState& state) {
+ if (entry.pointerCount != 1 || !state.dragWindow) {
+ return;
+ }
+
+ int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
+ int32_t x = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+ int32_t y = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ if (maskedAction == AMOTION_EVENT_ACTION_MOVE) {
+ const sp<InputWindowHandle> hoverWindowHandle =
+ findTouchedWindowAtLocked(entry.displayId, x, y, &state,
+ false /*addOutsideTargets*/, false /*addPortalWindows*/,
+ true /*ignoreDragWindow*/);
+ // enqueue drag exit if needed.
+ if (hoverWindowHandle != state.dragHoverWindowHandle &&
+ !haveSameToken(hoverWindowHandle, state.dragHoverWindowHandle)) {
+ if (state.dragHoverWindowHandle != nullptr) {
+ enqueueDragEventLocked(state.dragHoverWindowHandle, true /*isExiting*/, entry);
+ }
+ state.dragHoverWindowHandle = hoverWindowHandle;
+ }
+ // enqueue drag location if needed.
+ if (hoverWindowHandle != nullptr) {
+ enqueueDragEventLocked(hoverWindowHandle, false /*isExiting*/, entry);
+ }
+ } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
+ maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+ state.dragWindow = nullptr;
+ state.dragHoverWindowHandle = nullptr;
+ }
+}
+
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::vector<InputTarget>& inputTargets) {
@@ -2543,7 +2621,8 @@
void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
if (eventEntry.type == EventEntry::Type::FOCUS ||
- eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED) {
+ eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED ||
+ eventEntry.type == EventEntry::Type::DRAG) {
// Focus or pointer capture changed events are passed to apps, but do not represent user
// activity.
return;
@@ -2585,7 +2664,8 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("%s events are not user activity",
NamedEnum::string(eventEntry.type).c_str());
break;
@@ -2799,7 +2879,8 @@
break;
}
case EventEntry::Type::FOCUS:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
break;
}
case EventEntry::Type::SENSOR: {
@@ -3022,6 +3103,15 @@
break;
}
+ case EventEntry::Type::DRAG: {
+ const DragEntry& dragEntry = static_cast<const DragEntry&>(eventEntry);
+ status = connection->inputPublisher.publishDragEvent(dispatchEntry->seq,
+ dragEntry.id, dragEntry.x,
+ dragEntry.y,
+ dragEntry.isExiting);
+ break;
+ }
+
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR: {
@@ -3319,7 +3409,8 @@
break;
}
case EventEntry::Type::FOCUS:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("Canceling %s events is not supported",
NamedEnum::string(cancelationEventEntry->type).c_str());
break;
@@ -3384,7 +3475,8 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
- case EventEntry::Type::SENSOR: {
+ case EventEntry::Type::SENSOR:
+ case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
NamedEnum::string(downEventEntry->type).c_str());
break;
@@ -4348,6 +4440,15 @@
++i;
}
}
+
+ // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. we
+ // could just clear the state here.
+ if (state.dragWindow &&
+ std::find(windowHandles.begin(), windowHandles.end(), state.dragWindow) ==
+ windowHandles.end()) {
+ state.dragWindow = nullptr;
+ state.dragHoverWindowHandle = nullptr;
+ }
}
// Release information for windows that are no longer present.
@@ -4538,7 +4639,8 @@
mBlockUntrustedTouchesMode = mode;
}
-bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
+bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool isDragDrop) {
if (fromToken == toToken) {
if (DEBUG_FOCUS) {
ALOGD("Trivial transfer to same window.");
@@ -4582,6 +4684,11 @@
InputTarget::FLAG_DISPATCH_AS_IS);
state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
+ // Store the dragging window.
+ if (isDragDrop) {
+ state.dragWindow = toWindowHandle;
+ }
+
found = true;
goto Found;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 83094c2..b2f3625 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -120,8 +120,8 @@
virtual void setMaximumObscuringOpacityForTouch(float opacity) override;
virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
- virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
- const sp<IBinder>& toToken) override;
+ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool isDragDrop = false) override;
virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
const std::string& name) override;
@@ -185,6 +185,9 @@
// Enqueues a focus event.
void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
const std::string& reason) REQUIRES(mLock);
+ // Enqueues a drag event.
+ void enqueueDragEventLocked(const sp<InputWindowHandle>& windowToken, bool isExiting,
+ const MotionEntry& motionEntry) REQUIRES(mLock);
// Adds an event to a queue of recent events for debugging purposes.
void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
@@ -204,7 +207,8 @@
sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
TouchState* touchState,
bool addOutsideTargets = false,
- bool addPortalWindows = false) REQUIRES(mLock);
+ bool addPortalWindows = false,
+ bool ignoreDragWindow = false) REQUIRES(mLock);
// All registered connections mapped by channel file descriptor.
std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
@@ -387,6 +391,7 @@
const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
void dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
+ void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) REQUIRES(mLock);
void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
@@ -489,10 +494,12 @@
std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
-
void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
const InjectionState* injectionState);
+ // Enqueue a drag event if needed, and update the touch state.
+ // Uses findTouchedWindowTargetsLocked to make the decision
+ void addDragEventLocked(const MotionEntry& entry, TouchState& state) REQUIRES(mLock);
struct TouchOcclusionInfo {
bool hasBlockingOcclusion;
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 81b3cf0..4165f49 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -49,6 +49,8 @@
windows = other.windows;
portalWindows = other.portalWindows;
gestureMonitors = other.gestureMonitors;
+ dragHoverWindowHandle = other.dragHoverWindowHandle;
+ dragWindow = other.dragWindow;
}
void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 623c6a8..d7a561c 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -41,6 +41,11 @@
std::vector<TouchedMonitor> gestureMonitors;
+ // The last drag hover window which could receive the drag event.
+ sp<InputWindowHandle> dragHoverWindowHandle;
+ // The window being dragged.
+ sp<InputWindowHandle> dragWindow;
+
TouchState();
~TouchState();
void reset();
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 3491893..b601dfc 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -149,8 +149,8 @@
*
* Returns true on success. False if the window did not actually have touch focus.
*/
- virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
-
+ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool isDragDrop) = 0;
/**
* Sets focus on the specified window.
*/
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 5cdcfaf..32f9b69 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -766,6 +766,9 @@
case AINPUT_EVENT_TYPE_CAPTURE: {
FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events";
}
+ case AINPUT_EVENT_TYPE_DRAG: {
+ FAIL() << "Use 'consumeDragEvent' for DRAG events";
+ }
default: {
FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType;
}
@@ -803,6 +806,23 @@
EXPECT_EQ(hasCapture, captureEvent.getPointerCaptureEnabled());
}
+ void consumeDragEvent(bool isExiting, float x, float y) {
+ const InputEvent* event = consume();
+ ASSERT_NE(nullptr, event) << mName.c_str()
+ << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_DRAG, event->getType())
+ << "Got " << inputEventTypeToString(event->getType())
+ << " event instead of DRAG event";
+
+ EXPECT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+ << mName.c_str() << ": event displayId should always be NONE.";
+
+ const auto& dragEvent = static_cast<const DragEvent&>(*event);
+ EXPECT_EQ(isExiting, dragEvent.isExiting());
+ EXPECT_EQ(x, dragEvent.getX());
+ EXPECT_EQ(y, dragEvent.getY());
+ }
+
void assertNoEvents() {
InputEvent* event = consume();
if (event == nullptr) {
@@ -905,7 +925,7 @@
mInfo.frameTop = frame.top;
mInfo.frameRight = frame.right;
mInfo.frameBottom = frame.bottom;
- mInfo.transform.set(frame.left, frame.top);
+ mInfo.transform.set(-frame.left, -frame.top);
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(frame);
}
@@ -1003,6 +1023,10 @@
expectedFlags);
}
+ void consumeDragEvent(bool isExiting, float x, float y) {
+ mInputReceiver->consumeDragEvent(isExiting, x, y);
+ }
+
std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
if (mInputReceiver == nullptr) {
ADD_FAILURE() << "Invalid receive event on window with no receiver";
@@ -4682,4 +4706,87 @@
mTouchWindow->consumeAnyMotionDown();
}
+class InputDispatcherDragTests : public InputDispatcherTest {
+protected:
+ std::shared_ptr<FakeApplicationHandle> mApp;
+ sp<FakeWindowHandle> mWindow;
+ sp<FakeWindowHandle> mSecondWindow;
+ sp<FakeWindowHandle> mDragWindow;
+
+ void SetUp() override {
+ InputDispatcherTest::SetUp();
+ mApp = std::make_shared<FakeApplicationHandle>();
+ mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mWindow->setFrame(Rect(0, 0, 100, 100));
+ mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
+ mSecondWindow->setFrame(Rect(100, 0, 200, 100));
+ mSecondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+ }
+
+ // Start performing drag, we will create a drag window and transfer touch to it.
+ void performDrag() {
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Window should receive motion event.
+ mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ // The drag window covers the entire display
+ mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
+
+ // Transfer touch focus to the drag window
+ mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(),
+ true /* isDragDrop */);
+ mWindow->consumeMotionCancel();
+ mDragWindow->consumeMotionDown();
+ }
+};
+
+TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
+ performDrag();
+
+ // Move on window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->assertNoEvents();
+
+ // Move to another window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(true, 150, 50);
+ mSecondWindow->consumeDragEvent(false, 50, 50);
+
+ // Move back to original window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->consumeDragEvent(true, -50, 50);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index d215298..5fed79f 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -182,7 +182,7 @@
bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
// Returns true if the next buffer should be presented at the expected present time
- bool shouldPresentNow(nsecs_t expectedPresentTime) const final;
+ bool shouldPresentNow(nsecs_t expectedPresentTime) const;
// Returns true if the next buffer should be presented at the expected present time,
// overridden by BufferStateLayer and BufferQueueLayer for implementation
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index e017cdc..96a0c3c 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -177,6 +177,8 @@
// leak.
ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
mName.c_str());
+ std::string miniDump = mPendingJankClassifications.front()->miniDump();
+ ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str());
mPendingJankClassifications.pop_front();
}
mPendingJankClassifications.emplace_back(surfaceFrame);
@@ -255,8 +257,8 @@
}
bool BufferStateLayer::setTransform(uint32_t transform) {
- if (mCurrentState.transform == transform) return false;
- mCurrentState.transform = transform;
+ if (mCurrentState.bufferTransform == transform) return false;
+ mCurrentState.bufferTransform = transform;
mCurrentState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
@@ -308,17 +310,17 @@
h = frame.bottom;
}
- if (mCurrentState.active.transform.tx() == x && mCurrentState.active.transform.ty() == y &&
- mCurrentState.active.w == w && mCurrentState.active.h == h) {
+ if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y &&
+ mCurrentState.width == w && mCurrentState.height == h) {
return false;
}
if (!frame.isValid()) {
x = y = w = h = 0;
}
- mCurrentState.active.transform.set(x, y);
- mCurrentState.active.w = w;
- mCurrentState.active.h = h;
+ mCurrentState.transform.set(x, y);
+ mCurrentState.width = w;
+ mCurrentState.height = h;
mCurrentState.sequence++;
mCurrentState.modified = true;
@@ -779,7 +781,7 @@
mBufferInfo.mDesiredPresentTime = s.desiredPresentTime;
mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence);
mBufferInfo.mFence = s.acquireFence;
- mBufferInfo.mTransform = s.transform;
+ mBufferInfo.mTransform = s.bufferTransform;
mBufferInfo.mDataspace = translateDataspace(s.dataspace);
mBufferInfo.mCrop = computeCrop(s);
mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
@@ -846,10 +848,10 @@
const State& s(getDrawingState());
if (radius <= 0 || (getActiveWidth(s) == UINT32_MAX && getActiveHeight(s) == UINT32_MAX))
return RoundedCornerState();
- return RoundedCornerState(FloatRect(static_cast<float>(s.active.transform.tx()),
- static_cast<float>(s.active.transform.ty()),
- static_cast<float>(s.active.transform.tx() + s.active.w),
- static_cast<float>(s.active.transform.ty() + s.active.h)),
+ return RoundedCornerState(FloatRect(static_cast<float>(s.transform.tx()),
+ static_cast<float>(s.transform.ty()),
+ static_cast<float>(s.transform.tx() + s.width),
+ static_cast<float>(s.transform.ty() + s.height)),
radius);
}
@@ -863,7 +865,7 @@
uint32_t bufferHeight = s.buffer->height;
// Undo any transformations on the buffer and return the result.
- if (s.transform & ui::Transform::ROT_90) {
+ if (s.bufferTransform & ui::Transform::ROT_90) {
std::swap(bufferWidth, bufferHeight);
}
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 003edd5..93fb2cd 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -55,11 +55,9 @@
void pushPendingState() override;*/
bool applyPendingStates(Layer::State* stateToCommit) override;
- uint32_t getActiveWidth(const Layer::State& s) const override { return s.active.w; }
- uint32_t getActiveHeight(const Layer::State& s) const override { return s.active.h; }
- ui::Transform getActiveTransform(const Layer::State& s) const override {
- return s.active.transform;
- }
+ uint32_t getActiveWidth(const Layer::State& s) const override { return s.width; }
+ uint32_t getActiveHeight(const Layer::State& s) const override { return s.height; }
+ ui::Transform getActiveTransform(const Layer::State& s) const override { return s.transform; }
Region getActiveTransparentRegion(const Layer::State& s) const override {
return s.transparentRegionHint;
}
@@ -118,6 +116,8 @@
std::atomic<int32_t>* getPendingBufferCounter() override { return &mPendingBufferTransactions; }
std::string getPendingBufferCounterName() override { return mBlastTransactionName; }
+ bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const override { return true; }
+
protected:
void gatherBufferInfo() override;
uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index 00424b2..fa87fb8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -17,15 +17,12 @@
#pragma once
#include <compositionengine/impl/planner/LayerState.h>
+#include <renderengine/RenderEngine.h>
#include <chrono>
namespace android {
-namespace renderengine {
-class RenderEngine;
-} // namespace renderengine
-
namespace compositionengine::impl::planner {
std::string durationString(std::chrono::milliseconds duration);
@@ -63,7 +60,7 @@
const Layer& getFirstLayer() const { return mLayers[0]; }
const Rect& getBounds() const { return mBounds; }
size_t getAge() const { return mAge; }
- const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+ const sp<GraphicBuffer>& getBuffer() const { return mTexture.getBuffer(); }
const sp<Fence>& getDrawFence() const { return mDrawFence; }
NonBufferHash getNonBufferHash() const;
@@ -82,7 +79,7 @@
void setLastUpdate(std::chrono::steady_clock::time_point now) { mLastUpdate = now; }
void append(const CachedSet& other) {
- mBuffer = nullptr;
+ mTexture.setBuffer(nullptr, nullptr);
mDrawFence = nullptr;
mLayers.insert(mLayers.end(), other.mLayers.cbegin(), other.mLayers.cend());
@@ -105,7 +102,32 @@
std::vector<Layer> mLayers;
Rect mBounds = Rect::EMPTY_RECT;
size_t mAge = 0;
- sp<GraphicBuffer> mBuffer;
+
+ class Texture {
+ public:
+ ~Texture() { setBuffer(nullptr, nullptr); }
+
+ void setBuffer(const sp<GraphicBuffer>& buffer, renderengine::RenderEngine* re) {
+ if (mRE && mBuffer) {
+ mRE->unbindExternalTextureBuffer(mBuffer->getId());
+ }
+
+ mBuffer = buffer;
+ mRE = re;
+
+ if (mRE && mBuffer) {
+ mRE->cacheExternalTextureBuffer(mBuffer);
+ }
+ }
+
+ const sp<GraphicBuffer>& getBuffer() const { return mBuffer; }
+
+ private:
+ sp<GraphicBuffer> mBuffer = nullptr;
+ renderengine::RenderEngine* mRE = nullptr;
+ };
+
+ Texture mTexture;
sp<Fence> mDrawFence;
static const bool sDebugHighlighLayers;
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index ab3fe9e..ba03655 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -126,7 +126,7 @@
}
bool CachedSet::hasReadyBuffer() const {
- return mBuffer != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
+ return mTexture.getBuffer() != nullptr && mDrawFence->getStatus() == Fence::Status::Signaled;
}
std::vector<CachedSet> CachedSet::decompose() const {
@@ -209,11 +209,12 @@
HAL_PIXEL_FORMAT_RGBA_8888, 1, usageFlags);
LOG_ALWAYS_FATAL_IF(buffer->initCheck() != OK);
base::unique_fd drawFence;
+
status_t result = renderEngine.drawLayers(displaySettings, layerSettingsPointers, buffer, false,
base::unique_fd(), &drawFence);
if (result == NO_ERROR) {
- mBuffer = buffer;
+ mTexture.setBuffer(buffer, &renderEngine);
mDrawFence = new Fence(drawFence.release());
}
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 6d1ce4c..c33828f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -310,8 +310,14 @@
EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1));
EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ EXPECT_CALL(mRenderEngine, cacheExternalTextureBuffer(_));
cachedSet.render(mRenderEngine);
expectReadyBuffer(cachedSet);
+
+ // Now check that appending a new cached set properly cleans up RenderEngine resources.
+ EXPECT_CALL(mRenderEngine, unbindExternalTextureBuffer(_));
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+ cachedSet.append(CachedSet(layer3));
}
} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 42bbfcc..c4bd5b3 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -55,8 +55,10 @@
// TODO(b/181192467): Once Flattener starts to do something useful with Predictor,
// mPredictor should be mocked and checked for expectations.
Predictor mPredictor;
- std::unique_ptr<Flattener> mFlattener;
+
+ // mRenderEngine may be held as a pointer to mFlattener, so mFlattener must be destroyed first.
renderengine::mock::RenderEngine mRenderEngine;
+ std::unique_ptr<Flattener> mFlattener;
const std::chrono::steady_clock::time_point kStartTime = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point mTime = kStartTime;
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 3442706..b1dff8d 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -296,7 +296,7 @@
frametimeline::TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats,
JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter)
+ TraceCookieCounter* traceCookieCounter, bool isBuffer)
: mToken(frameTimelineInfo.vsyncId),
mInputEventId(frameTimelineInfo.inputEventId),
mOwnerPid(ownerPid),
@@ -310,7 +310,8 @@
mActuals({0, 0, 0}),
mTimeStats(timeStats),
mJankClassificationThresholds(thresholds),
- mTraceCookieCounter(*traceCookieCounter) {}
+ mTraceCookieCounter(*traceCookieCounter),
+ mIsBuffer(isBuffer) {}
void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) {
std::scoped_lock lock(mMutex);
@@ -395,6 +396,20 @@
return mDropTime;
}
+void SurfaceFrame::promoteToBuffer() {
+ std::scoped_lock lock(mMutex);
+ LOG_ALWAYS_FATAL_IF(mIsBuffer == true,
+ "Trying to promote an already promoted BufferSurfaceFrame from layer %s "
+ "with token %" PRId64 "",
+ mDebugName.c_str(), mToken);
+ mIsBuffer = true;
+}
+
+bool SurfaceFrame::getIsBuffer() const {
+ std::scoped_lock lock(mMutex);
+ return mIsBuffer;
+}
+
void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) const {
std::scoped_lock lock(mMutex);
StringAppendF(&result, "%s", indent.c_str());
@@ -407,6 +422,8 @@
StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Token: %" PRId64 "\n", mToken);
StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Is Buffer?: %d\n", mIsBuffer);
+ StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Owner Pid : %d\n", mOwnerPid);
StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Scheduled rendering rate: %d fps\n",
@@ -444,6 +461,21 @@
dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
}
+std::string SurfaceFrame::miniDump() const {
+ std::scoped_lock lock(mMutex);
+ std::string result;
+ StringAppendF(&result, "Layer - %s\n", mDebugName.c_str());
+ StringAppendF(&result, "Token: %" PRId64 "\n", mToken);
+ StringAppendF(&result, "Is Buffer?: %d\n", mIsBuffer);
+ StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
+ StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
+ StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str());
+ StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str());
+ StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str());
+ StringAppendF(&result, "Present time: %" PRId64 "", mActuals.presentTime);
+ return result;
+}
+
void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
nsecs_t& deadlineDelta) {
if (mPredictionState == PredictionState::Expired ||
@@ -744,13 +776,14 @@
std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
- std::string layerName, std::string debugName) {
+ std::string layerName, std::string debugName, bool isBuffer) {
ATRACE_CALL();
if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::None, TimelineItem(), mTimeStats,
- mJankClassificationThresholds, &mTraceCookieCounter);
+ mJankClassificationThresholds, &mTraceCookieCounter,
+ isBuffer);
}
std::optional<TimelineItem> predictions =
mTokenManager.getPredictionsForToken(frameTimelineInfo.vsyncId);
@@ -759,12 +792,13 @@
std::move(layerName), std::move(debugName),
PredictionState::Valid, std::move(*predictions),
mTimeStats, mJankClassificationThresholds,
- &mTraceCookieCounter);
+ &mTraceCookieCounter, isBuffer);
}
return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
std::move(layerName), std::move(debugName),
PredictionState::Expired, TimelineItem(), mTimeStats,
- mJankClassificationThresholds, &mTraceCookieCounter);
+ mJankClassificationThresholds, &mTraceCookieCounter,
+ isBuffer);
}
FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats,
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index b66e02a..3cf35f0 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -159,7 +159,7 @@
int32_t layerId, std::string layerName, std::string debugName,
PredictionState predictionState, TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter);
+ TraceCookieCounter* traceCookieCounter, bool isBuffer);
~SurfaceFrame() = default;
// Returns std::nullopt if the frame hasn't been classified yet.
@@ -181,6 +181,10 @@
void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0);
void setRenderRate(Fps renderRate);
+ // When a bufferless SurfaceFrame is promoted to a buffer SurfaceFrame, we also have to update
+ // isBuffer.
+ void promoteToBuffer();
+
// Functions called by FrameTimeline
// BaseTime is the smallest timestamp in this SurfaceFrame.
// Used for dumping all timestamps relative to the oldest, making it easy to read.
@@ -192,6 +196,8 @@
nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta);
// All the timestamps are dumped relative to the baseTime
void dump(std::string& result, const std::string& indent, nsecs_t baseTime) const;
+ // Dumps only the layer, token, is buffer, jank metadata, prediction and present states.
+ std::string miniDump() const;
// Emits a packet for perfetto tracing. The function body will be executed only if tracing is
// enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding
// DisplayFrame at the trace processor side.
@@ -206,6 +212,7 @@
FrameReadyMetadata getFrameReadyMetadata() const;
FramePresentMetadata getFramePresentMetadata() const;
nsecs_t getDropTime() const;
+ bool getIsBuffer() const;
// For prediction expired frames, this delta is subtracted from the actual end time to get a
// start time decent enough to see in traces.
@@ -253,6 +260,9 @@
// TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto. Using a
// reference here because the counter is owned by FrameTimeline, which outlives SurfaceFrame.
TraceCookieCounter& mTraceCookieCounter;
+ // Tells if the SurfaceFrame is representing a buffer or a transaction without a
+ // buffer(animations)
+ bool mIsBuffer;
};
/*
@@ -272,7 +282,7 @@
// Debug name is the human-readable debugging string for dumpsys.
virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- int32_t layerId, std::string layerName, std::string debugName) = 0;
+ int32_t layerId, std::string layerName, std::string debugName, bool isBuffer) = 0;
// Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
// composited into one display frame.
@@ -431,7 +441,7 @@
frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
- int32_t layerId, std::string layerName, std::string debugName) override;
+ int32_t layerId, std::string layerName, std::string debugName, bool isBuffer) override;
void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
void setSfPresent(nsecs_t sfPresentTime,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 061ad0e..44f1a70 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -109,11 +109,11 @@
mCurrentState.layerStack = 0;
mCurrentState.sequence = 0;
mCurrentState.requested_legacy = mCurrentState.active_legacy;
- mCurrentState.active.w = UINT32_MAX;
- mCurrentState.active.h = UINT32_MAX;
- mCurrentState.active.transform.set(0, 0);
+ mCurrentState.width = UINT32_MAX;
+ mCurrentState.height = UINT32_MAX;
+ mCurrentState.transform.set(0, 0);
mCurrentState.frameNumber = 0;
- mCurrentState.transform = 0;
+ mCurrentState.bufferTransform = 0;
mCurrentState.transformToDisplayInverse = false;
mCurrentState.crop.makeInvalid();
mCurrentState.acquireFence = new Fence(-1);
@@ -1058,6 +1058,15 @@
}
void Layer::commitTransaction(State& stateToCommit) {
+ if (auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX;
+ mDrawingState.buffer != stateToCommit.buffer && bufferSurfaceFrame != nullptr &&
+ bufferSurfaceFrame->getPresentState() != PresentState::Presented) {
+ // If the previous buffer was committed but not latched (refreshPending - happens during
+ // back to back invalidates), it gets silently dropped here. Mark the corresponding
+ // SurfaceFrame as dropped to prevent it from getting stuck in the pending classification
+ // list.
+ addSurfaceFrameDroppedForBuffer(bufferSurfaceFrame);
+ }
mDrawingState = stateToCommit;
// Set the present state for all bufferlessSurfaceFramesTX to Presented. The
@@ -1537,6 +1546,7 @@
// Promote the bufferlessSurfaceFrame to a bufferSurfaceFrameTX
mCurrentState.bufferSurfaceFrameTX = it->second;
mCurrentState.bufferlessSurfaceFramesTX.erase(it);
+ mCurrentState.bufferSurfaceFrameTX->promoteToBuffer();
mCurrentState.bufferSurfaceFrameTX->setActualQueueTime(postTime);
} else {
mCurrentState.bufferSurfaceFrameTX =
@@ -1596,7 +1606,8 @@
auto surfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
getSequence(), mName,
- mTransactionName);
+ mTransactionName,
+ /*isBuffer*/ false);
// For Transactions, the post time is considered to be both queue and acquire fence time.
surfaceFrame->setActualQueueTime(postTime);
surfaceFrame->setAcquireFenceTime(postTime);
@@ -1612,7 +1623,8 @@
const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName) {
auto surfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
- getSequence(), mName, debugName);
+ getSequence(), mName, debugName,
+ /*isBuffer*/ true);
// For buffers, acquire fence time will set during latch.
surfaceFrame->setActualQueueTime(queueTime);
const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
@@ -2431,7 +2443,7 @@
const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
const State& state = useDrawing ? mDrawingState : mCurrentState;
- ui::Transform requestedTransform = state.active_legacy.transform;
+ ui::Transform requestedTransform = state.transform;
if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
layerInfo->set_id(sequence);
@@ -2460,11 +2472,10 @@
return layerInfo->mutable_requested_position();
});
- LayerProtoHelper::writeSizeToProto(state.active_legacy.w, state.active_legacy.h,
+ LayerProtoHelper::writeSizeToProto(state.width, state.height,
[&]() { return layerInfo->mutable_size(); });
- LayerProtoHelper::writeToProto(state.crop_legacy,
- [&]() { return layerInfo->mutable_crop(); });
+ LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); });
layerInfo->set_is_opaque(isOpaque(state));
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 34a9f39..4a114e2 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -250,9 +250,11 @@
// The fields below this point are only used by BufferStateLayer
uint64_t frameNumber;
- Geometry active;
+ uint32_t width;
+ uint32_t height;
+ ui::Transform transform;
- uint32_t transform;
+ uint32_t bufferTransform;
bool transformToDisplayInverse;
Rect crop;
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index ba43e70..6553efe 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -354,7 +354,7 @@
return mTokenManager->generateTokenForPredictions(
{timestamp, deadlineTimestamp, expectedVSyncTimestamp});
}
- return static_cast<int64_t>(0);
+ return FrameTimelineInfo::INVALID_VSYNC_ID;
}();
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
@@ -494,10 +494,16 @@
const auto now = systemTime(SYSTEM_TIME_MONOTONIC);
const auto deadlineTimestamp = now + timeout.count();
const auto expectedVSyncTime = deadlineTimestamp + timeout.count();
- // TODO(b/162890590): use TokenManager to populate vsyncId
+ const int64_t vsyncId = [&] {
+ if (mTokenManager != nullptr) {
+ return mTokenManager->generateTokenForPredictions(
+ {now, deadlineTimestamp, expectedVSyncTime});
+ }
+ return FrameTimelineInfo::INVALID_VSYNC_ID;
+ }();
mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now,
++mVSyncState->count, expectedVSyncTime,
- deadlineTimestamp, /*vsyncId=*/0));
+ deadlineTimestamp, vsyncId));
}
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 593855e..7d75657 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1879,7 +1879,12 @@
// underestimated.
mFrameStartTime = frameStart;
}
- signalRefresh();
+
+ // Run the refresh immediately after invalidate as there is no point going thru the message
+ // queue again, and to ensure that we actually refresh the screen instead of handling
+ // other messages that were queued us already in the MessageQueue.
+ mRefreshPending = true;
+ onMessageRefresh();
}
}
@@ -3041,9 +3046,8 @@
void SurfaceFlinger::commitTransaction() {
commitTransactionLocked();
- mTransactionPending = false;
+ signalSynchronousTransactions();
mAnimTransactionPending = false;
- mTransactionCV.broadcast();
}
void SurfaceFlinger::commitTransactionLocked() {
@@ -3312,15 +3316,16 @@
auto& [applyToken, transactionQueue] = *it;
while (!transactionQueue.empty()) {
- const auto& transaction = transactionQueue.front();
+ auto& transaction = transactionQueue.front();
if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
transaction.isAutoTimestamp,
transaction.desiredPresentTime,
- transaction.states, pendingBuffers)) {
+ transaction.originUid, transaction.states,
+ pendingBuffers)) {
setTransactionFlags(eTransactionFlushNeeded);
break;
}
- transactions.push_back(transaction);
+ transactions.emplace_back(std::move(transaction));
transactionQueue.pop();
}
@@ -3337,17 +3342,18 @@
// Case 2: push to pending when there exist a pending queue.
// Case 3: others are ready to apply.
while (!mTransactionQueue.empty()) {
- const auto& transaction = mTransactionQueue.front();
+ auto& transaction = mTransactionQueue.front();
bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
mPendingTransactionQueues.end();
if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
transaction.isAutoTimestamp,
transaction.desiredPresentTime,
- transaction.states, pendingBuffers) ||
+ transaction.originUid, transaction.states,
+ pendingBuffers) ||
pendingTransactions) {
- mPendingTransactionQueues[transaction.applyToken].push(transaction);
+ mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
} else {
- transactions.push_back(transaction);
+ transactions.emplace_back(std::move(transaction));
}
mTransactionQueue.pop();
ATRACE_INT("TransactionQueue", mTransactionQueue.size());
@@ -3363,25 +3369,36 @@
transaction.postTime, transaction.permissions,
transaction.hasListenerCallbacks, transaction.listenerCallbacks,
transaction.originPid, transaction.originUid, transaction.id);
+ if (transaction.transactionCommittedSignal) {
+ mTransactionCommittedSignals.emplace_back(
+ std::move(transaction.transactionCommittedSignal));
+ }
}
}
}
bool SurfaceFlinger::transactionFlushNeeded() {
Mutex::Autolock _l(mQueueLock);
- return !mPendingTransactionQueues.empty();
+ return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
}
bool SurfaceFlinger::transactionIsReadyToBeApplied(
const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
- const Vector<ComposerState>& states,
+ uid_t originUid, const Vector<ComposerState>& states,
std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers) {
+ ATRACE_CALL();
const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
bool ready = true;
// Do not present if the desiredPresentTime has not passed unless it is more than one second
// in the future. We ignore timestamps more than 1 second in the future for stability reasons.
if (!isAutoTimestamp && desiredPresentTime >= expectedPresentTime &&
desiredPresentTime < expectedPresentTime + s2ns(1)) {
+ ATRACE_NAME("not current");
+ ready = false;
+ }
+
+ if (!mScheduler->isVsyncValid(expectedPresentTime, originUid)) {
+ ATRACE_NAME("!isVsyncValid");
ready = false;
}
@@ -3404,15 +3421,12 @@
continue;
}
+ ATRACE_NAME(layer->getName().c_str());
+
const bool frameTimelineInfoChanged = (s.what & layer_state_t::eFrameTimelineInfoChanged);
const auto vsyncId = frameTimelineInfoChanged ? s.frameTimelineInfo.vsyncId : info.vsyncId;
if (isAutoTimestamp && layer->frameIsEarly(expectedPresentTime, vsyncId)) {
ATRACE_NAME("frameIsEarly()");
- return false;
- }
-
- if (!mScheduler->isVsyncValid(expectedPresentTime, layer->getOwnerUid())) {
- ATRACE_NAME("!isVsyncValidForUid");
ready = false;
}
@@ -3421,16 +3435,16 @@
// transaction in the queue.
const bool hasPendingBuffer = pendingBuffers.find(s.surface) != pendingBuffers.end();
if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
+ ATRACE_NAME("hasPendingBuffer");
ready = false;
}
pendingBuffers.insert(s.surface);
}
- pendingBuffers.insert(s.surface);
}
return ready;
}
-void SurfaceFlinger::queueTransaction(TransactionState state) {
+void SurfaceFlinger::queueTransaction(TransactionState& state) {
Mutex::Autolock _l(mQueueLock);
// If its TransactionQueue already has a pending TransactionState or if it is pending
@@ -3450,20 +3464,15 @@
}
}
+ // Generate a CountDownLatch pending state if this is a synchronous transaction.
+ if ((state.flags & eSynchronous) || state.inputWindowCommands.syncInputWindows) {
+ state.transactionCommittedSignal = std::make_shared<CountDownLatch>(
+ (state.inputWindowCommands.syncInputWindows ? 2 : 1));
+ }
+
mTransactionQueue.emplace(state);
ATRACE_INT("TransactionQueue", mTransactionQueue.size());
- // TODO(b/159125966): Remove eEarlyWakeup completely as no client should use this flag
- if (state.flags & eEarlyWakeup) {
- ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
- }
-
- if (!(state.permissions & Permission::ACCESS_SURFACE_FLINGER) &&
- (state.flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
- ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
- state.flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
- }
-
const auto schedule = [](uint32_t flags) {
if (flags & eEarlyWakeup) return TransactionSchedule::Early;
if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd;
@@ -3474,28 +3483,23 @@
setTransactionFlags(eTransactionFlushNeeded, schedule);
}
-void SurfaceFlinger::waitForSynchronousTransaction(bool synchronous, bool syncInput) {
- Mutex::Autolock _l(mStateLock);
- if (synchronous) {
- mTransactionPending = true;
+void SurfaceFlinger::waitForSynchronousTransaction(
+ const CountDownLatch& transactionCommittedSignal) {
+ // applyTransactionState is called on the main SF thread. While a given process may wish
+ // to wait on synchronous transactions, the main SF thread should apply the transaction and
+ // set the value to notify this after committed.
+ if (!transactionCommittedSignal.wait_until(std::chrono::seconds(5))) {
+ ALOGE("setTransactionState timed out!");
}
- if (syncInput) {
- mPendingSyncInputWindows = true;
- }
+}
- // applyTransactionState can be called by either the main SF thread or by
- // another process through setTransactionState. While a given process may wish
- // to wait on synchronous transactions, the main SF thread should never
- // be blocked. Therefore, we only wait if isMainThread is false.
- while (mTransactionPending || mPendingSyncInputWindows) {
- status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
- if (CC_UNLIKELY(err != NO_ERROR)) {
- // just in case something goes wrong in SF, return to the
- // called after a few seconds.
- ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!");
- mTransactionPending = false;
- mPendingSyncInputWindows = false;
- break;
+void SurfaceFlinger::signalSynchronousTransactions() {
+ for (auto it = mTransactionCommittedSignals.begin();
+ it != mTransactionCommittedSignals.end();) {
+ if ((*it)->countDown() == 0) {
+ it = mTransactionCommittedSignals.erase(it);
+ } else {
+ it++;
}
}
}
@@ -3524,21 +3528,35 @@
permissions |= Permission::ROTATE_SURFACE_FLINGER;
}
+ // TODO(b/159125966): Remove eEarlyWakeup completely as no client should use this flag
+ if (flags & eEarlyWakeup) {
+ ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
+ }
+
+ if (!(permissions & Permission::ACCESS_SURFACE_FLINGER) &&
+ (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
+ ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
+ flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
+ }
+
const int64_t postTime = systemTime();
IPCThreadState* ipc = IPCThreadState::self();
const int originPid = ipc->getCallingPid();
const int originUid = ipc->getCallingUid();
+ TransactionState state{frameTimelineInfo, states,
+ displays, flags,
+ applyToken, inputWindowCommands,
+ desiredPresentTime, isAutoTimestamp,
+ uncacheBuffer, postTime,
+ permissions, hasListenerCallbacks,
+ listenerCallbacks, originPid,
+ originUid, transactionId};
+ queueTransaction(state);
- queueTransaction({frameTimelineInfo, states, displays, flags, applyToken, inputWindowCommands,
- desiredPresentTime, isAutoTimestamp, uncacheBuffer, postTime, permissions,
- hasListenerCallbacks, listenerCallbacks, originPid, originUid,
- transactionId});
-
- const bool synchronous = flags & eSynchronous;
- const bool syncInput = inputWindowCommands.syncInputWindows;
- if (synchronous || syncInput) {
- waitForSynchronousTransaction(synchronous, syncInput);
+ // Check the pending state to make sure the transaction is synchronous.
+ if (state.transactionCommittedSignal) {
+ waitForSynchronousTransaction(*state.transactionCommittedSignal);
}
return NO_ERROR;
@@ -6078,10 +6096,7 @@
void SurfaceFlinger::setInputWindowsFinished() {
Mutex::Autolock _l(mStateLock);
-
- mPendingSyncInputWindows = false;
-
- mTransactionCV.broadcast();
+ signalSynchronousTransactions();
}
// ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 40d63b2..3787b9d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -474,6 +474,41 @@
hal::Connection connection = hal::Connection::INVALID;
};
+ class CountDownLatch {
+ public:
+ explicit CountDownLatch(int32_t count) : mCount(count) {}
+
+ int32_t countDown() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ if (mCount == 0) {
+ return 0;
+ }
+ if (--mCount == 0) {
+ mCountDownComplete.notify_all();
+ }
+ return mCount;
+ }
+
+ // Return true if triggered.
+ bool wait_until(const std::chrono::seconds& timeout) const {
+ std::unique_lock<std::mutex> lock(mMutex);
+ const auto untilTime = std::chrono::system_clock::now() + timeout;
+ while (mCount != 0) {
+ // Conditional variables can be woken up sporadically, so we check count
+ // to verify the wakeup was triggered by |countDown|.
+ if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ int32_t mCount;
+ mutable std::condition_variable mCountDownComplete;
+ mutable std::mutex mMutex;
+ };
+
struct TransactionState {
TransactionState(const FrameTimelineInfo& frameTimelineInfo,
const Vector<ComposerState>& composerStates,
@@ -517,6 +552,7 @@
int originPid;
int originUid;
uint64_t id;
+ std::shared_ptr<CountDownLatch> transactionCommittedSignal;
};
template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr>
@@ -801,7 +837,7 @@
void commitOffscreenLayers();
bool transactionIsReadyToBeApplied(
const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
- const Vector<ComposerState>& states,
+ uid_t originUid, const Vector<ComposerState>& states,
std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& pendingBuffers)
REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
@@ -1080,8 +1116,9 @@
status_t CheckTransactCodeCredentials(uint32_t code);
// Add transaction to the Transaction Queue
- void queueTransaction(TransactionState state) EXCLUDES(mQueueLock);
- void waitForSynchronousTransaction(bool synchronous, bool syncInput) EXCLUDES(mStateLock);
+ void queueTransaction(TransactionState& state) EXCLUDES(mQueueLock);
+ void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal);
+ void signalSynchronousTransactions();
/*
* Generic Layer Metadata
@@ -1111,8 +1148,7 @@
mutable Mutex mStateLock;
State mCurrentState{LayerVector::StateSet::Current};
std::atomic<int32_t> mTransactionFlags = 0;
- Condition mTransactionCV;
- bool mTransactionPending = false;
+ std::vector<std::shared_ptr<CountDownLatch>> mTransactionCommittedSignals;
bool mAnimTransactionPending = false;
SortedVector<sp<Layer>> mLayersPendingRemoval;
bool mForceTraversal = false;
@@ -1324,7 +1360,6 @@
sp<SetInputWindowsListener> mSetInputWindowsListener;
- bool mPendingSyncInputWindows GUARDED_BY(mStateLock) = false;
Hwc2::impl::PowerAdvisor mPowerAdvisor;
// This should only be accessed on the main thread.
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 256be27..c3946cb 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -836,8 +836,8 @@
static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) {
auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
layerDrawingState.layerStack = DEFAULT_LAYER_STACK;
- layerDrawingState.active.w = 100;
- layerDrawingState.active.h = 100;
+ layerDrawingState.width = 100;
+ layerDrawingState.height = 100;
layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */);
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 81efe0b..d1385c0 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -193,10 +193,12 @@
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({}, sPidTwo, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
}
@@ -204,7 +206,8 @@
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
}
@@ -213,7 +216,8 @@
flushTokens(systemTime() + maxTokenRetentionTime);
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
}
@@ -222,7 +226,8 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
@@ -233,7 +238,8 @@
constexpr int32_t inputEventId = 1;
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({token1, inputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
EXPECT_EQ(inputEventId, surfaceFrame->getInputEventId());
}
@@ -243,7 +249,8 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -271,11 +278,11 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdTwo, sLayerNameTwo,
- sLayerNameTwo);
+ sLayerNameTwo, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -317,7 +324,8 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId},
sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -339,7 +347,7 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -353,18 +361,20 @@
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
- "acquireFenceAfterQueue",
- "acquireFenceAfterQueue");
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue", /*isBuffer*/ true);
surfaceFrame->setActualQueueTime(123);
surfaceFrame->setAcquireFenceTime(456);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
}
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
- "acquireFenceAfterQueue",
- "acquireFenceAfterQueue");
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+ "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue", /*isBuffer*/ true);
surfaceFrame->setActualQueueTime(456);
surfaceFrame->setAcquireFenceTime(123);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -378,7 +388,8 @@
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -394,7 +405,8 @@
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -410,7 +422,8 @@
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -430,7 +443,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -458,7 +471,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -484,7 +497,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setAcquireFenceTime(20);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -508,7 +521,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
surfaceFrame1->setAcquireFenceTime(20);
@@ -532,7 +545,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(45);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
@@ -558,7 +571,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(50);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
@@ -584,7 +597,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
@@ -610,7 +623,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken1, 82, refreshRate);
@@ -637,7 +650,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(45);
mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate);
@@ -665,7 +678,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(45);
// Trigger a prediction expiry
flushTokens(systemTime() + maxTokenRetentionTime);
@@ -701,7 +714,8 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -727,7 +741,8 @@
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token2, 20, Fps::fromPeriodNsecs(11));
@@ -772,7 +787,8 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
- sLayerNameOne, sLayerNameOne);
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -1085,11 +1101,11 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setActualQueueTime(10);
surfaceFrame1->setDropTime(15);
@@ -1245,7 +1261,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setActualQueueTime(appEndTime);
surfaceFrame1->setAcquireFenceTime(appEndTime);
@@ -1315,7 +1331,7 @@
auto surfaceFrame =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -1493,7 +1509,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(16);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1513,7 +1529,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame2->setAcquireFenceTime(36);
mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1573,7 +1589,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(16);
mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1593,7 +1609,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame2->setAcquireFenceTime(36);
mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1652,7 +1668,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken1, 42, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1696,7 +1712,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(26);
mFrameTimeline->setSfWakeUp(sfToken1, 32, Fps::fromPeriodNsecs(11));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1716,7 +1732,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame2->setAcquireFenceTime(40);
mFrameTimeline->setSfWakeUp(sfToken2, 43, Fps::fromPeriodNsecs(11));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1770,7 +1786,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(50);
mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1790,7 +1806,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame2->setAcquireFenceTime(84);
mFrameTimeline->setSfWakeUp(sfToken2, 112, Fps::fromPeriodNsecs(30));
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
@@ -1848,7 +1864,7 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame1->setAcquireFenceTime(50);
mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1868,7 +1884,7 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
sUidOne, sLayerIdOne, sLayerNameOne,
- sLayerNameOne);
+ sLayerNameOne, /*isBuffer*/ true);
surfaceFrame2->setAcquireFenceTime(80);
mFrameTimeline->setSfWakeUp(sfToken2, 82, Fps::fromPeriodNsecs(30));
// Setting previous latch time to 54, adjusted deadline will be 54 + vsyncTime(30) = 84
@@ -1923,7 +1939,8 @@
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1938,7 +1955,8 @@
const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1947,7 +1965,8 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -1962,7 +1981,8 @@
const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1971,7 +1991,8 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+ /*isBuffer*/ true);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -1986,7 +2007,8 @@
const auto twoHundredMs = std::chrono::nanoseconds(200ms).count();
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -1995,7 +2017,8 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+ /*isBuffer*/ true);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -2013,7 +2036,8 @@
const auto sixHundredMs = std::chrono::nanoseconds(600ms).count();
auto surfaceFrame1 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -2022,7 +2046,8 @@
auto surfaceFrame2 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame2);
@@ -2031,7 +2056,8 @@
auto surfaceFrame3 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+ sLayerIdTwo, sLayerNameTwo, sLayerNameTwo,
+ /*isBuffer*/ true);
auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame3);
@@ -2040,7 +2066,8 @@
auto surfaceFrame4 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
surfaceFrame4->setPresentState(SurfaceFrame::PresentState::Presented);
mFrameTimeline->addSurfaceFrame(surfaceFrame4);
@@ -2049,7 +2076,8 @@
auto surfaceFrame5 =
mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
- sLayerIdOne, sLayerNameOne, sLayerNameOne);
+ sLayerIdOne, sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true);
auto presentFence5 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
// Dropped frames will be excluded from fps computation
surfaceFrame5->setPresentState(SurfaceFrame::PresentState::Dropped);
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 363bd80..623a5e0 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -116,6 +116,7 @@
const auto surfaceFrame = layer->mCurrentState.bufferlessSurfaceFramesTX.at(/*token*/ 1);
commitTransaction(layer.get());
EXPECT_EQ(1, surfaceFrame->getToken());
+ EXPECT_EQ(false, surfaceFrame->getIsBuffer());
EXPECT_EQ(PresentState::Presented, surfaceFrame->getPresentState());
}
@@ -139,6 +140,7 @@
layer->updateTexImage(computeVisisbleRegions, 15, 0);
EXPECT_EQ(1, surfaceFrame->getToken());
+ EXPECT_EQ(true, surfaceFrame->getIsBuffer());
EXPECT_EQ(PresentState::Presented, surfaceFrame->getPresentState());
}
@@ -172,12 +174,14 @@
layer->updateTexImage(computeVisisbleRegions, 15, 0);
EXPECT_EQ(1, droppedSurfaceFrame->getToken());
+ EXPECT_EQ(true, droppedSurfaceFrame->getIsBuffer());
EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame->getPresentState());
EXPECT_EQ(0u, droppedSurfaceFrame->getActuals().endTime);
auto dropTime = droppedSurfaceFrame->getDropTime();
EXPECT_TRUE(dropTime > start && dropTime < end);
EXPECT_EQ(1, presentedSurfaceFrame->getToken());
+ EXPECT_EQ(true, presentedSurfaceFrame->getIsBuffer());
EXPECT_EQ(PresentState::Presented, presentedSurfaceFrame->getPresentState());
}
@@ -204,6 +208,7 @@
commitTransaction(layer.get());
EXPECT_EQ(1, surfaceFrame->getToken());
+ EXPECT_EQ(true, surfaceFrame->getIsBuffer());
// Buffers are presented only at latch time.
EXPECT_EQ(PresentState::Unknown, surfaceFrame->getPresentState());
@@ -260,12 +265,15 @@
commitTransaction(layer.get());
EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame1->getPresentState());
EXPECT_EQ(4, bufferlessSurfaceFrame2->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
EXPECT_EQ(3, bufferSurfaceFrameTX->getToken());
+ EXPECT_EQ(true, bufferSurfaceFrameTX->getIsBuffer());
// Buffers are presented only at latch time.
EXPECT_EQ(PresentState::Unknown, bufferSurfaceFrameTX->getPresentState());
@@ -297,10 +305,12 @@
commitTransaction(layer.get());
EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame1->getPresentState());
EXPECT_EQ(10, bufferlessSurfaceFrame1->getActuals().endTime);
EXPECT_EQ(2, bufferlessSurfaceFrame2->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
EXPECT_EQ(12, bufferlessSurfaceFrame2->getActuals().endTime);
}
@@ -327,9 +337,11 @@
commitTransaction(layer.get());
EXPECT_EQ(1, bufferlessSurfaceFrame1->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame1->getIsBuffer());
EXPECT_EQ(PresentState::Unknown, bufferlessSurfaceFrame1->getPresentState());
EXPECT_EQ(1, bufferlessSurfaceFrame2->getToken());
+ EXPECT_EQ(false, bufferlessSurfaceFrame2->getIsBuffer());
EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState());
EXPECT_EQ(12, bufferlessSurfaceFrame2->getActuals().endTime);
}
@@ -410,20 +422,85 @@
layer->updateTexImage(computeVisisbleRegions, 15, 0);
EXPECT_EQ(1, droppedSurfaceFrame1->getToken());
+ EXPECT_EQ(true, droppedSurfaceFrame1->getIsBuffer());
EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame1->getPresentState());
EXPECT_EQ(0u, droppedSurfaceFrame1->getActuals().endTime);
auto dropTime1 = droppedSurfaceFrame1->getDropTime();
EXPECT_TRUE(dropTime1 > dropStartTime1 && dropTime1 < dropEndTime1);
EXPECT_EQ(FrameTimelineInfo::INVALID_VSYNC_ID, droppedSurfaceFrame2->getToken());
+ EXPECT_EQ(true, droppedSurfaceFrame2->getIsBuffer());
EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame2->getPresentState());
EXPECT_EQ(0u, droppedSurfaceFrame2->getActuals().endTime);
auto dropTime2 = droppedSurfaceFrame2->getDropTime();
EXPECT_TRUE(dropTime2 > dropStartTime2 && dropTime2 < dropEndTime2);
EXPECT_EQ(2, presentedSurfaceFrame->getToken());
+ EXPECT_EQ(true, presentedSurfaceFrame->getIsBuffer());
EXPECT_EQ(PresentState::Presented, presentedSurfaceFrame->getPresentState());
}
+
+ void MultipleCommitsBeforeLatch() {
+ sp<BufferStateLayer> layer = createBufferStateLayer();
+ uint32_t surfaceFramesPendingClassification = 0;
+ std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> bufferlessSurfaceFrames;
+ for (int i = 0; i < 10; i += 2) {
+ sp<Fence> fence1(new Fence());
+ sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
+ layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt,
+ {/*vsyncId*/ 1, /*inputEventId*/ 0});
+ layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2,
+ /*inputEventId*/ 0},
+ 10);
+ ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX);
+ EXPECT_EQ(1u, layer->mCurrentState.bufferlessSurfaceFramesTX.size());
+ auto& bufferlessSurfaceFrame =
+ layer->mCurrentState.bufferlessSurfaceFramesTX.at(/*vsyncId*/ 2);
+ bufferlessSurfaceFrames.push_back(bufferlessSurfaceFrame);
+
+ commitTransaction(layer.get());
+ surfaceFramesPendingClassification += 2;
+ EXPECT_EQ(surfaceFramesPendingClassification,
+ layer->mPendingJankClassifications.size());
+ }
+
+ auto presentedBufferSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
+ bool computeVisisbleRegions;
+ layer->updateTexImage(computeVisisbleRegions, 15, 0);
+ // BufferlessSurfaceFrames are immediately set to presented and added to the DisplayFrame.
+ // Since we don't have access to DisplayFrame here, trigger an onPresent directly.
+ for (auto& surfaceFrame : bufferlessSurfaceFrames) {
+ surfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+ /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0);
+ }
+ presentedBufferSurfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11),
+ /*displayDeadlineDelta*/ 0,
+ /*displayPresentDelta*/ 0);
+
+ // There should be 10 bufferlessSurfaceFrames and 1 bufferSurfaceFrame
+ ASSERT_EQ(10u, surfaceFramesPendingClassification);
+ ASSERT_EQ(surfaceFramesPendingClassification, layer->mPendingJankClassifications.size());
+
+ // For the frames upto 8, the bufferSurfaceFrame should have been dropped while the
+ // bufferlessSurfaceFrame presented
+ for (uint32_t i = 0; i < 8; i += 2) {
+ auto& bufferSurfaceFrame = layer->mPendingJankClassifications[i];
+ auto& bufferlessSurfaceFrame = layer->mPendingJankClassifications[i + 1];
+ EXPECT_EQ(bufferSurfaceFrame->getPresentState(), PresentState::Dropped);
+ EXPECT_EQ(bufferlessSurfaceFrame->getPresentState(), PresentState::Presented);
+ }
+ {
+ auto& bufferSurfaceFrame = layer->mPendingJankClassifications[8u];
+ auto& bufferlessSurfaceFrame = layer->mPendingJankClassifications[9u];
+ EXPECT_EQ(bufferSurfaceFrame->getPresentState(), PresentState::Presented);
+ EXPECT_EQ(bufferlessSurfaceFrame->getPresentState(), PresentState::Presented);
+ }
+
+ layer->releasePendingBuffer(25);
+
+ // There shouldn't be any pending classifications. Everything should have been cleared.
+ EXPECT_EQ(0u, layer->mPendingJankClassifications.size());
+ }
};
TEST_F(TransactionSurfaceFrameTest, PresentedBufferlessSurfaceFrame) {
@@ -469,4 +546,8 @@
BufferSurfaceFrame_ReplaceValidTokenBufferWithInvalidTokenBuffer();
}
+TEST_F(TransactionSurfaceFrameTest, MultipleCommitsBeforeLatch) {
+ MultipleCommitsBeforeLatch();
+}
+
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index 7efd730..ddaa5a1 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -85,6 +85,7 @@
void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
ASSERT_NE(nullptr, mOutBuffer);
+ ASSERT_NE(nullptr, mPixels);
ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
TransactionUtils::expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance);
}
diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp
index 537e49b..2de2e0e 100644
--- a/services/vibratorservice/VibratorHalController.cpp
+++ b/services/vibratorservice/VibratorHalController.cpp
@@ -209,6 +209,18 @@
return apply(getSupportedPrimitivesFn, "getSupportedPrimitives");
}
+HalResult<float> HalController::getResonantFrequency() {
+ hal_fn<float> getResonantFrequencyFn = [](std::shared_ptr<HalWrapper> hal) {
+ return hal->getResonantFrequency();
+ };
+ return apply(getResonantFrequencyFn, "getResonantFrequency");
+}
+
+HalResult<float> HalController::getQFactor() {
+ hal_fn<float> getQFactorFn = [](std::shared_ptr<HalWrapper> hal) { return hal->getQFactor(); };
+ return apply(getQFactorFn, "getQFactor");
+}
+
HalResult<milliseconds> HalController::performEffect(
Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
hal_fn<milliseconds> performEffectFn = [&](std::shared_ptr<HalWrapper> hal) {
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 6faab38..3d20fa1 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -205,6 +205,17 @@
mSupportedPrimitives);
}
+HalResult<float> AidlHalWrapper::getResonantFrequency() {
+ std::lock_guard<std::mutex> lock(mResonantFrequencyMutex);
+ return loadCached<float>(std::bind(&AidlHalWrapper::getResonantFrequencyInternal, this),
+ mResonantFrequency);
+}
+
+HalResult<float> AidlHalWrapper::getQFactor() {
+ std::lock_guard<std::mutex> lock(mQFactorMutex);
+ return loadCached<float>(std::bind(&AidlHalWrapper::getQFactorInternal, this), mQFactor);
+}
+
HalResult<milliseconds> AidlHalWrapper::performEffect(
Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) {
HalResult<Capabilities> capabilities = getCapabilities();
@@ -283,6 +294,18 @@
return HalResult<std::vector<CompositePrimitive>>::fromStatus(result, supportedPrimitives);
}
+HalResult<float> AidlHalWrapper::getResonantFrequencyInternal() {
+ float f0 = 0;
+ auto result = getHal()->getResonantFrequency(&f0);
+ return HalResult<float>::fromStatus(result, f0);
+}
+
+HalResult<float> AidlHalWrapper::getQFactorInternal() {
+ float qFactor = 0;
+ auto result = getHal()->getQFactor(&qFactor);
+ return HalResult<float>::fromStatus(result, qFactor);
+}
+
sp<Aidl::IVibrator> AidlHalWrapper::getHal() {
std::lock_guard<std::mutex> lock(mHandleMutex);
return mHandle;
@@ -366,6 +389,18 @@
}
template <typename I>
+HalResult<float> HidlHalWrapper<I>::getResonantFrequency() {
+ ALOGV("Skipped getResonantFrequency because Vibrator HAL AIDL is not available");
+ return HalResult<float>::unsupported();
+}
+
+template <typename I>
+HalResult<float> HidlHalWrapper<I>::getQFactor() {
+ ALOGV("Skipped getQFactor because Vibrator HAL AIDL is not available");
+ return HalResult<float>::unsupported();
+}
+
+template <typename I>
HalResult<std::chrono::milliseconds> HidlHalWrapper<I>::performComposedEffect(
const std::vector<CompositeEffect>&, const std::function<void()>&) {
ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available");
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
index 16d571d..14ec7b2 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h
@@ -73,6 +73,9 @@
HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
final override;
+ HalResult<float> getResonantFrequency() final override;
+ HalResult<float> getQFactor() final override;
+
HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
const std::function<void()>& completionCallback) final override;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index e22ad34..039a2d9 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -185,6 +185,9 @@
virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>>
getSupportedPrimitives() = 0;
+ virtual HalResult<float> getResonantFrequency() = 0;
+ virtual HalResult<float> getQFactor() = 0;
+
virtual HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
const std::function<void()>& completionCallback) = 0;
@@ -232,6 +235,9 @@
HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
override final;
+ HalResult<float> getResonantFrequency() override final;
+ HalResult<float> getQFactor() override final;
+
HalResult<std::chrono::milliseconds> performEffect(
hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength,
const std::function<void()>& completionCallback) override final;
@@ -246,6 +252,8 @@
std::mutex mCapabilitiesMutex;
std::mutex mSupportedEffectsMutex;
std::mutex mSupportedPrimitivesMutex;
+ std::mutex mResonantFrequencyMutex;
+ std::mutex mQFactorMutex;
sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex);
std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex);
std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects
@@ -254,6 +262,8 @@
GUARDED_BY(mSupportedPrimitivesMutex);
std::vector<std::optional<std::chrono::milliseconds>> mPrimitiveDurations
GUARDED_BY(mSupportedPrimitivesMutex);
+ std::optional<float> mResonantFrequency GUARDED_BY(mResonantFrequencyMutex);
+ std::optional<float> mQFactor GUARDED_BY(mQFactorMutex);
// Loads and caches from IVibrator.
HalResult<std::chrono::milliseconds> getPrimitiveDuration(
@@ -263,6 +273,10 @@
HalResult<Capabilities> getCapabilitiesInternal();
HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal();
HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal();
+
+ HalResult<float> getResonantFrequencyInternal();
+ HalResult<float> getQFactorInternal();
+
sp<hardware::vibrator::IVibrator> getHal();
};
@@ -293,6 +307,9 @@
HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives()
override final;
+ HalResult<float> getResonantFrequency() override final;
+ HalResult<float> getQFactor() override final;
+
HalResult<std::chrono::milliseconds> performComposedEffect(
const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects,
const std::function<void()>& completionCallback) override final;
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index c4b39ed..0c39247 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -67,6 +67,10 @@
MOCK_METHOD(vibrator::HalResult<std::vector<Effect>>, getSupportedEffects, (), (override));
MOCK_METHOD(vibrator::HalResult<std::vector<CompositePrimitive>>, getSupportedPrimitives, (),
(override));
+
+ MOCK_METHOD(vibrator::HalResult<float>, getResonantFrequency, (), (override));
+ MOCK_METHOD(vibrator::HalResult<float>, getQFactor, (), (override));
+
MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect,
(Effect effect, EffectStrength strength,
const std::function<void()>& completionCallback),
@@ -106,6 +110,8 @@
vibrator::HalResult<vibrator::Capabilities> capabilitiesResult,
vibrator::HalResult<std::vector<Effect>> effectsResult,
vibrator::HalResult<std::vector<CompositePrimitive>> primitivesResult,
+ vibrator::HalResult<float> resonantFrequencyResult,
+ vibrator::HalResult<float> qFactorResult,
vibrator::HalResult<milliseconds> durationResult) {
EXPECT_CALL(*mMockHal.get(), ping())
.Times(Exactly(cardinality))
@@ -138,6 +144,12 @@
EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives())
.Times(Exactly(cardinality))
.WillRepeatedly(Return(primitivesResult));
+ EXPECT_CALL(*mMockHal.get(), getResonantFrequency())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(resonantFrequencyResult));
+ EXPECT_CALL(*mMockHal.get(), getQFactor())
+ .Times(Exactly(cardinality))
+ .WillRepeatedly(Return(qFactorResult));
EXPECT_CALL(*mMockHal.get(), performEffect(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _))
.Times(Exactly(cardinality))
.WillRepeatedly(Return(durationResult));
@@ -147,7 +159,7 @@
if (cardinality > 1) {
// One reconnection call after each failure.
- EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(12 * cardinality));
+ EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(14 * cardinality));
}
}
};
@@ -164,23 +176,21 @@
}
TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) {
- std::vector<Effect> effects;
- effects.push_back(Effect::CLICK);
- effects.push_back(Effect::TICK);
- std::vector<CompositePrimitive> primitives;
- primitives.push_back(CompositePrimitive::CLICK);
- primitives.push_back(CompositePrimitive::THUD);
- std::vector<CompositeEffect> compositeEffects;
- compositeEffects.push_back(
- vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f));
- compositeEffects.push_back(
- vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f));
+ std::vector<Effect> effects = {Effect::CLICK, Effect::TICK};
+ std::vector<CompositePrimitive> primitives = {CompositePrimitive::CLICK,
+ CompositePrimitive::THUD};
+ constexpr float F0 = 123.f;
+ constexpr float Q_FACTOR = 12.f;
+ const std::vector<CompositeEffect> compositeEffects =
+ {vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f),
+ vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f)};
setHalExpectations(/* cardinality= */ 1, compositeEffects, vibrator::HalResult<void>::ok(),
vibrator::HalResult<vibrator::Capabilities>::ok(
vibrator::Capabilities::ON_CALLBACK),
vibrator::HalResult<std::vector<Effect>>::ok(effects),
vibrator::HalResult<std::vector<CompositePrimitive>>::ok(primitives),
+ vibrator::HalResult<float>::ok(F0), vibrator::HalResult<float>::ok(Q_FACTOR),
vibrator::HalResult<milliseconds>::ok(100ms));
ASSERT_TRUE(mController->ping().isOk());
@@ -203,6 +213,14 @@
ASSERT_TRUE(getSupportedPrimitivesResult.isOk());
ASSERT_EQ(primitives, getSupportedPrimitivesResult.value());
+ auto getResonantFrequencyResult = mController->getResonantFrequency();
+ ASSERT_TRUE(getResonantFrequencyResult.isOk());
+ ASSERT_EQ(F0, getResonantFrequencyResult.value());
+
+ auto getQFactorResult = mController->getQFactor();
+ ASSERT_TRUE(getQFactorResult.isOk());
+ ASSERT_EQ(Q_FACTOR, getQFactorResult.value());
+
auto performEffectResult =
mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {});
ASSERT_TRUE(performEffectResult.isOk());
@@ -222,6 +240,8 @@
vibrator::HalResult<vibrator::Capabilities>::unsupported(),
vibrator::HalResult<std::vector<Effect>>::unsupported(),
vibrator::HalResult<std::vector<CompositePrimitive>>::unsupported(),
+ vibrator::HalResult<float>::unsupported(),
+ vibrator::HalResult<float>::unsupported(),
vibrator::HalResult<milliseconds>::unsupported());
ASSERT_EQ(0, mConnectCounter);
@@ -237,6 +257,8 @@
ASSERT_TRUE(mController->getCapabilities().isUnsupported());
ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
+ ASSERT_TRUE(mController->getResonantFrequency().isUnsupported());
+ ASSERT_TRUE(mController->getQFactor().isUnsupported());
ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
.isUnsupported());
ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
@@ -251,6 +273,8 @@
vibrator::HalResult<vibrator::Capabilities>::failed("message"),
vibrator::HalResult<std::vector<Effect>>::failed("message"),
vibrator::HalResult<std::vector<CompositePrimitive>>::failed("message"),
+ vibrator::HalResult<float>::failed("message"),
+ vibrator::HalResult<float>::failed("message"),
vibrator::HalResult<milliseconds>::failed("message"));
ASSERT_EQ(0, mConnectCounter);
@@ -265,6 +289,8 @@
ASSERT_TRUE(mController->getCapabilities().isFailed());
ASSERT_TRUE(mController->getSupportedEffects().isFailed());
ASSERT_TRUE(mController->getSupportedPrimitives().isFailed());
+ ASSERT_TRUE(mController->getResonantFrequency().isFailed());
+ ASSERT_TRUE(mController->getQFactor().isFailed());
ASSERT_TRUE(
mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}).isFailed());
ASSERT_TRUE(
@@ -327,13 +353,15 @@
ASSERT_TRUE(mController->getCapabilities().isUnsupported());
ASSERT_TRUE(mController->getSupportedEffects().isUnsupported());
ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported());
+ ASSERT_TRUE(mController->getResonantFrequency().isUnsupported());
+ ASSERT_TRUE(mController->getQFactor().isUnsupported());
ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {})
.isUnsupported());
ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {})
.isUnsupported());
// One connection attempt per api call.
- ASSERT_EQ(13, mConnectCounter);
+ ASSERT_EQ(15, mConnectCounter);
}
TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) {
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index 8b5caa5..5d77595 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -413,6 +413,82 @@
ASSERT_EQ(supportedPrimitives, result.value());
}
+TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyDoesNotCacheFailedResult) {
+ constexpr float F0 = 123.f;
+ EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
+
+ ASSERT_TRUE(mWrapper->getResonantFrequency().isUnsupported());
+ ASSERT_TRUE(mWrapper->getResonantFrequency().isFailed());
+
+ auto result = mWrapper->getResonantFrequency();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(F0, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetResonantFrequencyCachesResult) {
+ constexpr float F0 = 123.f;
+ EXPECT_CALL(*mMockHal.get(), getResonantFrequency(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(F0), Return(Status())));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getResonantFrequency();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(F0, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getResonantFrequency();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(F0, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorDoesNotCacheFailedResult) {
+ constexpr float Q_FACTOR = 123.f;
+ EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+ .Times(Exactly(3))
+ .WillOnce(
+ Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION)))
+ .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
+
+ ASSERT_TRUE(mWrapper->getQFactor().isUnsupported());
+ ASSERT_TRUE(mWrapper->getQFactor().isFailed());
+
+ auto result = mWrapper->getQFactor();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(Q_FACTOR, result.value());
+}
+
+TEST_F(VibratorHalWrapperAidlTest, TestGetQFactorCachesResult) {
+ constexpr float Q_FACTOR = 123.f;
+ EXPECT_CALL(*mMockHal.get(), getQFactor(_))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(Q_FACTOR), Return(Status())));
+
+ std::vector<std::thread> threads;
+ for (int i = 0; i < 10; i++) {
+ threads.push_back(std::thread([&]() {
+ auto result = mWrapper->getQFactor();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(Q_FACTOR, result.value());
+ }));
+ }
+ std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+ auto result = mWrapper->getQFactor();
+ ASSERT_TRUE(result.isOk());
+ ASSERT_EQ(Q_FACTOR, result.value());
+}
+
TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
{
InSequence seq;