Merge changes from topic "bump android.hardware.graphics.common to V5" into main
* changes:
Refactoring HAL Dataspace to AIDL Dataspace
Bump android.hardware.graphics.common V4->V5
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 376d57a..33f6d38 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -187,6 +187,7 @@
#define CGROUPFS_DIR "/sys/fs/cgroup"
#define SDK_EXT_INFO "/apex/com.android.sdkext/bin/derive_sdk"
#define DROPBOX_DIR "/data/system/dropbox"
+#define PRINT_FLAGS "/system/bin/printflags"
// TODO(narayan): Since this information has to be kept in sync
// with tombstoned, we should just put it in a common header.
@@ -1763,14 +1764,8 @@
DumpFile("PRODUCT BUILD-TIME RELEASE FLAGS", "/product/etc/build_flags.json");
DumpFile("VENDOR BUILD-TIME RELEASE FLAGS", "/vendor/etc/build_flags.json");
- DumpFile("SYSTEM BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime values)",
- "/system/etc/aconfig_flags.textproto");
- DumpFile("SYSTEM_EXT BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime"
- " values)", "/system_ext/etc/aconfig_flags.textproto");
- DumpFile("PRODUCT BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime values)",
- "/product/etc/aconfig_flags.textproto");
- DumpFile("VENDOR BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime values)",
- "/vendor/etc/aconfig_flags.textproto");
+ RunCommand("ACONFIG FLAGS", {PRINT_FLAGS},
+ CommandOptions::WithTimeout(10).Always().DropRoot().Build());
RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"});
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 39bbac3..1418e1f 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -83,6 +83,12 @@
}
prebuilt_etc {
+ name: "android.hardware.consumerir.prebuilt.xml",
+ src: "android.hardware.consumerir.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.ethernet.prebuilt.xml",
src: "android.hardware.ethernet.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/libs/binder/RpcTlsUtils.cpp b/libs/binder/RpcTlsUtils.cpp
index f3ca02a..d5c86d7 100644
--- a/libs/binder/RpcTlsUtils.cpp
+++ b/libs/binder/RpcTlsUtils.cpp
@@ -21,6 +21,8 @@
#include "Utils.h"
+#include <limits>
+
namespace android {
namespace {
diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp
index f3575cc..ddbcb57 100644
--- a/libs/binder/RpcTransportRaw.cpp
+++ b/libs/binder/RpcTransportRaw.cpp
@@ -79,7 +79,7 @@
altPoll);
}
- virtual bool isWaiting() { return mSocket.isInPollingState(); }
+ bool isWaiting() override { return mSocket.isInPollingState(); }
private:
android::RpcTransportFd mSocket;
@@ -88,7 +88,8 @@
// RpcTransportCtx with TLS disabled.
class RpcTransportCtxRaw : public RpcTransportCtx {
public:
- std::unique_ptr<RpcTransport> newTransport(android::RpcTransportFd socket, FdTrigger*) const {
+ std::unique_ptr<RpcTransport> newTransport(android::RpcTransportFd socket,
+ FdTrigger*) const override {
return std::make_unique<RpcTransportRaw>(std::move(socket));
}
std::vector<uint8_t> getCertificate(RpcCertificateFormat) const override { return {}; }
diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp
index 785f6ce..efb09e9 100644
--- a/libs/binder/RpcTransportTls.cpp
+++ b/libs/binder/RpcTransportTls.cpp
@@ -292,7 +292,7 @@
const std::optional<android::base::function_ref<status_t()>>& altPoll,
std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override;
- bool isWaiting() { return mSocket.isInPollingState(); };
+ bool isWaiting() override { return mSocket.isInPollingState(); };
private:
android::RpcTransportFd mSocket;
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index 2d05fb2..c432b3a 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -75,7 +75,7 @@
Stability::Level Stability::getLocalLevel() {
#ifdef __ANDROID_APEX__
-#error APEX can't use libbinder (must use libbinder_ndk)
+#error "APEX can't use libbinder (must use libbinder_ndk)"
#endif
#ifdef __ANDROID_VNDK__
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index a5c6094..28fb9f1 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -21,6 +21,7 @@
#include <utils/Mutex.h>
#include <map>
+#include <optional>
#include <unordered_map>
#include <variant>
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 4e231ed..45e5ace 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -17,7 +17,9 @@
#pragma once
#include <array>
+#include <limits>
#include <map> // for legacy reasons
+#include <optional>
#include <string>
#include <type_traits>
#include <variant>
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index b804f7b..2153f16 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -23,6 +23,7 @@
#include <utils/Errors.h>
#include <utils/RefBase.h>
+#include <bitset>
#include <mutex>
#include <thread>
diff --git a/libs/binder/include/binder/RpcThreads.h b/libs/binder/include/binder/RpcThreads.h
index 8abf04e..b80d116 100644
--- a/libs/binder/include/binder/RpcThreads.h
+++ b/libs/binder/include/binder/RpcThreads.h
@@ -19,6 +19,7 @@
#include <android-base/threads.h>
+#include <condition_variable>
#include <functional>
#include <memory>
#include <thread>
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index ed53891..18769b1 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -31,6 +31,7 @@
#include <android/binder_parcel.h>
#include <android/binder_status.h>
#include <assert.h>
+#include <string.h>
#include <unistd.h>
#include <cstddef>
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 9618502..9893c71 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -31,10 +31,13 @@
#include <gui/test/CallbackUtils.h>
#include <private/gui/ComposerService.h>
#include <private/gui/ComposerServiceAIDL.h>
+#include <tests/utils/ScreenshotUtils.h>
#include <ui/DisplayMode.h>
#include <ui/DisplayState.h>
#include <ui/GraphicBuffer.h>
#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Size.h>
#include <ui/Transform.h>
#include <gtest/gtest.h>
@@ -197,18 +200,23 @@
ALOGD("Display: %dx%d orientation:%d", mDisplayWidth, mDisplayHeight,
displayState.orientation);
+ mRootSurfaceControl = mClient->createSurface(String8("RootTestSurface"), mDisplayWidth,
+ mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ /*parent*/ nullptr);
+
+ t.setLayerStack(mRootSurfaceControl, ui::DEFAULT_LAYER_STACK)
+ .setLayer(mRootSurfaceControl, std::numeric_limits<int32_t>::max())
+ .show(mRootSurfaceControl)
+ .apply();
+
mSurfaceControl = mClient->createSurface(String8("TestSurface"), mDisplayWidth,
mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceBufferState,
- /*parent*/ nullptr);
- t.setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
- .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
- .show(mSurfaceControl)
- .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
- .apply();
+ /*parent*/ mRootSurfaceControl->getHandle());
- mCaptureArgs.displayToken = mDisplayToken;
- mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB;
+ mCaptureArgs.sourceCrop = Rect(ui::Size(mDisplayWidth, mDisplayHeight));
+ mCaptureArgs.layerHandle = mRootSurfaceControl->getHandle();
}
void setUpProducer(BLASTBufferQueueHelper& adapter, sp<IGraphicBufferProducer>& producer,
@@ -295,21 +303,6 @@
captureBuf->unlock();
}
- static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
- ScreenCaptureResults& captureResults) {
- const auto sf = ComposerServiceAIDL::getComposerService();
- SurfaceComposerClient::Transaction().apply(true);
-
- const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
- binder::Status status = sf->captureDisplay(captureArgs, captureListener);
- status_t err = gui::aidl_utils::statusTFromBinderStatus(status);
- if (err != NO_ERROR) {
- return err;
- }
- captureResults = captureListener->waitForResults();
- return fenceStatus(captureResults.fenceResult);
- }
-
void queueBuffer(sp<IGraphicBufferProducer> igbp, uint8_t r, uint8_t g, uint8_t b,
nsecs_t presentTimeDelay) {
int slot;
@@ -342,11 +335,12 @@
sp<IBinder> mDisplayToken;
sp<SurfaceControl> mSurfaceControl;
+ sp<SurfaceControl> mRootSurfaceControl;
uint32_t mDisplayWidth;
uint32_t mDisplayHeight;
- DisplayCaptureArgs mCaptureArgs;
+ LayerCaptureArgs mCaptureArgs;
ScreenCaptureResults mCaptureResults;
sp<CountProducerListener> mProducerListener;
};
@@ -364,7 +358,9 @@
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
sp<SurfaceControl> updateSurface =
mClient->createSurface(String8("UpdateTest"), mDisplayWidth / 2, mDisplayHeight / 2,
- PIXEL_FORMAT_RGBA_8888);
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ /*parent*/ mRootSurfaceControl->getHandle());
adapter.update(updateSurface, mDisplayWidth / 2, mDisplayHeight / 2);
ASSERT_EQ(updateSurface, adapter.getSurfaceControl());
sp<IGraphicBufferProducer> igbProducer;
@@ -451,7 +447,7 @@
Transaction().apply(true /* synchronous */);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -536,7 +532,7 @@
Transaction().apply(true /* synchronous */);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b,
@@ -552,16 +548,6 @@
(mDisplayWidth < mDisplayHeight) ? mDisplayWidth / 2 : mDisplayHeight / 2;
int32_t finalCropSideLength = bufferSideLength / 2;
- auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceEffect);
- ASSERT_NE(nullptr, bg.get());
- Transaction t;
- t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
- .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
- .setColor(bg, half3{0, 0, 0})
- .setLayer(bg, 0)
- .apply();
-
BLASTBufferQueueHelper adapter(mSurfaceControl, bufferSideLength, bufferSideLength);
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
@@ -597,7 +583,7 @@
Transaction().apply(true /* synchronous */);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b,
{10, 10, (int32_t)bufferSideLength - 10,
(int32_t)bufferSideLength - 10}));
@@ -608,17 +594,6 @@
}
TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToBufferSize) {
- // add black background
- auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceEffect);
- ASSERT_NE(nullptr, bg.get());
- Transaction t;
- t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
- .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
- .setColor(bg, half3{0, 0, 0})
- .setLayer(bg, 0)
- .apply();
-
Rect windowSize(1000, 1000);
Rect bufferSize(windowSize);
Rect bufferCrop(200, 200, 700, 700);
@@ -661,7 +636,7 @@
// ensure the buffer queue transaction has been committed
Transaction().apply(true /* synchronous */);
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
// Verify cropped region is scaled correctly.
ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {10, 10, 490, 490}));
@@ -676,17 +651,6 @@
}
TEST_F(BLASTBufferQueueTest, ScaleCroppedBufferToWindowSize) {
- // add black background
- auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceEffect);
- ASSERT_NE(nullptr, bg.get());
- Transaction t;
- t.setLayerStack(bg, ui::DEFAULT_LAYER_STACK)
- .setCrop(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
- .setColor(bg, half3{0, 0, 0})
- .setLayer(bg, 0)
- .apply();
-
Rect windowSize(1000, 1000);
Rect bufferSize(500, 500);
Rect bufferCrop(100, 100, 350, 350);
@@ -729,7 +693,7 @@
// ensure the buffer queue transaction has been committed
Transaction().apply(true /* synchronous */);
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
// Verify cropped region is scaled correctly.
ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0, {10, 10, 490, 490}));
ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 255, 0, {10, 510, 490, 990}));
@@ -779,7 +743,7 @@
Transaction().apply(true /* synchronous */);
}
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b,
@@ -815,7 +779,7 @@
Transaction().apply(true /* synchronous */);
}
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
// verify we still scale the buffer to the new size (half the screen height)
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b,
@@ -850,12 +814,12 @@
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is green
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(0, 255, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
mProducerListener->waitOnNumberReleased(1);
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -895,7 +859,7 @@
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -943,7 +907,7 @@
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -993,7 +957,7 @@
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -1051,7 +1015,7 @@
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -1084,13 +1048,13 @@
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is blue
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(0, 0, 255, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
mProducerListener->waitOnNumberReleased(2);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(255, 0, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
@@ -1186,7 +1150,7 @@
CallbackData callbackData;
transactionCallback.getCallbackData(&callbackData);
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
sync.apply();
@@ -1205,7 +1169,7 @@
mClient->createSurface(String8("TestSurface"), mDisplayWidth, mDisplayHeight,
PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceBufferState,
- /*parent*/ nullptr);
+ /*parent*/ mRootSurfaceControl->getHandle());
Transaction()
.setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
.setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
@@ -1230,7 +1194,7 @@
CallbackData callbackData;
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(255, 0, 0,
{0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
@@ -1248,7 +1212,7 @@
mClient->createSurface(String8("TestSurface"), mDisplayWidth, mDisplayHeight,
PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceBufferState,
- /*parent*/ nullptr);
+ /*parent*/ mRootSurfaceControl->getHandle());
Transaction()
.setLayerStack(mSurfaceControl, ui::DEFAULT_LAYER_STACK)
.setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
@@ -1273,7 +1237,7 @@
CallbackData callbackData;
transactionCallback.getCallbackData(&callbackData);
// capture screen and verify that it is red
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(255, 0, 0,
{0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
@@ -1406,7 +1370,7 @@
ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
Transaction().apply(true /* synchronous */);
- ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
switch (tr) {
case ui::Transform::ROT_0:
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index e8575a6..6302ff5 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -34,3 +34,10 @@
description: "Set to true to enable multi-device input: touch and stylus can be active at the same time, but in different windows"
bug: "211379801"
}
+
+flag {
+ name: "a11y_crash_on_inconsistent_event_stream"
+ namespace: "accessibility"
+ description: "Brings back fatal logging for inconsistent event streams originating from accessibility."
+ bug: "299977100"
+}
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
index 9725b00..804f96d 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -35,7 +35,20 @@
bitflags! {
/// Source of the input device or input events.
+ #[derive(Debug, PartialEq)]
pub struct Source: u32 {
+ // Constants from SourceClass, added here for compatibility reasons
+ /// SourceClass::Button
+ const SourceClassButton = SourceClass::Button as u32;
+ /// SourceClass::Pointer
+ const SourceClassPointer = SourceClass::Pointer as u32;
+ /// SourceClass::Navigation
+ const SourceClassNavigation = SourceClass::Navigation as u32;
+ /// SourceClass::Position
+ const SourceClassPosition = SourceClass::Position as u32;
+ /// SourceClass::Joystick
+ const SourceClassJoystick = SourceClass::Joystick as u32;
+
/// SOURCE_UNKNOWN
const Unknown = input_bindgen::AINPUT_SOURCE_UNKNOWN;
/// SOURCE_KEYBOARD
@@ -190,3 +203,14 @@
self.bits() & class_bits == class_bits
}
}
+
+#[cfg(test)]
+mod tests {
+ use crate::input::SourceClass;
+ use crate::Source;
+ #[test]
+ fn convert_source_class_pointer() {
+ let source = Source::from_bits(input_bindgen::AINPUT_SOURCE_CLASS_POINTER).unwrap();
+ assert!(source.is_from_class(SourceClass::Pointer));
+ }
+}
diff --git a/libs/input/tests/InputVerifier_test.cpp b/libs/input/tests/InputVerifier_test.cpp
index e24fa6e..e2eb080 100644
--- a/libs/input/tests/InputVerifier_test.cpp
+++ b/libs/input/tests/InputVerifier_test.cpp
@@ -20,10 +20,35 @@
namespace android {
+using android::base::Result;
+
TEST(InputVerifierTest, CreationWithInvalidUtfStringDoesNotCrash) {
constexpr char bytes[] = {static_cast<char>(0xC0), static_cast<char>(0x80)};
const std::string name(bytes, sizeof(bytes));
InputVerifier verifier(name);
}
+TEST(InputVerifierTest, ProcessSourceClassPointer) {
+ InputVerifier verifier("Verify testOnTouchEventScroll");
+
+ std::vector<PointerProperties> properties;
+ properties.push_back({});
+ properties.back().clear();
+ properties.back().id = 0;
+ properties.back().toolType = ToolType::UNKNOWN;
+
+ std::vector<PointerCoords> coords;
+ coords.push_back({});
+ coords.back().clear();
+ coords.back().setAxisValue(AMOTION_EVENT_AXIS_X, 75);
+ coords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, 300);
+
+ const Result<void> result =
+ verifier.processMovement(/*deviceId=*/0, AINPUT_SOURCE_CLASS_POINTER,
+ AMOTION_EVENT_ACTION_DOWN,
+ /*pointerCount=*/properties.size(), properties.data(),
+ coords.data(), /*flags=*/0);
+ ASSERT_TRUE(result.ok());
+}
+
} // namespace android
diff --git a/libs/input/tests/TfLiteMotionPredictor_test.cpp b/libs/input/tests/TfLiteMotionPredictor_test.cpp
index b5ed9e4..c3ac0b7 100644
--- a/libs/input/tests/TfLiteMotionPredictor_test.cpp
+++ b/libs/input/tests/TfLiteMotionPredictor_test.cpp
@@ -130,19 +130,19 @@
std::unique_ptr<TfLiteMotionPredictorModel> model = TfLiteMotionPredictorModel::create();
ASSERT_GT(model->inputLength(), 0u);
- const int inputLength = model->inputLength();
- ASSERT_EQ(inputLength, model->inputR().size());
- ASSERT_EQ(inputLength, model->inputPhi().size());
- ASSERT_EQ(inputLength, model->inputPressure().size());
- ASSERT_EQ(inputLength, model->inputOrientation().size());
- ASSERT_EQ(inputLength, model->inputTilt().size());
+ const size_t inputLength = model->inputLength();
+ ASSERT_EQ(inputLength, static_cast<size_t>(model->inputR().size()));
+ ASSERT_EQ(inputLength, static_cast<size_t>(model->inputPhi().size()));
+ ASSERT_EQ(inputLength, static_cast<size_t>(model->inputPressure().size()));
+ ASSERT_EQ(inputLength, static_cast<size_t>(model->inputOrientation().size()));
+ ASSERT_EQ(inputLength, static_cast<size_t>(model->inputTilt().size()));
ASSERT_TRUE(model->invoke());
- const int outputLength = model->outputLength();
- ASSERT_EQ(outputLength, model->outputR().size());
- ASSERT_EQ(outputLength, model->outputPhi().size());
- ASSERT_EQ(outputLength, model->outputPressure().size());
+ const size_t outputLength = model->outputLength();
+ ASSERT_EQ(outputLength, static_cast<size_t>(model->outputR().size()));
+ ASSERT_EQ(outputLength, static_cast<size_t>(model->outputPhi().size()));
+ ASSERT_EQ(outputLength, static_cast<size_t>(model->outputPressure().size()));
}
TEST(TfLiteMotionPredictorTest, ModelOutput) {
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index 9fa5569..47a4bfc 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -456,6 +456,7 @@
* Adobe RGB
*
* Use full range, gamma 2.2 transfer and Adobe RGB primaries
+ *
* Note: Application is responsible for gamma encoding the data as
* a 2.2 gamma encoding is not supported in HW.
*/
@@ -493,7 +494,7 @@
*
* Ultra High-definition television
*
- * Use full range, BT.709 transfer and BT2020 standard
+ * Use full range, SMPTE 170M transfer and BT2020 standard
*/
ADATASPACE_BT2020 = 147193856, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL
@@ -502,7 +503,7 @@
*
* High-definition television
*
- * Use limited range, BT.709 transfer and BT.709 standard.
+ * Use limited range, SMPTE 170M transfer and BT.709 standard.
*/
ADATASPACE_BT709 = 281083904, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED
@@ -512,6 +513,7 @@
* Digital Cinema DCI-P3
*
* Use full range, gamma 2.6 transfer and D65 DCI-P3 standard
+ *
* Note: Application is responsible for gamma encoding the data as
* a 2.6 gamma encoding is not supported in HW.
*/
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 28aa4dd..8ac0af4 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -28,6 +28,7 @@
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <ui/ShadowSettings.h>
#include <ui/StretchEffect.h>
#include <ui/Transform.h>
@@ -97,36 +98,6 @@
half3 solidColor = half3(0.0f, 0.0f, 0.0f);
};
-/*
- * Contains the configuration for the shadows drawn by single layer. Shadow follows
- * material design guidelines.
- */
-struct ShadowSettings {
- // Boundaries of the shadow.
- FloatRect boundaries = FloatRect();
-
- // Color to the ambient shadow. The alpha is premultiplied.
- vec4 ambientColor = vec4();
-
- // Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
- // depends on the light position.
- vec4 spotColor = vec4();
-
- // Position of the light source used to cast the spot shadow.
- vec3 lightPos = vec3();
-
- // Radius of the spot light source. Smaller radius will have sharper edges,
- // larger radius will have softer shadows
- float lightRadius = 0.f;
-
- // Length of the cast shadow. If length is <= 0.f no shadows will be drawn.
- float length = 0.f;
-
- // If true fill in the casting layer is translucent and the shadow needs to fill the bounds.
- // Otherwise the shadow will only be drawn around the edges of the casting layer.
- bool casterIsTranslucent = false;
-};
-
// The settings that RenderEngine requires for correctly rendering a Layer.
struct LayerSettings {
// Geometry information
@@ -194,17 +165,6 @@
return lhs.buffer == rhs.buffer && lhs.solidColor == rhs.solidColor;
}
-static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& rhs) {
- return lhs.boundaries == rhs.boundaries && lhs.ambientColor == rhs.ambientColor &&
- lhs.spotColor == rhs.spotColor && lhs.lightPos == rhs.lightPos &&
- lhs.lightRadius == rhs.lightRadius && lhs.length == rhs.length &&
- lhs.casterIsTranslucent == rhs.casterIsTranslucent;
-}
-
-static inline bool operator!=(const ShadowSettings& lhs, const ShadowSettings& rhs) {
- return !(operator==(lhs, rhs));
-}
-
static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
if (lhs.blurRegions.size() != rhs.blurRegions.size()) {
return false;
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 7dde716..6023808 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -403,7 +403,7 @@
}
void expectShadowColor(const renderengine::LayerSettings& castingLayer,
- const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
+ const ShadowSettings& shadow, const ubyte4& casterColor,
const ubyte4& backgroundColor) {
const Rect casterRect(castingLayer.geometry.boundaries);
Region casterRegion = Region(casterRect);
@@ -443,8 +443,7 @@
backgroundColor.a);
}
- void expectShadowColorWithoutCaster(const FloatRect& casterBounds,
- const renderengine::ShadowSettings& shadow,
+ void expectShadowColorWithoutCaster(const FloatRect& casterBounds, const ShadowSettings& shadow,
const ubyte4& backgroundColor) {
const float shadowInset = shadow.length * -1.0f;
const Rect casterRect(casterBounds);
@@ -463,9 +462,9 @@
backgroundColor.a);
}
- static renderengine::ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength,
- bool casterIsTranslucent) {
- renderengine::ShadowSettings shadow;
+ static ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength,
+ bool casterIsTranslucent) {
+ ShadowSettings shadow;
shadow.ambientColor = {0.0f, 0.0f, 0.0f, 0.039f};
shadow.spotColor = {0.0f, 0.0f, 0.0f, 0.19f};
shadow.lightPos = vec3(casterPos.x, casterPos.y, 0);
@@ -602,12 +601,10 @@
void fillGreenColorBufferThenClearRegion();
template <typename SourceVariant>
- void drawShadow(const renderengine::LayerSettings& castingLayer,
- const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
- const ubyte4& backgroundColor);
+ void drawShadow(const renderengine::LayerSettings& castingLayer, const ShadowSettings& shadow,
+ const ubyte4& casterColor, const ubyte4& backgroundColor);
- void drawShadowWithoutCaster(const FloatRect& castingBounds,
- const renderengine::ShadowSettings& shadow,
+ void drawShadowWithoutCaster(const FloatRect& castingBounds, const ShadowSettings& shadow,
const ubyte4& backgroundColor);
// Tonemaps grey values from sourceDataspace -> Display P3 and checks that GPU and CPU
@@ -1337,8 +1334,8 @@
template <typename SourceVariant>
void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLayer,
- const renderengine::ShadowSettings& shadow,
- const ubyte4& casterColor, const ubyte4& backgroundColor) {
+ const ShadowSettings& shadow, const ubyte4& casterColor,
+ const ubyte4& backgroundColor) {
renderengine::DisplaySettings settings;
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
settings.physicalDisplay = fullscreenRect();
@@ -1374,7 +1371,7 @@
}
void RenderEngineTest::drawShadowWithoutCaster(const FloatRect& castingBounds,
- const renderengine::ShadowSettings& shadow,
+ const ShadowSettings& shadow,
const ubyte4& backgroundColor) {
renderengine::DisplaySettings settings;
settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -2103,9 +2100,8 @@
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
- renderengine::ShadowSettings settings =
- getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
- false /* casterIsTranslucent */);
+ ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+ shadowLength, false /* casterIsTranslucent */);
drawShadowWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor);
expectShadowColorWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor);
@@ -2127,9 +2123,8 @@
renderengine::LayerSettings castingLayer;
castingLayer.geometry.boundaries = casterBounds.toFloatRect();
castingLayer.alpha = 1.0f;
- renderengine::ShadowSettings settings =
- getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
- false /* casterIsTranslucent */);
+ ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+ shadowLength, false /* casterIsTranslucent */);
drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
@@ -2152,9 +2147,8 @@
castingLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
castingLayer.geometry.boundaries = casterBounds.toFloatRect();
castingLayer.alpha = 1.0f;
- renderengine::ShadowSettings settings =
- getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
- false /* casterIsTranslucent */);
+ ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+ shadowLength, false /* casterIsTranslucent */);
drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
@@ -2177,9 +2171,8 @@
castingLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
castingLayer.geometry.boundaries = casterBounds.toFloatRect();
castingLayer.alpha = 1.0f;
- renderengine::ShadowSettings settings =
- getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
- false /* casterIsTranslucent */);
+ ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+ shadowLength, false /* casterIsTranslucent */);
drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
backgroundColor);
@@ -2204,9 +2197,8 @@
castingLayer.geometry.roundedCornersRadius = {3.0f, 3.0f};
castingLayer.geometry.roundedCornersCrop = casterBounds.toFloatRect();
castingLayer.alpha = 1.0f;
- renderengine::ShadowSettings settings =
- getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
- false /* casterIsTranslucent */);
+ ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+ shadowLength, false /* casterIsTranslucent */);
drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
backgroundColor);
@@ -2227,9 +2219,8 @@
renderengine::LayerSettings castingLayer;
castingLayer.geometry.boundaries = casterBounds.toFloatRect();
castingLayer.alpha = 0.5f;
- renderengine::ShadowSettings settings =
- getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
- true /* casterIsTranslucent */);
+ ShadowSettings settings = getShadowSettings(vec2(casterBounds.left, casterBounds.top),
+ shadowLength, true /* casterIsTranslucent */);
drawShadow<BufferSourceVariant<RelaxOpaqueBufferVariant>>(castingLayer, settings, casterColor,
backgroundColor);
diff --git a/libs/ui/include/ui/ShadowSettings.h b/libs/ui/include/ui/ShadowSettings.h
new file mode 100644
index 0000000..c0b83b8
--- /dev/null
+++ b/libs/ui/include/ui/ShadowSettings.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/vec4.h>
+#include "FloatRect.h"
+
+namespace android {
+
+/*
+ * Contains the configuration for the shadows drawn by single layer. Shadow follows
+ * material design guidelines.
+ */
+struct ShadowSettings {
+ // Boundaries of the shadow.
+ FloatRect boundaries = FloatRect();
+
+ // Color to the ambient shadow. The alpha is premultiplied.
+ vec4 ambientColor = vec4();
+
+ // Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+ // depends on the light position.
+ vec4 spotColor = vec4();
+
+ // Position of the light source used to cast the spot shadow.
+ vec3 lightPos = vec3();
+
+ // Radius of the spot light source. Smaller radius will have sharper edges,
+ // larger radius will have softer shadows
+ float lightRadius = 0.f;
+
+ // Length of the cast shadow. If length is <= 0.f no shadows will be drawn.
+ float length = 0.f;
+
+ // If true fill in the casting layer is translucent and the shadow needs to fill the bounds.
+ // Otherwise the shadow will only be drawn around the edges of the casting layer.
+ bool casterIsTranslucent = false;
+};
+
+static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& rhs) {
+ return lhs.boundaries == rhs.boundaries && lhs.ambientColor == rhs.ambientColor &&
+ lhs.spotColor == rhs.spotColor && lhs.lightPos == rhs.lightPos &&
+ lhs.lightRadius == rhs.lightRadius && lhs.length == rhs.length &&
+ lhs.casterIsTranslucent == rhs.casterIsTranslucent;
+}
+
+static inline bool operator!=(const ShadowSettings& lhs, const ShadowSettings& rhs) {
+ return !(operator==(lhs, rhs));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 5d1d4af..76729ef 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -182,6 +182,7 @@
filegroup {
name: "libinputflinger_base_sources",
srcs: [
+ "InputDeviceMetricsSource.cpp",
"InputListener.cpp",
"InputReaderBase.cpp",
"InputThread.cpp",
@@ -199,6 +200,7 @@
"libcutils",
"libinput",
"liblog",
+ "libstatslog",
"libutils",
],
header_libs: [
diff --git a/services/inputflinger/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp
index 8e04150..cefb140 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.cpp
+++ b/services/inputflinger/InputDeviceMetricsCollector.cpp
@@ -17,11 +17,10 @@
#define LOG_TAG "InputDeviceMetricsCollector"
#include "InputDeviceMetricsCollector.h"
-#include "KeyCodeClassifications.h"
+#include "InputDeviceMetricsSource.h"
#include <android-base/stringprintf.h>
#include <input/PrintTools.h>
-#include <linux/input.h>
namespace android {
@@ -113,96 +112,6 @@
} // namespace
-InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t keyboardType,
- const NotifyKeyArgs& keyArgs) {
- if (!isFromSource(keyArgs.source, AINPUT_SOURCE_KEYBOARD)) {
- return InputDeviceUsageSource::UNKNOWN;
- }
-
- if (isFromSource(keyArgs.source, AINPUT_SOURCE_DPAD) &&
- DPAD_ALL_KEYCODES.count(keyArgs.keyCode) != 0) {
- return InputDeviceUsageSource::DPAD;
- }
-
- if (isFromSource(keyArgs.source, AINPUT_SOURCE_GAMEPAD) &&
- GAMEPAD_KEYCODES.count(keyArgs.keyCode) != 0) {
- return InputDeviceUsageSource::GAMEPAD;
- }
-
- if (keyboardType == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
- return InputDeviceUsageSource::KEYBOARD;
- }
-
- return InputDeviceUsageSource::BUTTONS;
-}
-
-std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs& motionArgs) {
- LOG_ALWAYS_FATAL_IF(motionArgs.getPointerCount() < 1, "Received motion args without pointers");
- std::set<InputDeviceUsageSource> sources;
-
- for (uint32_t i = 0; i < motionArgs.getPointerCount(); i++) {
- const auto toolType = motionArgs.pointerProperties[i].toolType;
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE)) {
- if (toolType == ToolType::MOUSE) {
- sources.emplace(InputDeviceUsageSource::MOUSE);
- continue;
- }
- if (toolType == ToolType::FINGER) {
- sources.emplace(InputDeviceUsageSource::TOUCHPAD);
- continue;
- }
- if (isStylusToolType(toolType)) {
- sources.emplace(InputDeviceUsageSource::STYLUS_INDIRECT);
- continue;
- }
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE_RELATIVE) &&
- toolType == ToolType::MOUSE) {
- sources.emplace(InputDeviceUsageSource::MOUSE_CAPTURED);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHPAD) &&
- toolType == ToolType::FINGER) {
- sources.emplace(InputDeviceUsageSource::TOUCHPAD_CAPTURED);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_BLUETOOTH_STYLUS) &&
- isStylusToolType(toolType)) {
- sources.emplace(InputDeviceUsageSource::STYLUS_FUSED);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_STYLUS) && isStylusToolType(toolType)) {
- sources.emplace(InputDeviceUsageSource::STYLUS_DIRECT);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCH_NAVIGATION)) {
- sources.emplace(InputDeviceUsageSource::TOUCH_NAVIGATION);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_JOYSTICK)) {
- sources.emplace(InputDeviceUsageSource::JOYSTICK);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_ROTARY_ENCODER)) {
- sources.emplace(InputDeviceUsageSource::ROTARY_ENCODER);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_TRACKBALL)) {
- sources.emplace(InputDeviceUsageSource::TRACKBALL);
- continue;
- }
- if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHSCREEN)) {
- sources.emplace(InputDeviceUsageSource::TOUCHSCREEN);
- continue;
- }
- sources.emplace(InputDeviceUsageSource::UNKNOWN);
- }
-
- return sources;
-}
-
-// --- InputDeviceMetricsCollector ---
-
InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener)
: InputDeviceMetricsCollector(listener, sStatsdLogger, DEFAULT_USAGE_SESSION_TIMEOUT) {}
diff --git a/services/inputflinger/InputDeviceMetricsCollector.h b/services/inputflinger/InputDeviceMetricsCollector.h
index 7775087..9633664 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.h
+++ b/services/inputflinger/InputDeviceMetricsCollector.h
@@ -16,6 +16,7 @@
#pragma once
+#include "InputDeviceMetricsSource.h"
#include "InputListener.h"
#include "NotifyArgs.h"
#include "SyncQueue.h"
@@ -23,7 +24,6 @@
#include <ftl/mixins.h>
#include <gui/WindowInfo.h>
#include <input/InputDevice.h>
-#include <statslog.h>
#include <chrono>
#include <functional>
#include <map>
@@ -52,38 +52,6 @@
virtual void dump(std::string& dump) = 0;
};
-/**
- * Enum representation of the InputDeviceUsageSource.
- */
-enum class InputDeviceUsageSource : int32_t {
- UNKNOWN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__UNKNOWN,
- BUTTONS = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__BUTTONS,
- KEYBOARD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__KEYBOARD,
- DPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__DPAD,
- GAMEPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__GAMEPAD,
- JOYSTICK = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__JOYSTICK,
- MOUSE = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE,
- MOUSE_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE_CAPTURED,
- TOUCHPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD,
- TOUCHPAD_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD_CAPTURED,
- ROTARY_ENCODER = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__ROTARY_ENCODER,
- STYLUS_DIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_DIRECT,
- STYLUS_INDIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_INDIRECT,
- STYLUS_FUSED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_FUSED,
- TOUCH_NAVIGATION = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCH_NAVIGATION,
- TOUCHSCREEN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHSCREEN,
- TRACKBALL = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TRACKBALL,
-
- ftl_first = UNKNOWN,
- ftl_last = TRACKBALL,
-};
-
-/** Returns the InputDeviceUsageSource that corresponds to the key event. */
-InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t keyboardType, const NotifyKeyArgs&);
-
-/** Returns the InputDeviceUsageSources that correspond to the motion event. */
-std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs&);
-
/** The logging interface for the metrics collector, injected for testing. */
class InputDeviceMetricsLogger {
public:
diff --git a/services/inputflinger/InputDeviceMetricsSource.cpp b/services/inputflinger/InputDeviceMetricsSource.cpp
new file mode 100644
index 0000000..dee4cb8
--- /dev/null
+++ b/services/inputflinger/InputDeviceMetricsSource.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2023 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 "InputDeviceMetricsSource.h"
+
+#include "KeyCodeClassifications.h"
+
+#include <android/input.h>
+#include <input/Input.h>
+#include <linux/input.h>
+#include <log/log_main.h>
+
+#include <set>
+
+namespace android {
+
+InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t keyboardType,
+ const NotifyKeyArgs& keyArgs) {
+ if (!isFromSource(keyArgs.source, AINPUT_SOURCE_KEYBOARD)) {
+ return InputDeviceUsageSource::UNKNOWN;
+ }
+
+ if (isFromSource(keyArgs.source, AINPUT_SOURCE_DPAD) &&
+ DPAD_ALL_KEYCODES.count(keyArgs.keyCode) != 0) {
+ return InputDeviceUsageSource::DPAD;
+ }
+
+ if (isFromSource(keyArgs.source, AINPUT_SOURCE_GAMEPAD) &&
+ GAMEPAD_KEYCODES.count(keyArgs.keyCode) != 0) {
+ return InputDeviceUsageSource::GAMEPAD;
+ }
+
+ if (keyboardType == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
+ return InputDeviceUsageSource::KEYBOARD;
+ }
+
+ return InputDeviceUsageSource::BUTTONS;
+}
+
+std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs& motionArgs) {
+ LOG_ALWAYS_FATAL_IF(motionArgs.getPointerCount() < 1, "Received motion args without pointers");
+ std::set<InputDeviceUsageSource> sources;
+
+ for (uint32_t i = 0; i < motionArgs.getPointerCount(); i++) {
+ const auto toolType = motionArgs.pointerProperties[i].toolType;
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE)) {
+ if (toolType == ToolType::MOUSE) {
+ sources.emplace(InputDeviceUsageSource::MOUSE);
+ continue;
+ }
+ if (toolType == ToolType::FINGER) {
+ sources.emplace(InputDeviceUsageSource::TOUCHPAD);
+ continue;
+ }
+ if (isStylusToolType(toolType)) {
+ sources.emplace(InputDeviceUsageSource::STYLUS_INDIRECT);
+ continue;
+ }
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE_RELATIVE) &&
+ toolType == ToolType::MOUSE) {
+ sources.emplace(InputDeviceUsageSource::MOUSE_CAPTURED);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHPAD) &&
+ toolType == ToolType::FINGER) {
+ sources.emplace(InputDeviceUsageSource::TOUCHPAD_CAPTURED);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_BLUETOOTH_STYLUS) &&
+ isStylusToolType(toolType)) {
+ sources.emplace(InputDeviceUsageSource::STYLUS_FUSED);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_STYLUS) && isStylusToolType(toolType)) {
+ sources.emplace(InputDeviceUsageSource::STYLUS_DIRECT);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCH_NAVIGATION)) {
+ sources.emplace(InputDeviceUsageSource::TOUCH_NAVIGATION);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_JOYSTICK)) {
+ sources.emplace(InputDeviceUsageSource::JOYSTICK);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_ROTARY_ENCODER)) {
+ sources.emplace(InputDeviceUsageSource::ROTARY_ENCODER);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_TRACKBALL)) {
+ sources.emplace(InputDeviceUsageSource::TRACKBALL);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHSCREEN)) {
+ sources.emplace(InputDeviceUsageSource::TOUCHSCREEN);
+ continue;
+ }
+ sources.emplace(InputDeviceUsageSource::UNKNOWN);
+ }
+
+ return sources;
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputDeviceMetricsSource.h b/services/inputflinger/InputDeviceMetricsSource.h
new file mode 100644
index 0000000..3ac91c8
--- /dev/null
+++ b/services/inputflinger/InputDeviceMetricsSource.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "NotifyArgs.h"
+
+#include <linux/input.h>
+#include <statslog.h>
+
+namespace android {
+
+/**
+ * Enum representation of the InputDeviceUsageSource.
+ */
+enum class InputDeviceUsageSource : int32_t {
+ UNKNOWN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__UNKNOWN,
+ BUTTONS = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__BUTTONS,
+ KEYBOARD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__KEYBOARD,
+ DPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__DPAD,
+ GAMEPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__GAMEPAD,
+ JOYSTICK = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__JOYSTICK,
+ MOUSE = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE,
+ MOUSE_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE_CAPTURED,
+ TOUCHPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD,
+ TOUCHPAD_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD_CAPTURED,
+ ROTARY_ENCODER = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__ROTARY_ENCODER,
+ STYLUS_DIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_DIRECT,
+ STYLUS_INDIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_INDIRECT,
+ STYLUS_FUSED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_FUSED,
+ TOUCH_NAVIGATION = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCH_NAVIGATION,
+ TOUCHSCREEN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHSCREEN,
+ TRACKBALL = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TRACKBALL,
+
+ ftl_first = UNKNOWN,
+ ftl_last = TRACKBALL,
+};
+
+/** Returns the InputDeviceUsageSource that corresponds to the key event. */
+InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t keyboardType, const NotifyKeyArgs&);
+
+/** Returns the InputDeviceUsageSources that correspond to the motion event. */
+std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs&);
+
+} // namespace android
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 4251682..27b86bd 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -25,6 +25,7 @@
#include <android-base/stringprintf.h>
#include <android/os/IInputConstants.h>
#include <binder/Binder.h>
+#include <com_android_input_flags.h>
#include <ftl/enum.h>
#include <log/log_event_list.h>
#if defined(__ANDROID__)
@@ -67,6 +68,7 @@
using android::gui::WindowInfoHandle;
using android::os::InputEventInjectionResult;
using android::os::InputEventInjectionSync;
+namespace input_flags = com::android::input::flags;
namespace android::inputdispatcher {
@@ -670,11 +672,11 @@
// This pointer was already sent to the window. Use ACTION_HOVER_MOVE.
if (CC_UNLIKELY(maskedAction != AMOTION_EVENT_ACTION_HOVER_MOVE)) {
android::base::LogSeverity severity = android::base::LogSeverity::FATAL;
- if (entry.flags & AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT) {
+ if (!input_flags::a11y_crash_on_inconsistent_event_stream() &&
+ entry.flags & AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT) {
// The Accessibility injected touch exploration event stream
// has known inconsistencies, so log ERROR instead of
// crashing the device with FATAL.
- // TODO(b/299977100): Move a11y severity back to FATAL.
severity = android::base::LogSeverity::ERROR;
}
LOG(severity) << "Expected ACTION_HOVER_MOVE instead of " << entry.getDescription();
@@ -3921,6 +3923,16 @@
void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
const std::shared_ptr<Connection>& connection, const CancelationOptions& options) {
+ if ((options.mode == CancelationOptions::Mode::CANCEL_POINTER_EVENTS ||
+ options.mode == CancelationOptions::Mode::CANCEL_ALL_EVENTS) &&
+ mDragState && mDragState->dragWindow->getToken() == connection->inputChannel->getToken()) {
+ LOG(INFO) << __func__
+ << ": Canceling drag and drop because the pointers for the drag window are being "
+ "canceled.";
+ sendDropWindowCommandLocked(nullptr, /*x=*/0, /*y=*/0);
+ mDragState.reset();
+ }
+
if (connection->status == Connection::Status::BROKEN) {
return;
}
@@ -3944,7 +3956,6 @@
android_log_event_list(LOGTAG_INPUT_CANCEL)
<< connection->getInputChannelName().c_str() << reason << LOG_ID_EVENTS;
- InputTarget target;
sp<WindowInfoHandle> windowHandle;
if (options.displayId) {
windowHandle = getWindowHandleLocked(connection->inputChannel->getConnectionToken(),
@@ -3952,27 +3963,47 @@
} else {
windowHandle = getWindowHandleLocked(connection->inputChannel->getConnectionToken());
}
- if (windowHandle != nullptr) {
- const WindowInfo* windowInfo = windowHandle->getInfo();
- target.setDefaultPointerTransform(windowInfo->transform);
- target.globalScaleFactor = windowInfo->globalScaleFactor;
- }
- target.inputChannel = connection->inputChannel;
- target.flags = InputTarget::Flags::DISPATCH_AS_IS;
const bool wasEmpty = connection->outboundQueue.empty();
for (size_t i = 0; i < cancelationEvents.size(); i++) {
std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]);
+ std::vector<InputTarget> targets{};
+ // The target to use if we don't find a window associated with the channel.
+ const InputTarget fallbackTarget{.inputChannel = connection->inputChannel,
+ .flags = InputTarget::Flags::DISPATCH_AS_IS};
+
switch (cancelationEventEntry->type) {
case EventEntry::Type::KEY: {
- logOutboundKeyDetails("cancel - ",
- static_cast<const KeyEntry&>(*cancelationEventEntry));
+ const auto& keyEntry = static_cast<const KeyEntry&>(*cancelationEventEntry);
+ if (windowHandle != nullptr) {
+ addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_IS,
+ /*pointerIds=*/{}, keyEntry.downTime, targets);
+ } else {
+ targets.emplace_back(fallbackTarget);
+ }
+ logOutboundKeyDetails("cancel - ", keyEntry);
break;
}
case EventEntry::Type::MOTION: {
- logOutboundMotionDetails("cancel - ",
- static_cast<const MotionEntry&>(*cancelationEventEntry));
+ const auto& motionEntry = static_cast<const MotionEntry&>(*cancelationEventEntry);
+ if (windowHandle != nullptr) {
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
+ for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount;
+ pointerIndex++) {
+ pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
+ }
+ addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_IS,
+ pointerIds, motionEntry.downTime, targets);
+ } else {
+ targets.emplace_back(fallbackTarget);
+ const auto it = mDisplayInfos.find(motionEntry.displayId);
+ if (it != mDisplayInfos.end()) {
+ targets.back().displayTransform = it->second.transform;
+ targets.back().setDefaultPointerTransform(it->second.transform);
+ }
+ }
+ logOutboundMotionDetails("cancel - ", motionEntry);
break;
}
case EventEntry::Type::FOCUS:
@@ -3992,7 +4023,8 @@
}
}
- enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), target,
+ if (targets.size() != 1) LOG(FATAL) << __func__ << ": InputTarget not created";
+ enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), targets[0],
InputTarget::Flags::DISPATCH_AS_IS);
}
@@ -4021,23 +4053,33 @@
connection->getInputChannelName().c_str(), downEvents.size());
}
- InputTarget target;
sp<WindowInfoHandle> windowHandle =
getWindowHandleLocked(connection->inputChannel->getConnectionToken());
- if (windowHandle != nullptr) {
- const WindowInfo* windowInfo = windowHandle->getInfo();
- target.setDefaultPointerTransform(windowInfo->transform);
- target.globalScaleFactor = windowInfo->globalScaleFactor;
- }
- target.inputChannel = connection->inputChannel;
- target.flags = targetFlags;
const bool wasEmpty = connection->outboundQueue.empty();
for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) {
+ std::vector<InputTarget> targets{};
switch (downEventEntry->type) {
case EventEntry::Type::MOTION: {
- logOutboundMotionDetails("down - ",
- static_cast<const MotionEntry&>(*downEventEntry));
+ const auto& motionEntry = static_cast<const MotionEntry&>(*downEventEntry);
+ if (windowHandle != nullptr) {
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
+ for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount;
+ pointerIndex++) {
+ pointerIds.set(motionEntry.pointerProperties[pointerIndex].id);
+ }
+ addWindowTargetLocked(windowHandle, targetFlags, pointerIds,
+ motionEntry.downTime, targets);
+ } else {
+ targets.emplace_back(InputTarget{.inputChannel = connection->inputChannel,
+ .flags = targetFlags});
+ const auto it = mDisplayInfos.find(motionEntry.displayId);
+ if (it != mDisplayInfos.end()) {
+ targets.back().displayTransform = it->second.transform;
+ targets.back().setDefaultPointerTransform(it->second.transform);
+ }
+ }
+ logOutboundMotionDetails("down - ", motionEntry);
break;
}
@@ -4055,7 +4097,8 @@
}
}
- enqueueDispatchEntryLocked(connection, std::move(downEventEntry), target,
+ if (targets.size() != 1) LOG(FATAL) << __func__ << ": InputTarget not created";
+ enqueueDispatchEntryLocked(connection, std::move(downEventEntry), targets[0],
InputTarget::Flags::DISPATCH_AS_IS);
}
@@ -6596,6 +6639,7 @@
}
}
if (changes.newFocus) {
+ resetNoFocusedWindowTimeoutLocked();
enqueueFocusEventLocked(changes.newFocus, /*hasFocus=*/true, changes.reason);
}
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index d87a5a7..76b04b6 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -50,6 +50,7 @@
"HardwareProperties_test.cpp",
"HardwareStateConverter_test.cpp",
"InputDeviceMetricsCollector_test.cpp",
+ "InputDeviceMetricsSource_test.cpp",
"InputMapperTest.cpp",
"InputProcessor_test.cpp",
"InputProcessorConverter_test.cpp",
@@ -93,6 +94,7 @@
},
},
static_libs: [
+ "libflagtest",
"libc++fs",
"libgmock",
],
diff --git a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
index fdf9ed1..85e055d 100644
--- a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
+++ b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
@@ -36,7 +36,6 @@
constexpr auto USAGE_TIMEOUT = 8765309ns;
constexpr auto TIME = 999999ns;
-constexpr auto ALL_USAGE_SOURCES = ftl::enum_range<InputDeviceUsageSource>();
constexpr int32_t DEVICE_ID = 3;
constexpr int32_t DEVICE_ID_2 = 4;
@@ -48,10 +47,6 @@
const std::string UNIQUE_ID = "Yosemite";
constexpr uint32_t TOUCHSCREEN = AINPUT_SOURCE_TOUCHSCREEN;
constexpr uint32_t STYLUS = AINPUT_SOURCE_STYLUS;
-constexpr uint32_t KEY_SOURCES =
- AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD;
-constexpr int32_t POINTER_1_DOWN =
- AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
InputDeviceIdentifier generateTestIdentifier(int32_t id = DEVICE_ID) {
InputDeviceIdentifier identifier;
@@ -87,258 +82,6 @@
} // namespace
-// --- InputDeviceMetricsCollectorDeviceClassificationTest ---
-
-class DeviceClassificationFixture : public ::testing::Test,
- public ::testing::WithParamInterface<InputDeviceUsageSource> {};
-
-TEST_P(DeviceClassificationFixture, ValidClassifications) {
- const InputDeviceUsageSource usageSource = GetParam();
-
- // Use a switch to ensure a test is added for all source classifications.
- switch (usageSource) {
- case InputDeviceUsageSource::UNKNOWN: {
- ASSERT_EQ(InputDeviceUsageSource::UNKNOWN,
- getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NONE,
- KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, TOUCHSCREEN)
- .build()));
-
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::UNKNOWN};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_KEYBOARD)
- .pointer(PointerBuilder(/*id=*/1, ToolType::PALM)
- .x(100)
- .y(200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::BUTTONS: {
- ASSERT_EQ(InputDeviceUsageSource::BUTTONS,
- getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
- KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
- .keyCode(AKEYCODE_STYLUS_BUTTON_TAIL)
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::KEYBOARD: {
- ASSERT_EQ(InputDeviceUsageSource::KEYBOARD,
- getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
- KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::DPAD: {
- ASSERT_EQ(InputDeviceUsageSource::DPAD,
- getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
- KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
- .keyCode(AKEYCODE_DPAD_CENTER)
- .build()));
-
- ASSERT_EQ(InputDeviceUsageSource::DPAD,
- getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
- KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
- .keyCode(AKEYCODE_DPAD_CENTER)
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::GAMEPAD: {
- ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
- getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
- KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
- .keyCode(AKEYCODE_BUTTON_A)
- .build()));
-
- ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
- getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
- KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
- .keyCode(AKEYCODE_BUTTON_A)
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::JOYSTICK: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::JOYSTICK};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_JOYSTICK)
- .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
- .axis(AMOTION_EVENT_AXIS_GAS, 1.f))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::MOUSE: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
- AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
- .x(100)
- .y(200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::MOUSE_CAPTURED: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE_CAPTURED};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
- AINPUT_SOURCE_MOUSE_RELATIVE)
- .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
- .x(100)
- .y(200)
- .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 100)
- .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::TOUCHPAD: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
- .x(100)
- .y(200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::TOUCHPAD_CAPTURED: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD_CAPTURED};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHPAD)
- .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
- .x(100)
- .y(200)
- .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 1)
- .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 2))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::ROTARY_ENCODER: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::ROTARY_ENCODER};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
- AINPUT_SOURCE_ROTARY_ENCODER)
- .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
- .axis(AMOTION_EVENT_AXIS_SCROLL, 10)
- .axis(AMOTION_EVENT_AXIS_VSCROLL, 10))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::STYLUS_DIRECT: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_DIRECT};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
- STYLUS | TOUCHSCREEN)
- .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
- .x(100)
- .y(200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::STYLUS_INDIRECT: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_INDIRECT};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
- STYLUS | TOUCHSCREEN | AINPUT_SOURCE_MOUSE)
- .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
- .x(100)
- .y(200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::STYLUS_FUSED: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_FUSED};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
- AINPUT_SOURCE_BLUETOOTH_STYLUS | TOUCHSCREEN)
- .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
- .x(100)
- .y(200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::TOUCH_NAVIGATION: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCH_NAVIGATION};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
- AINPUT_SOURCE_TOUCH_NAVIGATION)
- .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
- .x(100)
- .y(200))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::TOUCHSCREEN: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN)
- .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
- .x(100)
- .y(200))
- .pointer(PointerBuilder(/*id=*/2, ToolType::FINGER)
- .x(300)
- .y(400))
- .build()));
- break;
- }
-
- case InputDeviceUsageSource::TRACKBALL: {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TRACKBALL};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
- AINPUT_SOURCE_TRACKBALL)
- .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
- .axis(AMOTION_EVENT_AXIS_VSCROLL, 100)
- .axis(AMOTION_EVENT_AXIS_HSCROLL, 200))
- .build()));
- break;
- }
- }
-}
-
-INSTANTIATE_TEST_SUITE_P(InputDeviceMetricsCollectorDeviceClassificationTest,
- DeviceClassificationFixture,
- ::testing::ValuesIn(ALL_USAGE_SOURCES.begin(), ALL_USAGE_SOURCES.end()),
- [](const testing::TestParamInfo<InputDeviceUsageSource>& testParamInfo) {
- return ftl::enum_string(testParamInfo.param);
- });
-
-TEST(InputDeviceMetricsCollectorDeviceClassificationTest, MixedClassificationTouchscreenStylus) {
- std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN,
- InputDeviceUsageSource::STYLUS_DIRECT};
- ASSERT_EQ(srcs,
- getUsageSourcesForMotionArgs(
- MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN | STYLUS)
- .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(100).y(200))
- .pointer(PointerBuilder(/*id=*/2, ToolType::STYLUS).x(300).y(400))
- .build()));
-}
-
// --- InputDeviceMetricsCollectorTest ---
class InputDeviceMetricsCollectorTest : public testing::Test, public InputDeviceMetricsLogger {
diff --git a/services/inputflinger/tests/InputDeviceMetricsSource_test.cpp b/services/inputflinger/tests/InputDeviceMetricsSource_test.cpp
new file mode 100644
index 0000000..84ef52c
--- /dev/null
+++ b/services/inputflinger/tests/InputDeviceMetricsSource_test.cpp
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2023 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 "../InputDeviceMetricsSource.h"
+
+#include <NotifyArgsBuilders.h>
+
+#include <android/input.h>
+#include <ftl/enum.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <input/InputEventBuilders.h>
+#include <linux/input.h>
+
+#include <set>
+
+namespace android {
+
+namespace {
+
+constexpr auto ALL_USAGE_SOURCES = ftl::enum_range<InputDeviceUsageSource>();
+constexpr uint32_t TOUCHSCREEN = AINPUT_SOURCE_TOUCHSCREEN;
+constexpr uint32_t STYLUS = AINPUT_SOURCE_STYLUS;
+constexpr uint32_t KEY_SOURCES =
+ AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD;
+constexpr int32_t POINTER_1_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+} // namespace
+
+// --- InputDeviceMetricsSourceDeviceClassificationTest ---
+
+class DeviceClassificationFixture : public ::testing::Test,
+ public ::testing::WithParamInterface<InputDeviceUsageSource> {};
+
+TEST_P(DeviceClassificationFixture, ValidClassifications) {
+ const InputDeviceUsageSource usageSource = GetParam();
+
+ // Use a switch to ensure a test is added for all source classifications.
+ switch (usageSource) {
+ case InputDeviceUsageSource::UNKNOWN: {
+ ASSERT_EQ(InputDeviceUsageSource::UNKNOWN,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NONE,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, TOUCHSCREEN)
+ .build()));
+
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::UNKNOWN};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_KEYBOARD)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::PALM)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::BUTTONS: {
+ ASSERT_EQ(InputDeviceUsageSource::BUTTONS,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .keyCode(AKEYCODE_STYLUS_BUTTON_TAIL)
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::KEYBOARD: {
+ ASSERT_EQ(InputDeviceUsageSource::KEYBOARD,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::DPAD: {
+ ASSERT_EQ(InputDeviceUsageSource::DPAD,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .keyCode(AKEYCODE_DPAD_CENTER)
+ .build()));
+
+ ASSERT_EQ(InputDeviceUsageSource::DPAD,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .keyCode(AKEYCODE_DPAD_CENTER)
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::GAMEPAD: {
+ ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .keyCode(AKEYCODE_BUTTON_A)
+ .build()));
+
+ ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
+ getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .keyCode(AKEYCODE_BUTTON_A)
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::JOYSTICK: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::JOYSTICK};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_JOYSTICK)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
+ .axis(AMOTION_EVENT_AXIS_GAS, 1.f))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::MOUSE: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::MOUSE_CAPTURED: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE_CAPTURED};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
+ AINPUT_SOURCE_MOUSE_RELATIVE)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
+ .x(100)
+ .y(200)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 100)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::TOUCHPAD: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::TOUCHPAD_CAPTURED: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD_CAPTURED};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHPAD)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+ .x(100)
+ .y(200)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 1)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 2))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::ROTARY_ENCODER: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::ROTARY_ENCODER};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
+ AINPUT_SOURCE_ROTARY_ENCODER)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
+ .axis(AMOTION_EVENT_AXIS_SCROLL, 10)
+ .axis(AMOTION_EVENT_AXIS_VSCROLL, 10))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::STYLUS_DIRECT: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_DIRECT};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ STYLUS | TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::STYLUS_INDIRECT: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_INDIRECT};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ STYLUS | TOUCHSCREEN | AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::STYLUS_FUSED: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_FUSED};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_BLUETOOTH_STYLUS | TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::TOUCH_NAVIGATION: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCH_NAVIGATION};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
+ AINPUT_SOURCE_TOUCH_NAVIGATION)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::TOUCHSCREEN: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+ .x(100)
+ .y(200))
+ .pointer(PointerBuilder(/*id=*/2, ToolType::FINGER)
+ .x(300)
+ .y(400))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::TRACKBALL: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TRACKBALL};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
+ AINPUT_SOURCE_TRACKBALL)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
+ .axis(AMOTION_EVENT_AXIS_VSCROLL, 100)
+ .axis(AMOTION_EVENT_AXIS_HSCROLL, 200))
+ .build()));
+ break;
+ }
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(InputDeviceMetricsSourceDeviceClassificationTest,
+ DeviceClassificationFixture,
+ ::testing::ValuesIn(ALL_USAGE_SOURCES.begin(), ALL_USAGE_SOURCES.end()),
+ [](const testing::TestParamInfo<InputDeviceUsageSource>& testParamInfo) {
+ return ftl::enum_string(testParamInfo.param);
+ });
+
+TEST(InputDeviceMetricsSourceDeviceClassificationTest, MixedClassificationTouchscreenStylus) {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN,
+ InputDeviceUsageSource::STYLUS_DIRECT};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN | STYLUS)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(100).y(200))
+ .pointer(PointerBuilder(/*id=*/2, ToolType::STYLUS).x(300).y(400))
+ .build()));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 215989d..7925a27 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -24,7 +24,9 @@
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <binder/Binder.h>
+#include <com_android_input_flags.h>
#include <fcntl.h>
+#include <flag_macros.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <input/Input.h>
@@ -184,6 +186,18 @@
return receivedX == x && receivedY == y;
}
+MATCHER_P2(WithRawCoords, x, y, "MotionEvent with specified raw coordinates") {
+ if (arg.getPointerCount() != 1) {
+ *result_listener << "Expected 1 pointer, got " << arg.getPointerCount();
+ return false;
+ }
+ const float receivedX = arg.getRawX(/*pointerIndex=*/0);
+ const float receivedY = arg.getRawY(/*pointerIndex=*/0);
+ *result_listener << "expected raw coords (" << x << ", " << y << "), but got (" << receivedX
+ << ", " << receivedY << ")";
+ return receivedX == x && receivedY == y;
+}
+
MATCHER_P(WithPointerCount, pointerCount, "MotionEvent with specified number of pointers") {
*result_listener << "expected pointerCount " << pointerCount << ", but got "
<< arg.getPointerCount();
@@ -1451,6 +1465,68 @@
std::atomic<int32_t> FakeWindowHandle::sId{1};
+class FakeMonitorReceiver {
+public:
+ FakeMonitorReceiver(InputDispatcher& dispatcher, const std::string name, int32_t displayId) {
+ base::Result<std::unique_ptr<InputChannel>> channel =
+ dispatcher.createInputMonitor(displayId, name, MONITOR_PID);
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
+ }
+
+ sp<IBinder> getToken() { return mInputReceiver->getToken(); }
+
+ void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
+ expectedFlags);
+ }
+
+ std::optional<int32_t> receiveEvent() {
+ return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ }
+
+ void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
+
+ void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
+ expectedDisplayId, expectedFlags);
+ }
+
+ void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE,
+ expectedDisplayId, expectedFlags);
+ }
+
+ void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP,
+ expectedDisplayId, expectedFlags);
+ }
+
+ void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeMotionEvent(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
+ WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags | AMOTION_EVENT_FLAG_CANCELED)));
+ }
+
+ void consumeMotionPointerDown(int32_t pointerIdx) {
+ int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ mInputReceiver->consumeEvent(InputEventType::MOTION, action, ADISPLAY_ID_DEFAULT,
+ /*expectedFlags=*/0);
+ }
+
+ void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
+ mInputReceiver->consumeMotionEvent(matcher);
+ }
+
+ MotionEvent* consumeMotion() { return mInputReceiver->consumeMotion(); }
+
+ void assertNoEvents() { mInputReceiver->assertNoEvents(); }
+
+private:
+ std::unique_ptr<FakeInputReceiver> mInputReceiver;
+};
+
static InputEventInjectionResult injectKey(
InputDispatcher& dispatcher, int32_t action, int32_t repeatCount,
int32_t displayId = ADISPLAY_ID_NONE,
@@ -3764,7 +3840,9 @@
/**
* Test that invalid HOVER events sent by accessibility do not cause a fatal crash.
*/
-TEST_F(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash) {
+TEST_F_WITH_FLAGS(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(com::android::input::flags,
+ a11y_crash_on_inconsistent_event_stream))) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -4607,6 +4685,88 @@
EXPECT_EQ(80, event->getY(0));
}
+TEST_F(InputDispatcherDisplayProjectionTest, CancelMotionWithCorrectCoordinates) {
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+ // The monitor will always receive events in the logical display's coordinate space, because
+ // it does not have a window.
+ FakeMonitorReceiver monitor{*mDispatcher, "Monitor", ADISPLAY_ID_DEFAULT};
+
+ // Send down to the first window.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {PointF{50, 100}}));
+ firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 400)));
+ monitor.consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 400)));
+
+ // Second pointer goes down on second window.
+ mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT,
+ {PointF{50, 100}, PointF{150, 220}}));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 80)));
+ const std::map<int32_t, PointF> expectedMonitorPointers{{0, PointF{100, 400}},
+ {1, PointF{300, 880}}};
+ monitor.consumeMotionEvent(
+ AllOf(WithMotionAction(POINTER_1_DOWN), WithPointers(expectedMonitorPointers)));
+
+ mDispatcher->cancelCurrentTouch();
+
+ firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 400)));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 80)));
+ monitor.consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithPointers(expectedMonitorPointers)));
+}
+
+TEST_F(InputDispatcherDisplayProjectionTest, SynthesizeDownWithCorrectCoordinates) {
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+ // Send down to the first window.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {PointF{50, 100}}));
+ firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 400)));
+
+ // The pointer is transferred to the second window, and the second window receives it in the
+ // correct coordinate space.
+ mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+ firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 400)));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(-100, -400)));
+}
+
+TEST_F(InputDispatcherDisplayProjectionTest, SynthesizeHoverEnterExitWithCorrectCoordinates) {
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+ // Send hover move to the second window, and ensure it shows up as hover enter.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS,
+ ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
+ WithCoords(100, 80), WithRawCoords(300, 880)));
+
+ // Touch down at the same location and ensure a hover exit is synthesized.
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_STYLUS,
+ ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithCoords(100, 80),
+ WithRawCoords(300, 880)));
+ secondWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 80), WithRawCoords(300, 880)));
+ secondWindow->assertNoEvents();
+ firstWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherDisplayProjectionTest, SynthesizeHoverCancelationWithCorrectCoordinates) {
+ auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
+
+ // Send hover enter to second window
+ mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS,
+ ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
+ WithCoords(100, 80), WithRawCoords(300, 880)));
+
+ mDispatcher->cancelCurrentTouch();
+
+ secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithCoords(100, 80),
+ WithRawCoords(300, 880)));
+ secondWindow->assertNoEvents();
+ firstWindow->assertNoEvents();
+}
+
/** Ensure consistent behavior of InputDispatcher in all orientations. */
class InputDispatcherDisplayOrientationFixture
: public InputDispatcherDisplayProjectionTest,
@@ -5389,65 +5549,6 @@
mDispatcher->waitForIdle();
}
-class FakeMonitorReceiver {
-public:
- FakeMonitorReceiver(const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name,
- int32_t displayId) {
- base::Result<std::unique_ptr<InputChannel>> channel =
- dispatcher->createInputMonitor(displayId, name, MONITOR_PID);
- mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
- }
-
- sp<IBinder> getToken() { return mInputReceiver->getToken(); }
-
- void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
- expectedFlags);
- }
-
- std::optional<int32_t> receiveEvent() {
- return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED);
- }
-
- void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
-
- void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
- expectedDisplayId, expectedFlags);
- }
-
- void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE,
- expectedDisplayId, expectedFlags);
- }
-
- void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP,
- expectedDisplayId, expectedFlags);
- }
-
- void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- mInputReceiver->consumeMotionEvent(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
- WithDisplayId(expectedDisplayId),
- WithFlags(expectedFlags | AMOTION_EVENT_FLAG_CANCELED)));
- }
-
- void consumeMotionPointerDown(int32_t pointerIdx) {
- int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
- (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- mInputReceiver->consumeEvent(InputEventType::MOTION, action, ADISPLAY_ID_DEFAULT,
- /*expectedFlags=*/0);
- }
-
- MotionEvent* consumeMotion() { return mInputReceiver->consumeMotion(); }
-
- void assertNoEvents() { mInputReceiver->assertNoEvents(); }
-
-private:
- std::unique_ptr<FakeInputReceiver> mInputReceiver;
-};
-
using InputDispatcherMonitorTest = InputDispatcherTest;
/**
@@ -5463,7 +5564,7 @@
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
- FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -5505,7 +5606,7 @@
"Fake Window", ADISPLAY_ID_DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
- FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
@@ -5515,7 +5616,7 @@
}
TEST_F(InputDispatcherMonitorTest, MonitorCannotPilferPointers) {
- FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
@@ -5549,7 +5650,7 @@
window->setWindowOffset(20, 40);
window->setWindowTransform(0, 1, -1, 0);
- FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
@@ -5562,7 +5663,7 @@
TEST_F(InputDispatcherMonitorTest, InjectionFailsWithNoWindow) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
@@ -6452,9 +6553,9 @@
// Test per-display input monitors for motion event.
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) {
FakeMonitorReceiver monitorInPrimary =
- FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
FakeMonitorReceiver monitorInSecondary =
- FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+ FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -6497,9 +6598,9 @@
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) {
// Input monitor per display.
FakeMonitorReceiver monitorInPrimary =
- FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
FakeMonitorReceiver monitorInSecondary =
- FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+ FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test inject a key down.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
@@ -6535,9 +6636,9 @@
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CancelTouch_MultiDisplay) {
FakeMonitorReceiver monitorInPrimary =
- FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
FakeMonitorReceiver monitorInSecondary =
- FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+ FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -7461,7 +7562,7 @@
TEST_F(InputDispatcherSingleWindowAnr, UnresponsiveMonitorAnr) {
mDispatcher->setMonitorDispatchingTimeoutForTest(SPY_TIMEOUT);
- FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
@@ -7988,7 +8089,7 @@
TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) {
std::shared_ptr<FakeApplicationHandle> focusedApplication =
std::make_shared<FakeApplicationHandle>();
- focusedApplication->setDispatchingTimeout(200ms);
+ focusedApplication->setDispatchingTimeout(300ms);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
// The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
mFocusedWindow->setFocusable(false);
@@ -9318,6 +9419,76 @@
mSecondWindow->assertNoEvents();
}
+/**
+ * Start drag and drop with a pointer whose id is not 0, cancel the current touch, and ensure drag
+ * and drop is also canceled. Then inject a simple gesture, and ensure dispatcher does not crash.
+ */
+TEST_F(InputDispatcherDragTests, DragAndDropFinishedWhenCancelCurrentTouch) {
+ // Down on second window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ ASSERT_NO_FATAL_FAILURE(mSecondWindow->consumeMotionDown());
+ ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionDown());
+
+ // Down on first window
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(50))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
+ ASSERT_NO_FATAL_FAILURE(mSecondWindow->consumeMotionMove());
+ ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionPointerDown(1));
+
+ // Start drag on first window
+ ASSERT_TRUE(startDrag(/*sendDown=*/false, AINPUT_SOURCE_TOUCHSCREEN));
+
+ // Trigger cancel
+ mDispatcher->cancelCurrentTouch();
+ ASSERT_NO_FATAL_FAILURE(mSecondWindow->consumeMotionCancel());
+ ASSERT_NO_FATAL_FAILURE(mDragWindow->consumeMotionCancel());
+ ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionCancel());
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ // The D&D finished with nullptr
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, nullptr);
+
+ // Remove drag window
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0});
+
+ // Inject a simple gesture, ensure dispatcher not crashed
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ PointF{50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
+
+ const MotionEvent moveEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher, moveEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionMove());
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionUp());
+}
+
class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 35ca3a5..11759b8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -26,6 +26,7 @@
#include <ui/LayerStack.h>
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <ui/ShadowSettings.h>
#include <ui/Transform.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
@@ -132,8 +133,7 @@
// The bounds of the layer in layer local coordinates
FloatRect geomLayerBounds;
- // length of the shadow in screen space
- float shadowRadius{0.f};
+ ShadowSettings shadowSettings;
// List of regions that require blur
std::vector<BlurRegion> blurRegions;
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
index 97725ea..f339d41 100644
--- a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -392,6 +392,10 @@
dumpVal(out, "dv", hasDolbyVisionSupport());
dumpVal(out, "metadata", getSupportedPerFrameMetadata());
+ out.append("\n Hdr Luminance Info:");
+ dumpVal(out, "desiredMinLuminance", mHdrCapabilities.getDesiredMinLuminance());
+ dumpVal(out, "desiredMaxLuminance", mHdrCapabilities.getDesiredMaxLuminance());
+ dumpVal(out, "desiredMaxAverageLuminance", mHdrCapabilities.getDesiredMaxAverageLuminance());
out.append("\n");
}
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 426cc57..2d10866 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -68,7 +68,7 @@
dumpVal(out, "geomLayerBounds", geomLayerBounds);
out.append(" ");
- dumpVal(out, "shadowRadius", shadowRadius);
+ dumpVal(out, "shadowLength", shadowSettings.length);
out.append("\n ");
dumpVal(out, "blend", toString(blendMode), blendMode);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 78c23da..fa3733b 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -588,10 +588,10 @@
const Rect visibleRect(tr.transform(layerFEState->geomLayerBounds));
visibleRegion.set(visibleRect);
- if (layerFEState->shadowRadius > 0.0f) {
+ if (layerFEState->shadowSettings.length > 0.0f) {
// if the layer casts a shadow, offset the layers visible region and
// calculate the shadow region.
- const auto inset = static_cast<int32_t>(ceilf(layerFEState->shadowRadius) * -1.0f);
+ const auto inset = static_cast<int32_t>(ceilf(layerFEState->shadowSettings.length) * -1.0f);
Rect visibleRectWithShadows(visibleRect);
visibleRectWithShadows.inset(inset, inset, inset, inset);
visibleRegion.set(visibleRectWithShadows);
@@ -967,7 +967,7 @@
case ui::Dataspace::BT2020_ITU_HLG:
bestDataSpace = ui::Dataspace::DISPLAY_P3;
// When there's mixed PQ content and HLG content, we set the HDR
- // data space to be BT2020_PQ and convert HLG to PQ.
+ // data space to be BT2020_HLG and convert PQ to HLG.
if (*outHdrDataSpace == ui::Dataspace::UNKNOWN) {
*outHdrDataSpace = ui::Dataspace::BT2020_HLG;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index fe56969..7fe3369 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -222,8 +222,8 @@
// Some HWCs may clip client composited input to its displayFrame. Make sure
// that this does not cut off the shadow.
- if (layerState.forceClientComposition && layerState.shadowRadius > 0.0f) {
- const auto outset = layerState.shadowRadius;
+ if (layerState.forceClientComposition && layerState.shadowSettings.length > 0.0f) {
+ const auto outset = layerState.shadowSettings.length;
geomLayerBounds.left -= outset;
geomLayerBounds.top -= outset;
geomLayerBounds.right += outset;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 630906a..1c54469 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -334,7 +334,7 @@
TEST_F(OutputLayerDisplayFrameTest, shadowExpandsDisplayFrame) {
const int kShadowRadius = 5;
- mLayerFEState.shadowRadius = kShadowRadius;
+ mLayerFEState.shadowSettings.length = kShadowRadius;
mLayerFEState.forceClientComposition = true;
mLayerFEState.geomLayerBounds = FloatRect{100.f, 100.f, 200.f, 200.f};
@@ -345,7 +345,7 @@
TEST_F(OutputLayerDisplayFrameTest, shadowExpandsDisplayFrame_onlyIfForcingClientComposition) {
const int kShadowRadius = 5;
- mLayerFEState.shadowRadius = kShadowRadius;
+ mLayerFEState.shadowSettings.length = kShadowRadius;
mLayerFEState.forceClientComposition = false;
mLayerFEState.geomLayerBounds = FloatRect{100.f, 100.f, 200.f, 200.f};
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index ee6998a..cdcb68d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -1830,7 +1830,7 @@
ui::Transform translate;
translate.set(50, 50);
mLayer.layerFEState.geomLayerTransform = translate;
- mLayer.layerFEState.shadowRadius = 10.0f;
+ mLayer.layerFEState.shadowSettings.length = 10.0f;
mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
// half of the layer including the casting shadow is covered and opaque
@@ -1872,7 +1872,7 @@
ui::Transform translate;
translate.set(50, 50);
mLayer.layerFEState.geomLayerTransform = translate;
- mLayer.layerFEState.shadowRadius = 10.0f;
+ mLayer.layerFEState.shadowSettings.length = 10.0f;
mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
// Casting layer is covered by an opaque region leaving only part of its shadow to be drawn
@@ -1897,7 +1897,7 @@
ui::Transform translate;
translate.set(50, 50);
mLayer.layerFEState.geomLayerTransform = translate;
- mLayer.layerFEState.shadowRadius = 10.0f;
+ mLayer.layerFEState.shadowSettings.length = 10.0f;
mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
// Casting layer and its shadows are covered by an opaque region
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 899d2de..7a85da0 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -382,7 +382,6 @@
sidebandStream = requested.sidebandStream;
}
if (forceUpdate || requested.what & layer_state_t::eShadowRadiusChanged) {
- shadowRadius = requested.shadowRadius;
shadowSettings.length = requested.shadowRadius;
}
if (forceUpdate || requested.what & layer_state_t::eFrameRateSelectionPriority) {
@@ -398,6 +397,15 @@
geomCrop = requested.crop;
}
+ if (forceUpdate || requested.what & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
+ const auto compatibility =
+ Layer::FrameRate::convertCompatibility(requested.defaultFrameRateCompatibility);
+ if (defaultFrameRateCompatibility != compatibility) {
+ clientChanges |= layer_state_t::eDefaultFrameRateCompatibilityChanged;
+ }
+ defaultFrameRateCompatibility = compatibility;
+ }
+
if (forceUpdate ||
requested.what &
(layer_state_t::eFlagsChanged | layer_state_t::eBufferChanged |
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index a5e9368..80a51ea 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -69,7 +69,6 @@
RoundedCornerState roundedCorner;
FloatRect transformedBounds;
Rect transformedBoundsWithoutTransparentRegion;
- renderengine::ShadowSettings shadowSettings;
bool premultipliedAlpha;
ui::Transform parentTransform;
Rect bufferSize;
@@ -86,6 +85,8 @@
gui::GameMode gameMode;
scheduler::LayerInfo::FrameRate frameRate;
scheduler::LayerInfo::FrameRateSelectionStrategy frameRateSelectionStrategy;
+ scheduler::FrameRateCompatibility defaultFrameRateCompatibility =
+ scheduler::FrameRateCompatibility::Default;
ui::Transform::RotationFlags fixedTransformHint;
std::optional<ui::Transform::RotationFlags> transformHint;
bool handleSkipScreenshotFlag = false;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 55be398..03c0993 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -353,7 +353,7 @@
snapshot.isSecure = false;
snapshot.color.a = 1.0_hf;
snapshot.colorTransformIsIdentity = true;
- snapshot.shadowRadius = 0.f;
+ snapshot.shadowSettings.length = 0.f;
snapshot.layerMetadata.mMap.clear();
snapshot.relativeLayerMetadata.mMap.clear();
snapshot.inputInfo.touchOcclusionMode = gui::TouchOcclusionMode::BLOCK_UNTRUSTED;
@@ -990,9 +990,12 @@
}
void LayerSnapshotBuilder::updateShadows(LayerSnapshot& snapshot, const RequestedLayerState&,
- const renderengine::ShadowSettings& globalShadowSettings) {
- if (snapshot.shadowRadius > 0.f) {
- snapshot.shadowSettings = globalShadowSettings;
+ const ShadowSettings& globalShadowSettings) {
+ if (snapshot.shadowSettings.length > 0.f) {
+ snapshot.shadowSettings.ambientColor = globalShadowSettings.ambientColor;
+ snapshot.shadowSettings.spotColor = globalShadowSettings.spotColor;
+ snapshot.shadowSettings.lightPos = globalShadowSettings.lightPos;
+ snapshot.shadowSettings.lightRadius = globalShadowSettings.lightRadius;
// Note: this preserves existing behavior of shadowing the entire layer and not cropping
// it if transparent regions are present. This may not be necessary since shadows are
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 3d64b36..1506913 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -47,7 +47,7 @@
const DisplayInfos& displays;
// Set to true if there were display changes since last update.
bool displayChanges = false;
- const renderengine::ShadowSettings& globalShadowSettings;
+ const ShadowSettings& globalShadowSettings;
bool supportsBlur = true;
bool forceFullDamage = false;
std::optional<FloatRect> parentCrop = std::nullopt;
@@ -108,7 +108,7 @@
void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
const LayerSnapshot& parentSnapshot, uint32_t displayRotationFlags);
static void updateShadows(LayerSnapshot& snapshot, const RequestedLayerState& requested,
- const renderengine::ShadowSettings& globalShadowSettings);
+ const ShadowSettings& globalShadowSettings);
void updateInput(LayerSnapshot& snapshot, const RequestedLayerState& requested,
const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath& path,
const Args& args);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 415f55a..2dc8758 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -572,7 +572,7 @@
snapshot->outputFilter = getOutputFilter();
snapshot->isVisible = isVisible();
snapshot->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
- snapshot->shadowRadius = mEffectiveShadowRadius;
+ snapshot->shadowSettings.length = mEffectiveShadowRadius;
snapshot->contentDirty = contentDirty;
contentDirty = false;
@@ -1155,7 +1155,7 @@
if (mDrawingState.defaultFrameRateCompatibility == compatibility) return false;
mDrawingState.defaultFrameRateCompatibility = compatibility;
mDrawingState.modified = true;
- mFlinger->mScheduler->setDefaultFrameRateCompatibility(this);
+ mFlinger->mScheduler->setDefaultFrameRateCompatibility(sequence, compatibility);
setTransactionFlags(eTransactionNeeded);
return true;
}
@@ -1267,7 +1267,8 @@
return parentFrameRate;
}();
- *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate);
+ auto now = systemTime();
+ *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate, now);
// The frame rate is propagated to the children
bool childrenHaveFrameRate = false;
@@ -1283,7 +1284,8 @@
// layer as NoVote to allow the children to control the refresh rate
if (!frameRate.isValid() && childrenHaveFrameRate) {
*transactionNeeded |=
- setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote));
+ setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote),
+ now);
}
// We return whether this layer or its children has a vote. We ignore ExactOrMultiple votes for
@@ -1492,7 +1494,7 @@
addSurfaceFrameDroppedForBuffer(surfaceFrame, postTime);
}
-bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate) {
+bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate, nsecs_t now) {
if (mDrawingState.frameRateForLayerTree == frameRate) {
return false;
}
@@ -1506,19 +1508,20 @@
setTransactionFlags(eTransactionNeeded);
mFlinger->mScheduler
- ->recordLayerHistory(sequence, getLayerProps(), systemTime(),
+ ->recordLayerHistory(sequence, getLayerProps(), now, now,
scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
return true;
}
-bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps) {
+bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps,
+ nsecs_t now) {
if (mDrawingState.frameRateForLayerTree == frameRate) {
return false;
}
mDrawingState.frameRateForLayerTree = frameRate;
mFlinger->mScheduler
- ->recordLayerHistory(sequence, layerProps, systemTime(),
+ ->recordLayerHistory(sequence, layerProps, now, now,
scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
return true;
}
@@ -3225,7 +3228,7 @@
mOwnerUid, postTime, getGameMode());
if (mFlinger->mLegacyFrontEndEnabled) {
- recordLayerHistoryBufferUpdate(getLayerProps());
+ recordLayerHistoryBufferUpdate(getLayerProps(), systemTime());
}
setFrameTimelineVsyncForBufferTransaction(info, postTime);
@@ -3256,7 +3259,7 @@
mDrawingState.isAutoTimestamp = isAutoTimestamp;
}
-void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps) {
+void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps, nsecs_t now) {
ATRACE_CALL();
const nsecs_t presentTime = [&] {
if (!mDrawingState.isAutoTimestamp) {
@@ -3310,14 +3313,14 @@
ATRACE_FORMAT_INSTANT("presentIn %s", to_string(presentIn).c_str());
}
- mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
+ mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime, now,
scheduler::LayerHistory::LayerUpdateType::Buffer);
}
-void Layer::recordLayerHistoryAnimationTx(const scheduler::LayerProps& layerProps) {
+void Layer::recordLayerHistoryAnimationTx(const scheduler::LayerProps& layerProps, nsecs_t now) {
const nsecs_t presentTime =
mDrawingState.isAutoTimestamp ? 0 : mDrawingState.desiredPresentTime;
- mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime,
+ mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime, now,
scheduler::LayerHistory::LayerUpdateType::AnimationTX);
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 78a3a7c..0b66866 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -908,10 +908,10 @@
void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
const sp<GraphicBuffer>& buffer, uint64_t framenumber,
const sp<Fence>& releaseFence);
- bool setFrameRateForLayerTreeLegacy(FrameRate);
- bool setFrameRateForLayerTree(FrameRate, const scheduler::LayerProps&);
- void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&);
- void recordLayerHistoryAnimationTx(const scheduler::LayerProps&);
+ bool setFrameRateForLayerTreeLegacy(FrameRate, nsecs_t now);
+ bool setFrameRateForLayerTree(FrameRate, const scheduler::LayerProps&, nsecs_t now);
+ void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&, nsecs_t now);
+ void recordLayerHistoryAnimationTx(const scheduler::LayerProps&, nsecs_t now);
auto getLayerProps() const {
return scheduler::LayerProps{
.visible = isVisible(),
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index 97c4145..48a9190 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -310,7 +310,7 @@
void LayerFE::prepareShadowClientComposition(LayerFE::LayerSettings& caster,
const Rect& layerStackRect) const {
- renderengine::ShadowSettings state = mSnapshot->shadowSettings;
+ ShadowSettings state = mSnapshot->shadowSettings;
if (state.length <= 0.f || (state.ambientColor.a <= 0.f && state.spotColor.a <= 0.f)) {
return;
}
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 144e1f5..aa6026e 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -402,7 +402,7 @@
[&]() { return layerInfo->mutable_screen_bounds(); });
LayerProtoHelper::writeToProto(snapshot.roundedCorner.cropRect,
[&]() { return layerInfo->mutable_corner_radius_crop(); });
- layerInfo->set_shadow_radius(snapshot.shadowRadius);
+ layerInfo->set_shadow_radius(snapshot.shadowSettings.length);
layerInfo->set_id(snapshot.uniqueSequence);
layerInfo->set_original_id(snapshot.sequence);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index b98b800..4e5659e 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -141,20 +141,20 @@
}
}
-void LayerHistory::setDefaultFrameRateCompatibility(Layer* layer, bool contentDetectionEnabled) {
+void LayerHistory::setDefaultFrameRateCompatibility(int32_t id,
+ FrameRateCompatibility frameRateCompatibility,
+ bool contentDetectionEnabled) {
std::lock_guard lock(mLock);
- auto id = layer->getSequence();
auto [found, layerPair] = findLayer(id);
if (found == LayerStatus::NotFound) {
// Offscreen layer
- ALOGV("%s: %s not registered", __func__, layer->getName().c_str());
+ ALOGV("%s: %d not registered", __func__, id);
return;
}
const auto& info = layerPair->second;
- info->setDefaultLayerVote(
- getVoteType(layer->getDefaultFrameRateCompatibility(), contentDetectionEnabled));
+ info->setDefaultLayerVote(getVoteType(frameRateCompatibility, contentDetectionEnabled));
}
auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) -> Summary {
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 5750ea7..bac1ec6 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -29,6 +29,7 @@
#include "EventThread.h"
+#include "FrameRateCompatibility.h"
#include "RefreshRateSelector.h"
namespace android {
@@ -70,7 +71,8 @@
// Updates the default frame rate compatibility which takes effect when the app
// does not set a preference for refresh rate.
- void setDefaultFrameRateCompatibility(Layer*, bool contentDetectionEnabled);
+ void setDefaultFrameRateCompatibility(int32_t id, FrameRateCompatibility frameRateCompatibility,
+ bool contentDetectionEnabled);
using Summary = std::vector<RefreshRateSelector::LayerRequirement>;
@@ -89,6 +91,7 @@
private:
friend class LayerHistoryTest;
+ friend class LayerHistoryIntegrationTest;
friend class TestableScheduler;
using LayerPair = std::pair<Layer*, std::unique_ptr<LayerInfo>>;
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 6286b28..d580b58 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -62,6 +62,7 @@
static constexpr size_t kNumSmallDirtyThreshold = 2;
friend class LayerHistoryTest;
+ friend class LayerHistoryIntegrationTest;
friend class LayerInfoTest;
public:
@@ -264,6 +265,7 @@
private:
friend class LayerHistoryTest;
+ friend class LayerHistoryIntegrationTest;
// Holds the refresh rate when it was calculated
struct RefreshRateData {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 68e2ce9..5115c42 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -192,7 +192,8 @@
for (const auto& [id, display] : mDisplays) {
if (id == pacesetterId) continue;
- const FrameTargeter& targeter = *display.targeterPtr;
+ FrameTargeter& targeter = *display.targeterPtr;
+ targeter.beginFrame(beginFrameArgs, *display.schedulePtr);
targets.try_emplace(id, &targeter.target());
}
@@ -206,8 +207,6 @@
if (id == pacesetterId) continue;
FrameTargeter& targeter = *display.targeterPtr;
- targeter.beginFrame(beginFrameArgs, *display.schedulePtr);
-
targeters.try_emplace(id, &targeter);
}
@@ -625,9 +624,9 @@
}
void Scheduler::recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
- LayerHistory::LayerUpdateType updateType) {
+ nsecs_t now, LayerHistory::LayerUpdateType updateType) {
if (pacesetterSelectorPtr()->canSwitch()) {
- mLayerHistory.record(id, layerProps, presentTime, systemTime(), updateType);
+ mLayerHistory.record(id, layerProps, presentTime, now, updateType);
}
}
@@ -635,8 +634,9 @@
mLayerHistory.setModeChangePending(pending);
}
-void Scheduler::setDefaultFrameRateCompatibility(Layer* layer) {
- mLayerHistory.setDefaultFrameRateCompatibility(layer,
+void Scheduler::setDefaultFrameRateCompatibility(
+ int32_t id, scheduler::FrameRateCompatibility frameRateCompatibility) {
+ mLayerHistory.setDefaultFrameRateCompatibility(id, frameRateCompatibility,
mFeatures.test(Feature::kContentDetection));
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index f652bb2..3441318 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -230,9 +230,9 @@
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
void recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
- LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
+ nsecs_t now, LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
void setModeChangePending(bool pending);
- void setDefaultFrameRateCompatibility(Layer*);
+ void setDefaultFrameRateCompatibility(int32_t id, scheduler::FrameRateCompatibility);
void deregisterLayer(Layer*);
void onLayerDestroyed(Layer*) EXCLUDES(mChoreographerLock);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3f52444..2b70301 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2203,35 +2203,46 @@
return mustComposite;
}
-void SurfaceFlinger::updateLayerHistory(const frontend::LayerSnapshot& snapshot) {
- using Changes = frontend::RequestedLayerState::Changes;
- if (snapshot.path.isClone() ||
- !snapshot.changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation)) {
- return;
- }
+void SurfaceFlinger::updateLayerHistory(nsecs_t now) {
+ for (const auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
+ using Changes = frontend::RequestedLayerState::Changes;
+ if (snapshot->path.isClone()) {
+ continue;
+ }
- const auto layerProps = scheduler::LayerProps{
- .visible = snapshot.isVisible,
- .bounds = snapshot.geomLayerBounds,
- .transform = snapshot.geomLayerTransform,
- .setFrameRateVote = snapshot.frameRate,
- .frameRateSelectionPriority = snapshot.frameRateSelectionPriority,
- };
+ if (!snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation) &&
+ (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) == 0) {
+ continue;
+ }
- auto it = mLegacyLayers.find(snapshot.sequence);
- LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
- snapshot.getDebugString().c_str());
+ const auto layerProps = scheduler::LayerProps{
+ .visible = snapshot->isVisible,
+ .bounds = snapshot->geomLayerBounds,
+ .transform = snapshot->geomLayerTransform,
+ .setFrameRateVote = snapshot->frameRate,
+ .frameRateSelectionPriority = snapshot->frameRateSelectionPriority,
+ };
- if (snapshot.changes.test(Changes::Animation)) {
- it->second->recordLayerHistoryAnimationTx(layerProps);
- }
+ auto it = mLegacyLayers.find(snapshot->sequence);
+ LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s",
+ snapshot->getDebugString().c_str());
- if (snapshot.changes.test(Changes::FrameRate)) {
- it->second->setFrameRateForLayerTree(snapshot.frameRate, layerProps);
- }
+ if (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
+ mScheduler->setDefaultFrameRateCompatibility(snapshot->sequence,
+ snapshot->defaultFrameRateCompatibility);
+ }
- if (snapshot.changes.test(Changes::Buffer)) {
- it->second->recordLayerHistoryBufferUpdate(layerProps);
+ if (snapshot->changes.test(Changes::Animation)) {
+ it->second->recordLayerHistoryAnimationTx(layerProps, now);
+ }
+
+ if (snapshot->changes.test(Changes::FrameRate)) {
+ it->second->setFrameRateForLayerTree(snapshot->frameRate, layerProps, now);
+ }
+
+ if (snapshot->changes.test(Changes::Buffer)) {
+ it->second->recordLayerHistoryBufferUpdate(layerProps, now);
+ }
}
}
@@ -2370,8 +2381,8 @@
mLayersIdsWithQueuedFrames.emplace(it->second->sequence);
}
+ updateLayerHistory(latchTime);
mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) {
- updateLayerHistory(snapshot);
if (mLayersIdsWithQueuedFrames.find(snapshot.path.id) ==
mLayersIdsWithQueuedFrames.end())
return;
@@ -4016,7 +4027,7 @@
if (sysprop::use_content_detection_for_refresh_rate(false)) {
features |= Feature::kContentDetection;
- if (base::GetBoolProperty("debug.sf.enable_small_dirty_detection"s, false)) {
+ if (flags::vrr_small_dirty_detection()) {
features |= Feature::kSmallDirtyContentDetection;
}
}
@@ -4823,7 +4834,7 @@
for (const auto& listener : listenerCallbacks) {
mTransactionCallbackInvoker.addEmptyTransaction(listener);
}
-
+ nsecs_t now = systemTime();
uint32_t clientStateFlags = 0;
for (auto& resolvedState : states) {
if (mLegacyFrontEndEnabled) {
@@ -4845,7 +4856,7 @@
.setFrameRateVote = layer->getFrameRateForLayerTree(),
.frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
};
- layer->recordLayerHistoryAnimationTx(layerProps);
+ layer->recordLayerHistoryAnimationTx(layerProps, now);
}
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index a5a2341..cbea312 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -419,7 +419,7 @@
bool colorMatrixChanged = true;
mat4 colorMatrix;
- renderengine::ShadowSettings globalShadowSettings;
+ ShadowSettings globalShadowSettings;
void traverse(const LayerVector::Visitor& visitor) const;
void traverseInZOrder(const LayerVector::Visitor& visitor) const;
@@ -732,7 +732,7 @@
bool& out) REQUIRES(kMainThreadContext);
bool updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
bool& out) REQUIRES(kMainThreadContext);
- void updateLayerHistory(const frontend::LayerSnapshot& snapshot);
+ void updateLayerHistory(nsecs_t now);
frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
void updateInputFlinger(VsyncId vsyncId, TimePoint frameTime);
diff --git a/services/surfaceflinger/TEST_MAPPING b/services/surfaceflinger/TEST_MAPPING
index 6f53d62..922fd07 100644
--- a/services/surfaceflinger/TEST_MAPPING
+++ b/services/surfaceflinger/TEST_MAPPING
@@ -36,6 +36,9 @@
"hwasan-presubmit": [
{
"name": "libscheduler_test"
+ },
+ {
+ "name": "libsurfaceflinger_unittest"
}
]
}
diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
index 23fe8fa..c2d1954 100644
--- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
+++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp
@@ -56,7 +56,7 @@
frontend::LayerSnapshotBuilder snapshotBuilder;
ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo> displayInfos;
- renderengine::ShadowSettings globalShadowSettings{.ambientColor = {1, 1, 1, 1}};
+ ShadowSettings globalShadowSettings{.ambientColor = {1, 1, 1, 1}};
char value[PROPERTY_VALUE_MAX];
property_get("ro.surface_flinger.supports_background_blur", value, "0");
bool supportsBlur = atoi(value);
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index d4ab786..fedf08b 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -30,3 +30,11 @@
bug: "284845445"
is_fixed_read_only: true
}
+
+flag {
+ name: "vrr_small_dirty_detection"
+ namespace: "core_graphics"
+ description: "Controls small dirty detection for VRR"
+ bug: "283055450"
+ is_fixed_read_only: true
+}
diff --git a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
index 2fcb9e0..3c09422 100644
--- a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
+++ b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp
@@ -126,7 +126,7 @@
<< info.touchableRegionBounds.right << "," << info.touchableRegionBounds.bottom << "}";
}
-struct find_id : std::unary_function<LayerInfo, bool> {
+struct find_id {
uint64_t id;
find_id(uint64_t id) : id(id) {}
bool operator()(LayerInfo const& m) const { return m.id == id; }
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index f4516c7..c99809b 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -50,22 +50,6 @@
"surfaceflinger_defaults",
],
test_suites: ["device-tests"],
- sanitize: {
- // Using the address sanitizer not only helps uncover issues in the code
- // covered by the tests, but also covers some of the tricky injection of
- // fakes the unit tests currently do.
- //
- // Note: If you get an runtime link error like:
- //
- // CANNOT LINK EXECUTABLE "/data/local/tmp/libsurfaceflinger_unittest": library "libclang_rt.asan-aarch64-android.so" not found
- //
- // it is because the address sanitizer shared objects are not installed
- // by default in the system image.
- //
- // You can either "make dist tests" before flashing, or set this
- // option to false temporarily.
- address: true,
- },
static_libs: ["libc++fs"],
srcs: [
":libsurfaceflinger_mock_sources",
@@ -93,6 +77,7 @@
"HWComposerTest.cpp",
"OneShotTimerTest.cpp",
"LayerHistoryTest.cpp",
+ "LayerHistoryIntegrationTest.cpp",
"LayerInfoTest.cpp",
"LayerMetadataTest.cpp",
"LayerHierarchyTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index d7ac038..d319dcc 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -18,11 +18,14 @@
#include <gtest/gtest.h>
#include <gui/fake/BufferData.h>
+#include <renderengine/mock/FakeExternalTexture.h>
+#include <ui/ShadowSettings.h>
#include "Client.h" // temporarily needed for LayerCreationArgs
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerHierarchy.h"
#include "FrontEnd/LayerLifecycleManager.h"
+#include "FrontEnd/LayerSnapshotBuilder.h"
namespace android::surfaceflinger::frontend {
@@ -358,6 +361,19 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setDefaultFrameRateCompatibility(uint32_t id, int8_t defaultFrameRateCompatibility) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what =
+ layer_state_t::eDefaultFrameRateCompatibilityChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.defaultFrameRateCompatibility =
+ defaultFrameRateCompatibility;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void setRoundedCorners(uint32_t id, float radius) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
@@ -384,6 +400,16 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setBuffer(uint32_t id) {
+ static uint64_t sBufferId = 1;
+ setBuffer(id,
+ std::make_shared<renderengine::mock::
+ FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+ sBufferId++,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_PROTECTED /*usage*/));
+ }
+
void setBufferCrop(uint32_t id, const Rect& bufferCrop) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
@@ -406,7 +432,64 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setShadowRadius(uint32_t id, float shadowRadius) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eShadowRadiusChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.shadowRadius = shadowRadius;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
LayerLifecycleManager mLifecycleManager;
};
+class LayerSnapshotTestBase : public LayerHierarchyTestBase {
+protected:
+ LayerSnapshotTestBase() : LayerHierarchyTestBase() {}
+
+ void createRootLayer(uint32_t id) override {
+ LayerHierarchyTestBase::createRootLayer(id);
+ setColor(id);
+ }
+
+ void createLayer(uint32_t id, uint32_t parentId) override {
+ LayerHierarchyTestBase::createLayer(id, parentId);
+ setColor(parentId);
+ }
+
+ void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) override {
+ LayerHierarchyTestBase::mirrorLayer(id, parent, layerToMirror);
+ setColor(id);
+ }
+
+ void update(LayerSnapshotBuilder& snapshotBuilder) {
+ if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
+ mHierarchyBuilder.update(mLifecycleManager.getLayers(),
+ mLifecycleManager.getDestroyedLayers());
+ }
+ LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(),
+ .layerLifecycleManager = mLifecycleManager,
+ .includeMetadata = false,
+ .displays = mFrontEndDisplayInfos,
+ .displayChanges = mHasDisplayChanges,
+ .globalShadowSettings = globalShadowSettings,
+ .supportsBlur = true,
+ .supportedLayerGenericMetadata = {},
+ .genericLayerMetadataKeyMap = {}};
+ snapshotBuilder.update(args);
+
+ mLifecycleManager.commitChanges();
+ }
+
+ LayerHierarchyBuilder mHierarchyBuilder{{}};
+
+ DisplayInfos mFrontEndDisplayInfos;
+ bool mHasDisplayChanges = false;
+
+ ShadowSettings globalShadowSettings;
+};
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
new file mode 100644
index 0000000..a462082
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -0,0 +1,873 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LayerHistoryIntegrationTest"
+
+#include <Layer.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+#include <renderengine/mock/FakeExternalTexture.h>
+
+#include "FpsOps.h"
+#include "LayerHierarchyTest.h"
+#include "Scheduler/LayerHistory.h"
+#include "Scheduler/LayerInfo.h"
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockSchedulerCallback.h"
+
+namespace android::scheduler {
+
+using android::mock::createDisplayMode;
+
+class LayerHistoryIntegrationTest : public surfaceflinger::frontend::LayerSnapshotTestBase {
+protected:
+ static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::HISTORY_SIZE;
+ static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::kMaxPeriodForFrequentLayerNs;
+ static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfo::kFrequentLayerWindowSize;
+ static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfo::HISTORY_DURATION;
+
+ static constexpr Fps LO_FPS = 30_Hz;
+ static constexpr auto LO_FPS_PERIOD = LO_FPS.getPeriodNsecs();
+
+ static constexpr Fps HI_FPS = 90_Hz;
+ static constexpr auto HI_FPS_PERIOD = HI_FPS.getPeriodNsecs();
+
+ LayerHistoryIntegrationTest() : LayerSnapshotTestBase() {
+ mFlinger.resetScheduler(mScheduler);
+ mLifecycleManager = {};
+ mHierarchyBuilder = {{}};
+ }
+
+ void updateLayerSnapshotsAndLayerHistory(nsecs_t now) {
+ LayerSnapshotTestBase::update(mFlinger.mutableLayerSnapshotBuilder());
+ mFlinger.updateLayerHistory(now);
+ }
+
+ void setBufferWithPresentTime(sp<Layer>& layer, nsecs_t time) {
+ uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+ setBuffer(sequence);
+ layer->setDesiredPresentTime(time, false /*autotimestamp*/);
+ updateLayerSnapshotsAndLayerHistory(time);
+ }
+
+ LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
+ const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
+
+ LayerHistory::Summary summarizeLayerHistory(nsecs_t now) {
+ // LayerHistory::summarize makes no guarantee of the order of the elements in the summary
+ // however, for testing only, a stable order is required, therefore we sort the list here.
+ // Any tests requiring ordered results must create layers with names.
+ auto summary = history().summarize(*mScheduler->refreshRateSelector(), now);
+ std::sort(summary.begin(), summary.end(),
+ [](const RefreshRateSelector::LayerRequirement& lhs,
+ const RefreshRateSelector::LayerRequirement& rhs) -> bool {
+ return lhs.name < rhs.name;
+ });
+ return summary;
+ }
+
+ size_t layerCount() const { return mScheduler->layerHistorySize(); }
+ size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS {
+ return history().mActiveLayerInfos.size();
+ }
+
+ auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ const auto& infos = history().mActiveLayerInfos;
+ return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+ return pair.second.second->isFrequent(now).isFrequent;
+ });
+ }
+
+ auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ const auto& infos = history().mActiveLayerInfos;
+ return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+ return pair.second.second->isAnimating(now);
+ });
+ }
+
+ auto clearLayerHistoryCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+ const auto& infos = history().mActiveLayerInfos;
+ return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+ return pair.second.second->isFrequent(now).clearHistory;
+ });
+ }
+
+ void setDefaultLayerVote(Layer* layer,
+ LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
+ auto [found, layerPair] = history().findLayer(layer->getSequence());
+ if (found != LayerHistory::LayerStatus::NotFound) {
+ layerPair->second->setDefaultLayerVote(vote);
+ }
+ }
+
+ auto createLegacyAndFrontedEndLayer(uint32_t sequence) {
+ std::string layerName = "test layer:" + std::to_string(sequence);
+ const auto layer =
+ sp<Layer>::make(LayerCreationArgs{mFlinger.flinger(),
+ nullptr,
+ layerName,
+ 0,
+ {},
+ std::make_optional<uint32_t>(sequence)});
+ mFlinger.injectLegacyLayer(layer);
+ createRootLayer(sequence);
+ return layer;
+ }
+
+ auto destroyLayer(sp<Layer>& layer) {
+ uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+ mFlinger.releaseLegacyLayer(sequence);
+ layer.clear();
+ destroyLayerHandle(sequence);
+ }
+
+ void recordFramesAndExpect(sp<Layer>& layer, nsecs_t& time, Fps frameRate,
+ Fps desiredRefreshRate, int numFrames) {
+ LayerHistory::Summary summary;
+ for (int i = 0; i < numFrames; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += frameRate.getPeriodNsecs();
+
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate);
+ }
+
+ std::shared_ptr<RefreshRateSelector> mSelector =
+ std::make_shared<RefreshRateSelector>(makeModes(createDisplayMode(DisplayModeId(0),
+ LO_FPS),
+ createDisplayMode(DisplayModeId(1),
+ HI_FPS)),
+ DisplayModeId(0));
+
+ mock::SchedulerCallback mSchedulerCallback;
+
+ TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback);
+
+ TestableSurfaceFlinger mFlinger;
+};
+
+namespace {
+
+TEST_F(LayerHistoryIntegrationTest, singleLayerNoVoteDefaultCompatibility) {
+ createLegacyAndFrontedEndLayer(1);
+ nsecs_t time = systemTime();
+
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ // No layers returned if no layers are active.
+ EXPECT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ setBuffer(1);
+ setDefaultFrameRateCompatibility(1, ANATIVEWINDOW_FRAME_RATE_NO_VOTE);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ EXPECT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(1u, activeLayerCount());
+}
+
+TEST_F(LayerHistoryIntegrationTest, singleLayerMinVoteDefaultCompatibility) {
+ createLegacyAndFrontedEndLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ EXPECT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ setBuffer(1);
+ setDefaultFrameRateCompatibility(1, ANATIVEWINDOW_FRAME_RATE_MIN);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ auto summary = summarizeLayerHistory(time);
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneInvisibleLayer) {
+ createLegacyAndFrontedEndLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ setBuffer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+ auto summary = summarizeLayerHistory(time);
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ // Layer is still considered inactive so we expect to get Min
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+
+ hideLayer(1);
+ setBuffer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ summary = summarizeLayerHistory(time);
+ EXPECT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0u, activeLayerCount());
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVote) {
+ createLegacyAndFrontedEndLayer(1);
+ setFrameRate(1, 73.4f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitExactVote) {
+ createLegacyAndFrontedEndLayer(1);
+ setFrameRate(1, 73.4f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitCategory) {
+ createLegacyAndFrontedEndLayer(1);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ // First LayerRequirement is the frame rate specification
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, multipleLayers) {
+ auto layer1 = createLegacyAndFrontedEndLayer(1);
+ auto layer2 = createLegacyAndFrontedEndLayer(2);
+ auto layer3 = createLegacyAndFrontedEndLayer(3);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(3u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ LayerHistory::Summary summary;
+
+ // layer1 is active but infrequent.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer1, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // layer2 is frequent and has high refresh rate.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer2, time);
+ time += HI_FPS_PERIOD;
+ summary = summarizeLayerHistory(time);
+ }
+
+ // layer1 is still active but infrequent.
+ setBufferWithPresentTime(layer1, time);
+
+ ASSERT_EQ(2u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+ EXPECT_EQ(HI_FPS, summarizeLayerHistory(time)[1].desiredRefreshRate);
+
+ EXPECT_EQ(2u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer1 is no longer active.
+ // layer2 is frequent and has low refresh rate.
+ for (size_t i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer2, time);
+ time += LO_FPS_PERIOD;
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer2 still has low refresh rate.
+ // layer3 has high refresh rate but not enough history.
+ constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+ if (i % RATIO == 0) {
+ setBufferWithPresentTime(layer2, time);
+ }
+
+ setBufferWithPresentTime(layer3, time);
+ time += HI_FPS_PERIOD;
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(2u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
+ EXPECT_EQ(2u, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer3 becomes recently active.
+ setBufferWithPresentTime(layer3, time);
+ summary = summarizeLayerHistory(time);
+ ASSERT_EQ(2u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+ EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate);
+ EXPECT_EQ(2u, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer1 expires.
+ destroyLayer(layer1);
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ summary = summarizeLayerHistory(time);
+ ASSERT_EQ(2u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+ EXPECT_EQ(HI_FPS, summary[1].desiredRefreshRate);
+ EXPECT_EQ(2u, layerCount());
+ EXPECT_EQ(2u, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+
+ // layer2 still has low refresh rate.
+ // layer3 becomes inactive.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer2, time);
+ time += LO_FPS_PERIOD;
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer2 expires.
+ destroyLayer(layer2);
+ updateLayerSnapshotsAndLayerHistory(time);
+ summary = summarizeLayerHistory(time);
+ EXPECT_TRUE(summary.empty());
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // layer3 becomes active and has high refresh rate.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
+ setBufferWithPresentTime(layer3, time);
+ time += HI_FPS_PERIOD;
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_EQ(HI_FPS, summary[0].desiredRefreshRate);
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer3 expires.
+ destroyLayer(layer3);
+ updateLayerSnapshotsAndLayerHistory(time);
+ summary = summarizeLayerHistory(time);
+ EXPECT_TRUE(summary.empty());
+ EXPECT_EQ(0u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, inactiveLayers) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ nsecs_t time = systemTime();
+
+ // the very first updates makes the layer frequent
+ for (size_t i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ EXPECT_EQ(1u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ }
+
+ // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ setBufferWithPresentTime(layer, time);
+
+ EXPECT_EQ(1u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // advance the time for the previous frame to be inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+
+ // Now even if we post a quick few frame we should stay infrequent
+ for (size_t i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+
+ EXPECT_EQ(1u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ }
+
+ // More quick frames will get us to frequent again
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+
+ EXPECT_EQ(1u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, invisibleExplicitLayer) {
+ auto explicitVisiblelayer = createLegacyAndFrontedEndLayer(1);
+ auto explicitInvisiblelayer = createLegacyAndFrontedEndLayer(2);
+ hideLayer(2);
+ setFrameRate(1, 60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRate(2, 90.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ nsecs_t time = systemTime();
+
+ // Post a buffer to the layers to make them active
+ setBufferWithPresentTime(explicitVisiblelayer, time);
+ setBufferWithPresentTime(explicitInvisiblelayer, time);
+
+ EXPECT_EQ(2u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(2u, activeLayerCount());
+ EXPECT_EQ(2, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, infrequentAnimatingLayer) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // layer is active but infrequent.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ }
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // another update with the same cadence keep in infrequent
+ setBufferWithPresentTime(layer, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->changes |=
+ frontend::RequestedLayerState::Changes::Animation;
+ mFlinger.updateLayerHistory(time);
+ // an update as animation will immediately vote for Max
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(1, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, frequentLayerBecomingInfrequentAndBack) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // Fill up the window with frequent updates
+ for (size_t i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += (60_Hz).getPeriodNsecs();
+
+ EXPECT_EQ(1u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ }
+
+ // posting a buffer after long inactivity should retain the layer as active
+ time += std::chrono::nanoseconds(3s).count();
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // posting more infrequent buffer should make the layer infrequent
+ time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
+ setBufferWithPresentTime(layer, time);
+ time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // posting another buffer should keep the layer infrequent
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // posting more buffers would mean starting of an animation, so making the layer frequent
+ setBufferWithPresentTime(layer, time);
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(1, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // posting a buffer after long inactivity should retain the layer as active
+ time += std::chrono::nanoseconds(3s).count();
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // posting another buffer should keep the layer frequent
+ time += (60_Hz).getPeriodNsecs();
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, inconclusiveLayerBecomingFrequent) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // Fill up the window with frequent updates
+ for (size_t i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += (60_Hz).getPeriodNsecs();
+
+ EXPECT_EQ(1u, layerCount());
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ }
+
+ // posting infrequent buffers after long inactivity should make the layer
+ // inconclusive but frequent.
+ time += std::chrono::nanoseconds(3s).count();
+ setBufferWithPresentTime(layer, time);
+ time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(0, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // posting more buffers should make the layer frequent and switch the refresh rate to max
+ // by clearing the history
+ setBufferWithPresentTime(layer, time);
+ setBufferWithPresentTime(layer, time);
+ setBufferWithPresentTime(layer, time);
+ EXPECT_EQ(1, clearLayerHistoryCount(time));
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, getFramerate) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // layer is active but infrequent.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ }
+
+ float expectedFramerate = 1e9f / MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ EXPECT_FLOAT_EQ(expectedFramerate, history().getLayerFramerate(time, layer->getSequence()));
+}
+
+TEST_F(LayerHistoryIntegrationTest, heuristicLayer60Hz) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+ for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
+ recordFramesAndExpect(layer, time, Fps::fromValue(fps), 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+ }
+}
+
+TEST_F(LayerHistoryIntegrationTest, heuristicLayer60_30Hz) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+ recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+
+ recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 30_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 30_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 60_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 60_Hz, 60_Hz, PRESENT_TIME_HISTORY_SIZE);
+}
+
+TEST_F(LayerHistoryIntegrationTest, heuristicLayerNotOscillating) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.9_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 26.9_Hz, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
+ recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
+}
+
+TEST_F(LayerHistoryIntegrationTest, smallDirtyLayer) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ LayerHistory::Summary summary;
+
+ // layer is active but infrequent.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ auto props = layer->getLayerProps();
+ if (i % 3 == 0) {
+ props.isSmallDirty = false;
+ } else {
+ props.isSmallDirty = true;
+ }
+
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+ EXPECT_GE(HI_FPS, summary[0].desiredRefreshRate);
+}
+
+TEST_F(LayerHistoryIntegrationTest, DISABLED_smallDirtyInMultiLayer) {
+ auto uiLayer = createLegacyAndFrontedEndLayer(1);
+ auto videoLayer = createLegacyAndFrontedEndLayer(2);
+ setFrameRate(2, 30.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(2u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ LayerHistory::Summary summary;
+
+ // uiLayer is updating small dirty.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
+ auto props = uiLayer->getLayerProps();
+ props.isSmallDirty = true;
+ setBuffer(1);
+ uiLayer->setDesiredPresentTime(0, false /*autotimestamp*/);
+ updateLayerSnapshotsAndLayerHistory(time);
+ setBufferWithPresentTime(videoLayer, time);
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+ ASSERT_EQ(30_Hz, summary[0].desiredRefreshRate);
+}
+
+class LayerHistoryIntegrationTestParameterized
+ : public LayerHistoryIntegrationTest,
+ public testing::WithParamInterface<std::chrono::nanoseconds> {};
+
+TEST_P(LayerHistoryIntegrationTestParameterized, HeuristicLayerWithInfrequentLayer) {
+ std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
+ auto heuristicLayer = createLegacyAndFrontedEndLayer(1);
+ auto infrequentLayer = createLegacyAndFrontedEndLayer(2);
+
+ const nsecs_t startTime = systemTime();
+
+ const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
+ setBufferWithPresentTime(heuristicLayer, startTime);
+ setBufferWithPresentTime(infrequentLayer, startTime);
+
+ nsecs_t time = startTime;
+ nsecs_t lastInfrequentUpdate = startTime;
+ const size_t totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
+ size_t infrequentLayerUpdates = 0;
+ while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
+ time += heuristicUpdateDelta.count();
+ setBufferWithPresentTime(heuristicLayer, time);
+
+ if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
+ ALOGI("submitting infrequent frame [%zu/%zu]", infrequentLayerUpdates,
+ totalInfrequentLayerUpdates);
+ lastInfrequentUpdate = time;
+ setBufferWithPresentTime(infrequentLayer, time);
+ infrequentLayerUpdates++;
+ }
+
+ if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
+ ASSERT_NE(0u, summarizeLayerHistory(time).size());
+ ASSERT_GE(2u, summarizeLayerHistory(time).size());
+
+ bool max = false;
+ bool min = false;
+ Fps heuristic;
+ for (const auto& layer : summarizeLayerHistory(time)) {
+ if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
+ heuristic = layer.desiredRefreshRate;
+ } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
+ max = true;
+ } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
+ min = true;
+ }
+ }
+
+ if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
+ EXPECT_EQ(24_Hz, heuristic);
+ EXPECT_FALSE(max);
+ if (summarizeLayerHistory(time).size() == 2) {
+ EXPECT_TRUE(min);
+ }
+ }
+ }
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryIntegrationTestParameterized,
+ ::testing::Values(1s, 2s, 3s, 4s, 5s));
+
+} // namespace
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index cc7a45c..33c1d86 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -45,6 +45,8 @@
using android::mock::createDisplayMode;
+// WARNING: LEGACY TESTS FOR LEGACY FRONT END
+// Update LayerHistoryIntegrationTest instead
class LayerHistoryTest : public testing::Test {
protected:
static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::HISTORY_SIZE;
@@ -165,7 +167,10 @@
history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
LayerHistory::LayerUpdateType::Buffer);
- history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
+ history().setDefaultFrameRateCompatibility(layer->getSequence(),
+
+ layer->getDefaultFrameRateCompatibility(),
+ true /* contentDetectionEnabled */);
EXPECT_TRUE(summarizeLayerHistory(time).empty());
EXPECT_EQ(1, activeLayerCount());
@@ -188,7 +193,9 @@
history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
LayerHistory::LayerUpdateType::Buffer);
- history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
+ history().setDefaultFrameRateCompatibility(layer->getSequence(),
+ layer->getDefaultFrameRateCompatibility(),
+ true /* contentDetectionEnabled */);
auto summary = summarizeLayerHistory(time);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 69316bf..50dfcaa 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -50,27 +50,12 @@
--gtest_filter="LayerSnapshotTest.*" --gtest_brief=1
*/
-class LayerSnapshotTest : public LayerHierarchyTestBase {
+class LayerSnapshotTest : public LayerSnapshotTestBase {
protected:
- LayerSnapshotTest() : LayerHierarchyTestBase() {
+ LayerSnapshotTest() : LayerSnapshotTestBase() {
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
}
- void createRootLayer(uint32_t id) override {
- LayerHierarchyTestBase::createRootLayer(id);
- setColor(id);
- }
-
- void createLayer(uint32_t id, uint32_t parentId) override {
- LayerHierarchyTestBase::createLayer(id, parentId);
- setColor(parentId);
- }
-
- void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) override {
- LayerHierarchyTestBase::mirrorLayer(id, parent, layerToMirror);
- setColor(id);
- }
-
void update(LayerSnapshotBuilder& actualBuilder, LayerSnapshotBuilder::Args& args) {
if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
mHierarchyBuilder.update(mLifecycleManager.getLayers(),
@@ -111,11 +96,7 @@
LayerSnapshot* getSnapshot(const LayerHierarchy::TraversalPath path) {
return mSnapshotBuilder.getSnapshot(path);
}
-
- LayerHierarchyBuilder mHierarchyBuilder{{}};
LayerSnapshotBuilder mSnapshotBuilder;
- DisplayInfos mFrontEndDisplayInfos;
- renderengine::ShadowSettings globalShadowSettings;
static const std::vector<uint32_t> STARTING_ZORDER;
};
const std::vector<uint32_t> LayerSnapshotTest::STARTING_ZORDER = {1, 11, 111, 12, 121,
@@ -872,4 +853,12 @@
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
EXPECT_EQ(getSnapshot(1)->geomContentCrop, Rect(0, 0, 100, 100));
}
+
+TEST_F(LayerSnapshotTest, setShadowRadius) {
+ static constexpr float SHADOW_RADIUS = 123.f;
+ setShadowRadius(1, SHADOW_RADIUS);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->shadowSettings.length, SHADOW_RADIUS);
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 173f941..87fae2c 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -192,7 +192,7 @@
// recordLayerHistory should be a noop
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
- mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+ mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, 0,
LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
@@ -218,7 +218,7 @@
kDisplay1Mode60->getId()));
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
- mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+ mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, 0,
LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1u, mScheduler->getNumActiveLayers());
}
@@ -273,7 +273,7 @@
const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
- mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0,
+ mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, systemTime(),
LayerHistory::LayerUpdateType::Buffer);
constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
@@ -444,6 +444,63 @@
}
}
+TEST_F(SchedulerTest, onFrameSignalMultipleDisplays) {
+ mScheduler->registerDisplay(kDisplayId1,
+ std::make_shared<RefreshRateSelector>(kDisplay1Modes,
+ kDisplay1Mode60->getId()));
+ mScheduler->registerDisplay(kDisplayId2,
+ std::make_shared<RefreshRateSelector>(kDisplay2Modes,
+ kDisplay2Mode60->getId()));
+
+ using VsyncIds = std::vector<std::pair<PhysicalDisplayId, VsyncId>>;
+
+ struct Compositor final : ICompositor {
+ VsyncIds vsyncIds;
+ bool committed = true;
+
+ void configure() override {}
+
+ bool commit(PhysicalDisplayId, const scheduler::FrameTargets& targets) override {
+ vsyncIds.clear();
+
+ for (const auto& [id, target] : targets) {
+ vsyncIds.emplace_back(id, target->vsyncId());
+ }
+
+ return committed;
+ }
+
+ CompositeResultsPerDisplay composite(PhysicalDisplayId,
+ const scheduler::FrameTargeters&) override {
+ CompositeResultsPerDisplay results;
+
+ for (const auto& [id, _] : vsyncIds) {
+ results.try_emplace(id,
+ CompositeResult{.compositionCoverage =
+ CompositionCoverage::Hwc});
+ }
+
+ return results;
+ }
+
+ void sample() override {}
+ } compositor;
+
+ mScheduler->doFrameSignal(compositor, VsyncId(42));
+
+ const auto makeVsyncIds = [](VsyncId vsyncId) -> VsyncIds {
+ return {{kDisplayId1, vsyncId}, {kDisplayId2, vsyncId}};
+ };
+
+ EXPECT_EQ(makeVsyncIds(VsyncId(42)), compositor.vsyncIds);
+
+ compositor.committed = false;
+ mScheduler->doFrameSignal(compositor, VsyncId(43));
+
+ // FrameTargets should be updated despite the skipped commit.
+ EXPECT_EQ(makeVsyncIds(VsyncId(43)), compositor.vsyncIds);
+}
+
class AttachedChoreographerTest : public SchedulerTest {
protected:
void frameRateTestScenario(Fps layerFps, int8_t frameRateCompatibility, Fps displayFps,
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
index a2c54ac..db6df22 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
@@ -26,8 +26,6 @@
namespace android {
-using aidl::android::hardware::graphics::common::HdrConversionCapability;
-using aidl::android::hardware::graphics::common::HdrConversionStrategy;
using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag;
using gui::aidl_utils::statusTFromBinderStatus;
@@ -66,17 +64,15 @@
sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport);
ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(getSupportStatus));
- std::vector<HdrConversionStrategy> strategies =
- {HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
- GuiHdrConversionStrategyTag::passthrough)>),
- HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
- GuiHdrConversionStrategyTag::autoAllowedHdrTypes)>),
- HdrConversionStrategy(std::in_place_index<static_cast<size_t>(
- GuiHdrConversionStrategyTag::forceHdrConversion)>)};
+ std::vector<gui::HdrConversionStrategy> strategies = {
+ gui::HdrConversionStrategy::make<GuiHdrConversionStrategyTag::passthrough>(),
+ gui::HdrConversionStrategy::make<GuiHdrConversionStrategyTag::autoAllowedHdrTypes>(),
+ gui::HdrConversionStrategy::make<GuiHdrConversionStrategyTag::forceHdrConversion>(),
+ };
int32_t outPreferredHdrOutputType = 0;
- for (HdrConversionStrategy strategy : strategies) {
- binder::Status status = sf->setHdrConversionStrategy(&strategy, &outPreferredHdrOutputType);
+ for (const gui::HdrConversionStrategy& strategy : strategies) {
+ binder::Status status = sf->setHdrConversionStrategy(strategy, &outPreferredHdrOutputType);
if (hdrOutputConversionSupport) {
ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 151b178..014d07c 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -59,6 +59,12 @@
MOCK_METHOD(void, scheduleFrame, (), (override));
MOCK_METHOD(void, postMessage, (sp<MessageHandler>&&), (override));
+ void doFrameSignal(ICompositor& compositor, VsyncId vsyncId) {
+ ftl::FakeGuard guard1(kMainThreadContext);
+ ftl::FakeGuard guard2(mDisplayLock);
+ Scheduler::onFrameSignal(compositor, vsyncId, TimePoint());
+ }
+
// Used to inject mock event thread.
ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
return Scheduler::createConnection(std::move(eventThread));
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index dd998ba..b54392e 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -604,6 +604,13 @@
return static_cast<mock::FrameTracer*>(mFlinger->mFrameTracer.get());
}
+ void injectLegacyLayer(sp<Layer> layer) {
+ mFlinger->mLegacyLayers[static_cast<uint32_t>(layer->sequence)] = layer;
+ };
+
+ void releaseLegacyLayer(uint32_t sequence) { mFlinger->mLegacyLayers.erase(sequence); };
+
+ auto updateLayerHistory(nsecs_t now) { return mFlinger->updateLayerHistory(now); };
/* ------------------------------------------------------------------------
* Read-write access to private data to set up preconditions and assert
* post-conditions.
@@ -644,8 +651,8 @@
}
auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; }
-
auto& mutableLayersPendingRemoval() { return mFlinger->mLayersPendingRemoval; }
+ auto& mutableLayerSnapshotBuilder() { return mFlinger->mLayerSnapshotBuilder; };
auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); }
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index e78f470..7c8e695 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -60,7 +60,12 @@
*
* This version of the extension cleans up a bug introduced in version 9
*/
-#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 10
+/*
+ * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 11
+ *
+ * This version of the extension deprecates the last of grallocusage
+ */
+#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 11
#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer"
#define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \
@@ -151,6 +156,8 @@
* pNext: NULL or a pointer to a structure extending this structure
* format: value specifying the format the image will be created with
* imageUsage: bitmask of VkImageUsageFlagBits describing intended usage
+ *
+ * DEPRECATED in SPEC_VERSION 10
*/
typedef struct {
VkStructureType sType;
@@ -167,6 +174,8 @@
* format: value specifying the format the image will be created with
* imageUsage: bitmask of VkImageUsageFlagBits describing intended usage
* swapchainImageUsage: is a bitmask of VkSwapchainImageUsageFlagsANDROID
+ *
+ * DEPRECATED in SPEC_VERSION 11
*/
typedef struct {
VkStructureType sType;
@@ -198,7 +207,7 @@
const VkGrallocUsageInfoANDROID* grallocUsageInfo,
uint64_t* grallocUsage);
-/* ADDED in SPEC_VERSION 10 */
+/* DEPRECATED in SPEC_VERSION 11 */
typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage4ANDROID)(
VkDevice device,
const VkGrallocUsageInfo2ANDROID* grallocUsageInfo,
@@ -245,7 +254,7 @@
uint64_t* grallocUsage
);
-/* ADDED in SPEC_VERSION 10 */
+/* DEPRECATED in SPEC_VERSION 11 */
VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage4ANDROID(
VkDevice device,
const VkGrallocUsageInfo2ANDROID* grallocUsageInfo,
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index bdba27e..5d7a4aa 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1456,6 +1456,7 @@
}
data->driver_device = dev;
+ data->driver_physical_device = physicalDevice;
*pDevice = dev;
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 4d2bbd6..4b855e5 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -98,6 +98,7 @@
VkDevice driver_device;
DeviceDriverTable driver;
+ VkPhysicalDevice driver_physical_device;
};
bool OpenHAL();
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 483863d..0c48611 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -19,6 +19,7 @@
#include <aidl/android/hardware/graphics/common/Dataspace.h>
#include <aidl/android/hardware/graphics/common/PixelFormat.h>
#include <android/hardware/graphics/common/1.0/types.h>
+#include <android/hardware_buffer.h>
#include <grallocusage/GrallocUsageConversion.h>
#include <graphicsenv/GraphicsEnv.h>
#include <hardware/gralloc.h>
@@ -1359,6 +1360,187 @@
allocator->pfnFree(allocator->pUserData, swapchain);
}
+static VkResult getProducerUsage(const VkDevice& device,
+ const VkSwapchainCreateInfoKHR* create_info,
+ const VkSwapchainImageUsageFlagsANDROID swapchain_image_usage,
+ bool create_protected_swapchain,
+ uint64_t* producer_usage) {
+ // Get the physical device to query the appropriate producer usage
+ const VkPhysicalDevice& pdev = GetData(device).driver_physical_device;
+ const InstanceData& instance_data = GetData(pdev);
+ const InstanceDriverTable& instance_dispatch = instance_data.driver;
+ if (!instance_dispatch.GetPhysicalDeviceImageFormatProperties2 &&
+ !instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR) {
+ uint64_t native_usage = 0;
+ void* usage_info_pNext = nullptr;
+ VkResult result;
+ VkImageCompressionControlEXT image_compression = {};
+ const auto& dispatch = GetData(device).driver;
+ if (dispatch.GetSwapchainGrallocUsage4ANDROID) {
+ ATRACE_BEGIN("GetSwapchainGrallocUsage4ANDROID");
+ VkGrallocUsageInfo2ANDROID gralloc_usage_info = {};
+ gralloc_usage_info.sType =
+ VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID;
+ gralloc_usage_info.format = create_info->imageFormat;
+ gralloc_usage_info.imageUsage = create_info->imageUsage;
+ gralloc_usage_info.swapchainImageUsage = swapchain_image_usage;
+
+ // Look through the pNext chain for an image compression control struct
+ // if one is found AND the appropriate extensions are enabled,
+ // append it to be the gralloc usage pNext chain
+ const VkSwapchainCreateInfoKHR* create_infos = create_info;
+ while (create_infos->pNext) {
+ create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
+ create_infos->pNext);
+ switch (create_infos->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+ const VkImageCompressionControlEXT* compression_infos =
+ reinterpret_cast<const VkImageCompressionControlEXT*>(
+ create_infos);
+ image_compression = *compression_infos;
+ image_compression.pNext = nullptr;
+ usage_info_pNext = &image_compression;
+ } break;
+
+ default:
+ // Ignore all other info structs
+ break;
+ }
+ }
+ gralloc_usage_info.pNext = usage_info_pNext;
+
+ result = dispatch.GetSwapchainGrallocUsage4ANDROID(
+ device, &gralloc_usage_info, &native_usage);
+ ATRACE_END();
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsage4ANDROID failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ } else if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
+ ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID");
+ VkGrallocUsageInfoANDROID gralloc_usage_info = {};
+ gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID;
+ gralloc_usage_info.format = create_info->imageFormat;
+ gralloc_usage_info.imageUsage = create_info->imageUsage;
+
+ // Look through the pNext chain for an image compression control struct
+ // if one is found AND the appropriate extensions are enabled,
+ // append it to be the gralloc usage pNext chain
+ const VkSwapchainCreateInfoKHR* create_infos = create_info;
+ while (create_infos->pNext) {
+ create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
+ create_infos->pNext);
+ switch (create_infos->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+ const VkImageCompressionControlEXT* compression_infos =
+ reinterpret_cast<const VkImageCompressionControlEXT*>(
+ create_infos);
+ image_compression = *compression_infos;
+ image_compression.pNext = nullptr;
+ usage_info_pNext = &image_compression;
+ } break;
+
+ default:
+ // Ignore all other info structs
+ break;
+ }
+ }
+ gralloc_usage_info.pNext = usage_info_pNext;
+
+ result = dispatch.GetSwapchainGrallocUsage3ANDROID(
+ device, &gralloc_usage_info, &native_usage);
+ ATRACE_END();
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsage3ANDROID failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ } else if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
+ uint64_t consumer_usage, producer_usage;
+ ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID");
+ result = dispatch.GetSwapchainGrallocUsage2ANDROID(
+ device, create_info->imageFormat, create_info->imageUsage,
+ swapchain_image_usage, &consumer_usage, &producer_usage);
+ ATRACE_END();
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ native_usage =
+ convertGralloc1ToBufferUsage(producer_usage, consumer_usage);
+ } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
+ ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID");
+ int32_t legacy_usage = 0;
+ result = dispatch.GetSwapchainGrallocUsageANDROID(
+ device, create_info->imageFormat, create_info->imageUsage,
+ &legacy_usage);
+ ATRACE_END();
+ if (result != VK_SUCCESS) {
+ ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ native_usage = static_cast<uint64_t>(legacy_usage);
+ }
+ *producer_usage = native_usage;
+
+ return VK_SUCCESS;
+ }
+
+ // call GetPhysicalDeviceImageFormatProperties2KHR
+ VkPhysicalDeviceExternalImageFormatInfo external_image_format_info = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO,
+ .pNext = nullptr,
+ .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
+ };
+
+ // AHB does not have an sRGB format so we can't pass it to GPDIFP
+ // We need to convert the format to unorm if it is srgb
+ VkFormat format = create_info->imageFormat;
+ if (format == VK_FORMAT_R8G8B8A8_SRGB) {
+ format = VK_FORMAT_R8G8B8A8_UNORM;
+ }
+
+ VkPhysicalDeviceImageFormatInfo2 image_format_info = {
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2,
+ .pNext = &external_image_format_info,
+ .format = format,
+ .type = VK_IMAGE_TYPE_2D,
+ .tiling = VK_IMAGE_TILING_OPTIMAL,
+ .usage = create_info->imageUsage,
+ .flags = create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
+ };
+
+ VkAndroidHardwareBufferUsageANDROID ahb_usage;
+ ahb_usage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
+ ahb_usage.pNext = nullptr;
+
+ VkImageFormatProperties2 image_format_properties;
+ image_format_properties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
+ image_format_properties.pNext = &ahb_usage;
+
+ if (instance_dispatch.GetPhysicalDeviceImageFormatProperties2) {
+ VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2(
+ pdev, &image_format_info, &image_format_properties);
+ if (result != VK_SUCCESS) {
+ ALOGE("VkGetPhysicalDeviceImageFormatProperties2 for AHB usage failed: %d", result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ }
+ else {
+ VkResult result = instance_dispatch.GetPhysicalDeviceImageFormatProperties2KHR(
+ pdev, &image_format_info,
+ &image_format_properties);
+ if (result != VK_SUCCESS) {
+ ALOGE("VkGetPhysicalDeviceImageFormatProperties2KHR for AHB usage failed: %d",
+ result);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ }
+
+ *producer_usage = ahb_usage.androidHardwareBufferUsage;
+
+ return VK_SUCCESS;
+}
+
VKAPI_ATTR
VkResult CreateSwapchainKHR(VkDevice device,
const VkSwapchainCreateInfoKHR* create_info,
@@ -1594,120 +1776,48 @@
num_images = 1;
}
+ // Look through the create_info pNext chain passed to createSwapchainKHR
+ // for an image compression control struct.
+ // if one is found AND the appropriate extensions are enabled, create a
+ // VkImageCompressionControlEXT structure to pass on to VkImageCreateInfo
+ // TODO check for imageCompressionControlSwapchain feature is enabled
void* usage_info_pNext = nullptr;
VkImageCompressionControlEXT image_compression = {};
- uint64_t native_usage = 0;
- if (dispatch.GetSwapchainGrallocUsage4ANDROID) {
- ATRACE_BEGIN("GetSwapchainGrallocUsage4ANDROID");
- VkGrallocUsageInfo2ANDROID gralloc_usage_info = {};
- gralloc_usage_info.sType =
- VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_2_ANDROID;
- gralloc_usage_info.format = create_info->imageFormat;
- gralloc_usage_info.imageUsage = create_info->imageUsage;
- gralloc_usage_info.swapchainImageUsage = swapchain_image_usage;
+ const VkSwapchainCreateInfoKHR* create_infos = create_info;
+ while (create_infos->pNext) {
+ create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(create_infos->pNext);
+ switch (create_infos->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
+ const VkImageCompressionControlEXT* compression_infos =
+ reinterpret_cast<const VkImageCompressionControlEXT*>(create_infos);
+ image_compression = *compression_infos;
+ image_compression.pNext = nullptr;
+ usage_info_pNext = &image_compression;
+ } break;
- // Look through the pNext chain for an image compression control struct
- // if one is found AND the appropriate extensions are enabled,
- // append it to be the gralloc usage pNext chain
- const VkSwapchainCreateInfoKHR* create_infos = create_info;
- while (create_infos->pNext) {
- create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
- create_infos->pNext);
- switch (create_infos->sType) {
- case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
- const VkImageCompressionControlEXT* compression_infos =
- reinterpret_cast<const VkImageCompressionControlEXT*>(
- create_infos);
- image_compression = *compression_infos;
- image_compression.pNext = nullptr;
- usage_info_pNext = &image_compression;
- } break;
-
- default:
- // Ignore all other info structs
- break;
- }
+ default:
+ // Ignore all other info structs
+ break;
}
- gralloc_usage_info.pNext = usage_info_pNext;
-
- result = dispatch.GetSwapchainGrallocUsage4ANDROID(
- device, &gralloc_usage_info, &native_usage);
- ATRACE_END();
- if (result != VK_SUCCESS) {
- ALOGE("vkGetSwapchainGrallocUsage4ANDROID failed: %d", result);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- } else if (dispatch.GetSwapchainGrallocUsage3ANDROID) {
- ATRACE_BEGIN("GetSwapchainGrallocUsage3ANDROID");
- VkGrallocUsageInfoANDROID gralloc_usage_info = {};
- gralloc_usage_info.sType = VK_STRUCTURE_TYPE_GRALLOC_USAGE_INFO_ANDROID;
- gralloc_usage_info.format = create_info->imageFormat;
- gralloc_usage_info.imageUsage = create_info->imageUsage;
-
- // Look through the pNext chain for an image compression control struct
- // if one is found AND the appropriate extensions are enabled,
- // append it to be the gralloc usage pNext chain
- const VkSwapchainCreateInfoKHR* create_infos = create_info;
- while (create_infos->pNext) {
- create_infos = reinterpret_cast<const VkSwapchainCreateInfoKHR*>(
- create_infos->pNext);
- switch (create_infos->sType) {
- case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT: {
- const VkImageCompressionControlEXT* compression_infos =
- reinterpret_cast<const VkImageCompressionControlEXT*>(
- create_infos);
- image_compression = *compression_infos;
- image_compression.pNext = nullptr;
- usage_info_pNext = &image_compression;
- } break;
-
- default:
- // Ignore all other info structs
- break;
- }
- }
- gralloc_usage_info.pNext = usage_info_pNext;
-
- result = dispatch.GetSwapchainGrallocUsage3ANDROID(
- device, &gralloc_usage_info, &native_usage);
- ATRACE_END();
- if (result != VK_SUCCESS) {
- ALOGE("vkGetSwapchainGrallocUsage3ANDROID failed: %d", result);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- } else if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
- uint64_t consumer_usage, producer_usage;
- ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID");
- result = dispatch.GetSwapchainGrallocUsage2ANDROID(
- device, create_info->imageFormat, create_info->imageUsage,
- swapchain_image_usage, &consumer_usage, &producer_usage);
- ATRACE_END();
- if (result != VK_SUCCESS) {
- ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- native_usage =
- convertGralloc1ToBufferUsage(producer_usage, consumer_usage);
- } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
- ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID");
- int32_t legacy_usage = 0;
- result = dispatch.GetSwapchainGrallocUsageANDROID(
- device, create_info->imageFormat, create_info->imageUsage,
- &legacy_usage);
- ATRACE_END();
- if (result != VK_SUCCESS) {
- ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
- return VK_ERROR_SURFACE_LOST_KHR;
- }
- native_usage = static_cast<uint64_t>(legacy_usage);
}
- native_usage |= surface.consumer_usage;
- bool createProtectedSwapchain = false;
+ // Get the appropriate native_usage for the images
+ // Get the consumer usage
+ uint64_t native_usage = surface.consumer_usage;
+ // Determine if the swapchain is protected
+ bool create_protected_swapchain = false;
if (create_info->flags & VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR) {
- createProtectedSwapchain = true;
+ create_protected_swapchain = true;
native_usage |= BufferUsage::PROTECTED;
}
+ // Get the producer usage
+ uint64_t producer_usage;
+ result = getProducerUsage(device, create_info, swapchain_image_usage, create_protected_swapchain, &producer_usage);
+ if (result != VK_SUCCESS) {
+ return result;
+ }
+ native_usage |= producer_usage;
+
err = native_window_set_usage(window, native_usage);
if (err != android::OK) {
ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), err);
@@ -1735,8 +1845,10 @@
void* mem = allocator->pfnAllocation(allocator->pUserData,
sizeof(Swapchain), alignof(Swapchain),
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+
if (!mem)
return VK_ERROR_OUT_OF_HOST_MEMORY;
+
Swapchain* swapchain = new (mem)
Swapchain(surface, num_images, create_info->presentMode,
TranslateVulkanToNativeTransform(create_info->preTransform),
@@ -1760,7 +1872,7 @@
VkImageCreateInfo image_create = {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr,
- .flags = createProtectedSwapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
+ .flags = create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u,
.imageType = VK_IMAGE_TYPE_2D,
.format = create_info->imageFormat,
.extent = {