Merge "Add support to PWLE V2 APIs" into main
diff --git a/Android.bp b/Android.bp
index 4befb1b..72311f0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -38,6 +38,7 @@
cc_library_headers {
name: "native_headers",
+ vendor_available: true,
host_supported: true,
target: {
windows: {
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 2ce3fb0..df1ef29 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -24,6 +24,7 @@
libs/nativewindow/
libs/renderengine/
libs/ui/
+ libs/vibrator/
libs/vr/
opengl/libs/
services/bufferhub/
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 6576ffd..220fef6 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1559,6 +1559,13 @@
CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
printf("========================================================\n");
+ printf("== Networking Policy\n");
+ printf("========================================================\n");
+
+ RunDumpsys("DUMPSYS NETWORK POLICY", {"netpolicy"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
+
+ printf("========================================================\n");
printf("== Dropbox crashes\n");
printf("========================================================\n");
diff --git a/cmds/evemu-record/main.rs b/cmds/evemu-record/main.rs
index db3fd77..e91e5da 100644
--- a/cmds/evemu-record/main.rs
+++ b/cmds/evemu-record/main.rs
@@ -50,8 +50,10 @@
/// The first event received from the device.
FirstEvent,
- /// The time when the system booted.
- Boot,
+ /// The Unix epoch (00:00:00 UTC on 1st January 1970), so that all timestamps are Unix
+ /// timestamps. This makes the events in the recording easier to match up with those from other
+ /// log sources.
+ Epoch,
}
fn get_choice(max: u32) -> u32 {
@@ -188,7 +190,7 @@
//
// [0]: https://gitlab.freedesktop.org/libevdev/evemu/-/commit/eba96a4d2be7260b5843e65c4b99c8b06a1f4c9d
TimestampBase::FirstEvent => event.time - TimeVal::new(0, 1),
- TimestampBase::Boot => TimeVal::new(0, 0),
+ TimestampBase::Epoch => TimeVal::new(0, 0),
};
print_event(output, &event.offset_time_by(start_time))?;
loop {
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index c163095..77e7328 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -18,6 +18,7 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/SurfaceComposerClient.h>
#include <ui/DisplayMode.h>
@@ -202,6 +203,14 @@
bool GLHelper::createNamedSurfaceTexture(GLuint name, uint32_t w, uint32_t h,
sp<GLConsumer>* glConsumer, EGLSurface* surface) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<GLConsumer> glc = new GLConsumer(name, GL_TEXTURE_EXTERNAL_OES, false, true);
+ glc->setDefaultBufferSize(w, h);
+ glc->getSurface()->setMaxDequeuedBufferCount(2);
+ glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
+
+ sp<ANativeWindow> anw = glc->getSurface();
+#else
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
@@ -212,6 +221,7 @@
glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER);
sp<ANativeWindow> anw = new Surface(producer);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), nullptr);
if (s == EGL_NO_SURFACE) {
fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError());
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index 9384926..28bd793 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -50,37 +50,6 @@
exit 1
fi
-# A source that infinitely emits arbitrary lines.
-# When connected to STDIN of another process, this source keeps STDIN open until
-# the consumer process closes STDIN or this script dies.
-function infinite_source {
- while echo .; do
- sleep 1
- done
-}
-
-PR_DEXOPT_JOB_VERSION="$(pm art pr-dexopt-job --version)"
-if (( $? == 0 )) && (( $PR_DEXOPT_JOB_VERSION >= 2 )); then
- # Delegate to Pre-reboot Dexopt, a feature of ART Service.
- # ART Service decides what to do with this request:
- # - If Pre-reboot Dexopt is disabled or unsupported, the command returns
- # non-zero. This is always the case if the current system is Android 14 or
- # earlier.
- # - If Pre-reboot Dexopt is enabled in synchronous mode, the command blocks
- # until Pre-reboot Dexopt finishes, and returns zero no matter it succeeds or
- # not. This is the default behavior if the current system is Android 15.
- # - If Pre-reboot Dexopt is enabled in asynchronous mode, the command schedules
- # an asynchronous job and returns 0 immediately. The job will then run by the
- # job scheduler when the device is idle and charging.
- if infinite_source | pm art on-ota-staged --slot "$TARGET_SLOT_SUFFIX"; then
- # Handled by Pre-reboot Dexopt.
- exit 0
- fi
- echo "Pre-reboot Dexopt not enabled. Fall back to otapreopt."
-else
- echo "Pre-reboot Dexopt is too old. Fall back to otapreopt."
-fi
-
if [ "$(/system/bin/otapreopt_chroot --version)" != 2 ]; then
# We require an updated chroot wrapper that reads dexopt commands from stdin.
# Even if we kept compat with the old binary, the OTA preopt wouldn't work due
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index d85182d..fa7cb64 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -29,6 +29,7 @@
#include <thread>
#if !defined(VENDORSERVICEMANAGER) && !defined(__ANDROID_RECOVERY__)
+#include "perfetto/public/protos/trace/android/android_track_event.pzc.h"
#include "perfetto/public/te_category_macros.h"
#include "perfetto/public/te_macros.h"
#endif // !defined(VENDORSERVICEMANAGER) && !defined(__ANDROID_RECOVERY__)
@@ -57,6 +58,12 @@
#define SM_PERFETTO_TRACE_FUNC(...) \
PERFETTO_TE_SCOPED(servicemanager, PERFETTO_TE_SLICE_BEGIN(__func__) __VA_OPT__(, ) __VA_ARGS__)
+constexpr uint32_t kProtoServiceName =
+ perfetto_protos_AndroidTrackEvent_binder_service_name_field_number;
+constexpr uint32_t kProtoInterfaceName =
+ perfetto_protos_AndroidTrackEvent_binder_interface_name_field_number;
+constexpr uint32_t kProtoApexName = perfetto_protos_AndroidTrackEvent_apex_name_field_number;
+
#endif // !(defined(VENDORSERVICEMANAGER) || defined(__ANDROID_RECOVERY__))
bool is_multiuser_uid_isolated(uid_t uid) {
@@ -385,8 +392,18 @@
}
}
-Status ServiceManager::getService(const std::string& name, os::Service* outService) {
- SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+Status ServiceManager::getService(const std::string& name, sp<IBinder>* outBinder) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
+
+ *outBinder = tryGetBinder(name, true);
+ // returns ok regardless of result for legacy reasons
+ return Status::ok();
+}
+
+Status ServiceManager::getService2(const std::string& name, os::Service* outService) {
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
*outService = tryGetService(name, true);
// returns ok regardless of result for legacy reasons
@@ -394,7 +411,8 @@
}
Status ServiceManager::checkService(const std::string& name, os::Service* outService) {
- SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
*outService = tryGetService(name, false);
// returns ok regardless of result for legacy reasons
@@ -419,7 +437,8 @@
}
sp<IBinder> ServiceManager::tryGetBinder(const std::string& name, bool startIfNotFound) {
- SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
auto ctx = mAccess->getCallingContext();
@@ -459,7 +478,8 @@
}
bool isValidServiceName(const std::string& name) {
- SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
if (name.size() == 0) return false;
if (name.size() > 127) return false;
@@ -476,7 +496,8 @@
}
Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {
- SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
auto ctx = mAccess->getCallingContext();
@@ -484,8 +505,9 @@
return Status::fromExceptionCode(Status::EX_SECURITY, "App UIDs cannot add services.");
}
- if (!mAccess->canAdd(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
+ std::optional<std::string> accessorName;
+ if (auto status = canAddService(ctx, name, &accessorName); !status.isOk()) {
+ return status;
}
if (binder == nullptr) {
@@ -599,7 +621,8 @@
Status ServiceManager::registerForNotifications(
const std::string& name, const sp<IServiceCallback>& callback) {
- SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
auto ctx = mAccess->getCallingContext();
@@ -650,7 +673,8 @@
}
Status ServiceManager::unregisterForNotifications(
const std::string& name, const sp<IServiceCallback>& callback) {
- SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
auto ctx = mAccess->getCallingContext();
@@ -676,7 +700,8 @@
}
Status ServiceManager::isDeclared(const std::string& name, bool* outReturn) {
- SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
auto ctx = mAccess->getCallingContext();
@@ -694,7 +719,8 @@
}
binder::Status ServiceManager::getDeclaredInstances(const std::string& interface, std::vector<std::string>* outReturn) {
- SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("interface", interface.c_str()));
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoInterfaceName, interface.c_str())));
auto ctx = mAccess->getCallingContext();
@@ -722,7 +748,8 @@
Status ServiceManager::updatableViaApex(const std::string& name,
std::optional<std::string>* outReturn) {
- SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
auto ctx = mAccess->getCallingContext();
@@ -741,7 +768,8 @@
Status ServiceManager::getUpdatableNames([[maybe_unused]] const std::string& apexName,
std::vector<std::string>* outReturn) {
- SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("apexName", apexName.c_str()));
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoApexName, apexName.c_str())));
auto ctx = mAccess->getCallingContext();
@@ -767,7 +795,8 @@
Status ServiceManager::getConnectionInfo(const std::string& name,
std::optional<ConnectionInfo>* outReturn) {
- SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
auto ctx = mAccess->getCallingContext();
@@ -852,15 +881,17 @@
Status ServiceManager::registerClientCallback(const std::string& name, const sp<IBinder>& service,
const sp<IClientCallback>& cb) {
- SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
if (cb == nullptr) {
return Status::fromExceptionCode(Status::EX_NULL_POINTER, "Callback null.");
}
auto ctx = mAccess->getCallingContext();
- if (!mAccess->canAdd(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
+ std::optional<std::string> accessorName;
+ if (auto status = canAddService(ctx, name, &accessorName); !status.isOk()) {
+ return status;
}
auto serviceIt = mNameToService.find(name);
@@ -1014,15 +1045,17 @@
}
Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IBinder>& binder) {
- SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_ARG_STRING("name", name.c_str()));
+ SM_PERFETTO_TRACE_FUNC(PERFETTO_TE_PROTO_FIELDS(
+ PERFETTO_TE_PROTO_FIELD_CSTR(kProtoServiceName, name.c_str())));
if (binder == nullptr) {
return Status::fromExceptionCode(Status::EX_NULL_POINTER, "Null service.");
}
auto ctx = mAccess->getCallingContext();
- if (!mAccess->canAdd(ctx, name)) {
- return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied.");
+ std::optional<std::string> accessorName;
+ if (auto status = canAddService(ctx, name, &accessorName); !status.isOk()) {
+ return status;
}
auto serviceIt = mNameToService.find(name);
@@ -1080,6 +1113,23 @@
return Status::ok();
}
+Status ServiceManager::canAddService(const Access::CallingContext& ctx, const std::string& name,
+ std::optional<std::string>* accessor) {
+ if (!mAccess->canAdd(ctx, name)) {
+ return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied for service.");
+ }
+#ifndef VENDORSERVICEMANAGER
+ *accessor = getVintfAccessorName(name);
+#endif
+ if (accessor->has_value()) {
+ if (!mAccess->canAdd(ctx, accessor->value())) {
+ return Status::fromExceptionCode(Status::EX_SECURITY,
+ "SELinux denied for the accessor of the service.");
+ }
+ }
+ return Status::ok();
+}
+
Status ServiceManager::canFindService(const Access::CallingContext& ctx, const std::string& name,
std::optional<std::string>* accessor) {
if (!mAccess->canFind(ctx, name)) {
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 18bae68..c92141b 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -44,7 +44,8 @@
~ServiceManager();
// getService will try to start any services it cannot find
- binder::Status getService(const std::string& name, os::Service* outService) override;
+ binder::Status getService(const std::string& name, sp<IBinder>* outBinder) override;
+ binder::Status getService2(const std::string& name, os::Service* outService) override;
binder::Status checkService(const std::string& name, os::Service* outService) override;
binder::Status addService(const std::string& name, const sp<IBinder>& binder,
bool allowIsolated, int32_t dumpPriority) override;
@@ -114,6 +115,8 @@
os::Service tryGetService(const std::string& name, bool startIfNotFound);
sp<IBinder> tryGetBinder(const std::string& name, bool startIfNotFound);
+ binder::Status canAddService(const Access::CallingContext& ctx, const std::string& name,
+ std::optional<std::string>* accessor);
binder::Status canFindService(const Access::CallingContext& ctx, const std::string& name,
std::optional<std::string>* accessor);
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 9d22641..95f459f 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -155,8 +155,11 @@
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
Service outA;
- EXPECT_TRUE(sm->getService("foo", &outA).isOk());
+ EXPECT_TRUE(sm->getService2("foo", &outA).isOk());
EXPECT_EQ(serviceA, outA.get<Service::Tag::binder>());
+ sp<IBinder> outBinderA;
+ EXPECT_TRUE(sm->getService("foo", &outBinderA).isOk());
+ EXPECT_EQ(serviceA, outBinderA);
// serviceA should be overwritten by serviceB
sp<IBinder> serviceB = getBinder();
@@ -164,8 +167,11 @@
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
Service outB;
- EXPECT_TRUE(sm->getService("foo", &outB).isOk());
+ EXPECT_TRUE(sm->getService2("foo", &outB).isOk());
EXPECT_EQ(serviceB, outB.get<Service::Tag::binder>());
+ sp<IBinder> outBinderB;
+ EXPECT_TRUE(sm->getService("foo", &outBinderB).isOk());
+ EXPECT_EQ(serviceB, outBinderB);
}
TEST(AddService, NoPermissions) {
@@ -188,16 +194,22 @@
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
Service out;
- EXPECT_TRUE(sm->getService("foo", &out).isOk());
+ EXPECT_TRUE(sm->getService2("foo", &out).isOk());
EXPECT_EQ(service, out.get<Service::Tag::binder>());
+ sp<IBinder> outBinder;
+ EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
+ EXPECT_EQ(service, outBinder);
}
TEST(GetService, NonExistant) {
auto sm = getPermissiveServiceManager();
Service out;
- EXPECT_TRUE(sm->getService("foo", &out).isOk());
+ EXPECT_TRUE(sm->getService2("foo", &out).isOk());
EXPECT_EQ(nullptr, out.get<Service::Tag::binder>());
+ sp<IBinder> outBinder;
+ EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
+ EXPECT_EQ(nullptr, outBinder);
}
TEST(GetService, NoPermissionsForGettingService) {
@@ -205,7 +217,7 @@
EXPECT_CALL(*access, getCallingContext()).WillRepeatedly(Return(Access::CallingContext{}));
EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
- EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(false));
+ EXPECT_CALL(*access, canFind(_, _)).WillRepeatedly(Return(false));
sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
@@ -214,22 +226,28 @@
Service out;
// returns nullptr but has OK status for legacy compatibility
- EXPECT_TRUE(sm->getService("foo", &out).isOk());
+ EXPECT_TRUE(sm->getService2("foo", &out).isOk());
EXPECT_EQ(nullptr, out.get<Service::Tag::binder>());
+ sp<IBinder> outBinder;
+ EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
+ EXPECT_EQ(nullptr, outBinder);
}
TEST(GetService, AllowedFromIsolated) {
std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
EXPECT_CALL(*access, getCallingContext())
- // something adds it
- .WillOnce(Return(Access::CallingContext{}))
- // next call is from isolated app
- .WillOnce(Return(Access::CallingContext{
- .uid = AID_ISOLATED_START,
- }));
+ // something adds it
+ .WillOnce(Return(Access::CallingContext{}))
+ // next calls is from isolated app
+ .WillOnce(Return(Access::CallingContext{
+ .uid = AID_ISOLATED_START,
+ }))
+ .WillOnce(Return(Access::CallingContext{
+ .uid = AID_ISOLATED_START,
+ }));
EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
- EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(*access, canFind(_, _)).WillRepeatedly(Return(true));
sp<ServiceManager> sm = sp<NiceMock<MockServiceManager>>::make(std::move(access));
@@ -238,20 +256,26 @@
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
Service out;
- EXPECT_TRUE(sm->getService("foo", &out).isOk());
+ EXPECT_TRUE(sm->getService2("foo", &out).isOk());
EXPECT_EQ(service, out.get<Service::Tag::binder>());
+ sp<IBinder> outBinder;
+ EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
+ EXPECT_EQ(service, outBinder);
}
TEST(GetService, NotAllowedFromIsolated) {
std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
EXPECT_CALL(*access, getCallingContext())
- // something adds it
- .WillOnce(Return(Access::CallingContext{}))
- // next call is from isolated app
- .WillOnce(Return(Access::CallingContext{
- .uid = AID_ISOLATED_START,
- }));
+ // something adds it
+ .WillOnce(Return(Access::CallingContext{}))
+ // next calls is from isolated app
+ .WillOnce(Return(Access::CallingContext{
+ .uid = AID_ISOLATED_START,
+ }))
+ .WillOnce(Return(Access::CallingContext{
+ .uid = AID_ISOLATED_START,
+ }));
EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
// TODO(b/136023468): when security check is first, this should be called first
@@ -264,8 +288,11 @@
Service out;
// returns nullptr but has OK status for legacy compatibility
- EXPECT_TRUE(sm->getService("foo", &out).isOk());
+ EXPECT_TRUE(sm->getService2("foo", &out).isOk());
EXPECT_EQ(nullptr, out.get<Service::Tag::binder>());
+ sp<IBinder> outBinder;
+ EXPECT_TRUE(sm->getService("foo", &outBinder).isOk());
+ EXPECT_EQ(nullptr, outBinder);
}
TEST(ListServices, NoPermissions) {
diff --git a/data/etc/input/motion_predictor_config.xml b/data/etc/input/motion_predictor_config.xml
index 14540ec..f593eda 100644
--- a/data/etc/input/motion_predictor_config.xml
+++ b/data/etc/input/motion_predictor_config.xml
@@ -38,7 +38,8 @@
<low-jerk>1.5</low-jerk>
<high-jerk>2.0</high-jerk>
- <!-- The forget factor in the first-order IIR filter for jerk smoothing -->
- <jerk-forget-factor>0.25</jerk-forget-factor>
+ <!-- The alpha in the first-order IIR filter for jerk smoothing. An alpha
+ of 1 results in no smoothing.-->
+ <jerk-alpha>0.25</jerk-alpha>
</motion-predictor>
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 97e4dc0..3f32a5a 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -53,6 +53,7 @@
*/
#include <android/api-level.h>
+#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
@@ -84,7 +85,6 @@
/**
* An opaque type representing a handle to a performance hint manager.
- * It must be released after use.
*
* To use:<ul>
* <li>Obtain the performance hint manager instance by calling
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 099a2bc..82cacca 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -373,6 +373,28 @@
float b, float alpha, enum ADataSpace dataspace)
__INTRODUCED_IN(29);
+// These APIs (setGeometry and setCrop) were originally written in a
+// C-incompatible form using references instead of pointers, and the OS shipped
+// that version for years before it was noticed. Fortunately the compiled code
+// for callers is the same regardless of whether it's a pointer or a reference,
+// so we can declare this as a nonnull pointer for C and keep the existing C++
+// decl and definition.
+//
+// We could alternatively change the decl and the definition to both be a
+// pointer (with an inline definition using references to preserve source compat
+// for existing C++ callers), but that requires changing the definition of an
+// API that has been in the OS for years. It's theoretically a safe change, but
+// without being able to prove it that's a very big risk to take. By keeping the
+// C-compatibility hack in the header, we can be sure that we haven't changed
+// anything for existing callers. By definition there were no C users of the
+// reference-based decl; if there were any C callers of the API at all, they were
+// using the same workaround that is now used below.
+//
+// Even if this workaround turns out to not work for C, there's no permanent
+// damage done to the platform (unlike if we were to change the definition). At
+// worst it continues to work for C++ (since the preprocessed header as seen by
+// C++ hasn't changed, nor has the definition) and continues to not work for C.
+
/**
* \param source The sub-rect within the buffer's content to be rendered inside the surface's area
* The surface's source rect is clipped by the bounds of its current buffer. The source rect's width
@@ -383,7 +405,7 @@
* clipped by the bounds of its parent. The destination rect's width and height must be > 0.
*
* \param transform The transform applied after the source rect is applied to the buffer. This
- * parameter should be set to 0 for no transform. To specify a transfrom use the
+ * parameter should be set to 0 for no transform. To specify a transform use the
* NATIVE_WINDOW_TRANSFORM_* enum.
*
* Available since API level 29.
@@ -394,9 +416,14 @@
* properties at once.
*/
void ASurfaceTransaction_setGeometry(ASurfaceTransaction* _Nonnull transaction,
- ASurfaceControl* _Nonnull surface_control, const ARect& source,
- const ARect& destination, int32_t transform)
- __INTRODUCED_IN(29);
+ ASurfaceControl* _Nonnull surface_control,
+#if defined(__cplusplus)
+ const ARect& source, const ARect& destination,
+#else
+ const ARect* _Nonnull source,
+ const ARect* _Nonnull destination,
+#endif
+ int32_t transform) __INTRODUCED_IN(29);
/**
* Bounds the surface and its children to the bounds specified. The crop and buffer size will be
@@ -408,7 +435,12 @@
* Available since API level 31.
*/
void ASurfaceTransaction_setCrop(ASurfaceTransaction* _Nonnull transaction,
- ASurfaceControl* _Nonnull surface_control, const ARect& crop)
+ ASurfaceControl* _Nonnull surface_control,
+#if defined(__cplusplus)
+ const ARect& crop)
+#else
+ const ARect* _Nonnull crop)
+#endif
__INTRODUCED_IN(31);
/**
diff --git a/include/android/surface_control_input_receiver.h b/include/android/surface_control_input_receiver.h
index bdc5249..f0503f6 100644
--- a/include/android/surface_control_input_receiver.h
+++ b/include/android/surface_control_input_receiver.h
@@ -59,17 +59,13 @@
AInputEvent *_Nonnull keyEvent)
__INTRODUCED_IN(__ANDROID_API_V__);
-struct AInputReceiverCallbacks;
-
-struct AInputReceiver;
+typedef struct AInputReceiverCallbacks AInputReceiverCallbacks;
/**
* The InputReceiver that holds the reference to the registered input channel. This must be released
* using AInputReceiver_release
- *
- * Available since API level 35.
*/
-typedef struct AInputReceiver AInputReceiver __INTRODUCED_IN(__ANDROID_API_V__);
+typedef struct AInputReceiver AInputReceiver;
/**
* Registers an input receiver for an ASurfaceControl that will receive batched input event. For
diff --git a/include/android/thermal.h b/include/android/thermal.h
index fa168cd..7f9d2ed 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -85,6 +85,7 @@
/** Need shutdown immediately. */
ATHERMAL_STATUS_SHUTDOWN = 6,
};
+typedef enum AThermalStatus AThermalStatus;
/**
* An opaque type representing a handle to a thermal manager.
@@ -240,6 +241,7 @@
float headroom;
AThermalStatus thermalStatus;
};
+typedef struct AThermalHeadroomThreshold AThermalHeadroomThreshold;
/**
* Gets the thermal headroom thresholds for all available thermal status.
diff --git a/include/ftl/fake_guard.h b/include/ftl/fake_guard.h
index e601251..0bf2870 100644
--- a/include/ftl/fake_guard.h
+++ b/include/ftl/fake_guard.h
@@ -76,12 +76,8 @@
FTL_ATTRIBUTE(release_capability(mutex))
#endif
-// The parentheses around `expr` are needed to deduce an lvalue or rvalue reference.
-#define FTL_FAKE_GUARD2(mutex, expr) \
- [&]() -> decltype(auto) { \
- const android::ftl::FakeGuard guard(mutex); \
- return (expr); \
- }()
+#define FTL_FAKE_GUARD2(mutex, expr) \
+ (android::ftl::FakeGuard(mutex), expr)
#define FTL_MAKE_FAKE_GUARD(arg1, arg2, guard, ...) guard
diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h
index ae8de5f..65c2914 100644
--- a/include/input/InputConsumerNoResampling.h
+++ b/include/input/InputConsumerNoResampling.h
@@ -17,6 +17,7 @@
#pragma once
#include <input/InputTransport.h>
+#include <input/Resampler.h>
#include <utils/Looper.h>
namespace android {
@@ -47,13 +48,13 @@
/**
* Consumes input events from an input channel.
*
- * This is a re-implementation of InputConsumer that does not have resampling at the current moment.
- * A lot of the higher-level logic has been folded into this class, to make it easier to use.
- * In the legacy class, InputConsumer, the consumption logic was partially handled in the jni layer,
- * as well as various actions like adding the fd to the Choreographer.
+ * This is a re-implementation of InputConsumer. At the moment it only supports resampling for
+ * single pointer events. A lot of the higher-level logic has been folded into this class, to make
+ * it easier to use. In the legacy class, InputConsumer, the consumption logic was partially handled
+ * in the jni layer, as well as various actions like adding the fd to the Choreographer.
*
* TODO(b/297226446): use this instead of "InputConsumer":
- * - Add resampling to this class
+ * - Add resampling for multiple pointer events.
* - Allow various resampling strategies to be specified
* - Delete the old "InputConsumer" and use this class instead, renaming it to "InputConsumer".
* - Add tracing
@@ -64,8 +65,18 @@
*/
class InputConsumerNoResampling final {
public:
+ /**
+ * @param callbacks are used to interact with InputConsumerNoResampling. They're called whenever
+ * the event is ready to consume.
+ * @param looper needs to be sp and not shared_ptr because it inherits from
+ * RefBase
+ * @param resampler the resampling strategy to use. If null, no resampling will be
+ * performed.
+ */
explicit InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
- sp<Looper> looper, InputConsumerCallbacks& callbacks);
+ sp<Looper> looper, InputConsumerCallbacks& callbacks,
+ std::unique_ptr<Resampler> resampler);
+
~InputConsumerNoResampling();
/**
@@ -99,6 +110,7 @@
std::shared_ptr<InputChannel> mChannel;
sp<Looper> mLooper;
InputConsumerCallbacks& mCallbacks;
+ std::unique_ptr<Resampler> mResampler;
// Looper-related infrastructure
/**
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
index 25d35e9..55e0583 100644
--- a/include/input/InputEventBuilders.h
+++ b/include/input/InputEventBuilders.h
@@ -127,7 +127,7 @@
return *this;
}
- MotionEvent build() {
+ MotionEvent build() const {
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
for (const PointerBuilder& pointer : mPointers) {
@@ -135,20 +135,22 @@
pointerCoords.push_back(pointer.buildCoords());
}
+ auto [xCursorPosition, yCursorPosition] =
+ std::make_pair(mRawXCursorPosition, mRawYCursorPosition);
// Set mouse cursor position for the most common cases to avoid boilerplate.
if (mSource == AINPUT_SOURCE_MOUSE &&
- !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
- mRawXCursorPosition = pointerCoords[0].getX();
- mRawYCursorPosition = pointerCoords[0].getY();
+ !MotionEvent::isValidCursorPosition(xCursorPosition, yCursorPosition)) {
+ xCursorPosition = pointerCoords[0].getX();
+ yCursorPosition = pointerCoords[0].getY();
}
MotionEvent event;
event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC,
mAction, mActionButton, mFlags, /*edgeFlags=*/0, AMETA_NONE, mButtonState,
MotionClassification::NONE, mTransform,
- /*xPrecision=*/0, /*yPrecision=*/0, mRawXCursorPosition,
- mRawYCursorPosition, mRawTransform, mDownTime, mEventTime,
- mPointers.size(), pointerProperties.data(), pointerCoords.data());
+ /*xPrecision=*/0, /*yPrecision=*/0, xCursorPosition, yCursorPosition,
+ mRawTransform, mDownTime, mEventTime, mPointers.size(),
+ pointerProperties.data(), pointerCoords.data());
return event;
}
diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h
index 2f1ef86..200c301 100644
--- a/include/input/MotionPredictor.h
+++ b/include/input/MotionPredictor.h
@@ -43,7 +43,9 @@
class JerkTracker {
public:
// Initialize the tracker. If normalizedDt is true, assume that each sample pushed has dt=1.
- JerkTracker(bool normalizedDt);
+ // alpha is the coefficient of the first-order IIR filter for jerk. A factor of 1 results
+ // in no smoothing.
+ JerkTracker(bool normalizedDt, float alpha);
// Add a position to the tracker and update derivative estimates.
void pushSample(int64_t timestamp, float xPos, float yPos);
@@ -56,15 +58,10 @@
// acceleration) and has the units of d^3p/dt^3.
std::optional<float> jerkMagnitude() const;
- // forgetFactor is the coefficient of the first-order IIR filter for jerk. A factor of 1 results
- // in no smoothing.
- void setForgetFactor(float forgetFactor);
- float getForgetFactor() const;
-
private:
const bool mNormalizedDt;
// Coefficient of first-order IIR filter to smooth jerk calculation.
- float mForgetFactor = 1;
+ const float mAlpha;
RingBuffer<int64_t> mTimestamps{4};
std::array<float, 4> mXDerivatives{}; // [x, x', x'', x''']
@@ -124,11 +121,6 @@
bool isPredictionAvailable(int32_t deviceId, int32_t source);
- /**
- * Currently used to expose config constants in testing.
- */
- const TfLiteMotionPredictorModel::Config& getModelConfig();
-
private:
const nsecs_t mPredictionTimestampOffsetNanos;
const std::function<bool()> mCheckMotionPredictionEnabled;
@@ -137,15 +129,17 @@
std::unique_ptr<TfLiteMotionPredictorBuffers> mBuffers;
std::optional<MotionEvent> mLastEvent;
- // mJerkTracker assumes normalized dt = 1 between recorded samples because
- // the underlying mModel input also assumes fixed-interval samples.
- // Normalized dt as 1 is also used to correspond with the similar Jank
- // implementation from the JetPack MotionPredictor implementation.
- JerkTracker mJerkTracker{true};
- std::optional<MotionPredictorMetricsManager> mMetricsManager;
+ std::unique_ptr<JerkTracker> mJerkTracker;
+
+ std::unique_ptr<MotionPredictorMetricsManager> mMetricsManager;
const ReportAtomFunction mReportAtomFunction;
+
+ // Initialize prediction model and associated objects.
+ // Called during lazy initialization.
+ // TODO: b/210158587 Consider removing lazy initialization.
+ void initializeObjects();
};
} // namespace android
diff --git a/include/input/OWNERS b/include/input/OWNERS
index c88bfe9..21d208f 100644
--- a/include/input/OWNERS
+++ b/include/input/OWNERS
@@ -1 +1,2 @@
+# Bug component: 136048
include platform/frameworks/base:/INPUT_OWNERS
diff --git a/include/input/Resampler.h b/include/input/Resampler.h
new file mode 100644
index 0000000..2892137
--- /dev/null
+++ b/include/input/Resampler.h
@@ -0,0 +1,146 @@
+/**
+ * Copyright 2024 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 <chrono>
+#include <optional>
+#include <vector>
+
+#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <input/RingBuffer.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+/**
+ * Resampler is an interface for resampling MotionEvents. Every resampling implementation
+ * must use this interface to enable resampling inside InputConsumer's logic.
+ */
+struct Resampler {
+ virtual ~Resampler() = default;
+
+ /**
+ * Tries to resample motionEvent at resampleTime. The provided resampleTime must be greater than
+ * the latest sample time of motionEvent. It is not guaranteed that resampling occurs at
+ * resampleTime. Interpolation may occur is futureSample is available. Otherwise, motionEvent
+ * may be resampled by another method, or not resampled at all. Furthermore, it is the
+ * implementer's responsibility to guarantee the following:
+ * - If resampling occurs, a single additional sample should be added to motionEvent. That is,
+ * if motionEvent had N samples before being passed to Resampler, then it will have N + 1
+ * samples by the end of the resampling. No other field of motionEvent should be modified.
+ * - If resampling does not occur, then motionEvent must not be modified in any way.
+ */
+ virtual void resampleMotionEvent(std::chrono::nanoseconds resampleTime,
+ MotionEvent& motionEvent,
+ const InputMessage* futureSample) = 0;
+};
+
+class LegacyResampler final : public Resampler {
+public:
+ /**
+ * Tries to resample `motionEvent` at `resampleTime` by adding a resampled sample at the end of
+ * `motionEvent` with eventTime equal to `resampleTime` and pointer coordinates determined by
+ * linear interpolation or linear extrapolation. An earlier `resampleTime` will be used if
+ * extrapolation takes place and `resampleTime` is too far in the future. If `futureSample` is
+ * not null, interpolation will occur. If `futureSample` is null and there is enough historical
+ * data, LegacyResampler will extrapolate. Otherwise, no resampling takes place and
+ * `motionEvent` is unmodified.
+ */
+ void resampleMotionEvent(std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent,
+ const InputMessage* futureSample) override;
+
+private:
+ struct Pointer {
+ PointerProperties properties;
+ PointerCoords coords;
+ };
+
+ struct Sample {
+ std::chrono::nanoseconds eventTime;
+ std::vector<Pointer> pointers;
+
+ std::vector<PointerCoords> asPointerCoords() const {
+ std::vector<PointerCoords> pointersCoords;
+ for (const Pointer& pointer : pointers) {
+ pointersCoords.push_back(pointer.coords);
+ }
+ return pointersCoords;
+ }
+ };
+
+ /**
+ * Keeps track of the previous MotionEvent deviceId to enable comparison between the previous
+ * and the current deviceId.
+ */
+ std::optional<DeviceId> mPreviousDeviceId;
+
+ /**
+ * Up to two latest samples from MotionEvent. Updated every time resampleMotionEvent is called.
+ * Note: We store up to two samples in order to simplify the implementation. Although,
+ * calculations are possible with only one previous sample.
+ */
+ RingBuffer<Sample> mLatestSamples{/*capacity=*/2};
+
+ /**
+ * Adds up to mLatestSamples.capacity() of motionEvent's latest samples to mLatestSamples. If
+ * motionEvent has fewer samples than mLatestSamples.capacity(), then the available samples are
+ * added to mLatestSamples.
+ */
+ void updateLatestSamples(const MotionEvent& motionEvent);
+
+ static Sample messageToSample(const InputMessage& message);
+
+ /**
+ * Checks if auxiliary sample has the same pointer properties of target sample. That is,
+ * auxiliary pointer IDs must appear in the same order as target pointer IDs, their toolType
+ * must match and be resampleable.
+ */
+ static bool pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary);
+
+ /**
+ * Checks if there are necessary conditions to interpolate. For example, interpolation cannot
+ * take place if samples are too far apart in time. mLatestSamples must have at least one sample
+ * when canInterpolate is invoked.
+ */
+ bool canInterpolate(const InputMessage& futureSample) const;
+
+ /**
+ * Returns a sample interpolated between the latest sample of mLatestSamples and futureSample,
+ * if the conditions from canInterpolate are satisfied. Otherwise, returns nullopt.
+ * mLatestSamples must have at least one sample when attemptInterpolation is called.
+ */
+ std::optional<Sample> attemptInterpolation(std::chrono::nanoseconds resampleTime,
+ const InputMessage& futureSample) const;
+
+ /**
+ * Checks if there are necessary conditions to extrapolate. That is, there are at least two
+ * samples in mLatestSamples, and delta is bounded within a time interval.
+ */
+ bool canExtrapolate() const;
+
+ /**
+ * Returns a sample extrapolated from the two samples of mLatestSamples, if the conditions from
+ * canExtrapolate are satisfied. The returned sample either has eventTime equal to resampleTime,
+ * or an earlier time if resampleTime is too far in the future. If canExtrapolate returns false,
+ * this function returns nullopt.
+ */
+ std::optional<Sample> attemptExtrapolation(std::chrono::nanoseconds resampleTime) const;
+
+ inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
+};
+} // namespace android
diff --git a/include/input/TfLiteMotionPredictor.h b/include/input/TfLiteMotionPredictor.h
index 08a4330..49e909e 100644
--- a/include/input/TfLiteMotionPredictor.h
+++ b/include/input/TfLiteMotionPredictor.h
@@ -112,7 +112,7 @@
float highJerk = 0;
// Coefficient for the first-order IIR filter for jerk calculation.
- float jerkForgetFactor = 1;
+ float jerkAlpha = 1;
};
// Creates a model from an encoded Flatbuffer model.
diff --git a/include/input/VirtualInputDevice.h b/include/input/VirtualInputDevice.h
index dabe45c..b6c6305 100644
--- a/include/input/VirtualInputDevice.h
+++ b/include/input/VirtualInputDevice.h
@@ -17,14 +17,30 @@
#pragma once
#include <android-base/unique_fd.h>
+#include <input/Input.h>
+#include <map>
namespace android {
+enum class DeviceType {
+ KEYBOARD,
+ MOUSE,
+ TOUCHSCREEN,
+ DPAD,
+ STYLUS,
+ ROTARY_ENCODER,
+};
+
+android::base::unique_fd openUinput(const char* readableName, int32_t vendorId, int32_t productId,
+ const char* phys, DeviceType deviceType, int32_t screenHeight,
+ int32_t screenWidth);
+
enum class UinputAction {
RELEASE = 0,
PRESS = 1,
MOVE = 2,
CANCEL = 3,
+ ftl_last = CANCEL,
};
class VirtualInputDevice {
diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp
index 0bf3cad..54f687b 100644
--- a/libs/binder/BackendUnifiedServiceManager.cpp
+++ b/libs/binder/BackendUnifiedServiceManager.cpp
@@ -33,10 +33,19 @@
sp<AidlServiceManager> BackendUnifiedServiceManager::getImpl() {
return mTheRealServiceManager;
}
+
binder::Status BackendUnifiedServiceManager::getService(const ::std::string& name,
- os::Service* _out) {
+ sp<IBinder>* _aidl_return) {
os::Service service;
- binder::Status status = mTheRealServiceManager->getService(name, &service);
+ binder::Status status = getService2(name, &service);
+ *_aidl_return = service.get<os::Service::Tag::binder>();
+ return status;
+}
+
+binder::Status BackendUnifiedServiceManager::getService2(const ::std::string& name,
+ os::Service* _out) {
+ os::Service service;
+ binder::Status status = mTheRealServiceManager->getService2(name, &service);
toBinderService(service, _out);
return status;
}
diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h
index 4715be4..8f3839f 100644
--- a/libs/binder/BackendUnifiedServiceManager.h
+++ b/libs/binder/BackendUnifiedServiceManager.h
@@ -26,7 +26,8 @@
explicit BackendUnifiedServiceManager(const sp<os::IServiceManager>& impl);
sp<os::IServiceManager> getImpl();
- binder::Status getService(const ::std::string& name, os::Service* out) override;
+ binder::Status getService(const ::std::string& name, sp<IBinder>* _aidl_return) override;
+ binder::Status getService2(const ::std::string& name, os::Service* out) override;
binder::Status checkService(const ::std::string& name, os::Service* out) override;
binder::Status addService(const ::std::string& name, const sp<IBinder>& service,
bool allowIsolated, int32_t dumpPriority) override;
@@ -56,8 +57,6 @@
return mTheRealServiceManager->getInterfaceDescriptor();
}
- IBinder* onAsBinder() override { return IInterface::asBinder(mTheRealServiceManager).get(); }
-
private:
sp<os::IServiceManager> mTheRealServiceManager;
void toBinderService(const os::Service& in, os::Service* _out);
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index c57c9cd..53bd08d 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -143,6 +143,22 @@
return reply.readNullableStrongBinder(out);
}
+status_t IBinder::addFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ BpBinder* proxy = this->remoteBinder();
+ if (proxy != nullptr) {
+ return proxy->addFrozenStateChangeCallback(callback);
+ }
+ return INVALID_OPERATION;
+}
+
+status_t IBinder::removeFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ BpBinder* proxy = this->remoteBinder();
+ if (proxy != nullptr) {
+ return proxy->removeFrozenStateChangeCallback(callback);
+ }
+ return INVALID_OPERATION;
+}
+
status_t IBinder::getDebugPid(pid_t* out) {
BBinder* local = this->localBinder();
if (local != nullptr) {
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 6594aa6..eae844c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -160,11 +160,12 @@
// ---------------------------------------------------------------------------
-sp<BpBinder> BpBinder::create(int32_t handle) {
+sp<BpBinder> BpBinder::create(int32_t handle, std::function<void()>* postTask) {
if constexpr (!kEnableKernelIpc) {
LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
return nullptr;
}
+ LOG_ALWAYS_FATAL_IF(postTask == nullptr, "BAD STATE");
int32_t trackedUid = -1;
if (sCountByUidEnabled) {
@@ -183,7 +184,11 @@
ALOGE("Still too many binder proxy objects sent to uid %d from uid %d (%d proxies "
"held)",
getuid(), trackedUid, trackedValue);
- if (sLimitCallback) sLimitCallback(trackedUid);
+
+ if (sLimitCallback) {
+ *postTask = [=]() { sLimitCallback(trackedUid); };
+ }
+
sLastLimitCallbackMap[trackedUid] = trackedValue;
}
} else {
@@ -197,7 +202,11 @@
ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)",
getuid(), trackedUid, trackedValue);
sTrackingMap[trackedUid] |= LIMIT_REACHED_MASK;
- if (sLimitCallback) sLimitCallback(trackedUid);
+
+ if (sLimitCallback) {
+ *postTask = [=]() { sLimitCallback(trackedUid); };
+ }
+
sLastLimitCallbackMap[trackedUid] = trackedValue & COUNTING_VALUE_MASK;
if (sBinderProxyThrottleCreate) {
ALOGI("Throttling binder proxy creates from uid %d in uid %d until binder proxy"
@@ -557,6 +566,123 @@
}
}
+status_t BpBinder::addFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ LOG_ALWAYS_FATAL_IF(isRpcBinder(),
+ "addFrozenStateChangeCallback() is not supported for RPC Binder.");
+ LOG_ALWAYS_FATAL_IF(!kEnableKernelIpc, "Binder kernel driver disabled at build time");
+ LOG_ALWAYS_FATAL_IF(ProcessState::self()->getThreadPoolMaxTotalThreadCount() == 0,
+ "addFrozenStateChangeCallback on %s but there are no threads "
+ "(yet?) listening to incoming transactions. See "
+ "ProcessState::startThreadPool "
+ "and ProcessState::setThreadPoolMaxThreadCount. Generally you should "
+ "setup the binder threadpool before other initialization steps.",
+ String8(getInterfaceDescriptor()).c_str());
+ LOG_ALWAYS_FATAL_IF(callback == nullptr,
+ "addFrozenStateChangeCallback(): callback must be non-NULL");
+
+ const sp<FrozenStateChangeCallback> strongCallback = callback.promote();
+ if (strongCallback == nullptr) {
+ return BAD_VALUE;
+ }
+
+ {
+ RpcMutexUniqueLock _l(mLock);
+ if (!mFrozen) {
+ ALOGV("Requesting freeze notification: %p handle %d\n", this, binderHandle());
+ IPCThreadState* self = IPCThreadState::self();
+ status_t status = self->addFrozenStateChangeCallback(binderHandle(), this);
+ if (status != NO_ERROR) {
+ // Avoids logspam if kernel does not support freeze
+ // notification.
+ if (status != INVALID_OPERATION) {
+ ALOGE("IPCThreadState.addFrozenStateChangeCallback "
+ "failed with %s. %p handle %d\n",
+ statusToString(status).c_str(), this, binderHandle());
+ }
+ return status;
+ }
+ mFrozen = std::make_unique<FrozenStateChange>();
+ if (!mFrozen) {
+ std::ignore =
+ IPCThreadState::self()->removeFrozenStateChangeCallback(binderHandle(),
+ this);
+ return NO_MEMORY;
+ }
+ }
+ if (mFrozen->initialStateReceived) {
+ strongCallback->onStateChanged(wp<BpBinder>::fromExisting(this),
+ mFrozen->isFrozen
+ ? FrozenStateChangeCallback::State::FROZEN
+ : FrozenStateChangeCallback::State::UNFROZEN);
+ }
+ ssize_t res = mFrozen->callbacks.add(callback);
+ if (res < 0) {
+ return res;
+ }
+ return NO_ERROR;
+ }
+}
+
+status_t BpBinder::removeFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) {
+ LOG_ALWAYS_FATAL_IF(isRpcBinder(),
+ "removeFrozenStateChangeCallback() is not supported for RPC Binder.");
+ LOG_ALWAYS_FATAL_IF(!kEnableKernelIpc, "Binder kernel driver disabled at build time");
+
+ RpcMutexUniqueLock _l(mLock);
+
+ const size_t N = mFrozen ? mFrozen->callbacks.size() : 0;
+ for (size_t i = 0; i < N; i++) {
+ if (mFrozen->callbacks.itemAt(i) == callback) {
+ mFrozen->callbacks.removeAt(i);
+ if (mFrozen->callbacks.size() == 0) {
+ ALOGV("Clearing freeze notification: %p handle %d\n", this, binderHandle());
+ status_t status =
+ IPCThreadState::self()->removeFrozenStateChangeCallback(binderHandle(),
+ this);
+ if (status != NO_ERROR) {
+ ALOGE("Unexpected error from "
+ "IPCThreadState.removeFrozenStateChangeCallback: %s. "
+ "%p handle %d\n",
+ statusToString(status).c_str(), this, binderHandle());
+ }
+ mFrozen.reset();
+ }
+ return NO_ERROR;
+ }
+ }
+
+ return NAME_NOT_FOUND;
+}
+
+void BpBinder::onFrozenStateChanged(bool isFrozen) {
+ LOG_ALWAYS_FATAL_IF(isRpcBinder(), "onFrozenStateChanged is not supported for RPC Binder.");
+ LOG_ALWAYS_FATAL_IF(!kEnableKernelIpc, "Binder kernel driver disabled at build time");
+
+ ALOGV("Sending frozen state change notification for proxy %p handle %d, isFrozen=%s\n", this,
+ binderHandle(), isFrozen ? "true" : "false");
+
+ RpcMutexUniqueLock _l(mLock);
+ if (!mFrozen) {
+ return;
+ }
+ bool stateChanged = !mFrozen->initialStateReceived || mFrozen->isFrozen != isFrozen;
+ if (stateChanged) {
+ mFrozen->isFrozen = isFrozen;
+ mFrozen->initialStateReceived = true;
+ for (size_t i = 0; i < mFrozen->callbacks.size();) {
+ sp<FrozenStateChangeCallback> callback = mFrozen->callbacks.itemAt(i).promote();
+ if (callback != nullptr) {
+ callback->onStateChanged(wp<BpBinder>::fromExisting(this),
+ isFrozen ? FrozenStateChangeCallback::State::FROZEN
+ : FrozenStateChangeCallback::State::UNFROZEN);
+ i++;
+ } else {
+ mFrozen->callbacks.removeItemsAt(i);
+ }
+ }
+ }
+}
+
void BpBinder::reportOneDeath(const Obituary& obit)
{
sp<DeathRecipient> recipient = obit.recipient.promote();
@@ -686,6 +812,10 @@
if (ipc) ipc->clearDeathNotification(binderHandle(), this);
mObituaries = nullptr;
}
+ if (mFrozen != nullptr) {
+ std::ignore = IPCThreadState::self()->removeFrozenStateChangeCallback(binderHandle(), this);
+ mFrozen.reset();
+ }
mLock.unlock();
if (obits != nullptr) {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 984c93d..1d26d85 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -89,26 +89,33 @@
"BR_FROZEN_REPLY",
"BR_ONEWAY_SPAM_SUSPECT",
"BR_TRANSACTION_PENDING_FROZEN",
+ "BR_FROZEN_BINDER",
+ "BR_CLEAR_FREEZE_NOTIFICATION_DONE",
};
-static const char *kCommandStrings[] = {
- "BC_TRANSACTION",
- "BC_REPLY",
- "BC_ACQUIRE_RESULT",
- "BC_FREE_BUFFER",
- "BC_INCREFS",
- "BC_ACQUIRE",
- "BC_RELEASE",
- "BC_DECREFS",
- "BC_INCREFS_DONE",
- "BC_ACQUIRE_DONE",
- "BC_ATTEMPT_ACQUIRE",
- "BC_REGISTER_LOOPER",
- "BC_ENTER_LOOPER",
- "BC_EXIT_LOOPER",
- "BC_REQUEST_DEATH_NOTIFICATION",
- "BC_CLEAR_DEATH_NOTIFICATION",
- "BC_DEAD_BINDER_DONE"
+static const char* kCommandStrings[] = {
+ "BC_TRANSACTION",
+ "BC_REPLY",
+ "BC_ACQUIRE_RESULT",
+ "BC_FREE_BUFFER",
+ "BC_INCREFS",
+ "BC_ACQUIRE",
+ "BC_RELEASE",
+ "BC_DECREFS",
+ "BC_INCREFS_DONE",
+ "BC_ACQUIRE_DONE",
+ "BC_ATTEMPT_ACQUIRE",
+ "BC_REGISTER_LOOPER",
+ "BC_ENTER_LOOPER",
+ "BC_EXIT_LOOPER",
+ "BC_REQUEST_DEATH_NOTIFICATION",
+ "BC_CLEAR_DEATH_NOTIFICATION",
+ "BC_DEAD_BINDER_DONE",
+ "BC_TRANSACTION_SG",
+ "BC_REPLY_SG",
+ "BC_REQUEST_FREEZE_NOTIFICATION",
+ "BC_CLEAR_FREEZE_NOTIFICATION",
+ "BC_FREEZE_NOTIFICATION_DONE",
};
static const int64_t kWorkSourcePropagatedBitIndex = 32;
@@ -203,6 +210,18 @@
out << ": death cookie " << (void*)(uint64_t)c;
} break;
+ case BR_FROZEN_BINDER: {
+ const int32_t c = *cmd++;
+ const int32_t h = *cmd++;
+ const int32_t isFrozen = *cmd++;
+ out << ": freeze cookie " << (void*)(uint64_t)c << " isFrozen: " << isFrozen;
+ } break;
+
+ case BR_CLEAR_FREEZE_NOTIFICATION_DONE: {
+ const int32_t c = *cmd++;
+ out << ": freeze cookie " << (void*)(uint64_t)c;
+ } break;
+
default:
// no details to show for: BR_OK, BR_DEAD_REPLY,
// BR_TRANSACTION_COMPLETE, BR_FINISHED
@@ -270,11 +289,23 @@
out << ": handle=" << h << " (death cookie " << (void*)(uint64_t)c << ")";
} break;
+ case BC_REQUEST_FREEZE_NOTIFICATION:
+ case BC_CLEAR_FREEZE_NOTIFICATION: {
+ const int32_t h = *cmd++;
+ const int32_t c = *cmd++;
+ out << ": handle=" << h << " (freeze cookie " << (void*)(uint64_t)c << ")";
+ } break;
+
case BC_DEAD_BINDER_DONE: {
const int32_t c = *cmd++;
out << ": death cookie " << (void*)(uint64_t)c;
} break;
+ case BC_FREEZE_NOTIFICATION_DONE: {
+ const int32_t c = *cmd++;
+ out << ": freeze cookie " << (void*)(uint64_t)c;
+ } break;
+
default:
// no details to show for: BC_REGISTER_LOOPER, BC_ENTER_LOOPER,
// BC_EXIT_LOOPER
@@ -953,6 +984,33 @@
return NO_ERROR;
}
+status_t IPCThreadState::addFrozenStateChangeCallback(int32_t handle, BpBinder* proxy) {
+ static bool isSupported =
+ ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION);
+ if (!isSupported) {
+ return INVALID_OPERATION;
+ }
+ proxy->getWeakRefs()->incWeak(proxy);
+ mOut.writeInt32(BC_REQUEST_FREEZE_NOTIFICATION);
+ mOut.writeInt32((int32_t)handle);
+ mOut.writePointer((uintptr_t)proxy);
+ flushCommands();
+ return NO_ERROR;
+}
+
+status_t IPCThreadState::removeFrozenStateChangeCallback(int32_t handle, BpBinder* proxy) {
+ static bool isSupported =
+ ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION);
+ if (!isSupported) {
+ return INVALID_OPERATION;
+ }
+ mOut.writeInt32(BC_CLEAR_FREEZE_NOTIFICATION);
+ mOut.writeInt32((int32_t)handle);
+ mOut.writePointer((uintptr_t)proxy);
+ flushCommands();
+ return NO_ERROR;
+}
+
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
mServingStackPointer(nullptr),
@@ -1487,6 +1545,26 @@
proxy->getWeakRefs()->decWeak(proxy);
} break;
+ case BR_FROZEN_BINDER: {
+ const struct binder_frozen_state_info* data =
+ reinterpret_cast<const struct binder_frozen_state_info*>(
+ mIn.readInplace(sizeof(struct binder_frozen_state_info)));
+ if (data == nullptr) {
+ result = UNKNOWN_ERROR;
+ break;
+ }
+ BpBinder* proxy = (BpBinder*)data->cookie;
+ bool isFrozen = mIn.readInt32() > 0;
+ proxy->getPrivateAccessor().onFrozenStateChanged(data->is_frozen);
+ mOut.writeInt32(BC_FREEZE_NOTIFICATION_DONE);
+ mOut.writePointer(data->cookie);
+ } break;
+
+ case BR_CLEAR_FREEZE_NOTIFICATION_DONE: {
+ BpBinder* proxy = (BpBinder*)mIn.readPointer();
+ proxy->getWeakRefs()->decWeak(proxy);
+ } break;
+
case BR_FINISHED:
result = TIMED_OUT;
break;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 12a18f2..c55dd9d 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -79,10 +79,9 @@
IServiceManager::~IServiceManager() {}
// From the old libbinder IServiceManager interface to IServiceManager.
-class ServiceManagerShim : public IServiceManager
-{
+class CppBackendShim : public IServiceManager {
public:
- explicit ServiceManagerShim (const sp<AidlServiceManager>& impl);
+ explicit CppBackendShim(const sp<BackendUnifiedServiceManager>& impl);
sp<IBinder> getService(const String16& name) const override;
sp<IBinder> checkService(const String16& name) const override;
@@ -136,14 +135,14 @@
sp<RegistrationWaiter>* waiter);
// Directly get the service in a way that, for lazy services, requests the service to be started
- // if it is not currently started. This way, calls directly to ServiceManagerShim::getService
+ // if it is not currently started. This way, calls directly to CppBackendShim::getService
// will still have the 5s delay that is expected by a large amount of Android code.
//
- // When implementing ServiceManagerShim, use realGetService instead of
- // mUnifiedServiceManager->getService so that it can be overridden in ServiceManagerHostShim.
+ // When implementing CppBackendShim, use realGetService instead of
+ // mUnifiedServiceManager->getService so that it can be overridden in CppServiceManagerHostShim.
virtual Status realGetService(const std::string& name, sp<IBinder>* _aidl_return) {
Service service;
- Status status = mUnifiedServiceManager->getService(name, &service);
+ Status status = mUnifiedServiceManager->getService2(name, &service);
*_aidl_return = service.get<Service::Tag::binder>();
return status;
}
@@ -155,7 +154,7 @@
sp<IServiceManager> defaultServiceManager()
{
std::call_once(gSmOnce, []() {
- gDefaultServiceManager = sp<ServiceManagerShim>::make(getBackendUnifiedServiceManager());
+ gDefaultServiceManager = sp<CppBackendShim>::make(getBackendUnifiedServiceManager());
});
return gDefaultServiceManager;
@@ -253,8 +252,11 @@
}
}
+#endif //__ANDROID_VNDK__
+
void* openDeclaredPassthroughHal(const String16& interface, const String16& instance, int flag) {
-#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__) && !defined(__ANDROID_NATIVE_BRIDGE__)
+#if defined(__ANDROID__) && !defined(__ANDROID_VENDOR__) && !defined(__ANDROID_RECOVERY__) && \
+ !defined(__ANDROID_NATIVE_BRIDGE__)
sp<IServiceManager> sm = defaultServiceManager();
String16 name = interface + String16("/") + instance;
if (!sm->isDeclared(name)) {
@@ -274,20 +276,16 @@
#endif
}
-#endif //__ANDROID_VNDK__
-
// ----------------------------------------------------------------------
-ServiceManagerShim::ServiceManagerShim(const sp<AidlServiceManager>& impl) {
- mUnifiedServiceManager = sp<BackendUnifiedServiceManager>::make(impl);
-}
+CppBackendShim::CppBackendShim(const sp<BackendUnifiedServiceManager>& impl)
+ : mUnifiedServiceManager(impl) {}
// This implementation could be simplified and made more efficient by delegating
// to waitForService. However, this changes the threading structure in some
// cases and could potentially break prebuilts. Once we have higher logistical
// complexity, this could be attempted.
-sp<IBinder> ServiceManagerShim::getService(const String16& name) const
-{
+sp<IBinder> CppBackendShim::getService(const String16& name) const {
static bool gSystemBootCompleted = false;
sp<IBinder> svc = checkService(name);
@@ -331,8 +329,7 @@
return nullptr;
}
-sp<IBinder> ServiceManagerShim::checkService(const String16& name) const
-{
+sp<IBinder> CppBackendShim::checkService(const String16& name) const {
Service ret;
if (!mUnifiedServiceManager->checkService(String8(name).c_str(), &ret).isOk()) {
return nullptr;
@@ -340,16 +337,14 @@
return ret.get<Service::Tag::binder>();
}
-status_t ServiceManagerShim::addService(const String16& name, const sp<IBinder>& service,
- bool allowIsolated, int dumpsysPriority)
-{
+status_t CppBackendShim::addService(const String16& name, const sp<IBinder>& service,
+ bool allowIsolated, int dumpsysPriority) {
Status status = mUnifiedServiceManager->addService(String8(name).c_str(), service,
allowIsolated, dumpsysPriority);
return status.exceptionCode();
}
-Vector<String16> ServiceManagerShim::listServices(int dumpsysPriority)
-{
+Vector<String16> CppBackendShim::listServices(int dumpsysPriority) {
std::vector<std::string> ret;
if (!mUnifiedServiceManager->listServices(dumpsysPriority, &ret).isOk()) {
return {};
@@ -363,8 +358,7 @@
return res;
}
-sp<IBinder> ServiceManagerShim::waitForService(const String16& name16)
-{
+sp<IBinder> CppBackendShim::waitForService(const String16& name16) {
class Waiter : public android::os::BnServiceCallback {
Status onRegistration(const std::string& /*name*/,
const sp<IBinder>& binder) override {
@@ -453,7 +447,7 @@
}
}
-bool ServiceManagerShim::isDeclared(const String16& name) {
+bool CppBackendShim::isDeclared(const String16& name) {
bool declared;
if (Status status = mUnifiedServiceManager->isDeclared(String8(name).c_str(), &declared);
!status.isOk()) {
@@ -464,7 +458,7 @@
return declared;
}
-Vector<String16> ServiceManagerShim::getDeclaredInstances(const String16& interface) {
+Vector<String16> CppBackendShim::getDeclaredInstances(const String16& interface) {
std::vector<std::string> out;
if (Status status =
mUnifiedServiceManager->getDeclaredInstances(String8(interface).c_str(), &out);
@@ -482,7 +476,7 @@
return res;
}
-std::optional<String16> ServiceManagerShim::updatableViaApex(const String16& name) {
+std::optional<String16> CppBackendShim::updatableViaApex(const String16& name) {
std::optional<std::string> declared;
if (Status status = mUnifiedServiceManager->updatableViaApex(String8(name).c_str(), &declared);
!status.isOk()) {
@@ -493,7 +487,7 @@
return declared ? std::optional<String16>(String16(declared.value().c_str())) : std::nullopt;
}
-Vector<String16> ServiceManagerShim::getUpdatableNames(const String16& apexName) {
+Vector<String16> CppBackendShim::getUpdatableNames(const String16& apexName) {
std::vector<std::string> out;
if (Status status = mUnifiedServiceManager->getUpdatableNames(String8(apexName).c_str(), &out);
!status.isOk()) {
@@ -510,7 +504,7 @@
return res;
}
-std::optional<IServiceManager::ConnectionInfo> ServiceManagerShim::getConnectionInfo(
+std::optional<IServiceManager::ConnectionInfo> CppBackendShim::getConnectionInfo(
const String16& name) {
std::optional<os::ConnectionInfo> connectionInfo;
if (Status status =
@@ -525,8 +519,8 @@
: std::nullopt;
}
-status_t ServiceManagerShim::registerForNotifications(const String16& name,
- const sp<AidlRegistrationCallback>& cb) {
+status_t CppBackendShim::registerForNotifications(const String16& name,
+ const sp<AidlRegistrationCallback>& cb) {
if (cb == nullptr) {
ALOGE("%s: null cb passed", __FUNCTION__);
return BAD_VALUE;
@@ -545,9 +539,9 @@
return OK;
}
-void ServiceManagerShim::removeRegistrationCallbackLocked(const sp<AidlRegistrationCallback>& cb,
- ServiceCallbackMap::iterator* it,
- sp<RegistrationWaiter>* waiter) {
+void CppBackendShim::removeRegistrationCallbackLocked(const sp<AidlRegistrationCallback>& cb,
+ ServiceCallbackMap::iterator* it,
+ sp<RegistrationWaiter>* waiter) {
std::vector<LocalRegistrationAndWaiter>& localRegistrationAndWaiters = (*it)->second;
for (auto lit = localRegistrationAndWaiters.begin();
lit != localRegistrationAndWaiters.end();) {
@@ -566,8 +560,8 @@
}
}
-status_t ServiceManagerShim::unregisterForNotifications(const String16& name,
- const sp<AidlRegistrationCallback>& cb) {
+status_t CppBackendShim::unregisterForNotifications(const String16& name,
+ const sp<AidlRegistrationCallback>& cb) {
if (cb == nullptr) {
ALOGE("%s: null cb passed", __FUNCTION__);
return BAD_VALUE;
@@ -596,7 +590,7 @@
return OK;
}
-std::vector<IServiceManager::ServiceDebugInfo> ServiceManagerShim::getServiceDebugInfo() {
+std::vector<IServiceManager::ServiceDebugInfo> CppBackendShim::getServiceDebugInfo() {
std::vector<os::ServiceDebugInfo> serviceDebugInfos;
std::vector<IServiceManager::ServiceDebugInfo> ret;
if (Status status = mUnifiedServiceManager->getServiceDebugInfo(&serviceDebugInfos);
@@ -614,21 +608,21 @@
}
#ifndef __ANDROID__
-// ServiceManagerShim for host. Implements the old libbinder android::IServiceManager API.
+// CppBackendShim for host. Implements the old libbinder android::IServiceManager API.
// The internal implementation of the AIDL interface android::os::IServiceManager calls into
// on-device service manager.
-class ServiceManagerHostShim : public ServiceManagerShim {
+class CppServiceManagerHostShim : public CppBackendShim {
public:
- ServiceManagerHostShim(const sp<AidlServiceManager>& impl,
- const RpcDelegateServiceManagerOptions& options)
- : ServiceManagerShim(impl), mOptions(options) {}
- // ServiceManagerShim::getService is based on checkService, so no need to override it.
+ CppServiceManagerHostShim(const sp<AidlServiceManager>& impl,
+ const RpcDelegateServiceManagerOptions& options)
+ : CppBackendShim(sp<BackendUnifiedServiceManager>::make(impl)), mOptions(options) {}
+ // CppBackendShim::getService is based on checkService, so no need to override it.
sp<IBinder> checkService(const String16& name) const override {
return getDeviceService({String8(name).c_str()}, mOptions);
}
protected:
- // Override realGetService for ServiceManagerShim::waitForService.
+ // Override realGetService for CppBackendShim::waitForService.
Status realGetService(const std::string& name, sp<IBinder>* _aidl_return) override {
*_aidl_return = getDeviceService({"-g", name}, mOptions);
return Status::ok();
@@ -649,7 +643,7 @@
ALOGE("getDeviceService(\"manager\") returns non service manager");
return nullptr;
}
- return sp<ServiceManagerHostShim>::make(interface, options);
+ return sp<CppServiceManagerHostShim>::make(interface, options);
}
#endif
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index a42ede2..5e7f151 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -24,7 +24,6 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/Stability.h>
-#include <cutils/atomic.h>
#include <utils/AndroidThreads.h>
#include <utils/String8.h>
#include <utils/Thread.h>
@@ -57,6 +56,25 @@
// -------------------------------------------------------------------------
+namespace {
+bool readDriverFeatureFile(const char* filename) {
+ int fd = open(filename, O_RDONLY | O_CLOEXEC);
+ char on;
+ if (fd == -1) {
+ ALOGE_IF(errno != ENOENT, "%s: cannot open %s: %s", __func__, filename, strerror(errno));
+ return false;
+ }
+ if (read(fd, &on, sizeof(on)) == -1) {
+ ALOGE("%s: error reading to %s: %s", __func__, filename, strerror(errno));
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return on == '1';
+}
+
+} // namespace
+
namespace android {
using namespace android::binder::impl;
@@ -311,6 +329,7 @@
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
+ std::function<void()> postTask;
std::unique_lock<std::mutex> _l(mLock);
@@ -358,7 +377,7 @@
return nullptr;
}
- sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle);
+ sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle, &postTask);
e->binder = b.get();
if (b) e->refs = b->getWeakRefs();
result = b;
@@ -371,6 +390,10 @@
}
}
+ _l.unlock();
+
+ if (postTask) postTask();
+
return result;
}
@@ -387,7 +410,7 @@
}
String8 ProcessState::makeBinderThreadName() {
- int32_t s = android_atomic_add(1, &mThreadPoolSeq);
+ int32_t s = mThreadPoolSeq.fetch_add(1, std::memory_order_release);
pid_t pid = getpid();
std::string_view driverName = mDriverName.c_str();
@@ -429,8 +452,17 @@
}
size_t ProcessState::getThreadPoolMaxTotalThreadCount() const {
+ // Need to read `mKernelStartedThreads` before `mThreadPoolStarted` (with
+ // non-relaxed memory ordering) to avoid a race like the following:
+ //
+ // thread A: if (mThreadPoolStarted) { // evaluates false
+ // thread B: mThreadPoolStarted = true;
+ // thread B: mKernelStartedThreads++;
+ // thread A: size_t kernelStarted = mKernelStartedThreads;
+ // thread A: LOG_ALWAYS_FATAL_IF(kernelStarted != 0, ...);
+ size_t kernelStarted = mKernelStartedThreads;
+
if (mThreadPoolStarted) {
- size_t kernelStarted = mKernelStartedThreads;
size_t max = mMaxThreads;
size_t current = mCurrentThreads;
@@ -460,7 +492,6 @@
// must not be initialized or maybe has poll thread setup, we
// currently don't track this in libbinder
- size_t kernelStarted = mKernelStartedThreads;
LOG_ALWAYS_FATAL_IF(kernelStarted != 0, "Expecting 0 kernel started threads but have %zu",
kernelStarted);
return mCurrentThreads;
@@ -472,27 +503,20 @@
#define DRIVER_FEATURES_PATH "/dev/binderfs/features/"
bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) {
- static const char* const names[] = {
- [static_cast<int>(DriverFeature::ONEWAY_SPAM_DETECTION)] =
- DRIVER_FEATURES_PATH "oneway_spam_detection",
- [static_cast<int>(DriverFeature::EXTENDED_ERROR)] =
- DRIVER_FEATURES_PATH "extended_error",
- };
- int fd = open(names[static_cast<int>(feature)], O_RDONLY | O_CLOEXEC);
- char on;
- if (fd == -1) {
- ALOGE_IF(errno != ENOENT, "%s: cannot open %s: %s", __func__,
- names[static_cast<int>(feature)], strerror(errno));
- return false;
+ // Use static variable to cache the results.
+ if (feature == DriverFeature::ONEWAY_SPAM_DETECTION) {
+ static bool enabled = readDriverFeatureFile(DRIVER_FEATURES_PATH "oneway_spam_detection");
+ return enabled;
}
- if (read(fd, &on, sizeof(on)) == -1) {
- ALOGE("%s: error reading to %s: %s", __func__,
- names[static_cast<int>(feature)], strerror(errno));
- close(fd);
- return false;
+ if (feature == DriverFeature::EXTENDED_ERROR) {
+ static bool enabled = readDriverFeatureFile(DRIVER_FEATURES_PATH "extended_error");
+ return enabled;
}
- close(fd);
- return on == '1';
+ if (feature == DriverFeature::FREEZE_NOTIFICATION) {
+ static bool enabled = readDriverFeatureFile(DRIVER_FEATURES_PATH "freeze_notification");
+ return enabled;
+ }
+ return false;
}
status_t ProcessState::enableOnewaySpamDetection(bool enable) {
@@ -577,7 +601,7 @@
#ifdef __ANDROID__
LOG_ALWAYS_FATAL_IF(!opened.ok(),
"Binder driver '%s' could not be opened. Error: %s. Terminating.",
- error.c_str(), driver);
+ driver, error.c_str());
#endif
if (opened.ok()) {
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index ac95188..1d1f84f 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -60,9 +60,23 @@
* exists for legacy purposes.
*
* Returns null if the service does not exist.
+ *
+ * @deprecated TODO(b/355394904): Use getService2 instead.
*/
@UnsupportedAppUsage
- Service getService(@utf8InCpp String name);
+ @nullable IBinder getService(@utf8InCpp String name);
+
+ /**
+ * Retrieve an existing service called @a name from the
+ * service manager.
+ *
+ * This is the same as checkService (returns immediately) but
+ * exists for legacy purposes.
+ *
+ * Returns an enum Service that can be of different types. The
+ * enum value is null if the service does not exist.
+ */
+ Service getService2(@utf8InCpp String name);
/**
* Retrieve an existing service called @a name from the service
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index b3a2d9e..65cdcd7 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -32,4 +32,34 @@
#include <linux/android/binder.h>
#include <sys/ioctl.h>
+struct binder_frozen_state_info {
+ binder_uintptr_t cookie;
+ __u32 is_frozen;
+};
+
+#ifndef BR_FROZEN_BINDER
+// Temporary definition of BR_FROZEN_BINDER until UAPI binder.h includes it.
+#define BR_FROZEN_BINDER _IOR('r', 21, struct binder_frozen_state_info)
+#endif // BR_FROZEN_BINDER
+
+#ifndef BR_CLEAR_FREEZE_NOTIFICATION_DONE
+// Temporary definition of BR_CLEAR_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it.
+#define BR_CLEAR_FREEZE_NOTIFICATION_DONE _IOR('r', 22, binder_uintptr_t)
+#endif // BR_CLEAR_FREEZE_NOTIFICATION_DONE
+
+#ifndef BC_REQUEST_FREEZE_NOTIFICATION
+// Temporary definition of BC_REQUEST_FREEZE_NOTIFICATION until UAPI binder.h includes it.
+#define BC_REQUEST_FREEZE_NOTIFICATION _IOW('c', 19, struct binder_handle_cookie)
+#endif // BC_REQUEST_FREEZE_NOTIFICATION
+
+#ifndef BC_CLEAR_FREEZE_NOTIFICATION
+// Temporary definition of BC_CLEAR_FREEZE_NOTIFICATION until UAPI binder.h includes it.
+#define BC_CLEAR_FREEZE_NOTIFICATION _IOW('c', 20, struct binder_handle_cookie)
+#endif // BC_CLEAR_FREEZE_NOTIFICATION
+
+#ifndef BC_FREEZE_NOTIFICATION_DONE
+// Temporary definition of BC_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it.
+#define BC_FREEZE_NOTIFICATION_DONE _IOW('c', 21, binder_uintptr_t)
+#endif // BC_FREEZE_NOTIFICATION_DONE
+
#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index d7f74c4..7518044 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -29,6 +29,7 @@
// ---------------------------------------------------------------------------
namespace android {
+class IPCThreadState;
class RpcSession;
class RpcState;
namespace internal {
@@ -66,6 +67,12 @@
void* cookie = nullptr, uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = nullptr);
+ [[nodiscard]] status_t addFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& recipient);
+
+ [[nodiscard]] status_t removeFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& recipient);
+
LIBBINDER_EXPORTED virtual void* attachObject(const void* objectID, void* object,
void* cleanupCookie,
object_cleanup_func func) final;
@@ -75,7 +82,6 @@
LIBBINDER_EXPORTED sp<IBinder> lookupOrCreateWeak(const void* objectID,
IBinder::object_make_func make,
const void* makeArgs);
-
LIBBINDER_EXPORTED virtual BpBinder* remoteBinder();
LIBBINDER_EXPORTED void sendObituary();
@@ -132,9 +138,14 @@
friend class ::android::ProcessState;
friend class ::android::RpcSession;
friend class ::android::RpcState;
- explicit PrivateAccessor(const BpBinder* binder) : mBinder(binder) {}
+ friend class ::android::IPCThreadState;
+ explicit PrivateAccessor(const BpBinder* binder)
+ : mBinder(binder), mMutableBinder(nullptr) {}
+ explicit PrivateAccessor(BpBinder* binder) : mBinder(binder), mMutableBinder(binder) {}
- static sp<BpBinder> create(int32_t handle) { return BpBinder::create(handle); }
+ static sp<BpBinder> create(int32_t handle, std::function<void()>* postTask) {
+ return BpBinder::create(handle, postTask);
+ }
static sp<BpBinder> create(const sp<RpcSession>& session, uint64_t address) {
return BpBinder::create(session, address);
}
@@ -146,17 +157,22 @@
uint64_t rpcAddress() const { return mBinder->rpcAddress(); }
const sp<RpcSession>& rpcSession() const { return mBinder->rpcSession(); }
+ void onFrozenStateChanged(bool isFrozen) { mMutableBinder->onFrozenStateChanged(isFrozen); }
const BpBinder* mBinder;
+ BpBinder* mMutableBinder;
};
+
LIBBINDER_EXPORTED const PrivateAccessor getPrivateAccessor() const {
return PrivateAccessor(this);
}
+ PrivateAccessor getPrivateAccessor() { return PrivateAccessor(this); }
+
private:
friend PrivateAccessor;
friend class sp<BpBinder>;
- static sp<BpBinder> create(int32_t handle);
+ static sp<BpBinder> create(int32_t handle, std::function<void()>* postTask);
static sp<BpBinder> create(const sp<RpcSession>& session, uint64_t address);
struct BinderHandle {
@@ -192,6 +208,14 @@
uint32_t flags;
};
+ void onFrozenStateChanged(bool isFrozen);
+
+ struct FrozenStateChange {
+ bool isFrozen = false;
+ Vector<wp<FrozenStateChangeCallback>> callbacks;
+ bool initialStateReceived = false;
+ };
+
void reportOneDeath(const Obituary& obit);
bool isDescriptorCached() const;
@@ -199,6 +223,7 @@
volatile int32_t mAlive;
volatile int32_t mObitsSent;
Vector<Obituary>* mObituaries;
+ std::unique_ptr<FrozenStateChange> mFrozen;
ObjectManager mObjects;
mutable String16 mDescriptorCache;
int32_t mTrackedUid;
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 4eb1c08..1ed7c91 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -202,9 +202,18 @@
virtual void binderDied(const wp<IBinder>& who) = 0;
};
- #if defined(__clang__)
- #pragma clang diagnostic pop
- #endif
+ class FrozenStateChangeCallback : public virtual RefBase {
+ public:
+ enum class State {
+ FROZEN,
+ UNFROZEN,
+ };
+ virtual void onStateChanged(const wp<IBinder>& who, State state) = 0;
+ };
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
/**
* Register the @a recipient for a notification if this binder
@@ -253,6 +262,48 @@
uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = nullptr) = 0;
+ /**
+ * addFrozenStateChangeCallback provides a callback mechanism to notify
+ * about process frozen/unfrozen events. Upon registration and any
+ * subsequent state changes, the callback is invoked with the latest process
+ * frozen state.
+ *
+ * If the listener process (the one using this API) is itself frozen, state
+ * change events might be combined into a single one with the latest state.
+ * (meaning 'frozen, unfrozen' might just be 'unfrozen'). This single event
+ * would then be delivered when the listener process becomes unfrozen.
+ * Similarly, if an event happens before the previous event is consumed,
+ * they might be combined. This means the callback might not be called for
+ * every single state change, so don't rely on this API to count how many
+ * times the state has changed.
+ *
+ * @note When all references to the binder are dropped, the callback is
+ * automatically removed. So, you must hold onto a binder in order to
+ * receive notifications about it.
+ *
+ * @note You will only receive freeze notifications for remote binders, as
+ * local binders by definition can't be frozen without you being frozen as
+ * well. Trying to use this function on a local binder will result in an
+ * INVALID_OPERATION code being returned and nothing happening.
+ *
+ * @note This binder always holds a weak reference to the callback.
+ *
+ * @note You will only receive a weak reference to the binder object. You
+ * should not try to promote this to a strong reference. (Nor should you
+ * need to, as there is nothing useful you can directly do with it now that
+ * it has passed on.)
+ */
+ [[nodiscard]] status_t addFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& callback);
+
+ /**
+ * Remove a previously registered freeze callback.
+ * The @a callback will no longer be called if this object
+ * changes its frozen state.
+ */
+ [[nodiscard]] status_t removeFrozenStateChangeCallback(
+ const wp<FrozenStateChangeCallback>& callback);
+
virtual bool checkSubclass(const void* subclassID) const;
typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie);
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 09ab442..9ef4e69 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -174,6 +174,8 @@
LIBBINDER_EXPORTED static void expungeHandle(int32_t handle, IBinder* binder);
LIBBINDER_EXPORTED status_t requestDeathNotification(int32_t handle, BpBinder* proxy);
LIBBINDER_EXPORTED status_t clearDeathNotification(int32_t handle, BpBinder* proxy);
+ [[nodiscard]] status_t addFrozenStateChangeCallback(int32_t handle, BpBinder* proxy);
+ [[nodiscard]] status_t removeFrozenStateChangeCallback(int32_t handle, BpBinder* proxy);
LIBBINDER_EXPORTED static void shutdown();
@@ -210,13 +212,14 @@
IPCThreadState();
~IPCThreadState();
- status_t sendReply(const Parcel& reply, uint32_t flags);
- status_t waitForResponse(Parcel* reply, status_t* acquireResult = nullptr);
- status_t talkWithDriver(bool doReceive = true);
- status_t writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle, uint32_t code,
- const Parcel& data, status_t* statusBuffer);
- status_t getAndExecuteCommand();
- status_t executeCommand(int32_t command);
+ [[nodiscard]] status_t sendReply(const Parcel& reply, uint32_t flags);
+ [[nodiscard]] status_t waitForResponse(Parcel* reply, status_t* acquireResult = nullptr);
+ [[nodiscard]] status_t talkWithDriver(bool doReceive = true);
+ [[nodiscard]] status_t writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle,
+ uint32_t code, const Parcel& data,
+ status_t* statusBuffer);
+ [[nodiscard]] status_t getAndExecuteCommand();
+ [[nodiscard]] status_t executeCommand(int32_t command);
void processPendingDerefs();
void processPostWriteDerefs();
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 021bd58..21bfd42 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -133,6 +133,7 @@
enum class DriverFeature {
ONEWAY_SPAM_DETECTION,
EXTENDED_ERROR,
+ FREEZE_NOTIFICATION,
};
// Determine whether a feature is supported by the binder driver.
LIBBINDER_EXPORTED static bool isDriverFeatureEnabled(const DriverFeature feature);
@@ -188,8 +189,8 @@
Vector<handle_entry> mHandleToObject;
bool mForked;
- bool mThreadPoolStarted;
- volatile int32_t mThreadPoolSeq;
+ std::atomic_bool mThreadPoolStarted;
+ std::atomic_int32_t mThreadPoolSeq;
CallRestriction mCallRestriction;
};
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 26c228d..4e02ace 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -255,6 +255,9 @@
"include_cpp/android/*.h",
],
license: "NOTICE",
+ // These are intentionally not C. It's a mistake that they're in the NDK.
+ // See the bug above.
+ skip_verification: true,
}
ndk_library {
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index 68528e1..6aff994 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -47,8 +47,11 @@
* be called once before startThreadPool. The number of threads can never decrease.
*
* This count refers to the number of threads that will be created lazily by the kernel, in
- * addition to the threads created by ABinderProcess_startThreadPool or
- * ABinderProcess_joinThreadPool.
+ * addition to the single threads created by ABinderProcess_startThreadPool (+1) or
+ * ABinderProcess_joinThreadPool (+1). Note: ABinderProcess_startThreadPool starts a thread
+ * itself, but it also enables up to the number of threads passed to this function to start.
+ * This function does not start any threads itself; it only configures
+ * ABinderProcess_startThreadPool.
*
* Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
* function should be responsible for configuring the threadpool for the entire application.
@@ -63,8 +66,8 @@
bool ABinderProcess_isThreadPoolStarted(void);
/**
* This adds the current thread to the threadpool. This thread will be in addition to the thread
- * started by ABinderProcess_startThreadPool and the lazy kernel-started threads specified by
- * ABinderProcess_setThreadPoolMaxThreadCount.
+ * configured with ABinderProcess_setThreadPoolMaxThreadCount and started with
+ * ABinderProcess_startThreadPool.
*
* Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
* function should be responsible for configuring the threadpool for the entire application.
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
index 2e46345..174fe8a 100644
--- a/libs/binder/rust/rpcbinder/Android.bp
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -32,6 +32,7 @@
apex_available: [
"//apex_available:platform",
"com.android.compos",
+ "com.android.microfuchsia",
"com.android.uwb",
"com.android.virt",
],
@@ -60,6 +61,7 @@
apex_available: [
"//apex_available:platform",
"com.android.compos",
+ "com.android.microfuchsia",
"com.android.uwb",
"com.android.virt",
],
@@ -93,6 +95,7 @@
apex_available: [
"//apex_available:platform",
"com.android.compos",
+ "com.android.microfuchsia",
"com.android.uwb",
"com.android.virt",
],
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index 33dfe19..7f70396 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -1333,7 +1333,7 @@
let vec = Vec::<u8>::deserialize(parcel.borrowed_ref()).unwrap();
assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]);
- let u16s = [u16::max_value(), 12_345, 42, 117];
+ let u16s = [u16::MAX, 12_345, 42, 117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1348,7 +1348,7 @@
}
assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::MAX
assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
@@ -1361,9 +1361,9 @@
let vec = Vec::<u16>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]);
+ assert_eq!(vec, [u16::MAX, 12_345, 42, 117]);
- let i16s = [i16::max_value(), i16::min_value(), 42, -117];
+ let i16s = [i16::MAX, i16::MIN, 42, -117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1378,8 +1378,8 @@
}
assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::MAX
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::MIN
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117
@@ -1391,9 +1391,9 @@
let vec = Vec::<i16>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]);
+ assert_eq!(vec, [i16::MAX, i16::MIN, 42, -117]);
- let u32s = [u32::max_value(), 12_345, 42, 117];
+ let u32s = [u32::MAX, 12_345, 42, 117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1408,7 +1408,7 @@
}
assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::MAX
assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
@@ -1421,9 +1421,9 @@
let vec = Vec::<u32>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]);
+ assert_eq!(vec, [u32::MAX, 12_345, 42, 117]);
- let i32s = [i32::max_value(), i32::min_value(), 42, -117];
+ let i32s = [i32::MAX, i32::MIN, 42, -117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1438,8 +1438,8 @@
}
assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::MAX
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::MIN
assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117
@@ -1451,9 +1451,9 @@
let vec = Vec::<i32>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]);
+ assert_eq!(vec, [i32::MAX, i32::MIN, 42, -117]);
- let u64s = [u64::max_value(), 12_345, 42, 117];
+ let u64s = [u64::MAX, 12_345, 42, 117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1469,9 +1469,9 @@
let vec = Vec::<u64>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]);
+ assert_eq!(vec, [u64::MAX, 12_345, 42, 117]);
- let i64s = [i64::max_value(), i64::min_value(), 42, -117];
+ let i64s = [i64::MAX, i64::MIN, 42, -117];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1487,9 +1487,9 @@
let vec = Vec::<i64>::deserialize(parcel.borrowed_ref()).unwrap();
- assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]);
+ assert_eq!(vec, [i64::MAX, i64::MIN, 42, -117]);
- let f32s = [std::f32::NAN, std::f32::INFINITY, 1.23456789, std::f32::EPSILON];
+ let f32s = [f32::NAN, f32::INFINITY, 1.23456789, f32::EPSILON];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
@@ -1509,7 +1509,7 @@
assert!(vec[0].is_nan());
assert_eq!(vec[1..], f32s[1..]);
- let f64s = [std::f64::NAN, std::f64::INFINITY, 1.234567890123456789, std::f64::EPSILON];
+ let f64s = [f64::NAN, f64::INFINITY, 1.234567890123456789, f64::EPSILON];
// SAFETY: start is less than the current size of the parcel data buffer, because we haven't
// made it any shorter since we got the position.
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index 2b6c282..a902e96 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -124,7 +124,7 @@
bindings::Transaction_TEST_BYTE => {
assert_eq!(parcel.read::<i8>()?, 0);
assert_eq!(parcel.read::<i8>()?, 1);
- assert_eq!(parcel.read::<i8>()?, i8::max_value());
+ assert_eq!(parcel.read::<i8>()?, i8::MAX);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<i8>>()?, unsafe { bindings::TESTDATA_I8 });
// SAFETY: Just reading an extern constant.
@@ -133,7 +133,7 @@
reply.write(&0i8)?;
reply.write(&1i8)?;
- reply.write(&i8::max_value())?;
+ reply.write(&i8::MAX)?;
// SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_I8 }[..])?;
// SAFETY: Just reading an extern constant.
@@ -143,14 +143,14 @@
bindings::Transaction_TEST_U16 => {
assert_eq!(parcel.read::<u16>()?, 0);
assert_eq!(parcel.read::<u16>()?, 1);
- assert_eq!(parcel.read::<u16>()?, u16::max_value());
+ assert_eq!(parcel.read::<u16>()?, u16::MAX);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<u16>>()?, unsafe { bindings::TESTDATA_CHARS });
assert_eq!(parcel.read::<Option<Vec<u16>>>()?, None);
reply.write(&0u16)?;
reply.write(&1u16)?;
- reply.write(&u16::max_value())?;
+ reply.write(&u16::MAX)?;
// SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_CHARS }[..])?;
reply.write(&(None as Option<Vec<u16>>))?;
@@ -158,14 +158,14 @@
bindings::Transaction_TEST_I32 => {
assert_eq!(parcel.read::<i32>()?, 0);
assert_eq!(parcel.read::<i32>()?, 1);
- assert_eq!(parcel.read::<i32>()?, i32::max_value());
+ assert_eq!(parcel.read::<i32>()?, i32::MAX);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<i32>>()?, unsafe { bindings::TESTDATA_I32 });
assert_eq!(parcel.read::<Option<Vec<i32>>>()?, None);
reply.write(&0i32)?;
reply.write(&1i32)?;
- reply.write(&i32::max_value())?;
+ reply.write(&i32::MAX)?;
// SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_I32 }[..])?;
reply.write(&(None as Option<Vec<i32>>))?;
@@ -173,14 +173,14 @@
bindings::Transaction_TEST_I64 => {
assert_eq!(parcel.read::<i64>()?, 0);
assert_eq!(parcel.read::<i64>()?, 1);
- assert_eq!(parcel.read::<i64>()?, i64::max_value());
+ assert_eq!(parcel.read::<i64>()?, i64::MAX);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<i64>>()?, unsafe { bindings::TESTDATA_I64 });
assert_eq!(parcel.read::<Option<Vec<i64>>>()?, None);
reply.write(&0i64)?;
reply.write(&1i64)?;
- reply.write(&i64::max_value())?;
+ reply.write(&i64::MAX)?;
// SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_I64 }[..])?;
reply.write(&(None as Option<Vec<i64>>))?;
@@ -188,14 +188,14 @@
bindings::Transaction_TEST_U64 => {
assert_eq!(parcel.read::<u64>()?, 0);
assert_eq!(parcel.read::<u64>()?, 1);
- assert_eq!(parcel.read::<u64>()?, u64::max_value());
+ assert_eq!(parcel.read::<u64>()?, u64::MAX);
// SAFETY: Just reading an extern constant.
assert_eq!(parcel.read::<Vec<u64>>()?, unsafe { bindings::TESTDATA_U64 });
assert_eq!(parcel.read::<Option<Vec<u64>>>()?, None);
reply.write(&0u64)?;
reply.write(&1u64)?;
- reply.write(&u64::max_value())?;
+ reply.write(&u64::MAX)?;
// SAFETY: Just reading an extern constant.
reply.write(&unsafe { bindings::TESTDATA_U64 }[..])?;
reply.write(&(None as Option<Vec<u64>>))?;
diff --git a/libs/binder/servicedispatcher.cpp b/libs/binder/servicedispatcher.cpp
index 201dfbc..be99065 100644
--- a/libs/binder/servicedispatcher.cpp
+++ b/libs/binder/servicedispatcher.cpp
@@ -118,7 +118,12 @@
class ServiceManagerProxyToNative : public android::os::BnServiceManager {
public:
ServiceManagerProxyToNative(const sp<android::os::IServiceManager>& impl) : mImpl(impl) {}
- android::binder::Status getService(const std::string&, android::os::Service*) override {
+ android::binder::Status getService(const std::string&,
+ android::sp<android::IBinder>*) override {
+ // We can't send BpBinder for regular binder over RPC.
+ return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
+ }
+ android::binder::Status getService2(const std::string&, android::os::Service*) override {
// We can't send BpBinder for regular binder over RPC.
return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 4c7684c..21c32ac 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -42,6 +42,9 @@
defaults: ["binder_test_defaults"],
header_libs: ["libbinder_headers"],
srcs: ["binderDriverInterfaceTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ ],
test_suites: [
"device-tests",
"vts",
@@ -127,6 +130,7 @@
"libbase",
"libbinder",
"liblog",
+ "libprocessgroup",
"libutils",
],
static_libs: [
@@ -235,6 +239,16 @@
"binder_test_defaults",
],
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
static_libs: [
"libbinder_test_utils",
"libbinder_tls_static",
@@ -267,7 +281,6 @@
defaults: [
"binderRpcTest_common_defaults",
],
- compile_multilib: "first",
srcs: [
"binderRpcTest.cpp",
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index 7be4f21..af82860 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -20,12 +20,15 @@
#include <stdlib.h>
#include <binder/IBinder.h>
+#include <binder/ProcessState.h>
#include <gtest/gtest.h>
#include <linux/android/binder.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include "../binder_module.h"
+
#define BINDER_DEV_NAME "/dev/binder"
testing::Environment* binder_env;
@@ -365,6 +368,251 @@
binderTestReadEmpty();
}
+TEST_F(BinderDriverInterfaceTest, RequestFrozenNotification) {
+ if (!android::ProcessState::isDriverFeatureEnabled(
+ android::ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support freeze notification";
+ return;
+ }
+ binder_uintptr_t cookie = 1234;
+ struct {
+ uint32_t cmd0;
+ uint32_t arg0;
+ uint32_t cmd1;
+ struct binder_handle_cookie arg1;
+ } __attribute__((packed)) bc1 = {
+ .cmd0 = BC_INCREFS,
+ .arg0 = 0,
+ .cmd1 = BC_REQUEST_FREEZE_NOTIFICATION,
+ .arg1 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ };
+ struct {
+ uint32_t cmd0;
+ // Expecting a BR_FROZEN_BINDER since BC_REQUEST_FREEZE_NOTIFICATION
+ // above should lead to an immediate notification of the current state.
+ uint32_t cmd1;
+ struct binder_frozen_state_info arg1;
+ uint32_t pad[16];
+ } __attribute__((packed)) br1;
+ struct {
+ uint32_t cmd2;
+ binder_uintptr_t arg2;
+ uint32_t cmd3;
+ struct binder_handle_cookie arg3;
+ uint32_t cmd4;
+ uint32_t arg4;
+ } __attribute__((packed)) bc2 = {
+ // Tell kernel that userspace has done handling BR_FROZEN_BINDER.
+ .cmd2 = BC_FREEZE_NOTIFICATION_DONE,
+ .arg2 = cookie,
+ .cmd3 = BC_CLEAR_FREEZE_NOTIFICATION,
+ .arg3 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ .cmd4 = BC_DECREFS,
+ .arg4 = 0,
+ };
+ struct {
+ uint32_t cmd2;
+ uint32_t cmd3;
+ binder_uintptr_t arg3;
+ uint32_t pad[16];
+ } __attribute__((packed)) br2;
+
+ struct binder_write_read bwr1 = binder_write_read();
+ bwr1.write_buffer = (uintptr_t)&bc1;
+ bwr1.write_size = sizeof(bc1);
+ bwr1.read_buffer = (uintptr_t)&br1;
+ bwr1.read_size = sizeof(br1);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr1);
+ EXPECT_EQ(sizeof(bc1), bwr1.write_consumed);
+ EXPECT_EQ(sizeof(br1) - sizeof(br1.pad), bwr1.read_consumed);
+ EXPECT_EQ(BR_NOOP, br1.cmd0);
+ ASSERT_EQ(BR_FROZEN_BINDER, br1.cmd1);
+ EXPECT_FALSE(br1.arg1.is_frozen);
+
+ struct binder_write_read bwr2 = binder_write_read();
+ bwr2.write_buffer = (uintptr_t)&bc2;
+ bwr2.write_size = sizeof(bc2);
+ bwr2.read_buffer = (uintptr_t)&br2;
+ bwr2.read_size = sizeof(br2);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr2);
+ EXPECT_EQ(sizeof(bc2), bwr2.write_consumed);
+ EXPECT_EQ(sizeof(br2) - sizeof(br2.pad), bwr2.read_consumed);
+ EXPECT_EQ(BR_NOOP, br2.cmd2);
+ EXPECT_EQ(BR_CLEAR_FREEZE_NOTIFICATION_DONE, br2.cmd3);
+ EXPECT_EQ(cookie, br2.arg3);
+
+ binderTestReadEmpty();
+}
+
+TEST_F(BinderDriverInterfaceTest, OverwritePendingFrozenNotification) {
+ if (!android::ProcessState::isDriverFeatureEnabled(
+ android::ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support freeze notification";
+ return;
+ }
+ binder_uintptr_t cookie = 1234;
+ struct {
+ uint32_t cmd0;
+ uint32_t arg0;
+ uint32_t cmd1;
+ struct binder_handle_cookie arg1;
+ uint32_t cmd2;
+ struct binder_handle_cookie arg2;
+ uint32_t cmd3;
+ uint32_t arg3;
+ } __attribute__((packed)) bc = {
+ .cmd0 = BC_INCREFS,
+ .arg0 = 0,
+ .cmd1 = BC_REQUEST_FREEZE_NOTIFICATION,
+ // This BC_REQUEST_FREEZE_NOTIFICATION should lead to a pending
+ // frozen notification inserted into the queue.
+ .arg1 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ // Send BC_CLEAR_FREEZE_NOTIFICATION before the above frozen
+ // notification has a chance of being sent. The notification should
+ // be overwritten. Userspace is expected to only receive
+ // BR_CLEAR_FREEZE_NOTIFICATION_DONE.
+ .cmd2 = BC_CLEAR_FREEZE_NOTIFICATION,
+ .arg2 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ .cmd3 = BC_DECREFS,
+ .arg3 = 0,
+ };
+ struct {
+ uint32_t cmd0;
+ uint32_t cmd1;
+ binder_uintptr_t arg1;
+ uint32_t pad[16];
+ } __attribute__((packed)) br;
+ struct binder_write_read bwr = binder_write_read();
+
+ bwr.write_buffer = (uintptr_t)&bc;
+ bwr.write_size = sizeof(bc);
+ bwr.read_buffer = (uintptr_t)&br;
+ bwr.read_size = sizeof(br);
+
+ binderTestIoctl(BINDER_WRITE_READ, &bwr);
+ EXPECT_EQ(sizeof(bc), bwr.write_consumed);
+ EXPECT_EQ(sizeof(br) - sizeof(br.pad), bwr.read_consumed);
+ EXPECT_EQ(BR_NOOP, br.cmd0);
+ EXPECT_EQ(BR_CLEAR_FREEZE_NOTIFICATION_DONE, br.cmd1);
+ EXPECT_EQ(cookie, br.arg1);
+ binderTestReadEmpty();
+}
+
+TEST_F(BinderDriverInterfaceTest, ResendFrozenNotification) {
+ if (!android::ProcessState::isDriverFeatureEnabled(
+ android::ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support freeze notification";
+ return;
+ }
+ binder_uintptr_t cookie = 1234;
+ struct {
+ uint32_t cmd0;
+ uint32_t arg0;
+ uint32_t cmd1;
+ struct binder_handle_cookie arg1;
+ } __attribute__((packed)) bc1 = {
+ .cmd0 = BC_INCREFS,
+ .arg0 = 0,
+ .cmd1 = BC_REQUEST_FREEZE_NOTIFICATION,
+ .arg1 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ };
+ struct {
+ uint32_t cmd0;
+ uint32_t cmd1;
+ struct binder_frozen_state_info arg1;
+ uint32_t pad[16];
+ } __attribute__((packed)) br1;
+ struct {
+ uint32_t cmd2;
+ struct binder_handle_cookie arg2;
+ } __attribute__((packed)) bc2 = {
+ // Clear the notification before acknowledging the in-flight
+ // BR_FROZEN_BINDER. Kernel should hold off sending
+ // BR_CLEAR_FREEZE_NOTIFICATION_DONE until the acknowledgement
+ // reaches kernel.
+ .cmd2 = BC_CLEAR_FREEZE_NOTIFICATION,
+ .arg2 =
+ {
+ .handle = 0,
+ .cookie = cookie,
+ },
+ };
+ struct {
+ uint32_t pad[16];
+ } __attribute__((packed)) br2;
+ struct {
+ uint32_t cmd3;
+ binder_uintptr_t arg3;
+ uint32_t cmd4;
+ uint32_t arg4;
+ } __attribute__((packed)) bc3 = {
+ // Send the acknowledgement. Now the kernel should send out
+ // BR_CLEAR_FREEZE_NOTIFICATION_DONE.
+ .cmd3 = BC_FREEZE_NOTIFICATION_DONE,
+ .arg3 = cookie,
+ .cmd4 = BC_DECREFS,
+ .arg4 = 0,
+ };
+ struct {
+ uint32_t cmd2;
+ uint32_t cmd3;
+ binder_uintptr_t arg3;
+ uint32_t pad[16];
+ } __attribute__((packed)) br3;
+
+ struct binder_write_read bwr1 = binder_write_read();
+ bwr1.write_buffer = (uintptr_t)&bc1;
+ bwr1.write_size = sizeof(bc1);
+ bwr1.read_buffer = (uintptr_t)&br1;
+ bwr1.read_size = sizeof(br1);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr1);
+ EXPECT_EQ(sizeof(bc1), bwr1.write_consumed);
+ EXPECT_EQ(sizeof(br1) - sizeof(br1.pad), bwr1.read_consumed);
+ EXPECT_EQ(BR_NOOP, br1.cmd0);
+ ASSERT_EQ(BR_FROZEN_BINDER, br1.cmd1);
+ EXPECT_FALSE(br1.arg1.is_frozen);
+
+ struct binder_write_read bwr2 = binder_write_read();
+ bwr2.write_buffer = (uintptr_t)&bc2;
+ bwr2.write_size = sizeof(bc2);
+ bwr2.read_buffer = (uintptr_t)&br2;
+ bwr2.read_size = sizeof(br2);
+ binderTestIoctlSuccessOrError(BINDER_WRITE_READ, &bwr2, EAGAIN);
+ binderTestReadEmpty();
+
+ struct binder_write_read bwr3 = binder_write_read();
+ bwr3.write_buffer = (uintptr_t)&bc3;
+ bwr3.write_size = sizeof(bc3);
+ bwr3.read_buffer = (uintptr_t)&br3;
+ bwr3.read_size = sizeof(br3);
+ binderTestIoctl(BINDER_WRITE_READ, &bwr3);
+ EXPECT_EQ(sizeof(bc3), bwr3.write_consumed);
+ EXPECT_EQ(sizeof(br3) - sizeof(br3.pad), bwr3.read_consumed);
+ EXPECT_EQ(BR_CLEAR_FREEZE_NOTIFICATION_DONE, br3.cmd3);
+ EXPECT_EQ(cookie, br3.arg3);
+ binderTestReadEmpty();
+}
+
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 9b1ba01..bcab6de 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -27,6 +27,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/result-gmock.h>
#include <binder/Binder.h>
@@ -39,6 +40,8 @@
#include <binder/RpcSession.h>
#include <binder/Status.h>
#include <binder/unique_fd.h>
+#include <input/BlockingQueue.h>
+#include <processgroup/processgroup.h>
#include <utils/Flattenable.h>
#include <linux/sched.h>
@@ -57,6 +60,7 @@
using android::base::testing::HasValue;
using android::binder::Status;
using android::binder::unique_fd;
+using std::chrono_literals::operator""ms;
using testing::ExplainMatchResult;
using testing::Matcher;
using testing::Not;
@@ -115,6 +119,8 @@
BINDER_LIB_TEST_NOP_TRANSACTION_WAIT,
BINDER_LIB_TEST_GETPID,
BINDER_LIB_TEST_GETUID,
+ BINDER_LIB_TEST_LISTEN_FOR_FROZEN_STATE_CHANGE,
+ BINDER_LIB_TEST_CONSUME_STATE_CHANGE_EVENTS,
BINDER_LIB_TEST_ECHO_VECTOR,
BINDER_LIB_TEST_GET_NON_BLOCKING_FD,
BINDER_LIB_TEST_REJECT_OBJECTS,
@@ -247,6 +253,43 @@
sp<IBinder> m_server;
};
+class TestFrozenStateChangeCallback : public IBinder::FrozenStateChangeCallback {
+public:
+ BlockingQueue<std::pair<const wp<IBinder>, State>> events;
+
+ virtual void onStateChanged(const wp<IBinder>& who, State state) {
+ events.push(std::make_pair(who, state));
+ }
+
+ void ensureFrozenEventReceived() {
+ auto event = events.popWithTimeout(500ms);
+ ASSERT_TRUE(event.has_value());
+ EXPECT_EQ(State::FROZEN, event->second); // isFrozen should be true
+ EXPECT_EQ(0u, events.size());
+ }
+
+ void ensureUnfrozenEventReceived() {
+ auto event = events.popWithTimeout(500ms);
+ ASSERT_TRUE(event.has_value());
+ EXPECT_EQ(State::UNFROZEN, event->second); // isFrozen should be false
+ EXPECT_EQ(0u, events.size());
+ }
+
+ std::vector<bool> getAllAndClear() {
+ std::vector<bool> results;
+ while (true) {
+ auto event = events.popWithTimeout(0ms);
+ if (!event.has_value()) {
+ break;
+ }
+ results.push_back(event->second == State::FROZEN);
+ }
+ return results;
+ }
+
+ sp<IBinder> binder;
+};
+
class BinderLibTest : public ::testing::Test {
public:
virtual void SetUp() {
@@ -291,6 +334,51 @@
EXPECT_EQ(1, ret);
}
+ bool checkFreezeSupport() {
+ std::ifstream freezer_file("/sys/fs/cgroup/uid_0/cgroup.freeze");
+ // Pass test on devices where the cgroup v2 freezer is not supported
+ if (freezer_file.fail()) {
+ return false;
+ }
+ return IPCThreadState::self()->freeze(getpid(), false, 0) == NO_ERROR;
+ }
+
+ bool checkFreezeAndNotificationSupport() {
+ if (!checkFreezeSupport()) {
+ return false;
+ }
+ return ProcessState::isDriverFeatureEnabled(
+ ProcessState::DriverFeature::FREEZE_NOTIFICATION);
+ }
+
+ bool getBinderPid(int32_t* pid, sp<IBinder> server) {
+ Parcel data, replypid;
+ if (server->transact(BINDER_LIB_TEST_GETPID, data, &replypid) != NO_ERROR) {
+ ALOGE("BINDER_LIB_TEST_GETPID failed");
+ return false;
+ }
+ *pid = replypid.readInt32();
+ if (*pid <= 0) {
+ ALOGE("pid should be greater than zero");
+ return false;
+ }
+ return true;
+ }
+
+ void freezeProcess(int32_t pid) {
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000));
+ }
+
+ void unfreezeProcess(int32_t pid) {
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, false, 0));
+ }
+
+ void removeCallbackAndValidateNoEvent(sp<IBinder> binder,
+ sp<TestFrozenStateChangeCallback> callback) {
+ EXPECT_THAT(binder->removeFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ EXPECT_EQ(0u, callback->events.size());
+ }
+
sp<IBinder> m_server;
};
@@ -516,29 +604,18 @@
}
TEST_F(BinderLibTest, Freeze) {
- Parcel data, reply, replypid;
- std::ifstream freezer_file("/sys/fs/cgroup/uid_0/cgroup.freeze");
-
- // Pass test on devices where the cgroup v2 freezer is not supported
- if (freezer_file.fail()) {
- GTEST_SKIP();
+ if (!checkFreezeSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support proceess freezing";
return;
}
-
+ Parcel data, reply, replypid;
EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid), StatusEq(NO_ERROR));
int32_t pid = replypid.readInt32();
for (int i = 0; i < 10; i++) {
EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, data, &reply, TF_ONE_WAY));
}
- // Pass test on devices where BINDER_FREEZE ioctl is not supported
- int ret = IPCThreadState::self()->freeze(pid, false, 0);
- if (ret == -EINVAL) {
- GTEST_SKIP();
- return;
- }
- EXPECT_EQ(NO_ERROR, ret);
-
+ EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, false, 0));
EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
// b/268232063 - succeeds ~0.08% of the time
@@ -835,6 +912,199 @@
EXPECT_THAT(callback->getResult(), StatusEq(NO_ERROR));
}
+TEST_F(BinderLibTest, ReturnErrorIfKernelDoesNotSupportFreezeNotification) {
+ if (ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION)) {
+ GTEST_SKIP() << "Skipping test for kernels that support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ ASSERT_EQ(nullptr, binder->localBinder());
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(INVALID_OPERATION));
+}
+
+TEST_F(BinderLibTest, FrozenStateChangeNotificatiion) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback->ensureUnfrozenEventReceived();
+ // Check that the process hasn't died otherwise there's a risk of freezing
+ // the wrong process.
+ EXPECT_EQ(OK, binder->pingBinder());
+ freezeProcess(pid);
+ callback->ensureFrozenEventReceived();
+ unfreezeProcess(pid);
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+}
+
+TEST_F(BinderLibTest, AddFrozenCallbackWhenFrozen) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ // Check that the process hasn't died otherwise there's a risk of freezing
+ // the wrong process.
+ EXPECT_EQ(OK, binder->pingBinder());
+ freezeProcess(pid);
+ // Add the callback while the target process is frozen.
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ callback->ensureFrozenEventReceived();
+ unfreezeProcess(pid);
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+
+ // Check that the process hasn't died otherwise there's a risk of freezing
+ // the wrong process.
+ EXPECT_EQ(OK, binder->pingBinder());
+ freezeProcess(pid);
+ unfreezeProcess(pid);
+ // Make sure no callback happens since the listener has been removed.
+ EXPECT_EQ(0u, callback->events.size());
+}
+
+TEST_F(BinderLibTest, NoFrozenNotificationAfterCallbackRemoval) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+
+ // Make sure no callback happens after the listener is removed.
+ freezeProcess(pid);
+ unfreezeProcess(pid);
+ EXPECT_EQ(0u, callback->events.size());
+}
+
+TEST_F(BinderLibTest, MultipleFrozenStateChangeCallbacks) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback1 = sp<TestFrozenStateChangeCallback>::make();
+ sp<TestFrozenStateChangeCallback> callback2 = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback1), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback1->ensureUnfrozenEventReceived();
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback2), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback2->ensureUnfrozenEventReceived();
+
+ freezeProcess(pid);
+ callback1->ensureFrozenEventReceived();
+ callback2->ensureFrozenEventReceived();
+
+ removeCallbackAndValidateNoEvent(binder, callback1);
+ unfreezeProcess(pid);
+ EXPECT_EQ(0u, callback1->events.size());
+ callback2->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback2);
+
+ freezeProcess(pid);
+ EXPECT_EQ(0u, callback2->events.size());
+}
+
+TEST_F(BinderLibTest, RemoveThenAddFrozenStateChangeCallbacks) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ sp<IBinder> binder = addServer();
+ ASSERT_NE(nullptr, binder);
+ int32_t pid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ // Expect current state (unfrozen) to be delivered immediately.
+ callback->ensureUnfrozenEventReceived();
+ removeCallbackAndValidateNoEvent(binder, callback);
+
+ EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR));
+ callback->ensureUnfrozenEventReceived();
+}
+
+TEST_F(BinderLibTest, CoalesceFreezeCallbacksWhenListenerIsFrozen) {
+ if (!checkFreezeAndNotificationSupport()) {
+ GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION";
+ return;
+ }
+ sp<IBinder> binder = addServer();
+ sp<IBinder> listener = addServer();
+ ASSERT_NE(nullptr, binder);
+ ASSERT_NE(nullptr, listener);
+ int32_t pid, listenerPid;
+ ASSERT_TRUE(getBinderPid(&pid, binder));
+ ASSERT_TRUE(getBinderPid(&listenerPid, listener));
+
+ // Ask the listener process to register for state change callbacks.
+ {
+ Parcel data, reply;
+ data.writeStrongBinder(binder);
+ ASSERT_THAT(listener->transact(BINDER_LIB_TEST_LISTEN_FOR_FROZEN_STATE_CHANGE, data,
+ &reply),
+ StatusEq(NO_ERROR));
+ }
+ // Freeze the listener process.
+ freezeProcess(listenerPid);
+ createProcessGroup(getuid(), listenerPid);
+ ASSERT_TRUE(SetProcessProfiles(getuid(), listenerPid, {"Frozen"}));
+ // Repeatedly flip the target process between frozen and unfrozen states.
+ for (int i = 0; i < 1000; i++) {
+ usleep(50);
+ unfreezeProcess(pid);
+ usleep(50);
+ freezeProcess(pid);
+ }
+ // Unfreeze the listener process. Now it should receive the frozen state
+ // change notifications.
+ ASSERT_TRUE(SetProcessProfiles(getuid(), listenerPid, {"Unfrozen"}));
+ unfreezeProcess(listenerPid);
+ // Wait for 500ms to give the process enough time to wake up and handle
+ // notifications.
+ usleep(500 * 1000);
+ {
+ std::vector<bool> events;
+ Parcel data, reply;
+ ASSERT_THAT(listener->transact(BINDER_LIB_TEST_CONSUME_STATE_CHANGE_EVENTS, data, &reply),
+ StatusEq(NO_ERROR));
+ reply.readBoolVector(&events);
+ // There should only be one single state change notifications delievered.
+ ASSERT_EQ(1u, events.size());
+ EXPECT_TRUE(events[0]);
+ }
+}
+
TEST_F(BinderLibTest, PassFile) {
int ret;
int pipefd[2];
@@ -1981,6 +2251,26 @@
reply->writeInt32(param.sched_priority);
return NO_ERROR;
}
+ case BINDER_LIB_TEST_LISTEN_FOR_FROZEN_STATE_CHANGE: {
+ sp<IBinder> binder = data.readStrongBinder();
+ frozenStateChangeCallback = sp<TestFrozenStateChangeCallback>::make();
+ // Hold an strong pointer to the binder object so it doesn't go
+ // away.
+ frozenStateChangeCallback->binder = binder;
+ int ret = binder->addFrozenStateChangeCallback(frozenStateChangeCallback);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ auto event = frozenStateChangeCallback->events.popWithTimeout(10ms);
+ if (!event.has_value()) {
+ return NOT_ENOUGH_DATA;
+ }
+ return NO_ERROR;
+ }
+ case BINDER_LIB_TEST_CONSUME_STATE_CHANGE_EVENTS: {
+ reply->writeBoolVector(frozenStateChangeCallback->getAllAndClear());
+ return NO_ERROR;
+ }
case BINDER_LIB_TEST_ECHO_VECTOR: {
std::vector<uint64_t> vector;
auto err = data.readUint64Vector(&vector);
@@ -2067,6 +2357,7 @@
sp<IBinder> m_callback;
bool m_exitOnDestroy;
std::mutex m_blockMutex;
+ sp<TestFrozenStateChangeCallback> frozenStateChangeCallback;
};
int run_server(int index, int readypipefd, bool usePoll)
diff --git a/libs/binder/tests/binderRecordReplayTest.cpp b/libs/binder/tests/binderRecordReplayTest.cpp
index b975fad..f867b42 100644
--- a/libs/binder/tests/binderRecordReplayTest.cpp
+++ b/libs/binder/tests/binderRecordReplayTest.cpp
@@ -99,12 +99,12 @@
GENERATE_GETTER_SETTER(SingleDataParcelableArray, std::vector<SingleDataParcelable>);
Status setFileDescriptor(unique_fd input) {
- mFd = std::move(unique_fd(dup(input)));
+ mFd = unique_fd(dup(input));
return Status::ok();
}
Status getFileDescriptor(unique_fd* output) {
- *output = std::move(unique_fd(dup(mFd)));
+ *output = unique_fd(dup(mFd));
return Status::ok();
}
unique_fd mFd;
@@ -117,7 +117,7 @@
std::vector<uint8_t> buffer(fdStat.st_size);
auto readResult = android::base::ReadFully(fd, buffer.data(), fdStat.st_size);
EXPECT_TRUE(readResult != 0);
- return std::move(buffer);
+ return buffer;
}
void replayFuzzService(const sp<BpBinder>& binder, const RecordedTransaction& transaction) {
@@ -387,8 +387,8 @@
// When fds are replayed, it will be replaced by /dev/null..reading from it should yield
// null data
- recordReplay(&IBinderRecordReplayTest::setFileDescriptor, std::move(unique_fd(dup(saved))),
- &IBinderRecordReplayTest::getFileDescriptor, std::move(unique_fd(dup(changed))));
+ recordReplay(&IBinderRecordReplayTest::setFileDescriptor, unique_fd(dup(saved)),
+ &IBinderRecordReplayTest::getFileDescriptor, unique_fd(dup(changed)));
}
int main(int argc, char** argv) {
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 3efd84f..3038de9 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -19,6 +19,12 @@
#include <aidl/IBinderRpcTest.h>
#endif
+#if defined(__LP64__)
+#define TEST_FILE_SUFFIX "64"
+#else
+#define TEST_FILE_SUFFIX "32"
+#endif
+
#include <chrono>
#include <cstdlib>
#include <iostream>
@@ -259,7 +265,8 @@
std::string path = GetExecutableDirectory();
auto servicePath = path + "/binder_rpc_test_service" +
- (singleThreaded ? "_single_threaded" : "") + (noKernel ? "_no_kernel" : "");
+ (singleThreaded ? "_single_threaded" : "") + (noKernel ? "_no_kernel" : "") +
+ TEST_FILE_SUFFIX;
unique_fd bootstrapClientFd, socketFd;
@@ -1377,8 +1384,8 @@
sp<IServiceManager> sm = defaultServiceManager();
ASSERT_NE(nullptr, sm);
// Any Java service with non-empty getInterfaceDescriptor() would do.
- // Let's pick batteryproperties.
- auto binder = sm->checkService(String16("batteryproperties"));
+ // Let's pick activity.
+ auto binder = sm->checkService(String16("activity"));
ASSERT_NE(nullptr, binder);
auto descriptor = binder->getInterfaceDescriptor();
ASSERT_GE(descriptor.size(), 0u);
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index f480780..c6fd487 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -301,7 +301,8 @@
auto proc = createRpcTestSocketServerProcess({});
- sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
+ sp<IBinder> someRealBinder = defaultServiceManager()->getService(String16("activity"));
+ ASSERT_NE(someRealBinder, nullptr);
sp<IBinder> outBinder;
EXPECT_EQ(INVALID_OPERATION,
proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError());
@@ -323,6 +324,22 @@
// END TESTS FOR LIMITATIONS OF SOCKET BINDER
+class TestFrozenStateChangeCallback : public IBinder::FrozenStateChangeCallback {
+public:
+ virtual void onStateChanged(const wp<IBinder>&, State) {}
+};
+
+TEST_P(BinderRpc, RpcBinderShouldFailOnFrozenStateCallbacks) {
+ auto proc = createRpcTestSocketServerProcess({});
+
+ sp<IBinder> a;
+ sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make();
+ EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a));
+ EXPECT_DEATH_IF_SUPPORTED(
+ { std::ignore = a->addFrozenStateChangeCallback(callback); },
+ "addFrozenStateChangeCallback\\(\\) is not supported for RPC Binder.");
+}
+
TEST_P(BinderRpc, RepeatRootObject) {
auto proc = createRpcTestSocketServerProcess({});
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp
index fd9777a..0ed8a55 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp
@@ -55,7 +55,7 @@
offset += CHAR_BIT;
}
- return std::move(reverseData);
+ return reverseData;
}
template <typename T>
diff --git a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferDemosAppBar.kt b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferDemosAppBar.kt
index ff3ae5a..4f293b9 100644
--- a/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferDemosAppBar.kt
+++ b/libs/bufferstreams/examples/app/java/com/android/graphics/bufferstreamsdemoapp/BufferDemosAppBar.kt
@@ -1,7 +1,5 @@
package com.android.graphics.bufferstreamsdemoapp
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@@ -29,10 +27,11 @@
navigationIcon = {
if (canNavigateBack) {
IconButton(onClick = navigateUp) {
- Icon(
- imageVector = Icons.AutoMirrored.Filled.ArrowBack,
- contentDescription = stringResource(R.string.back_button)
- )
+ // b/355293776
+ // Icon(
+ // imageVector = Icons.AutoMirrored.Filled.ArrowBack,
+ // contentDescription = stringResource(R.string.back_button)
+ // )
}
}
}
diff --git a/libs/bufferstreams/rust/src/lib.rs b/libs/bufferstreams/rust/src/lib.rs
index 17d4d87..9c48b49 100644
--- a/libs/bufferstreams/rust/src/lib.rs
+++ b/libs/bufferstreams/rust/src/lib.rs
@@ -37,23 +37,23 @@
/// BufferPublishers are required to adhere to the following, based on the
/// reactive streams specification:
/// * The total number of on_next´s signalled by a Publisher to a Subscriber
-/// MUST be less than or equal to the total number of elements requested by that
-/// Subscriber´s Subscription at all times.
+/// MUST be less than or equal to the total number of elements requested by that
+/// Subscriber´s Subscription at all times.
/// * A Publisher MAY signal fewer on_next than requested and terminate the
-/// Subscription by calling on_complete or on_error.
+/// Subscription by calling on_complete or on_error.
/// * on_subscribe, on_next, on_error and on_complete signaled to a Subscriber
-/// MUST be signaled serially.
+/// MUST be signaled serially.
/// * If a Publisher fails it MUST signal an on_error.
/// * If a Publisher terminates successfully (finite stream) it MUST signal an
-/// on_complete.
+/// on_complete.
/// * If a Publisher signals either on_error or on_complete on a Subscriber,
-/// that Subscriber’s Subscription MUST be considered cancelled.
+/// that Subscriber’s Subscription MUST be considered cancelled.
/// * Once a terminal state has been signaled (on_error, on_complete) it is
-/// REQUIRED that no further signals occur.
+/// REQUIRED that no further signals occur.
/// * If a Subscription is cancelled its Subscriber MUST eventually stop being
-/// signaled.
+/// signaled.
/// * A Publisher MAY support multiple Subscribers and decides whether each
-/// Subscription is unicast or multicast.
+/// Subscription is unicast or multicast.
pub trait BufferPublisher {
/// Returns the StreamConfig of buffers that publisher creates.
fn get_publisher_stream_config(&self) -> StreamConfig;
@@ -69,25 +69,25 @@
/// BufferSubcribers are required to adhere to the following, based on the
/// reactive streams specification:
/// * The total number of on_next´s signalled by a Publisher to a Subscriber
-/// MUST be less than or equal to the total number of elements requested by that
-/// Subscriber´s Subscription at all times.
+/// MUST be less than or equal to the total number of elements requested by that
+/// Subscriber´s Subscription at all times.
/// * A Publisher MAY signal fewer on_next than requested and terminate the
-/// Subscription by calling on_complete or on_error.
+/// Subscription by calling on_complete or on_error.
/// * on_subscribe, on_next, on_error and on_complete signaled to a Subscriber
-/// MUST be signaled serially.
+/// MUST be signaled serially.
/// * If a Publisher fails it MUST signal an on_error.
/// * If a Publisher terminates successfully (finite stream) it MUST signal an
-/// on_complete.
+/// on_complete.
/// * If a Publisher signals either on_error or on_complete on a Subscriber,
-/// that Subscriber’s Subscription MUST be considered cancelled.
+/// that Subscriber’s Subscription MUST be considered cancelled.
/// * Once a terminal state has been signaled (on_error, on_complete) it is
-/// REQUIRED that no further signals occur.
+/// REQUIRED that no further signals occur.
/// * If a Subscription is cancelled its Subscriber MUST eventually stop being
-/// signaled.
+/// signaled.
/// * Publisher.subscribe MAY be called as many times as wanted but MUST be
-/// with a different Subscriber each time.
+/// with a different Subscriber each time.
/// * A Publisher MAY support multiple Subscribers and decides whether each
-/// Subscription is unicast or multicast.
+/// Subscription is unicast or multicast.
pub trait BufferSubscriber {
/// The StreamConfig of buffers that this subscriber expects.
fn get_subscriber_stream_config(&self) -> StreamConfig;
@@ -111,39 +111,39 @@
/// BufferSubcriptions are required to adhere to the following, based on the
/// reactive streams specification:
/// * Subscription.request and Subscription.cancel MUST only be called inside
-/// of its Subscriber context.
+/// of its Subscriber context.
/// * The Subscription MUST allow the Subscriber to call Subscription.request
-/// synchronously from within on_next or on_subscribe.
+/// synchronously from within on_next or on_subscribe.
/// * Subscription.request MUST place an upper bound on possible synchronous
-/// recursion between Publisher and Subscriber.
+/// recursion between Publisher and Subscriber.
/// * Subscription.request SHOULD respect the responsivity of its caller by
-/// returning in a timely manner.
+/// returning in a timely manner.
/// * Subscription.cancel MUST respect the responsivity of its caller by
-/// returning in a timely manner, MUST be idempotent and MUST be thread-safe.
+/// returning in a timely manner, MUST be idempotent and MUST be thread-safe.
/// * After the Subscription is cancelled, additional
-/// Subscription.request(n: u64) MUST be NOPs.
+/// Subscription.request(n: u64) MUST be NOPs.
/// * After the Subscription is cancelled, additional Subscription.cancel()
-/// MUST be NOPs.
+/// MUST be NOPs.
/// * While the Subscription is not cancelled, Subscription.request(n: u64)
-/// MUST register the given number of additional elements to be produced to the
-/// respective subscriber.
+/// MUST register the given number of additional elements to be produced to the
+/// respective subscriber.
/// * While the Subscription is not cancelled, Subscription.request(n: u64)
-/// MUST signal on_error if the argument is <= 0. The cause message SHOULD
-/// explain that non-positive request signals are illegal.
+/// MUST signal on_error if the argument is <= 0. The cause message SHOULD
+/// explain that non-positive request signals are illegal.
/// * While the Subscription is not cancelled, Subscription.request(n: u64)
-/// MAY synchronously call on_next on this (or other) subscriber(s).
+/// MAY synchronously call on_next on this (or other) subscriber(s).
/// * While the Subscription is not cancelled, Subscription.request(n: u64)
-/// MAY synchronously call on_complete or on_error on this (or other)
-/// subscriber(s).
+/// MAY synchronously call on_complete or on_error on this (or other)
+/// subscriber(s).
/// * While the Subscription is not cancelled, Subscription.cancel() MUST
-/// request the Publisher to eventually stop signaling its Subscriber. The
-/// operation is NOT REQUIRED to affect the Subscription immediately.
+/// request the Publisher to eventually stop signaling its Subscriber. The
+/// operation is NOT REQUIRED to affect the Subscription immediately.
/// * While the Subscription is not cancelled, Subscription.cancel() MUST
-/// request the Publisher to eventually drop any references to the corresponding
-/// subscriber.
+/// request the Publisher to eventually drop any references to the corresponding
+/// subscriber.
/// * While the Subscription is not cancelled, calling Subscription.cancel MAY
-/// cause the Publisher, if stateful, to transition into the shut-down state if
-/// no other Subscription exists at this point.
+/// cause the Publisher, if stateful, to transition into the shut-down state if
+/// no other Subscription exists at this point.
/// * Calling Subscription.cancel MUST return normally.
/// * Calling Subscription.request MUST return normally.
pub trait BufferSubscription: Send + Sync + 'static {
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index 81f6a58..44cdc02 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -41,7 +41,7 @@
static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365;
// Declare busy loop variable globally to prevent removal during optimization
-static long sum __attribute__((used)) = 0;
+static volatile long sum __attribute__((used)) = 1;
using std::vector;
@@ -579,8 +579,8 @@
// Keeps CPU busy with some number crunching
void useCpu() {
- sum = 0;
- for (int i = 0; i < 100000; i++) {
+ sum = 1;
+ for (int i = 1; i < 100000; i++) {
sum *= i;
}
}
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 52383ac..4c3f4a6 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -83,6 +83,55 @@
static const char* kLlndkLibrariesTxtPath = "/system/etc/llndk.libraries.txt";
+// List of libraries that were previously available via VNDK-SP,
+// and are now available via SPHAL.
+// On modern devices that lack the VNDK APEX, the device no longer
+// contains a helpful list of these libraries on the filesystem as above.
+// See system/sepolicy/vendor/file_contexts
+static const char* kFormerlyVndkspLibrariesList =
+ "android.hardware.common-V2-ndk.so:"
+ "android.hardware.common.fmq-V1-ndk.so:"
+ "android.hardware.graphics.allocator-V2-ndk.so:"
+ "android.hardware.graphics.common-V5-ndk.so:"
+ "android.hardware.graphics.common@1.0.so:"
+ "android.hardware.graphics.common@1.1.so:"
+ "android.hardware.graphics.common@1.2.so:"
+ "android.hardware.graphics.composer3-V1-ndk.so:"
+ "android.hardware.graphics.mapper@2.0.so:"
+ "android.hardware.graphics.mapper@2.1.so:"
+ "android.hardware.graphics.mapper@3.0.so:"
+ "android.hardware.graphics.mapper@4.0.so:"
+ "android.hardware.renderscript@1.0.so:"
+ "android.hidl.memory.token@1.0.so:"
+ "android.hidl.memory@1.0-impl.so:"
+ "android.hidl.memory@1.0.so:"
+ "android.hidl.safe_union@1.0.so:"
+ "libRSCpuRef.so:"
+ "libRSDriver.so:"
+ "libRS_internal.so:"
+ "libbacktrace.so:"
+ "libbase.so:"
+ "libbcinfo.so:"
+ "libblas.so:"
+ "libc++.so:"
+ "libcompiler_rt.so:"
+ "libcutils.so:"
+ "libdmabufheap.so:"
+ "libft2.so:"
+ "libgralloctypes.so:"
+ "libhardware.so:"
+ "libhidlbase.so:"
+ "libhidlmemory.so:"
+ "libion.so:"
+ "libjsoncpp.so:"
+ "liblzma.so:"
+ "libpng.so:"
+ "libprocessgroup.so:"
+ "libunwindstack.so:"
+ "libutils.so:"
+ "libutilscallstack.so:"
+ "libz.so";
+
static std::string vndkVersionStr() {
#ifdef __BIONIC__
return base::GetProperty("ro.vndk.version", "");
@@ -122,8 +171,12 @@
static const std::string getSystemNativeLibraries(NativeLibrary type) {
std::string nativeLibrariesSystemConfig = "";
- if (!isVndkEnabled() && type == NativeLibrary::LLNDK) {
- nativeLibrariesSystemConfig = kLlndkLibrariesTxtPath;
+ if (!isVndkEnabled()) {
+ if (type == NativeLibrary::VNDKSP) {
+ return kFormerlyVndkspLibrariesList;
+ } else {
+ nativeLibrariesSystemConfig = kLlndkLibrariesTxtPath;
+ }
} else {
nativeLibrariesSystemConfig = kNativeLibrariesSystemConfigPath[type];
insertVndkVersionStr(&nativeLibrariesSystemConfig);
@@ -263,7 +316,7 @@
ALOGI("Driver path is setup via UPDATABLE_GFX_DRIVER: %s", mDriverPath.c_str());
}
- auto vndkNamespace = android_get_exported_namespace("vndk");
+ auto vndkNamespace = android_get_exported_namespace(isVndkEnabled() ? "vndk" : "sphal");
if (!vndkNamespace) {
mDriverNamespace = nullptr;
return mDriverNamespace;
@@ -631,7 +684,7 @@
return mAngleNamespace;
}
- auto vndkNamespace = android_get_exported_namespace("vndk");
+ auto vndkNamespace = android_get_exported_namespace(isVndkEnabled() ? "vndk" : "sphal");
if (!vndkNamespace) {
mAngleNamespace = nullptr;
return mAngleNamespace;
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index 23f583b..7f45581 100644
--- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -125,6 +125,11 @@
VULKAN_DEVICE_EXTENSION = 9,
};
+ enum GLTelemetryHints {
+ NO_HINT = 0,
+ SKIP_TELEMETRY = 1,
+ };
+
GpuStatsInfo() = default;
GpuStatsInfo(const GpuStatsInfo&) = default;
virtual ~GpuStatsInfo() = default;
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 51d2e53..1243b21 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -255,6 +255,7 @@
"BitTube.cpp",
"BLASTBufferQueue.cpp",
"BufferItemConsumer.cpp",
+ "BufferReleaseChannel.cpp",
"Choreographer.cpp",
"CompositorTiming.cpp",
"ConsumerBase.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 739c3c2..f065ffa 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -20,6 +20,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
+#include <com_android_graphics_libgui_flags.h>
#include <cutils/atomic.h>
#include <gui/BLASTBufferQueue.h>
#include <gui/BufferItemConsumer.h>
@@ -39,7 +40,6 @@
#include <private/gui/ComposerServiceAIDL.h>
#include <android-base/thread_annotations.h>
-#include <chrono>
#include <com_android_graphics_libgui_flags.h>
@@ -175,16 +175,21 @@
mSyncTransaction(nullptr),
mUpdateDestinationFrame(updateDestinationFrame) {
createBufferQueue(&mProducer, &mConsumer);
- // since the adapter is in the client process, set dequeue timeout
- // explicitly so that dequeueBuffer will block
- mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());
-
- // safe default, most producers are expected to override this
- mProducer->setMaxDequeuedBufferCount(2);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mBufferItemConsumer = new BLASTBufferItemConsumer(mProducer, mConsumer,
+ GraphicBuffer::USAGE_HW_COMPOSER |
+ GraphicBuffer::USAGE_HW_TEXTURE,
+ 1, false, this);
+#else
mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
GraphicBuffer::USAGE_HW_COMPOSER |
GraphicBuffer::USAGE_HW_TEXTURE,
1, false, this);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // since the adapter is in the client process, set dequeue timeout
+ // explicitly so that dequeueBuffer will block
+ mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());
+
static std::atomic<uint32_t> nextId = 0;
mProducerId = nextId++;
mName = name + "#" + std::to_string(mProducerId);
@@ -236,6 +241,11 @@
}
}
+void BLASTBufferQueue::onFirstRef() {
+ // safe default, most producers are expected to override this
+ mProducer->setMaxDequeuedBufferCount(2);
+}
+
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,
int32_t format) {
LOG_ALWAYS_FATAL_IF(surface == nullptr, "BLASTBufferQueue: mSurfaceControl must not be NULL");
@@ -499,7 +509,13 @@
callbackId.to_string().c_str());
return;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ if (!it->second.disconnectedAfterAcquired) {
+ mNumAcquired--;
+ }
+#else
mNumAcquired--;
+#endif
BBQ_TRACE("frame=%" PRIu64, callbackId.framenumber);
BQA_LOGV("released %s", callbackId.to_string().c_str());
mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
@@ -550,7 +566,7 @@
applyTransaction = false;
}
- BufferItem bufferItem;
+ BLASTBufferItem bufferItem;
status_t status =
mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false);
@@ -761,6 +777,9 @@
}
// add to shadow queue
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ mNumDequeued--;
+#endif
mNumFrameAvailable++;
if (waitForTransactionCallback && mNumFrameAvailable >= 2) {
acquireAndReleaseBuffer();
@@ -815,9 +834,18 @@
};
void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) {
- std::lock_guard _lock{mTimestampMutex};
- mDequeueTimestamps.erase(bufferId);
-};
+ {
+ std::lock_guard _lock{mTimestampMutex};
+ mDequeueTimestamps.erase(bufferId);
+ }
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ {
+ std::lock_guard lock{mMutex};
+ mNumDequeued--;
+ }
+#endif
+}
bool BLASTBufferQueue::syncNextTransaction(
std::function<void(SurfaceComposerClient::Transaction*)> callback,
@@ -1102,9 +1130,9 @@
// can be non-blocking when the producer is in the client process.
class BBQBufferQueueProducer : public BufferQueueProducer {
public:
- BBQBufferQueueProducer(const sp<BufferQueueCore>& core, wp<BLASTBufferQueue> bbq)
+ BBQBufferQueueProducer(const sp<BufferQueueCore>& core, const wp<BLASTBufferQueue>& bbq)
: BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/),
- mBLASTBufferQueue(std::move(bbq)) {}
+ mBLASTBufferQueue(bbq) {}
status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp,
QueueBufferOutput* output) override {
@@ -1116,30 +1144,152 @@
producerControlledByApp, output);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ status_t disconnect(int api, DisconnectMode mode) override {
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (!bbq) {
+ return BufferQueueProducer::disconnect(api, mode);
+ }
+
+ std::lock_guard lock{bbq->mMutex};
+ if (status_t status = BufferQueueProducer::disconnect(api, mode); status != OK) {
+ return status;
+ }
+
+ // We need to reset dequeued and acquired counts because BufferQueueProducer::disconnect
+ // calls BufferQueueCore::freeAllBuffersLocked which frees all dequeued and acquired
+ // buffers. We don't reset mNumFrameAvailable because these buffers are still available
+ // in BufferItemConsumer.
+ bbq->mNumDequeued = 0;
+ bbq->mNumAcquired = 0;
+ // SurfaceFlinger sends release callbacks for buffers that have been acquired after a
+ // disconnect. We set disconnectedAfterAcquired to true so that we can ignore any stale
+ // releases that come in after the producer is disconnected. Otherwise, releaseBuffer will
+ // decrement mNumAcquired for a buffer that was acquired before we reset mNumAcquired to
+ // zero.
+ for (auto& [releaseId, bufferItem] : bbq->mSubmitted) {
+ bufferItem.disconnectedAfterAcquired = true;
+ }
+
+ return OK;
+ }
+
+ status_t setAsyncMode(bool asyncMode) override {
+ if (status_t status = BufferQueueProducer::setAsyncMode(asyncMode); status != OK) {
+ return status;
+ }
+
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (!bbq) {
+ return OK;
+ }
+
+ {
+ std::lock_guard lock{bbq->mMutex};
+ bbq->mAsyncMode = asyncMode;
+ }
+
+ return OK;
+ }
+
+ status_t setSharedBufferMode(bool sharedBufferMode) override {
+ if (status_t status = BufferQueueProducer::setSharedBufferMode(sharedBufferMode);
+ status != OK) {
+ return status;
+ }
+
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (!bbq) {
+ return OK;
+ }
+
+ {
+ std::lock_guard lock{bbq->mMutex};
+ bbq->mSharedBufferMode = sharedBufferMode;
+ }
+
+ return OK;
+ }
+
+ status_t detachBuffer(int slot) override {
+ if (status_t status = BufferQueueProducer::detachBuffer(slot); status != OK) {
+ return status;
+ }
+
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (!bbq) {
+ return OK;
+ }
+
+ {
+ std::lock_guard lock{bbq->mMutex};
+ bbq->mNumDequeued--;
+ }
+
+ return OK;
+ }
+
+ status_t dequeueBuffer(int* outSlot, sp<Fence>* outFence, uint32_t width, uint32_t height,
+ PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
+ FrameEventHistoryDelta* outTimestamps) override {
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (!bbq) {
+ return BufferQueueProducer::dequeueBuffer(outSlot, outFence, width, height, format,
+ usage, outBufferAge, outTimestamps);
+ }
+
+ {
+ std::lock_guard lock{bbq->mMutex};
+ bbq->mNumDequeued++;
+ }
+
+ status_t status =
+ BufferQueueProducer::dequeueBuffer(outSlot, outFence, width, height, format, usage,
+ outBufferAge, outTimestamps);
+ if (status < 0) {
+ std::lock_guard lock{bbq->mMutex};
+ bbq->mNumDequeued--;
+ }
+ return status;
+ }
+#endif
+
// We want to resize the frame history when changing the size of the buffer queue
status_t setMaxDequeuedBufferCount(int maxDequeuedBufferCount) override {
int maxBufferCount;
- status_t status = BufferQueueProducer::setMaxDequeuedBufferCount(maxDequeuedBufferCount,
- &maxBufferCount);
- // if we can't determine the max buffer count, then just skip growing the history size
- if (status == OK) {
- size_t newFrameHistorySize = maxBufferCount + 2; // +2 because triple buffer rendering
- // optimize away resizing the frame history unless it will grow
- if (newFrameHistorySize > FrameEventHistory::INITIAL_MAX_FRAME_HISTORY) {
- sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
- if (bbq != nullptr) {
- ALOGV("increasing frame history size to %zu", newFrameHistorySize);
- bbq->resizeFrameEventHistory(newFrameHistorySize);
- }
- }
+ if (status_t status = BufferQueueProducer::setMaxDequeuedBufferCount(maxDequeuedBufferCount,
+ &maxBufferCount);
+ status != OK) {
+ return status;
}
- return status;
+
+ sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote();
+ if (!bbq) {
+ return OK;
+ }
+
+ // if we can't determine the max buffer count, then just skip growing the history size
+ size_t newFrameHistorySize = maxBufferCount + 2; // +2 because triple buffer rendering
+ // optimize away resizing the frame history unless it will grow
+ if (newFrameHistorySize > FrameEventHistory::INITIAL_MAX_FRAME_HISTORY) {
+ ALOGV("increasing frame history size to %zu", newFrameHistorySize);
+ bbq->resizeFrameEventHistory(newFrameHistorySize);
+ }
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ {
+ std::lock_guard lock{bbq->mMutex};
+ bbq->mMaxDequeuedBuffers = maxDequeuedBufferCount;
+ }
+#endif
+
+ return OK;
}
int query(int what, int* value) override {
if (what == NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER) {
*value = 1;
- return NO_ERROR;
+ return OK;
}
return BufferQueueProducer::query(what, value);
}
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp
index e6331e7..bfe3d6e 100644
--- a/libs/gui/BufferItemConsumer.cpp
+++ b/libs/gui/BufferItemConsumer.cpp
@@ -21,8 +21,11 @@
#include <inttypes.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
+#include <ui/BufferQueueDefs.h>
+#include <ui/GraphicBuffer.h>
#define BI_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
// #define BI_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__)
@@ -32,18 +35,37 @@
namespace android {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+BufferItemConsumer::BufferItemConsumer(uint64_t consumerUsage, int bufferCount,
+ bool controlledByApp, bool isConsumerSurfaceFlinger)
+ : ConsumerBase(controlledByApp, isConsumerSurfaceFlinger) {
+ initialize(consumerUsage, bufferCount);
+}
+
+BufferItemConsumer::BufferItemConsumer(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer,
+ uint64_t consumerUsage, int bufferCount,
+ bool controlledByApp)
+ : ConsumerBase(producer, consumer, controlledByApp) {
+ initialize(consumerUsage, bufferCount);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
BufferItemConsumer::BufferItemConsumer(
const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
int bufferCount, bool controlledByApp) :
ConsumerBase(consumer, controlledByApp)
{
+ initialize(consumerUsage, bufferCount);
+}
+
+void BufferItemConsumer::initialize(uint64_t consumerUsage, int bufferCount) {
status_t err = mConsumer->setConsumerUsageBits(consumerUsage);
- LOG_ALWAYS_FATAL_IF(err != OK,
- "Failed to set consumer usage bits to %#" PRIx64, consumerUsage);
+ LOG_ALWAYS_FATAL_IF(err != OK, "Failed to set consumer usage bits to %#" PRIx64, consumerUsage);
if (bufferCount != DEFAULT_MAX_BUFFERS) {
err = mConsumer->setMaxAcquiredBufferCount(bufferCount);
- LOG_ALWAYS_FATAL_IF(err != OK,
- "Failed to set max acquired buffer count to %d", bufferCount);
+ LOG_ALWAYS_FATAL_IF(err != OK, "Failed to set max acquired buffer count to %d",
+ bufferCount);
}
}
@@ -87,17 +109,38 @@
status_t BufferItemConsumer::releaseBuffer(const BufferItem &item,
const sp<Fence>& releaseFence) {
- status_t err;
+ Mutex::Autolock _l(mMutex);
+ return releaseBufferSlotLocked(item.mSlot, item.mGraphicBuffer, releaseFence);
+}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+status_t BufferItemConsumer::releaseBuffer(const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& releaseFence) {
Mutex::Autolock _l(mMutex);
- err = addReleaseFenceLocked(item.mSlot, item.mGraphicBuffer, releaseFence);
+ if (buffer == nullptr) {
+ return BAD_VALUE;
+ }
+
+ int slotIndex = getSlotForBufferLocked(buffer);
+ if (slotIndex == INVALID_BUFFER_SLOT) {
+ return BAD_VALUE;
+ }
+
+ return releaseBufferSlotLocked(slotIndex, buffer, releaseFence);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
+status_t BufferItemConsumer::releaseBufferSlotLocked(int slotIndex, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& releaseFence) {
+ status_t err;
+
+ err = addReleaseFenceLocked(slotIndex, buffer, releaseFence);
if (err != OK) {
BI_LOGE("Failed to addReleaseFenceLocked");
}
- err = releaseBufferLocked(item.mSlot, item.mGraphicBuffer, EGL_NO_DISPLAY,
- EGL_NO_SYNC_KHR);
+ err = releaseBufferLocked(slotIndex, buffer, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
if (err != OK && err != IGraphicBufferConsumer::STALE_BUFFER_SLOT) {
BI_LOGE("Failed to release buffer: %s (%d)",
strerror(-err), err);
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 11f5174..69d25be 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -42,6 +42,8 @@
#include <system/window.h>
+#include <com_android_graphics_libgui_flags.h>
+
namespace android {
// Macros for include BufferQueueCore information in log messages
@@ -370,79 +372,94 @@
return BAD_VALUE;
}
- std::lock_guard<std::mutex> lock(mCore->mMutex);
+ sp<IProducerListener> listener;
+ {
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
- if (mCore->mSharedBufferMode) {
- BQ_LOGE("attachBuffer: cannot attach a buffer in shared buffer mode");
- return BAD_VALUE;
- }
-
- // Make sure we don't have too many acquired buffers
- int numAcquiredBuffers = 0;
- for (int s : mCore->mActiveBuffers) {
- if (mSlots[s].mBufferState.isAcquired()) {
- ++numAcquiredBuffers;
+ if (mCore->mSharedBufferMode) {
+ BQ_LOGE("attachBuffer: cannot attach a buffer in shared buffer mode");
+ return BAD_VALUE;
}
+
+ // Make sure we don't have too many acquired buffers
+ int numAcquiredBuffers = 0;
+ for (int s : mCore->mActiveBuffers) {
+ if (mSlots[s].mBufferState.isAcquired()) {
+ ++numAcquiredBuffers;
+ }
+ }
+
+ if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
+ BQ_LOGE("attachBuffer: max acquired buffer count reached: %d "
+ "(max %d)", numAcquiredBuffers,
+ mCore->mMaxAcquiredBufferCount);
+ return INVALID_OPERATION;
+ }
+
+ if (buffer->getGenerationNumber() != mCore->mGenerationNumber) {
+ BQ_LOGE("attachBuffer: generation number mismatch [buffer %u] "
+ "[queue %u]", buffer->getGenerationNumber(),
+ mCore->mGenerationNumber);
+ return BAD_VALUE;
+ }
+
+ // Find a free slot to put the buffer into
+ int found = BufferQueueCore::INVALID_BUFFER_SLOT;
+ if (!mCore->mFreeSlots.empty()) {
+ auto slot = mCore->mFreeSlots.begin();
+ found = *slot;
+ mCore->mFreeSlots.erase(slot);
+ } else if (!mCore->mFreeBuffers.empty()) {
+ found = mCore->mFreeBuffers.front();
+ mCore->mFreeBuffers.remove(found);
+ }
+ if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
+ BQ_LOGE("attachBuffer: could not find free buffer slot");
+ return NO_MEMORY;
+ }
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+ if (mCore->mBufferAttachedCbEnabled) {
+ listener = mCore->mConnectedProducerListener;
+ }
+#endif
+
+ mCore->mActiveBuffers.insert(found);
+ *outSlot = found;
+ ATRACE_BUFFER_INDEX(*outSlot);
+ BQ_LOGV("attachBuffer: returning slot %d", *outSlot);
+
+ mSlots[*outSlot].mGraphicBuffer = buffer;
+ mSlots[*outSlot].mBufferState.attachConsumer();
+ mSlots[*outSlot].mNeedsReallocation = true;
+ mSlots[*outSlot].mFence = Fence::NO_FENCE;
+ mSlots[*outSlot].mFrameNumber = 0;
+
+ // mAcquireCalled tells BufferQueue that it doesn't need to send a valid
+ // GraphicBuffer pointer on the next acquireBuffer call, which decreases
+ // Binder traffic by not un/flattening the GraphicBuffer. However, it
+ // requires that the consumer maintain a cached copy of the slot <--> buffer
+ // mappings, which is why the consumer doesn't need the valid pointer on
+ // acquire.
+ //
+ // The StreamSplitter is one of the primary users of the attach/detach
+ // logic, and while it is running, all buffers it acquires are immediately
+ // detached, and all buffers it eventually releases are ones that were
+ // attached (as opposed to having been obtained from acquireBuffer), so it
+ // doesn't make sense to maintain the slot/buffer mappings, which would
+ // become invalid for every buffer during detach/attach. By setting this to
+ // false, the valid GraphicBuffer pointer will always be sent with acquire
+ // for attached buffers.
+ mSlots[*outSlot].mAcquireCalled = false;
+
+ VALIDATE_CONSISTENCY();
}
- if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
- BQ_LOGE("attachBuffer: max acquired buffer count reached: %d "
- "(max %d)", numAcquiredBuffers,
- mCore->mMaxAcquiredBufferCount);
- return INVALID_OPERATION;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+ if (listener != nullptr) {
+ listener->onBufferAttached();
}
-
- if (buffer->getGenerationNumber() != mCore->mGenerationNumber) {
- BQ_LOGE("attachBuffer: generation number mismatch [buffer %u] "
- "[queue %u]", buffer->getGenerationNumber(),
- mCore->mGenerationNumber);
- return BAD_VALUE;
- }
-
- // Find a free slot to put the buffer into
- int found = BufferQueueCore::INVALID_BUFFER_SLOT;
- if (!mCore->mFreeSlots.empty()) {
- auto slot = mCore->mFreeSlots.begin();
- found = *slot;
- mCore->mFreeSlots.erase(slot);
- } else if (!mCore->mFreeBuffers.empty()) {
- found = mCore->mFreeBuffers.front();
- mCore->mFreeBuffers.remove(found);
- }
- if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
- BQ_LOGE("attachBuffer: could not find free buffer slot");
- return NO_MEMORY;
- }
-
- mCore->mActiveBuffers.insert(found);
- *outSlot = found;
- ATRACE_BUFFER_INDEX(*outSlot);
- BQ_LOGV("attachBuffer: returning slot %d", *outSlot);
-
- mSlots[*outSlot].mGraphicBuffer = buffer;
- mSlots[*outSlot].mBufferState.attachConsumer();
- mSlots[*outSlot].mNeedsReallocation = true;
- mSlots[*outSlot].mFence = Fence::NO_FENCE;
- mSlots[*outSlot].mFrameNumber = 0;
-
- // mAcquireCalled tells BufferQueue that it doesn't need to send a valid
- // GraphicBuffer pointer on the next acquireBuffer call, which decreases
- // Binder traffic by not un/flattening the GraphicBuffer. However, it
- // requires that the consumer maintain a cached copy of the slot <--> buffer
- // mappings, which is why the consumer doesn't need the valid pointer on
- // acquire.
- //
- // The StreamSplitter is one of the primary users of the attach/detach
- // logic, and while it is running, all buffers it acquires are immediately
- // detached, and all buffers it eventually releases are ones that were
- // attached (as opposed to having been obtained from acquireBuffer), so it
- // doesn't make sense to maintain the slot/buffer mappings, which would
- // become invalid for every buffer during detach/attach. By setting this to
- // false, the valid GraphicBuffer pointer will always be sent with acquire
- // for attached buffers.
- mSlots[*outSlot].mAcquireCalled = false;
-
- VALIDATE_CONSISTENCY();
+#endif
return NO_ERROR;
}
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 648db67..e0c5b1f 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -96,6 +96,7 @@
mLinkedToDeath(),
mConnectedProducerListener(),
mBufferReleasedCbEnabled(false),
+ mBufferAttachedCbEnabled(false),
mSlots(),
mQueue(),
mFreeSlots(),
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 69345a9..a4d105d 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -1360,6 +1360,9 @@
#endif
mCore->mConnectedProducerListener = listener;
mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify();
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+ mCore->mBufferAttachedCbEnabled = listener->needsAttachNotify();
+#endif
}
break;
default:
diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp
new file mode 100644
index 0000000..27367aa
--- /dev/null
+++ b/libs/gui/BufferReleaseChannel.cpp
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#define LOG_TAG "BufferReleaseChannel"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <android-base/result.h>
+#include <android/binder_status.h>
+#include <binder/Parcel.h>
+#include <utils/Flattenable.h>
+
+#include <gui/BufferReleaseChannel.h>
+#include <private/gui/ParcelUtils.h>
+
+using android::base::Result;
+
+namespace android::gui {
+
+namespace {
+
+template <typename T>
+static void readAligned(const void*& buffer, size_t& size, T& value) {
+ size -= FlattenableUtils::align<alignof(T)>(buffer);
+ FlattenableUtils::read(buffer, size, value);
+}
+
+template <typename T>
+static void writeAligned(void*& buffer, size_t& size, T value) {
+ size -= FlattenableUtils::align<alignof(T)>(buffer);
+ FlattenableUtils::write(buffer, size, value);
+}
+
+template <typename T>
+static void addAligned(size_t& size, T /* value */) {
+ size = FlattenableUtils::align<sizeof(T)>(size);
+ size += sizeof(T);
+}
+
+template <typename T>
+static inline constexpr uint32_t low32(const T n) {
+ return static_cast<uint32_t>(static_cast<uint64_t>(n));
+}
+
+template <typename T>
+static inline constexpr uint32_t high32(const T n) {
+ return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32);
+}
+
+template <typename T>
+static inline constexpr T to64(const uint32_t lo, const uint32_t hi) {
+ return static_cast<T>(static_cast<uint64_t>(hi) << 32 | lo);
+}
+
+} // namespace
+
+size_t BufferReleaseChannel::Message::getPodSize() const {
+ size_t size = 0;
+ addAligned(size, low32(releaseCallbackId.bufferId));
+ addAligned(size, high32(releaseCallbackId.bufferId));
+ addAligned(size, low32(releaseCallbackId.framenumber));
+ addAligned(size, high32(releaseCallbackId.framenumber));
+ addAligned(size, maxAcquiredBufferCount);
+ return size;
+}
+
+size_t BufferReleaseChannel::Message::getFlattenedSize() const {
+ size_t size = releaseFence->getFlattenedSize();
+ size = FlattenableUtils::align<4>(size);
+ size += getPodSize();
+ return size;
+}
+
+status_t BufferReleaseChannel::Message::flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const {
+ if (status_t err = releaseFence->flatten(buffer, size, fds, count); err != OK) {
+ return err;
+ }
+ size -= FlattenableUtils::align<4>(buffer);
+
+ // Check we still have enough space
+ if (size < getPodSize()) {
+ return NO_MEMORY;
+ }
+
+ writeAligned(buffer, size, low32(releaseCallbackId.bufferId));
+ writeAligned(buffer, size, high32(releaseCallbackId.bufferId));
+ writeAligned(buffer, size, low32(releaseCallbackId.framenumber));
+ writeAligned(buffer, size, high32(releaseCallbackId.framenumber));
+ writeAligned(buffer, size, maxAcquiredBufferCount);
+ return OK;
+}
+
+status_t BufferReleaseChannel::Message::unflatten(void const*& buffer, size_t& size,
+ int const*& fds, size_t& count) {
+ releaseFence = new Fence();
+ if (status_t err = releaseFence->unflatten(buffer, size, fds, count); err != OK) {
+ return err;
+ }
+ size -= FlattenableUtils::align<4>(buffer);
+
+ // Check we still have enough space
+ if (size < getPodSize()) {
+ return OK;
+ }
+
+ uint32_t bufferIdLo = 0, bufferIdHi = 0;
+ uint32_t frameNumberLo = 0, frameNumberHi = 0;
+
+ readAligned(buffer, size, bufferIdLo);
+ readAligned(buffer, size, bufferIdHi);
+ releaseCallbackId.bufferId = to64<int64_t>(bufferIdLo, bufferIdHi);
+ readAligned(buffer, size, frameNumberLo);
+ readAligned(buffer, size, frameNumberHi);
+ releaseCallbackId.framenumber = to64<uint64_t>(frameNumberLo, frameNumberHi);
+ readAligned(buffer, size, maxAcquiredBufferCount);
+
+ return OK;
+}
+
+status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence(
+ ReleaseCallbackId& outReleaseCallbackId, sp<Fence>& outReleaseFence,
+ uint32_t& outMaxAcquiredBufferCount) {
+ Message message;
+ mFlattenedBuffer.resize(message.getFlattenedSize());
+ std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
+
+ iovec iov{
+ .iov_base = mFlattenedBuffer.data(),
+ .iov_len = mFlattenedBuffer.size(),
+ };
+
+ msghdr msg{
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = controlMessageBuffer.data(),
+ .msg_controllen = controlMessageBuffer.size(),
+ };
+
+ int result;
+ do {
+ result = recvmsg(mFd, &msg, 0);
+ } while (result == -1 && errno == EINTR);
+ if (result == -1) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ return WOULD_BLOCK;
+ }
+ ALOGE("Error reading release fence from socket: error %#x (%s)", errno, strerror(errno));
+ return UNKNOWN_ERROR;
+ }
+
+ if (msg.msg_iovlen != 1) {
+ ALOGE("Error reading release fence from socket: bad data length");
+ return UNKNOWN_ERROR;
+ }
+
+ if (msg.msg_controllen % sizeof(int) != 0) {
+ ALOGE("Error reading release fence from socket: bad fd length");
+ return UNKNOWN_ERROR;
+ }
+
+ size_t dataLen = msg.msg_iov->iov_len;
+ const void* data = static_cast<const void*>(msg.msg_iov->iov_base);
+ if (!data) {
+ ALOGE("Error reading release fence from socket: no buffer data");
+ return UNKNOWN_ERROR;
+ }
+
+ size_t fdCount = 0;
+ const int* fdData = nullptr;
+ if (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg)) {
+ fdData = reinterpret_cast<const int*>(CMSG_DATA(cmsg));
+ fdCount = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+ }
+
+ if (status_t err = message.unflatten(data, dataLen, fdData, fdCount); err != OK) {
+ return err;
+ }
+
+ outReleaseCallbackId = message.releaseCallbackId;
+ outReleaseFence = std::move(message.releaseFence);
+ outMaxAcquiredBufferCount = message.maxAcquiredBufferCount;
+
+ return OK;
+}
+
+int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId,
+ const sp<Fence>& fence,
+ uint32_t maxAcquiredBufferCount) {
+ Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount};
+ mFlattenedBuffer.resize(message.getFlattenedSize());
+ int flattenedFd;
+ {
+ // Make copies of needed items since flatten modifies them, and we don't
+ // want to send anything if there's an error during flatten.
+ void* flattenedBufferPtr = mFlattenedBuffer.data();
+ size_t flattenedBufferSize = mFlattenedBuffer.size();
+ int* flattenedFdPtr = &flattenedFd;
+ size_t flattenedFdCount = 1;
+ if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr,
+ flattenedFdCount);
+ err != OK) {
+ ALOGE("Failed to flatten BufferReleaseChannel message.");
+ return err;
+ }
+ }
+
+ iovec iov{
+ .iov_base = mFlattenedBuffer.data(),
+ .iov_len = mFlattenedBuffer.size(),
+ };
+
+ msghdr msg{
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+
+ std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer;
+ if (fence && fence->isValid()) {
+ msg.msg_control = controlMessageBuffer.data();
+ msg.msg_controllen = controlMessageBuffer.size();
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &flattenedFd, sizeof(int));
+ }
+
+ int result;
+ do {
+ result = sendmsg(mFd, &msg, 0);
+ } while (result == -1 && errno == EINTR);
+ if (result == -1) {
+ ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno));
+ return -errno;
+ }
+
+ return OK;
+}
+
+status_t BufferReleaseChannel::ProducerEndpoint::readFromParcel(const android::Parcel* parcel) {
+ if (!parcel) return STATUS_BAD_VALUE;
+ SAFE_PARCEL(parcel->readUtf8FromUtf16, &mName);
+ SAFE_PARCEL(parcel->readUniqueFileDescriptor, &mFd);
+ return STATUS_OK;
+}
+
+status_t BufferReleaseChannel::ProducerEndpoint::writeToParcel(android::Parcel* parcel) const {
+ if (!parcel) return STATUS_BAD_VALUE;
+ SAFE_PARCEL(parcel->writeUtf8AsUtf16, mName);
+ SAFE_PARCEL(parcel->writeUniqueFileDescriptor, mFd);
+ return STATUS_OK;
+}
+
+status_t BufferReleaseChannel::open(std::string name,
+ std::unique_ptr<ConsumerEndpoint>& outConsumer,
+ std::shared_ptr<ProducerEndpoint>& outProducer) {
+ outConsumer.reset();
+ outProducer.reset();
+
+ int sockets[2];
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
+ ALOGE("[%s] Failed to create socket pair. errorno=%d message='%s'", name.c_str(), errno,
+ strerror(errno));
+ return -errno;
+ }
+
+ android::base::unique_fd consumerFd(sockets[0]);
+ android::base::unique_fd producerFd(sockets[1]);
+
+ // Socket buffer size. The default is typically about 128KB, which is much larger than
+ // we really need.
+ size_t bufferSize = 32 * 1024;
+ if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) ==
+ -1) {
+ ALOGE("[%s] Failed to set consumer socket send buffer size. errno=%d message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+ if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) ==
+ -1) {
+ ALOGE("[%s] Failed to set consumer socket receive buffer size. errno=%d "
+ "message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+ if (setsockopt(producerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) ==
+ -1) {
+ ALOGE("[%s] Failed to set producer socket send buffer size. errno=%d message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+ if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) ==
+ -1) {
+ ALOGE("[%s] Failed to set producer socket receive buffer size. errno=%d "
+ "message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+
+ // Configure the consumer socket to be non-blocking.
+ int flags = fcntl(consumerFd.get(), F_GETFL, 0);
+ if (flags == -1) {
+ ALOGE("[%s] Failed to get consumer socket flags. errno=%d message='%s'", name.c_str(),
+ errno, strerror(errno));
+ return -errno;
+ }
+ if (fcntl(consumerFd.get(), F_SETFL, flags | O_NONBLOCK) == -1) {
+ ALOGE("[%s] Failed to set consumer socket to non-blocking mode. errno=%d "
+ "message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+
+ // Configure a timeout for the producer socket.
+ const timeval timeout{.tv_sec = 1, .tv_usec = 0};
+ if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeval)) == -1) {
+ ALOGE("[%s] Failed to set producer socket timeout. errno=%d message='%s'", name.c_str(),
+ errno, strerror(errno));
+ return -errno;
+ }
+
+ // Make the consumer read-only
+ if (shutdown(consumerFd.get(), SHUT_WR) == -1) {
+ ALOGE("[%s] Failed to shutdown writing on consumer socket. errno=%d message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+
+ // Make the producer write-only
+ if (shutdown(producerFd.get(), SHUT_RD) == -1) {
+ ALOGE("[%s] Failed to shutdown reading on producer socket. errno=%d message='%s'",
+ name.c_str(), errno, strerror(errno));
+ return -errno;
+ }
+
+ outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd));
+ outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd));
+ return STATUS_OK;
+}
+
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index b625c3f..602bba8 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#include <inttypes.h>
-
#define LOG_TAG "ConsumerBase"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
@@ -29,17 +27,23 @@
#include <cutils/atomic.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
+#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
+#include <log/log.h>
#include <utils/Log.h>
#include <utils/String8.h>
#include <utils/Trace.h>
+#include <inttypes.h>
+
// Macros for including the ConsumerBase name in log messages
#define CB_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
// #define CB_LOGD(x, ...) ALOGD("[%s] " x, mName.c_str(), ##__VA_ARGS__)
@@ -59,6 +63,30 @@
mAbandoned(false),
mConsumer(bufferQueue),
mPrevFinalReleaseFence(Fence::NO_FENCE) {
+ initialize(controlledByApp);
+}
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ConsumerBase::ConsumerBase(bool controlledByApp, bool consumerIsSurfaceFlinger)
+ : mAbandoned(false), mPrevFinalReleaseFence(Fence::NO_FENCE) {
+ sp<IGraphicBufferProducer> producer;
+ BufferQueue::createBufferQueue(&producer, &mConsumer, consumerIsSurfaceFlinger);
+ mSurface = sp<Surface>::make(producer, controlledByApp);
+ initialize(controlledByApp);
+}
+
+ConsumerBase::ConsumerBase(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp)
+ : mAbandoned(false),
+ mConsumer(consumer),
+ mSurface(sp<Surface>::make(producer, controlledByApp)),
+ mPrevFinalReleaseFence(Fence::NO_FENCE) {
+ initialize(controlledByApp);
+}
+
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
+void ConsumerBase::initialize(bool controlledByApp) {
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
@@ -96,6 +124,35 @@
abandon();
}
+int ConsumerBase::getSlotForBufferLocked(const sp<GraphicBuffer>& buffer) {
+ if (!buffer) {
+ return BufferQueue::INVALID_BUFFER_SLOT;
+ }
+
+ uint64_t id = buffer->getId();
+ for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
+ auto& slot = mSlots[i];
+ if (slot.mGraphicBuffer && slot.mGraphicBuffer->getId() == id) {
+ return i;
+ }
+ }
+
+ return BufferQueue::INVALID_BUFFER_SLOT;
+}
+
+status_t ConsumerBase::detachBufferLocked(int slotIndex) {
+ status_t result = mConsumer->detachBuffer(slotIndex);
+
+ if (result != NO_ERROR) {
+ CB_LOGE("Failed to detach buffer: %d", result);
+ return result;
+ }
+
+ freeBufferLocked(slotIndex);
+
+ return result;
+}
+
void ConsumerBase::freeBufferLocked(int slotIndex) {
CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
mSlots[slotIndex].mGraphicBuffer = nullptr;
@@ -252,16 +309,30 @@
return NO_INIT;
}
- status_t result = mConsumer->detachBuffer(slot);
- if (result != NO_ERROR) {
- CB_LOGE("Failed to detach buffer: %d", result);
- return result;
+ return detachBufferLocked(slot);
+}
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+status_t ConsumerBase::detachBuffer(const sp<GraphicBuffer>& buffer) {
+ CB_LOGV("detachBuffer");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ CB_LOGE("detachBuffer: ConsumerBase is abandoned!");
+ return NO_INIT;
+ }
+ if (buffer == nullptr) {
+ return BAD_VALUE;
}
- freeBufferLocked(slot);
+ int slotIndex = getSlotForBufferLocked(buffer);
+ if (slotIndex == BufferQueue::INVALID_BUFFER_SLOT) {
+ return BAD_VALUE;
+ }
- return result;
+ return detachBufferLocked(slotIndex);
}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
status_t ConsumerBase::setDefaultBufferSize(uint32_t width, uint32_t height) {
Mutex::Autolock _l(mMutex);
@@ -309,6 +380,17 @@
return mConsumer->setTransformHint(hint);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+status_t ConsumerBase::setMaxBufferCount(int bufferCount) {
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ CB_LOGE("setMaxBufferCount: ConsumerBase is abandoned!");
+ return NO_INIT;
+ }
+ return mConsumer->setMaxBufferCount(bufferCount);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
status_t ConsumerBase::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -318,6 +400,17 @@
return mConsumer->setMaxAcquiredBufferCount(maxAcquiredBuffers);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+status_t ConsumerBase::setConsumerIsProtected(bool isProtected) {
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ CB_LOGE("setConsumerIsProtected: ConsumerBase is abandoned!");
+ return NO_INIT;
+ }
+ return mConsumer->setConsumerIsProtected(isProtected);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
sp<NativeHandle> ConsumerBase::getSidebandStream() const {
Mutex::Autolock _l(mMutex);
if (mAbandoned) {
@@ -384,6 +477,19 @@
}
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+sp<Surface> ConsumerBase::getSurface() const {
+ LOG_ALWAYS_FATAL_IF(mSurface == nullptr,
+ "It's illegal to get the surface of a Consumer that does not own it. This "
+ "should be impossible once the old CTOR is removed.");
+ return mSurface;
+}
+
+sp<IGraphicBufferConsumer> ConsumerBase::getIGraphicBufferConsumer() const {
+ return mConsumer;
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
status_t ConsumerBase::acquireBufferLocked(BufferItem *item,
nsecs_t presentWhen, uint64_t maxFrameNumber) {
if (mAbandoned) {
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 3031fa1..23b432e 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -18,9 +18,9 @@
#define LOG_TAG "CpuConsumer"
//#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <gui/CpuConsumer.h>
-
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
+#include <gui/CpuConsumer.h>
#include <utils/Log.h>
#define CC_LOGV(x, ...) ALOGV("[%s] " x, mName.c_str(), ##__VA_ARGS__)
@@ -31,12 +31,25 @@
namespace android {
-CpuConsumer::CpuConsumer(const sp<IGraphicBufferConsumer>& bq,
- size_t maxLockedBuffers, bool controlledByApp) :
- ConsumerBase(bq, controlledByApp),
- mMaxLockedBuffers(maxLockedBuffers),
- mCurrentLockedBuffers(0)
-{
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+CpuConsumer::CpuConsumer(size_t maxLockedBuffers, bool controlledByApp,
+ bool isConsumerSurfaceFlinger)
+ : ConsumerBase(controlledByApp, isConsumerSurfaceFlinger),
+ mMaxLockedBuffers(maxLockedBuffers),
+ mCurrentLockedBuffers(0) {
+ // Create tracking entries for locked buffers
+ mAcquiredBuffers.insertAt(0, maxLockedBuffers);
+
+ mConsumer->setConsumerUsageBits(GRALLOC_USAGE_SW_READ_OFTEN);
+ mConsumer->setMaxAcquiredBufferCount(static_cast<int32_t>(maxLockedBuffers));
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
+CpuConsumer::CpuConsumer(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers,
+ bool controlledByApp)
+ : ConsumerBase(bq, controlledByApp),
+ mMaxLockedBuffers(maxLockedBuffers),
+ mCurrentLockedBuffers(0) {
// Create tracking entries for locked buffers
mAcquiredBuffers.insertAt(0, maxLockedBuffers);
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index d49489c..95cce5c 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -101,6 +101,34 @@
return hasIt;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+GLConsumer::GLConsumer(uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
+ : ConsumerBase(isControlledByApp, /* isConsumerSurfaceFlinger */ false),
+ mCurrentCrop(Rect::EMPTY_RECT),
+ mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
+ mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+ mCurrentFrameNumber(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mFilteringEnabled(true),
+ mTexName(tex),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglContext(EGL_NO_CONTEXT),
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mAttached(true) {
+ GLC_LOGV("GLConsumer");
+
+ memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+ mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
uint32_t texTarget, bool useFenceSync, bool isControlledByApp) :
ConsumerBase(bq, isControlledByApp),
@@ -130,27 +158,54 @@
mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
}
-GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget,
- bool useFenceSync, bool isControlledByApp) :
- ConsumerBase(bq, isControlledByApp),
- mCurrentCrop(Rect::EMPTY_RECT),
- mCurrentTransform(0),
- mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
- mCurrentFence(Fence::NO_FENCE),
- mCurrentTimestamp(0),
- mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
- mCurrentFrameNumber(0),
- mDefaultWidth(1),
- mDefaultHeight(1),
- mFilteringEnabled(true),
- mTexName(0),
- mUseFenceSync(useFenceSync),
- mTexTarget(texTarget),
- mEglDisplay(EGL_NO_DISPLAY),
- mEglContext(EGL_NO_CONTEXT),
- mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
- mAttached(false)
-{
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+GLConsumer::GLConsumer(uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
+ : ConsumerBase(isControlledByApp, /* isConsumerSurfaceFlinger */ false),
+ mCurrentCrop(Rect::EMPTY_RECT),
+ mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
+ mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+ mCurrentFrameNumber(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mFilteringEnabled(true),
+ mTexName(0),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglContext(EGL_NO_CONTEXT),
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mAttached(false) {
+ GLC_LOGV("GLConsumer");
+
+ memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+ mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
+GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget, bool useFenceSync,
+ bool isControlledByApp)
+ : ConsumerBase(bq, isControlledByApp),
+ mCurrentCrop(Rect::EMPTY_RECT),
+ mCurrentTransform(0),
+ mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+ mCurrentFence(Fence::NO_FENCE),
+ mCurrentTimestamp(0),
+ mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+ mCurrentFrameNumber(0),
+ mDefaultWidth(1),
+ mDefaultHeight(1),
+ mFilteringEnabled(true),
+ mTexName(0),
+ mUseFenceSync(useFenceSync),
+ mTexTarget(texTarget),
+ mEglDisplay(EGL_NO_DISPLAY),
+ mEglContext(EGL_NO_CONTEXT),
+ mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+ mAttached(false) {
GLC_LOGV("GLConsumer");
memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(),
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 0683087..7700795 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -25,6 +25,9 @@
ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION,
NEEDS_RELEASE_NOTIFY,
ON_BUFFERS_DISCARDED,
+ ON_BUFFER_DETACHED,
+ ON_BUFFER_ATTACHED,
+ NEEDS_ATTACH_NOTIFY,
};
class BpProducerListener : public BpInterface<IProducerListener>
@@ -64,6 +67,38 @@
data.writeInt32Vector(discardedSlots);
remote()->transact(ON_BUFFERS_DISCARDED, data, &reply, IBinder::FLAG_ONEWAY);
}
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+ virtual void onBufferDetached(int slot) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
+ data.writeInt32(slot);
+ remote()->transact(ON_BUFFER_DETACHED, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ virtual void onBufferAttached() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
+ remote()->transact(ON_BUFFER_ATTACHED, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ virtual bool needsAttachNotify() {
+ bool result;
+ Parcel data, reply;
+ data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor());
+ status_t err = remote()->transact(NEEDS_ATTACH_NOTIFY, data, &reply);
+ if (err != NO_ERROR) {
+ ALOGE("IProducerListener: binder call \'needsAttachNotify\' failed");
+ return true;
+ }
+ err = reply.readBool(&result);
+ if (err != NO_ERROR) {
+ ALOGE("IProducerListener: malformed binder reply");
+ return true;
+ }
+ return result;
+ }
+#endif
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -115,6 +150,27 @@
onBuffersDiscarded(discardedSlots);
return NO_ERROR;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+ case ON_BUFFER_DETACHED: {
+ CHECK_INTERFACE(IProducerListener, data, reply);
+ int slot;
+ status_t result = data.readInt32(&slot);
+ if (result != NO_ERROR) {
+ ALOGE("ON_BUFFER_DETACHED failed to read slot: %d", result);
+ return result;
+ }
+ onBufferDetached(slot);
+ return NO_ERROR;
+ }
+ case ON_BUFFER_ATTACHED:
+ CHECK_INTERFACE(IProducerListener, data, reply);
+ onBufferAttached();
+ return NO_ERROR;
+ case NEEDS_ATTACH_NOTIFY:
+ CHECK_INTERFACE(IProducerListener, data, reply);
+ reply->writeBool(needsAttachNotify());
+ return NO_ERROR;
+#endif
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index ff6b558..2699368 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -62,7 +62,7 @@
status_t setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands commands, int64_t desiredPresentTime, bool isAutoTimestamp,
const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 307ae39..b109969 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "LayerState"
#include <cinttypes>
-#include <cmath>
#include <android/gui/ISurfaceComposerClient.h>
#include <android/native_window.h>
@@ -194,6 +193,13 @@
SAFE_PARCEL(output.writeFloat, currentHdrSdrRatio);
SAFE_PARCEL(output.writeFloat, desiredHdrSdrRatio);
SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint));
+
+ const bool hasBufferReleaseChannel = (bufferReleaseChannel != nullptr);
+ SAFE_PARCEL(output.writeBool, hasBufferReleaseChannel);
+ if (hasBufferReleaseChannel) {
+ SAFE_PARCEL(output.writeParcelable, *bufferReleaseChannel);
+ }
+
return NO_ERROR;
}
@@ -339,6 +345,13 @@
SAFE_PARCEL(input.readInt32, &tmpInt32);
cachingHint = static_cast<gui::CachingHint>(tmpInt32);
+ bool hasBufferReleaseChannel;
+ SAFE_PARCEL(input.readBool, &hasBufferReleaseChannel);
+ if (hasBufferReleaseChannel) {
+ bufferReleaseChannel = std::make_shared<gui::BufferReleaseChannel::ProducerEndpoint>();
+ SAFE_PARCEL(input.readParcelable, bufferReleaseChannel.get());
+ }
+
return NO_ERROR;
}
@@ -718,6 +731,10 @@
if (other.what & eFlushJankData) {
what |= eFlushJankData;
}
+ if (other.what & eBufferReleaseChannelChanged) {
+ what |= eBufferReleaseChannelChanged;
+ bufferReleaseChannel = other.bufferReleaseChannel;
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64,
@@ -797,6 +814,7 @@
CHECK_DIFF(diff, eColorChanged, other, color.rgb);
CHECK_DIFF(diff, eColorSpaceAgnosticChanged, other, colorSpaceAgnostic);
CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled);
+ if (other.what & eBufferReleaseChannelChanged) diff |= eBufferReleaseChannelChanged;
return diff;
}
@@ -874,88 +892,6 @@
// ----------------------------------------------------------------------------
-namespace gui {
-
-status_t CaptureArgs::writeToParcel(Parcel* output) const {
- SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(pixelFormat));
- SAFE_PARCEL(output->write, sourceCrop);
- SAFE_PARCEL(output->writeFloat, frameScaleX);
- SAFE_PARCEL(output->writeFloat, frameScaleY);
- SAFE_PARCEL(output->writeBool, captureSecureLayers);
- SAFE_PARCEL(output->writeInt32, uid);
- SAFE_PARCEL(output->writeInt32, static_cast<int32_t>(dataspace));
- SAFE_PARCEL(output->writeBool, allowProtected);
- SAFE_PARCEL(output->writeBool, grayscale);
- SAFE_PARCEL(output->writeInt32, excludeHandles.size());
- for (auto& excludeHandle : excludeHandles) {
- SAFE_PARCEL(output->writeStrongBinder, excludeHandle);
- }
- SAFE_PARCEL(output->writeBool, hintForSeamlessTransition);
- return NO_ERROR;
-}
-
-status_t CaptureArgs::readFromParcel(const Parcel* input) {
- int32_t value = 0;
- SAFE_PARCEL(input->readInt32, &value);
- pixelFormat = static_cast<ui::PixelFormat>(value);
- SAFE_PARCEL(input->read, sourceCrop);
- SAFE_PARCEL(input->readFloat, &frameScaleX);
- SAFE_PARCEL(input->readFloat, &frameScaleY);
- SAFE_PARCEL(input->readBool, &captureSecureLayers);
- SAFE_PARCEL(input->readInt32, &uid);
- SAFE_PARCEL(input->readInt32, &value);
- dataspace = static_cast<ui::Dataspace>(value);
- SAFE_PARCEL(input->readBool, &allowProtected);
- SAFE_PARCEL(input->readBool, &grayscale);
- int32_t numExcludeHandles = 0;
- SAFE_PARCEL_READ_SIZE(input->readInt32, &numExcludeHandles, input->dataSize());
- excludeHandles.reserve(numExcludeHandles);
- for (int i = 0; i < numExcludeHandles; i++) {
- sp<IBinder> binder;
- SAFE_PARCEL(input->readStrongBinder, &binder);
- excludeHandles.emplace(binder);
- }
- SAFE_PARCEL(input->readBool, &hintForSeamlessTransition);
- return NO_ERROR;
-}
-
-status_t DisplayCaptureArgs::writeToParcel(Parcel* output) const {
- SAFE_PARCEL(CaptureArgs::writeToParcel, output);
-
- SAFE_PARCEL(output->writeStrongBinder, displayToken);
- SAFE_PARCEL(output->writeUint32, width);
- SAFE_PARCEL(output->writeUint32, height);
- return NO_ERROR;
-}
-
-status_t DisplayCaptureArgs::readFromParcel(const Parcel* input) {
- SAFE_PARCEL(CaptureArgs::readFromParcel, input);
-
- SAFE_PARCEL(input->readStrongBinder, &displayToken);
- SAFE_PARCEL(input->readUint32, &width);
- SAFE_PARCEL(input->readUint32, &height);
- return NO_ERROR;
-}
-
-status_t LayerCaptureArgs::writeToParcel(Parcel* output) const {
- SAFE_PARCEL(CaptureArgs::writeToParcel, output);
-
- SAFE_PARCEL(output->writeStrongBinder, layerHandle);
- SAFE_PARCEL(output->writeBool, childrenOnly);
- return NO_ERROR;
-}
-
-status_t LayerCaptureArgs::readFromParcel(const Parcel* input) {
- SAFE_PARCEL(CaptureArgs::readFromParcel, input);
-
- SAFE_PARCEL(input->readStrongBinder, &layerHandle);
-
- SAFE_PARCEL(input->readBool, &childrenOnly);
- return NO_ERROR;
-}
-
-}; // namespace gui
-
ReleaseCallbackId BufferData::generateReleaseCallbackId() const {
uint64_t bufferId;
if (buffer) {
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index 070f6bf..b97cee3 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -1,6 +1,8 @@
# Bug component: 1075131
+carlosmr@google.com
chrisforbes@google.com
+jshargo@google.com
jreck@google.com
file:/services/surfaceflinger/OWNERS
@@ -9,10 +11,10 @@
# BufferQueue is feature-frozen
per-file BufferQueue* = set noparent
-per-file BufferQueue* = jreck@google.com, sumir@google.com, alecmouri@google.com
+per-file BufferQueue* = jreck@google.com, sumir@google.com, alecmouri@google.com, jshargo@google.com, carlosmr@google.com
per-file IGraphicBuffer* = set noparent
-per-file IGraphicBuffer* = jreck@google.com, sumir@google.com, alecmouri@google.com
+per-file IGraphicBuffer* = jreck@google.com, sumir@google.com, alecmouri@google.com, jshargo@google.com, carlosmr@google.com
per-file include/gui/BufferQueue* = set noparent
-per-file include/gui/BufferQueue* = jreck@google.com, sumir@google.com, alecmouri@google.com
+per-file include/gui/BufferQueue* = jreck@google.com, sumir@google.com, alecmouri@google.com, jshargo@google.com, carlosmr@google.com
per-file include/gui/IGraphicBuffer* = set noparent
-per-file include/gui/IGraphicBuffer* = jreck@google.com, sumir@google.com, alecmouri@google.com
+per-file include/gui/IGraphicBuffer* = jreck@google.com, sumir@google.com, alecmouri@google.com, jshargo@google.com, carlosmr@google.com
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 685391e..da3886c 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -21,6 +21,8 @@
#include <gui/Surface.h>
#include <condition_variable>
+#include <cstddef>
+#include <cstdint>
#include <deque>
#include <mutex>
#include <thread>
@@ -41,7 +43,7 @@
#include <ui/GraphicBuffer.h>
#include <ui/Region.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/BufferItem.h>
#include <gui/ISurfaceComposer.h>
@@ -161,6 +163,12 @@
mReqFormat, mReqUsage);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+status_t Surface::allowAllocation(bool allowAllocation) {
+ return mGraphicBufferProducer->allowAllocation(allowAllocation);
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
status_t Surface::setGenerationNumber(uint32_t generation) {
status_t result = mGraphicBufferProducer->setGenerationNumber(generation);
if (result == NO_ERROR) {
@@ -693,6 +701,50 @@
return OK;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
+status_t Surface::dequeueBuffer(sp<GraphicBuffer>* buffer, sp<Fence>* outFence) {
+ if (buffer == nullptr || outFence == nullptr) {
+ return BAD_VALUE;
+ }
+
+ android_native_buffer_t* anb;
+ int fd = -1;
+ status_t res = dequeueBuffer(&anb, &fd);
+ *buffer = GraphicBuffer::from(anb);
+ *outFence = sp<Fence>::make(fd);
+ return res;
+}
+
+status_t Surface::queueBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& fd) {
+ if (buffer == nullptr) {
+ return BAD_VALUE;
+ }
+ return queueBuffer(buffer.get(), fd ? fd->get() : -1);
+}
+
+status_t Surface::detachBuffer(const sp<GraphicBuffer>& buffer) {
+ if (nullptr == buffer) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mMutex);
+
+ uint64_t bufferId = buffer->getId();
+ for (int slot = 0; slot < Surface::NUM_BUFFER_SLOTS; ++slot) {
+ auto& bufferSlot = mSlots[slot];
+ if (bufferSlot.buffer != nullptr && bufferSlot.buffer->getId() == bufferId) {
+ bufferSlot.buffer = nullptr;
+ bufferSlot.dirtyRegion = Region::INVALID_REGION;
+ return mGraphicBufferProducer->detachBuffer(slot);
+ }
+ }
+
+ return BAD_VALUE;
+}
+
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
int Surface::dequeueBuffers(std::vector<BatchBuffer>* buffers) {
using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput;
using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput;
@@ -1116,6 +1168,117 @@
}
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
+int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
+ ATRACE_CALL();
+ ALOGV("Surface::queueBuffer");
+
+ IGraphicBufferProducer::QueueBufferOutput output;
+ IGraphicBufferProducer::QueueBufferInput input;
+ int slot;
+ sp<Fence> fence;
+ {
+ Mutex::Autolock lock(mMutex);
+
+ slot = getSlotFromBufferLocked(buffer);
+ if (slot < 0) {
+ if (fenceFd >= 0) {
+ close(fenceFd);
+ }
+ return slot;
+ }
+ if (mSharedBufferSlot == slot && mSharedBufferHasBeenQueued) {
+ if (fenceFd >= 0) {
+ close(fenceFd);
+ }
+ return OK;
+ }
+
+ getQueueBufferInputLocked(buffer, fenceFd, mTimestamp, &input);
+ applyGrallocMetadataLocked(buffer, input);
+ fence = input.fence;
+ }
+ nsecs_t now = systemTime();
+ // Drop the lock temporarily while we touch the underlying producer. In the case of a local
+ // BufferQueue, the following should be allowable:
+ //
+ // Surface::queueBuffer
+ // -> IConsumerListener::onFrameAvailable callback triggers automatically
+ // -> implementation calls IGraphicBufferConsumer::acquire/release immediately
+ // -> SurfaceListener::onBufferRelesed callback triggers automatically
+ // -> implementation calls Surface::dequeueBuffer
+ status_t err = mGraphicBufferProducer->queueBuffer(slot, input, &output);
+ {
+ Mutex::Autolock lock(mMutex);
+
+ mLastQueueDuration = systemTime() - now;
+ if (err != OK) {
+ ALOGE("queueBuffer: error queuing buffer, %d", err);
+ }
+
+ onBufferQueuedLocked(slot, fence, output);
+ }
+
+ return err;
+}
+
+int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers) {
+ ATRACE_CALL();
+ ALOGV("Surface::queueBuffers");
+
+ size_t numBuffers = buffers.size();
+ std::vector<IGraphicBufferProducer::QueueBufferInput> queueBufferInputs(numBuffers);
+ std::vector<IGraphicBufferProducer::QueueBufferOutput> queueBufferOutputs;
+ std::vector<int> bufferSlots(numBuffers, -1);
+ std::vector<sp<Fence>> bufferFences(numBuffers);
+
+ int err;
+ {
+ Mutex::Autolock lock(mMutex);
+
+ if (mSharedBufferMode) {
+ ALOGE("%s: batched operation is not supported in shared buffer mode", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+
+ for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) {
+ int i = getSlotFromBufferLocked(buffers[batchIdx].buffer);
+ if (i < 0) {
+ if (buffers[batchIdx].fenceFd >= 0) {
+ close(buffers[batchIdx].fenceFd);
+ }
+ return i;
+ }
+ bufferSlots[batchIdx] = i;
+
+ IGraphicBufferProducer::QueueBufferInput input;
+ getQueueBufferInputLocked(buffers[batchIdx].buffer, buffers[batchIdx].fenceFd,
+ buffers[batchIdx].timestamp, &input);
+ bufferFences[batchIdx] = input.fence;
+ queueBufferInputs[batchIdx] = input;
+ }
+ }
+ nsecs_t now = systemTime();
+ err = mGraphicBufferProducer->queueBuffers(queueBufferInputs, &queueBufferOutputs);
+ {
+ Mutex::Autolock lock(mMutex);
+ mLastQueueDuration = systemTime() - now;
+ if (err != OK) {
+ ALOGE("%s: error queuing buffer, %d", __FUNCTION__, err);
+ }
+
+ for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) {
+ onBufferQueuedLocked(bufferSlots[batchIdx], bufferFences[batchIdx],
+ queueBufferOutputs[batchIdx]);
+ }
+ }
+
+ return err;
+}
+
+#else
+
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
ATRACE_CALL();
ALOGV("Surface::queueBuffer");
@@ -1203,6 +1366,8 @@
return err;
}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
void Surface::querySupportedTimestampsLocked() const {
// mMutex must be locked when calling this method.
@@ -1862,15 +2027,6 @@
return connect(api, listener);
}
-int Surface::connect(int api, const sp<SurfaceListener>& listener) {
- return connect(api, listener, false);
-}
-
-int Surface::connect(
- int api, bool reportBufferRemoval, const sp<SurfaceListener>& sListener) {
- return connect(api, sListener, reportBufferRemoval);
-}
-
int Surface::connect(int api, const sp<SurfaceListener>& listener, bool reportBufferRemoval) {
ATRACE_CALL();
ALOGV("Surface::connect");
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 2821b51..7d3e5c1 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -43,7 +43,7 @@
#include <system/graphics.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/BufferItemConsumer.h>
#include <gui/CpuConsumer.h>
#include <gui/IGraphicBufferProducer.h>
@@ -827,7 +827,6 @@
SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
: mId(other.mId),
- mTransactionNestCount(other.mTransactionNestCount),
mAnimation(other.mAnimation),
mEarlyWakeupStart(other.mEarlyWakeupStart),
mEarlyWakeupEnd(other.mEarlyWakeupEnd),
@@ -867,7 +866,6 @@
status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
const uint64_t transactionId = parcel->readUint64();
- const uint32_t transactionNestCount = parcel->readUint32();
const bool animation = parcel->readBool();
const bool earlyWakeupStart = parcel->readBool();
const bool earlyWakeupEnd = parcel->readBool();
@@ -964,7 +962,6 @@
// Parsing was successful. Update the object.
mId = transactionId;
- mTransactionNestCount = transactionNestCount;
mAnimation = animation;
mEarlyWakeupStart = earlyWakeupStart;
mEarlyWakeupEnd = earlyWakeupEnd;
@@ -996,7 +993,6 @@
const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers();
parcel->writeUint64(mId);
- parcel->writeUint32(mTransactionNestCount);
parcel->writeBool(mAnimation);
parcel->writeBool(mEarlyWakeupStart);
parcel->writeBool(mEarlyWakeupEnd);
@@ -1148,7 +1144,6 @@
mInputWindowCommands.clear();
mUncacheBuffers.clear();
mMayContainBuffer = false;
- mTransactionNestCount = 0;
mAnimation = false;
mEarlyWakeupStart = false;
mEarlyWakeupEnd = false;
@@ -1174,7 +1169,8 @@
uncacheBuffer.token = BufferCache::getInstance().getToken();
uncacheBuffer.id = cacheId;
Vector<ComposerState> composerStates;
- status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, {},
+ Vector<DisplayState> displayStates;
+ status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, displayStates,
ISurfaceComposer::eOneWay,
Transaction::getDefaultApplyToken(), {}, systemTime(),
true, {uncacheBuffer}, false, {}, generateId(), {});
@@ -2394,6 +2390,22 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferReleaseChannel(
+ const sp<SurfaceControl>& sc,
+ const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eBufferReleaseChannelChanged;
+ s->bufferReleaseChannel = channel;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp
index 0929b8e..91c9a85 100644
--- a/libs/gui/WindowInfosListenerReporter.cpp
+++ b/libs/gui/WindowInfosListenerReporter.cpp
@@ -15,7 +15,7 @@
*/
#include <android/gui/ISurfaceComposer.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/WindowInfosListenerReporter.h>
#include "gui/WindowInfosUpdate.h"
diff --git a/libs/gui/aidl/Android.bp b/libs/gui/aidl/Android.bp
index 8ed08c2..fd035f6 100644
--- a/libs/gui/aidl/Android.bp
+++ b/libs/gui/aidl/Android.bp
@@ -28,9 +28,6 @@
":libgui_extra_unstructured_aidl_files",
"android/gui/BitTube.aidl",
- "android/gui/CaptureArgs.aidl",
- "android/gui/DisplayCaptureArgs.aidl",
- "android/gui/LayerCaptureArgs.aidl",
"android/gui/LayerMetadata.aidl",
"android/gui/ParcelableVsyncEventData.aidl",
"android/gui/ScreenCaptureResults.aidl",
diff --git a/libs/gui/aidl/android/gui/CaptureArgs.aidl b/libs/gui/aidl/android/gui/CaptureArgs.aidl
index 9f198ca..2bbed2b 100644
--- a/libs/gui/aidl/android/gui/CaptureArgs.aidl
+++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl
@@ -16,4 +16,58 @@
package android.gui;
-parcelable CaptureArgs cpp_header "gui/DisplayCaptureArgs.h" rust_type "gui_aidl_types_rs::CaptureArgs";
+import android.gui.ARect;
+
+// Common arguments for capturing content on-screen
+parcelable CaptureArgs {
+ const int UNSET_UID = -1;
+
+ // Desired pixel format of the final screenshotted buffer
+ int /*ui::PixelFormat*/ pixelFormat = 1;
+
+ // Crop in layer space: all content outside of the crop will not be captured.
+ ARect sourceCrop;
+
+ // Scale in the x-direction for the screenshotted result.
+ float frameScaleX = 1.0f;
+
+ // Scale in the y-direction for the screenshotted result.
+ float frameScaleY = 1.0f;
+
+ // True if capturing secure layers is permitted
+ boolean captureSecureLayers = false;
+
+ // UID whose content we want to screenshot
+ int uid = UNSET_UID;
+
+ // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured
+ // result will be in a colorspace appropriate for capturing the display contents
+ // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be
+ // different from SRGB (byte per color), and failed when checking colors in tests.
+ // NOTE: In normal cases, we want the screen to be captured in display's colorspace.
+ int /*ui::Dataspace*/ dataspace = 0;
+
+ // The receiver of the capture can handle protected buffer. A protected buffer has
+ // GRALLOC_USAGE_PROTECTED usage bit and must not be accessed unprotected behaviour.
+ // Any read/write access from unprotected context will result in undefined behaviour.
+ // Protected contents are typically DRM contents. This has no direct implication to the
+ // secure property of the surface, which is specified by the application explicitly to avoid
+ // the contents being accessed/captured by screenshot or unsecure display.
+ boolean allowProtected = false;
+
+ // True if the content should be captured in grayscale
+ boolean grayscale = false;
+
+ // List of layers to exclude capturing from
+ IBinder[] excludeHandles;
+
+ // Hint that the caller will use the screenshot animation as part of a transition animation.
+ // The canonical example would be screen rotation - in such a case any color shift in the
+ // screenshot is a detractor so composition in the display's colorspace is required.
+ // Otherwise, the system may choose a colorspace that is more appropriate for use-cases
+ // such as file encoding or for blending HDR content into an ap's UI, where the display's
+ // exact colorspace is not an appropriate intermediate result.
+ // Note that if the caller is requesting a specific dataspace, this hint does nothing.
+ boolean hintForSeamlessTransition = false;
+}
+
diff --git a/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl b/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl
index fc97dbf..e00a2df 100644
--- a/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl
+++ b/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl
@@ -16,5 +16,18 @@
package android.gui;
-parcelable DisplayCaptureArgs cpp_header "gui/DisplayCaptureArgs.h" rust_type "gui_aidl_types_rs::DisplayCaptureArgs";
+import android.gui.CaptureArgs;
+
+// Arguments for screenshotting an entire display
+parcelable DisplayCaptureArgs {
+ CaptureArgs captureArgs;
+
+ // The display that we want to screenshot
+ IBinder displayToken;
+
+ // The width of the render area when we screenshot
+ int width = 0;
+ // The length of the render area when we screenshot
+ int height = 0;
+}
diff --git a/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl b/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl
index 18d293f..004c35a 100644
--- a/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl
+++ b/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl
@@ -16,4 +16,15 @@
package android.gui;
-parcelable LayerCaptureArgs cpp_header "gui/LayerCaptureArgs.h" rust_type "gui_aidl_types_rs::LayerCaptureArgs";
+import android.gui.CaptureArgs;
+
+// Arguments for capturing a layer and/or its children
+parcelable LayerCaptureArgs {
+ CaptureArgs captureArgs;
+
+ // The Layer that we may want to capture. We would also capture its children
+ IBinder layerHandle;
+ // True if we don't actually want to capture the layer and want to capture
+ // its children instead.
+ boolean childrenOnly = false;
+}
diff --git a/libs/gui/include/gui/AidlStatusUtil.h b/libs/gui/include/gui/AidlUtil.h
similarity index 84%
rename from libs/gui/include/gui/AidlStatusUtil.h
rename to libs/gui/include/gui/AidlUtil.h
index 55be27b..a3ecd84 100644
--- a/libs/gui/include/gui/AidlStatusUtil.h
+++ b/libs/gui/include/gui/AidlUtil.h
@@ -16,9 +16,11 @@
#pragma once
+#include <android/gui/ARect.h>
#include <binder/Status.h>
+#include <ui/Rect.h>
-// Extracted from frameworks/av/media/libaudioclient/include/media/AidlConversionUtil.h
+// Originally extracted from frameworks/av/media/libaudioclient/include/media/AidlConversionUtil.h
namespace android::gui::aidl_utils {
/**
@@ -68,7 +70,7 @@
*
* return_type method(type0 param0, ...)
*/
-static inline status_t statusTFromBinderStatus(const ::android::binder::Status &status) {
+static inline status_t statusTFromBinderStatus(const ::android::binder::Status& status) {
return status.isOk() ? OK // check OK,
: status.serviceSpecificErrorCode() // service-side error, not standard Java exception
// (fromServiceSpecificError)
@@ -84,8 +86,8 @@
* where Java callers expect an exception, not an integer return value.
*/
static inline ::android::binder::Status binderStatusFromStatusT(
- status_t status, const char *optionalMessage = nullptr) {
- const char *const emptyIfNull = optionalMessage == nullptr ? "" : optionalMessage;
+ status_t status, const char* optionalMessage = nullptr) {
+ const char* const emptyIfNull = optionalMessage == nullptr ? "" : optionalMessage;
// From binder::Status instructions:
// Prefer a generic exception code when possible, then a service specific
// code, and finally a status_t for low level failures or legacy support.
@@ -111,4 +113,26 @@
return Status::fromServiceSpecificError(status, emptyIfNull);
}
+static inline Rect fromARect(ARect rect) {
+ return Rect(rect.left, rect.top, rect.right, rect.bottom);
+}
+
+static inline ARect toARect(Rect rect) {
+ ARect aRect;
+
+ aRect.left = rect.left;
+ aRect.top = rect.top;
+ aRect.right = rect.right;
+ aRect.bottom = rect.bottom;
+ return aRect;
+}
+
+static inline ARect toARect(int32_t left, int32_t top, int32_t right, int32_t bottom) {
+ return toARect(Rect(left, top, right, bottom));
+}
+
+static inline ARect toARect(int32_t width, int32_t height) {
+ return toARect(Rect(width, height));
+}
+
} // namespace android::gui::aidl_utils
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 0e1a505..c2dcd25 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -17,9 +17,10 @@
#ifndef ANDROID_GUI_BLAST_BUFFER_QUEUE_H
#define ANDROID_GUI_BLAST_BUFFER_QUEUE_H
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
#include <gui/BufferItemConsumer.h>
-
+#include <gui/IGraphicBufferConsumer.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/SurfaceComposerClient.h>
@@ -28,7 +29,6 @@
#include <utils/RefBase.h>
#include <system/window.h>
-#include <thread>
#include <queue>
#include <com_android_graphics_libgui_flags.h>
@@ -40,12 +40,20 @@
class BLASTBufferItemConsumer : public BufferItemConsumer {
public:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ BLASTBufferItemConsumer(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+ int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
+ : BufferItemConsumer(producer, consumer, consumerUsage, bufferCount, controlledByApp),
+#else
BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
int bufferCount, bool controlledByApp, wp<BLASTBufferQueue> bbq)
: BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
mBLASTBufferQueue(std::move(bbq)),
mCurrentlyConnected(false),
- mPreviouslyConnected(false) {}
+ mPreviouslyConnected(false) {
+ }
void onDisconnect() override EXCLUDES(mMutex);
void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
@@ -131,6 +139,8 @@
virtual ~BLASTBufferQueue();
+ void onFirstRef() override;
+
private:
friend class BLASTBufferQueueHelper;
friend class BBQBufferQueueProducer;
@@ -170,8 +180,16 @@
// BufferQueue internally allows 1 more than
// the max to be acquired
- int32_t mMaxAcquiredBuffers = 1;
+ int32_t mMaxAcquiredBuffers GUARDED_BY(mMutex) = 1;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
+ int32_t mMaxDequeuedBuffers GUARDED_BY(mMutex) = 1;
+ static constexpr int32_t kMaxBufferCount = BufferQueueDefs::NUM_BUFFER_SLOTS;
+ bool mAsyncMode GUARDED_BY(mMutex) = false;
+ bool mSharedBufferMode GUARDED_BY(mMutex) = false;
+
+ int32_t mNumDequeued GUARDED_BY(mMutex) = 0;
+#endif
int32_t mNumFrameAvailable GUARDED_BY(mMutex) = 0;
int32_t mNumAcquired GUARDED_BY(mMutex) = 0;
@@ -180,9 +198,16 @@
// latch stale buffers and that we don't wait on barriers from an old producer.
uint32_t mProducerId = 0;
+ class BLASTBufferItem : public BufferItem {
+ public:
+ // True if BBQBufferQueueProducer is disconnected after the buffer is acquried but
+ // before it is released.
+ bool disconnectedAfterAcquired{false};
+ };
+
// Keep a reference to the submitted buffers so we can release when surfaceflinger drops the
// buffer or the buffer has been presented and a new buffer is ready to be presented.
- std::unordered_map<ReleaseCallbackId, BufferItem, ReleaseBufferCallbackIdHash> mSubmitted
+ std::unordered_map<ReleaseCallbackId, BLASTBufferItem, ReleaseBufferCallbackIdHash> mSubmitted
GUARDED_BY(mMutex);
// Keep a queue of the released buffers instead of immediately releasing
diff --git a/libs/gui/include/gui/BufferItemConsumer.h b/libs/gui/include/gui/BufferItemConsumer.h
index a905610..6810eda 100644
--- a/libs/gui/include/gui/BufferItemConsumer.h
+++ b/libs/gui/include/gui/BufferItemConsumer.h
@@ -17,13 +17,15 @@
#ifndef ANDROID_GUI_BUFFERITEMCONSUMER_H
#define ANDROID_GUI_BUFFERITEMCONSUMER_H
-#include <gui/ConsumerBase.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferQueue.h>
+#include <gui/ConsumerBase.h>
#define ANDROID_GRAPHICS_BUFFERITEMCONSUMER_JNI_ID "mBufferItemConsumer"
namespace android {
+class GraphicBuffer;
class String8;
/**
@@ -51,9 +53,17 @@
// access at the same time.
// controlledByApp tells whether this consumer is controlled by the
// application.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ BufferItemConsumer(uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS,
+ bool controlledByApp = false, bool isConsumerSurfaceFlinger = false);
+ BufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+ int bufferCount = DEFAULT_MAX_BUFFERS, bool controlledByApp = false)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+#else
BufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer,
uint64_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS,
bool controlledByApp = false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
~BufferItemConsumer() override;
@@ -85,7 +95,25 @@
status_t releaseBuffer(const BufferItem &item,
const sp<Fence>& releaseFence = Fence::NO_FENCE);
- private:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ status_t releaseBuffer(const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& releaseFence = Fence::NO_FENCE);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
+protected:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // This should only be used by BLASTBufferQueue:
+ BufferItemConsumer(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+ int bufferCount = DEFAULT_MAX_BUFFERS, bool controlledByApp = false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
+private:
+ void initialize(uint64_t consumerUsage, int bufferCount);
+
+ status_t releaseBufferSlotLocked(int slotIndex, const sp<GraphicBuffer>& buffer,
+ const sp<Fence>& releaseFence);
+
void freeBufferLocked(int slotIndex) override;
// mBufferFreedListener is the listener object that will be called when
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index bb52c8e..d5dd7c8 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -192,6 +192,10 @@
// callback is registered by the listener. When set to false,
// mConnectedProducerListener will not trigger onBufferReleased() callback.
bool mBufferReleasedCbEnabled;
+ // mBufferAttachedCbEnabled is used to indicate whether onBufferAttached()
+ // callback is registered by the listener. When set to false,
+ // mConnectedProducerListener will not trigger onBufferAttached() callback.
+ bool mBufferAttachedCbEnabled;
// mSlots is an array of buffer slots that must be mirrored on the producer
// side. This allows buffer ownership to be transferred between the producer
diff --git a/libs/gui/include/gui/BufferReleaseChannel.h b/libs/gui/include/gui/BufferReleaseChannel.h
new file mode 100644
index 0000000..51fe0b6
--- /dev/null
+++ b/libs/gui/include/gui/BufferReleaseChannel.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 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 <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include <binder/Parcelable.h>
+#include <gui/ITransactionCompletedListener.h>
+#include <ui/Fence.h>
+#include <utils/Errors.h>
+
+namespace android::gui {
+
+/**
+ * IPC wrapper to pass release fences from SurfaceFlinger to apps via a local unix domain socket.
+ */
+class BufferReleaseChannel {
+private:
+ class Endpoint {
+ public:
+ Endpoint(std::string name, android::base::unique_fd fd)
+ : mName(std::move(name)), mFd(std::move(fd)) {}
+ Endpoint() {}
+
+ Endpoint(Endpoint&&) noexcept = default;
+ Endpoint& operator=(Endpoint&&) noexcept = default;
+
+ Endpoint(const Endpoint&) = delete;
+ void operator=(const Endpoint&) = delete;
+
+ const android::base::unique_fd& getFd() const { return mFd; }
+
+ protected:
+ std::string mName;
+ android::base::unique_fd mFd;
+ };
+
+public:
+ class ConsumerEndpoint : public Endpoint {
+ public:
+ ConsumerEndpoint(std::string name, android::base::unique_fd fd)
+ : Endpoint(std::move(name), std::move(fd)) {}
+
+ /**
+ * Reads a release fence from the BufferReleaseChannel.
+ *
+ * Returns OK on success.
+ * Returns WOULD_BLOCK if there is no fence present.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t readReleaseFence(ReleaseCallbackId& outReleaseCallbackId,
+ sp<Fence>& outReleaseFence, uint32_t& maxAcquiredBufferCount);
+
+ private:
+ std::vector<uint8_t> mFlattenedBuffer;
+ };
+
+ class ProducerEndpoint : public Endpoint, public Parcelable {
+ public:
+ ProducerEndpoint(std::string name, android::base::unique_fd fd)
+ : Endpoint(std::move(name), std::move(fd)) {}
+ ProducerEndpoint() {}
+
+ status_t readFromParcel(const android::Parcel* parcel) override;
+ status_t writeToParcel(android::Parcel* parcel) const override;
+
+ status_t writeReleaseFence(const ReleaseCallbackId&, const sp<Fence>& releaseFence,
+ uint32_t maxAcquiredBufferCount);
+
+ private:
+ std::vector<uint8_t> mFlattenedBuffer;
+ };
+
+ /**
+ * Create two endpoints that make up the BufferReleaseChannel.
+ *
+ * Return OK on success.
+ */
+ static status_t open(const std::string name, std::unique_ptr<ConsumerEndpoint>& outConsumer,
+ std::shared_ptr<ProducerEndpoint>& outProducer);
+
+ struct Message : public Flattenable<Message> {
+ ReleaseCallbackId releaseCallbackId;
+ sp<Fence> releaseFence = Fence::NO_FENCE;
+ uint32_t maxAcquiredBufferCount;
+
+ Message() = default;
+ Message(ReleaseCallbackId releaseCallbackId, sp<Fence> releaseFence,
+ uint32_t maxAcquiredBufferCount)
+ : releaseCallbackId{releaseCallbackId},
+ releaseFence{std::move(releaseFence)},
+ maxAcquiredBufferCount{maxAcquiredBufferCount} {}
+
+ // Flattenable protocol
+ size_t getFlattenedSize() const;
+
+ size_t getFdCount() const { return releaseFence->getFdCount(); }
+
+ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+
+ status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+ private:
+ size_t getPodSize() const;
+ };
+};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index 8ff0cd0..e976aa4 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -17,18 +17,17 @@
#ifndef ANDROID_GUI_CONSUMERBASE_H
#define ANDROID_GUI_CONSUMERBASE_H
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferQueueDefs.h>
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
#include <gui/OccupancyTracker.h>
-
#include <ui/PixelFormat.h>
-
#include <utils/String8.h>
#include <utils/Vector.h>
#include <utils/threads.h>
-
namespace android {
// ----------------------------------------------------------------------------
@@ -76,12 +75,28 @@
void dumpState(String8& result) const;
void dumpState(String8& result, const char* prefix) const;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // Returns a Surface that can be used as the producer for this consumer.
+ sp<Surface> getSurface() const;
+
+ // DEPRECATED, DO NOT USE. Returns the underlying IGraphicBufferConsumer
+ // that backs this ConsumerBase.
+ sp<IGraphicBufferConsumer> getIGraphicBufferConsumer() const
+ __attribute((deprecated("DO NOT USE: Temporary hack for refactoring")));
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
// setFrameAvailableListener sets the listener object that will be notified
// when a new frame becomes available.
void setFrameAvailableListener(const wp<FrameAvailableListener>& listener);
// See IGraphicBufferConsumer::detachBuffer
- status_t detachBuffer(int slot);
+ status_t detachBuffer(int slot) __attribute((
+ deprecated("Please use the GraphicBuffer variant--slots are deprecated.")));
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ // See IGraphicBufferConsumer::detachBuffer
+ status_t detachBuffer(const sp<GraphicBuffer>& buffer);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
// See IGraphicBufferConsumer::setDefaultBufferSize
status_t setDefaultBufferSize(uint32_t width, uint32_t height);
@@ -98,9 +113,18 @@
// See IGraphicBufferConsumer::setTransformHint
status_t setTransformHint(uint32_t hint);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // See IGraphicBufferConsumer::setMaxBufferCount
+ status_t setMaxBufferCount(int bufferCount);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
// See IGraphicBufferConsumer::setMaxAcquiredBufferCount
status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ status_t setConsumerIsProtected(bool isProtected);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
// See IGraphicBufferConsumer::getSidebandStream
sp<NativeHandle> getSidebandStream() const;
@@ -115,12 +139,24 @@
ConsumerBase(const ConsumerBase&);
void operator=(const ConsumerBase&);
+ void initialize(bool controlledByApp);
+
protected:
// ConsumerBase constructs a new ConsumerBase object to consume image
// buffers from the given IGraphicBufferConsumer.
// The controlledByApp flag indicates that this consumer is under the application's
// control.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ explicit ConsumerBase(bool controlledByApp = false, bool consumerIsSurfaceFlinger = false);
+ explicit ConsumerBase(const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false);
+
+ explicit ConsumerBase(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false)
+ __attribute((deprecated("ConsumerBase should own its own producer, and constructing it "
+ "without one is fragile! This method is going away soon.")));
+#else
explicit ConsumerBase(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
// onLastStrongRef gets called by RefBase just before the dtor of the most
// derived class. It is used to clean up the buffers so that ConsumerBase
@@ -150,6 +186,10 @@
virtual void onBuffersReleased() override;
virtual void onSidebandStreamChanged() override;
+ virtual int getSlotForBufferLocked(const sp<GraphicBuffer>& buffer);
+
+ virtual status_t detachBufferLocked(int slotIndex);
+
// freeBufferLocked frees up the given buffer slot. If the slot has been
// initialized this will release the reference to the GraphicBuffer in that
// slot. Otherwise it has no effect.
@@ -264,10 +304,16 @@
Mutex mFrameAvailableMutex;
wp<FrameAvailableListener> mFrameAvailableListener;
- // The ConsumerBase has-a BufferQueue and is responsible for creating this object
- // if none is supplied
+ // The ConsumerBase has-a BufferQueue and is responsible for creating these
+ // objects if not supplied.
sp<IGraphicBufferConsumer> mConsumer;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // This Surface wraps the IGraphicBufferConsumer created for this
+ // ConsumerBase.
+ sp<Surface> mSurface;
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
// The final release fence of the most recent buffer released by
// releaseBufferLocked.
sp<Fence> mPrevFinalReleaseFence;
diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h
index 806fbe8..2bba61b 100644
--- a/libs/gui/include/gui/CpuConsumer.h
+++ b/libs/gui/include/gui/CpuConsumer.h
@@ -19,8 +19,9 @@
#include <system/window.h>
-#include <gui/ConsumerBase.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferQueue.h>
+#include <gui/ConsumerBase.h>
#include <utils/Vector.h>
@@ -91,8 +92,17 @@
// Create a new CPU consumer. The maxLockedBuffers parameter specifies
// how many buffers can be locked for user access at the same time.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ CpuConsumer(size_t maxLockedBuffers, bool controlledByApp = false,
+ bool isConsumerSurfaceFlinger = false);
+
+ CpuConsumer(const sp<IGraphicBufferConsumer>& bq, size_t maxLockedBuffers,
+ bool controlledByApp = false)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+#else
CpuConsumer(const sp<IGraphicBufferConsumer>& bq,
size_t maxLockedBuffers, bool controlledByApp = false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
// Gets the next graphics buffer from the producer and locks it for CPU use,
// filling out the passed-in locked buffer structure with the native pointer
diff --git a/libs/gui/include/gui/DisplayCaptureArgs.h b/libs/gui/include/gui/DisplayCaptureArgs.h
deleted file mode 100644
index e29ce41..0000000
--- a/libs/gui/include/gui/DisplayCaptureArgs.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2022 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 <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IBinder.h>
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
-#include <gui/SpHash.h>
-#include <ui/GraphicTypes.h>
-#include <ui/PixelFormat.h>
-#include <ui/Rect.h>
-#include <unordered_set>
-
-namespace android::gui {
-
-struct CaptureArgs : public Parcelable {
- const static int32_t UNSET_UID = -1;
- virtual ~CaptureArgs() = default;
-
- ui::PixelFormat pixelFormat{ui::PixelFormat::RGBA_8888};
- Rect sourceCrop;
- float frameScaleX{1};
- float frameScaleY{1};
- bool captureSecureLayers{false};
- int32_t uid{UNSET_UID};
- // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured
- // result will be in a colorspace appropriate for capturing the display contents
- // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be
- // different from SRGB (byte per color), and failed when checking colors in tests.
- // NOTE: In normal cases, we want the screen to be captured in display's colorspace.
- ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
-
- // The receiver of the capture can handle protected buffer. A protected buffer has
- // GRALLOC_USAGE_PROTECTED usage bit and must not be accessed unprotected behaviour.
- // Any read/write access from unprotected context will result in undefined behaviour.
- // Protected contents are typically DRM contents. This has no direct implication to the
- // secure property of the surface, which is specified by the application explicitly to avoid
- // the contents being accessed/captured by screenshot or unsecure display.
- bool allowProtected = false;
-
- bool grayscale = false;
-
- std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles;
-
- // Hint that the caller will use the screenshot animation as part of a transition animation.
- // The canonical example would be screen rotation - in such a case any color shift in the
- // screenshot is a detractor so composition in the display's colorspace is required.
- // Otherwise, the system may choose a colorspace that is more appropriate for use-cases
- // such as file encoding or for blending HDR content into an ap's UI, where the display's
- // exact colorspace is not an appropriate intermediate result.
- // Note that if the caller is requesting a specific dataspace, this hint does nothing.
- bool hintForSeamlessTransition = false;
-
- virtual status_t writeToParcel(Parcel* output) const;
- virtual status_t readFromParcel(const Parcel* input);
-};
-
-struct DisplayCaptureArgs : CaptureArgs {
- sp<IBinder> displayToken;
- uint32_t width{0};
- uint32_t height{0};
-
- status_t writeToParcel(Parcel* output) const override;
- status_t readFromParcel(const Parcel* input) override;
-};
-
-}; // namespace android::gui
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
index 3d1be4d..462081b 100644
--- a/libs/gui/include/gui/FrameTimestamps.h
+++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -116,7 +116,7 @@
// Public for testing.
static nsecs_t snapToNextTick(
nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval);
- nsecs_t getReportedCompositeDeadline() const { return mCompositorTiming.deadline; };
+ nsecs_t getReportedCompositeDeadline() const { return mCompositorTiming.deadline; }
nsecs_t getNextCompositeDeadline(const nsecs_t now) const;
nsecs_t getCompositeInterval() const { return mCompositorTiming.interval; }
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h
index ba268ab..bfe3eb3 100644
--- a/libs/gui/include/gui/GLConsumer.h
+++ b/libs/gui/include/gui/GLConsumer.h
@@ -20,6 +20,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferQueueDefs.h>
#include <gui/ConsumerBase.h>
@@ -82,12 +83,25 @@
// If the constructor without the tex parameter is used, the GLConsumer is
// created in a detached state, and attachToContext must be called before
// calls to updateTexImage.
- GLConsumer(const sp<IGraphicBufferConsumer>& bq,
- uint32_t tex, uint32_t texureTarget, bool useFenceSync,
- bool isControlledByApp);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ GLConsumer(uint32_t tex, uint32_t textureTarget, bool useFenceSync, bool isControlledByApp);
- GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t texureTarget,
- bool useFenceSync, bool isControlledByApp);
+ GLConsumer(uint32_t textureTarget, bool useFenceSync, bool isControlledByApp);
+
+ GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget,
+ bool useFenceSync, bool isControlledByApp)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+
+ GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync,
+ bool isControlledByApp)
+ __attribute((deprecated("Prefer ctors that create their own surface and consumer.")));
+#else
+ GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget,
+ bool useFenceSync, bool isControlledByApp);
+
+ GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync,
+ bool isControlledByApp);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
// updateTexImage acquires the most recently queued buffer, and sets the
// image contents of the target texture to it.
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 8fca946..3aac457 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -867,6 +867,6 @@
#endif
// ----------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_GUI_IGRAPHICBUFFERPRODUCER_H
diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h
index b15f501..3dcc6b6 100644
--- a/libs/gui/include/gui/IProducerListener.h
+++ b/libs/gui/include/gui/IProducerListener.h
@@ -25,6 +25,8 @@
#include <hidl/HybridInterface.h>
#include <utils/RefBase.h>
+#include <com_android_graphics_libgui_flags.h>
+
namespace android {
// ProducerListener is the interface through which the BufferQueue notifies the
@@ -55,6 +57,16 @@
// This is called without any lock held and can be called concurrently by
// multiple threads.
virtual void onBufferDetached(int /*slot*/) {} // Asynchronous
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+ // onBufferAttached is called from IGraphicBufferConsumer::attachBuffer to
+ // notify the producer that a buffer is attached.
+ //
+ // This is called without any lock held and can be called concurrently by
+ // multiple threads. This callback is enabled only when needsAttachNotify()
+ // returns {@code true}.
+ virtual void onBufferAttached() {} // Asynchronous
+ virtual bool needsAttachNotify() { return false; }
+#endif
};
#ifndef NO_BINDER
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index eb4a802..9a422fd 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -18,6 +18,7 @@
#include <android/gui/CachingHint.h>
#include <android/gui/DisplayBrightness.h>
+#include <android/gui/DisplayCaptureArgs.h>
#include <android/gui/FrameTimelineInfo.h>
#include <android/gui/IDisplayEventConnection.h>
#include <android/gui/IFpsListener.h>
@@ -27,6 +28,7 @@
#include <android/gui/ITunnelModeEnabledListener.h>
#include <android/gui/IWindowInfosListener.h>
#include <android/gui/IWindowInfosPublisher.h>
+#include <android/gui/LayerCaptureArgs.h>
#include <binder/IBinder.h>
#include <binder/IInterface.h>
#include <gui/ITransactionCompletedListener.h>
@@ -70,13 +72,6 @@
using gui::IScreenCaptureListener;
using gui::SpHash;
-namespace gui {
-
-struct DisplayCaptureArgs;
-struct LayerCaptureArgs;
-
-} // namespace gui
-
namespace ui {
struct DisplayMode;
@@ -112,7 +107,7 @@
/* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
virtual status_t setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffer,
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
diff --git a/libs/gui/include/gui/LayerCaptureArgs.h b/libs/gui/include/gui/LayerCaptureArgs.h
deleted file mode 100644
index fae2bcc..0000000
--- a/libs/gui/include/gui/LayerCaptureArgs.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2022 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 <stdint.h>
-#include <sys/types.h>
-
-#include <gui/DisplayCaptureArgs.h>
-
-namespace android::gui {
-
-struct LayerCaptureArgs : CaptureArgs {
- sp<IBinder> layerHandle;
- bool childrenOnly{false};
-
- status_t writeToParcel(Parcel* output) const override;
- status_t readFromParcel(const Parcel* input) override;
-};
-
-}; // namespace android::gui
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 3fb1894..2cdde32 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -21,7 +21,9 @@
#include <stdint.h>
#include <sys/types.h>
+#include <android/gui/DisplayCaptureArgs.h>
#include <android/gui/IWindowInfosReportedListener.h>
+#include <android/gui/LayerCaptureArgs.h>
#include <android/gui/TrustedPresentationThresholds.h>
#include <android/native_window.h>
#include <gui/IGraphicBufferProducer.h>
@@ -34,9 +36,8 @@
#include <android/gui/TrustedOverlay.h>
#include <ftl/flags.h>
-#include <gui/DisplayCaptureArgs.h>
+#include <gui/BufferReleaseChannel.h>
#include <gui/ISurfaceComposer.h>
-#include <gui/LayerCaptureArgs.h>
#include <gui/LayerMetadata.h>
#include <gui/SpHash.h>
#include <gui/SurfaceControl.h>
@@ -220,6 +221,7 @@
eDropInputModeChanged = 0x8000'00000000,
eExtendedRangeBrightnessChanged = 0x10000'00000000,
eEdgeExtensionChanged = 0x20000'00000000,
+ eBufferReleaseChannelChanged = 0x40000'00000000,
};
layer_state_t();
@@ -412,6 +414,8 @@
TrustedPresentationThresholds trustedPresentationThresholds;
TrustedPresentationListener trustedPresentationListener;
+
+ std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel;
};
class ComposerState {
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 1245501..0f51f2d 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -18,6 +18,7 @@
#define ANDROID_GUI_SURFACE_H
#include <android/gui/FrameTimelineInfo.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferQueueDefs.h>
#include <gui/HdrMetadata.h>
#include <gui/IGraphicBufferProducer.h>
@@ -33,8 +34,12 @@
#include <shared_mutex>
#include <unordered_set>
+#include <com_android_graphics_libgui_flags.h>
+
namespace android {
+class GraphicBuffer;
+
namespace gui {
class ISurfaceComposer;
} // namespace gui
@@ -57,6 +62,10 @@
virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers) = 0;
virtual void onBufferDetached(int slot) = 0;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+ virtual void onBufferAttached() {}
+ virtual bool needsAttachNotify() { return false; }
+#endif
};
class StubSurfaceListener : public SurfaceListener {
@@ -164,6 +173,11 @@
*/
virtual void allocateBuffers();
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ // See IGraphicBufferProducer::allowAllocation
+ status_t allowAllocation(bool allowAllocation);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
/* Sets the generation number on the IGraphicBufferProducer and updates the
* generation number on any buffers attached to the Surface after this call.
* See IGBP::setGenerationNumber for more information. */
@@ -180,6 +194,14 @@
* in <system/window.h>. */
int setScalingMode(int mode);
+ virtual int setBuffersTimestamp(int64_t timestamp);
+ virtual int setBuffersDataSpace(ui::Dataspace dataSpace);
+ virtual int setCrop(Rect const* rect);
+ virtual int setBuffersTransform(uint32_t transform);
+ virtual int setBuffersStickyTransform(uint32_t transform);
+ virtual int setBuffersFormat(PixelFormat format);
+ virtual int setUsage(uint64_t reqUsage);
+
// See IGraphicBufferProducer::setDequeueTimeout
status_t setDequeueTimeout(nsecs_t timeout);
@@ -340,16 +362,9 @@
virtual int connect(int api);
virtual int setBufferCount(int bufferCount);
virtual int setBuffersUserDimensions(uint32_t width, uint32_t height);
- virtual int setBuffersFormat(PixelFormat format);
- virtual int setBuffersTransform(uint32_t transform);
- virtual int setBuffersStickyTransform(uint32_t transform);
- virtual int setBuffersTimestamp(int64_t timestamp);
- virtual int setBuffersDataSpace(ui::Dataspace dataSpace);
virtual int setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata);
virtual int setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata);
virtual int setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata);
- virtual int setCrop(Rect const* rect);
- virtual int setUsage(uint64_t reqUsage);
virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects);
public:
@@ -367,20 +382,15 @@
virtual int unlockAndPost();
virtual int query(int what, int* value) const;
- virtual int connect(int api, const sp<SurfaceListener>& listener);
-
// When reportBufferRemoval is true, clients must call getAndFlushRemovedBuffers to fetch
// GraphicBuffers removed from this surface after a dequeueBuffer, detachNextBuffer or
// attachBuffer call. This allows clients with their own buffer caches to free up buffers no
// longer in use by this surface.
- virtual int connect(int api, const sp<SurfaceListener>& listener, bool reportBufferRemoval);
- virtual int detachNextBuffer(sp<GraphicBuffer>* outBuffer,
- sp<Fence>* outFence);
+ virtual int connect(int api, const sp<SurfaceListener>& listener,
+ bool reportBufferRemoval = false);
+ virtual int detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence);
virtual int attachBuffer(ANativeWindowBuffer*);
- virtual int connect(
- int api, bool reportBufferRemoval,
- const sp<SurfaceListener>& sListener);
virtual void destroy();
// When client connects to Surface with reportBufferRemoval set to true, any buffers removed
@@ -395,6 +405,20 @@
static status_t attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer,
ui::Dataspace dataspace);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+ // Dequeues a buffer and its outFence, which must be signalled before the buffer can be used.
+ status_t dequeueBuffer(sp<GraphicBuffer>* buffer, sp<Fence>* outFence);
+
+ // Queues a buffer, with an optional fd fence that captures pending work on the buffer. This
+ // buffer must have been returned by dequeueBuffer or associated with this Surface via an
+ // attachBuffer operation.
+ status_t queueBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& fd = Fence::NO_FENCE);
+
+ // Detaches this buffer, dissociating it from this Surface. This buffer must have been returned
+ // by queueBuffer or associated with this Surface via an attachBuffer operation.
+ status_t detachBuffer(const sp<GraphicBuffer>& buffer);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
// Batch version of dequeueBuffer, cancelBuffer and queueBuffer
// Note that these batched operations are not supported when shared buffer mode is being used.
struct BatchBuffer {
@@ -433,6 +457,15 @@
virtual void onBufferDetached(int slot) { mSurfaceListener->onBufferDetached(slot); }
virtual void onBuffersDiscarded(const std::vector<int32_t>& slots);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+ virtual void onBufferAttached() {
+ mSurfaceListener->onBufferAttached();
+ }
+
+ virtual bool needsAttachNotify() {
+ return mSurfaceListener->needsAttachNotify();
+ }
+#endif
private:
wp<Surface> mParent;
sp<SurfaceListener> mSurfaceListener;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 95574ee..4f9af16 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -45,6 +45,7 @@
#include <android/gui/BnJankListener.h>
#include <android/gui/ISurfaceComposerClient.h>
+#include <gui/BufferReleaseChannel.h>
#include <gui/CpuConsumer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ITransactionCompletedListener.h>
@@ -451,7 +452,6 @@
uint64_t mId;
- uint32_t mTransactionNestCount = 0;
bool mAnimation = false;
bool mEarlyWakeupStart = false;
bool mEarlyWakeupEnd = false;
@@ -763,6 +763,10 @@
const Rect& destinationFrame);
Transaction& setDropInputMode(const sp<SurfaceControl>& sc, gui::DropInputMode mode);
+ Transaction& setBufferReleaseChannel(
+ const sp<SurfaceControl>& sc,
+ const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel);
+
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h
index b7aba2b..7ddac81 100644
--- a/libs/gui/include/gui/view/Surface.h
+++ b/libs/gui/include/gui/view/Surface.h
@@ -59,8 +59,9 @@
// of the full parceling to happen on its native side.
status_t readFromParcel(const Parcel* parcel, bool nameAlreadyRead);
- private:
+ std::string toString() const;
+private:
static String16 readMaybeEmptyString16(const Parcel* parcel);
};
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index 74d806e..c367e75 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -10,6 +10,14 @@
} # bq_setframerate
flag {
+ name: "bq_consumer_attach_callback"
+ namespace: "core_graphics"
+ description: "Controls IProducerListener to have consumer side attach callback"
+ bug: "353202582"
+ is_fixed_read_only: true
+} # bq_consumer_attach_callback
+
+flag {
name: "frametimestamps_previousrelease"
namespace: "core_graphics"
description: "Controls a fence fixup for timestamp apis"
@@ -37,6 +45,14 @@
} # trace_frame_rate_override
flag {
+ name: "wb_consumer_base_owns_bq"
+ namespace: "core_graphics"
+ description: "ConsumerBase-based classes now own their own bufferqueue"
+ bug: "340933754"
+ is_fixed_read_only: true
+} # wb_consumer_base_owns_bq
+
+flag {
name: "wb_platform_api_improvements"
namespace: "core_graphics"
description: "Simple improvements to Surface and ConsumerBase classes"
@@ -45,9 +61,33 @@
} # wb_platform_api_improvements
flag {
+ name: "wb_stream_splitter"
+ namespace: "core_graphics"
+ description: "Removes IGBP/IGBCs from Camera3StreamSplitter as part of BufferQueue refactors"
+ bug: "340933206"
+ is_fixed_read_only: true
+} # wb_stream_splitter
+
+flag {
name: "edge_extension_shader"
namespace: "windowing_frontend"
description: "Enable edge extension via shader"
bug: "322036393"
is_fixed_read_only: true
} # edge_extension_shader
+
+flag {
+ name: "buffer_release_channel"
+ namespace: "window_surfaces"
+ description: "Enable BufferReleaseChannel to optimize buffer releases"
+ bug: "294133380"
+ is_fixed_read_only: true
+} # buffer_release_channel
+
+flag {
+ name: "wb_ring_buffer"
+ namespace: "core_graphics"
+ description: "Remove slot dependency in the Ring Buffer Consumer."
+ bug: "342197847"
+ is_fixed_read_only: true
+} # wb_ring_buffer
diff --git a/libs/gui/rust/aidl_types/src/lib.rs b/libs/gui/rust/aidl_types/src/lib.rs
index fead018..2351df0 100644
--- a/libs/gui/rust/aidl_types/src/lib.rs
+++ b/libs/gui/rust/aidl_types/src/lib.rs
@@ -42,10 +42,7 @@
}
stub_unstructured_parcelable!(BitTube);
-stub_unstructured_parcelable!(CaptureArgs);
-stub_unstructured_parcelable!(DisplayCaptureArgs);
stub_unstructured_parcelable!(DisplayInfo);
-stub_unstructured_parcelable!(LayerCaptureArgs);
stub_unstructured_parcelable!(LayerDebugInfo);
stub_unstructured_parcelable!(LayerMetadata);
stub_unstructured_parcelable!(ParcelableVsyncEventData);
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index ea8acbb..f07747f 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -12,6 +12,34 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+aidl_interface {
+ name: "libgui_test_server_aidl",
+ unstable: true,
+ srcs: ["testserver/aidl/**/*.aidl"],
+ local_include_dir: "testserver/aidl",
+ include_dirs: [
+ "frameworks/native/aidl/gui",
+ ],
+ backend: {
+ cpp: {
+ enabled: true,
+ additional_shared_libraries: [
+ "libgui",
+ "libui",
+ ],
+ },
+ java: {
+ enabled: false,
+ },
+ ndk: {
+ enabled: false,
+ },
+ rust: {
+ enabled: false,
+ },
+ },
+}
+
cc_test {
name: "libgui_test",
test_suites: ["device-tests"],
@@ -25,34 +53,41 @@
"-Wthread-safety",
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_SETFRAMERATE=true",
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_EXTENDEDALLOCATE=true",
+ "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_CONSUMER_BASE_OWNS_BQ=true",
+ "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_PLATFORM_API_IMPROVEMENTS=true",
],
srcs: [
- "LibGuiMain.cpp", // Custom gtest entrypoint
"BLASTBufferQueue_test.cpp",
"BufferItemConsumer_test.cpp",
"BufferQueue_test.cpp",
+ "BufferReleaseChannel_test.cpp",
"Choreographer_test.cpp",
"CompositorTiming_test.cpp",
"CpuConsumer_test.cpp",
- "EndToEndNativeInputTest.cpp",
- "FrameRateUtilsTest.cpp",
- "DisplayInfo_test.cpp",
"DisplayedContentSampling_test.cpp",
+ "DisplayInfo_test.cpp",
+ "EndToEndNativeInputTest.cpp",
"FillBuffer.cpp",
+ "FrameRateUtilsTest.cpp",
"GLTest.cpp",
"IGraphicBufferProducer_test.cpp",
+ "LibGuiMain.cpp", // Custom gtest entrypoint
"Malicious.cpp",
"MultiTextureConsumer_test.cpp",
"RegionSampling_test.cpp",
"StreamSplitter_test.cpp",
+ "Surface_test.cpp",
"SurfaceTextureClient_test.cpp",
"SurfaceTextureFBO_test.cpp",
+ "SurfaceTextureGL_test.cpp",
"SurfaceTextureGLThreadToGL_test.cpp",
"SurfaceTextureGLToGL_test.cpp",
- "SurfaceTextureGL_test.cpp",
"SurfaceTextureMultiContextGL_test.cpp",
- "Surface_test.cpp",
+ "TestServer_test.cpp",
+ "testserver/TestServer.cpp",
+ "testserver/TestServerClient.cpp",
+ "testserver/TestServerHost.cpp",
"TextureRenderer.cpp",
"VsyncEventData_test.cpp",
"WindowInfo_test.cpp",
@@ -63,10 +98,17 @@
"android.hardware.configstore-utils",
"libSurfaceFlingerProp",
"libGLESv1_CM",
+ "libgui_test_server_aidl-cpp",
"libinput",
"libnativedisplay",
],
+ // This needs to get copied over for the test since it's not part of the
+ // platform.
+ data_libs: [
+ "libgui_test_server_aidl-cpp",
+ ],
+
static_libs: [
"libgmock",
],
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 6852589..eb2a61d 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -20,7 +20,7 @@
#include <android-base/thread_annotations.h>
#include <android/hardware/graphics/common/1.2/types.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/BufferQueueCore.h>
#include <gui/BufferQueueProducer.h>
#include <gui/FrameTimestamps.h>
@@ -229,7 +229,8 @@
ISurfaceComposerClient::eFXSurfaceBufferState,
/*parent*/ mRootSurfaceControl->getHandle());
- mCaptureArgs.sourceCrop = Rect(ui::Size(mDisplayWidth, mDisplayHeight));
+ mCaptureArgs.captureArgs.sourceCrop =
+ gui::aidl_utils::toARect(mDisplayWidth, mDisplayHeight);
mCaptureArgs.layerHandle = mRootSurfaceControl->getHandle();
}
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index 6880678..845a1ca 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -17,10 +17,12 @@
#define LOG_TAG "BufferItemConsumer_test"
//#define LOG_NDEBUG 0
+#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <gui/BufferItemConsumer.h>
#include <gui/IProducerListener.h>
#include <gui/Surface.h>
+#include <ui/GraphicBuffer.h>
namespace android {
@@ -42,16 +44,26 @@
BufferItemConsumerTest* mTest;
};
+ struct TrackingProducerListener : public BnProducerListener {
+ TrackingProducerListener(BufferItemConsumerTest* test) : mTest(test) {}
+
+ virtual void onBufferReleased() override {}
+ virtual bool needsReleaseNotify() override { return true; }
+ virtual void onBuffersDiscarded(const std::vector<int32_t>&) override {}
+ virtual void onBufferDetached(int slot) override { mTest->HandleBufferDetached(slot); }
+
+ BufferItemConsumerTest* mTest;
+ };
+
void SetUp() override {
- BufferQueue::createBufferQueue(&mProducer, &mConsumer);
- mBIC =
- new BufferItemConsumer(mConsumer, kFormat, kMaxLockedBuffers, true);
+ mBIC = new BufferItemConsumer(kFormat, kMaxLockedBuffers, true);
String8 name("BufferItemConsumer_Under_Test");
mBIC->setName(name);
mBFL = new BufferFreedListener(this);
mBIC->setBufferFreedListener(mBFL);
- sp<IProducerListener> producerListener = new StubProducerListener();
+ sp<IProducerListener> producerListener = new TrackingProducerListener(this);
+ mProducer = mBIC->getSurface()->getIGraphicBufferProducer();
IGraphicBufferProducer::QueueBufferOutput bufferOutput;
ASSERT_EQ(NO_ERROR,
mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU,
@@ -71,6 +83,13 @@
ALOGD("HandleBufferFreed, mFreedBufferCount=%d", mFreedBufferCount);
}
+ void HandleBufferDetached(int slot) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mDetachedBufferSlots.push_back(slot);
+ ALOGD("HandleBufferDetached, slot=%d mDetachedBufferSlots-count=%zu", slot,
+ mDetachedBufferSlots.size());
+ }
+
void DequeueBuffer(int* outSlot) {
ASSERT_NE(outSlot, nullptr);
@@ -120,6 +139,7 @@
std::mutex mMutex;
int mFreedBufferCount{0};
+ std::vector<int> mDetachedBufferSlots = {};
sp<BufferItemConsumer> mBIC;
sp<BufferFreedListener> mBFL;
@@ -203,4 +223,19 @@
ASSERT_EQ(1, GetFreedBufferCount());
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+// Test that delete BufferItemConsumer triggers onBufferFreed.
+TEST_F(BufferItemConsumerTest, DetachBufferWithBuffer) {
+ int slot;
+ // Let buffer go through the cycle at least once.
+ DequeueBuffer(&slot);
+ QueueBuffer(slot);
+ AcquireBuffer(&slot);
+
+ sp<GraphicBuffer> buffer = mBuffers[slot];
+ EXPECT_EQ(OK, mBIC->detachBuffer(buffer));
+ EXPECT_THAT(mDetachedBufferSlots, testing::ElementsAre(slot));
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
} // namespace android
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 272c5ed..2e6ffcb 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -1262,6 +1262,91 @@
ASSERT_TRUE(result == WOULD_BLOCK || result == TIMED_OUT || result == INVALID_OPERATION);
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+struct BufferAttachedListener : public BnProducerListener {
+public:
+ BufferAttachedListener(bool enable) : mEnabled(enable), mAttached(0) {}
+ virtual ~BufferAttachedListener() = default;
+
+ virtual void onBufferReleased() {}
+ virtual bool needsReleaseNotify() { return true; }
+ virtual void onBufferAttached() {
+ ++mAttached;
+ }
+ virtual bool needsAttachNotify() { return mEnabled; }
+
+ int getNumAttached() const { return mAttached; }
+private:
+ const bool mEnabled;
+ int mAttached;
+};
+
+TEST_F(BufferQueueTest, TestConsumerAttachProducerListener) {
+ createBufferQueue();
+ sp<MockConsumer> mc1(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc1, true));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ // Do not enable attach callback.
+ sp<BufferAttachedListener> pl1(new BufferAttachedListener(false));
+ ASSERT_EQ(OK, mProducer->connect(pl1, NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK, mProducer->setDequeueTimeout(0));
+ ASSERT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
+
+ sp<Fence> fence = Fence::NO_FENCE;
+ sp<GraphicBuffer> buffer = nullptr;
+
+ int slot;
+ status_t result = OK;
+
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(1));
+
+ result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
+ GRALLOC_USAGE_SW_READ_RARELY, nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->detachBuffer(slot));
+
+ // Check # of attach is zero.
+ ASSERT_EQ(0, pl1->getNumAttached());
+
+ // Attach a buffer and check the callback was not called.
+ ASSERT_EQ(OK, mConsumer->attachBuffer(&slot, buffer));
+ ASSERT_EQ(0, pl1->getNumAttached());
+
+ mProducer = nullptr;
+ mConsumer = nullptr;
+ createBufferQueue();
+
+ sp<MockConsumer> mc2(new MockConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(mc2, true));
+ // Enable attach callback.
+ sp<BufferAttachedListener> pl2(new BufferAttachedListener(true));
+ ASSERT_EQ(OK, mProducer->connect(pl2, NATIVE_WINDOW_API_CPU, true, &output));
+ ASSERT_EQ(OK, mProducer->setDequeueTimeout(0));
+ ASSERT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1));
+
+ fence = Fence::NO_FENCE;
+ buffer = nullptr;
+
+ result = OK;
+
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(1));
+
+ result = mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0,
+ GRALLOC_USAGE_SW_READ_RARELY, nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, result);
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->detachBuffer(slot));
+
+ // Check # of attach is zero.
+ ASSERT_EQ(0, pl2->getNumAttached());
+
+ // Attach a buffer and check the callback was called.
+ ASSERT_EQ(OK, mConsumer->attachBuffer(&slot, buffer));
+ ASSERT_EQ(1, pl2->getNumAttached());
+}
+#endif
+
TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) {
createBufferQueue();
sp<MockConsumer> mc(new MockConsumer);
@@ -1345,19 +1430,15 @@
}
struct BufferItemConsumerSetFrameRateListener : public BufferItemConsumer {
- BufferItemConsumerSetFrameRateListener(const sp<IGraphicBufferConsumer>& consumer)
- : BufferItemConsumer(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 1) {}
+ BufferItemConsumerSetFrameRateListener() : BufferItemConsumer(GRALLOC_USAGE_SW_READ_OFTEN, 1) {}
MOCK_METHOD(void, onSetFrameRate, (float, int8_t, int8_t), (override));
};
TEST_F(BufferQueueTest, TestSetFrameRate) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
-
sp<BufferItemConsumerSetFrameRateListener> bufferConsumer =
- sp<BufferItemConsumerSetFrameRateListener>::make(consumer);
+ sp<BufferItemConsumerSetFrameRateListener>::make();
+ sp<IGraphicBufferProducer> producer = bufferConsumer->getSurface()->getIGraphicBufferProducer();
EXPECT_CALL(*bufferConsumer, onSetFrameRate(12.34f, 1, 0)).Times(1);
producer->setFrameRate(12.34f, 1, 0);
@@ -1408,14 +1489,10 @@
// See b/270004534
TEST(BufferQueueThreading, TestProducerDequeueConsumerDestroy) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
-
sp<BufferItemConsumer> bufferConsumer =
- sp<BufferItemConsumer>::make(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 2);
+ sp<BufferItemConsumer>::make(GRALLOC_USAGE_SW_READ_OFTEN, 2);
ASSERT_NE(nullptr, bufferConsumer.get());
- sp<Surface> surface = sp<Surface>::make(producer);
+ sp<Surface> surface = bufferConsumer->getSurface();
native_window_set_buffers_format(surface.get(), PIXEL_FORMAT_RGBA_8888);
native_window_set_buffers_dimensions(surface.get(), 100, 100);
@@ -1446,14 +1523,10 @@
}
TEST_F(BufferQueueTest, TestAdditionalOptions) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
-
sp<BufferItemConsumer> bufferConsumer =
- sp<BufferItemConsumer>::make(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 2);
+ sp<BufferItemConsumer>::make(GRALLOC_USAGE_SW_READ_OFTEN, 2);
ASSERT_NE(nullptr, bufferConsumer.get());
- sp<Surface> surface = sp<Surface>::make(producer);
+ sp<Surface> surface = bufferConsumer->getSurface();
native_window_set_buffers_format(surface.get(), PIXEL_FORMAT_RGBA_8888);
native_window_set_buffers_dimensions(surface.get(), 100, 100);
diff --git a/libs/gui/tests/BufferReleaseChannel_test.cpp b/libs/gui/tests/BufferReleaseChannel_test.cpp
new file mode 100644
index 0000000..11d122b
--- /dev/null
+++ b/libs/gui/tests/BufferReleaseChannel_test.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 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 <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <gui/BufferReleaseChannel.h>
+
+using namespace std::string_literals;
+using android::gui::BufferReleaseChannel;
+
+namespace android {
+
+namespace {
+
+// Helper function to check if two file descriptors point to the same file.
+bool is_same_file(int fd1, int fd2) {
+ struct stat stat1;
+ if (fstat(fd1, &stat1) != 0) {
+ return false;
+ }
+ struct stat stat2;
+ if (fstat(fd2, &stat2) != 0) {
+ return false;
+ }
+ return (stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino);
+}
+
+} // namespace
+
+TEST(BufferReleaseChannelTest, MessageFlattenable) {
+ ReleaseCallbackId releaseCallbackId{1, 2};
+ sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0));
+ uint32_t maxAcquiredBufferCount = 5;
+
+ std::vector<uint8_t> dataBuffer;
+ std::vector<int> fdBuffer;
+
+ // Verify that we can flatten a message
+ {
+ BufferReleaseChannel::Message message{releaseCallbackId, releaseFence,
+ maxAcquiredBufferCount};
+
+ dataBuffer.resize(message.getFlattenedSize());
+ void* dataPtr = dataBuffer.data();
+ size_t dataSize = dataBuffer.size();
+
+ fdBuffer.resize(message.getFdCount());
+ int* fdPtr = fdBuffer.data();
+ size_t fdSize = fdBuffer.size();
+
+ ASSERT_EQ(OK, message.flatten(dataPtr, dataSize, fdPtr, fdSize));
+
+ // Fence's unique_fd uses fdsan to check ownership of the file descriptor. Normally the file
+ // descriptor is passed through the Unix socket and duplicated (and sent to another process)
+ // so there's no problem with duplicate file descriptor ownership. For this unit test, we
+ // need to set up a duplicate file descriptor to avoid crashing due to duplicate ownership.
+ ASSERT_EQ(releaseFence->get(), fdBuffer[0]);
+ fdBuffer[0] = message.releaseFence->dup();
+ }
+
+ // Verify that we can unflatten a message
+ {
+ BufferReleaseChannel::Message message;
+
+ const void* dataPtr = dataBuffer.data();
+ size_t dataSize = dataBuffer.size();
+
+ const int* fdPtr = fdBuffer.data();
+ size_t fdSize = fdBuffer.size();
+
+ ASSERT_EQ(OK, message.unflatten(dataPtr, dataSize, fdPtr, fdSize));
+ ASSERT_EQ(releaseCallbackId, message.releaseCallbackId);
+ ASSERT_TRUE(is_same_file(releaseFence->get(), message.releaseFence->get()));
+ ASSERT_EQ(maxAcquiredBufferCount, message.maxAcquiredBufferCount);
+ }
+}
+
+// Verify that the BufferReleaseChannel consume returns WOULD_BLOCK when there's no message
+// available.
+TEST(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) {
+ std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer;
+ std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer;
+ ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer));
+
+ ReleaseCallbackId releaseCallbackId;
+ sp<Fence> releaseFence;
+ uint32_t maxAcquiredBufferCount;
+ ASSERT_EQ(WOULD_BLOCK,
+ consumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount));
+}
+
+// Verify that we can write a message to the BufferReleaseChannel producer and read that message
+// using the BufferReleaseChannel consumer.
+TEST(BufferReleaseChannelTest, ProduceAndConsume) {
+ std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer;
+ std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer;
+ ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer));
+
+ sp<Fence> fence = sp<Fence>::make(memfd_create("fake-fence-fd", 0));
+
+ for (uint64_t i = 0; i < 64; i++) {
+ ReleaseCallbackId producerId{i, i + 1};
+ uint32_t maxAcquiredBufferCount = i + 2;
+ ASSERT_EQ(OK, producer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount));
+ }
+
+ for (uint64_t i = 0; i < 64; i++) {
+ ReleaseCallbackId expectedId{i, i + 1};
+ uint32_t expectedMaxAcquiredBufferCount = i + 2;
+
+ ReleaseCallbackId consumerId;
+ sp<Fence> consumerFence;
+ uint32_t maxAcquiredBufferCount;
+ ASSERT_EQ(OK,
+ consumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount));
+
+ ASSERT_EQ(expectedId, consumerId);
+ ASSERT_TRUE(is_same_file(fence->get(), consumerFence->get()));
+ ASSERT_EQ(expectedMaxAcquiredBufferCount, maxAcquiredBufferCount);
+ }
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp
index d80bd9c..f4239cb 100644
--- a/libs/gui/tests/CpuConsumer_test.cpp
+++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -66,13 +66,10 @@
test_info->name(),
params.width, params.height,
params.maxLockedBuffers, params.format);
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- mCC = new CpuConsumer(consumer, params.maxLockedBuffers);
+ mCC = new CpuConsumer(params.maxLockedBuffers);
String8 name("CpuConsumer_Under_Test");
mCC->setName(name);
- mSTC = new Surface(producer);
+ mSTC = mCC->getSurface();
mANW = mSTC;
}
diff --git a/libs/gui/tests/LibGuiMain.cpp b/libs/gui/tests/LibGuiMain.cpp
index 10f7207..7c7c2cc 100644
--- a/libs/gui/tests/LibGuiMain.cpp
+++ b/libs/gui/tests/LibGuiMain.cpp
@@ -14,8 +14,15 @@
* limitations under the License.
*/
-#include "gtest/gtest.h"
-#include "log/log.h"
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+#include "testserver/TestServer.h"
+#include "testserver/TestServerClient.h"
+#include "testserver/TestServerHost.h"
+
+using namespace android;
namespace {
@@ -32,7 +39,34 @@
} // namespace
int main(int argc, char** argv) {
+ // There are three modes that we can run in to support the libgui TestServer:
+ //
+ // - libgui_test : normal mode, runs tests and fork/execs the testserver host process
+ // - libgui_test --test-server-host $recvPipeFd $sendPipeFd : TestServerHost mode, listens on
+ // $recvPipeFd for commands and sends responses over $sendPipeFd
+ // - libgui_test --test-server $name : TestServer mode, starts a ITestService binder service
+ // under $name
+ for (int i = 1; i < argc; i++) {
+ std::string arg = argv[i];
+ if (arg == "--test-server-host") {
+ LOG_ALWAYS_FATAL_IF(argc < (i + 2), "--test-server-host requires two pipe fds");
+ // Note that the send/recv are from our perspective.
+ base::unique_fd recvPipeFd = base::unique_fd(atoi(argv[i + 1]));
+ base::unique_fd sendPipeFd = base::unique_fd(atoi(argv[i + 2]));
+ return TestServerHostMain(argv[0], std::move(sendPipeFd), std::move(recvPipeFd));
+ }
+ if (arg == "--test-server") {
+ LOG_ALWAYS_FATAL_IF(argc < (i + 1), "--test-server requires a name");
+ return TestServerMain(argv[i + 1]);
+ }
+ }
testing::InitGoogleTest(&argc, argv);
testing::UnitTest::GetInstance()->listeners().Append(new TestCaseLogger());
+
+ // This has to be run *before* any test initialization, because it fork/execs a TestServerHost,
+ // which will later create new binder service. You can't do that in a forked thread after you've
+ // initialized any binder stuff, which some tests do.
+ TestServerClient::InitializeOrDie(argv[0]);
+
return RUN_ALL_TESTS();
}
\ No newline at end of file
diff --git a/libs/gui/tests/MultiTextureConsumer_test.cpp b/libs/gui/tests/MultiTextureConsumer_test.cpp
index 7d3d4aa..2428bb3 100644
--- a/libs/gui/tests/MultiTextureConsumer_test.cpp
+++ b/libs/gui/tests/MultiTextureConsumer_test.cpp
@@ -34,12 +34,8 @@
virtual void SetUp() {
GLTest::SetUp();
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- mGlConsumer = new GLConsumer(consumer, TEX_ID,
- GLConsumer::TEXTURE_EXTERNAL, true, false);
- mSurface = new Surface(producer);
+ mGlConsumer = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
+ mSurface = mGlConsumer->getSurface();
mANW = mSurface.get();
}
diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp
index 223e4b6..a0d8c53 100644
--- a/libs/gui/tests/RegionSampling_test.cpp
+++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -19,7 +19,7 @@
#include <android/gui/BnRegionSamplingListener.h>
#include <binder/ProcessState.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/DisplayEventReceiver.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index b28dca8..59d05b6 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -40,12 +40,8 @@
}
virtual void SetUp() {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- mST = new GLConsumer(consumer, 123, GLConsumer::TEXTURE_EXTERNAL, true,
- false);
- mSTC = new Surface(producer);
+ mST = new GLConsumer(123, GLConsumer::TEXTURE_EXTERNAL, true, false);
+ mSTC = mST->getSurface();
mANW = mSTC;
// We need a valid GL context so we can test updateTexImage()
@@ -731,12 +727,8 @@
ASSERT_NE(EGL_NO_CONTEXT, mEglContext);
for (int i = 0; i < NUM_SURFACE_TEXTURES; i++) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<GLConsumer> st(new GLConsumer(consumer, i,
- GLConsumer::TEXTURE_EXTERNAL, true, false));
- sp<Surface> stc(new Surface(producer));
+ sp<GLConsumer> st(new GLConsumer(i, GLConsumer::TEXTURE_EXTERNAL, true, false));
+ sp<Surface> stc = st->getSurface();
mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig,
static_cast<ANativeWindow*>(stc.get()), nullptr);
ASSERT_EQ(EGL_SUCCESS, eglGetError());
diff --git a/libs/gui/tests/SurfaceTextureGL.h b/libs/gui/tests/SurfaceTextureGL.h
index 9d8af5d..1309635 100644
--- a/libs/gui/tests/SurfaceTextureGL.h
+++ b/libs/gui/tests/SurfaceTextureGL.h
@@ -38,11 +38,8 @@
void SetUp() {
GLTest::SetUp();
- sp<IGraphicBufferProducer> producer;
- BufferQueue::createBufferQueue(&producer, &mConsumer);
- mST = new GLConsumer(mConsumer, TEX_ID, GLConsumer::TEXTURE_EXTERNAL,
- true, false);
- mSTC = new Surface(producer);
+ mST = new GLConsumer(TEX_ID, GLConsumer::TEXTURE_EXTERNAL, true, false);
+ mSTC = mST->getSurface();
mANW = mSTC;
ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), TEST_PRODUCER_USAGE_BITS));
mTextureRenderer = new TextureRenderer(TEX_ID, mST);
@@ -63,7 +60,6 @@
mTextureRenderer->drawTexture();
}
- sp<IGraphicBufferConsumer> mConsumer;
sp<GLConsumer> mST;
sp<Surface> mSTC;
sp<ANativeWindow> mANW;
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp
index f76c0be..449533a 100644
--- a/libs/gui/tests/SurfaceTextureGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -480,8 +480,8 @@
};
sp<DisconnectWaiter> dw(new DisconnectWaiter());
- mConsumer->consumerConnect(dw, false);
-
+ sp<IGraphicBufferConsumer> consumer = mST->getIGraphicBufferConsumer();
+ consumer->consumerConnect(dw, false);
sp<Thread> pt(new ProducerThread(mANW));
pt->run("ProducerThread");
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index f7a8212..ab09dfc 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "gui/view/Surface.h"
#include "Constants.h"
#include "MockConsumer.h"
@@ -23,27 +24,38 @@
#include <android/gui/IDisplayEventConnection.h>
#include <android/gui/ISurfaceComposer.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware_buffer.h>
#include <binder/ProcessState.h>
+#include <com_android_graphics_libgui_flags.h>
#include <configstore/Utils.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/BufferItemConsumer.h>
+#include <gui/BufferQueue.h>
+#include <gui/CpuConsumer.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SyncScreenCaptureListener.h>
-#include <inttypes.h>
#include <private/gui/ComposerService.h>
#include <private/gui/ComposerServiceAIDL.h>
#include <sys/types.h>
+#include <system/window.h>
#include <ui/BufferQueueDefs.h>
#include <ui/DisplayMode.h>
+#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
#include <utils/Errors.h>
#include <utils/String8.h>
+#include <cstddef>
#include <limits>
#include <thread>
+#include "testserver/TestServerClient.h"
+
namespace android {
using namespace std::chrono_literals;
@@ -142,10 +154,10 @@
if (hasSurfaceListener) {
listener = new FakeSurfaceListener(enableReleasedCb);
}
- ASSERT_EQ(OK, surface->connect(
- NATIVE_WINDOW_API_CPU,
- /*reportBufferRemoval*/true,
- /*listener*/listener));
+ ASSERT_EQ(OK,
+ surface->connect(NATIVE_WINDOW_API_CPU,
+ /*listener*/ listener,
+ /*reportBufferRemoval*/ true));
const int BUFFER_COUNT = 4 + extraDiscardedBuffers;
ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS));
@@ -263,13 +275,9 @@
TEST_F(SurfaceTest, QueryConsumerUsage) {
const int TEST_USAGE_FLAGS =
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<BufferItemConsumer> c = new BufferItemConsumer(consumer,
- TEST_USAGE_FLAGS);
- sp<Surface> s = new Surface(producer);
+ sp<BufferItemConsumer> c = new BufferItemConsumer(TEST_USAGE_FLAGS);
+ sp<Surface> s = c->getSurface();
sp<ANativeWindow> anw(s);
int flags = -1;
@@ -281,15 +289,11 @@
TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) {
const android_dataspace TEST_DATASPACE = HAL_DATASPACE_V0_SRGB;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
cpuConsumer->setDefaultBufferDataSpace(TEST_DATASPACE);
- sp<Surface> s = new Surface(producer);
-
+ sp<Surface> s = cpuConsumer->getSurface();
sp<ANativeWindow> anw(s);
android_dataspace dataSpace;
@@ -302,11 +306,8 @@
}
TEST_F(SurfaceTest, SettingGenerationNumber) {
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
- sp<Surface> surface = new Surface(producer);
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
+ sp<Surface> surface = cpuConsumer->getSurface();
sp<ANativeWindow> window(surface);
// Allocate a buffer with a generation number of 0
@@ -491,10 +492,10 @@
sp<Surface> surface = new Surface(producer);
sp<ANativeWindow> window(surface);
sp<StubSurfaceListener> listener = new StubSurfaceListener();
- ASSERT_EQ(OK, surface->connect(
- NATIVE_WINDOW_API_CPU,
- /*listener*/listener,
- /*reportBufferRemoval*/true));
+ ASSERT_EQ(OK,
+ surface->connect(NATIVE_WINDOW_API_CPU,
+ /*listener*/ listener,
+ /*reportBufferRemoval*/ true));
const int BUFFER_COUNT = 4;
ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT));
ASSERT_EQ(NO_ERROR, native_window_set_usage(window.get(), TEST_PRODUCER_USAGE_BITS));
@@ -635,7 +636,7 @@
status_t setTransactionState(
const FrameTimelineInfo& /*frameTimelineInfo*/, Vector<ComposerState>& /*state*/,
- const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
+ Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
const sp<IBinder>& /*applyToken*/, InputWindowCommands /*inputWindowCommands*/,
int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
const std::vector<client_cache_t>& /*cachedBuffer*/, bool /*hasListenerCallbacks*/,
@@ -2146,12 +2147,9 @@
TEST_F(SurfaceTest, BatchOperations) {
const int BUFFER_COUNT = 16;
const int BATCH_SIZE = 8;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
- sp<Surface> surface = new Surface(producer);
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
+ sp<Surface> surface = cpuConsumer->getSurface();
sp<ANativeWindow> window(surface);
sp<StubSurfaceListener> listener = new StubSurfaceListener();
@@ -2198,12 +2196,9 @@
TEST_F(SurfaceTest, BatchIllegalOperations) {
const int BUFFER_COUNT = 16;
const int BATCH_SIZE = 8;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
- sp<Surface> surface = new Surface(producer);
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(1);
+ sp<Surface> surface = cpuConsumer->getSurface();
sp<ANativeWindow> window(surface);
sp<StubSurfaceListener> listener = new StubSurfaceListener();
@@ -2225,4 +2220,160 @@
ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
+TEST_F(SurfaceTest, PlatformBufferMethods) {
+ sp<CpuConsumer> cpuConsumer = sp<CpuConsumer>::make(1);
+ sp<Surface> surface = cpuConsumer->getSurface();
+ sp<StubSurfaceListener> listener = sp<StubSurfaceListener>::make();
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+
+ EXPECT_EQ(OK,
+ surface->connect(NATIVE_WINDOW_API_CPU, listener, /* reportBufferRemoval */ false));
+
+ //
+ // Verify nullptrs are handled safely:
+ //
+
+ EXPECT_EQ(BAD_VALUE, surface->dequeueBuffer((sp<GraphicBuffer>*)nullptr, nullptr));
+ EXPECT_EQ(BAD_VALUE, surface->dequeueBuffer((sp<GraphicBuffer>*)nullptr, &fence));
+ EXPECT_EQ(BAD_VALUE, surface->dequeueBuffer(&buffer, nullptr));
+ EXPECT_EQ(BAD_VALUE, surface->queueBuffer(nullptr, nullptr));
+ EXPECT_EQ(BAD_VALUE, surface->detachBuffer(nullptr));
+
+ //
+ // Verify dequeue/queue:
+ //
+
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+ EXPECT_NE(nullptr, buffer);
+ EXPECT_EQ(OK, surface->queueBuffer(buffer, fence));
+
+ //
+ // Verify dequeue/detach:
+ //
+
+ wp<GraphicBuffer> weakBuffer;
+ {
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+
+ EXPECT_EQ(OK, surface->detachBuffer(buffer));
+
+ weakBuffer = buffer;
+ buffer = nullptr;
+ }
+ EXPECT_EQ(nullptr, weakBuffer.promote()) << "Weak buffer still held by Surface.";
+
+ //
+ // Verify detach without borrowing the buffer does not work:
+ //
+
+ sp<GraphicBuffer> heldTooLongBuffer;
+ EXPECT_EQ(OK, surface->dequeueBuffer(&heldTooLongBuffer, &fence));
+ EXPECT_EQ(OK, surface->queueBuffer(heldTooLongBuffer));
+ EXPECT_EQ(BAD_VALUE, surface->detachBuffer(heldTooLongBuffer));
+}
+
+TEST_F(SurfaceTest, AllowAllocation) {
+ // controlledByApp must be true to disable blocking
+ sp<CpuConsumer> cpuConsumer = sp<CpuConsumer>::make(1, /*controlledByApp*/ true);
+ sp<Surface> surface = cpuConsumer->getSurface();
+ sp<StubSurfaceListener> listener = sp<StubSurfaceListener>::make();
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+
+ EXPECT_EQ(OK,
+ surface->connect(NATIVE_WINDOW_API_CPU, listener, /* reportBufferRemoval */ false));
+ EXPECT_EQ(OK, surface->allowAllocation(false));
+
+ EXPECT_EQ(OK, surface->setDequeueTimeout(-1));
+ EXPECT_EQ(WOULD_BLOCK, surface->dequeueBuffer(&buffer, &fence));
+
+ EXPECT_EQ(OK, surface->setDequeueTimeout(10));
+ EXPECT_EQ(TIMED_OUT, surface->dequeueBuffer(&buffer, &fence));
+
+ EXPECT_EQ(OK, surface->allowAllocation(true));
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+}
+
+TEST_F(SurfaceTest, QueueAcquireReleaseDequeue_CalledInStack_DoesNotDeadlock) {
+ class DequeuingSurfaceListener : public SurfaceListener {
+ public:
+ DequeuingSurfaceListener(const wp<Surface>& surface) : mSurface(surface) {}
+
+ virtual void onBufferReleased() override {
+ sp<Surface> surface = mSurface.promote();
+ ASSERT_NE(nullptr, surface);
+ EXPECT_EQ(OK, surface->dequeueBuffer(&mBuffer, &mFence));
+ }
+
+ virtual bool needsReleaseNotify() override { return true; }
+ virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>&) override {}
+ virtual void onBufferDetached(int) override {}
+
+ sp<GraphicBuffer> mBuffer;
+ sp<Fence> mFence;
+
+ private:
+ wp<Surface> mSurface;
+ };
+
+ class ImmediateReleaseConsumerListener : public BufferItemConsumer::FrameAvailableListener {
+ public:
+ ImmediateReleaseConsumerListener(const wp<BufferItemConsumer>& consumer)
+ : mConsumer(consumer) {}
+
+ virtual void onFrameAvailable(const BufferItem&) override {
+ sp<BufferItemConsumer> consumer = mConsumer.promote();
+ ASSERT_NE(nullptr, consumer);
+
+ mCalls += 1;
+
+ BufferItem buffer;
+ EXPECT_EQ(OK, consumer->acquireBuffer(&buffer, 0));
+ EXPECT_EQ(OK, consumer->releaseBuffer(buffer));
+ }
+
+ size_t mCalls = 0;
+
+ private:
+ wp<BufferItemConsumer> mConsumer;
+ };
+
+ sp<IGraphicBufferProducer> bqProducer;
+ sp<IGraphicBufferConsumer> bqConsumer;
+ BufferQueue::createBufferQueue(&bqProducer, &bqConsumer);
+
+ sp<BufferItemConsumer> consumer = sp<BufferItemConsumer>::make(bqConsumer, 3);
+ sp<Surface> surface = sp<Surface>::make(bqProducer);
+ sp<ImmediateReleaseConsumerListener> consumerListener =
+ sp<ImmediateReleaseConsumerListener>::make(consumer);
+ consumer->setFrameAvailableListener(consumerListener);
+
+ sp<DequeuingSurfaceListener> surfaceListener = sp<DequeuingSurfaceListener>::make(surface);
+ EXPECT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, surfaceListener, false));
+
+ EXPECT_EQ(OK, surface->setMaxDequeuedBufferCount(2));
+
+ sp<GraphicBuffer> buffer;
+ sp<Fence> fence;
+ EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence));
+ EXPECT_EQ(OK, surface->queueBuffer(buffer, fence));
+
+ EXPECT_EQ(1u, consumerListener->mCalls);
+ EXPECT_NE(nullptr, surfaceListener->mBuffer);
+
+ EXPECT_EQ(OK, surface->disconnect(NATIVE_WINDOW_API_CPU));
+}
+
+TEST_F(SurfaceTest, ViewSurface_toString) {
+ view::Surface surface{};
+ EXPECT_EQ("", surface.toString());
+
+ surface.name = String16("name");
+ EXPECT_EQ("name", surface.toString());
+}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)
+
} // namespace android
diff --git a/libs/gui/tests/TestServer_test.cpp b/libs/gui/tests/TestServer_test.cpp
new file mode 100644
index 0000000..d640782
--- /dev/null
+++ b/libs/gui/tests/TestServer_test.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 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 <gtest/gtest.h>
+
+#include <SurfaceFlingerProperties.h>
+#include <android/gui/IDisplayEventConnection.h>
+#include <android/gui/ISurfaceComposer.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware_buffer.h>
+#include <binder/ProcessState.h>
+#include <com_android_graphics_libgui_flags.h>
+#include <configstore/Utils.h>
+#include <gui/AidlUtil.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/BufferQueue.h>
+#include <gui/CpuConsumer.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SyncScreenCaptureListener.h>
+#include <private/gui/ComposerService.h>
+#include <private/gui/ComposerServiceAIDL.h>
+#include <sys/types.h>
+#include <system/window.h>
+#include <ui/BufferQueueDefs.h>
+#include <ui/DisplayMode.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+
+#include <cstddef>
+#include <limits>
+#include <thread>
+
+#include "binder/IInterface.h"
+#include "testserver/TestServerClient.h"
+
+namespace android {
+
+namespace {
+
+class TestServerTest : public ::testing::Test {
+protected:
+ TestServerTest() { ProcessState::self()->startThreadPool(); }
+};
+
+} // namespace
+
+TEST_F(TestServerTest, Create) {
+ EXPECT_NE(nullptr, TestServerClient::Create());
+}
+
+TEST_F(TestServerTest, CreateProducer) {
+ sp<TestServerClient> client = TestServerClient::Create();
+ EXPECT_NE(nullptr, client->CreateProducer());
+}
+
+TEST_F(TestServerTest, KillServer) {
+ class DeathWaiter : public IBinder::DeathRecipient {
+ public:
+ virtual void binderDied(const wp<IBinder>&) override { mPromise.set_value(true); }
+ std::future<bool> getFuture() { return mPromise.get_future(); }
+
+ std::promise<bool> mPromise;
+ };
+
+ sp<TestServerClient> client = TestServerClient::Create();
+ sp<IGraphicBufferProducer> producer = client->CreateProducer();
+ EXPECT_NE(nullptr, producer);
+
+ sp<DeathWaiter> deathWaiter = sp<DeathWaiter>::make();
+ EXPECT_EQ(OK, IInterface::asBinder(producer)->linkToDeath(deathWaiter));
+
+ auto deathWaiterFuture = deathWaiter->getFuture();
+ EXPECT_EQ(OK, client->Kill());
+ EXPECT_EQ(nullptr, client->CreateProducer());
+
+ EXPECT_TRUE(deathWaiterFuture.get());
+}
+
+} // namespace android
diff --git a/libs/gui/tests/testserver/TestServer.cpp b/libs/gui/tests/testserver/TestServer.cpp
new file mode 100644
index 0000000..cd8824e
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServer.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#define LOG_TAG "TestServer"
+
+#include <android-base/stringprintf.h>
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/Status.h>
+#include <gui/BufferQueue.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/view/Surface.h>
+#include <libgui_test_server/BnTestServer.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+#include <cstdint>
+#include <cstdlib>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "TestServer.h"
+
+namespace android {
+
+namespace {
+class TestConsumerListener : public BnConsumerListener {
+ virtual void onFrameAvailable(const BufferItem&) override {}
+ virtual void onBuffersReleased() override {}
+ virtual void onSidebandStreamChanged() override {}
+};
+
+class TestServiceImpl : public libgui_test_server::BnTestServer {
+public:
+ TestServiceImpl(const char* name) : mName(name) {}
+
+ virtual binder::Status createProducer(view::Surface* out) override {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ BufferQueueHolder bq;
+ BufferQueue::createBufferQueue(&bq.producer, &bq.consumer);
+ sp<TestConsumerListener> listener = sp<TestConsumerListener>::make();
+ bq.consumer->consumerConnect(listener, /*controlledByApp*/ true);
+
+ uint64_t id = 0;
+ bq.producer->getUniqueId(&id);
+ std::string name = base::StringPrintf("%s-%" PRIu64, mName, id);
+
+ out->name = String16(name.c_str());
+ out->graphicBufferProducer = bq.producer;
+ mBqs.push_back(std::move(bq));
+
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status killNow() override {
+ ALOGE("LibGUI Test Service %s dying in response to killNow", mName);
+ _exit(0);
+ // Not reached:
+ return binder::Status::ok();
+ }
+
+private:
+ std::mutex mMutex;
+ const char* mName;
+
+ struct BufferQueueHolder {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ };
+
+ std::vector<BufferQueueHolder> mBqs;
+};
+} // namespace
+
+int TestServerMain(const char* name) {
+ ProcessState::self()->startThreadPool();
+
+ sp<TestServiceImpl> testService = sp<TestServiceImpl>::make(name);
+ ALOGE("service");
+ sp<IServiceManager> serviceManager(defaultServiceManager());
+ LOG_ALWAYS_FATAL_IF(OK != serviceManager->addService(String16(name), testService));
+
+ ALOGD("LibGUI Test Service %s STARTED", name);
+
+ IPCThreadState::self()->joinThreadPool();
+
+ ALOGW("LibGUI Test Service %s DIED", name);
+
+ return 0;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/testserver/TestServer.h b/libs/gui/tests/testserver/TestServer.h
new file mode 100644
index 0000000..4226f1b
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServer.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android {
+
+/*
+ * Main method for a libgui ITestServer server.
+ *
+ * This must be called without any binder setup having been done, because you can't fork and do
+ * binder things once ProcessState is set up.
+ * @param name The service name of the test server to start.
+ * @return retcode
+ */
+int TestServerMain(const char* name);
+
+} // namespace android
diff --git a/libs/gui/tests/testserver/TestServerClient.cpp b/libs/gui/tests/testserver/TestServerClient.cpp
new file mode 100644
index 0000000..e388074
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServerClient.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2024 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 <sys/wait.h>
+#include <cerrno>
+#define LOG_TAG "TestServerClient"
+
+#include <android-base/stringprintf.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <libgui_test_server/ITestServer.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <csignal>
+#include <cstdlib>
+#include <mutex>
+#include <string>
+
+#include "TestServerClient.h"
+#include "TestServerCommon.h"
+
+namespace android {
+
+namespace {
+
+std::string GetUniqueServiceName() {
+ static std::atomic<int> uniqueId = 1;
+
+ pid_t pid = getpid();
+ int id = uniqueId++;
+ return base::StringPrintf("Libgui-TestServer-%d-%d", pid, id);
+}
+
+struct RemoteTestServerHostHolder {
+ RemoteTestServerHostHolder(pid_t pid, int sendFd, int recvFd)
+ : mPid(pid), mSendFd(sendFd), mRecvFd(recvFd) {}
+ ~RemoteTestServerHostHolder() {
+ std::lock_guard lock(mMutex);
+
+ kill(mPid, SIGKILL);
+ close(mSendFd);
+ close(mRecvFd);
+ }
+
+ pid_t CreateTestServerOrDie(std::string name) {
+ std::lock_guard lock(mMutex);
+
+ CreateServerRequest request;
+ strlcpy(request.name, name.c_str(), sizeof(request.name) / sizeof(request.name[0]));
+
+ ssize_t bytes = write(mSendFd, &request, sizeof(request));
+ LOG_ALWAYS_FATAL_IF(bytes != sizeof(request));
+
+ CreateServerResponse response;
+ bytes = read(mRecvFd, &response, sizeof(response));
+ LOG_ALWAYS_FATAL_IF(bytes != sizeof(response));
+
+ return response.pid;
+ }
+
+private:
+ std::mutex mMutex;
+
+ pid_t mPid;
+ int mSendFd;
+ int mRecvFd;
+};
+
+std::unique_ptr<RemoteTestServerHostHolder> g_remoteTestServerHostHolder = nullptr;
+
+} // namespace
+
+void TestServerClient::InitializeOrDie(const char* filename) {
+ int sendPipeFds[2];
+ int ret = pipe(sendPipeFds);
+ LOG_ALWAYS_FATAL_IF(ret, "Unable to create subprocess send pipe");
+
+ int recvPipeFds[2];
+ ret = pipe(recvPipeFds);
+ LOG_ALWAYS_FATAL_IF(ret, "Unable to create subprocess recv pipe");
+
+ pid_t childPid = fork();
+ LOG_ALWAYS_FATAL_IF(childPid < 0, "Unable to fork child process");
+
+ if (childPid == 0) {
+ // We forked!
+ close(sendPipeFds[1]);
+ close(recvPipeFds[0]);
+
+ // We'll be reading from the parent's "send" and writing to the parent's "recv".
+ std::string sendPipe = std::to_string(sendPipeFds[0]);
+ std::string recvPipe = std::to_string(recvPipeFds[1]);
+ char* args[] = {
+ const_cast<char*>(filename),
+ const_cast<char*>("--test-server-host"),
+ const_cast<char*>(sendPipe.c_str()),
+ const_cast<char*>(recvPipe.c_str()),
+ nullptr,
+ };
+
+ ret = execv(filename, args);
+ ALOGE("Failed to exec libguiTestServer. ret=%d errno=%d (%s)", ret, errno, strerror(errno));
+ status_t status = -errno;
+ write(recvPipeFds[1], &status, sizeof(status));
+ _exit(EXIT_FAILURE);
+ }
+
+ close(sendPipeFds[0]);
+ close(recvPipeFds[1]);
+
+ // Check for an OK status that the host started. If so, we're good to go.
+ status_t status;
+ ret = read(recvPipeFds[0], &status, sizeof(status));
+ LOG_ALWAYS_FATAL_IF(ret != sizeof(status), "Unable to read from pipe: %d", ret);
+ LOG_ALWAYS_FATAL_IF(OK != status, "Pipe returned failed status: %d", status);
+
+ g_remoteTestServerHostHolder =
+ std::make_unique<RemoteTestServerHostHolder>(childPid, sendPipeFds[1], recvPipeFds[0]);
+}
+
+sp<TestServerClient> TestServerClient::Create() {
+ std::string serviceName = GetUniqueServiceName();
+
+ pid_t childPid = g_remoteTestServerHostHolder->CreateTestServerOrDie(serviceName);
+ ALOGD("Created child server %s with pid %d", serviceName.c_str(), childPid);
+
+ sp<libgui_test_server::ITestServer> server =
+ waitForService<libgui_test_server::ITestServer>(String16(serviceName.c_str()));
+ LOG_ALWAYS_FATAL_IF(server == nullptr);
+ ALOGD("Created connected to child server %s", serviceName.c_str());
+
+ return sp<TestServerClient>::make(server);
+}
+
+TestServerClient::TestServerClient(const sp<libgui_test_server::ITestServer>& server)
+ : mServer(server) {}
+
+TestServerClient::~TestServerClient() {
+ Kill();
+}
+
+sp<IGraphicBufferProducer> TestServerClient::CreateProducer() {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (!mIsAlive) {
+ return nullptr;
+ }
+
+ view::Surface surface;
+ binder::Status status = mServer->createProducer(&surface);
+
+ if (!status.isOk()) {
+ ALOGE("Failed to create remote producer. Error: %s", status.exceptionMessage().c_str());
+ return nullptr;
+ }
+
+ if (!surface.graphicBufferProducer) {
+ ALOGE("Remote producer returned no IGBP.");
+ return nullptr;
+ }
+
+ return surface.graphicBufferProducer;
+}
+
+status_t TestServerClient::Kill() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mIsAlive) {
+ return DEAD_OBJECT;
+ }
+
+ mServer->killNow();
+ mServer = nullptr;
+ mIsAlive = false;
+
+ return OK;
+}
+
+} // namespace android
diff --git a/libs/gui/tests/testserver/TestServerClient.h b/libs/gui/tests/testserver/TestServerClient.h
new file mode 100644
index 0000000..5329634
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServerClient.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 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 <libgui_test_server/ITestServer.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class TestServerClient : public RefBase {
+public:
+ static void InitializeOrDie(const char* filename);
+ static sp<TestServerClient> Create();
+
+ TestServerClient(const sp<libgui_test_server::ITestServer>& server);
+ virtual ~TestServerClient() override;
+
+ sp<IGraphicBufferProducer> CreateProducer();
+ status_t Kill();
+
+private:
+ std::mutex mMutex;
+
+ sp<libgui_test_server::ITestServer> mServer;
+ bool mIsAlive = true;
+};
+
+} // namespace android
diff --git a/libs/gui/tests/testserver/TestServerCommon.h b/libs/gui/tests/testserver/TestServerCommon.h
new file mode 100644
index 0000000..7370f20
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServerCommon.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 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 <fcntl.h>
+
+namespace android {
+
+/*
+ * Test -> TestServerHost Request to create a new ITestServer fork.
+ */
+struct CreateServerRequest {
+ /*
+ * Service name for new ITestServer.
+ */
+ char name[128];
+};
+
+/*
+ * TestServerHost -> Test Response for creating an ITestServer fork.
+ */
+struct CreateServerResponse {
+ /*
+ * pid of new ITestServer.
+ */
+ pid_t pid;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/testserver/TestServerHost.cpp b/libs/gui/tests/testserver/TestServerHost.cpp
new file mode 100644
index 0000000..696c3b9
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServerHost.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#define LOG_TAG "TestServerHost"
+
+#include <android-base/unique_fd.h>
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/Status.h>
+#include <gui/BufferQueue.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <libgui_test_server/BnTestServer.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+
+#include <memory>
+#include <vector>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <cstddef>
+#include <cstdlib>
+
+#include "TestServerCommon.h"
+#include "TestServerHost.h"
+
+namespace android {
+
+namespace {
+
+pid_t ForkTestServer(const char* filename, char* name) {
+ pid_t childPid = fork();
+ LOG_ALWAYS_FATAL_IF(childPid == -1);
+
+ if (childPid != 0) {
+ return childPid;
+ }
+
+ // We forked!
+ const char* test_server_flag = "--test-server";
+ char* args[] = {
+ const_cast<char*>(filename),
+ const_cast<char*>(test_server_flag),
+ name,
+ nullptr,
+ };
+
+ int ret = execv(filename, args);
+ ALOGE("Failed to exec libgui_test as a TestServer. ret=%d errno=%d (%s)", ret, errno,
+ strerror(errno));
+ _exit(EXIT_FAILURE);
+}
+
+} // namespace
+
+int TestServerHostMain(const char* filename, base::unique_fd sendPipeFd,
+ base::unique_fd recvPipeFd) {
+ status_t status = OK;
+ LOG_ALWAYS_FATAL_IF(sizeof(status) != write(sendPipeFd.get(), &status, sizeof(status)));
+
+ ALOGE("Launched TestServerHost");
+
+ while (true) {
+ CreateServerRequest request = {};
+ ssize_t bytes = read(recvPipeFd.get(), &request, sizeof(request));
+ LOG_ALWAYS_FATAL_IF(bytes != sizeof(request));
+ pid_t childPid = ForkTestServer(filename, request.name);
+
+ CreateServerResponse response = {};
+ response.pid = childPid;
+ bytes = write(sendPipeFd.get(), &response, sizeof(response));
+ LOG_ALWAYS_FATAL_IF(bytes != sizeof(response));
+ }
+
+ return 0;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/gui/tests/testserver/TestServerHost.h b/libs/gui/tests/testserver/TestServerHost.h
new file mode 100644
index 0000000..df22c0c
--- /dev/null
+++ b/libs/gui/tests/testserver/TestServerHost.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 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 <android-base/unique_fd.h>
+
+#include <string>
+
+namespace android {
+
+/*
+ * Main method for a host process for TestServers.
+ *
+ * This must be called without any binder setup having been done, because you can't fork and do
+ * binder things once ProcessState is set up.
+ * @param filename File name of this binary / the binary to execve into
+ * @param sendPipeFd Pipe FD to send data to.
+ * @param recvPipeFd Pipe FD to receive data from.
+ * @return retcode
+ */
+int TestServerHostMain(const char* filename, base::unique_fd sendPipeFd,
+ base::unique_fd recvPipeFd);
+
+} // namespace android
diff --git a/libs/gui/tests/testserver/aidl/libgui_test_server/ITestServer.aidl b/libs/gui/tests/testserver/aidl/libgui_test_server/ITestServer.aidl
new file mode 100644
index 0000000..c939ea0
--- /dev/null
+++ b/libs/gui/tests/testserver/aidl/libgui_test_server/ITestServer.aidl
@@ -0,0 +1,12 @@
+package libgui_test_server;
+
+import android.view.Surface;
+
+// Test server for libgui_test
+interface ITestServer {
+ // Create a new producer. The server will have connected to the consumer.
+ Surface createProducer();
+
+ // Kills the server immediately.
+ void killNow();
+}
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index 7c15e7c..84c2a6a 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -121,5 +121,11 @@
return str.value_or(String16());
}
+std::string Surface::toString() const {
+ std::stringstream out;
+ out << name;
+ return out.str();
+}
+
} // namespace view
} // namespace android
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 8fbf5c6..e4e81ad 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -232,6 +232,7 @@
"MotionPredictorMetricsManager.cpp",
"PrintTools.cpp",
"PropertyMap.cpp",
+ "Resampler.cpp",
"TfLiteMotionPredictor.cpp",
"TouchVideoFrame.cpp",
"VelocityControl.cpp",
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index c145d5c..99ffa68 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "InputTransport"
#define ATRACE_TAG ATRACE_TAG_INPUT
+#include <chrono>
+
#include <inttypes.h>
#include <android-base/logging.h>
@@ -168,6 +170,10 @@
return msg;
}
+bool isPointerEvent(const MotionEvent& motionEvent) {
+ return (motionEvent.getSource() & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER;
+}
+
} // namespace
using android::base::Result;
@@ -177,8 +183,13 @@
InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
sp<Looper> looper,
- InputConsumerCallbacks& callbacks)
- : mChannel(channel), mLooper(looper), mCallbacks(callbacks), mFdEvents(0) {
+ InputConsumerCallbacks& callbacks,
+ std::unique_ptr<Resampler> resampler)
+ : mChannel(channel),
+ mLooper(looper),
+ mCallbacks(callbacks),
+ mResampler(std::move(resampler)),
+ mFdEvents(0) {
LOG_ALWAYS_FATAL_IF(mLooper == nullptr);
mCallback = sp<LooperEventCallback>::make(
std::bind(&InputConsumerNoResampling::handleReceiveCallback, this,
@@ -463,6 +474,15 @@
}
messages.pop();
}
+ // Check if resampling should be performed.
+ if (motionEvent != nullptr && isPointerEvent(*motionEvent) && mResampler != nullptr) {
+ InputMessage* futureSample = nullptr;
+ if (!messages.empty()) {
+ futureSample = &messages.front();
+ }
+ mResampler->resampleMotionEvent(static_cast<std::chrono::nanoseconds>(frameTime),
+ *motionEvent, futureSample);
+ }
return std::make_pair(std::move(motionEvent), firstSeqForBatch);
}
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index 5088188..f3241c9 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -240,8 +240,9 @@
std::vector<int32_t> KeyLayoutMap::findScanCodesForKey(int32_t keyCode) const {
std::vector<int32_t> scanCodes;
+ // b/354333072: Only consider keys without FUNCTION flag
for (const auto& [scanCode, key] : mKeysByScanCode) {
- if (keyCode == key.keyCode) {
+ if (keyCode == key.keyCode && !(key.flags & POLICY_FLAG_FUNCTION)) {
scanCodes.push_back(scanCode);
}
}
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 9c70535..c61d394 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -72,7 +72,8 @@
// --- JerkTracker ---
-JerkTracker::JerkTracker(bool normalizedDt) : mNormalizedDt(normalizedDt) {}
+JerkTracker::JerkTracker(bool normalizedDt, float alpha)
+ : mNormalizedDt(normalizedDt), mAlpha(alpha) {}
void JerkTracker::pushSample(int64_t timestamp, float xPos, float yPos) {
// If we previously had full samples, we have a previous jerk calculation
@@ -122,7 +123,7 @@
float newJerkMagnitude = std::hypot(newXDerivatives[3], newYDerivatives[3]);
ALOGD_IF(isDebug(), "raw jerk: %f", newJerkMagnitude);
if (applySmoothing) {
- mJerkMagnitude = mJerkMagnitude + (mForgetFactor * (newJerkMagnitude - mJerkMagnitude));
+ mJerkMagnitude = mJerkMagnitude + (mAlpha * (newJerkMagnitude - mJerkMagnitude));
} else {
mJerkMagnitude = newJerkMagnitude;
}
@@ -143,14 +144,6 @@
return std::nullopt;
}
-void JerkTracker::setForgetFactor(float forgetFactor) {
- mForgetFactor = forgetFactor;
-}
-
-float JerkTracker::getForgetFactor() const {
- return mForgetFactor;
-}
-
// --- MotionPredictor ---
MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
@@ -160,6 +153,24 @@
mCheckMotionPredictionEnabled(std::move(checkMotionPredictionEnabled)),
mReportAtomFunction(reportAtomFunction) {}
+void MotionPredictor::initializeObjects() {
+ mModel = TfLiteMotionPredictorModel::create();
+ LOG_ALWAYS_FATAL_IF(!mModel);
+
+ // mJerkTracker assumes normalized dt = 1 between recorded samples because
+ // the underlying mModel input also assumes fixed-interval samples.
+ // Normalized dt as 1 is also used to correspond with the similar Jank
+ // implementation from the JetPack MotionPredictor implementation.
+ mJerkTracker = std::make_unique<JerkTracker>(/*normalizedDt=*/true, mModel->config().jerkAlpha);
+
+ mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength());
+
+ mMetricsManager =
+ std::make_unique<MotionPredictorMetricsManager>(mModel->config().predictionInterval,
+ mModel->outputLength(),
+ mReportAtomFunction);
+}
+
android::base::Result<void> MotionPredictor::record(const MotionEvent& event) {
if (mLastEvent && mLastEvent->getDeviceId() != event.getDeviceId()) {
// We still have an active gesture for another device. The provided MotionEvent is not
@@ -176,29 +187,18 @@
return {};
}
- // Initialise the model now that it's likely to be used.
if (!mModel) {
- mModel = TfLiteMotionPredictorModel::create();
- LOG_ALWAYS_FATAL_IF(!mModel);
- mJerkTracker.setForgetFactor(mModel->config().jerkForgetFactor);
- }
-
- if (!mBuffers) {
- mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength());
+ initializeObjects();
}
// Pass input event to the MetricsManager.
- if (!mMetricsManager) {
- mMetricsManager.emplace(mModel->config().predictionInterval, mModel->outputLength(),
- mReportAtomFunction);
- }
mMetricsManager->onRecord(event);
const int32_t action = event.getActionMasked();
if (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL) {
ALOGD_IF(isDebug(), "End of event stream");
mBuffers->reset();
- mJerkTracker.reset();
+ mJerkTracker->reset();
mLastEvent.reset();
return {};
} else if (action != AMOTION_EVENT_ACTION_DOWN && action != AMOTION_EVENT_ACTION_MOVE) {
@@ -233,9 +233,9 @@
0, i),
.orientation = event.getHistoricalOrientation(0, i),
});
- mJerkTracker.pushSample(event.getHistoricalEventTime(i),
- coords->getAxisValue(AMOTION_EVENT_AXIS_X),
- coords->getAxisValue(AMOTION_EVENT_AXIS_Y));
+ mJerkTracker->pushSample(event.getHistoricalEventTime(i),
+ coords->getAxisValue(AMOTION_EVENT_AXIS_X),
+ coords->getAxisValue(AMOTION_EVENT_AXIS_Y));
}
if (!mLastEvent) {
@@ -283,7 +283,7 @@
int64_t predictionTime = mBuffers->lastTimestamp();
const int64_t futureTime = timestamp + mPredictionTimestampOffsetNanos;
- const float jerkMagnitude = mJerkTracker.jerkMagnitude().value_or(0);
+ const float jerkMagnitude = mJerkTracker->jerkMagnitude().value_or(0);
const float fractionKept =
1 - normalizeRange(jerkMagnitude, mModel->config().lowJerk, mModel->config().highJerk);
// float to ensure proper division below.
@@ -379,12 +379,4 @@
return true;
}
-const TfLiteMotionPredictorModel::Config& MotionPredictor::getModelConfig() {
- if (!mModel) {
- mModel = TfLiteMotionPredictorModel::create();
- LOG_ALWAYS_FATAL_IF(!mModel);
- }
- return mModel->config();
-}
-
} // namespace android
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
new file mode 100644
index 0000000..c663649
--- /dev/null
+++ b/libs/input/Resampler.cpp
@@ -0,0 +1,260 @@
+/**
+ * Copyright 2024 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.
+ */
+
+#define LOG_TAG "LegacyResampler"
+
+#include <algorithm>
+#include <chrono>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <ftl/enum.h>
+
+#include <input/Resampler.h>
+#include <utils/Timers.h>
+
+using std::chrono::nanoseconds;
+
+namespace android {
+
+namespace {
+
+const bool IS_DEBUGGABLE_BUILD =
+#if defined(__ANDROID__)
+ android::base::GetBoolProperty("ro.debuggable", false);
+#else
+ true;
+#endif
+
+bool debugResampling() {
+ if (!IS_DEBUGGABLE_BUILD) {
+ static const bool DEBUG_TRANSPORT_RESAMPLING =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling",
+ ANDROID_LOG_INFO);
+ return DEBUG_TRANSPORT_RESAMPLING;
+ }
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
+}
+
+constexpr std::chrono::milliseconds RESAMPLE_LATENCY{5};
+
+constexpr std::chrono::milliseconds RESAMPLE_MIN_DELTA{2};
+
+constexpr std::chrono::milliseconds RESAMPLE_MAX_DELTA{20};
+
+constexpr std::chrono::milliseconds RESAMPLE_MAX_PREDICTION{8};
+
+bool canResampleTool(ToolType toolType) {
+ return toolType == ToolType::FINGER || toolType == ToolType::MOUSE ||
+ toolType == ToolType::STYLUS || toolType == ToolType::UNKNOWN;
+}
+
+inline float lerp(float a, float b, float alpha) {
+ return a + alpha * (b - a);
+}
+
+PointerCoords calculateResampledCoords(const PointerCoords& a, const PointerCoords& b,
+ float alpha) {
+ // We use the value of alpha to initialize resampledCoords with the latest sample information.
+ PointerCoords resampledCoords = (alpha < 1.0f) ? a : b;
+ resampledCoords.isResampled = true;
+ resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, lerp(a.getX(), b.getX(), alpha));
+ resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(a.getY(), b.getY(), alpha));
+ return resampledCoords;
+}
+} // namespace
+
+void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) {
+ const size_t numSamples = motionEvent.getHistorySize() + 1;
+ const size_t latestIndex = numSamples - 1;
+ const size_t secondToLatestIndex = (latestIndex > 0) ? (latestIndex - 1) : 0;
+ for (size_t sampleIndex = secondToLatestIndex; sampleIndex < numSamples; ++sampleIndex) {
+ std::vector<Pointer> pointers;
+ const size_t numPointers = motionEvent.getPointerCount();
+ for (size_t pointerIndex = 0; pointerIndex < numPointers; ++pointerIndex) {
+ // getSamplePointerCoords is the vector representation of a getHistorySize by
+ // getPointerCount matrix.
+ const PointerCoords& pointerCoords =
+ motionEvent.getSamplePointerCoords()[sampleIndex * numPointers + pointerIndex];
+ pointers.push_back(
+ Pointer{*motionEvent.getPointerProperties(pointerIndex), pointerCoords});
+ }
+ mLatestSamples.pushBack(
+ Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointers});
+ }
+}
+
+LegacyResampler::Sample LegacyResampler::messageToSample(const InputMessage& message) {
+ std::vector<Pointer> pointers;
+ for (uint32_t i = 0; i < message.body.motion.pointerCount; ++i) {
+ pointers.push_back(Pointer{message.body.motion.pointers[i].properties,
+ message.body.motion.pointers[i].coords});
+ }
+ return Sample{nanoseconds{message.body.motion.eventTime}, pointers};
+}
+
+bool LegacyResampler::pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary) {
+ if (target.pointers.size() > auxiliary.pointers.size()) {
+ LOG_IF(INFO, debugResampling())
+ << "Not resampled. Auxiliary sample has fewer pointers than target sample.";
+ return false;
+ }
+ for (size_t i = 0; i < target.pointers.size(); ++i) {
+ if (target.pointers[i].properties.id != auxiliary.pointers[i].properties.id) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ID mismatch.";
+ return false;
+ }
+ if (target.pointers[i].properties.toolType != auxiliary.pointers[i].properties.toolType) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ToolType mismatch.";
+ return false;
+ }
+ if (!canResampleTool(target.pointers[i].properties.toolType)) {
+ LOG_IF(INFO, debugResampling())
+ << "Not resampled. Cannot resample "
+ << ftl::enum_string(target.pointers[i].properties.toolType) << " ToolType.";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool LegacyResampler::canInterpolate(const InputMessage& message) const {
+ LOG_IF(FATAL, mLatestSamples.empty())
+ << "Not resampled. mLatestSamples must not be empty to interpolate.";
+
+ const Sample& pastSample = *(mLatestSamples.end() - 1);
+ const Sample& futureSample = messageToSample(message);
+
+ if (!pointerPropertiesResampleable(pastSample, futureSample)) {
+ return false;
+ }
+
+ const nanoseconds delta = futureSample.eventTime - pastSample.eventTime;
+ if (delta < RESAMPLE_MIN_DELTA) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
+ return false;
+ }
+ return true;
+}
+
+std::optional<LegacyResampler::Sample> LegacyResampler::attemptInterpolation(
+ nanoseconds resampleTime, const InputMessage& futureSample) const {
+ if (!canInterpolate(futureSample)) {
+ return std::nullopt;
+ }
+ LOG_IF(FATAL, mLatestSamples.empty())
+ << "Not resampled. mLatestSamples must not be empty to interpolate.";
+
+ const Sample& pastSample = *(mLatestSamples.end() - 1);
+
+ const nanoseconds delta =
+ nanoseconds{futureSample.body.motion.eventTime} - pastSample.eventTime;
+ const float alpha =
+ std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta;
+
+ std::vector<Pointer> resampledPointers;
+ for (size_t i = 0; i < pastSample.pointers.size(); ++i) {
+ const PointerCoords& resampledCoords =
+ calculateResampledCoords(pastSample.pointers[i].coords,
+ futureSample.body.motion.pointers[i].coords, alpha);
+ resampledPointers.push_back(Pointer{pastSample.pointers[i].properties, resampledCoords});
+ }
+ return Sample{resampleTime, resampledPointers};
+}
+
+bool LegacyResampler::canExtrapolate() const {
+ if (mLatestSamples.size() < 2) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Not enough data.";
+ return false;
+ }
+
+ const Sample& pastSample = *(mLatestSamples.end() - 2);
+ const Sample& presentSample = *(mLatestSamples.end() - 1);
+
+ if (!pointerPropertiesResampleable(presentSample, pastSample)) {
+ return false;
+ }
+
+ const nanoseconds delta = presentSample.eventTime - pastSample.eventTime;
+ if (delta < RESAMPLE_MIN_DELTA) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
+ return false;
+ } else if (delta > RESAMPLE_MAX_DELTA) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too large: " << delta << "ns.";
+ return false;
+ }
+ return true;
+}
+
+std::optional<LegacyResampler::Sample> LegacyResampler::attemptExtrapolation(
+ nanoseconds resampleTime) const {
+ if (!canExtrapolate()) {
+ return std::nullopt;
+ }
+ LOG_IF(FATAL, mLatestSamples.size() < 2)
+ << "Not resampled. mLatestSamples must have at least two samples to extrapolate.";
+
+ const Sample& pastSample = *(mLatestSamples.end() - 2);
+ const Sample& presentSample = *(mLatestSamples.end() - 1);
+
+ const nanoseconds delta = presentSample.eventTime - pastSample.eventTime;
+ // The farthest future time to which we can extrapolate. If the given resampleTime exceeds this,
+ // we use this value as the resample time target.
+ const nanoseconds farthestPrediction =
+ presentSample.eventTime + std::min<nanoseconds>(delta / 2, RESAMPLE_MAX_PREDICTION);
+ const nanoseconds newResampleTime =
+ (resampleTime > farthestPrediction) ? (farthestPrediction) : (resampleTime);
+ LOG_IF(INFO, debugResampling() && newResampleTime == farthestPrediction)
+ << "Resample time is too far in the future. Adjusting prediction from "
+ << (resampleTime - presentSample.eventTime) << " to "
+ << (farthestPrediction - presentSample.eventTime) << "ns.";
+ const float alpha =
+ std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) /
+ delta;
+
+ std::vector<Pointer> resampledPointers;
+ for (size_t i = 0; i < presentSample.pointers.size(); ++i) {
+ const PointerCoords& resampledCoords =
+ calculateResampledCoords(pastSample.pointers[i].coords,
+ presentSample.pointers[i].coords, alpha);
+ resampledPointers.push_back(Pointer{presentSample.pointers[i].properties, resampledCoords});
+ }
+ return Sample{newResampleTime, resampledPointers};
+}
+
+inline void LegacyResampler::addSampleToMotionEvent(const Sample& sample,
+ MotionEvent& motionEvent) {
+ motionEvent.addSample(sample.eventTime.count(), sample.asPointerCoords().data(),
+ motionEvent.getId());
+}
+
+void LegacyResampler::resampleMotionEvent(nanoseconds resampleTime, MotionEvent& motionEvent,
+ const InputMessage* futureSample) {
+ if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) {
+ mLatestSamples.clear();
+ }
+ mPreviousDeviceId = motionEvent.getDeviceId();
+
+ updateLatestSamples(motionEvent);
+
+ const std::optional<Sample> sample = (futureSample != nullptr)
+ ? (attemptInterpolation(resampleTime, *futureSample))
+ : (attemptExtrapolation(resampleTime));
+ if (sample.has_value()) {
+ addSampleToMotionEvent(*sample, motionEvent);
+ }
+}
+} // namespace android
diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp
index b401c98..5250a9d 100644
--- a/libs/input/TfLiteMotionPredictor.cpp
+++ b/libs/input/TfLiteMotionPredictor.cpp
@@ -283,7 +283,7 @@
.distanceNoiseFloor = parseXMLFloat(*configRoot, "distance-noise-floor"),
.lowJerk = parseXMLFloat(*configRoot, "low-jerk"),
.highJerk = parseXMLFloat(*configRoot, "high-jerk"),
- .jerkForgetFactor = parseXMLFloat(*configRoot, "jerk-forget-factor"),
+ .jerkAlpha = parseXMLFloat(*configRoot, "jerk-alpha"),
};
return std::unique_ptr<TfLiteMotionPredictorModel>(
diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp
index 0579967..51edbf1 100644
--- a/libs/input/VirtualInputDevice.cpp
+++ b/libs/input/VirtualInputDevice.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "VirtualInputDevice"
+#include <android-base/logging.h>
#include <android/input.h>
#include <android/keycodes.h>
#include <android_companion_virtualdevice_flags.h>
@@ -23,26 +24,259 @@
#include <input/Input.h>
#include <input/VirtualInputDevice.h>
#include <linux/uinput.h>
-#include <math.h>
-#include <utils/Log.h>
-#include <map>
#include <string>
using android::base::unique_fd;
+namespace {
+
/**
* Log debug messages about native virtual input devices.
* Enable this via "adb shell setprop log.tag.VirtualInputDevice DEBUG"
*/
-static bool isDebug() {
+bool isDebug() {
return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO);
}
+unique_fd invalidFd() {
+ return unique_fd(-1);
+}
+
+} // namespace
+
namespace android {
namespace vd_flags = android::companion::virtualdevice::flags;
+/** Creates a new uinput device and assigns a file descriptor. */
+unique_fd openUinput(const char* readableName, int32_t vendorId, int32_t productId,
+ const char* phys, DeviceType deviceType, int32_t screenHeight,
+ int32_t screenWidth) {
+ unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
+ if (fd < 0) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return invalidFd();
+ }
+
+ ioctl(fd, UI_SET_PHYS, phys);
+
+ ioctl(fd, UI_SET_EVBIT, EV_KEY);
+ ioctl(fd, UI_SET_EVBIT, EV_SYN);
+ switch (deviceType) {
+ case DeviceType::DPAD:
+ for (const auto& [_, keyCode] : VirtualDpad::DPAD_KEY_CODE_MAPPING) {
+ ioctl(fd, UI_SET_KEYBIT, keyCode);
+ }
+ break;
+ case DeviceType::KEYBOARD:
+ for (const auto& [_, keyCode] : VirtualKeyboard::KEY_CODE_MAPPING) {
+ ioctl(fd, UI_SET_KEYBIT, keyCode);
+ }
+ break;
+ case DeviceType::MOUSE:
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+ ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
+ ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
+ ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
+ ioctl(fd, UI_SET_KEYBIT, BTN_BACK);
+ ioctl(fd, UI_SET_KEYBIT, BTN_FORWARD);
+ ioctl(fd, UI_SET_RELBIT, REL_X);
+ ioctl(fd, UI_SET_RELBIT, REL_Y);
+ ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
+ ioctl(fd, UI_SET_RELBIT, REL_HWHEEL);
+ if (vd_flags::high_resolution_scroll()) {
+ ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
+ ioctl(fd, UI_SET_RELBIT, REL_HWHEEL_HI_RES);
+ }
+ break;
+ case DeviceType::TOUCHSCREEN:
+ ioctl(fd, UI_SET_EVBIT, EV_ABS);
+ ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);
+ ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+ break;
+ case DeviceType::STYLUS:
+ ioctl(fd, UI_SET_EVBIT, EV_ABS);
+ ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
+ ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS);
+ ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2);
+ ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_PEN);
+ ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_RUBBER);
+ ioctl(fd, UI_SET_ABSBIT, ABS_X);
+ ioctl(fd, UI_SET_ABSBIT, ABS_Y);
+ ioctl(fd, UI_SET_ABSBIT, ABS_TILT_X);
+ ioctl(fd, UI_SET_ABSBIT, ABS_TILT_Y);
+ ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE);
+ ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+ break;
+ case DeviceType::ROTARY_ENCODER:
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+ ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
+ if (vd_flags::high_resolution_scroll()) {
+ ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
+ }
+ break;
+ default:
+ ALOGE("Invalid input device type %d", static_cast<int32_t>(deviceType));
+ return invalidFd();
+ }
+
+ int version;
+ if (ioctl(fd, UI_GET_VERSION, &version) == 0 && version >= 5) {
+ uinput_setup setup;
+ memset(&setup, 0, sizeof(setup));
+ std::strncpy(setup.name, readableName, UINPUT_MAX_NAME_SIZE);
+ setup.id.version = 1;
+ setup.id.bustype = BUS_VIRTUAL;
+ setup.id.vendor = vendorId;
+ setup.id.product = productId;
+ if (deviceType == DeviceType::TOUCHSCREEN) {
+ uinput_abs_setup xAbsSetup;
+ xAbsSetup.code = ABS_MT_POSITION_X;
+ xAbsSetup.absinfo.maximum = screenWidth - 1;
+ xAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput x axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup yAbsSetup;
+ yAbsSetup.code = ABS_MT_POSITION_Y;
+ yAbsSetup.absinfo.maximum = screenHeight - 1;
+ yAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput y axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup majorAbsSetup;
+ majorAbsSetup.code = ABS_MT_TOUCH_MAJOR;
+ majorAbsSetup.absinfo.maximum = screenWidth - 1;
+ majorAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &majorAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput major axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup pressureAbsSetup;
+ pressureAbsSetup.code = ABS_MT_PRESSURE;
+ pressureAbsSetup.absinfo.maximum = 255;
+ pressureAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup slotAbsSetup;
+ slotAbsSetup.code = ABS_MT_SLOT;
+ slotAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
+ slotAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &slotAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput slots: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup trackingIdAbsSetup;
+ trackingIdAbsSetup.code = ABS_MT_TRACKING_ID;
+ trackingIdAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
+ trackingIdAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &trackingIdAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput tracking ids: %s", strerror(errno));
+ return invalidFd();
+ }
+ } else if (deviceType == DeviceType::STYLUS) {
+ uinput_abs_setup xAbsSetup;
+ xAbsSetup.code = ABS_X;
+ xAbsSetup.absinfo.maximum = screenWidth - 1;
+ xAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
+ ALOGE("Error creating stylus uinput x axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup yAbsSetup;
+ yAbsSetup.code = ABS_Y;
+ yAbsSetup.absinfo.maximum = screenHeight - 1;
+ yAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
+ ALOGE("Error creating stylus uinput y axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup tiltXAbsSetup;
+ tiltXAbsSetup.code = ABS_TILT_X;
+ tiltXAbsSetup.absinfo.maximum = 90;
+ tiltXAbsSetup.absinfo.minimum = -90;
+ if (ioctl(fd, UI_ABS_SETUP, &tiltXAbsSetup) != 0) {
+ ALOGE("Error creating stylus uinput tilt x axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup tiltYAbsSetup;
+ tiltYAbsSetup.code = ABS_TILT_Y;
+ tiltYAbsSetup.absinfo.maximum = 90;
+ tiltYAbsSetup.absinfo.minimum = -90;
+ if (ioctl(fd, UI_ABS_SETUP, &tiltYAbsSetup) != 0) {
+ ALOGE("Error creating stylus uinput tilt y axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ uinput_abs_setup pressureAbsSetup;
+ pressureAbsSetup.code = ABS_PRESSURE;
+ pressureAbsSetup.absinfo.maximum = 255;
+ pressureAbsSetup.absinfo.minimum = 0;
+ if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
+ ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
+ return invalidFd();
+ }
+ }
+ if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return invalidFd();
+ }
+ } else {
+ // UI_DEV_SETUP was not introduced until version 5. Try setting up manually.
+ ALOGI("Falling back to version %d manual setup", version);
+ uinput_user_dev fallback;
+ memset(&fallback, 0, sizeof(fallback));
+ std::strncpy(fallback.name, readableName, UINPUT_MAX_NAME_SIZE);
+ fallback.id.version = 1;
+ fallback.id.bustype = BUS_VIRTUAL;
+ fallback.id.vendor = vendorId;
+ fallback.id.product = productId;
+ if (deviceType == DeviceType::TOUCHSCREEN) {
+ fallback.absmin[ABS_MT_POSITION_X] = 0;
+ fallback.absmax[ABS_MT_POSITION_X] = screenWidth - 1;
+ fallback.absmin[ABS_MT_POSITION_Y] = 0;
+ fallback.absmax[ABS_MT_POSITION_Y] = screenHeight - 1;
+ fallback.absmin[ABS_MT_TOUCH_MAJOR] = 0;
+ fallback.absmax[ABS_MT_TOUCH_MAJOR] = screenWidth - 1;
+ fallback.absmin[ABS_MT_PRESSURE] = 0;
+ fallback.absmax[ABS_MT_PRESSURE] = 255;
+ } else if (deviceType == DeviceType::STYLUS) {
+ fallback.absmin[ABS_X] = 0;
+ fallback.absmax[ABS_X] = screenWidth - 1;
+ fallback.absmin[ABS_Y] = 0;
+ fallback.absmax[ABS_Y] = screenHeight - 1;
+ fallback.absmin[ABS_TILT_X] = -90;
+ fallback.absmax[ABS_TILT_X] = 90;
+ fallback.absmin[ABS_TILT_Y] = -90;
+ fallback.absmax[ABS_TILT_Y] = 90;
+ fallback.absmin[ABS_PRESSURE] = 0;
+ fallback.absmax[ABS_PRESSURE] = 255;
+ }
+ if (TEMP_FAILURE_RETRY(write(fd, &fallback, sizeof(fallback))) != sizeof(fallback)) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return invalidFd();
+ }
+ }
+
+ if (ioctl(fd, UI_DEV_CREATE) != 0) {
+ ALOGE("Error creating uinput device: %s", strerror(errno));
+ return invalidFd();
+ }
+
+ return fd;
+}
+
VirtualInputDevice::VirtualInputDevice(unique_fd fd) : mFd(std::move(fd)) {}
VirtualInputDevice::~VirtualInputDevice() {
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 3830751..500f7b4 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -37,6 +37,13 @@
}
flag {
+ name: "split_all_touches"
+ namespace: "input"
+ description: "Set FLAG_SPLIT_TOUCHES to true for all windows, regardless of what they specify. This is essentially deprecating this flag by forcefully enabling the split functionality"
+ bug: "239934827"
+}
+
+flag {
name: "a11y_crash_on_inconsistent_event_stream"
namespace: "accessibility"
description: "Brings back fatal logging for inconsistent event streams originating from accessibility."
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index e9d799e..132866b 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -23,6 +23,7 @@
"InputVerifier_test.cpp",
"MotionPredictor_test.cpp",
"MotionPredictorMetricsManager_test.cpp",
+ "Resampler_test.cpp",
"RingBuffer_test.cpp",
"TfLiteMotionPredictor_test.cpp",
"TouchResampling_test.cpp",
diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
index e710613..467c3b4 100644
--- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
@@ -396,8 +396,9 @@
break;
}
case LooperMessage::CREATE_CONSUMER: {
- mConsumer = std::make_unique<InputConsumerNoResampling>(std::move(mClientChannel),
- mLooper, *this);
+ mConsumer =
+ std::make_unique<InputConsumerNoResampling>(std::move(mClientChannel), mLooper,
+ *this, /*resampler=*/nullptr);
break;
}
case LooperMessage::DESTROY_CONSUMER: {
diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp
index 5bd5794..106e686 100644
--- a/libs/input/tests/MotionPredictor_test.cpp
+++ b/libs/input/tests/MotionPredictor_test.cpp
@@ -70,7 +70,7 @@
}
TEST(JerkTrackerTest, JerkReadiness) {
- JerkTracker jerkTracker(true);
+ JerkTracker jerkTracker(/*normalizedDt=*/true, /*alpha=*/1);
EXPECT_FALSE(jerkTracker.jerkMagnitude());
jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
EXPECT_FALSE(jerkTracker.jerkMagnitude());
@@ -87,8 +87,8 @@
}
TEST(JerkTrackerTest, JerkCalculationNormalizedDtTrue) {
- JerkTracker jerkTracker(true);
- jerkTracker.setForgetFactor(.5);
+ const float alpha = .5;
+ JerkTracker jerkTracker(/*normalizedDt=*/true, alpha);
jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
jerkTracker.pushSample(/*timestamp=*/1, 25, 53);
jerkTracker.pushSample(/*timestamp=*/2, 30, 60);
@@ -119,14 +119,13 @@
* y'': 3 -> -15
* y''': -18
*/
- const float newJerk = (1 - jerkTracker.getForgetFactor()) * std::hypot(10, -1) +
- jerkTracker.getForgetFactor() * std::hypot(-50, -18);
+ const float newJerk = (1 - alpha) * std::hypot(10, -1) + alpha * std::hypot(-50, -18);
EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), newJerk);
}
TEST(JerkTrackerTest, JerkCalculationNormalizedDtFalse) {
- JerkTracker jerkTracker(false);
- jerkTracker.setForgetFactor(.5);
+ const float alpha = .5;
+ JerkTracker jerkTracker(/*normalizedDt=*/false, alpha);
jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
jerkTracker.pushSample(/*timestamp=*/10, 25, 53);
jerkTracker.pushSample(/*timestamp=*/20, 30, 60);
@@ -157,13 +156,12 @@
* y'': .03 -> -.125 (delta above, divide by 10)
* y''': -.0155 (delta above, divide by 10)
*/
- const float newJerk = (1 - jerkTracker.getForgetFactor()) * std::hypot(.01, -.001) +
- jerkTracker.getForgetFactor() * std::hypot(-.0375, -.0155);
+ const float newJerk = (1 - alpha) * std::hypot(.01, -.001) + alpha * std::hypot(-.0375, -.0155);
EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), newJerk);
}
TEST(JerkTrackerTest, JerkCalculationAfterReset) {
- JerkTracker jerkTracker(true);
+ JerkTracker jerkTracker(/*normalizedDt=*/true, /*alpha=*/1);
jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
jerkTracker.pushSample(/*timestamp=*/1, 25, 53);
jerkTracker.pushSample(/*timestamp=*/2, 30, 60);
@@ -297,8 +295,11 @@
MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
[]() { return true /*enable prediction*/; });
+ // Create another instance of TfLiteMotionPredictorModel to read config details.
+ std::unique_ptr<TfLiteMotionPredictorModel> testTfLiteModel =
+ TfLiteMotionPredictorModel::create();
const float mediumJerk =
- (predictor.getModelConfig().lowJerk + predictor.getModelConfig().highJerk) / 2;
+ (testTfLiteModel->config().lowJerk + testTfLiteModel->config().highJerk) / 2;
const float a = 3; // initial acceleration
const float b = 4; // initial velocity
const float c = 5; // initial position
diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp
new file mode 100644
index 0000000..b372c0b
--- /dev/null
+++ b/libs/input/tests/Resampler_test.cpp
@@ -0,0 +1,858 @@
+/**
+ * Copyright 2024 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 <input/Resampler.h>
+
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <memory>
+#include <vector>
+
+#include <input/Input.h>
+#include <input/InputEventBuilders.h>
+#include <input/InputTransport.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+namespace {
+
+using namespace std::literals::chrono_literals;
+
+constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+
+struct Pointer {
+ int32_t id{0};
+ ToolType toolType{ToolType::FINGER};
+ float x{0.0f};
+ float y{0.0f};
+ bool isResampled{false};
+ /**
+ * Converts from Pointer to PointerCoords. Enables calling LegacyResampler methods and
+ * assertions only with the relevant data for tests.
+ */
+ operator PointerCoords() const;
+};
+
+Pointer::operator PointerCoords() const {
+ PointerCoords pointerCoords;
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ pointerCoords.isResampled = isResampled;
+ return pointerCoords;
+}
+
+struct InputSample {
+ std::chrono::milliseconds eventTime{0};
+ std::vector<Pointer> pointers{};
+
+ explicit InputSample(std::chrono::milliseconds eventTime, const std::vector<Pointer>& pointers)
+ : eventTime{eventTime}, pointers{pointers} {}
+ /**
+ * Converts from InputSample to InputMessage. Enables calling LegacyResampler methods only with
+ * the relevant data for tests.
+ */
+ operator InputMessage() const;
+};
+
+InputSample::operator InputMessage() const {
+ InputMessage message;
+ message.header.type = InputMessage::Type::MOTION;
+ message.body.motion.pointerCount = pointers.size();
+ message.body.motion.eventTime = static_cast<std::chrono::nanoseconds>(eventTime).count();
+ message.body.motion.source = AINPUT_SOURCE_CLASS_POINTER;
+ message.body.motion.downTime = 0;
+
+ const uint32_t pointerCount = message.body.motion.pointerCount;
+ for (uint32_t i = 0; i < pointerCount; ++i) {
+ message.body.motion.pointers[i].properties.id = pointers[i].id;
+ message.body.motion.pointers[i].properties.toolType = pointers[i].toolType;
+ message.body.motion.pointers[i].coords.setAxisValue(AMOTION_EVENT_AXIS_X, pointers[i].x);
+ message.body.motion.pointers[i].coords.setAxisValue(AMOTION_EVENT_AXIS_Y, pointers[i].y);
+ message.body.motion.pointers[i].coords.isResampled = pointers[i].isResampled;
+ }
+ return message;
+}
+
+struct InputStream {
+ std::vector<InputSample> samples{};
+ int32_t action{0};
+ DeviceId deviceId{0};
+ /**
+ * Converts from InputStream to MotionEvent. Enables calling LegacyResampler methods only with
+ * the relevant data for tests.
+ */
+ operator MotionEvent() const;
+};
+
+InputStream::operator MotionEvent() const {
+ const InputSample& firstSample{*samples.begin()};
+ MotionEventBuilder motionEventBuilder =
+ MotionEventBuilder(action, AINPUT_SOURCE_CLASS_POINTER)
+ .downTime(0)
+ .eventTime(static_cast<std::chrono::nanoseconds>(firstSample.eventTime).count())
+ .deviceId(deviceId);
+ for (const Pointer& pointer : firstSample.pointers) {
+ const PointerBuilder pointerBuilder =
+ PointerBuilder(pointer.id, pointer.toolType).x(pointer.x).y(pointer.y);
+ motionEventBuilder.pointer(pointerBuilder);
+ }
+ MotionEvent motionEvent = motionEventBuilder.build();
+ const size_t numSamples = samples.size();
+ for (size_t i = 1; i < numSamples; ++i) {
+ std::vector<PointerCoords> pointersCoords{samples[i].pointers.begin(),
+ samples[i].pointers.end()};
+ motionEvent.addSample(static_cast<std::chrono::nanoseconds>(samples[i].eventTime).count(),
+ pointersCoords.data(), motionEvent.getId());
+ }
+ return motionEvent;
+}
+
+} // namespace
+
+class ResamplerTest : public testing::Test {
+protected:
+ ResamplerTest() : mResampler(std::make_unique<LegacyResampler>()) {}
+
+ ~ResamplerTest() override {}
+
+ void SetUp() override {}
+
+ void TearDown() override {}
+
+ std::unique_ptr<Resampler> mResampler;
+
+ /**
+ * Checks that beforeCall and afterCall are equal except for the mutated attributes by addSample
+ * member function.
+ * @param beforeCall MotionEvent before passing it to resampleMotionEvent
+ * @param afterCall MotionEvent after passing it to resampleMotionEvent
+ */
+ void assertMotionEventMetaDataDidNotMutate(const MotionEvent& beforeCall,
+ const MotionEvent& afterCall);
+
+ /**
+ * Asserts the MotionEvent is resampled by checking an increment in history size and that the
+ * resampled coordinates are near the expected ones.
+ */
+ void assertMotionEventIsResampledAndCoordsNear(
+ const MotionEvent& original, const MotionEvent& resampled,
+ const std::vector<PointerCoords>& expectedCoords);
+
+ void assertMotionEventIsNotResampled(const MotionEvent& original,
+ const MotionEvent& notResampled);
+};
+
+void ResamplerTest::assertMotionEventMetaDataDidNotMutate(const MotionEvent& beforeCall,
+ const MotionEvent& afterCall) {
+ EXPECT_EQ(beforeCall.getDeviceId(), afterCall.getDeviceId());
+ EXPECT_EQ(beforeCall.getAction(), afterCall.getAction());
+ EXPECT_EQ(beforeCall.getActionButton(), afterCall.getActionButton());
+ EXPECT_EQ(beforeCall.getButtonState(), afterCall.getButtonState());
+ EXPECT_EQ(beforeCall.getFlags(), afterCall.getFlags());
+ EXPECT_EQ(beforeCall.getEdgeFlags(), afterCall.getEdgeFlags());
+ EXPECT_EQ(beforeCall.getClassification(), afterCall.getClassification());
+ EXPECT_EQ(beforeCall.getPointerCount(), afterCall.getPointerCount());
+ EXPECT_EQ(beforeCall.getMetaState(), afterCall.getMetaState());
+ EXPECT_EQ(beforeCall.getSource(), afterCall.getSource());
+ EXPECT_EQ(beforeCall.getXPrecision(), afterCall.getXPrecision());
+ EXPECT_EQ(beforeCall.getYPrecision(), afterCall.getYPrecision());
+ EXPECT_EQ(beforeCall.getDownTime(), afterCall.getDownTime());
+ EXPECT_EQ(beforeCall.getDisplayId(), afterCall.getDisplayId());
+}
+
+void ResamplerTest::assertMotionEventIsResampledAndCoordsNear(
+ const MotionEvent& original, const MotionEvent& resampled,
+ const std::vector<PointerCoords>& expectedCoords) {
+ assertMotionEventMetaDataDidNotMutate(original, resampled);
+
+ const size_t originalSampleSize = original.getHistorySize() + 1;
+ const size_t resampledSampleSize = resampled.getHistorySize() + 1;
+ EXPECT_EQ(originalSampleSize + 1, resampledSampleSize);
+
+ const size_t numPointers = resampled.getPointerCount();
+ const size_t beginLatestSample = resampledSampleSize - 1;
+ for (size_t i = 0; i < numPointers; ++i) {
+ SCOPED_TRACE(i);
+ EXPECT_EQ(original.getPointerId(i), resampled.getPointerId(i));
+ EXPECT_EQ(original.getToolType(i), resampled.getToolType(i));
+
+ const PointerCoords& resampledCoords =
+ resampled.getSamplePointerCoords()[beginLatestSample * numPointers + i];
+
+ EXPECT_TRUE(resampledCoords.isResampled);
+ EXPECT_NEAR(expectedCoords[i].getX(), resampledCoords.getX(), EPSILON);
+ EXPECT_NEAR(expectedCoords[i].getY(), resampledCoords.getY(), EPSILON);
+ }
+}
+
+void ResamplerTest::assertMotionEventIsNotResampled(const MotionEvent& original,
+ const MotionEvent& notResampled) {
+ assertMotionEventMetaDataDidNotMutate(original, notResampled);
+ const size_t originalSampleSize = original.getHistorySize() + 1;
+ const size_t notResampledSampleSize = notResampled.getHistorySize() + 1;
+ EXPECT_EQ(originalSampleSize, notResampledSampleSize);
+}
+
+TEST_F(ResamplerTest, NonResampledAxesArePreserved) {
+ constexpr float TOUCH_MAJOR_VALUE = 1.0f;
+
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ constexpr std::chrono::nanoseconds eventTime{10ms};
+ PointerCoords pointerCoords{};
+ pointerCoords.isResampled = false;
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, 2.0f);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, 2.0f);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, TOUCH_MAJOR_VALUE);
+
+ motionEvent.addSample(eventTime.count(), &pointerCoords, motionEvent.getId());
+
+ const InputMessage futureSample =
+ InputSample{15ms, {{.id = 0, .x = 3.0f, .y = 4.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+
+ EXPECT_EQ(motionEvent.getTouchMajor(0), TOUCH_MAJOR_VALUE);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 2.4f,
+ .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, SinglePointerNotEnoughDataToResample) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, SinglePointerDifferentDeviceIdBetweenMotionEvents) {
+ MotionEvent motionFromFirstDevice =
+ InputStream{{InputSample{4ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
+ InputSample{8ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE,
+ .deviceId = 0};
+
+ mResampler->resampleMotionEvent(10ms, motionFromFirstDevice, nullptr);
+
+ MotionEvent motionFromSecondDevice =
+ InputStream{{InputSample{11ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE,
+ .deviceId = 1};
+ const MotionEvent originalMotionEvent = motionFromSecondDevice;
+
+ mResampler->resampleMotionEvent(12ms, motionFromSecondDevice, nullptr);
+ // The MotionEvent should not be resampled because the second event came from a different device
+ // than the previous event.
+ assertMotionEventIsNotResampled(originalMotionEvent, motionFromSecondDevice);
+}
+
+// Increments of 16 ms for display refresh rate
+// Increments of 6 ms for input frequency
+// Resampling latency is known to be 5 ms
+// Therefore, first resampling time will be 11 ms
+
+/**
+ * Timeline
+ * ----+----------------------+---------+---------+---------+----------
+ * 0ms 10ms 11ms 15ms 16ms
+ * DOWN MOVE | MSG |
+ * resample frame
+ * Resampling occurs at 11ms. It is possible to interpolate because there is a sample available
+ * after the resample time. It is assumed that the InputMessage frequency is 100Hz, and the frame
+ * frequency is 60Hz. This means the time between InputMessage samples is 10ms, and the time between
+ * frames is ~16ms. Resample time is frameTime - RESAMPLE_LATENCY. The resampled sample must be the
+ * last one in the batch to consume.
+ */
+TEST_F(ResamplerTest, SinglePointerSingleSampleInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+ const InputMessage futureSample =
+ InputSample{15ms, {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.id = 0,
+ .x = 1.2f,
+ .y = 2.4f,
+ .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, SinglePointerDeltaTooSmallInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+ const InputMessage futureSample =
+ InputSample{11ms, {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(10'500'000ns, motionEvent, &futureSample);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+/**
+ * Tests extrapolation given two MotionEvents with a single sample.
+ */
+TEST_F(ResamplerTest, SinglePointerSingleSampleExtrapolation) {
+ MotionEvent firstMotionEvent =
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(9ms, firstMotionEvent, nullptr);
+
+ MotionEvent secondMotionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(11ms, secondMotionEvent, nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, secondMotionEvent,
+ {Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 4.4f,
+ .isResampled = true}});
+ // Integrity of the whole motionEvent
+ // History size should increment by 1
+ // Check if the resampled value is the last one
+ // Check if the resampleTime is correct
+ // Check if the PointerCoords are consistent with the other computations
+}
+
+TEST_F(ResamplerTest, SinglePointerMultipleSampleInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}},
+ InputSample{10ms,
+ {{.id = 0, .x = 2.0f, .y = 3.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample =
+ InputSample{15ms, {{.id = 0, .x = 3.0f, .y = 5.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 3.4f,
+ .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, SinglePointerMultipleSampleExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}},
+ InputSample{10ms,
+ {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.id = 0,
+ .x = 2.2f,
+ .y = 4.4f,
+ .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, SinglePointerDeltaTooSmallExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{9ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}},
+ InputSample{10ms,
+ {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, nullptr);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, SinglePointerDeltaTooLargeExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}},
+ InputSample{26ms,
+ {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(27ms, motionEvent, nullptr);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, SinglePointerResampleTimeTooFarExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms, {{.id = 0, .x = 1.0f, .y = 2.0f, .isResampled = false}}},
+ InputSample{25ms,
+ {{.id = 0, .x = 2.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(43ms, motionEvent, nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.id = 0,
+ .x = 2.4f,
+ .y = 4.8f,
+ .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, MultiplePointerSingleSampleInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample =
+ InputSample{15ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.x = 2.2f, .y = 2.2f, .isResampled = true},
+ Pointer{.x = 3.2f, .y = 3.2f, .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, MultiplePointerSingleSampleExtrapolation) {
+ MotionEvent firstMotionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr);
+
+ MotionEvent secondMotionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, secondMotionEvent,
+ {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true},
+ Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, MultiplePointerMultipleSampleInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}},
+ InputSample{10ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+ const InputMessage futureSample =
+ InputSample{15ms,
+ {{.id = 0, .x = 5.0f, .y = 5.0f, .isResampled = false},
+ {.id = 1, .x = 6.0f, .y = 6.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true},
+ Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, MultiplePointerMultipleSampleExtrapolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}},
+ InputSample{10ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true},
+ Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, MultiplePointerIncreaseNumPointersInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample =
+ InputSample{15ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
+ {Pointer{.x = 1.4f, .y = 1.4f, .isResampled = true},
+ Pointer{.x = 2.4f, .y = 2.4f, .isResampled = true}});
+
+ MotionEvent secondMotionEvent =
+ InputStream{{InputSample{25ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage secondFutureSample =
+ InputSample{30ms,
+ {{.id = 0, .x = 5.0f, .y = 5.0f, .isResampled = false},
+ {.id = 1, .x = 6.0f, .y = 6.0f, .isResampled = false},
+ {.id = 2, .x = 7.0f, .y = 7.0f, .isResampled = false}}};
+
+ const MotionEvent originalSecondMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(27ms, secondMotionEvent, &secondFutureSample);
+
+ assertMotionEventIsResampledAndCoordsNear(originalSecondMotionEvent, secondMotionEvent,
+ {Pointer{.x = 3.8f, .y = 3.8f, .isResampled = true},
+ Pointer{.x = 4.8f, .y = 4.8f, .isResampled = true},
+ Pointer{.x = 5.8f, .y = 5.8f, .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, MultiplePointerIncreaseNumPointersExtrapolation) {
+ MotionEvent firstMotionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr);
+
+ MotionEvent secondMotionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent secondOriginalMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerDecreaseNumPointersInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 2, .x = 5.0f, .y = 5.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample =
+ InputSample{15ms,
+ {{.id = 0, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 1, .x = 5.0f, .y = 5.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerDecreaseNumPointersExtrapolation) {
+ MotionEvent firstMotionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false},
+ {.id = 2, .x = 3.0f, .y = 3.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr);
+
+ MotionEvent secondMotionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false},
+ {.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent secondOriginalMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsResampledAndCoordsNear(secondOriginalMotionEvent, secondMotionEvent,
+ {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true},
+ Pointer{.x = 4.4f, .y = 4.4f, .isResampled = true}});
+}
+
+TEST_F(ResamplerTest, MultiplePointerDifferentIdOrderInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample =
+ InputSample{15ms,
+ {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerDifferentIdOrderExtrapolation) {
+ MotionEvent firstMotionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr);
+
+ MotionEvent secondMotionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent secondOriginalMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerDifferentIdsInterpolation) {
+ MotionEvent motionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample =
+ InputSample{15ms,
+ {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 2, .x = 3.0f, .y = 3.0f, .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerDifferentIdsExtrapolation) {
+ MotionEvent firstMotionEvent =
+ InputStream{{InputSample{5ms,
+ {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false},
+ {.id = 1, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr);
+
+ MotionEvent secondMotionEvent =
+ InputStream{{InputSample{10ms,
+ {{.id = 1, .x = 4.0f, .y = 4.0f, .isResampled = false},
+ {.id = 2, .x = 3.0f, .y = 3.0f, .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent secondOriginalMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerDifferentToolTypeInterpolation) {
+ MotionEvent motionEvent = InputStream{{InputSample{10ms,
+ {{.id = 0,
+ .toolType = ToolType::FINGER,
+ .x = 1.0f,
+ .y = 1.0f,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::FINGER,
+ .x = 2.0f,
+ .y = 2.0f,
+ .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample = InputSample{15ms,
+ {{.id = 0,
+ .toolType = ToolType::FINGER,
+ .x = 3.0,
+ .y = 3.0,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::STYLUS,
+ .x = 4.0,
+ .y = 4.0,
+ .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerDifferentToolTypeExtrapolation) {
+ MotionEvent firstMotionEvent = InputStream{{InputSample{5ms,
+ {{.id = 0,
+ .toolType = ToolType::FINGER,
+ .x = 1.0f,
+ .y = 1.0f,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::FINGER,
+ .x = 2.0f,
+ .y = 2.0f,
+ .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ mResampler->resampleMotionEvent(9ms, firstMotionEvent, /*futureSample=*/nullptr);
+
+ MotionEvent secondMotionEvent = InputStream{{InputSample{10ms,
+ {{.id = 0,
+ .toolType = ToolType::FINGER,
+ .x = 1.0f,
+ .y = 1.0f,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::STYLUS,
+ .x = 2.0f,
+ .y = 2.0f,
+ .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent secondOriginalMotionEvent = secondMotionEvent;
+
+ mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerShouldNotResampleToolTypeInterpolation) {
+ MotionEvent motionEvent = InputStream{{InputSample{10ms,
+ {{.id = 0,
+ .toolType = ToolType::PALM,
+ .x = 1.0f,
+ .y = 1.0f,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::PALM,
+ .x = 2.0f,
+ .y = 2.0f,
+ .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const InputMessage futureSample = InputSample{15ms,
+ {{.id = 0,
+ .toolType = ToolType::PALM,
+ .x = 3.0,
+ .y = 3.0,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::PALM,
+ .x = 4.0,
+ .y = 4.0,
+ .isResampled = false}}};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+
+TEST_F(ResamplerTest, MultiplePointerShouldNotResampleToolTypeExtrapolation) {
+ MotionEvent motionEvent = InputStream{{InputSample{5ms,
+ {{.id = 0,
+ .toolType = ToolType::PALM,
+ .x = 1.0f,
+ .y = 1.0f,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::PALM,
+ .x = 2.0f,
+ .y = 2.0f,
+ .isResampled = false}}},
+ InputSample{10ms,
+ {{.id = 0,
+ .toolType = ToolType::PALM,
+ .x = 3.0f,
+ .y = 3.0f,
+ .isResampled = false},
+ {.id = 1,
+ .toolType = ToolType::PALM,
+ .x = 4.0f,
+ .y = 4.0f,
+ .isResampled = false}}}},
+ AMOTION_EVENT_ACTION_MOVE};
+
+ const MotionEvent originalMotionEvent = motionEvent;
+
+ mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr);
+
+ assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
+}
+} // namespace android
diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h
index 0dac662..7278d2d 100644
--- a/libs/math/include/math/TVecHelpers.h
+++ b/libs/math/include/math/TVecHelpers.h
@@ -620,15 +620,10 @@
} // namespace details
} // namespace android
-namespace std {
- template<template<typename T> class VECTOR, typename T>
- struct hash<VECTOR<T>> {
- static constexpr bool IS_VECTOR =
- std::is_base_of<android::details::TVecUnaryOperators<VECTOR, T>, VECTOR<T>>::value;
-
- typename std::enable_if<IS_VECTOR, size_t>::type
- operator()(const VECTOR<T>& v) const {
- return v.hash();
- }
- };
-}
+#define TVECHELPERS_STD_HASH(VECTOR) \
+ template <typename T> \
+ struct std::hash<VECTOR<T>> { \
+ size_t operator()(const VECTOR<T>& v) const { \
+ return v.hash(); \
+ } \
+ }
diff --git a/libs/math/include/math/mat2.h b/libs/math/include/math/mat2.h
index 3e6cd4c..24c2bad 100644
--- a/libs/math/include/math/mat2.h
+++ b/libs/math/include/math/mat2.h
@@ -373,5 +373,7 @@
// ----------------------------------------------------------------------------------------
} // namespace android
+TVECHELPERS_STD_HASH(android::details::TMat22);
+
#undef PURE
#undef CONSTEXPR
diff --git a/libs/math/include/math/mat3.h b/libs/math/include/math/mat3.h
index 5c8a9b2..4647a60 100644
--- a/libs/math/include/math/mat3.h
+++ b/libs/math/include/math/mat3.h
@@ -436,5 +436,7 @@
// ----------------------------------------------------------------------------------------
} // namespace android
+TVECHELPERS_STD_HASH(android::details::TMat33);
+
#undef PURE
#undef CONSTEXPR
diff --git a/libs/math/include/math/mat4.h b/libs/math/include/math/mat4.h
index c630d97..c9e118a 100644
--- a/libs/math/include/math/mat4.h
+++ b/libs/math/include/math/mat4.h
@@ -590,5 +590,7 @@
// ----------------------------------------------------------------------------------------
} // namespace android
+TVECHELPERS_STD_HASH(android::details::TMat44);
+
#undef PURE
#undef CONSTEXPR
diff --git a/libs/math/include/math/quat.h b/libs/math/include/math/quat.h
index 07573c5..43c8038 100644
--- a/libs/math/include/math/quat.h
+++ b/libs/math/include/math/quat.h
@@ -187,6 +187,8 @@
// ----------------------------------------------------------------------------------------
} // namespace android
+TVECHELPERS_STD_HASH(android::details::TQuaternion);
+
#pragma clang diagnostic pop
#undef PURE
diff --git a/libs/math/include/math/vec2.h b/libs/math/include/math/vec2.h
index e0adb7f..909c77e 100644
--- a/libs/math/include/math/vec2.h
+++ b/libs/math/include/math/vec2.h
@@ -122,4 +122,6 @@
// ----------------------------------------------------------------------------------------
} // namespace android
+TVECHELPERS_STD_HASH(android::details::TVec2);
+
#pragma clang diagnostic pop
diff --git a/libs/math/include/math/vec3.h b/libs/math/include/math/vec3.h
index 21fb684..ff2b3e4 100644
--- a/libs/math/include/math/vec3.h
+++ b/libs/math/include/math/vec3.h
@@ -128,4 +128,6 @@
// ----------------------------------------------------------------------------------------
} // namespace android
+TVECHELPERS_STD_HASH(android::details::TVec3);
+
#pragma clang diagnostic pop
diff --git a/libs/math/include/math/vec4.h b/libs/math/include/math/vec4.h
index 1e279fe..16509c9 100644
--- a/libs/math/include/math/vec4.h
+++ b/libs/math/include/math/vec4.h
@@ -125,4 +125,6 @@
// ----------------------------------------------------------------------------------------
} // namespace android
+TVECHELPERS_STD_HASH(android::details::TVec4);
+
#pragma clang diagnostic pop
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index dd78049..ca41346 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -196,10 +196,10 @@
return BAD_VALUE;
}
- if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
+ if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK) ||
+ usage == 0) {
ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
- "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+ "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
return BAD_VALUE;
}
@@ -248,10 +248,10 @@
if (!buffer) return BAD_VALUE;
- if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
+ if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK) ||
+ usage == 0) {
ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
- "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+ "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
return BAD_VALUE;
}
@@ -277,10 +277,10 @@
int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) {
if (!buffer || !outPlanes) return BAD_VALUE;
- if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
- AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
+ if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK) ||
+ usage == 0) {
ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
- " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+ " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
return BAD_VALUE;
}
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index ecf98c6..7639fab 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -101,6 +101,7 @@
"skia/debug/SkiaMemoryReporter.cpp",
"skia/filters/BlurFilter.cpp",
"skia/filters/GaussianBlurFilter.cpp",
+ "skia/filters/KawaseBlurDualFilter.cpp",
"skia/filters/KawaseBlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
"skia/filters/MouriMap.cpp",
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
index 05a2063..326d1ce 100644
--- a/libs/renderengine/benchmark/RenderEngineBench.cpp
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -64,14 +64,15 @@
return std::pair<uint32_t, uint32_t>(width, height);
}
-static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::Threaded threaded,
- RenderEngine::GraphicsApi graphicsApi) {
+static std::unique_ptr<RenderEngine> createRenderEngine(
+ RenderEngine::Threaded threaded, RenderEngine::GraphicsApi graphicsApi,
+ RenderEngine::BlurAlgorithm blurAlgorithm = RenderEngine::BlurAlgorithm::KAWASE) {
auto args = RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
.setImageCacheSize(1)
.setEnableProtectedContext(true)
.setPrecacheToneMapperShaderOnly(false)
- .setBlurAlgorithm(renderengine::RenderEngine::BlurAlgorithm::KAWASE)
+ .setBlurAlgorithm(blurAlgorithm)
.setContextPriority(RenderEngine::ContextPriority::REALTIME)
.setThreaded(threaded)
.setGraphicsApi(graphicsApi)
@@ -180,7 +181,8 @@
void BM_blur(benchmark::State& benchState, Args&&... args) {
auto args_tuple = std::make_tuple(std::move(args)...);
auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)),
- static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple)));
+ static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple)),
+ static_cast<RenderEngine::BlurAlgorithm>(std::get<2>(args_tuple)));
// Initially use cpu access so we can decode into it with AImageDecoder.
auto [width, height] = getDisplaySize();
@@ -224,5 +226,11 @@
benchDrawLayers(*re, layers, benchState, "blurred");
}
-BENCHMARK_CAPTURE(BM_blur, SkiaGLThreaded, RenderEngine::Threaded::YES,
- RenderEngine::GraphicsApi::GL);
+BENCHMARK_CAPTURE(BM_blur, gaussian, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL,
+ RenderEngine::BlurAlgorithm::GAUSSIAN);
+
+BENCHMARK_CAPTURE(BM_blur, kawase, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL,
+ RenderEngine::BlurAlgorithm::KAWASE);
+
+BENCHMARK_CAPTURE(BM_blur, kawase_dual_filter, RenderEngine::Threaded::YES,
+ RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER);
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 7207394..9bc2c48 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -131,6 +131,7 @@
NONE,
GAUSSIAN,
KAWASE,
+ KAWASE_DUAL_FILTER,
};
static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
index b5cb21b..390ad6e 100644
--- a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
+++ b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
@@ -23,6 +23,7 @@
#include <include/gpu/graphite/BackendSemaphore.h>
#include <include/gpu/graphite/Context.h>
#include <include/gpu/graphite/Recording.h>
+#include <include/gpu/graphite/vk/VulkanGraphiteTypes.h>
#include <log/log_main.h>
#include <sync/sync.h>
@@ -77,7 +78,7 @@
base::unique_fd fenceDup(dupedFd);
VkSemaphore waitSemaphore =
getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release());
- graphite::BackendSemaphore beSemaphore(waitSemaphore);
+ auto beSemaphore = graphite::BackendSemaphores::MakeVulkan(waitSemaphore);
mStagedWaitSemaphores.push_back(beSemaphore);
}
@@ -92,7 +93,7 @@
// This "signal" semaphore is called after rendering, but it is cleaned up in the same mechanism
// as "wait" semaphores from waitFence.
VkSemaphore vkSignalSemaphore = vulkanInterface.createExportableSemaphore();
- graphite::BackendSemaphore backendSignalSemaphore(vkSignalSemaphore);
+ auto backendSignalSemaphore = graphite::BackendSemaphores::MakeVulkan(vkSignalSemaphore);
// Collect all Vk semaphores that DestroySemaphoreInfo needs to own and delete after GPU work.
std::vector<VkSemaphore> vkSemaphoresToCleanUp;
@@ -100,7 +101,8 @@
vkSemaphoresToCleanUp.push_back(vkSignalSemaphore);
}
for (auto backendWaitSemaphore : mStagedWaitSemaphores) {
- vkSemaphoresToCleanUp.push_back(backendWaitSemaphore.getVkSemaphore());
+ vkSemaphoresToCleanUp.push_back(
+ graphite::BackendSemaphores::GetVkSemaphore(backendWaitSemaphore));
}
DestroySemaphoreInfo* destroySemaphoreInfo = nullptr;
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 9709cd1..d58f303 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -76,6 +76,7 @@
#include "compat/SkiaGpuContext.h"
#include "filters/BlurFilter.h"
#include "filters/GaussianBlurFilter.h"
+#include "filters/KawaseBlurDualFilter.h"
#include "filters/KawaseBlurFilter.h"
#include "filters/LinearEffect.h"
#include "filters/MouriMap.h"
@@ -285,6 +286,11 @@
mBlurFilter = new KawaseBlurFilter();
break;
}
+ case BlurAlgorithm::KAWASE_DUAL_FILTER: {
+ ALOGD("Background Blurs Enabled (Kawase dual-filtering algorithm)");
+ mBlurFilter = new KawaseBlurDualFilter();
+ break;
+ }
default: {
mBlurFilter = nullptr;
break;
diff --git a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
index 1dbcc29..4164c4b 100644
--- a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
+++ b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.cpp
@@ -19,6 +19,7 @@
#include <SkRuntimeEffect.h>
#include <SkStream.h>
#include <SkString.h>
+#include <com_android_graphics_libgui_flags.h>
#include "log/log_main.h"
namespace android::renderengine::skia {
@@ -56,25 +57,33 @@
}
)");
+EdgeExtensionShaderFactory::EdgeExtensionShaderFactory() {
+ if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
+ return;
+ }
+ mResult = std::make_unique<SkRuntimeEffect::Result>(SkRuntimeEffect::MakeForShader(edgeShader));
+ LOG_ALWAYS_FATAL_IF(!mResult->errorText.isEmpty(),
+ "EdgeExtensionShaderFactory compilation "
+ "failed with an unexpected error: %s",
+ mResult->errorText.c_str());
+}
+
sk_sp<SkShader> EdgeExtensionShaderFactory::createSkShader(const sk_sp<SkShader>& inputShader,
const LayerSettings& layer,
- const SkRect& imageBounds) {
- if (mBuilder == nullptr) {
- const static SkRuntimeEffect::Result instance = SkRuntimeEffect::MakeForShader(edgeShader);
- if (!instance.errorText.isEmpty()) {
- ALOGE("EdgeExtensionShaderFactory terminated with an error: %s",
- instance.errorText.c_str());
- return nullptr;
- }
- mBuilder = std::make_unique<SkRuntimeShaderBuilder>(instance.effect);
- }
- mBuilder->child("uContentTexture") = inputShader;
+ const SkRect& imageBounds) const {
+ LOG_ALWAYS_FATAL_IF(mResult == nullptr,
+ "EdgeExtensionShaderFactory did not initialize mResult. "
+ "This means that we unexpectedly applied the edge extension shader");
+
+ SkRuntimeShaderBuilder builder = SkRuntimeShaderBuilder(mResult->effect);
+
+ builder.child("uContentTexture") = inputShader;
if (imageBounds.isEmpty()) {
- mBuilder->uniform("uImgSize") = SkPoint{layer.geometry.boundaries.getWidth(),
- layer.geometry.boundaries.getHeight()};
+ builder.uniform("uImgSize") = SkPoint{layer.geometry.boundaries.getWidth(),
+ layer.geometry.boundaries.getHeight()};
} else {
- mBuilder->uniform("uImgSize") = SkPoint{imageBounds.width(), imageBounds.height()};
+ builder.uniform("uImgSize") = SkPoint{imageBounds.width(), imageBounds.height()};
}
- return mBuilder->makeShader();
+ return builder.makeShader();
}
} // namespace android::renderengine::skia
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h
index b0a8a93..17c6b91 100644
--- a/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h
+++ b/libs/renderengine/skia/filters/EdgeExtensionShaderFactory.h
@@ -33,10 +33,12 @@
*/
class EdgeExtensionShaderFactory {
public:
+ EdgeExtensionShaderFactory();
+
sk_sp<SkShader> createSkShader(const sk_sp<SkShader>& inputShader, const LayerSettings& layer,
- const SkRect& imageBounds);
+ const SkRect& imageBounds) const;
private:
- std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
+ std::unique_ptr<const SkRuntimeEffect::Result> mResult;
};
} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
new file mode 100644
index 0000000..db0b133
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2024 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "KawaseBlurDualFilter.h"
+#include <SkAlphaType.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkShader.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <SkTileMode.h>
+#include <include/gpu/GpuTypes.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+KawaseBlurDualFilter::KawaseBlurDualFilter() : BlurFilter() {
+ // A shader to sample each vertex of a unit regular heptagon
+ // plus the original fragment coordinate.
+ SkString blurString(R"(
+ uniform shader child;
+ uniform float in_blurOffset;
+ uniform float in_crossFade;
+
+ const float2 STEP_0 = float2( 1.0, 0.0);
+ const float2 STEP_1 = float2( 0.623489802, 0.781831482);
+ const float2 STEP_2 = float2(-0.222520934, 0.974927912);
+ const float2 STEP_3 = float2(-0.900968868, 0.433883739);
+ const float2 STEP_4 = float2( 0.900968868, -0.433883739);
+ const float2 STEP_5 = float2(-0.222520934, -0.974927912);
+ const float2 STEP_6 = float2(-0.623489802, -0.781831482);
+
+ half4 main(float2 xy) {
+ half3 c = child.eval(xy).rgb;
+
+ c += child.eval(xy + STEP_0 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_1 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_2 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_3 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_4 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_5 * in_blurOffset).rgb;
+ c += child.eval(xy + STEP_6 * in_blurOffset).rgb;
+
+ return half4(c * 0.125 * in_crossFade, in_crossFade);
+ }
+ )");
+
+ auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
+ LOG_ALWAYS_FATAL_IF(!blurEffect, "RuntimeShader error: %s", error.c_str());
+ mBlurEffect = std::move(blurEffect);
+}
+
+static sk_sp<SkSurface> makeSurface(SkiaGpuContext* context, const SkRect& origRect, int scale) {
+ SkImageInfo scaledInfo =
+ SkImageInfo::MakeN32Premul(ceil(static_cast<float>(origRect.width()) / scale),
+ ceil(static_cast<float>(origRect.height()) / scale));
+ return context->createRenderTarget(scaledInfo);
+}
+
+void KawaseBlurDualFilter::blurInto(const sk_sp<SkSurface>& drawSurface,
+ const sk_sp<SkImage>& readImage, const float radius,
+ const float alpha) const {
+ const float scale = static_cast<float>(drawSurface->width()) / readImage->width();
+ SkMatrix blurMatrix = SkMatrix::Scale(scale, scale);
+ blurInto(drawSurface,
+ readImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone),
+ blurMatrix),
+ readImage->width() / static_cast<float>(drawSurface->width()), radius, alpha);
+}
+
+void KawaseBlurDualFilter::blurInto(const sk_sp<SkSurface>& drawSurface, sk_sp<SkShader> input,
+ const float inverseScale, const float radius,
+ const float alpha) const {
+ SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
+ blurBuilder.child("child") = std::move(input);
+ blurBuilder.uniform("in_inverseScale") = inverseScale;
+ blurBuilder.uniform("in_blurOffset") = radius;
+ blurBuilder.uniform("in_crossFade") = alpha;
+ SkPaint paint;
+ paint.setShader(blurBuilder.makeShader(nullptr));
+ paint.setBlendMode(alpha == 1.0f ? SkBlendMode::kSrc : SkBlendMode::kSrcOver);
+ drawSurface->getCanvas()->drawPaint(paint);
+}
+
+sk_sp<SkImage> KawaseBlurDualFilter::generate(SkiaGpuContext* context, const uint32_t blurRadius,
+ const sk_sp<SkImage> input,
+ const SkRect& blurRect) const {
+ // Apply a conversion factor of (1 / sqrt(3)) to match Skia's built-in blur as used by
+ // RenderEffect. See the comment in SkBlurMask.cpp for reasoning behind this.
+ const float radius = blurRadius * 0.57735f;
+
+ // Use a variable number of blur passes depending on the radius. The non-integer part of this
+ // calculation is used to mix the final pass into the second-last with an alpha blend.
+ constexpr int kMaxSurfaces = 4;
+ const float filterDepth =
+ std::min(kMaxSurfaces - 1.0f, 1.0f + std::max(0.0f, log2f(radius * kInputScale)));
+ const int filterPasses = std::min(kMaxSurfaces - 1, static_cast<int>(ceil(filterDepth)));
+
+ // Render into surfaces downscaled by 1x, 1x, 2x, and 4x from the initial downscale.
+ sk_sp<SkSurface> surfaces[kMaxSurfaces] =
+ {filterPasses >= 0 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr,
+ filterPasses >= 1 ? makeSurface(context, blurRect, 1 * kInverseInputScale) : nullptr,
+ filterPasses >= 2 ? makeSurface(context, blurRect, 2 * kInverseInputScale) : nullptr,
+ filterPasses >= 3 ? makeSurface(context, blurRect, 4 * kInverseInputScale) : nullptr};
+
+ // These weights for scaling offsets per-pass are handpicked to look good at 1 <= radius <= 600.
+ static const float kWeights[7] = {1.0f, 2.0f, 3.5f, 1.0f, 2.0f, 2.0f, 2.0f};
+
+ // Kawase is an approximation of Gaussian, but behaves differently because it is made up of many
+ // simpler blurs. A transformation is required to approximate the same effect as Gaussian.
+ float sumSquaredR = powf(kWeights[0] * powf(2.0f, 1), 2.0f);
+ for (int i = 0; i < filterPasses; i++) {
+ const float alpha = std::min(1.0f, filterDepth - i);
+ sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[1 + i], 2.0f);
+ sumSquaredR += powf(powf(2.0f, i + 1) * alpha * kWeights[6 - i], 2.0f);
+ }
+ // Solve for R = sqrt(sum(r_i^2)). Divide R by hypot(1,1) to find some (x,y) offsets.
+ const float step = M_SQRT1_2 *
+ sqrtf(max(0.0f, (powf(radius, 2.0f) - powf(kInverseInputScale, 2.0f)) / sumSquaredR));
+
+ // Start by downscaling and doing the first blur pass.
+ {
+ // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
+ // case one may expect Translate(blurRect.fLeft, blurRect.fTop) * Scale(kInverseInputScale)
+ // but instead we must do the inverse.
+ SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
+ blurMatrix.postScale(kInputScale, kInputScale);
+ const auto sourceShader =
+ input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone),
+ blurMatrix);
+ blurInto(surfaces[0], std::move(sourceShader), kInputScale, kWeights[0] * step, 1.0f);
+ }
+ // Next the remaining downscale blur passes.
+ for (int i = 0; i < filterPasses; i++) {
+ blurInto(surfaces[i + 1], surfaces[i]->makeImageSnapshot(), kWeights[1 + i] * step, 1.0f);
+ }
+ // Finally blur+upscale back to our original size.
+ for (int i = filterPasses - 1; i >= 0; i--) {
+ blurInto(surfaces[i], surfaces[i + 1]->makeImageSnapshot(), kWeights[6 - i] * step,
+ std::min(1.0f, filterDepth - i));
+ }
+ return surfaces[0]->makeImageSnapshot();
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/KawaseBlurDualFilter.h b/libs/renderengine/skia/filters/KawaseBlurDualFilter.h
new file mode 100644
index 0000000..6f4adbf
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurDualFilter.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2024 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 <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+#include "BlurFilter.h"
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Kawase blur with dual-filtering passes, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_slides.pdf
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
+class KawaseBlurDualFilter : public BlurFilter {
+public:
+ explicit KawaseBlurDualFilter();
+ virtual ~KawaseBlurDualFilter() {}
+
+ // Execute blur, saving it to a texture
+ sk_sp<SkImage> generate(SkiaGpuContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override;
+
+private:
+ sk_sp<SkRuntimeEffect> mBlurEffect;
+
+ void blurInto(const sk_sp<SkSurface>& drawSurface, const sk_sp<SkImage>& readImage,
+ const float radius, const float alpha) const;
+
+ void blurInto(const sk_sp<SkSurface>& drawSurface, const sk_sp<SkShader> input,
+ const float inverseScale, const float radius, const float alpha) const;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp
index 84852ea..bec9255 100644
--- a/libs/sensor/SensorEventQueue.cpp
+++ b/libs/sensor/SensorEventQueue.cpp
@@ -42,10 +42,11 @@
// ----------------------------------------------------------------------------
SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection,
- SensorManager& sensorManager)
+ SensorManager& sensorManager, String8 packageName)
: mSensorEventConnection(connection),
mRecBuffer(nullptr),
mSensorManager(sensorManager),
+ mPackageName(packageName),
mAvailable(0),
mConsumed(0),
mNumAcksToSend(0) {
@@ -92,8 +93,11 @@
mSensorManager.getSensorNameByHandle(events->sensor);
if (sensorName.has_value()) {
char buffer[UINT8_MAX];
- std::snprintf(buffer, sizeof(buffer), "Sensor event from %s",
- sensorName.value().data());
+ IPCThreadState* thread = IPCThreadState::self();
+ pid_t pid = (thread != nullptr) ? thread->getCallingPid() : -1;
+ std::snprintf(buffer, sizeof(buffer),
+ "Sensor event from %s to %s PID: %d (%zu/%zu)",
+ sensorName.value().data(), mPackageName.c_str(), pid, i, count);
ATRACE_INSTANT_FOR_TRACK(LOG_TAG, buffer);
}
}
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 3ca6f0f..7b4a86c 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -73,8 +73,6 @@
return deviceId;
}
}
- } else {
- ALOGW("Cannot get virtualdevice_native service");
}
return DEVICE_ID_DEFAULT;
}
@@ -86,7 +84,7 @@
std::ostringstream oss;
oss << sensor.getStringType() << ":" << sensor.getName();
if (outString) {
- *outString = std::move(oss.str());
+ *outString = oss.str();
}
return true;
}
@@ -403,7 +401,7 @@
ALOGE("createEventQueue: connection is NULL.");
return nullptr;
}
- queue = new SensorEventQueue(connection, *this);
+ queue = new SensorEventQueue(connection, *this, packageName);
break;
}
return queue;
diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h
index 0bcaadc..d31def7 100644
--- a/libs/sensor/include/sensor/SensorEventQueue.h
+++ b/libs/sensor/include/sensor/SensorEventQueue.h
@@ -20,9 +20,10 @@
#include <sys/types.h>
#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Timers.h>
#include <sensor/BitTube.h>
@@ -67,7 +68,7 @@
static constexpr int32_t SENSOR_DELAY_NORMAL = 200000;
explicit SensorEventQueue(const sp<ISensorEventConnection>& connection,
- SensorManager& sensorManager);
+ SensorManager& sensorManager, String8 packageName);
virtual ~SensorEventQueue();
virtual void onFirstRef();
@@ -110,6 +111,7 @@
mutable sp<Looper> mLooper;
ASensorEvent* mRecBuffer;
SensorManager& mSensorManager;
+ String8 mPackageName;
size_t mAvailable;
size_t mConsumed;
uint32_t mNumAcksToSend;
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 42dd85e..23249fa 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -110,10 +110,12 @@
return mMatrix[i];
}
+// x translate
float Transform::tx() const {
return mMatrix[2][0];
}
+// y translate
float Transform::ty() const {
return mMatrix[2][1];
}
@@ -167,11 +169,15 @@
}
}
-void Transform::set(float a, float b, float c, float d) {
+// x and y are the coordinates in the destination (i.e. the screen)
+// s and t are the coordinates in the source (i.e. the texture)
+// d means derivative
+// dsdx means ds/dx derivative of s with respect to x, etc.
+void Transform::set(float dsdx, float dtdy, float dtdx, float dsdy) {
mat33& M(mMatrix);
- M[0][0] = a; M[1][0] = b;
- M[0][1] = c; M[1][1] = d;
- M[0][2] = 0; M[1][2] = 0;
+ M[0][0] = dsdx; M[1][0] = dtdy;
+ M[0][1] = dtdx; M[1][1] = dsdy;
+ M[0][2] = 0; M[1][2] = 0;
mType = UNKNOWN_TYPE;
}
diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h
index 9aae145..a75ba37 100644
--- a/libs/ui/include/ui/Fence.h
+++ b/libs/ui/include/ui/Fence.h
@@ -156,6 +156,6 @@
base::unique_fd mFenceFd;
};
-}; // namespace android
+} // namespace android
#endif // ANDROID_FENCE_H
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index 652d8ba..936bf8f 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -297,6 +297,6 @@
mDeathCallbacks;
};
-}; // namespace android
+} // namespace android
#endif // ANDROID_GRAPHIC_BUFFER_H
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index bbb2d77..97ed05a 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -137,6 +137,6 @@
};
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_BUFFER_ALLOCATOR_H
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 9da1447..91aabe9 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -188,7 +188,7 @@
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_UI_BUFFER_MAPPER_H
diff --git a/libs/ui/include/ui/PixelFormat.h b/libs/ui/include/ui/PixelFormat.h
index cf5c2e8..1f20787 100644
--- a/libs/ui/include/ui/PixelFormat.h
+++ b/libs/ui/include/ui/PixelFormat.h
@@ -72,6 +72,6 @@
uint32_t bytesPerPixel(PixelFormat format);
-}; // namespace android
+} // namespace android
#endif // UI_PIXELFORMAT_H
diff --git a/libs/ui/include/ui/Point.h b/libs/ui/include/ui/Point.h
index d050ede..97a54be 100644
--- a/libs/ui/include/ui/Point.h
+++ b/libs/ui/include/ui/Point.h
@@ -83,6 +83,6 @@
ANDROID_BASIC_TYPES_TRAITS(Point)
-}; // namespace android
+} // namespace android
#endif // ANDROID_UI_POINT
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 9e24a07..2eb9330 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -233,7 +233,7 @@
ANDROID_BASIC_TYPES_TRAITS(Rect)
-}; // namespace android
+} // namespace android
namespace std {
template <>
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 927c334..d1b38f3 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -233,7 +233,7 @@
}
// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
namespace std {
template <>
diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp
index 2af51a7..d3b3a73 100644
--- a/libs/vibrator/Android.bp
+++ b/libs/vibrator/Android.bp
@@ -24,6 +24,10 @@
cc_defaults {
name: "libvibrator_defaults",
+ defaults: [
+ "aconfig_lib_cc_shared_link.defaults",
+ ],
+
cflags: [
"-Wall",
"-Werror",
@@ -50,9 +54,11 @@
"libbinder",
"liblog",
"libutils",
+ "server_configurable_flags",
],
whole_static_libs: [
+ "android.os.vibrator.flags-aconfig-cc",
"libvibratorutils",
],
@@ -79,8 +85,14 @@
vendor_available: true,
double_loadable: true,
+ static_libs: [
+ "android.os.vibrator.flags-aconfig-cc",
+ ],
+
shared_libs: [
+ "liblog",
"libutils",
+ "server_configurable_flags",
],
srcs: [
@@ -89,6 +101,7 @@
visibility: [
"//frameworks/native/libs/vibrator",
+ "//frameworks/native/libs/vibrator/tests",
"//frameworks/av/media/libeffects/hapticgenerator",
],
}
diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp
index c97e496..cae2de2 100644
--- a/libs/vibrator/ExternalVibration.cpp
+++ b/libs/vibrator/ExternalVibration.cpp
@@ -93,8 +93,8 @@
externalVibrationScale.scaleLevel);
}
- return {/*level=*/scaleLevel, /*adaptiveScaleFactor=*/
- externalVibrationScale.adaptiveHapticsScale};
+ return os::HapticScale(scaleLevel, externalVibrationScale.scaleFactor,
+ externalVibrationScale.adaptiveHapticsScale);
}
} // namespace os
diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp
index 761ac1b..ca13afc 100644
--- a/libs/vibrator/ExternalVibrationUtils.cpp
+++ b/libs/vibrator/ExternalVibrationUtils.cpp
@@ -13,10 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define LOG_TAG "ExternalVibrationUtils"
+
#include <cstring>
+#include <android_os_vibrator.h>
+
+#include <algorithm>
#include <math.h>
+#include <log/log.h>
#include <vibrator/ExternalVibrationUtils.h>
namespace android::os {
@@ -25,8 +31,10 @@
static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f;
static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f;
static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
+static constexpr float SCALE_GAMMA = 0.65f; // Same as VibrationEffect.SCALE_GAMMA
+static constexpr float SCALE_LEVEL_GAIN = 1.4f; // Same as VibrationConfig.DEFAULT_SCALE_LEVEL_GAIN
-float getHapticScaleGamma(HapticLevel level) {
+float getOldHapticScaleGamma(HapticLevel level) {
switch (level) {
case HapticLevel::VERY_LOW:
return 2.0f;
@@ -41,7 +49,7 @@
}
}
-float getHapticMaxAmplitudeRatio(HapticLevel level) {
+float getOldHapticMaxAmplitudeRatio(HapticLevel level) {
switch (level) {
case HapticLevel::VERY_LOW:
return HAPTIC_SCALE_VERY_LOW_RATIO;
@@ -56,6 +64,85 @@
}
}
+/* Same as VibrationScaler.getScaleFactor */
+float getHapticScaleFactor(HapticScale scale) {
+ if (android_os_vibrator_haptics_scale_v2_enabled()) {
+ if (scale.getScaleFactor() >= 0) {
+ // ExternalVibratorService provided the scale factor, use it.
+ return scale.getScaleFactor();
+ }
+
+ HapticLevel level = scale.getLevel();
+ switch (level) {
+ case HapticLevel::MUTE:
+ return 0.0f;
+ case HapticLevel::NONE:
+ return 1.0f;
+ default:
+ float scaleFactor = powf(SCALE_LEVEL_GAIN, static_cast<int32_t>(level));
+ if (scaleFactor <= 0) {
+ ALOGE("Invalid scale factor %.2f for level %d, using fallback to 1.0",
+ scaleFactor, static_cast<int32_t>(level));
+ scaleFactor = 1.0f;
+ }
+ return scaleFactor;
+ }
+ }
+ // Same as VibrationScaler.SCALE_FACTOR_*
+ switch (scale.getLevel()) {
+ case HapticLevel::MUTE:
+ return 0.0f;
+ case HapticLevel::VERY_LOW:
+ return 0.6f;
+ case HapticLevel::LOW:
+ return 0.8f;
+ case HapticLevel::HIGH:
+ return 1.2f;
+ case HapticLevel::VERY_HIGH:
+ return 1.4f;
+ default:
+ return 1.0f;
+ }
+}
+
+float applyOldHapticScale(float value, float gamma, float maxAmplitudeRatio) {
+ float sign = value >= 0 ? 1.0 : -1.0;
+ return powf(fabsf(value / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
+ * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
+}
+
+float applyNewHapticScale(float value, float scaleFactor) {
+ if (android_os_vibrator_haptics_scale_v2_enabled()) {
+ if (scaleFactor <= 1 || value == 0) {
+ return value * scaleFactor;
+ } else {
+ // Using S * x / (1 + (S - 1) * x^2) as the scale up function to converge to 1.0.
+ return (value * scaleFactor) / (1 + (scaleFactor - 1) * value * value);
+ }
+ }
+ float scale = powf(scaleFactor, 1.0f / SCALE_GAMMA);
+ if (scaleFactor <= 1) {
+ // Scale down is simply a gamma corrected application of scaleFactor to the intensity.
+ // Scale up requires a different curve to ensure the intensity will not become > 1.
+ return value * scale;
+ }
+
+ float sign = value >= 0 ? 1.0f : -1.0f;
+ float extraScale = powf(scaleFactor, 4.0f - scaleFactor);
+ float x = fabsf(value) * scale * extraScale;
+ float maxX = scale * extraScale; // scaled x for intensity == 1
+
+ float expX = expf(x);
+ float expMaxX = expf(maxX);
+
+ // Using f = tanh as the scale up function so the max value will converge.
+ // a = 1/f(maxX), used to scale f so that a*f(maxX) = 1 (the value will converge to 1).
+ float a = (expMaxX + 1.0f) / (expMaxX - 1.0f);
+ float fx = (expX - 1.0f) / (expX + 1.0f);
+
+ return sign * std::clamp(a * fx, 0.0f, 1.0f);
+}
+
void applyHapticScale(float* buffer, size_t length, HapticScale scale) {
if (scale.isScaleMute()) {
memset(buffer, 0, length * sizeof(float));
@@ -65,18 +152,22 @@
return;
}
HapticLevel hapticLevel = scale.getLevel();
+ float scaleFactor = getHapticScaleFactor(scale);
float adaptiveScaleFactor = scale.getAdaptiveScaleFactor();
- float gamma = getHapticScaleGamma(hapticLevel);
- float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(hapticLevel);
+ float oldGamma = getOldHapticScaleGamma(hapticLevel);
+ float oldMaxAmplitudeRatio = getOldHapticMaxAmplitudeRatio(hapticLevel);
for (size_t i = 0; i < length; i++) {
if (hapticLevel != HapticLevel::NONE) {
- float sign = buffer[i] >= 0 ? 1.0 : -1.0;
- buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
- * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
+ if (android_os_vibrator_fix_audio_coupled_haptics_scaling() ||
+ android_os_vibrator_haptics_scale_v2_enabled()) {
+ buffer[i] = applyNewHapticScale(buffer[i], scaleFactor);
+ } else {
+ buffer[i] = applyOldHapticScale(buffer[i], oldGamma, oldMaxAmplitudeRatio);
+ }
}
- if (adaptiveScaleFactor != 1.0f) {
+ if (adaptiveScaleFactor >= 0 && adaptiveScaleFactor != 1.0f) {
buffer[i] *= adaptiveScaleFactor;
}
}
diff --git a/libs/vibrator/TEST_MAPPING b/libs/vibrator/TEST_MAPPING
new file mode 100644
index 0000000..d782b43
--- /dev/null
+++ b/libs/vibrator/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "libvibrator_test"
+ }
+ ]
+}
diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
index d9a2b81..f0760fd 100644
--- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
+++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h
@@ -17,9 +17,13 @@
#ifndef ANDROID_EXTERNAL_VIBRATION_UTILS_H
#define ANDROID_EXTERNAL_VIBRATION_UTILS_H
+#include <cstring>
+#include <sstream>
+#include <string>
+
namespace android::os {
-enum class HapticLevel {
+enum class HapticLevel : int32_t {
MUTE = -100,
VERY_LOW = -2,
LOW = -1,
@@ -31,32 +35,44 @@
class HapticScale {
private:
HapticLevel mLevel = HapticLevel::NONE;
+float mScaleFactor = -1.0f; // undefined, use haptic level to define scale factor
float mAdaptiveScaleFactor = 1.0f;
public:
-constexpr HapticScale(HapticLevel level, float adaptiveScaleFactor)
- : mLevel(level), mAdaptiveScaleFactor(adaptiveScaleFactor) {}
-constexpr HapticScale(HapticLevel level) : mLevel(level) {}
-constexpr HapticScale() {}
+ explicit HapticScale(HapticLevel level, float scaleFactor, float adaptiveScaleFactor)
+ : mLevel(level), mScaleFactor(scaleFactor), mAdaptiveScaleFactor(adaptiveScaleFactor) {}
+ explicit HapticScale(HapticLevel level) : mLevel(level) {}
+ constexpr HapticScale() {}
-HapticLevel getLevel() const { return mLevel; }
-float getAdaptiveScaleFactor() const { return mAdaptiveScaleFactor; }
+ HapticLevel getLevel() const { return mLevel; }
+ float getScaleFactor() const { return mScaleFactor; }
+ float getAdaptiveScaleFactor() const { return mAdaptiveScaleFactor; }
-bool operator==(const HapticScale& other) const {
- return mLevel == other.mLevel && mAdaptiveScaleFactor == other.mAdaptiveScaleFactor;
-}
+ bool operator==(const HapticScale& other) const {
+ return mLevel == other.mLevel && mScaleFactor == other.mScaleFactor &&
+ mAdaptiveScaleFactor == other.mAdaptiveScaleFactor;
+ }
bool isScaleNone() const {
- return mLevel == HapticLevel::NONE && mAdaptiveScaleFactor == 1.0f;
+ return (mLevel == HapticLevel::NONE || mScaleFactor == 1.0f) && mAdaptiveScaleFactor == 1.0f;
}
bool isScaleMute() const {
- return mLevel == HapticLevel::MUTE;
+ return mLevel == HapticLevel::MUTE || mScaleFactor == 0 || mAdaptiveScaleFactor == 0;
}
-static HapticScale mute() {
- return {/*level=*/os::HapticLevel::MUTE};
+std::string toString() const {
+ std::ostringstream os;
+ os << "HapticScale { level: " << static_cast<int>(mLevel);
+ os << ", scaleFactor: " << mScaleFactor;
+ os << ", adaptiveScaleFactor: " << mAdaptiveScaleFactor;
+ os << "}";
+ return os.str();
}
+
+static HapticScale mute() { return os::HapticScale(os::HapticLevel::MUTE); }
+
+static HapticScale none() { return os::HapticScale(os::HapticLevel::NONE); }
};
bool isValidHapticScale(HapticScale scale);
diff --git a/libs/vibrator/tests/Android.bp b/libs/vibrator/tests/Android.bp
new file mode 100644
index 0000000..2921a62
--- /dev/null
+++ b/libs/vibrator/tests/Android.bp
@@ -0,0 +1,53 @@
+// Copyright (C) 2024 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.
+package {
+ default_team: "trendy_team_haptics_framework",
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_test {
+ name: "libvibrator_test",
+ test_suites: ["general-tests"],
+ defaults: [
+ "aconfig_lib_cc_shared_link.defaults",
+ ],
+ srcs: [
+ "ExternalVibrationTest.cpp",
+ "ExternalVibrationUtilsTest.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ static_libs: [
+ "android.os.vibrator.flags-aconfig-cc",
+ "libflagtest",
+ "libgtest",
+ "liblog",
+ "libvibrator",
+ "libvibratorutils",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libutils",
+ "server_configurable_flags",
+ ],
+}
diff --git a/libs/vibrator/tests/ExternalVibrationTest.cpp b/libs/vibrator/tests/ExternalVibrationTest.cpp
new file mode 100644
index 0000000..4133836
--- /dev/null
+++ b/libs/vibrator/tests/ExternalVibrationTest.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2024 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 <binder/Parcel.h>
+#include <gtest/gtest.h>
+#include <vibrator/ExternalVibration.h>
+
+using namespace android;
+using namespace testing;
+
+using HapticLevel = os::HapticLevel;
+using ScaleLevel = os::ExternalVibrationScale::ScaleLevel;
+
+class TestVibrationController : public os::IExternalVibrationController {
+public:
+ explicit TestVibrationController() {}
+ IBinder *onAsBinder() override { return nullptr; }
+ binder::Status mute(/*out*/ bool *ret) override {
+ *ret = false;
+ return binder::Status::ok();
+ };
+ binder::Status unmute(/*out*/ bool *ret) override {
+ *ret = false;
+ return binder::Status::ok();
+ };
+};
+
+class ExternalVibrationTest : public Test {
+protected:
+ HapticLevel toHapticLevel(ScaleLevel level) {
+ os::ExternalVibrationScale externalVibrationScale;
+ externalVibrationScale.scaleLevel = level;
+ os::HapticScale hapticScale =
+ os::ExternalVibration::externalVibrationScaleToHapticScale(externalVibrationScale);
+ return hapticScale.getLevel();
+ }
+};
+
+TEST_F(ExternalVibrationTest, TestReadAndWriteToParcel) {
+ int32_t uid = 1;
+ std::string pkg("package.name");
+ audio_attributes_t originalAttrs;
+ originalAttrs.content_type = AUDIO_CONTENT_TYPE_SONIFICATION;
+ originalAttrs.usage = AUDIO_USAGE_ASSISTANCE_SONIFICATION;
+ originalAttrs.source = AUDIO_SOURCE_VOICE_COMMUNICATION;
+ originalAttrs.flags = AUDIO_FLAG_BYPASS_MUTE;
+
+ sp<TestVibrationController> vibrationController = new TestVibrationController();
+ ASSERT_NE(vibrationController, nullptr);
+
+ sp<os::ExternalVibration> original =
+ new os::ExternalVibration(uid, pkg, originalAttrs, vibrationController);
+ ASSERT_NE(original, nullptr);
+
+ EXPECT_EQ(original->getUid(), uid);
+ EXPECT_EQ(original->getPackage(), pkg);
+ EXPECT_EQ(original->getAudioAttributes().content_type, originalAttrs.content_type);
+ EXPECT_EQ(original->getAudioAttributes().usage, originalAttrs.usage);
+ EXPECT_EQ(original->getAudioAttributes().source, originalAttrs.source);
+ EXPECT_EQ(original->getAudioAttributes().flags, originalAttrs.flags);
+ EXPECT_EQ(original->getController(), vibrationController);
+
+ audio_attributes_t defaultAttrs;
+ defaultAttrs.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
+ defaultAttrs.usage = AUDIO_USAGE_UNKNOWN;
+ defaultAttrs.source = AUDIO_SOURCE_DEFAULT;
+ defaultAttrs.flags = AUDIO_FLAG_NONE;
+
+ sp<os::ExternalVibration> parceled =
+ new os::ExternalVibration(0, std::string(""), defaultAttrs, nullptr);
+ ASSERT_NE(parceled, nullptr);
+
+ Parcel parcel;
+ original->writeToParcel(&parcel);
+ parcel.setDataPosition(0);
+ parceled->readFromParcel(&parcel);
+
+ EXPECT_EQ(parceled->getUid(), uid);
+ EXPECT_EQ(parceled->getPackage(), pkg);
+ EXPECT_EQ(parceled->getAudioAttributes().content_type, originalAttrs.content_type);
+ EXPECT_EQ(parceled->getAudioAttributes().usage, originalAttrs.usage);
+ EXPECT_EQ(parceled->getAudioAttributes().source, originalAttrs.source);
+ EXPECT_EQ(parceled->getAudioAttributes().flags, originalAttrs.flags);
+ // TestVibrationController does not implement onAsBinder, skip controller parcel in this test.
+}
+
+TEST_F(ExternalVibrationTest, TestExternalVibrationScaleToHapticScale) {
+ os::ExternalVibrationScale externalVibrationScale;
+ externalVibrationScale.scaleLevel = ScaleLevel::SCALE_HIGH;
+ externalVibrationScale.scaleFactor = 0.5f;
+ externalVibrationScale.adaptiveHapticsScale = 0.8f;
+
+ os::HapticScale hapticScale =
+ os::ExternalVibration::externalVibrationScaleToHapticScale(externalVibrationScale);
+
+ // Check scale factors are forwarded.
+ EXPECT_EQ(hapticScale.getLevel(), HapticLevel::HIGH);
+ EXPECT_EQ(hapticScale.getScaleFactor(), 0.5f);
+ EXPECT_EQ(hapticScale.getAdaptiveScaleFactor(), 0.8f);
+
+ // Check conversion for all levels.
+ EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_MUTE), HapticLevel::MUTE);
+ EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_VERY_LOW), HapticLevel::VERY_LOW);
+ EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_LOW), HapticLevel::LOW);
+ EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_NONE), HapticLevel::NONE);
+ EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_HIGH), HapticLevel::HIGH);
+ EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_VERY_HIGH), HapticLevel::VERY_HIGH);
+}
diff --git a/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp
new file mode 100644
index 0000000..9369f80
--- /dev/null
+++ b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2024 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 <android_os_vibrator.h>
+#include <flag_macros.h>
+#include <gtest/gtest.h>
+#include <vibrator/ExternalVibrationUtils.h>
+
+#include "test_utils.h"
+
+#define FLAG_NS android::os::vibrator
+
+using namespace android;
+using namespace testing;
+
+using HapticScale = os::HapticScale;
+using HapticLevel = os::HapticLevel;
+
+static constexpr float TEST_TOLERANCE = 1e-2f;
+static constexpr size_t TEST_BUFFER_LENGTH = 4;
+static float TEST_BUFFER[TEST_BUFFER_LENGTH] = { 1, -1, 0.5f, -0.2f };
+
+class ExternalVibrationUtilsTest : public Test {
+public:
+ void SetUp() override {
+ std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(mBuffer));
+ }
+
+protected:
+ void scaleBuffer(HapticLevel hapticLevel) {
+ scaleBuffer(HapticScale(hapticLevel));
+ }
+
+ void scaleBuffer(HapticLevel hapticLevel, float adaptiveScaleFactor) {
+ scaleBuffer(hapticLevel, adaptiveScaleFactor, 0 /* limit */);
+ }
+
+ void scaleBuffer(HapticLevel hapticLevel, float adaptiveScaleFactor, float limit) {
+ scaleBuffer(HapticScale(hapticLevel, -1 /* scaleFactor */, adaptiveScaleFactor), limit);
+ }
+
+ void scaleBuffer(HapticScale hapticScale) {
+ scaleBuffer(hapticScale, 0 /* limit */);
+ }
+
+ void scaleBuffer(HapticScale hapticScale, float limit) {
+ std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(mBuffer));
+ os::scaleHapticData(&mBuffer[0], TEST_BUFFER_LENGTH, hapticScale, limit);
+ }
+
+ float mBuffer[TEST_BUFFER_LENGTH];
+};
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleMute,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expected[TEST_BUFFER_LENGTH];
+ std::fill(std::begin(expected), std::end(expected), 0);
+
+ scaleBuffer(HapticLevel::MUTE);
+ EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleMute,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expected[TEST_BUFFER_LENGTH];
+ std::fill(std::begin(expected), std::end(expected), 0);
+
+ scaleBuffer(HapticLevel::MUTE);
+ EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestScaleV2Mute,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expected[TEST_BUFFER_LENGTH];
+ std::fill(std::begin(expected), std::end(expected), 0);
+
+ scaleBuffer(HapticLevel::MUTE);
+ EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleNone,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expected[TEST_BUFFER_LENGTH];
+ std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected));
+
+ scaleBuffer(HapticLevel::NONE);
+ EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleNone,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expected[TEST_BUFFER_LENGTH];
+ std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected));
+
+ scaleBuffer(HapticLevel::NONE);
+ EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestScaleV2None,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expected[TEST_BUFFER_LENGTH];
+ std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected));
+
+ scaleBuffer(HapticLevel::NONE);
+ EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleToHapticLevel,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.84f, -0.66f };
+ scaleBuffer(HapticLevel::VERY_HIGH);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.7f, -0.44f };
+ scaleBuffer(HapticLevel::HIGH);
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.75f, -0.75f, 0.26f, -0.06f };
+ scaleBuffer(HapticLevel::LOW);
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.66f, -0.66f, 0.16f, -0.02f };
+ scaleBuffer(HapticLevel::VERY_LOW);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleToHapticLevel,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.79f, -0.39f };
+ scaleBuffer(HapticLevel::VERY_HIGH);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.62f, -0.27f };
+ scaleBuffer(HapticLevel::HIGH);
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.70f, -0.70f, 0.35f, -0.14f };
+ scaleBuffer(HapticLevel::LOW);
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.45f, -0.45f, 0.22f, -0.09f };
+ scaleBuffer(HapticLevel::VERY_LOW);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestScaleV2ToHapticLevel,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.8f, -0.38f };
+ scaleBuffer(HapticLevel::VERY_HIGH);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.63f, -0.27f };
+ scaleBuffer(HapticLevel::HIGH);
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.71f, -0.71f, 0.35f, -0.14f };
+ scaleBuffer(HapticLevel::LOW);
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.51f, -0.51f, 0.25f, -0.1f };
+ scaleBuffer(HapticLevel::VERY_LOW);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestScaleV2ToScaleFactorUndefinedUsesHapticLevel,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ constexpr float adaptiveScaleNone = 1.0f;
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = {1, -1, 0.8f, -0.38f};
+ scaleBuffer(HapticScale(HapticLevel::VERY_HIGH, -1.0f /* scaleFactor */, adaptiveScaleNone));
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestScaleV2ToScaleFactorIgnoresLevel,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ constexpr float adaptiveScaleNone = 1.0f;
+
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 1, -0.55f };
+ scaleBuffer(HapticScale(HapticLevel::LOW, 3.0f /* scaleFactor */, adaptiveScaleNone));
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.66f, -0.29f };
+ scaleBuffer(HapticScale(HapticLevel::LOW, 1.5f /* scaleFactor */, adaptiveScaleNone));
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.8f, -0.8f, 0.4f, -0.16f };
+ scaleBuffer(HapticScale(HapticLevel::HIGH, 0.8f /* scaleFactor */, adaptiveScaleNone));
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.4f, -0.4f, 0.2f, -0.08f };
+ scaleBuffer(HapticScale(HapticLevel::HIGH, 0.4f /* scaleFactor */, adaptiveScaleNone));
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorUndefinedIsIgnoredLegacyScale,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = {1, -1, 0.79f, -0.39f};
+ scaleBuffer(HapticLevel::VERY_HIGH, -1.0f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterLegacyScale,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Adaptive scale mutes vibration
+ float expectedMuted[TEST_BUFFER_LENGTH];
+ std::fill(std::begin(expectedMuted), std::end(expectedMuted), 0);
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.0f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedMuted, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale up then adaptive scale down
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.16f, -0.13f };
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale up then adaptive scale up
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1.5f, -1.5f, 1.06f, -0.67f };
+ scaleBuffer(HapticLevel::HIGH, 1.5f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale down then adaptive scale down
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.45f, -0.45f, 0.15f, -0.04f };
+ scaleBuffer(HapticLevel::LOW, 0.6f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale down then adaptive scale up
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 1.33f, -1.33f, 0.33f, -0.05f };
+ scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorUndefinedIgnoredFixedScale,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = {1, -1, 0.79f, -0.39f};
+ scaleBuffer(HapticLevel::VERY_HIGH, -1.0f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterFixedScale,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Adaptive scale mutes vibration
+ float expectedMuted[TEST_BUFFER_LENGTH];
+ std::fill(std::begin(expectedMuted), std::end(expectedMuted), 0);
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.0f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedMuted, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale up then adaptive scale down
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.16f, -0.07f };
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale up then adaptive scale up
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1.5f, -1.5f, 0.93f, -0.41f };
+ scaleBuffer(HapticLevel::HIGH, 1.5f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale down then adaptive scale down
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.42f, -0.42f, 0.21f, -0.08f };
+ scaleBuffer(HapticLevel::LOW, 0.6f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale down then adaptive scale up
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.91f, -0.91f, 0.45f, -0.18f };
+ scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestAdaptiveScaleFactorUndefinedIgnoredScaleV2,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = {1, -1, 0.8f, -0.38f};
+ scaleBuffer(HapticLevel::VERY_HIGH, -1.0f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterScaleV2,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Adaptive scale mutes vibration
+ float expectedMuted[TEST_BUFFER_LENGTH];
+ std::fill(std::begin(expectedMuted), std::end(expectedMuted), 0);
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.0f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedMuted, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale up then adaptive scale down
+ float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.15f, -0.07f };
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale up then adaptive scale up
+ float expectedHigh[TEST_BUFFER_LENGTH] = { 1.5f, -1.5f, 0.95f, -0.41f };
+ scaleBuffer(HapticLevel::HIGH, 1.5f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale down then adaptive scale down
+ float expectedLow[TEST_BUFFER_LENGTH] = { 0.42f, -0.42f, 0.21f, -0.08f };
+ scaleBuffer(HapticLevel::LOW, 0.6f /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Haptic level scale down then adaptive scale up
+ float expectedVeryLow[TEST_BUFFER_LENGTH] = { 1.02f, -1.02f, 0.51f, -0.2f };
+ scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLimitAppliedAfterLegacyScale,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling),
+ ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Scaled = { 0.2, -0.2, 0.16f, -0.13f };
+ float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.13f };
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Scaled = { 1, -1, 0.5f, -0.2f };
+ float expectedClippedVeryLow[TEST_BUFFER_LENGTH] = { 0.7f, -0.7f, 0.33f, -0.05f };
+ scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */, 0.7f /* limit */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLimitAppliedAfterFixedScale,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)),
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Scaled = { 0.2, -0.2, 0.16f, -0.13f };
+ float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.07f };
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Scaled = { 1, -1, 0.5f, -0.2f };
+ float expectedClippedVeryLow[TEST_BUFFER_LENGTH] = { 0.7f, -0.7f, 0.45f, -0.18f };
+ scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */, 0.7f /* limit */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
+
+TEST_F_WITH_FLAGS(
+ ExternalVibrationUtilsTest, TestLimitAppliedAfterScaleV2,
+ // Value of fix_audio_coupled_haptics_scaling is not important, should work with either
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) {
+ // Scaled = { 0.2, -0.2, 0.15f, -0.07f };
+ float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.07f };
+ scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+
+ // Scaled = { 1.02f, -1.02f, 0.51f, -0.2f }
+ float expectedClippedVeryLow[TEST_BUFFER_LENGTH] = { 0.7f, -0.7f, 0.51f, -0.2f };
+ scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */, 0.7f /* limit */);
+ EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE);
+}
diff --git a/libs/vibrator/tests/test_utils.h b/libs/vibrator/tests/test_utils.h
new file mode 100644
index 0000000..f491ea1
--- /dev/null
+++ b/libs/vibrator/tests/test_utils.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#ifndef LIBVIBRATOR_TEST_UTILS_H
+#define LIBVIBRATOR_TEST_UTILS_H
+
+#include <gtest/gtest.h>
+
+#if !defined(EXPECT_FLOATS_NEARLY_EQ)
+#define EXPECT_FLOATS_NEARLY_EQ(expected, actual, length, epsilon) \
+ for (size_t i = 0; i < length; i++) { \
+ EXPECT_NEAR(expected[i], actual[i], epsilon) << " at Index: " << i; \
+ }
+#else
+#error Macro EXPECT_FLOATS_NEARLY_EQ already defined
+#endif
+
+#endif //LIBVIBRATOR_TEST_UTILS_H
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 4454f36..37dc931 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -30,6 +30,10 @@
to: "",
srcs: ["include/EGL/**/*.h"],
license: "include/EGL/NOTICE",
+ // eglext.h is not self-contained. Safe to skip C-compat verification
+ // though since upstream also cares about C compatibility, and the header is
+ // auto-generated anyway.
+ skip_verification: true,
}
ndk_headers {
@@ -38,6 +42,10 @@
to: "",
srcs: ["include/GLES/**/*.h"],
license: "include/GLES/NOTICE",
+ // glext.h is not self-contained. Safe to skip C-compat verification
+ // though since upstream also cares about C compatibility, and the header is
+ // auto-generated anyway.
+ skip_verification: true,
}
ndk_headers {
@@ -46,6 +54,10 @@
to: "",
srcs: ["include/GLES2/**/*.h"],
license: "include/GLES2/NOTICE",
+ // gl2ext.h is not self-contained. Safe to skip C-compat verification
+ // though since upstream also cares about C compatibility, and the header is
+ // auto-generated anyway.
+ skip_verification: true,
}
ndk_headers {
diff --git a/opengl/include/EGL/egl.h b/opengl/include/EGL/egl.h
index c9e8b7c..782b6d9 100644
--- a/opengl/include/EGL/egl.h
+++ b/opengl/include/EGL/egl.h
@@ -6,39 +6,24 @@
#endif
/*
-** Copyright (c) 2013-2017 The Khronos Group Inc.
+** Copyright 2013-2020 The Khronos Group Inc.
+** SPDX-License-Identifier: Apache-2.0
**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-*/
-/*
-** This header is generated from the Khronos OpenGL / OpenGL ES XML
-** API Registry. The current version of the Registry, generator scripts
+** This header is generated from the Khronos EGL XML API Registry.
+** The current version of the Registry, generator scripts
** used to make the header, and the header can be found at
** http://www.khronos.org/registry/egl
**
-** Khronos $Git commit SHA1: bae3518c48 $ on $Git commit date: 2018-05-17 10:56:57 -0700 $
+** Khronos $Git commit SHA1: 800219cd6e $ on $Git commit date: 2024-05-13 00:13:13 -0700 $
*/
#include <EGL/eglplatform.h>
-/* Generated on date 20180517 */
+#ifndef EGL_EGL_PROTOTYPES
+#define EGL_EGL_PROTOTYPES 1
+#endif
+
+/* Generated on date 20240715 */
/* Generated C header for:
* API: egl
@@ -118,6 +103,31 @@
#define EGL_VERSION 0x3054
#define EGL_WIDTH 0x3057
#define EGL_WINDOW_BIT 0x0004
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLCHOOSECONFIGPROC) (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLCOPYBUFFERSPROC) (EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target);
+typedef EGLContext (EGLAPIENTRYP PFNEGLCREATECONTEXTPROC) (EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPBUFFERSURFACEPROC) (EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPIXMAPSURFACEPROC) (EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEWINDOWSURFACEPROC) (EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYCONTEXTPROC) (EGLDisplay dpy, EGLContext ctx);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSURFACEPROC) (EGLDisplay dpy, EGLSurface surface);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCONFIGATTRIBPROC) (EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCONFIGSPROC) (EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config);
+typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETCURRENTDISPLAYPROC) (void);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLGETCURRENTSURFACEPROC) (EGLint readdraw);
+typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETDISPLAYPROC) (EGLNativeDisplayType display_id);
+typedef EGLint (EGLAPIENTRYP PFNEGLGETERRORPROC) (void);
+typedef __eglMustCastToProperFunctionPointerType (EGLAPIENTRYP PFNEGLGETPROCADDRESSPROC) (const char *procname);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLINITIALIZEPROC) (EGLDisplay dpy, EGLint *major, EGLint *minor);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLMAKECURRENTPROC) (EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYCONTEXTPROC) (EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value);
+typedef const char *(EGLAPIENTRYP PFNEGLQUERYSTRINGPROC) (EGLDisplay dpy, EGLint name);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSURFACEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSPROC) (EGLDisplay dpy, EGLSurface surface);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLTERMINATEPROC) (EGLDisplay dpy);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITGLPROC) (void);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITNATIVEPROC) (EGLint engine);
+#if EGL_EGL_PROTOTYPES
EGLAPI EGLBoolean EGLAPIENTRY eglChooseConfig (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
EGLAPI EGLBoolean EGLAPIENTRY eglCopyBuffers (EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target);
EGLAPI EGLContext EGLAPIENTRY eglCreateContext (EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
@@ -142,6 +152,7 @@
EGLAPI EGLBoolean EGLAPIENTRY eglTerminate (EGLDisplay dpy);
EGLAPI EGLBoolean EGLAPIENTRY eglWaitGL (void);
EGLAPI EGLBoolean EGLAPIENTRY eglWaitNative (EGLint engine);
+#endif
#endif /* EGL_VERSION_1_0 */
#ifndef EGL_VERSION_1_1
@@ -160,10 +171,16 @@
#define EGL_TEXTURE_RGB 0x305D
#define EGL_TEXTURE_RGBA 0x305E
#define EGL_TEXTURE_TARGET 0x3081
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDTEXIMAGEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint buffer);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLRELEASETEXIMAGEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint buffer);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSURFACEATTRIBPROC) (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPINTERVALPROC) (EGLDisplay dpy, EGLint interval);
+#if EGL_EGL_PROTOTYPES
EGLAPI EGLBoolean EGLAPIENTRY eglBindTexImage (EGLDisplay dpy, EGLSurface surface, EGLint buffer);
EGLAPI EGLBoolean EGLAPIENTRY eglReleaseTexImage (EGLDisplay dpy, EGLSurface surface, EGLint buffer);
EGLAPI EGLBoolean EGLAPIENTRY eglSurfaceAttrib (EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value);
EGLAPI EGLBoolean EGLAPIENTRY eglSwapInterval (EGLDisplay dpy, EGLint interval);
+#endif
#endif /* EGL_VERSION_1_1 */
#ifndef EGL_VERSION_1_2
@@ -199,11 +216,18 @@
#define EGL_SWAP_BEHAVIOR 0x3093
#define EGL_UNKNOWN EGL_CAST(EGLint,-1)
#define EGL_VERTICAL_RESOLUTION 0x3091
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDAPIPROC) (EGLenum api);
+typedef EGLenum (EGLAPIENTRYP PFNEGLQUERYAPIPROC) (void);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPBUFFERFROMCLIENTBUFFERPROC) (EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLRELEASETHREADPROC) (void);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITCLIENTPROC) (void);
+#if EGL_EGL_PROTOTYPES
EGLAPI EGLBoolean EGLAPIENTRY eglBindAPI (EGLenum api);
EGLAPI EGLenum EGLAPIENTRY eglQueryAPI (void);
EGLAPI EGLSurface EGLAPIENTRY eglCreatePbufferFromClientBuffer (EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list);
EGLAPI EGLBoolean EGLAPIENTRY eglReleaseThread (void);
EGLAPI EGLBoolean EGLAPIENTRY eglWaitClient (void);
+#endif
#endif /* EGL_VERSION_1_2 */
#ifndef EGL_VERSION_1_3
@@ -232,7 +256,10 @@
#define EGL_OPENGL_API 0x30A2
#define EGL_OPENGL_BIT 0x0008
#define EGL_SWAP_BEHAVIOR_PRESERVED_BIT 0x0400
+typedef EGLContext (EGLAPIENTRYP PFNEGLGETCURRENTCONTEXTPROC) (void);
+#if EGL_EGL_PROTOTYPES
EGLAPI EGLContext EGLAPIENTRY eglGetCurrentContext (void);
+#endif
#endif /* EGL_VERSION_1_4 */
#ifndef EGL_VERSION_1_5
@@ -284,6 +311,17 @@
#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x30B8
#define EGL_IMAGE_PRESERVED 0x30D2
#define EGL_NO_IMAGE EGL_CAST(EGLImage,0)
+typedef EGLSync (EGLAPIENTRYP PFNEGLCREATESYNCPROC) (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSYNCPROC) (EGLDisplay dpy, EGLSync sync);
+typedef EGLint (EGLAPIENTRYP PFNEGLCLIENTWAITSYNCPROC) (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETSYNCATTRIBPROC) (EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value);
+typedef EGLImage (EGLAPIENTRYP PFNEGLCREATEIMAGEPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEPROC) (EGLDisplay dpy, EGLImage image);
+typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYPROC) (EGLenum platform, void *native_display, const EGLAttrib *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEPROC) (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list);
+typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMPIXMAPSURFACEPROC) (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLWAITSYNCPROC) (EGLDisplay dpy, EGLSync sync, EGLint flags);
+#if EGL_EGL_PROTOTYPES
EGLAPI EGLSync EGLAPIENTRY eglCreateSync (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);
EGLAPI EGLBoolean EGLAPIENTRY eglDestroySync (EGLDisplay dpy, EGLSync sync);
EGLAPI EGLint EGLAPIENTRY eglClientWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
@@ -294,6 +332,7 @@
EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformWindowSurface (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list);
EGLAPI EGLSurface EGLAPIENTRY eglCreatePlatformPixmapSurface (EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list);
EGLAPI EGLBoolean EGLAPIENTRY eglWaitSync (EGLDisplay dpy, EGLSync sync, EGLint flags);
+#endif
#endif /* EGL_VERSION_1_5 */
#ifdef __cplusplus
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index c787fc9..4d14c69 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -6,39 +6,20 @@
#endif
/*
-** Copyright (c) 2013-2017 The Khronos Group Inc.
+** Copyright 2013-2020 The Khronos Group Inc.
+** SPDX-License-Identifier: Apache-2.0
**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-*/
-/*
** This header is generated from the Khronos EGL XML API Registry.
** The current version of the Registry, generator scripts
** used to make the header, and the header can be found at
** http://www.khronos.org/registry/egl
**
-** Khronos $Git commit SHA1: 726475c203 $ on $Git commit date: 2018-10-03 23:51:49 -0700 $
+** Khronos $Git commit SHA1: 800219cd6e $ on $Git commit date: 2024-05-13 00:13:13 -0700 $
*/
#include <EGL/eglplatform.h>
-#define EGL_EGLEXT_VERSION 20181204
+#define EGL_EGLEXT_VERSION 20240715
/* Generated C header for:
* API: egl
@@ -443,9 +424,9 @@
#ifndef EGL_KHR_swap_buffers_with_damage
#define EGL_KHR_swap_buffers_with_damage 1
-typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC) (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects);
#ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageKHR (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageKHR (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects);
#endif
#endif /* EGL_KHR_swap_buffers_with_damage */
@@ -462,6 +443,10 @@
#endif
#endif /* EGL_KHR_wait_sync */
+#ifndef EGL_ANDROID_GLES_layers
+#define EGL_ANDROID_GLES_layers 1
+#endif /* EGL_ANDROID_GLES_layers */
+
#ifndef EGL_ANDROID_blob_cache
#define EGL_ANDROID_blob_cache 1
typedef khronos_ssize_t EGLsizeiANDROID;
@@ -566,6 +551,11 @@
#define EGL_RECORDABLE_ANDROID 0x3142
#endif /* EGL_ANDROID_recordable */
+#ifndef EGL_ANDROID_telemetry_hint
+#define EGL_ANDROID_telemetry_hint 1
+#define EGL_TELEMETRY_HINT_ANDROID 0x3570
+#endif /* EGL_ANDROID_telemetry_hint */
+
#ifndef EGL_ANGLE_d3d_share_handle_client_buffer
#define EGL_ANGLE_d3d_share_handle_client_buffer 1
#define EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE 0x3200
@@ -589,11 +579,25 @@
#define EGL_ANGLE_surface_d3d_texture_2d_share_handle 1
#endif /* EGL_ANGLE_surface_d3d_texture_2d_share_handle */
+#ifndef EGL_ANGLE_sync_control_rate
+#define EGL_ANGLE_sync_control_rate 1
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETMSCRATEANGLEPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *numerator, EGLint *denominator);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglGetMscRateANGLE (EGLDisplay dpy, EGLSurface surface, EGLint *numerator, EGLint *denominator);
+#endif
+#endif /* EGL_ANGLE_sync_control_rate */
+
#ifndef EGL_ANGLE_window_fixed_size
#define EGL_ANGLE_window_fixed_size 1
#define EGL_FIXED_SIZE_ANGLE 0x3201
#endif /* EGL_ANGLE_window_fixed_size */
+#ifndef EGL_ARM_image_format
+#define EGL_ARM_image_format 1
+#define EGL_COLOR_COMPONENT_TYPE_UNSIGNED_INTEGER_ARM 0x3287
+#define EGL_COLOR_COMPONENT_TYPE_INTEGER_ARM 0x3288
+#endif /* EGL_ARM_image_format */
+
#ifndef EGL_ARM_implicit_external_sync
#define EGL_ARM_implicit_external_sync 1
#define EGL_SYNC_PRIOR_COMMANDS_IMPLICIT_EXTERNAL_ARM 0x328A
@@ -652,6 +656,11 @@
#endif
#endif /* EGL_EXT_compositor */
+#ifndef EGL_EXT_config_select_group
+#define EGL_EXT_config_select_group 1
+#define EGL_CONFIG_SELECT_GROUP_EXT 0x34C0
+#endif /* EGL_EXT_config_select_group */
+
#ifndef EGL_EXT_create_context_robustness
#define EGL_EXT_create_context_robustness 1
#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT 0x30BF
@@ -684,6 +693,11 @@
#define EGL_DRM_MASTER_FD_EXT 0x333C
#endif /* EGL_EXT_device_drm */
+#ifndef EGL_EXT_device_drm_render_node
+#define EGL_EXT_device_drm_render_node 1
+#define EGL_DRM_RENDER_NODE_FILE_EXT 0x3377
+#endif /* EGL_EXT_device_drm_render_node */
+
#ifndef EGL_EXT_device_enumeration
#define EGL_EXT_device_enumeration 1
#endif /* EGL_EXT_device_enumeration */
@@ -691,12 +705,33 @@
#ifndef EGL_EXT_device_openwf
#define EGL_EXT_device_openwf 1
#define EGL_OPENWF_DEVICE_ID_EXT 0x3237
+#define EGL_OPENWF_DEVICE_EXT 0x333D
#endif /* EGL_EXT_device_openwf */
+#ifndef EGL_EXT_device_persistent_id
+#define EGL_EXT_device_persistent_id 1
+#define EGL_DEVICE_UUID_EXT 0x335C
+#define EGL_DRIVER_UUID_EXT 0x335D
+#define EGL_DRIVER_NAME_EXT 0x335E
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDEVICEBINARYEXTPROC) (EGLDeviceEXT device, EGLint name, EGLint max_size, void *value, EGLint *size);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryDeviceBinaryEXT (EGLDeviceEXT device, EGLint name, EGLint max_size, void *value, EGLint *size);
+#endif
+#endif /* EGL_EXT_device_persistent_id */
+
#ifndef EGL_EXT_device_query
#define EGL_EXT_device_query 1
#endif /* EGL_EXT_device_query */
+#ifndef EGL_EXT_device_query_name
+#define EGL_EXT_device_query_name 1
+#define EGL_RENDERER_EXT 0x335F
+#endif /* EGL_EXT_device_query_name */
+
+#ifndef EGL_EXT_explicit_device
+#define EGL_EXT_explicit_device 1
+#endif /* EGL_EXT_explicit_device */
+
#ifndef EGL_EXT_gl_colorspace_bt2020_hlg
#define EGL_EXT_gl_colorspace_bt2020_hlg 1
#define EGL_GL_COLORSPACE_BT2020_HLG_EXT 0x3540
@@ -878,6 +913,17 @@
#define EGL_PLATFORM_X11_SCREEN_EXT 0x31D6
#endif /* EGL_EXT_platform_x11 */
+#ifndef EGL_EXT_platform_xcb
+#define EGL_EXT_platform_xcb 1
+#define EGL_PLATFORM_XCB_EXT 0x31DC
+#define EGL_PLATFORM_XCB_SCREEN_EXT 0x31DE
+#endif /* EGL_EXT_platform_xcb */
+
+#ifndef EGL_EXT_present_opaque
+#define EGL_EXT_present_opaque 1
+#define EGL_PRESENT_OPAQUE_EXT 0x31DF
+#endif /* EGL_EXT_present_opaque */
+
#ifndef EGL_EXT_protected_content
#define EGL_EXT_protected_content 1
#define EGL_PROTECTED_CONTENT_EXT 0x32C0
@@ -887,6 +933,10 @@
#define EGL_EXT_protected_surface 1
#endif /* EGL_EXT_protected_surface */
+#ifndef EGL_EXT_query_reset_notification_strategy
+#define EGL_EXT_query_reset_notification_strategy 1
+#endif /* EGL_EXT_query_reset_notification_strategy */
+
#ifndef EGL_EXT_stream_consumer_egloutput
#define EGL_EXT_stream_consumer_egloutput 1
typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMEROUTPUTEXTPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLOutputLayerEXT layer);
@@ -916,11 +966,36 @@
#define EGL_METADATA_SCALING_EXT 50000
#endif /* EGL_EXT_surface_SMPTE2086_metadata */
+#ifndef EGL_EXT_surface_compression
+#define EGL_EXT_surface_compression 1
+#define EGL_SURFACE_COMPRESSION_EXT 0x34B0
+#define EGL_SURFACE_COMPRESSION_PLANE1_EXT 0x328E
+#define EGL_SURFACE_COMPRESSION_PLANE2_EXT 0x328F
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_NONE_EXT 0x34B1
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_DEFAULT_EXT 0x34B2
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_1BPC_EXT 0x34B4
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_2BPC_EXT 0x34B5
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_3BPC_EXT 0x34B6
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_4BPC_EXT 0x34B7
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_5BPC_EXT 0x34B8
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_6BPC_EXT 0x34B9
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_7BPC_EXT 0x34BA
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_8BPC_EXT 0x34BB
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_9BPC_EXT 0x34BC
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_10BPC_EXT 0x34BD
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_11BPC_EXT 0x34BE
+#define EGL_SURFACE_COMPRESSION_FIXED_RATE_12BPC_EXT 0x34BF
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSUPPORTEDCOMPRESSIONRATESEXTPROC) (EGLDisplay dpy, EGLConfig config, const EGLAttrib *attrib_list, EGLint *rates, EGLint rate_size, EGLint *num_rates);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglQuerySupportedCompressionRatesEXT (EGLDisplay dpy, EGLConfig config, const EGLAttrib *attrib_list, EGLint *rates, EGLint rate_size, EGLint *num_rates);
+#endif
+#endif /* EGL_EXT_surface_compression */
+
#ifndef EGL_EXT_swap_buffers_with_damage
#define EGL_EXT_swap_buffers_with_damage 1
-typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC) (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects);
#ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageEXT (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
+EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageEXT (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects);
#endif
#endif /* EGL_EXT_swap_buffers_with_damage */
@@ -1036,6 +1111,16 @@
#define EGL_PLATFORM_SURFACELESS_MESA 0x31DD
#endif /* EGL_MESA_platform_surfaceless */
+#ifndef EGL_MESA_query_driver
+#define EGL_MESA_query_driver 1
+typedef char *(EGLAPIENTRYP PFNEGLGETDISPLAYDRIVERCONFIGPROC) (EGLDisplay dpy);
+typedef const char *(EGLAPIENTRYP PFNEGLGETDISPLAYDRIVERNAMEPROC) (EGLDisplay dpy);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI char *EGLAPIENTRY eglGetDisplayDriverConfig (EGLDisplay dpy);
+EGLAPI const char *EGLAPIENTRY eglGetDisplayDriverName (EGLDisplay dpy);
+#endif
+#endif /* EGL_MESA_query_driver */
+
#ifndef EGL_NOK_swap_region
#define EGL_NOK_swap_region 1
typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSREGIONNOKPROC) (EGLDisplay dpy, EGLSurface surface, EGLint numRects, const EGLint *rects);
@@ -1124,11 +1209,39 @@
#endif
#endif /* EGL_NV_post_sub_buffer */
+#ifndef EGL_NV_quadruple_buffer
+#define EGL_NV_quadruple_buffer 1
+#define EGL_QUADRUPLE_BUFFER_NV 0x3231
+#endif /* EGL_NV_quadruple_buffer */
+
#ifndef EGL_NV_robustness_video_memory_purge
#define EGL_NV_robustness_video_memory_purge 1
#define EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x334C
#endif /* EGL_NV_robustness_video_memory_purge */
+#ifndef EGL_NV_stream_consumer_eglimage
+#define EGL_NV_stream_consumer_eglimage 1
+#define EGL_STREAM_CONSUMER_IMAGE_NV 0x3373
+#define EGL_STREAM_IMAGE_ADD_NV 0x3374
+#define EGL_STREAM_IMAGE_REMOVE_NV 0x3375
+#define EGL_STREAM_IMAGE_AVAILABLE_NV 0x3376
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMIMAGECONSUMERCONNECTNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLint num_modifiers, const EGLuint64KHR *modifiers, const EGLAttrib *attrib_list);
+typedef EGLint (EGLAPIENTRYP PFNEGLQUERYSTREAMCONSUMEREVENTNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLTime timeout, EGLenum *event, EGLAttrib *aux);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMACQUIREIMAGENVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLImage *pImage, EGLSync sync);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMRELEASEIMAGENVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLImage image, EGLSync sync);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamImageConsumerConnectNV (EGLDisplay dpy, EGLStreamKHR stream, EGLint num_modifiers, const EGLuint64KHR *modifiers, const EGLAttrib *attrib_list);
+EGLAPI EGLint EGLAPIENTRY eglQueryStreamConsumerEventNV (EGLDisplay dpy, EGLStreamKHR stream, EGLTime timeout, EGLenum *event, EGLAttrib *aux);
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamAcquireImageNV (EGLDisplay dpy, EGLStreamKHR stream, EGLImage *pImage, EGLSync sync);
+EGLAPI EGLBoolean EGLAPIENTRY eglStreamReleaseImageNV (EGLDisplay dpy, EGLStreamKHR stream, EGLImage image, EGLSync sync);
+#endif
+#endif /* EGL_NV_stream_consumer_eglimage */
+
+#ifndef EGL_NV_stream_consumer_eglimage_use_scanout_attrib
+#define EGL_NV_stream_consumer_eglimage_use_scanout_attrib 1
+#define EGL_STREAM_CONSUMER_IMAGE_USE_SCANOUT_NV 0x3378
+#endif /* EGL_NV_stream_consumer_eglimage_use_scanout_attrib */
+
#ifndef EGL_NV_stream_consumer_gltexture_yuv
#define EGL_NV_stream_consumer_gltexture_yuv 1
#define EGL_YUV_PLANE0_TEXTURE_UNIT_NV 0x332C
@@ -1165,6 +1278,12 @@
#define EGL_STREAM_CROSS_SYSTEM_NV 0x334F
#endif /* EGL_NV_stream_cross_system */
+#ifndef EGL_NV_stream_dma
+#define EGL_NV_stream_dma 1
+#define EGL_STREAM_DMA_NV 0x3371
+#define EGL_STREAM_DMA_SERVER_NV 0x3372
+#endif /* EGL_NV_stream_dma */
+
#ifndef EGL_NV_stream_fifo_next
#define EGL_NV_stream_fifo_next 1
#define EGL_PENDING_FRAME_NV 0x3329
@@ -1216,6 +1335,21 @@
#endif
#endif /* EGL_NV_stream_metadata */
+#ifndef EGL_NV_stream_origin
+#define EGL_NV_stream_origin 1
+#define EGL_STREAM_FRAME_ORIGIN_X_NV 0x3366
+#define EGL_STREAM_FRAME_ORIGIN_Y_NV 0x3367
+#define EGL_STREAM_FRAME_MAJOR_AXIS_NV 0x3368
+#define EGL_CONSUMER_AUTO_ORIENTATION_NV 0x3369
+#define EGL_PRODUCER_AUTO_ORIENTATION_NV 0x336A
+#define EGL_LEFT_NV 0x336B
+#define EGL_RIGHT_NV 0x336C
+#define EGL_TOP_NV 0x336D
+#define EGL_BOTTOM_NV 0x336E
+#define EGL_X_AXIS_NV 0x336F
+#define EGL_Y_AXIS_NV 0x3370
+#endif /* EGL_NV_stream_origin */
+
#ifndef EGL_NV_stream_remote
#define EGL_NV_stream_remote 1
#define EGL_STREAM_STATE_INITIALIZING_NV 0x3240
@@ -1312,6 +1446,21 @@
#endif /* KHRONOS_SUPPORT_INT64 */
#endif /* EGL_NV_system_time */
+#ifndef EGL_NV_triple_buffer
+#define EGL_NV_triple_buffer 1
+#define EGL_TRIPLE_BUFFER_NV 0x3230
+#endif /* EGL_NV_triple_buffer */
+
+#ifndef EGL_QNX_image_native_buffer
+#define EGL_QNX_image_native_buffer 1
+#define EGL_NATIVE_BUFFER_QNX 0x3551
+#endif /* EGL_QNX_image_native_buffer */
+
+#ifndef EGL_QNX_platform_screen
+#define EGL_QNX_platform_screen 1
+#define EGL_PLATFORM_SCREEN_QNX 0x3550
+#endif /* EGL_QNX_platform_screen */
+
#ifndef EGL_TIZEN_image_native_buffer
#define EGL_TIZEN_image_native_buffer 1
#define EGL_NATIVE_BUFFER_TIZEN 0x32A0
@@ -1322,6 +1471,40 @@
#define EGL_NATIVE_SURFACE_TIZEN 0x32A1
#endif /* EGL_TIZEN_image_native_surface */
+#ifndef EGL_WL_bind_wayland_display
+#define EGL_WL_bind_wayland_display 1
+#define PFNEGLBINDWAYLANDDISPLAYWL PFNEGLBINDWAYLANDDISPLAYWLPROC
+#define PFNEGLUNBINDWAYLANDDISPLAYWL PFNEGLUNBINDWAYLANDDISPLAYWLPROC
+#define PFNEGLQUERYWAYLANDBUFFERWL PFNEGLQUERYWAYLANDBUFFERWLPROC
+struct wl_display;
+struct wl_resource;
+#define EGL_WAYLAND_BUFFER_WL 0x31D5
+#define EGL_WAYLAND_PLANE_WL 0x31D6
+#define EGL_TEXTURE_Y_U_V_WL 0x31D7
+#define EGL_TEXTURE_Y_UV_WL 0x31D8
+#define EGL_TEXTURE_Y_XUXV_WL 0x31D9
+#define EGL_TEXTURE_EXTERNAL_WL 0x31DA
+#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWLPROC) (EGLDisplay dpy, struct wl_display *display);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWLPROC) (EGLDisplay dpy, struct wl_display *display);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWLPROC) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglBindWaylandDisplayWL (EGLDisplay dpy, struct wl_display *display);
+EGLAPI EGLBoolean EGLAPIENTRY eglUnbindWaylandDisplayWL (EGLDisplay dpy, struct wl_display *display);
+EGLAPI EGLBoolean EGLAPIENTRY eglQueryWaylandBufferWL (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value);
+#endif
+#endif /* EGL_WL_bind_wayland_display */
+
+#ifndef EGL_WL_create_wayland_buffer_from_image
+#define EGL_WL_create_wayland_buffer_from_image 1
+#define PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWLPROC
+struct wl_buffer;
+typedef struct wl_buffer *(EGLAPIENTRYP PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWLPROC) (EGLDisplay dpy, EGLImageKHR image);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI struct wl_buffer *EGLAPIENTRY eglCreateWaylandBufferFromImageWL (EGLDisplay dpy, EGLImageKHR image);
+#endif
+#endif /* EGL_WL_create_wayland_buffer_from_image */
+
#ifdef __cplusplus
}
#endif
diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h
index 0bc2cb9..6786afd 100644
--- a/opengl/include/EGL/eglplatform.h
+++ b/opengl/include/EGL/eglplatform.h
@@ -2,36 +2,17 @@
#define __eglplatform_h_
/*
-** Copyright (c) 2007-2016 The Khronos Group Inc.
-**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+** Copyright 2007-2020 The Khronos Group Inc.
+** SPDX-License-Identifier: Apache-2.0
*/
/* Platform-specific types and definitions for egl.h
- * $Revision: 30994 $ on $Date: 2015-04-30 13:36:48 -0700 (Thu, 30 Apr 2015) $
*
* Adopters may modify khrplatform.h and this file to suit their platform.
* You are encouraged to submit all modifications to the Khronos group so that
* they can be included in future versions of this file. Please submit changes
- * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla)
- * by filing a bug against product "EGL" component "Registry".
+ * by filing an issue or pull request on the public Khronos EGL Registry, at
+ * https://www.github.com/KhronosGroup/EGL-Registry/
*/
#include <KHR/khrplatform.h>
@@ -67,7 +48,13 @@
* implementations.
*/
-#if defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
+#if defined(EGL_NO_PLATFORM_SPECIFIC_TYPES)
+
+typedef void *EGLNativeDisplayType;
+typedef void *EGLNativePixmapType;
+typedef void *EGLNativeWindowType;
+
+#elif defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
@@ -77,11 +64,23 @@
typedef HBITMAP EGLNativePixmapType;
typedef HWND EGLNativeWindowType;
+#elif defined(__QNX__)
+
+typedef khronos_uintptr_t EGLNativeDisplayType;
+typedef struct _screen_pixmap* EGLNativePixmapType; /* screen_pixmap_t */
+typedef struct _screen_window* EGLNativeWindowType; /* screen_window_t */
+
+#elif defined(__EMSCRIPTEN__)
+
+typedef int EGLNativeDisplayType;
+typedef int EGLNativePixmapType;
+typedef int EGLNativeWindowType;
+
#elif defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */
typedef int EGLNativeDisplayType;
-typedef void *EGLNativeWindowType;
typedef void *EGLNativePixmapType;
+typedef void *EGLNativeWindowType;
#elif defined(WL_EGL_PLATFORM)
@@ -100,17 +99,17 @@
struct ANativeWindow;
struct egl_native_pixmap_t;
-typedef struct ANativeWindow* EGLNativeWindowType;
-typedef struct egl_native_pixmap_t* EGLNativePixmapType;
typedef void* EGLNativeDisplayType;
+typedef struct egl_native_pixmap_t* EGLNativePixmapType;
+typedef struct ANativeWindow* EGLNativeWindowType;
#elif defined(USE_OZONE)
typedef intptr_t EGLNativeDisplayType;
-typedef intptr_t EGLNativeWindowType;
typedef intptr_t EGLNativePixmapType;
+typedef intptr_t EGLNativeWindowType;
-#elif defined(__unix__) || defined(USE_X11)
+#elif defined(USE_X11)
/* X11 (tentative) */
#include <X11/Xlib.h>
@@ -120,11 +119,17 @@
typedef Pixmap EGLNativePixmapType;
typedef Window EGLNativeWindowType;
+#elif defined(__unix__)
+
+typedef void *EGLNativeDisplayType;
+typedef khronos_uintptr_t EGLNativePixmapType;
+typedef khronos_uintptr_t EGLNativeWindowType;
+
#elif defined(__APPLE__)
typedef int EGLNativeDisplayType;
-typedef void *EGLNativeWindowType;
typedef void *EGLNativePixmapType;
+typedef void *EGLNativeWindowType;
#elif defined(__HAIKU__)
@@ -134,6 +139,12 @@
typedef khronos_uintptr_t EGLNativePixmapType;
typedef khronos_uintptr_t EGLNativeWindowType;
+#elif defined(__Fuchsia__)
+
+typedef void *EGLNativeDisplayType;
+typedef khronos_uintptr_t EGLNativePixmapType;
+typedef khronos_uintptr_t EGLNativeWindowType;
+
#else
#error "Platform not recognized"
#endif
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index ec7b190..bf0e38e 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -270,7 +270,7 @@
hnd = attempt_to_load_updated_driver(cnx);
// If updated driver apk is set but fail to load, abort here.
- LOG_ALWAYS_FATAL_IF(android::GraphicsEnv::getInstance().getDriverNamespace(),
+ LOG_ALWAYS_FATAL_IF(android::GraphicsEnv::getInstance().getDriverNamespace() && !hnd,
"couldn't find an OpenGL ES implementation from %s",
android::GraphicsEnv::getInstance().getDriverPath().c_str());
}
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 502c14f..8cb637b 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -248,7 +248,7 @@
return cnx->platform.eglGetProcAddress(procname);
}
-EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, EGLint* rects,
+EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw, const EGLint* rects,
EGLint n_rects) {
ATRACE_CALL();
clearError();
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
index 1c91f1d..b6f2c34 100644
--- a/opengl/libs/EGL/egl_entries.in
+++ b/opengl/libs/EGL/egl_entries.in
@@ -106,5 +106,5 @@
/* Partial update extensions */
-EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint *, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, const EGLint *, EGLint)
EGL_ENTRY(EGLBoolean, eglSetDamageRegionKHR, EGLDisplay, EGLSurface, EGLint *, EGLint)
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index a6af713..6e35041 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -916,42 +916,72 @@
egl_context_t* const c = get_context(share_list);
share_list = c->context;
}
+
+ bool skip_telemetry = false;
+
+ auto findAttribute = [](const EGLint* attrib_ptr, GLint attribute, GLint* value) {
+ while (attrib_ptr && *attrib_ptr != EGL_NONE) {
+ GLint attr = *attrib_ptr++;
+ GLint val = *attrib_ptr++;
+ if (attr == attribute) {
+ if (value) {
+ *value = val;
+ }
+ return true;
+ }
+ }
+ return false;
+ };
+
+ std::vector<EGLint> replacement_attrib_list;
+ GLint telemetry_value;
+ if (findAttribute(attrib_list, EGL_TELEMETRY_HINT_ANDROID, &telemetry_value)) {
+ skip_telemetry = (telemetry_value == android::GpuStatsInfo::SKIP_TELEMETRY);
+
+ // We need to remove EGL_TELEMETRY_HINT_ANDROID or the underlying drivers will
+ // complain about an unexpected attribute
+ const EGLint* attrib_ptr = attrib_list;
+ while (attrib_ptr && *attrib_ptr != EGL_NONE) {
+ GLint attr = *attrib_ptr++;
+ GLint val = *attrib_ptr++;
+ if (attr != EGL_TELEMETRY_HINT_ANDROID) {
+ replacement_attrib_list.push_back(attr);
+ replacement_attrib_list.push_back(val);
+ }
+ }
+ replacement_attrib_list.push_back(EGL_NONE);
+ attrib_list = replacement_attrib_list.data();
+ }
// b/111083885 - If we are presenting EGL 1.4 interface to apps
// error out on robust access attributes that are invalid
// in EGL 1.4 as the driver may be fine with them but dEQP expects
// tests to fail according to spec.
if (attrib_list && (cnx->driverVersion < EGL_MAKE_VERSION(1, 5, 0))) {
- const EGLint* attrib_ptr = attrib_list;
- while (*attrib_ptr != EGL_NONE) {
- GLint attr = *attrib_ptr++;
- GLint value = *attrib_ptr++;
- if (attr == EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR) {
- // We are GL ES context with EGL 1.4, this is an invalid
- // attribute
- return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT);
- }
- };
+ if (findAttribute(attrib_list, EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR,
+ nullptr)) {
+ // We are GL ES context with EGL 1.4, this is an invalid attribute
+ return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT);
+ }
}
EGLContext context =
cnx->egl.eglCreateContext(dp->disp.dpy, config, share_list, attrib_list);
if (context != EGL_NO_CONTEXT) {
// figure out if it's a GLESv1 or GLESv2
int version = egl_connection_t::GLESv1_INDEX;
- if (attrib_list) {
- while (*attrib_list != EGL_NONE) {
- GLint attr = *attrib_list++;
- GLint value = *attrib_list++;
- if (attr == EGL_CONTEXT_CLIENT_VERSION && (value == 2 || value == 3)) {
- version = egl_connection_t::GLESv2_INDEX;
- }
- };
+ GLint version_value;
+ if (findAttribute(attrib_list, EGL_CONTEXT_CLIENT_VERSION, &version_value)) {
+ if (version_value == 2 || version_value == 3) {
+ version = egl_connection_t::GLESv2_INDEX;
+ }
}
if (version == egl_connection_t::GLESv1_INDEX) {
android::GraphicsEnv::getInstance().setTargetStats(
android::GpuStatsInfo::Stats::GLES_1_IN_USE);
}
- android::GraphicsEnv::getInstance().setTargetStats(
- android::GpuStatsInfo::Stats::CREATED_GLES_CONTEXT);
+ if (!skip_telemetry) {
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::CREATED_GLES_CONTEXT);
+ }
egl_context_t* c = new egl_context_t(dpy, context, config, cnx, version);
return c;
}
@@ -1324,7 +1354,7 @@
std::mutex mMutex;
};
-EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, EGLint* rects,
+EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, const EGLint* rects,
EGLint n_rects) {
const egl_display_t* dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
diff --git a/opengl/libs/GLES2/gl2ext_api.in b/opengl/libs/GLES2/gl2ext_api.in
index 4a0d4b9..dc99e09 100644
--- a/opengl/libs/GLES2/gl2ext_api.in
+++ b/opengl/libs/GLES2/gl2ext_api.in
@@ -427,9 +427,6 @@
void API_ENTRY(glDrawElementsInstancedBaseVertexEXT)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) {
CALL_GL_API(glDrawElementsInstancedBaseVertexEXT, mode, count, type, indices, instancecount, basevertex);
}
-void API_ENTRY(glMultiDrawElementsBaseVertexOES)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) {
- CALL_GL_API(glMultiDrawElementsBaseVertexOES, mode, count, type, indices, primcount, basevertex);
-}
void API_ENTRY(glDrawArraysInstancedEXT)(GLenum mode, GLint start, GLsizei count, GLsizei primcount) {
CALL_GL_API(glDrawArraysInstancedEXT, mode, start, count, primcount);
}
diff --git a/opengl/libs/entries.in b/opengl/libs/entries.in
index a306510..4c1eefc 100644
--- a/opengl/libs/entries.in
+++ b/opengl/libs/entries.in
@@ -605,7 +605,6 @@
GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount)
GL_ENTRY(void, glMultiDrawArraysIndirectEXT, GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride)
GL_ENTRY(void, glMultiDrawElementsBaseVertexEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex)
-GL_ENTRY(void, glMultiDrawElementsBaseVertexOES, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex)
GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount)
GL_ENTRY(void, glMultiDrawElementsIndirectEXT, GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride)
GL_ENTRY(void, glMultiTexCoord4f, GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
diff --git a/opengl/libs/platform_entries.in b/opengl/libs/platform_entries.in
index 4673411..004aa5a 100644
--- a/opengl/libs/platform_entries.in
+++ b/opengl/libs/platform_entries.in
@@ -24,7 +24,7 @@
EGL_ENTRY(EGLBoolean, eglWaitNative, EGLint)
EGL_ENTRY(EGLint, eglGetError, void)
EGL_ENTRY(__eglMustCastToProperFunctionPointerType, eglGetProcAddress, const char*)
-EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint*, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, const EGLint*, EGLint)
EGL_ENTRY(EGLBoolean, eglSwapBuffers, EGLDisplay, EGLSurface)
EGL_ENTRY(EGLBoolean, eglCopyBuffers, EGLDisplay, EGLSurface, NativePixmapType)
EGL_ENTRY(const char*, eglQueryString, EGLDisplay, EGLint)
diff --git a/services/gpuservice/bpfprogs/Android.bp b/services/gpuservice/bpfprogs/Android.bp
index 680b291..a391c81 100644
--- a/services/gpuservice/bpfprogs/Android.bp
+++ b/services/gpuservice/bpfprogs/Android.bp
@@ -24,9 +24,4 @@
bpf {
name: "gpuMem.o",
srcs: ["gpuMem.c"],
- btf: true,
- cflags: [
- "-Wall",
- "-Werror",
- ],
}
diff --git a/services/gpuservice/gpuwork/bpfprogs/Android.bp b/services/gpuservice/gpuwork/bpfprogs/Android.bp
index fe45c98..2e444fe 100644
--- a/services/gpuservice/gpuwork/bpfprogs/Android.bp
+++ b/services/gpuservice/gpuwork/bpfprogs/Android.bp
@@ -20,11 +20,7 @@
name: "gpuWork.o",
srcs: ["gpuWork.c"],
cflags: [
- "-Wall",
- "-Werror",
- "-Wformat",
"-Wthread-safety",
- "-Wunused",
"-Wunreachable-code",
],
}
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 70801dc..cb220ab 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -172,9 +172,8 @@
export_static_lib_headers: [
"libinputdispatcher",
],
- export_include_dirs: [
- ".",
- "include",
+ export_shared_lib_headers: [
+ "libinputflinger_base",
],
}
@@ -185,7 +184,16 @@
cc_library_headers {
name: "libinputflinger_headers",
host_supported: true,
- export_include_dirs: ["include"],
+ export_include_dirs: [
+ "include",
+ ".",
+ ],
+ header_libs: [
+ "libchrome-gestures_headers",
+ ],
+ export_header_lib_headers: [
+ "libchrome-gestures_headers",
+ ],
}
filegroup {
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 625599a..397feda 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -511,8 +511,9 @@
std::scoped_lock _l(mLock);
dump += "PointerChoreographer:\n";
- dump += StringPrintf("show touches: %s\n", mShowTouchesEnabled ? "true" : "false");
- dump += StringPrintf("stylus pointer icon enabled: %s\n",
+ dump += StringPrintf(INDENT "Show Touches Enabled: %s\n",
+ mShowTouchesEnabled ? "true" : "false");
+ dump += StringPrintf(INDENT "Stylus PointerIcon Enabled: %s\n",
mStylusPointerIconEnabled ? "true" : "false");
dump += INDENT "MousePointerControllers:\n";
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index a4dd909..af9d2eb 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -149,6 +149,9 @@
},
{
"name": "monkey_test"
+ },
+ {
+ "name": "CtsSurfaceControlTests"
}
],
"postsubmit": [
@@ -290,6 +293,9 @@
},
{
"name": "monkey_test"
+ },
+ {
+ "name": "CtsInputRootTestCases"
}
],
"staged-platinum-postsubmit": [
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 4076817..bcef350 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -693,7 +693,8 @@
*/
std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState,
const TouchState& newTouchState,
- const MotionEntry& entry) {
+ const MotionEntry& entry,
+ std::function<void()> dump) {
const int32_t maskedAction = MotionEvent::getActionMasked(entry.action);
if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
@@ -741,6 +742,7 @@
// crashing the device with FATAL.
severity = android::base::LogSeverity::ERROR;
}
+ dump();
LOG(severity) << "Expected ACTION_HOVER_MOVE instead of " << entry.getDescription();
}
touchedWindow.dispatchMode = InputTarget::DispatchMode::AS_IS;
@@ -2448,12 +2450,19 @@
if (isDown) {
targets += findOutsideTargetsLocked(displayId, newTouchedWindowHandle, pointer.id);
}
+ LOG_IF(INFO, newTouchedWindowHandle == nullptr)
+ << "No new touched window at (" << std::format("{:.1f}, {:.1f}", x, y)
+ << ") in display " << displayId;
// Handle the case where we did not find a window.
- if (newTouchedWindowHandle == nullptr) {
- ALOGD("No new touched window at (%.1f, %.1f) in display %s", x, y,
- displayId.toString().c_str());
- // Try to assign the pointer to the first foreground window we find, if there is one.
- newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
+ if (!input_flags::split_all_touches()) {
+ // If we are force splitting all touches, then touches outside of the window should
+ // be dropped, even if this device already has pointers down in another window.
+ if (newTouchedWindowHandle == nullptr) {
+ // Try to assign the pointer to the first foreground window we find, if there is
+ // one.
+ newTouchedWindowHandle =
+ tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
+ }
}
// Verify targeted injection.
@@ -2676,7 +2685,7 @@
// Check if the wallpaper window should deliver the corresponding event.
slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
- tempTouchState, entry.deviceId, pointer, targets);
+ tempTouchState, entry, targets);
tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id,
oldTouchedWindowHandle);
}
@@ -2703,7 +2712,9 @@
// Update dispatching for hover enter and exit.
{
std::vector<TouchedWindow> hoveringWindows =
- getHoveringWindowsLocked(oldState, tempTouchState, entry);
+ getHoveringWindowsLocked(oldState, tempTouchState, entry,
+ std::bind_front(&InputDispatcher::logDispatchStateLocked,
+ this));
// Hardcode to single hovering pointer for now.
std::bitset<MAX_POINTER_ID + 1> pointerIds;
pointerIds.set(entry.pointerProperties[0].id);
@@ -4872,6 +4883,10 @@
logDispatchStateLocked();
LOG(ERROR) << "Inconsistent event: " << motionEvent
<< ", reason: " << result.error();
+ if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) {
+ mLock.unlock();
+ return InputEventInjectionResult::FAILED;
+ }
}
}
@@ -7007,6 +7022,13 @@
for (const auto& info : update.windowInfos) {
handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
handlesPerDisplay[info.displayId].push_back(sp<WindowInfoHandle>::make(info));
+ if (input_flags::split_all_touches()) {
+ handlesPerDisplay[info.displayId]
+ .back()
+ ->editInfo()
+ ->setInputConfig(android::gui::WindowInfo::InputConfig::PREVENT_SPLITTING,
+ false);
+ }
}
{ // acquire lock
@@ -7082,9 +7104,11 @@
void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<WindowInfoHandle>& oldWindowHandle,
const sp<WindowInfoHandle>& newWindowHandle,
- TouchState& state, DeviceId deviceId,
- const PointerProperties& pointerProperties,
+ TouchState& state, const MotionEntry& entry,
std::vector<InputTarget>& targets) const {
+ LOG_IF(FATAL, entry.getPointerCount() != 1) << "Entry not eligible for slip: " << entry;
+ const DeviceId deviceId = entry.deviceId;
+ const PointerProperties& pointerProperties = entry.pointerProperties[0];
std::vector<PointerProperties> pointers{pointerProperties};
const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
@@ -7111,7 +7135,7 @@
state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::SLIPPERY_ENTER,
InputTarget::Flags::WINDOW_IS_OBSCURED |
InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED,
- deviceId, pointers);
+ deviceId, pointers, entry.eventTime);
}
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 698bdba..87dfd1d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -709,11 +709,23 @@
sp<InputReporterInterface> mReporter;
+ /**
+ * Slip the wallpaper touch if necessary.
+ *
+ * @param targetFlags the target flags
+ * @param oldWindowHandle the old window that the touch slipped out of
+ * @param newWindowHandle the new window that the touch is slipping into
+ * @param state the current touch state. This will be updated if necessary to reflect the new
+ * windows that are receiving touch.
+ * @param deviceId the device id of the current motion being processed
+ * @param pointerProperties the pointer properties of the current motion being processed
+ * @param targets the current targets to add the walpaper ones to
+ * @param eventTime the new downTime for the wallpaper target
+ */
void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
const sp<android::gui::WindowInfoHandle>& newWindowHandle,
- TouchState& state, DeviceId deviceId,
- const PointerProperties& pointerProperties,
+ TouchState& state, const MotionEntry& entry,
std::vector<InputTarget>& targets) const REQUIRES(mLock);
void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags,
ftl::Flags<InputTarget::Flags> newTargetFlags,
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 9d4bb3d..5a70dd5 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -47,7 +47,7 @@
const sp<android::gui::WindowInfoHandle>& windowHandle,
InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers,
- std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt);
+ std::optional<nsecs_t> firstDownTimeInTarget);
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
DeviceId deviceId, const PointerProperties& pointer);
void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h
index cb525a4..96e619c 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.h
+++ b/services/inputflinger/dispatcher/trace/InputTracer.h
@@ -65,10 +65,10 @@
void onEventProcessingComplete(nsecs_t processingTimestamp);
InputTracer& tracer;
- std::vector<const TracedEvent> events;
+ std::vector<TracedEvent> events;
bool isEventProcessingComplete{false};
// A queue to hold dispatch args from being traced until event processing is complete.
- std::vector<const WindowDispatchArgs> pendingDispatchArgs;
+ std::vector<WindowDispatchArgs> pendingDispatchArgs;
// The metadata should not be modified after event processing is complete.
TracedEventMetadata metadata{};
};
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 42a03c1..7bec94e 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -34,7 +34,9 @@
#include <vector>
#include "PointerControllerInterface.h"
+#include "TouchpadHardwareState.h"
#include "VibrationElement.h"
+#include "include/gestures.h"
// Maximum supported size of a vibration pattern.
// Must be at least 2.
@@ -227,6 +229,9 @@
// True to enable tap dragging on touchpads.
bool touchpadTapDraggingEnabled;
+ // True if hardware state update notifications should be sent to the policy.
+ bool shouldNotifyTouchpadHardwareState;
+
// True to enable a zone on the right-hand side of touchpads where clicks will be turned into
// context (a.k.a. "right") clicks.
bool touchpadRightClickZoneEnabled;
@@ -268,6 +273,7 @@
touchpadNaturalScrollingEnabled(true),
touchpadTapToClickEnabled(true),
touchpadTapDraggingEnabled(false),
+ shouldNotifyTouchpadHardwareState(false),
touchpadRightClickZoneEnabled(false),
stylusButtonMotionEventsEnabled(true),
stylusPointerIconEnabled(false) {}
@@ -363,6 +369,8 @@
virtual std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) = 0;
+ virtual std::optional<HardwareProperties> getTouchpadHardwareProperties(int32_t deviceId) = 0;
+
/* Return true if the device can send input events to the specified display. */
virtual bool canDispatchToDisplay(int32_t deviceId, ui::LogicalDisplayId displayId) = 0;
@@ -454,6 +462,10 @@
*/
virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) = 0;
+ /* Sends the hardware state of a connected touchpad */
+ virtual void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
+ int32_t deviceId) = 0;
+
/* Gets the keyboard layout for a particular input device. */
virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier,
diff --git a/services/inputflinger/include/TouchpadHardwareState.h b/services/inputflinger/include/TouchpadHardwareState.h
new file mode 100644
index 0000000..a8dd44c
--- /dev/null
+++ b/services/inputflinger/include/TouchpadHardwareState.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2024 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 <include/gestures.h>
+#include <vector>
+
+namespace android {
+
+// A Gestures library HardwareState struct (from libchrome-gestures), but bundled
+// with a vector to contain its FingerStates, so you don't have to worry about where
+// that memory is allocated.
+struct SelfContainedHardwareState {
+ HardwareState state;
+ std::vector<FingerState> fingers;
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index e48e94f..e11adb8 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -517,10 +517,10 @@
// --- RawAbsoluteAxisInfo ---
-std::ostream& operator<<(std::ostream& out, const RawAbsoluteAxisInfo& info) {
- if (info.valid) {
- out << "min=" << info.minValue << ", max=" << info.maxValue << ", flat=" << info.flat
- << ", fuzz=" << info.fuzz << ", resolution=" << info.resolution;
+std::ostream& operator<<(std::ostream& out, const std::optional<RawAbsoluteAxisInfo>& info) {
+ if (info) {
+ out << "min=" << info->minValue << ", max=" << info->maxValue << ", flat=" << info->flat
+ << ", fuzz=" << info->fuzz << ", resolution=" << info->resolution;
} else {
out << "unknown range";
}
@@ -649,7 +649,6 @@
continue;
}
auto& [axisInfo, value] = absState[axis];
- axisInfo.valid = true;
axisInfo.minValue = info.minimum;
axisInfo.maxValue = info.maximum;
axisInfo.flat = info.flat;
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 2daf195..70f024e 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -725,6 +725,15 @@
return count;
}
+std::optional<HardwareProperties> InputDevice::getTouchpadHardwareProperties() {
+ std::optional<HardwareProperties> result = first_in_mappers<HardwareProperties>(
+ [](InputMapper& mapper) -> std::optional<HardwareProperties> {
+ return mapper.getTouchpadHardwareProperties();
+ });
+
+ return result;
+}
+
void InputDevice::updateLedState(bool reset) {
for_each_mapper([reset](InputMapper& mapper) { mapper.updateLedState(reset); });
}
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index f0e53b5..a5b1249 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -33,6 +33,7 @@
#include <utils/Thread.h>
#include "InputDevice.h"
+#include "include/gestures.h"
using android::base::StringPrintf;
@@ -817,6 +818,18 @@
return device->getDeviceInfo().getSensors();
}
+std::optional<HardwareProperties> InputReader::getTouchpadHardwareProperties(int32_t deviceId) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+
+ if (device == nullptr) {
+ return {};
+ }
+
+ return device->getTouchpadHardwareProperties();
+}
+
bool InputReader::setLightColor(int32_t deviceId, int32_t lightId, int32_t color) {
std::scoped_lock _l(mLock);
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index feae6b6..657126a 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -71,18 +71,14 @@
/* Describes an absolute axis. */
struct RawAbsoluteAxisInfo {
- bool valid{false}; // true if the information is valid, false otherwise
-
int32_t minValue{}; // minimum value
int32_t maxValue{}; // maximum value
int32_t flat{}; // center flat position, eg. flat == 8 means center is between -8 and 8
int32_t fuzz{}; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
int32_t resolution{}; // resolution in units per mm or radians per mm
-
- inline void clear() { *this = RawAbsoluteAxisInfo(); }
};
-std::ostream& operator<<(std::ostream& out, const RawAbsoluteAxisInfo& info);
+std::ostream& operator<<(std::ostream& out, const std::optional<RawAbsoluteAxisInfo>& info);
/*
* Input device classes.
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 086c26f..021978d 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -43,7 +43,7 @@
public:
InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
const InputDeviceIdentifier& identifier);
- ~InputDevice();
+ virtual ~InputDevice();
inline InputReaderContext* getContext() { return mContext; }
inline int32_t getId() const { return mId; }
@@ -56,7 +56,7 @@
}
inline const std::string getLocation() const { return mIdentifier.location; }
inline ftl::Flags<InputDeviceClass> getClasses() const { return mClasses; }
- inline uint32_t getSources() const { return mSources; }
+ inline virtual uint32_t getSources() const { return mSources; }
inline bool hasEventHubDevices() const { return !mDevices.empty(); }
inline bool isExternal() { return mIsExternal; }
@@ -72,7 +72,7 @@
inline std::optional<std::string> getDeviceTypeAssociation() const {
return mAssociatedDeviceType;
}
- inline std::optional<DisplayViewport> getAssociatedViewport() const {
+ inline virtual std::optional<DisplayViewport> getAssociatedViewport() const {
return mAssociatedViewport;
}
inline bool hasMic() const { return mHasMic; }
@@ -132,7 +132,7 @@
[[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when);
- inline const PropertyMap& getConfiguration() { return mConfiguration; }
+ inline virtual const PropertyMap& getConfiguration() const { return mConfiguration; }
inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
std::optional<ui::LogicalDisplayId> getAssociatedDisplayId();
@@ -141,6 +141,8 @@
size_t getMapperCount();
+ std::optional<HardwareProperties> getTouchpadHardwareProperties();
+
// construct and add a mapper to the input device
template <class T, typename... Args>
T& addMapper(int32_t eventHubId, Args... args) {
@@ -299,28 +301,24 @@
inline ftl::Flags<InputDeviceClass> getDeviceClasses() const {
return mEventHub->getDeviceClasses(mId);
}
+ inline uint32_t getDeviceSources() const { return mDevice.getSources(); }
inline InputDeviceIdentifier getDeviceIdentifier() const {
return mEventHub->getDeviceIdentifier(mId);
}
inline int32_t getDeviceControllerNumber() const {
return mEventHub->getDeviceControllerNumber(mId);
}
- inline status_t getAbsoluteAxisInfo(int32_t code, RawAbsoluteAxisInfo* axisInfo) const {
+ inline std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t code) const {
std::optional<RawAbsoluteAxisInfo> info = mEventHub->getAbsoluteAxisInfo(mId, code);
- if (!info.has_value()) {
- axisInfo->clear();
- return NAME_NOT_FOUND;
- }
- *axisInfo = *info;
// Validate axis info for InputDevice.
- if (axisInfo->valid && axisInfo->minValue == axisInfo->maxValue) {
+ if (info && info->minValue == info->maxValue) {
// Historically, we deem axes with the same min and max values as invalid to avoid
// dividing by zero when scaling by max - min.
// TODO(b/291772515): Perform axis info validation on a per-axis basis when it is used.
- axisInfo->valid = false;
+ return std::nullopt;
}
- return OK;
+ return info;
}
inline bool hasRelativeAxis(int32_t code) const {
return mEventHub->hasRelativeAxis(mId, code);
@@ -435,8 +433,7 @@
}
inline bool hasAbsoluteAxis(int32_t code) const {
- std::optional<RawAbsoluteAxisInfo> info = mEventHub->getAbsoluteAxisInfo(mId, code);
- return info.has_value() && info->valid;
+ return mEventHub->getAbsoluteAxisInfo(mId, code).has_value();
}
inline bool isKeyPressed(int32_t scanCode) const {
return mEventHub->getScanCodeState(mId, scanCode) == AKEY_STATE_DOWN;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 4f60a8a..2cc0a00 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -104,6 +104,8 @@
std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) override;
+ std::optional<HardwareProperties> getTouchpadHardwareProperties(int32_t deviceId) override;
+
bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) override;
bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) override;
diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
index 90685de..c8e7790 100644
--- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
@@ -16,6 +16,7 @@
#include "CapturedTouchpadEventConverter.h"
+#include <optional>
#include <sstream>
#include <android-base/stringprintf.h>
@@ -53,32 +54,33 @@
mMotionAccumulator(motionAccumulator),
mHasTouchMinor(deviceContext.hasAbsoluteAxis(ABS_MT_TOUCH_MINOR)),
mHasToolMinor(deviceContext.hasAbsoluteAxis(ABS_MT_WIDTH_MINOR)) {
- RawAbsoluteAxisInfo orientationInfo;
- deviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &orientationInfo);
- if (orientationInfo.valid) {
- if (orientationInfo.maxValue > 0) {
- mOrientationScale = M_PI_2 / orientationInfo.maxValue;
- } else if (orientationInfo.minValue < 0) {
- mOrientationScale = -M_PI_2 / orientationInfo.minValue;
+ if (std::optional<RawAbsoluteAxisInfo> orientation =
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION);
+ orientation) {
+ if (orientation->maxValue > 0) {
+ mOrientationScale = M_PI_2 / orientation->maxValue;
+ } else if (orientation->minValue < 0) {
+ mOrientationScale = -M_PI_2 / orientation->minValue;
}
}
// TODO(b/275369880): support touch.pressure.calibration and .scale properties when captured.
- RawAbsoluteAxisInfo pressureInfo;
- deviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &pressureInfo);
- if (pressureInfo.valid && pressureInfo.maxValue > 0) {
- mPressureScale = 1.0 / pressureInfo.maxValue;
+ if (std::optional<RawAbsoluteAxisInfo> pressure =
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE);
+ pressure && pressure->maxValue > 0) {
+ mPressureScale = 1.0 / pressure->maxValue;
}
- RawAbsoluteAxisInfo touchMajorInfo, toolMajorInfo;
- deviceContext.getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &touchMajorInfo);
- deviceContext.getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &toolMajorInfo);
- mHasTouchMajor = touchMajorInfo.valid;
- mHasToolMajor = toolMajorInfo.valid;
- if (mHasTouchMajor && touchMajorInfo.maxValue != 0) {
- mSizeScale = 1.0f / touchMajorInfo.maxValue;
- } else if (mHasToolMajor && toolMajorInfo.maxValue != 0) {
- mSizeScale = 1.0f / toolMajorInfo.maxValue;
+ std::optional<RawAbsoluteAxisInfo> touchMajor =
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR);
+ std::optional<RawAbsoluteAxisInfo> toolMajor =
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR);
+ mHasTouchMajor = touchMajor.has_value();
+ mHasToolMajor = toolMajor.has_value();
+ if (mHasTouchMajor && touchMajor->maxValue != 0) {
+ mSizeScale = 1.0f / touchMajor->maxValue;
+ } else if (mHasToolMajor && toolMajor->maxValue != 0) {
+ mSizeScale = 1.0f / toolMajor->maxValue;
}
}
@@ -113,15 +115,13 @@
tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MAJOR, ABS_MT_WIDTH_MAJOR);
tryAddRawMotionRange(/*byref*/ info, AMOTION_EVENT_AXIS_TOOL_MINOR, ABS_MT_WIDTH_MINOR);
- RawAbsoluteAxisInfo pressureInfo;
- mDeviceContext.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &pressureInfo);
- if (pressureInfo.valid) {
+ if (mDeviceContext.hasAbsoluteAxis(ABS_MT_PRESSURE)) {
info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, SOURCE, 0, 1, 0, 0, 0);
}
- RawAbsoluteAxisInfo orientationInfo;
- mDeviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &orientationInfo);
- if (orientationInfo.valid && (orientationInfo.maxValue > 0 || orientationInfo.minValue < 0)) {
+ if (std::optional<RawAbsoluteAxisInfo> orientation =
+ mDeviceContext.getAbsoluteAxisInfo(ABS_MT_ORIENTATION);
+ orientation && (orientation->maxValue > 0 || orientation->minValue < 0)) {
info.addMotionRange(AMOTION_EVENT_AXIS_ORIENTATION, SOURCE, -M_PI_2, M_PI_2, 0, 0, 0);
}
@@ -133,11 +133,10 @@
void CapturedTouchpadEventConverter::tryAddRawMotionRange(InputDeviceInfo& deviceInfo,
int32_t androidAxis,
int32_t evdevAxis) const {
- RawAbsoluteAxisInfo info;
- mDeviceContext.getAbsoluteAxisInfo(evdevAxis, &info);
- if (info.valid) {
- deviceInfo.addMotionRange(androidAxis, SOURCE, info.minValue, info.maxValue, info.flat,
- info.fuzz, info.resolution);
+ std::optional<RawAbsoluteAxisInfo> info = mDeviceContext.getAbsoluteAxisInfo(evdevAxis);
+ if (info) {
+ deviceInfo.addMotionRange(androidAxis, SOURCE, info->minValue, info->maxValue, info->flat,
+ info->fuzz, info->resolution);
}
}
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 2108488..3fc370c 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -25,9 +25,6 @@
namespace android {
-class CursorButtonAccumulator;
-class CursorScrollAccumulator;
-
/* Keeps track of cursor movements. */
class CursorMotionAccumulator {
public:
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 3af1d04..4cd37d7 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -33,7 +33,7 @@
void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
InputMapper::populateDeviceInfo(info);
- if (mRawPressureAxis.valid) {
+ if (mRawPressureAxis || mTouchButtonAccumulator.hasButtonTouch()) {
info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f);
}
@@ -50,7 +50,7 @@
std::list<NotifyArgs> ExternalStylusInputMapper::reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
ConfigurationChanges changes) {
- getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
+ mRawPressureAxis = getAbsoluteAxisInfo(ABS_PRESSURE);
mTouchButtonAccumulator.configure();
return {};
}
@@ -82,10 +82,10 @@
mStylusState.toolType = ToolType::STYLUS;
}
- if (mRawPressureAxis.valid) {
+ if (mRawPressureAxis) {
auto rawPressure = static_cast<float>(mSingleTouchMotionAccumulator.getAbsolutePressure());
- mStylusState.pressure = (rawPressure - mRawPressureAxis.minValue) /
- static_cast<float>(mRawPressureAxis.maxValue - mRawPressureAxis.minValue);
+ mStylusState.pressure = (rawPressure - mRawPressureAxis->minValue) /
+ static_cast<float>(mRawPressureAxis->maxValue - mRawPressureAxis->minValue);
} else if (mTouchButtonAccumulator.hasButtonTouch()) {
mStylusState.pressure = mTouchButtonAccumulator.isHovering() ? 0.0f : 1.0f;
}
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index c040a7b..d48fd9b 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -16,6 +16,8 @@
#pragma once
+#include <optional>
+
#include "InputMapper.h"
#include "SingleTouchMotionAccumulator.h"
@@ -43,7 +45,7 @@
private:
SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
- RawAbsoluteAxisInfo mRawPressureAxis;
+ std::optional<RawAbsoluteAxisInfo> mRawPressureAxis;
TouchButtonAccumulator mTouchButtonAccumulator;
StylusState mStylusState;
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index b6c5c98..627df7f 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -18,6 +18,7 @@
#include "InputMapper.h"
+#include <optional>
#include <sstream>
#include <ftl/enum.h>
@@ -116,15 +117,16 @@
return {};
}
-status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) {
- return getDeviceContext().getAbsoluteAxisInfo(axis, axisInfo);
+std::optional<RawAbsoluteAxisInfo> InputMapper::getAbsoluteAxisInfo(int32_t axis) {
+ return getDeviceContext().getAbsoluteAxisInfo(axis);
}
void InputMapper::bumpGeneration() {
getDeviceContext().bumpGeneration();
}
-void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis,
+void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump,
+ const std::optional<RawAbsoluteAxisInfo>& axis,
const char* name) {
std::stringstream out;
out << INDENT4 << name << ": " << axis << "\n";
@@ -138,4 +140,7 @@
dump += StringPrintf(INDENT4 "Tool Type: %s\n", ftl::enum_string(state.toolType).c_str());
}
+std::optional<HardwareProperties> InputMapper::getTouchpadHardwareProperties() {
+ return std::nullopt;
+}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 2c51448..75cc4bb 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -16,6 +16,8 @@
#pragma once
+#include <optional>
+
#include "EventHub.h"
#include "InputDevice.h"
#include "InputListener.h"
@@ -120,16 +122,19 @@
virtual std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() { return std::nullopt; }
virtual void updateLedState(bool reset) {}
+ virtual std::optional<HardwareProperties> getTouchpadHardwareProperties();
+
protected:
InputDeviceContext& mDeviceContext;
explicit InputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
- status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo);
+ std::optional<RawAbsoluteAxisInfo> getAbsoluteAxisInfo(int32_t axis);
void bumpGeneration();
- static void dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis,
+ static void dumpRawAbsoluteAxisInfo(std::string& dump,
+ const std::optional<RawAbsoluteAxisInfo>& axis,
const char* name);
static void dumpStylusState(std::string& dump, const StylusState& state);
};
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 41e018d..3091714 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -117,9 +117,8 @@
continue; // axis must be claimed by a different device
}
- RawAbsoluteAxisInfo rawAxisInfo;
- getAbsoluteAxisInfo(abs, &rawAxisInfo);
- if (rawAxisInfo.valid) {
+ if (std::optional<RawAbsoluteAxisInfo> rawAxisInfo = getAbsoluteAxisInfo(abs);
+ rawAxisInfo) {
// Map axis.
AxisInfo axisInfo;
const bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo);
@@ -129,7 +128,7 @@
axisInfo.mode = AxisInfo::MODE_NORMAL;
axisInfo.axis = -1;
}
- mAxes.insert({abs, createAxis(axisInfo, rawAxisInfo, explicitlyMapped)});
+ mAxes.insert({abs, createAxis(axisInfo, rawAxisInfo.value(), explicitlyMapped)});
}
}
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 4a21e48..38dcd65 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -98,10 +98,10 @@
KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig,
uint32_t source)
- : InputMapper(deviceContext, readerConfig), mSource(source) {}
+ : InputMapper(deviceContext, readerConfig), mMapperSource(source) {}
uint32_t KeyboardInputMapper::getSources() const {
- return mSource;
+ return mMapperSource;
}
ui::Rotation KeyboardInputMapper::getOrientation() {
@@ -351,8 +351,8 @@
policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
}
- out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, deviceId, mSource,
- getDisplayId(), policyFlags,
+ out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, deviceId,
+ getEventSource(), getDisplayId(), policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, flags,
keyCode, scanCode, keyMetaState, downTime));
return out;
@@ -478,12 +478,12 @@
std::list<NotifyArgs> out;
size_t n = mKeyDowns.size();
for (size_t i = 0; i < n; i++) {
- out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when,
- systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource,
- getDisplayId(), /*policyFlags=*/0, AKEY_EVENT_ACTION_UP,
- mKeyDowns[i].flags | AKEY_EVENT_FLAG_CANCELED,
- mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
- mKeyDowns[i].downTime));
+ out.emplace_back(
+ NotifyKeyArgs(getContext()->getNextId(), when, systemTime(SYSTEM_TIME_MONOTONIC),
+ getDeviceId(), getEventSource(), getDisplayId(), /*policyFlags=*/0,
+ AKEY_EVENT_ACTION_UP, mKeyDowns[i].flags | AKEY_EVENT_FLAG_CANCELED,
+ mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
+ mKeyDowns[i].downTime));
}
mKeyDowns.clear();
mMetaState = AMETA_NONE;
@@ -495,4 +495,14 @@
context.setLastKeyDownTimestamp(downTime);
}
+uint32_t KeyboardInputMapper::getEventSource() const {
+ // For all input events generated by this mapper, use the source that's shared across all
+ // KeyboardInputMappers for this device in case there are more than one.
+ static constexpr auto ALL_KEYBOARD_SOURCES =
+ AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD;
+ const auto deviceSources = getDeviceContext().getDeviceSources();
+ LOG_ALWAYS_FATAL_IF((deviceSources & mMapperSource) != mMapperSource);
+ return deviceSources & ALL_KEYBOARD_SOURCES;
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index c7df558..2df0b85 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -60,7 +60,10 @@
int32_t flags{};
};
- uint32_t mSource{};
+ // The keyboard source for this mapper. Events generated should use the source shared
+ // by all KeyboardInputMappers for this input device.
+ uint32_t mMapperSource{};
+
std::optional<KeyboardLayoutInfo> mKeyboardLayoutInfo;
std::vector<KeyDown> mKeyDowns{}; // keys that are down
@@ -106,6 +109,7 @@
std::optional<DisplayViewport> findViewport(const InputReaderConfiguration& readerConfig);
[[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when);
void onKeyDownProcessed(nsecs_t downTime);
+ uint32_t getEventSource() const;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 1986fe2..fd8224a 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -133,7 +133,7 @@
bool isHovering = mTouchButtonAccumulator.getToolType() != ToolType::MOUSE &&
(mTouchButtonAccumulator.isHovering() ||
- (mRawPointerAxes.pressure.valid && inSlot.getPressure() <= 0));
+ (mRawPointerAxes.pressure && inSlot.getPressure() <= 0));
outPointer.isHovering = isHovering;
// Assign pointer id using tracking id if available.
@@ -189,21 +189,27 @@
void MultiTouchInputMapper::configureRawPointerAxes() {
TouchInputMapper::configureRawPointerAxes();
- getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
- getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
- getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
- getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
- getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
- getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
- getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
- getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
- getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
- getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
- getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
+ // TODO(b/351870641): Investigate why we are sometime not getting valid axis infos for the x/y
+ // axes, even though those axes are required to be supported.
+ if (const auto xInfo = getAbsoluteAxisInfo(ABS_MT_POSITION_X); xInfo.has_value()) {
+ mRawPointerAxes.x = *xInfo;
+ }
+ if (const auto yInfo = getAbsoluteAxisInfo(ABS_MT_POSITION_Y); yInfo.has_value()) {
+ mRawPointerAxes.y = *yInfo;
+ }
+ mRawPointerAxes.touchMajor = getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR);
+ mRawPointerAxes.touchMinor = getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR);
+ mRawPointerAxes.toolMajor = getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR);
+ mRawPointerAxes.toolMinor = getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR);
+ mRawPointerAxes.orientation = getAbsoluteAxisInfo(ABS_MT_ORIENTATION);
+ mRawPointerAxes.pressure = getAbsoluteAxisInfo(ABS_MT_PRESSURE);
+ mRawPointerAxes.distance = getAbsoluteAxisInfo(ABS_MT_DISTANCE);
+ mRawPointerAxes.trackingId = getAbsoluteAxisInfo(ABS_MT_TRACKING_ID);
+ mRawPointerAxes.slot = getAbsoluteAxisInfo(ABS_MT_SLOT);
- if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
- mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
- size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
+ if (mRawPointerAxes.trackingId && mRawPointerAxes.slot && mRawPointerAxes.slot->minValue == 0 &&
+ mRawPointerAxes.slot->maxValue > 0) {
+ size_t slotCount = mRawPointerAxes.slot->maxValue + 1;
if (slotCount > MAX_SLOTS) {
ALOGW("MultiTouch Device %s reported %zu slots but the framework "
"only supports a maximum of %zu slots at this time.",
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index d7f2993..4233f78 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -133,9 +133,8 @@
.test(InputDeviceClass::SENSOR))) {
continue;
}
- RawAbsoluteAxisInfo rawAxisInfo;
- getAbsoluteAxisInfo(abs, &rawAxisInfo);
- if (rawAxisInfo.valid) {
+ if (std::optional<RawAbsoluteAxisInfo> rawAxisInfo = getAbsoluteAxisInfo(abs);
+ rawAxisInfo) {
AxisInfo axisInfo;
// Axis doesn't need to be mapped, as sensor mapper doesn't generate any motion
// input events
@@ -146,7 +145,7 @@
if (ret.ok()) {
InputDeviceSensorType sensorType = (*ret).first;
int32_t sensorDataIndex = (*ret).second;
- const Axis& axis = createAxis(axisInfo, rawAxisInfo);
+ const Axis& axis = createAxis(axisInfo, rawAxisInfo.value());
parseSensorConfiguration(sensorType, abs, sensorDataIndex, axis);
mAxes.insert({abs, axis});
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
index 140bb0c..cef1837 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
@@ -44,7 +44,7 @@
bool isHovering = mTouchButtonAccumulator.getToolType() != ToolType::MOUSE &&
(mTouchButtonAccumulator.isHovering() ||
- (mRawPointerAxes.pressure.valid &&
+ (mRawPointerAxes.pressure &&
mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0));
outState->rawPointerData.markIdBit(0, isHovering);
@@ -72,13 +72,19 @@
void SingleTouchInputMapper::configureRawPointerAxes() {
TouchInputMapper::configureRawPointerAxes();
- getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x);
- getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y);
- getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure);
- getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor);
- getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance);
- getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX);
- getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY);
+ // TODO(b/351870641): Investigate why we are sometime not getting valid axis infos for the x/y
+ // axes, even though those axes are required to be supported.
+ if (const auto xInfo = getAbsoluteAxisInfo(ABS_X); xInfo.has_value()) {
+ mRawPointerAxes.x = *xInfo;
+ }
+ if (const auto yInfo = getAbsoluteAxisInfo(ABS_Y); yInfo.has_value()) {
+ mRawPointerAxes.y = *yInfo;
+ }
+ mRawPointerAxes.pressure = getAbsoluteAxisInfo(ABS_PRESSURE);
+ mRawPointerAxes.toolMajor = getAbsoluteAxisInfo(ABS_TOOL_WIDTH);
+ mRawPointerAxes.distance = getAbsoluteAxisInfo(ABS_DISTANCE);
+ mRawPointerAxes.tiltX = getAbsoluteAxisInfo(ABS_TILT_X);
+ mRawPointerAxes.tiltY = getAbsoluteAxisInfo(ABS_TILT_Y);
}
bool SingleTouchInputMapper::hasStylus() const {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 2d89208..5c90cbb 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -149,7 +149,10 @@
// The SOURCE_BLUETOOTH_STYLUS is added to events dynamically if the current stream is modified
// by the external stylus state. That's why we don't add it directly to mSource during
// configuration.
- return mSource | (hasExternalStylus() ? AINPUT_SOURCE_BLUETOOTH_STYLUS : 0);
+ return mSource |
+ (mExternalStylusPresence == ExternalStylusPresence::TOUCH_FUSION
+ ? AINPUT_SOURCE_BLUETOOTH_STYLUS
+ : 0);
}
void TouchInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
@@ -270,8 +273,8 @@
}
dump += INDENT3 "Stylus Fusion:\n";
- dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n",
- toString(mExternalStylusConnected));
+ dump += StringPrintf(INDENT4 "ExternalStylusPresence: %s\n",
+ ftl::enum_string(mExternalStylusPresence).c_str());
dump += StringPrintf(INDENT4 "Fused External Stylus Pointer ID: %s\n",
toString(mFusedStylusPointerId).c_str());
dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n",
@@ -356,11 +359,19 @@
void TouchInputMapper::resolveExternalStylusPresence() {
std::vector<InputDeviceInfo> devices;
getContext()->getExternalStylusDevices(devices);
- mExternalStylusConnected = !devices.empty();
-
- if (!mExternalStylusConnected) {
+ if (devices.empty()) {
+ mExternalStylusPresence = ExternalStylusPresence::NONE;
resetExternalStylus();
+ return;
}
+ mExternalStylusPresence =
+ std::any_of(devices.begin(), devices.end(),
+ [](const auto& info) {
+ return info.getMotionRange(AMOTION_EVENT_AXIS_PRESSURE,
+ AINPUT_SOURCE_STYLUS) != nullptr;
+ })
+ ? ExternalStylusPresence::TOUCH_FUSION
+ : ExternalStylusPresence::BUTTON_FUSION;
}
TouchInputMapper::Parameters TouchInputMapper::computeParameters(
@@ -520,7 +531,7 @@
}
bool TouchInputMapper::hasExternalStylus() const {
- return mExternalStylusConnected;
+ return mExternalStylusPresence != ExternalStylusPresence::NONE;
}
/**
@@ -600,10 +611,10 @@
const float diagonalSize = hypotf(mDisplayBounds.width, mDisplayBounds.height);
// Size factors.
- if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) {
- mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue;
- } else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) {
- mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue;
+ if (mRawPointerAxes.touchMajor && mRawPointerAxes.touchMajor->maxValue != 0) {
+ mSizeScale = 1.0f / mRawPointerAxes.touchMajor->maxValue;
+ } else if (mRawPointerAxes.toolMajor && mRawPointerAxes.toolMajor->maxValue != 0) {
+ mSizeScale = 1.0f / mRawPointerAxes.toolMajor->maxValue;
} else {
mSizeScale = 0.0f;
}
@@ -618,18 +629,18 @@
.resolution = 0,
};
- if (mRawPointerAxes.touchMajor.valid) {
- mRawPointerAxes.touchMajor.resolution =
- clampResolution("touchMajor", mRawPointerAxes.touchMajor.resolution);
- mOrientedRanges.touchMajor->resolution = mRawPointerAxes.touchMajor.resolution;
+ if (mRawPointerAxes.touchMajor) {
+ mRawPointerAxes.touchMajor->resolution =
+ clampResolution("touchMajor", mRawPointerAxes.touchMajor->resolution);
+ mOrientedRanges.touchMajor->resolution = mRawPointerAxes.touchMajor->resolution;
}
mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
mOrientedRanges.touchMinor->axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
- if (mRawPointerAxes.touchMinor.valid) {
- mRawPointerAxes.touchMinor.resolution =
- clampResolution("touchMinor", mRawPointerAxes.touchMinor.resolution);
- mOrientedRanges.touchMinor->resolution = mRawPointerAxes.touchMinor.resolution;
+ if (mRawPointerAxes.touchMinor) {
+ mRawPointerAxes.touchMinor->resolution =
+ clampResolution("touchMinor", mRawPointerAxes.touchMinor->resolution);
+ mOrientedRanges.touchMinor->resolution = mRawPointerAxes.touchMinor->resolution;
}
mOrientedRanges.toolMajor = InputDeviceInfo::MotionRange{
@@ -641,18 +652,18 @@
.fuzz = 0,
.resolution = 0,
};
- if (mRawPointerAxes.toolMajor.valid) {
- mRawPointerAxes.toolMajor.resolution =
- clampResolution("toolMajor", mRawPointerAxes.toolMajor.resolution);
- mOrientedRanges.toolMajor->resolution = mRawPointerAxes.toolMajor.resolution;
+ if (mRawPointerAxes.toolMajor) {
+ mRawPointerAxes.toolMajor->resolution =
+ clampResolution("toolMajor", mRawPointerAxes.toolMajor->resolution);
+ mOrientedRanges.toolMajor->resolution = mRawPointerAxes.toolMajor->resolution;
}
mOrientedRanges.toolMinor = mOrientedRanges.toolMajor;
mOrientedRanges.toolMinor->axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
- if (mRawPointerAxes.toolMinor.valid) {
- mRawPointerAxes.toolMinor.resolution =
- clampResolution("toolMinor", mRawPointerAxes.toolMinor.resolution);
- mOrientedRanges.toolMinor->resolution = mRawPointerAxes.toolMinor.resolution;
+ if (mRawPointerAxes.toolMinor) {
+ mRawPointerAxes.toolMinor->resolution =
+ clampResolution("toolMinor", mRawPointerAxes.toolMinor->resolution);
+ mOrientedRanges.toolMinor->resolution = mRawPointerAxes.toolMinor->resolution;
}
if (mCalibration.sizeCalibration == Calibration::SizeCalibration::GEOMETRIC) {
@@ -704,9 +715,10 @@
mCalibration.pressureCalibration == Calibration::PressureCalibration::AMPLITUDE) {
if (mCalibration.pressureScale) {
mPressureScale = *mCalibration.pressureScale;
- pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue;
- } else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) {
- mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue;
+ pressureMax = mPressureScale *
+ (mRawPointerAxes.pressure ? mRawPointerAxes.pressure->maxValue : 0);
+ } else if (mRawPointerAxes.pressure && mRawPointerAxes.pressure->maxValue != 0) {
+ mPressureScale = 1.0f / mRawPointerAxes.pressure->maxValue;
}
}
@@ -725,18 +737,18 @@
mTiltXScale = 0;
mTiltYCenter = 0;
mTiltYScale = 0;
- mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid;
+ mHaveTilt = mRawPointerAxes.tiltX && mRawPointerAxes.tiltY;
if (mHaveTilt) {
- mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, mRawPointerAxes.tiltX.maxValue);
- mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, mRawPointerAxes.tiltY.maxValue);
+ mTiltXCenter = avg(mRawPointerAxes.tiltX->minValue, mRawPointerAxes.tiltX->maxValue);
+ mTiltYCenter = avg(mRawPointerAxes.tiltY->minValue, mRawPointerAxes.tiltY->maxValue);
mTiltXScale = M_PI / 180;
mTiltYScale = M_PI / 180;
- if (mRawPointerAxes.tiltX.resolution) {
- mTiltXScale = 1.0 / mRawPointerAxes.tiltX.resolution;
+ if (mRawPointerAxes.tiltX->resolution) {
+ mTiltXScale = 1.0 / mRawPointerAxes.tiltX->resolution;
}
- if (mRawPointerAxes.tiltY.resolution) {
- mTiltYScale = 1.0 / mRawPointerAxes.tiltY.resolution;
+ if (mRawPointerAxes.tiltY->resolution) {
+ mTiltYScale = 1.0 / mRawPointerAxes.tiltY->resolution;
}
mOrientedRanges.tilt = InputDeviceInfo::MotionRange{
@@ -766,11 +778,11 @@
} else if (mCalibration.orientationCalibration != Calibration::OrientationCalibration::NONE) {
if (mCalibration.orientationCalibration ==
Calibration::OrientationCalibration::INTERPOLATED) {
- if (mRawPointerAxes.orientation.valid) {
- if (mRawPointerAxes.orientation.maxValue > 0) {
- mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue;
- } else if (mRawPointerAxes.orientation.minValue < 0) {
- mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue;
+ if (mRawPointerAxes.orientation) {
+ if (mRawPointerAxes.orientation->maxValue > 0) {
+ mOrientationScale = M_PI_2 / mRawPointerAxes.orientation->maxValue;
+ } else if (mRawPointerAxes.orientation->minValue < 0) {
+ mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation->minValue;
} else {
mOrientationScale = 0;
}
@@ -795,14 +807,14 @@
mDistanceScale = mCalibration.distanceScale.value_or(1.0f);
}
+ const bool hasDistance = mRawPointerAxes.distance.has_value();
mOrientedRanges.distance = InputDeviceInfo::MotionRange{
-
.axis = AMOTION_EVENT_AXIS_DISTANCE,
.source = mSource,
- .min = mRawPointerAxes.distance.minValue * mDistanceScale,
- .max = mRawPointerAxes.distance.maxValue * mDistanceScale,
+ .min = hasDistance ? mRawPointerAxes.distance->minValue * mDistanceScale : 0,
+ .max = hasDistance ? mRawPointerAxes.distance->maxValue * mDistanceScale : 0,
.flat = 0,
- .fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale,
+ .fuzz = hasDistance ? mRawPointerAxes.distance->fuzz * mDistanceScale : 0,
.resolution = 0,
};
}
@@ -943,12 +955,7 @@
const std::optional<DisplayViewport> newViewportOpt = findViewport();
// Ensure the device is valid and can be used.
- if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
- ALOGW("Touch device '%s' did not report support for X or Y axis! "
- "The device will be inoperable.",
- getDeviceName().c_str());
- mDeviceMode = DeviceMode::DISABLED;
- } else if (!newViewportOpt) {
+ if (!newViewportOpt) {
ALOGI("Touch device '%s' could not query the properties of its associated "
"display. The device will be inoperable until the display size "
"becomes available.",
@@ -1237,7 +1244,7 @@
void TouchInputMapper::resolveCalibration() {
// Size
- if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) {
+ if (mRawPointerAxes.touchMajor || mRawPointerAxes.toolMajor) {
if (mCalibration.sizeCalibration == Calibration::SizeCalibration::DEFAULT) {
mCalibration.sizeCalibration = Calibration::SizeCalibration::GEOMETRIC;
}
@@ -1246,7 +1253,7 @@
}
// Pressure
- if (mRawPointerAxes.pressure.valid) {
+ if (mRawPointerAxes.pressure) {
if (mCalibration.pressureCalibration == Calibration::PressureCalibration::DEFAULT) {
mCalibration.pressureCalibration = Calibration::PressureCalibration::PHYSICAL;
}
@@ -1255,7 +1262,7 @@
}
// Orientation
- if (mRawPointerAxes.orientation.valid) {
+ if (mRawPointerAxes.orientation) {
if (mCalibration.orientationCalibration == Calibration::OrientationCalibration::DEFAULT) {
mCalibration.orientationCalibration = Calibration::OrientationCalibration::INTERPOLATED;
}
@@ -1264,7 +1271,7 @@
}
// Distance
- if (mRawPointerAxes.distance.valid) {
+ if (mRawPointerAxes.distance) {
if (mCalibration.distanceCalibration == Calibration::DistanceCalibration::DEFAULT) {
mCalibration.distanceCalibration = Calibration::DistanceCalibration::SCALED;
}
@@ -2251,25 +2258,25 @@
case Calibration::SizeCalibration::DIAMETER:
case Calibration::SizeCalibration::BOX:
case Calibration::SizeCalibration::AREA:
- if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) {
+ if (mRawPointerAxes.touchMajor && mRawPointerAxes.toolMajor) {
touchMajor = in.touchMajor;
- touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
+ touchMinor = mRawPointerAxes.touchMinor ? in.touchMinor : in.touchMajor;
toolMajor = in.toolMajor;
- toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;
- size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor)
- : in.touchMajor;
- } else if (mRawPointerAxes.touchMajor.valid) {
+ toolMinor = mRawPointerAxes.toolMinor ? in.toolMinor : in.toolMajor;
+ size = mRawPointerAxes.touchMinor ? avg(in.touchMajor, in.touchMinor)
+ : in.touchMajor;
+ } else if (mRawPointerAxes.touchMajor) {
toolMajor = touchMajor = in.touchMajor;
toolMinor = touchMinor =
- mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor;
- size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor)
- : in.touchMajor;
- } else if (mRawPointerAxes.toolMajor.valid) {
+ mRawPointerAxes.touchMinor ? in.touchMinor : in.touchMajor;
+ size = mRawPointerAxes.touchMinor ? avg(in.touchMajor, in.touchMinor)
+ : in.touchMajor;
+ } else if (mRawPointerAxes.toolMajor) {
touchMajor = toolMajor = in.toolMajor;
touchMinor = toolMinor =
- mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor;
- size = mRawPointerAxes.toolMinor.valid ? avg(in.toolMajor, in.toolMinor)
- : in.toolMajor;
+ mRawPointerAxes.toolMinor ? in.toolMinor : in.toolMajor;
+ size = mRawPointerAxes.toolMinor ? avg(in.toolMajor, in.toolMinor)
+ : in.toolMajor;
} else {
ALOG_ASSERT(false,
"No touch or tool axes. "
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index a9a0190..ef0e02f 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -61,17 +61,17 @@
struct RawPointerAxes {
RawAbsoluteAxisInfo x{};
RawAbsoluteAxisInfo y{};
- RawAbsoluteAxisInfo pressure{};
- RawAbsoluteAxisInfo touchMajor{};
- RawAbsoluteAxisInfo touchMinor{};
- RawAbsoluteAxisInfo toolMajor{};
- RawAbsoluteAxisInfo toolMinor{};
- RawAbsoluteAxisInfo orientation{};
- RawAbsoluteAxisInfo distance{};
- RawAbsoluteAxisInfo tiltX{};
- RawAbsoluteAxisInfo tiltY{};
- RawAbsoluteAxisInfo trackingId{};
- RawAbsoluteAxisInfo slot{};
+ std::optional<RawAbsoluteAxisInfo> pressure{};
+ std::optional<RawAbsoluteAxisInfo> touchMajor{};
+ std::optional<RawAbsoluteAxisInfo> touchMinor{};
+ std::optional<RawAbsoluteAxisInfo> toolMajor{};
+ std::optional<RawAbsoluteAxisInfo> toolMinor{};
+ std::optional<RawAbsoluteAxisInfo> orientation{};
+ std::optional<RawAbsoluteAxisInfo> distance{};
+ std::optional<RawAbsoluteAxisInfo> tiltX{};
+ std::optional<RawAbsoluteAxisInfo> tiltY{};
+ std::optional<RawAbsoluteAxisInfo> trackingId{};
+ std::optional<RawAbsoluteAxisInfo> slot{};
inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; }
inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; }
@@ -365,6 +365,16 @@
RawState mLastRawState;
CookedState mLastCookedState;
+ enum class ExternalStylusPresence {
+ // No external stylus connected.
+ NONE,
+ // An external stylus that can report touch/pressure that can be fused with the touchscreen.
+ TOUCH_FUSION,
+ // An external stylus that can only report buttons.
+ BUTTON_FUSION,
+ ftl_last = BUTTON_FUSION,
+ };
+ ExternalStylusPresence mExternalStylusPresence{ExternalStylusPresence::NONE};
// State provided by an external stylus
StylusState mExternalStylusState;
// If an external stylus is capable of reporting pointer-specific data like pressure, we will
@@ -460,8 +470,6 @@
float mTiltYCenter;
float mTiltYScale;
- bool mExternalStylusConnected;
-
// Oriented motion ranges for input device info.
struct OrientedRanges {
InputDeviceInfo::MotionRange x;
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 128f515..dbc2872 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -36,6 +36,7 @@
#include <log/log_main.h>
#include <stats_pull_atom_callback.h>
#include <statslog.h>
+#include "InputReaderBase.h"
#include "TouchCursorInputMapperCommon.h"
#include "TouchpadInputMapper.h"
#include "gestures/HardwareProperties.h"
@@ -240,17 +241,19 @@
mGestureConverter(*getContext(), deviceContext, getDeviceId()),
mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()),
mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())) {
- RawAbsoluteAxisInfo slotAxisInfo;
- deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
- if (!slotAxisInfo.valid || slotAxisInfo.maxValue < 0) {
+ if (std::optional<RawAbsoluteAxisInfo> slotAxis =
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT);
+ slotAxis && slotAxis->maxValue >= 0) {
+ mMotionAccumulator.configure(deviceContext, slotAxis->maxValue + 1, true);
+ } else {
LOG(WARNING) << "Touchpad " << deviceContext.getName()
<< " doesn't have a valid ABS_MT_SLOT axis, and probably won't work properly.";
- slotAxisInfo.maxValue = 0;
+ mMotionAccumulator.configure(deviceContext, 1, true);
}
- mMotionAccumulator.configure(deviceContext, slotAxisInfo.maxValue + 1, true);
mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
- mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
+ mHardwareProperties = createHardwareProperties(deviceContext);
+ mGestureInterpreter->SetHardwareProperties(mHardwareProperties);
// Even though we don't explicitly delete copy/move semantics, it's safe to
// give away pointers to TouchpadInputMapper and its members here because
// 1) mGestureInterpreter's lifecycle is determined by TouchpadInputMapper, and
@@ -371,6 +374,7 @@
.setBoolValues({config.touchpadTapDraggingEnabled});
mPropertyProvider.getProperty("Button Right Click Zone Enable")
.setBoolValues({config.touchpadRightClickZoneEnabled});
+ mTouchpadHardwareStateNotificationsEnabled = config.shouldNotifyTouchpadHardwareState;
}
std::list<NotifyArgs> out;
if ((!changes.any() && config.pointerCaptureRequest.isEnable()) ||
@@ -420,6 +424,9 @@
}
std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent);
if (state) {
+ if (mTouchpadHardwareStateNotificationsEnabled) {
+ getPolicy()->notifyTouchpadHardwareState(*state, rawEvent.deviceId);
+ }
updatePalmDetectionMetrics();
return sendHardwareState(rawEvent.when, rawEvent.readTime, *state);
} else {
@@ -492,4 +499,8 @@
return mDisplayId;
}
+std::optional<HardwareProperties> TouchpadInputMapper::getTouchpadHardwareProperties() {
+ return mHardwareProperties;
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 8baa63e..a2c4be9 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -68,6 +68,8 @@
std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override;
+ std::optional<HardwareProperties> getTouchpadHardwareProperties() override;
+
private:
void resetGestureInterpreter(nsecs_t when);
explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
@@ -92,6 +94,7 @@
HardwareStateConverter mStateConverter;
GestureConverter mGestureConverter;
CapturedTouchpadEventConverter mCapturedEventConverter;
+ HardwareProperties mHardwareProperties;
bool mPointerCaptured = false;
bool mResettingInterpreter = false;
@@ -112,6 +115,10 @@
std::optional<ui::LogicalDisplayId> mDisplayId;
nsecs_t mGestureStartTime{0};
+
+ // True if hardware state update notifications is available for usage based on its feature flag
+ // and settings value.
+ bool mTouchpadHardwareStateNotificationsEnabled = false;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index e8e7376..9924d0d 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -66,10 +66,11 @@
const InputDeviceContext& deviceContext, int32_t deviceId)
: mDeviceId(deviceId),
mReaderContext(readerContext),
- mEnableFlingStop(input_flags::enable_touchpad_fling_stop()) {
- deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo);
- deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo);
-}
+ mEnableFlingStop(input_flags::enable_touchpad_fling_stop()),
+ // We can safely assume that ABS_MT_POSITION_X and _Y axes will be available, as EventHub
+ // won't classify a device as a touchpad if they're not present.
+ mXAxisInfo(deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X).value()),
+ mYAxisInfo(deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y).value()) {}
std::string GestureConverter::dump() const {
std::stringstream out;
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp b/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp
index 04655dc..d8a1f50 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareProperties.cpp
@@ -16,6 +16,8 @@
#include "HardwareProperties.h"
+#include <optional>
+
namespace android {
namespace {
@@ -33,26 +35,34 @@
HardwareProperties createHardwareProperties(const InputDeviceContext& context) {
HardwareProperties props;
- RawAbsoluteAxisInfo absMtPositionX;
- context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX);
+ // We can safely assume that ABS_MT_POSITION_X and _Y axes will be available, as EventHub won't
+ // classify a device as a touchpad if they're not present.
+ RawAbsoluteAxisInfo absMtPositionX = context.getAbsoluteAxisInfo(ABS_MT_POSITION_X).value();
props.left = absMtPositionX.minValue;
props.right = absMtPositionX.maxValue;
props.res_x = absMtPositionX.resolution;
- RawAbsoluteAxisInfo absMtPositionY;
- context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY);
+ RawAbsoluteAxisInfo absMtPositionY = context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y).value();
props.top = absMtPositionY.minValue;
props.bottom = absMtPositionY.maxValue;
props.res_y = absMtPositionY.resolution;
- RawAbsoluteAxisInfo absMtOrientation;
- context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation);
- props.orientation_minimum = absMtOrientation.minValue;
- props.orientation_maximum = absMtOrientation.maxValue;
+ if (std::optional<RawAbsoluteAxisInfo> absMtOrientation =
+ context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION);
+ absMtOrientation) {
+ props.orientation_minimum = absMtOrientation->minValue;
+ props.orientation_maximum = absMtOrientation->maxValue;
+ } else {
+ props.orientation_minimum = 0;
+ props.orientation_maximum = 0;
+ }
- RawAbsoluteAxisInfo absMtSlot;
- context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot);
- props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1;
+ if (std::optional<RawAbsoluteAxisInfo> absMtSlot = context.getAbsoluteAxisInfo(ABS_MT_SLOT);
+ absMtSlot) {
+ props.max_finger_cnt = absMtSlot->maxValue - absMtSlot->minValue + 1;
+ } else {
+ props.max_finger_cnt = 1;
+ }
props.max_touch_cnt = getMaxTouchCount(context);
// T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5
@@ -71,9 +81,7 @@
// are haptic.
props.is_haptic_pad = false;
- RawAbsoluteAxisInfo absMtPressure;
- context.getAbsoluteAxisInfo(ABS_MT_PRESSURE, &absMtPressure);
- props.reports_pressure = absMtPressure.valid;
+ props.reports_pressure = context.hasAbsoluteAxis(ABS_MT_PRESSURE);
return props;
}
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
index 07e62c6..148ca5a 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
@@ -26,18 +26,13 @@
#include "accumulator/CursorButtonAccumulator.h"
#include "accumulator/MultiTouchMotionAccumulator.h"
#include "accumulator/TouchButtonAccumulator.h"
+#include "include/TouchpadHardwareState.h"
+#include "TouchpadHardwareState.h"
#include "include/gestures.h"
namespace android {
-// A HardwareState struct, but bundled with a vector to contain its FingerStates, so you don't have
-// to worry about where that memory is allocated.
-struct SelfContainedHardwareState {
- HardwareState state;
- std::vector<FingerState> fingers;
-};
-
// Converts RawEvents into the HardwareState structs used by the gestures library.
class HardwareStateConverter {
public:
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index ab50646..95283ba 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -70,6 +70,7 @@
"InputTraceSession.cpp",
"InputTracingTest.cpp",
"InstrumentedInputReader.cpp",
+ "JoystickInputMapper_test.cpp",
"LatencyTracker_test.cpp",
"MultiTouchMotionAccumulator_test.cpp",
"NotifyArgs_test.cpp",
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index 727237f..b27d02d 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -17,6 +17,7 @@
#include "CursorInputMapper.h"
#include <list>
+#include <optional>
#include <string>
#include <tuple>
#include <variant>
@@ -93,38 +94,6 @@
return v;
}
-/**
- * A fake InputDeviceContext that allows the associated viewport to be specified for the mapper.
- *
- * This is currently necessary because InputMapperUnitTest doesn't register the mappers it creates
- * with the InputDevice object, meaning that InputDevice::isIgnored becomes true, and the input
- * device doesn't set its associated viewport when it's configured.
- *
- * TODO(b/319217713): work out a way to avoid this fake.
- */
-class ViewportFakingInputDeviceContext : public InputDeviceContext {
-public:
- ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId,
- std::optional<DisplayViewport> viewport)
- : InputDeviceContext(device, eventHubId), mAssociatedViewport(viewport) {}
-
- ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId,
- ui::Rotation orientation)
- : ViewportFakingInputDeviceContext(device, eventHubId,
- createPrimaryViewport(orientation)) {}
-
- std::optional<DisplayViewport> getAssociatedViewport() const override {
- return mAssociatedViewport;
- }
-
- void setViewport(const std::optional<DisplayViewport>& viewport) {
- mAssociatedViewport = viewport;
- }
-
-private:
- std::optional<DisplayViewport> mAssociatedViewport;
-};
-
} // namespace
namespace input_flags = com::android::input::flags;
@@ -163,7 +132,6 @@
}
void createMapper() {
- createDevice();
mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
}
@@ -542,9 +510,9 @@
// need to be rotated.
mPropertyMap.addProperty("cursor.mode", "navigation");
mPropertyMap.addProperty("cursor.orientationAware", "1");
- createDevice();
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation90);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation90)));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1));
@@ -560,9 +528,9 @@
// Since InputReader works in the un-rotated coordinate space, only devices that are not
// orientation-aware are affected by display rotation.
mPropertyMap.addProperty("cursor.mode", "navigation");
- createDevice();
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation0);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation0)));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1));
@@ -573,7 +541,8 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, -1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, 1));
- deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation90));
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation90)));
std::list<NotifyArgs> args =
mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
@@ -586,7 +555,8 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 0, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, -1));
- deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation180));
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation180)));
args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, -1));
@@ -598,7 +568,8 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, 1, -1));
- deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation270));
+ EXPECT_CALL((*mDevice), getAssociatedViewport)
+ .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation270)));
args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 1, 0));
@@ -652,9 +623,8 @@
mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
// Set up the secondary display as the display on which the pointer should be shown.
// The InputDevice is not associated with any display.
- createDevice();
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(secondaryViewport));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
std::list<NotifyArgs> args;
// Ensure input events are generated for the secondary display.
@@ -673,10 +643,9 @@
DisplayViewport secondaryViewport = createSecondaryViewport();
mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
// Set up the primary display as the display on which the pointer should be shown.
- createDevice();
// Associate the InputDevice with the secondary display.
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(secondaryViewport));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
// With PointerChoreographer enabled, there could be a PointerController for the associated
// display even if it is different from the pointer display. So the mapper should generate an
@@ -1032,9 +1001,8 @@
mPropertyMap.addProperty("cursor.mode", "pointer");
DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0);
mReaderConfiguration.setDisplayViewports({primaryViewport});
- createDevice();
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, primaryViewport);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(primaryViewport));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
std::list<NotifyArgs> args;
@@ -1070,12 +1038,10 @@
mReaderConfiguration.setDisplayViewports({primaryViewport});
// Disable acceleration for the display.
mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID);
- createDevice();
// Don't associate the device with the display yet.
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID,
- /*viewport=*/std::nullopt);
- mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(std::nullopt));
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
std::list<NotifyArgs> args;
@@ -1089,7 +1055,7 @@
ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y), 20.f);
// Now associate the device with the display, and verify that acceleration is disabled.
- deviceContext.setViewport(primaryViewport);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(primaryViewport));
args += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
args.clear();
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index 99db999..31fbf20 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -101,7 +101,6 @@
Device* device = getDevice(deviceId);
RawAbsoluteAxisInfo info;
- info.valid = true;
info.minValue = minValue;
info.maxValue = maxValue;
info.flat = flat;
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 6099c91..d77d539 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -65,6 +65,17 @@
ASSERT_FALSE(mDeviceIdOfNotifiedStylusGesture);
}
+void FakeInputReaderPolicy::assertTouchpadHardwareStateNotified() {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ const bool success =
+ mTouchpadHardwareStateNotified.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+ return mTouchpadHardwareState.has_value();
+ });
+ ASSERT_TRUE(success) << "Timed out waiting for hardware state to be notified";
+}
+
void FakeInputReaderPolicy::clearViewports() {
mViewports.clear();
mConfig.setDisplayViewports(mViewports);
@@ -234,6 +245,13 @@
mDevicesChangedCondition.notify_all();
}
+void FakeInputReaderPolicy::notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
+ int32_t deviceId) {
+ std::scoped_lock lock(mLock);
+ mTouchpadHardwareState = schs;
+ mTouchpadHardwareStateNotified.notify_all();
+}
+
std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay(
const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) {
return nullptr;
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 94f1311..e5ba620 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -42,6 +42,7 @@
void assertInputDevicesNotChanged();
void assertStylusGestureNotified(int32_t deviceId);
void assertStylusGestureNotNotified();
+ void assertTouchpadHardwareStateNotified();
virtual void clearViewports();
std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const;
@@ -82,6 +83,8 @@
private:
void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
+ void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
+ int32_t deviceId) override;
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override;
std::string getDeviceAlias(const InputDeviceIdentifier&) override;
@@ -101,6 +104,9 @@
std::condition_variable mStylusGestureNotifiedCondition;
std::optional<DeviceId> mDeviceIdOfNotifiedStylusGesture GUARDED_BY(mLock){};
+ std::condition_variable mTouchpadHardwareStateNotified;
+ std::optional<SelfContainedHardwareState> mTouchpadHardwareState GUARDED_BY(mLock){};
+
uint32_t mNextPointerCaptureSequenceNumber{0};
};
diff --git a/services/inputflinger/tests/HardwareProperties_test.cpp b/services/inputflinger/tests/HardwareProperties_test.cpp
index 643fab6..e87f822 100644
--- a/services/inputflinger/tests/HardwareProperties_test.cpp
+++ b/services/inputflinger/tests/HardwareProperties_test.cpp
@@ -50,7 +50,6 @@
void setupValidAxis(int axis, int32_t min, int32_t max, int32_t resolution) {
EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis))
.WillRepeatedly(Return(std::optional<RawAbsoluteAxisInfo>{{
- .valid = true,
.minValue = min,
.maxValue = max,
.flat = 0,
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index c70afd6..c2f174f 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -4168,6 +4168,7 @@
* the event routing because the first window prevents splitting.
*/
TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTimeForNewWindow) {
+ SCOPED_FLAG_OVERRIDE(split_all_touches, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window1 =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window1", DISPLAY_ID);
@@ -4225,6 +4226,7 @@
* (and the touch occurred outside of the bounds of window1).
*/
TEST_F(InputDispatcherTest, SplitTouchesDropsEventForNonSplittableSecondWindow) {
+ SCOPED_FLAG_OVERRIDE(split_all_touches, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window1 =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window1", DISPLAY_ID);
@@ -4600,6 +4602,7 @@
* This test attempts to reproduce a crash in the dispatcher.
*/
TEST_P(SpyThatPreventsSplittingWithApplicationFixture, SpyThatPreventsSplittingWithApplication) {
+ SCOPED_FLAG_OVERRIDE(split_all_touches, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
@@ -5034,6 +5037,54 @@
}
/**
+ * Invalid events injected by input filter are rejected.
+ */
+TEST_F(InputDispatcherTest, InvalidA11yEventsGetRejected) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ // a11y sets 'POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY' policy flag during injection, so define
+ // a custom injection function here for convenience.
+ auto injectFromAccessibility = [&](int32_t action, float x, float y) {
+ MotionEvent event = MotionEventBuilder(action, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(x).y(y))
+ .addFlag(AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT)
+ .build();
+ return injectMotionEvent(*mDispatcher, event, 100ms,
+ InputEventInjectionSync::WAIT_FOR_RESULT, /*targetUid=*/{},
+ POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_FILTERED |
+ POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY);
+ };
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectFromAccessibility(ACTION_DOWN, /*x=*/300, /*y=*/400));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectFromAccessibility(ACTION_MOVE, /*x=*/310, /*y=*/420));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ window->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ // finger is still down, so a new DOWN event should be rejected!
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
+ injectFromAccessibility(ACTION_DOWN, /*x=*/340, /*y=*/410));
+
+ // if the gesture is correctly finished, new down event will succeed
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectFromAccessibility(ACTION_MOVE, /*x=*/320, /*y=*/430));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectFromAccessibility(ACTION_UP, /*x=*/320, /*y=*/430));
+ window->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ window->consumeMotionEvent(WithMotionAction(ACTION_UP));
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectFromAccessibility(ACTION_DOWN, /*x=*/350, /*y=*/460));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+}
+
+/**
* If mouse is hovering when the touch goes down, the hovering should be stopped via HOVER_EXIT.
*/
TEST_F(InputDispatcherTest, TouchDownAfterMouseHover_legacy) {
@@ -5583,6 +5634,7 @@
}
TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) {
+ SCOPED_FLAG_OVERRIDE(split_all_touches, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
@@ -5628,6 +5680,7 @@
* "incomplete" gestures.
*/
TEST_F(InputDispatcherTest, SplittableAndNonSplittableWindows) {
+ SCOPED_FLAG_OVERRIDE(split_all_touches, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left splittable Window",
@@ -5658,6 +5711,273 @@
}
/**
+ * Three windows:
+ * 1) A window on the left, with flag dup_to_wallpaper
+ * 2) A window on the right, with flag slippery
+ * 3) A wallpaper window under the left window
+ * When touch slips from right window to left, the wallpaper should receive a similar slippery
+ * enter event. Later on, when another device becomes active, the wallpaper should receive
+ * consistent streams from the new device, and also from the old device.
+ * This test attempts to reproduce a crash in the dispatcher where the wallpaper target's downTime
+ * was not getting set during slippery entrance.
+ */
+TEST_F(InputDispatcherTest, WallpaperWindowWhenSlipperyAndMultiWindowMultiTouch) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application1 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application2 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application3 = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> wallpaper =
+ sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper",
+ ui::LogicalDisplayId::DEFAULT);
+ wallpaper->setIsWallpaper(true);
+ wallpaper->setPreventSplitting(true);
+ wallpaper->setTouchable(false);
+
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWindow->setTouchableRegion(Region{{0, 0, 100, 100}});
+ leftWindow->setDupTouchToWallpaper(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application3, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
+ rightWindow->setTouchableRegion(Region{{100, 0, 200, 100}});
+ rightWindow->setSlippery(true);
+ rightWindow->setWatchOutsideTouch(true);
+ rightWindow->setTrustedOverlay(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*rightWindow->getInfo(), *leftWindow->getInfo(), *wallpaper->getInfo()}, {}, 0, 0});
+
+ const DeviceId deviceA = 3;
+ const DeviceId deviceB = 9;
+
+ // First finger from device A into right window
+ NotifyMotionArgs deviceADownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceA)
+ .build();
+
+ mDispatcher->notifyMotion(deviceADownArgs);
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Move the finger of device A from right window into left window. It should slip.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(80).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ wallpaper->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Finger from device B down into left window
+ NotifyMotionArgs deviceBDownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40))
+ .deviceId(deviceB)
+ .build();
+ mDispatcher->notifyMotion(deviceBDownArgs);
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_OUTSIDE)));
+
+ // Move finger from device B, still keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+
+ // Lift the finger from device B
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+
+ // Move the finger of device A, keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE)));
+
+ // Second finger down from device A, into the right window. It should be split into:
+ // MOVE for the left window (due to existing implementation) + a DOWN into the right window
+ // Wallpaper will not receive this new pointer, and it will only get the MOVE event.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ auto firstFingerMoveFromDeviceA = AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE),
+ WithPointerCount(1), WithPointerId(0, 0));
+ leftWindow->consumeMotionEvent(firstFingerMoveFromDeviceA);
+ wallpaper->consumeMotionEvent(firstFingerMoveFromDeviceA);
+ rightWindow->consumeMotionEvent(
+ AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_DOWN), WithPointerId(0, 1)));
+
+ // Lift up the second finger.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+ leftWindow->consumeMotionEvent(firstFingerMoveFromDeviceA);
+ wallpaper->consumeMotionEvent(firstFingerMoveFromDeviceA);
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+ rightWindow->assertNoEvents();
+}
+
+/**
+ * Same test as above, but with enable_multi_device_same_window_stream flag set to false.
+ */
+TEST_F(InputDispatcherTest, WallpaperWindowWhenSlipperyAndMultiWindowMultiTouch_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
+ std::shared_ptr<FakeApplicationHandle> application1 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application2 = std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application3 = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> wallpaper =
+ sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper",
+ ui::LogicalDisplayId::DEFAULT);
+ wallpaper->setIsWallpaper(true);
+ wallpaper->setPreventSplitting(true);
+ wallpaper->setTouchable(false);
+
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWindow->setTouchableRegion(Region{{0, 0, 100, 100}});
+ leftWindow->setDupTouchToWallpaper(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application3, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
+ rightWindow->setTouchableRegion(Region{{100, 0, 200, 100}});
+ rightWindow->setSlippery(true);
+ rightWindow->setWatchOutsideTouch(true);
+ rightWindow->setTrustedOverlay(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*rightWindow->getInfo(), *leftWindow->getInfo(), *wallpaper->getInfo()}, {}, 0, 0});
+
+ const DeviceId deviceA = 3;
+ const DeviceId deviceB = 9;
+
+ // First finger from device A into right window
+ NotifyMotionArgs deviceADownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceA)
+ .build();
+
+ mDispatcher->notifyMotion(deviceADownArgs);
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Move the finger of device A from right window into left window. It should slip.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(80).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ leftWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ wallpaper->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Finger from device B down into left window
+ NotifyMotionArgs deviceBDownArgs =
+ MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40))
+ .deviceId(deviceB)
+ .build();
+ mDispatcher->notifyMotion(deviceBDownArgs);
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_CANCEL)));
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_CANCEL)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN)));
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_OUTSIDE)));
+
+ // Move finger from device B, still keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE)));
+
+ // Lift the finger from device B
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50))
+ .deviceId(deviceB)
+ .downTime(deviceBDownArgs.downTime)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+ wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP)));
+
+ // Move the finger of device A, keeping it in the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ // This device was already canceled, so MOVE events will not be arriving to the windows from it.
+
+ // Second finger down from device A, into the right window. It should be split into:
+ // MOVE for the left window (due to existing implementation) + a DOWN into the right window
+ // Wallpaper will not receive this new pointer, and it will only get the MOVE event.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ rightWindow->consumeMotionEvent(
+ AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_DOWN), WithPointerId(0, 1)));
+
+ // Lift up the second finger.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+
+ rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50))
+ .deviceId(deviceA)
+ .downTime(deviceADownArgs.downTime)
+ .build());
+ rightWindow->assertNoEvents();
+}
+
+/**
* Two windows: left and right. The left window has PREVENT_SPLITTING input config. Device A sends a
* down event to the right window. Device B sends a down event to the left window, and then a
* POINTER_DOWN event to the right window. However, since the left window prevents splitting, the
@@ -5665,6 +5985,7 @@
* This test attempts to reproduce a crash.
*/
TEST_F(InputDispatcherTest, MultiDeviceTwoWindowsPreventSplitting) {
+ SCOPED_FLAG_OVERRIDE(split_all_touches, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left window (prevent splitting)",
@@ -8411,6 +8732,7 @@
* the previous window should receive this event and not be dropped.
*/
TEST_F(InputDispatcherMultiDeviceTest, SingleDevicePointerDownEventRetentionWithoutWindowTarget) {
+ SCOPED_FLAG_OVERRIDE(split_all_touches, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
ui::LogicalDisplayId::DEFAULT);
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index 7e96d5f..7dff144 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -26,7 +26,9 @@
namespace android {
using testing::_;
+using testing::NiceMock;
using testing::Return;
+using testing::ReturnRef;
void InputMapperUnitTest::SetUpWithBus(int bus) {
mFakePolicy = sp<FakeInputReaderPolicy>::make();
@@ -43,23 +45,17 @@
EXPECT_CALL(mMockEventHub, getConfiguration(EVENTHUB_ID)).WillRepeatedly([&](int32_t) {
return mPropertyMap;
});
-}
-void InputMapperUnitTest::createDevice() {
- mDevice = std::make_unique<InputDevice>(&mMockInputReaderContext, DEVICE_ID,
- /*generation=*/2, mIdentifier);
- mDevice->addEmptyEventHubDevice(EVENTHUB_ID);
+ mDevice = std::make_unique<NiceMock<MockInputDevice>>(&mMockInputReaderContext, DEVICE_ID,
+ /*generation=*/2, mIdentifier);
+ ON_CALL((*mDevice), getConfiguration).WillByDefault(ReturnRef(mPropertyMap));
mDeviceContext = std::make_unique<InputDeviceContext>(*mDevice, EVENTHUB_ID);
- std::list<NotifyArgs> args =
- mDevice->configure(systemTime(), mReaderConfiguration, /*changes=*/{});
- ASSERT_THAT(args, testing::ElementsAre(testing::VariantWith<NotifyDeviceResetArgs>(_)));
}
void InputMapperUnitTest::setupAxis(int axis, bool valid, int32_t min, int32_t max,
int32_t resolution) {
EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis))
.WillRepeatedly(Return(valid ? std::optional<RawAbsoluteAxisInfo>{{
- .valid = true,
.minValue = min,
.maxValue = max,
.flat = 0,
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index 88057dc..fc27e4f 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -43,13 +43,6 @@
virtual void SetUp() override { SetUpWithBus(0); }
virtual void SetUpWithBus(int bus);
- /**
- * Initializes mDevice and mDeviceContext. When this happens, mDevice takes a copy of
- * mPropertyMap, so tests that need to set configuration properties should do so before calling
- * this. Others will most likely want to call it in their SetUp method.
- */
- void createDevice();
-
void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution);
void expectScanCodes(bool present, std::set<int> scanCodes);
@@ -67,7 +60,7 @@
MockEventHubInterface mMockEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
MockInputReaderContext mMockInputReaderContext;
- std::unique_ptr<InputDevice> mDevice;
+ std::unique_ptr<MockInputDevice> mDevice;
std::unique_ptr<InputDeviceContext> mDeviceContext;
InputReaderConfiguration mReaderConfiguration;
@@ -123,11 +116,12 @@
T& constructAndAddMapper(Args... args) {
// ensure a device entry exists for this eventHubId
mDevice->addEmptyEventHubDevice(EVENTHUB_ID);
- // configure the empty device
- configureDevice(/*changes=*/{});
- return mDevice->constructAndAddMapper<T>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
- args...);
+ auto& mapper =
+ mDevice->constructAndAddMapper<T>(EVENTHUB_ID,
+ mFakePolicy->getReaderConfiguration(), args...);
+ configureDevice(/*changes=*/{});
+ return mapper;
}
void setDisplayInfoAndReconfigure(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 267c400..4a9e893 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -24,7 +24,6 @@
#include <InputReader.h>
#include <InputReaderBase.h>
#include <InputReaderFactory.h>
-#include <JoystickInputMapper.h>
#include <KeyboardInputMapper.h>
#include <MultiTouchInputMapper.h>
#include <NotifyArgsBuilders.h>
@@ -4040,6 +4039,51 @@
ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, args.flags);
}
+/**
+ * When there is more than one KeyboardInputMapper for an InputDevice, each mapper should produce
+ * events that use the shared keyboard source across all mappers. This is to ensure that each
+ * input device generates key events in a consistent manner, regardless of which mapper produces
+ * the event.
+ */
+TEST_F(KeyboardInputMapperTest, UsesSharedKeyboardSource) {
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+
+ // Add a mapper with SOURCE_KEYBOARD
+ KeyboardInputMapper& keyboardMapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+
+ process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
+ process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
+
+ // Add a mapper with SOURCE_DPAD
+ KeyboardInputMapper& dpadMapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD);
+ for (auto* mapper : {&keyboardMapper, &dpadMapper}) {
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
+ }
+
+ // Add a mapper with SOURCE_GAMEPAD
+ KeyboardInputMapper& gamepadMapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_GAMEPAD);
+ for (auto* mapper : {&keyboardMapper, &dpadMapper, &gamepadMapper}) {
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
+ }
+}
+
// --- KeyboardInputMapperTest_ExternalAlphabeticDevice ---
class KeyboardInputMapperTest_ExternalAlphabeticDevice : public InputMapperTest {
@@ -6631,15 +6675,27 @@
class ExternalStylusFusionTest : public SingleTouchInputMapperTest {
public:
- SingleTouchInputMapper& initializeInputMapperWithExternalStylus() {
+ void SetUp() override {
+ SingleTouchInputMapperTest::SetUp();
+ mExternalStylusDeviceInfo = {};
+ mStylusState = {};
+ }
+
+ SingleTouchInputMapper& initializeInputMapperWithExternalStylus(bool supportsPressure = true) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(ui::ROTATION_0);
prepareButtons();
prepareAxes(POSITION);
auto& mapper = constructAndAddMapper<SingleTouchInputMapper>();
+ if (supportsPressure) {
+ mExternalStylusDeviceInfo.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE,
+ AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f);
+ mStylusState.pressure = 0.f;
+ }
+
mStylusState.when = ARBITRARY_TIME;
- mStylusState.pressure = 0.f;
mStylusState.toolType = ToolType::STYLUS;
mReader->getContext()->setExternalStylusDevices({mExternalStylusDeviceInfo});
configureDevice(InputReaderConfiguration::Change::EXTERNAL_STYLUS_PRESENCE);
@@ -6747,11 +6803,17 @@
InputDeviceInfo mExternalStylusDeviceInfo{};
};
-TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSource) {
+TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSourceWithPressure) {
SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
ASSERT_EQ(STYLUS_FUSION_SOURCE, mapper.getSources());
}
+TEST_F(ExternalStylusFusionTest, DoesNotUseBluetoothStylusSourceWithoutPressure) {
+ SingleTouchInputMapper& mapper =
+ initializeInputMapperWithExternalStylus(/*supportsPressure=*/false);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
+}
+
TEST_F(ExternalStylusFusionTest, UnsuccessfulFusion) {
SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus();
ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper));
@@ -10018,67 +10080,6 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}
-// --- JoystickInputMapperTest ---
-
-class JoystickInputMapperTest : public InputMapperTest {
-protected:
- static const int32_t RAW_X_MIN;
- static const int32_t RAW_X_MAX;
- static const int32_t RAW_Y_MIN;
- static const int32_t RAW_Y_MAX;
-
- void SetUp() override {
- InputMapperTest::SetUp(InputDeviceClass::JOYSTICK | InputDeviceClass::EXTERNAL);
- }
- void prepareAxes() {
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, RAW_X_MIN, RAW_X_MAX, 0, 0);
- mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0);
- }
-
- void processAxis(JoystickInputMapper& mapper, int32_t axis, int32_t value) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, axis, value);
- }
-
- void processSync(JoystickInputMapper& mapper) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- }
-
- void prepareVirtualDisplay(ui::Rotation orientation) {
- setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
- VIRTUAL_DISPLAY_HEIGHT, orientation, VIRTUAL_DISPLAY_UNIQUE_ID,
- NO_PORT, ViewportType::VIRTUAL);
- }
-};
-
-const int32_t JoystickInputMapperTest::RAW_X_MIN = -32767;
-const int32_t JoystickInputMapperTest::RAW_X_MAX = 32767;
-const int32_t JoystickInputMapperTest::RAW_Y_MIN = -32767;
-const int32_t JoystickInputMapperTest::RAW_Y_MAX = 32767;
-
-TEST_F(JoystickInputMapperTest, Configure_AssignsDisplayUniqueId) {
- prepareAxes();
- JoystickInputMapper& mapper = constructAndAddMapper<JoystickInputMapper>();
-
- mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, VIRTUAL_DISPLAY_UNIQUE_ID);
-
- prepareVirtualDisplay(ui::ROTATION_0);
-
- // Send an axis event
- processAxis(mapper, ABS_X, 100);
- processSync(mapper);
-
- NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(VIRTUAL_DISPLAY_ID, args.displayId);
-
- // Send another axis event
- processAxis(mapper, ABS_Y, 100);
- processSync(mapper);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(VIRTUAL_DISPLAY_ID, args.displayId);
-}
-
// --- PeripheralControllerTest ---
class PeripheralControllerTest : public testing::Test {
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index 48e0b4f..5a3d79d 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -26,6 +26,7 @@
#include <vector>
#include <EventHub.h>
+#include <InputDevice.h>
#include <InputReaderBase.h>
#include <InputReaderContext.h>
#include <NotifyArgs.h>
@@ -59,7 +60,7 @@
MOCK_METHOD(void, requestTimeoutAtTime, (nsecs_t when), (override));
int32_t bumpGeneration() override { return ++mGeneration; }
- MOCK_METHOD(void, getExternalStylusDevices, (std::vector<InputDeviceInfo> & outDevices),
+ MOCK_METHOD(void, getExternalStylusDevices, (std::vector<InputDeviceInfo>& outDevices),
(override));
MOCK_METHOD(std::list<NotifyArgs>, dispatchExternalStylusState, (const StylusState& outState),
(override));
@@ -172,7 +173,7 @@
MOCK_METHOD(void, requestReopenDevices, (), (override));
MOCK_METHOD(void, wake, (), (override));
- MOCK_METHOD(void, dump, (std::string & dump), (const, override));
+ MOCK_METHOD(void, dump, (std::string& dump), (const, override));
MOCK_METHOD(void, monitor, (), (const, override));
MOCK_METHOD(bool, isDeviceEnabled, (int32_t deviceId), (const, override));
MOCK_METHOD(status_t, enableDevice, (int32_t deviceId), (override));
@@ -190,4 +191,76 @@
MOCK_METHOD(void, notifyMouseCursorFadedOnTyping, (), (override));
};
+class MockInputDevice : public InputDevice {
+public:
+ MockInputDevice(InputReaderContext* context, int32_t id, int32_t generation,
+ const InputDeviceIdentifier& identifier)
+ : InputDevice(context, id, generation, identifier) {}
+
+ MOCK_METHOD(uint32_t, getSources, (), (const, override));
+ MOCK_METHOD(std::optional<DisplayViewport>, getAssociatedViewport, (), (const));
+ MOCK_METHOD(bool, isEnabled, (), ());
+
+ MOCK_METHOD(void, dump, (std::string& dump, const std::string& eventHubDevStr), ());
+ MOCK_METHOD(void, addEmptyEventHubDevice, (int32_t eventHubId), ());
+ MOCK_METHOD(std::list<NotifyArgs>, addEventHubDevice,
+ (nsecs_t when, int32_t eventHubId, const InputReaderConfiguration& readerConfig),
+ ());
+ MOCK_METHOD(void, removeEventHubDevice, (int32_t eventHubId), ());
+ MOCK_METHOD(std::list<NotifyArgs>, configure,
+ (nsecs_t when, const InputReaderConfiguration& readerConfig,
+ ConfigurationChanges changes),
+ ());
+ MOCK_METHOD(std::list<NotifyArgs>, reset, (nsecs_t when), ());
+ MOCK_METHOD(std::list<NotifyArgs>, process, (const RawEvent* rawEvents, size_t count), ());
+ MOCK_METHOD(std::list<NotifyArgs>, timeoutExpired, (nsecs_t when), ());
+ MOCK_METHOD(std::list<NotifyArgs>, updateExternalStylusState, (const StylusState& state), ());
+
+ MOCK_METHOD(InputDeviceInfo, getDeviceInfo, (), ());
+ MOCK_METHOD(int32_t, getKeyCodeState, (uint32_t sourceMask, int32_t keyCode), ());
+ MOCK_METHOD(int32_t, getScanCodeState, (uint32_t sourceMask, int32_t scanCode), ());
+ MOCK_METHOD(int32_t, getSwitchState, (uint32_t sourceMask, int32_t switchCode), ());
+ MOCK_METHOD(int32_t, getKeyCodeForKeyLocation, (int32_t locationKeyCode), (const));
+ MOCK_METHOD(bool, markSupportedKeyCodes,
+ (uint32_t sourceMask, const std::vector<int32_t>& keyCodes, uint8_t* outFlags), ());
+ MOCK_METHOD(std::list<NotifyArgs>, vibrate,
+ (const VibrationSequence& sequence, ssize_t repeat, int32_t token), ());
+ MOCK_METHOD(std::list<NotifyArgs>, cancelVibrate, (int32_t token), ());
+ MOCK_METHOD(bool, isVibrating, (), ());
+ MOCK_METHOD(std::vector<int32_t>, getVibratorIds, (), ());
+ MOCK_METHOD(std::list<NotifyArgs>, cancelTouch, (nsecs_t when, nsecs_t readTime), ());
+ MOCK_METHOD(bool, enableSensor,
+ (InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
+ std::chrono::microseconds maxBatchReportLatency),
+ ());
+
+ MOCK_METHOD(void, disableSensor, (InputDeviceSensorType sensorType), ());
+ MOCK_METHOD(void, flushSensor, (InputDeviceSensorType sensorType), ());
+
+ MOCK_METHOD(std::optional<int32_t>, getBatteryEventHubId, (), (const));
+
+ MOCK_METHOD(bool, setLightColor, (int32_t lightId, int32_t color), ());
+ MOCK_METHOD(bool, setLightPlayerId, (int32_t lightId, int32_t playerId), ());
+ MOCK_METHOD(std::optional<int32_t>, getLightColor, (int32_t lightId), ());
+ MOCK_METHOD(std::optional<int32_t>, getLightPlayerId, (int32_t lightId), ());
+
+ MOCK_METHOD(int32_t, getMetaState, (), ());
+ MOCK_METHOD(void, updateMetaState, (int32_t keyCode), ());
+
+ MOCK_METHOD(void, addKeyRemapping, (int32_t fromKeyCode, int32_t toKeyCode), ());
+
+ MOCK_METHOD(void, setKeyboardType, (KeyboardType keyboardType), ());
+
+ MOCK_METHOD(void, bumpGeneration, (), ());
+
+ MOCK_METHOD(const PropertyMap&, getConfiguration, (), (const, override));
+
+ MOCK_METHOD(NotifyDeviceResetArgs, notifyReset, (nsecs_t when), ());
+
+ MOCK_METHOD(std::optional<ui::LogicalDisplayId>, getAssociatedDisplayId, (), ());
+
+ MOCK_METHOD(void, updateLedState, (bool reset), ());
+
+ MOCK_METHOD(size_t, getMapperCount, (), ());
+};
} // namespace android
diff --git a/services/inputflinger/tests/JoystickInputMapper_test.cpp b/services/inputflinger/tests/JoystickInputMapper_test.cpp
new file mode 100644
index 0000000..adebd72
--- /dev/null
+++ b/services/inputflinger/tests/JoystickInputMapper_test.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2024 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 "JoystickInputMapper.h"
+
+#include <list>
+#include <optional>
+
+#include <EventHub.h>
+#include <NotifyArgs.h>
+#include <ftl/flags.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <input/DisplayViewport.h>
+#include <linux/input-event-codes.h>
+#include <ui/LogicalDisplayId.h>
+
+#include "InputMapperTest.h"
+#include "TestConstants.h"
+#include "TestEventMatchers.h"
+
+namespace android {
+
+using namespace ftl::flag_operators;
+using testing::ElementsAre;
+using testing::IsEmpty;
+using testing::Return;
+using testing::VariantWith;
+
+class JoystickInputMapperTest : public InputMapperUnitTest {
+protected:
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+ EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID))
+ .WillRepeatedly(Return(InputDeviceClass::JOYSTICK | InputDeviceClass::EXTERNAL));
+
+ // The mapper requests info on all ABS axis IDs, including ones which aren't actually used
+ // (e.g. in the range from 0x0b (ABS_BRAKE) to 0x0f (ABS_HAT0X)), so just return nullopt for
+ // all axes we don't explicitly set up below.
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, testing::_))
+ .WillRepeatedly(Return(std::nullopt));
+
+ setupAxis(ABS_X, /*valid=*/true, /*min=*/-32767, /*max=*/32767, /*resolution=*/0);
+ setupAxis(ABS_Y, /*valid=*/true, /*min=*/-32767, /*max=*/32767, /*resolution=*/0);
+ }
+};
+
+TEST_F(JoystickInputMapperTest, Configure_AssignsDisplayUniqueId) {
+ DisplayViewport viewport;
+ viewport.displayId = ui::LogicalDisplayId{1};
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(viewport));
+ mMapper = createInputMapper<JoystickInputMapper>(*mDeviceContext,
+ mFakePolicy->getReaderConfiguration());
+
+ std::list<NotifyArgs> out;
+
+ // Send an axis event
+ out = process(EV_ABS, ABS_X, 100);
+ ASSERT_THAT(out, IsEmpty());
+ out = process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(out, ElementsAre(VariantWith<NotifyMotionArgs>(WithDisplayId(viewport.displayId))));
+
+ // Send another axis event
+ out = process(EV_ABS, ABS_Y, 100);
+ ASSERT_THAT(out, IsEmpty());
+ out = process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(out, ElementsAre(VariantWith<NotifyMotionArgs>(WithDisplayId(viewport.displayId))));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index d3e8dee..88c25d3 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -55,7 +55,6 @@
void SetUp() override {
InputMapperUnitTest::SetUp();
- createDevice();
// set key-codes expected in tests
for (const auto& [scanCode, outKeycode] : mKeyCodeMap) {
@@ -66,6 +65,8 @@
mFakePolicy = sp<FakeInputReaderPolicy>::make();
EXPECT_CALL(mMockInputReaderContext, getPolicy).WillRepeatedly(Return(mFakePolicy.get()));
+ ON_CALL((*mDevice), getSources).WillByDefault(Return(AINPUT_SOURCE_KEYBOARD));
+
mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
AINPUT_SOURCE_KEYBOARD);
}
diff --git a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
index d4d3c38..9a6b266 100644
--- a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
+++ b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
@@ -109,7 +109,6 @@
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
/*isActive=*/true, "local:0", NO_PORT,
ViewportType::INTERNAL);
- createDevice();
mMapper = createInputMapper<MultiTouchInputMapper>(*mDeviceContext,
mFakePolicy->getReaderConfiguration());
}
diff --git a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
index b441a23..9ddb8c1 100644
--- a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
+++ b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
@@ -23,10 +23,7 @@
protected:
static constexpr size_t SLOT_COUNT = 8;
- void SetUp() override {
- InputMapperUnitTest::SetUp();
- createDevice();
- }
+ void SetUp() override { InputMapperUnitTest::SetUp(); }
MultiTouchMotionAccumulator mMotionAccumulator;
diff --git a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
index 366b3dc..6607bc7 100644
--- a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
+++ b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
@@ -78,36 +78,6 @@
return v;
}
-/**
- * A fake InputDeviceContext that allows the associated viewport to be specified for the mapper.
- *
- * This is currently necessary because InputMapperUnitTest doesn't register the mappers it creates
- * with the InputDevice object, meaning that InputDevice::isIgnored becomes true, and the input
- * device doesn't set its associated viewport when it's configured.
- *
- * TODO(b/319217713): work out a way to avoid this fake.
- */
-class ViewportFakingInputDeviceContext : public InputDeviceContext {
-public:
- ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId,
- std::optional<DisplayViewport> viewport)
- : InputDeviceContext(device, eventHubId), mAssociatedViewport(viewport) {}
-
- ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId)
- : ViewportFakingInputDeviceContext(device, eventHubId, createPrimaryViewport()) {}
-
- std::optional<DisplayViewport> getAssociatedViewport() const override {
- return mAssociatedViewport;
- }
-
- void setViewport(const std::optional<DisplayViewport>& viewport) {
- mAssociatedViewport = viewport;
- }
-
-private:
- std::optional<DisplayViewport> mAssociatedViewport;
-};
-
} // namespace
namespace vd_flags = android::companion::virtualdevice::flags;
@@ -138,9 +108,8 @@
mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
// Set up the secondary display as the associated viewport of the mapper.
- createDevice();
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
- mMapper = createInputMapper<RotaryEncoderInputMapper>(deviceContext, mReaderConfiguration);
+ EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(secondaryViewport));
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
std::list<NotifyArgs> args;
// Ensure input events are generated for the secondary display.
@@ -159,7 +128,6 @@
mFakePolicy->addDisplayViewport(createPrimaryViewport());
// Set up the mapper with no associated viewport.
- createDevice();
mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
// Ensure input events are generated without display ID
@@ -174,7 +142,6 @@
}
TEST_F(RotaryEncoderInputMapperTest, ProcessRegularScroll) {
- createDevice();
mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
std::list<NotifyArgs> args;
@@ -191,7 +158,6 @@
vd_flags::high_resolution_scroll(true);
EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
.WillRepeatedly(Return(true));
- createDevice();
mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
std::list<NotifyArgs> args;
@@ -208,7 +174,6 @@
vd_flags::high_resolution_scroll(true);
EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES))
.WillRepeatedly(Return(true));
- createDevice();
mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration);
std::list<NotifyArgs> args;
diff --git a/services/inputflinger/tests/SwitchInputMapper_test.cpp b/services/inputflinger/tests/SwitchInputMapper_test.cpp
index 4020e78..ebbf10b 100644
--- a/services/inputflinger/tests/SwitchInputMapper_test.cpp
+++ b/services/inputflinger/tests/SwitchInputMapper_test.cpp
@@ -33,7 +33,6 @@
protected:
void SetUp() override {
InputMapperUnitTest::SetUp();
- createDevice();
mMapper = createInputMapper<SwitchInputMapper>(*mDeviceContext,
mFakePolicy->getReaderConfiguration());
}
diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
index 1afb4f0..ea69fff 100644
--- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp
+++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
@@ -109,7 +109,6 @@
.WillRepeatedly([]() -> base::Result<std::vector<int32_t>> {
return base::ResultError("Axis not supported", NAME_NOT_FOUND);
});
- createDevice();
mMapper = createInputMapper<TouchpadInputMapper>(*mDeviceContext, mReaderConfiguration);
}
};
@@ -173,4 +172,22 @@
ASSERT_THAT(args, testing::IsEmpty());
}
+TEST_F(TouchpadInputMapperTest, TouchpadHardwareState) {
+ mReaderConfiguration.shouldNotifyTouchpadHardwareState = true;
+ std::list<NotifyArgs> args =
+ mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
+
+ args += process(EV_ABS, ABS_MT_TRACKING_ID, 1);
+ args += process(EV_KEY, BTN_TOUCH, 1);
+ setScanCodeState(KeyState::DOWN, {BTN_TOOL_FINGER});
+ args += process(EV_KEY, BTN_TOOL_FINGER, 1);
+ args += process(EV_ABS, ABS_MT_POSITION_X, 50);
+ args += process(EV_ABS, ABS_MT_POSITION_Y, 50);
+ args += process(EV_ABS, ABS_MT_PRESSURE, 1);
+ args += process(EV_SYN, SYN_REPORT, 0);
+
+ mFakePolicy->assertTouchpadHardwareStateNotified();
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/VibratorInputMapper_test.cpp b/services/inputflinger/tests/VibratorInputMapper_test.cpp
index aa4a6bb..6e3344c 100644
--- a/services/inputflinger/tests/VibratorInputMapper_test.cpp
+++ b/services/inputflinger/tests/VibratorInputMapper_test.cpp
@@ -36,7 +36,6 @@
protected:
void SetUp() override {
InputMapperUnitTest::SetUp();
- createDevice();
EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID))
.WillRepeatedly(testing::Return(InputDeviceClass::VIBRATOR));
EXPECT_CALL(mMockEventHub, getVibratorIds(EVENTHUB_ID))
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index d552c19..3e4a19b 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -117,6 +117,10 @@
return reader->getSensors(deviceId);
}
+ std::optional<HardwareProperties> getTouchpadHardwareProperties(int32_t deviceId) {
+ return reader->getTouchpadHardwareProperties(deviceId);
+ }
+
bool canDispatchToDisplay(int32_t deviceId, ui::LogicalDisplayId displayId) {
return reader->canDispatchToDisplay(deviceId, displayId);
}
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index bf56d3a..ddc3310 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -129,7 +129,6 @@
}
if (mFdp->ConsumeBool()) {
return std::optional<RawAbsoluteAxisInfo>({
- .valid = mFdp->ConsumeBool(),
.minValue = mFdp->ConsumeIntegral<int32_t>(),
.maxValue = mFdp->ConsumeIntegral<int32_t>(),
.flat = mFdp->ConsumeIntegral<int32_t>(),
@@ -282,6 +281,8 @@
FuzzInputReaderPolicy(std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp) : mFdp(mFdp) {}
void getReaderConfiguration(InputReaderConfiguration* outConfig) override {}
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {}
+ void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
+ int32_t deviceId) override {}
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier,
const std::optional<KeyboardLayoutInfo> layoutInfo) override {
diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
index c620032..ebbb311 100644
--- a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
@@ -34,7 +34,6 @@
if (fdp.ConsumeBool()) {
eventHub.setAbsoluteAxisInfo(id, axis,
RawAbsoluteAxisInfo{
- .valid = fdp.ConsumeBool(),
.minValue = fdp.ConsumeIntegral<int32_t>(),
.maxValue = fdp.ConsumeIntegral<int32_t>(),
.flat = fdp.ConsumeIntegral<int32_t>(),
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index 555b80a..33724a9 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -26,12 +26,17 @@
using util::ProtoOutputStream;
-SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorService>& service,
- uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle,
- const String16& opPackageName, int deviceId)
- : mService(service), mUid(uid), mMem(*mem),
+SensorService::SensorDirectConnection::SensorDirectConnection(
+ const sp<SensorService>& service, uid_t uid, pid_t pid, const sensors_direct_mem_t* mem,
+ int32_t halChannelHandle, const String16& opPackageName, int deviceId)
+ : mService(service),
+ mUid(uid),
+ mPid(pid),
+ mMem(*mem),
mHalChannelHandle(halChannelHandle),
- mOpPackageName(opPackageName), mDeviceId(deviceId), mDestroyed(false) {
+ mOpPackageName(opPackageName),
+ mDeviceId(deviceId),
+ mDestroyed(false) {
mUserId = multiuser_get_user_id(mUid);
ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection");
}
@@ -62,10 +67,21 @@
void SensorService::SensorDirectConnection::dump(String8& result) const {
Mutex::Autolock _l(mConnectionLock);
- result.appendFormat("\tPackage %s, HAL channel handle %d, total sensor activated %zu\n",
- String8(mOpPackageName).c_str(), getHalChannelHandle(), mActivated.size());
- for (auto &i : mActivated) {
- result.appendFormat("\t\tSensor %#08x, rate %d\n", i.first, i.second);
+ result.appendFormat("\t%s | HAL channel handle %d | uid %d | pid %d\n",
+ String8(mOpPackageName).c_str(), getHalChannelHandle(), mUid, mPid);
+ result.appendFormat("\tActivated sensor count: %zu\n", mActivated.size());
+ dumpSensorInfoWithLock(result, mActivated);
+
+ result.appendFormat("\tBackup sensor (opened but UID idle) count: %zu\n",
+ mActivatedBackup.size());
+ dumpSensorInfoWithLock(result, mActivatedBackup);
+}
+
+void SensorService::SensorDirectConnection::dumpSensorInfoWithLock(
+ String8& result, std::unordered_map<int, int> sensors) const {
+ for (auto& i : sensors) {
+ result.appendFormat("\t\t%s 0x%08x | rate %d\n", mService->getSensorName(i.first).c_str(),
+ i.first, i.second);
}
}
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
index bfaf811..9f21731 100644
--- a/services/sensorservice/SensorDirectConnection.h
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -17,9 +17,10 @@
#ifndef ANDROID_SENSOR_DIRECT_CONNECTION_H
#define ANDROID_SENSOR_DIRECT_CONNECTION_H
-#include <optional>
+#include <android-base/thread_annotations.h>
#include <stdint.h>
#include <sys/types.h>
+#include <optional>
#include <binder/BinderService.h>
@@ -37,15 +38,15 @@
class SensorService::SensorDirectConnection: public BnSensorEventConnection {
public:
- SensorDirectConnection(const sp<SensorService>& service, uid_t uid,
- const sensors_direct_mem_t *mem, int32_t halChannelHandle,
- const String16& opPackageName, int deviceId);
+ SensorDirectConnection(const sp<SensorService>& service, uid_t uid, pid_t pid,
+ const sensors_direct_mem_t* mem, int32_t halChannelHandle,
+ const String16& opPackageName, int deviceId);
void dump(String8& result) const;
void dump(util::ProtoOutputStream* proto) const;
uid_t getUid() const { return mUid; }
const String16& getOpPackageName() const { return mOpPackageName; }
int32_t getHalChannelHandle() const;
- bool isEquivalent(const sensors_direct_mem_t *mem) const;
+ bool isEquivalent(const sensors_direct_mem_t* mem) const;
// Invoked when access to sensors for this connection has changed, e.g. lost or
// regained due to changes in the sensor restricted/privacy mode or the
@@ -94,8 +95,13 @@
// Recover sensor requests previously capped by capRates().
void uncapRates();
+ // Dumps a set of sensor infos.
+ void dumpSensorInfoWithLock(String8& result, std::unordered_map<int, int> sensors) const
+ EXCLUSIVE_LOCKS_REQUIRED(mConnectionLock);
+
const sp<SensorService> mService;
const uid_t mUid;
+ const pid_t mPid;
const sensors_direct_mem_t mMem;
const int32_t mHalChannelHandle;
const String16 mOpPackageName;
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 3446f58..130c112 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -90,15 +90,14 @@
result.append("NORMAL\n");
}
result.appendFormat("\t %s | WakeLockRefCount %d | uid %d | cache size %d | "
- "max cache size %d\n", mPackageName.c_str(), mWakeLockRefCount, mUid, mCacheSize,
- mMaxCacheSize);
+ "max cache size %d | has sensor access: %s\n",
+ mPackageName.c_str(), mWakeLockRefCount, mUid, mCacheSize, mMaxCacheSize,
+ hasSensorAccess() ? "true" : "false");
for (auto& it : mSensorInfo) {
const FlushInfo& flushInfo = it.second;
- result.appendFormat("\t %s 0x%08x | status: %s | pending flush events %d \n",
- mService->getSensorName(it.first).c_str(),
- it.first,
- flushInfo.mFirstFlushPending ? "First flush pending" :
- "active",
+ result.appendFormat("\t %s 0x%08x | first flush pending: %s | pending flush events %d \n",
+ mService->getSensorName(it.first).c_str(), it.first,
+ flushInfo.mFirstFlushPending ? "true" : "false",
flushInfo.mPendingFlushEventsToSend);
}
#if DEBUG_CONNECTIONS
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 31b7f88..3895ffe 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -682,14 +682,14 @@
mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled");
const auto& activeConnections = connLock.getActiveConnections();
- result.appendFormat("%zd active connections\n", activeConnections.size());
+ result.appendFormat("%zd open event connections\n", activeConnections.size());
for (size_t i=0 ; i < activeConnections.size() ; i++) {
result.appendFormat("Connection Number: %zu \n", i);
activeConnections[i]->dump(result);
}
const auto& directConnections = connLock.getDirectConnections();
- result.appendFormat("%zd direct connections\n", directConnections.size());
+ result.appendFormat("%zd open direct connections\n", directConnections.size());
for (size_t i = 0 ; i < directConnections.size() ; i++) {
result.appendFormat("Direct connection %zu:\n", i);
directConnections[i]->dump(result);
@@ -1729,7 +1729,10 @@
ALOGE("SensorDevice::registerDirectChannel returns %d", channelHandle);
} else {
mem.handle = clone;
- conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName, deviceId);
+ IPCThreadState* thread = IPCThreadState::self();
+ pid_t pid = (thread != nullptr) ? thread->getCallingPid() : -1;
+ conn = new SensorDirectConnection(this, uid, pid, &mem, channelHandle, opPackageName,
+ deviceId);
}
if (conn == nullptr) {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index a37433c..c2a9880 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -47,6 +47,7 @@
"libtimestats_deps",
"libsurfaceflinger_common_deps",
"surfaceflinger_defaults",
+ "libsurfaceflinger_proto_deps",
],
cflags: [
"-DLOG_TAG=\"SurfaceFlinger\"",
@@ -93,7 +94,6 @@
"libcompositionengine",
"libframetimeline",
"libgui_aidl_static",
- "liblayers_proto",
"libperfetto_client_experimental",
"librenderengine",
"libscheduler",
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 6b4215e..abeb2a9 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -21,7 +21,7 @@
#include <private/android_filesystem_config.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/SchedulingPolicy.h>
#include "Client.h"
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 7fa58df..b4ac9ba 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -17,6 +17,7 @@
"librenderengine_deps",
"libtimestats_deps",
"surfaceflinger_defaults",
+ "libsurfaceflinger_proto_deps",
],
cflags: [
"-DLOG_TAG=\"CompositionEngine\"",
@@ -41,13 +42,13 @@
"libutils",
],
static_libs: [
- "liblayers_proto",
"libmath",
"librenderengine",
"libtimestats",
"libtonemap",
"libaidlcommonsupport",
"libprocessgroup",
+ "libprocessgroup_util",
"libcgrouprc",
"libjsoncpp",
"libcgrouprc_format",
@@ -148,6 +149,7 @@
"tests/CompositionEngineTest.cpp",
"tests/DisplayColorProfileTest.cpp",
"tests/DisplayTest.cpp",
+ "tests/HwcAsyncWorkerTest.cpp",
"tests/HwcBufferCacheTest.cpp",
"tests/MockHWC2.cpp",
"tests/MockHWComposer.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp b/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
index 6086f0b..91385b4 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcAsyncWorker.cpp
@@ -24,6 +24,7 @@
#include <android-base/thread_annotations.h>
#include <cutils/sched_policy.h>
+#include <ftl/fake_guard.h>
namespace android::compositionengine::impl {
@@ -60,7 +61,7 @@
std::unique_lock<std::mutex> lock(mMutex);
android::base::ScopedLockAssertion assumeLock(mMutex);
while (!mDone) {
- mCv.wait(lock);
+ mCv.wait(lock, [this]() FTL_FAKE_GUARD(mMutex) { return mTaskRequested || mDone; });
if (mTaskRequested && mTask.valid()) {
mTask();
mTaskRequested = false;
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 64cded8..2d8f98f 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1101,10 +1101,10 @@
}
ftl::Future<std::monostate> Output::presentFrameAndReleaseLayersAsync(bool flushEvenWhenDisabled) {
- return ftl::Future<bool>(std::move(mHwComposerAsyncWorker->send([this, flushEvenWhenDisabled]() {
+ return ftl::Future<bool>(mHwComposerAsyncWorker->send([this, flushEvenWhenDisabled]() {
presentFrameAndReleaseLayers(flushEvenWhenDisabled);
return true;
- })))
+ }))
.then([](bool) { return std::monostate{}; });
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcAsyncWorkerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcAsyncWorkerTest.cpp
new file mode 100644
index 0000000..dd04df6
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcAsyncWorkerTest.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2024 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 <future>
+
+#include <compositionengine/impl/HwcAsyncWorker.h>
+#include <gtest/gtest.h>
+
+namespace android::compositionengine {
+namespace {
+
+using namespace std::chrono_literals;
+
+// For the edge case tests below, how much real time should be spent trying to reproduce edge cases
+// problems in a loop.
+//
+// Larger values mean problems are more likely to be detected, at the cost of making the unit test
+// run slower.
+//
+// As we expect the tests to be run continuously, even a short loop will eventually catch
+// problems, though not necessarily from changes in the same build that introduce them.
+constexpr auto kWallTimeForEdgeCaseTests = 5ms;
+
+TEST(HwcAsyncWorker, continuousTasksEdgeCase) {
+ // Ensures that a single worker that is given multiple tasks in short succession will run them.
+
+ impl::HwcAsyncWorker worker;
+ const auto endTime = std::chrono::steady_clock::now() + kWallTimeForEdgeCaseTests;
+ while (std::chrono::steady_clock::now() < endTime) {
+ auto f1 = worker.send([] { return false; });
+ EXPECT_FALSE(f1.get());
+ auto f2 = worker.send([] { return true; });
+ EXPECT_TRUE(f2.get());
+ }
+}
+
+TEST(HwcAsyncWorker, constructAndDestroyEdgeCase) {
+ // Ensures that newly created HwcAsyncWorkers can be immediately destroyed.
+
+ const auto endTime = std::chrono::steady_clock::now() + kWallTimeForEdgeCaseTests;
+ while (std::chrono::steady_clock::now() < endTime) {
+ impl::HwcAsyncWorker worker;
+ }
+}
+
+TEST(HwcAsyncWorker, newlyCreatedRunsTasksEdgeCase) {
+ // Ensures that newly created HwcAsyncWorkers will run a task if given one immediately.
+
+ const auto endTime = std::chrono::steady_clock::now() + kWallTimeForEdgeCaseTests;
+ while (std::chrono::steady_clock::now() < endTime) {
+ impl::HwcAsyncWorker worker;
+ auto f = worker.send([] { return true; });
+ f.get();
+ }
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
index c43d585..0e9218c 100644
--- a/services/surfaceflinger/Display/DisplayModeController.cpp
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -22,7 +22,9 @@
#include "Display/DisplaySnapshot.h"
#include "DisplayHardware/HWComposer.h"
+#include <android-base/properties.h>
#include <common/FlagManager.h>
+#include <common/trace.h>
#include <ftl/concat.h>
#include <ftl/expected.h>
#include <log/log.h>
@@ -237,4 +239,63 @@
}
}
+void DisplayModeController::updateKernelIdleTimer(PhysicalDisplayId displayId) {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+
+ const auto controllerOpt = displayPtr->selectorPtr->kernelIdleTimerController();
+ if (!controllerOpt) return;
+
+ using KernelIdleTimerAction = scheduler::RefreshRateSelector::KernelIdleTimerAction;
+
+ switch (displayPtr->selectorPtr->getIdleTimerAction()) {
+ case KernelIdleTimerAction::TurnOff:
+ if (displayPtr->isKernelIdleTimerEnabled) {
+ SFTRACE_INT("KernelIdleTimer", 0);
+ updateKernelIdleTimer(displayId, std::chrono::milliseconds::zero(), *controllerOpt);
+ displayPtr->isKernelIdleTimerEnabled = false;
+ }
+ break;
+ case KernelIdleTimerAction::TurnOn:
+ if (!displayPtr->isKernelIdleTimerEnabled) {
+ SFTRACE_INT("KernelIdleTimer", 1);
+ const auto timeout = displayPtr->selectorPtr->getIdleTimerTimeout();
+ updateKernelIdleTimer(displayId, timeout, *controllerOpt);
+ displayPtr->isKernelIdleTimerEnabled = true;
+ }
+ break;
+ }
+}
+
+void DisplayModeController::updateKernelIdleTimer(PhysicalDisplayId displayId,
+ std::chrono::milliseconds timeout,
+ KernelIdleTimerController controller) {
+ switch (controller) {
+ case KernelIdleTimerController::HwcApi:
+ mComposerPtr->setIdleTimerEnabled(displayId, timeout);
+ break;
+
+ case KernelIdleTimerController::Sysprop:
+ using namespace std::string_literals;
+ base::SetProperty("graphics.display.kernel_idle_timer.enabled"s,
+ timeout > std::chrono::milliseconds::zero() ? "true"s : "false"s);
+ break;
+ }
+}
+
+auto DisplayModeController::getKernelIdleTimerState(PhysicalDisplayId displayId) const
+ -> KernelIdleTimerState {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr =
+ FTL_EXPECT(mDisplays.get(displayId).ok_or(KernelIdleTimerState())).get();
+
+ const auto desiredModeIdOpt =
+ (std::scoped_lock(displayPtr->desiredModeLock), displayPtr->desiredModeOpt)
+ .transform([](const display::DisplayModeRequest& request) {
+ return request.mode.modePtr->getId();
+ });
+
+ return {desiredModeIdOpt, displayPtr->isKernelIdleTimerEnabled};
+}
+
} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h
index 258b04b..9ec603d 100644
--- a/services/surfaceflinger/Display/DisplayModeController.h
+++ b/services/surfaceflinger/Display/DisplayModeController.h
@@ -97,6 +97,17 @@
void setActiveMode(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
EXCLUDES(mDisplayLock);
+ void updateKernelIdleTimer(PhysicalDisplayId) REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock);
+
+ struct KernelIdleTimerState {
+ std::optional<DisplayModeId> desiredModeIdOpt = std::nullopt;
+ bool isEnabled = false;
+ };
+
+ KernelIdleTimerState getKernelIdleTimerState(PhysicalDisplayId) const
+ REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+
private:
struct Display {
template <size_t N>
@@ -121,6 +132,8 @@
DisplayModeRequestOpt pendingModeOpt GUARDED_BY(kMainThreadContext);
bool isModeSetPending GUARDED_BY(kMainThreadContext) = false;
+
+ bool isKernelIdleTimerEnabled GUARDED_BY(kMainThreadContext) = false;
};
using DisplayPtr = std::unique_ptr<Display>;
@@ -128,6 +141,10 @@
void setActiveModeLocked(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
REQUIRES(mDisplayLock);
+ using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController;
+ void updateKernelIdleTimer(PhysicalDisplayId, std::chrono::milliseconds timeout,
+ KernelIdleTimerController) REQUIRES(mDisplayLock);
+
// Set once when initializing the DisplayModeController, which the HWComposer must outlive.
HWComposer* mComposerPtr = nullptr;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 75b07a8..402a3d2 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -201,19 +201,6 @@
return mPowerMode != hal::PowerMode::OFF;
}
-nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
- const auto physicalId = getPhysicalId();
- if (!mHwComposer.isConnected(physicalId)) {
- return 0;
- }
-
- if (const auto vsyncPeriodOpt = mHwComposer.getDisplayVsyncPeriod(physicalId).value_opt()) {
- return *vsyncPeriodOpt;
- }
-
- return refreshRateSelector().getActiveMode().modePtr->getVsyncRate().getPeriodNsecs();
-}
-
ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
return mCompositionDisplay->getState().dataspace;
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 1b8a3a8..3e3f558 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -203,8 +203,6 @@
void updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio);
bool isHdrSdrRatioOverlayEnabled() const { return mHdrSdrRatioOverlay != nullptr; }
- nsecs_t getVsyncPeriodFromHWC() const;
-
Fps getAdjustedRefreshRate() const { return mAdjustedRefreshRate; }
// Round the requested refresh rate to match a divisor of the pacesetter
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index d50a0bc..0eced73 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -44,6 +44,7 @@
using aidl::android::hardware::graphics::composer3::BnComposerCallback;
using aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
+using aidl::android::hardware::graphics::composer3::Lut;
using aidl::android::hardware::graphics::composer3::PowerMode;
using aidl::android::hardware::graphics::composer3::VirtualDisplay;
@@ -1539,6 +1540,18 @@
return error;
}
+Error AidlComposer::getDisplayLuts(Display display, std::vector<Lut>* outLuts) {
+ Error error = Error::NONE;
+ mMutex.lock_shared();
+ if (auto reader = getReader(display)) {
+ *outLuts = reader->get().takeDisplayLuts(translate<int64_t>(display));
+ } else {
+ error = Error::BAD_DISPLAY;
+ }
+ mMutex.unlock_shared();
+ return error;
+}
+
Error AidlComposer::setLayerBrightness(Display display, Layer layer, float brightness) {
Error error = Error::NONE;
mMutex.lock_shared();
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index ea0e53a..3669d4c 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -244,6 +244,9 @@
Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime,
int32_t frameIntervalNs) override;
+ Error getDisplayLuts(
+ Display display,
+ std::vector<aidl::android::hardware::graphics::composer3::Lut>* outLuts) override;
private:
// Many public functions above simply write a command into the command
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index bc067a0..888dc08 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -41,6 +41,7 @@
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
#include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h>
#include <aidl/android/hardware/graphics/composer3/IComposerCallback.h>
+#include <aidl/android/hardware/graphics/composer3/Lut.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
#include <aidl/android/hardware/graphics/common/Transform.h>
@@ -303,6 +304,7 @@
virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0;
virtual Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime,
int32_t frameIntervalNs) = 0;
+ virtual Error getDisplayLuts(Display display, std::vector<V3_0::Lut>* outLuts) = 0;
};
} // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index c77cdd4..748765a 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -31,10 +31,11 @@
#include <utils/String8.h>
#include <log/log.h>
-#include <hardware/hardware.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/BufferItem.h>
#include <gui/BufferQueue.h>
#include <gui/Surface.h>
+#include <hardware/hardware.h>
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
@@ -48,10 +49,18 @@
using ui::Dataspace;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+FramebufferSurface::FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
+ const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer,
+ const ui::Size& size, const ui::Size& maxSize)
+ : ConsumerBase(producer, consumer),
+#else
FramebufferSurface::FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
const sp<IGraphicBufferConsumer>& consumer,
const ui::Size& size, const ui::Size& maxSize)
: ConsumerBase(consumer),
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
mDisplayId(displayId),
mMaxSize(maxSize),
mCurrentBufferSlot(-1),
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 2728cf6..6ca64a2 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <com_android_graphics_libgui_flags.h>
#include <compositionengine/DisplaySurface.h>
#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
@@ -40,9 +41,16 @@
class FramebufferSurface : public ConsumerBase, public compositionengine::DisplaySurface {
public:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
+ const sp<IGraphicBufferProducer>& producer,
+ const sp<IGraphicBufferConsumer>& consumer, const ui::Size& size,
+ const ui::Size& maxSize);
+#else
FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId,
const sp<IGraphicBufferConsumer>& consumer, const ui::Size& size,
const ui::Size& maxSize);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
virtual status_t beginFrame(bool mustRecompose);
virtual status_t prepareFrame(CompositionType compositionType);
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 8c0f81e..d5f65c6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -41,6 +41,7 @@
using aidl::android::hardware::graphics::composer3::Composition;
using AidlCapability = aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::DisplayCapability;
+using aidl::android::hardware::graphics::composer3::Lut;
using aidl::android::hardware::graphics::composer3::OverlayProperties;
namespace android {
@@ -607,6 +608,18 @@
return static_cast<Error>(error);
}
+Error Display::getDisplayLuts(std::vector<Lut>* outLuts) {
+ std::vector<Lut> tmpLuts;
+ const auto error = mComposer.getDisplayLuts(mId, &tmpLuts);
+ for (Lut& lut : tmpLuts) {
+ if (lut.pfd.get() >= 0) {
+ outLuts->push_back(
+ {lut.layer, ndk::ScopedFileDescriptor(lut.pfd.release()), lut.lutProperties});
+ }
+ }
+ return static_cast<Error>(error);
+}
+
Error Display::getDisplayDecorationSupport(
std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
support) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 5b94831..be2059a 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -45,6 +45,7 @@
#include <aidl/android/hardware/graphics/composer3/Color.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
+#include <aidl/android/hardware/graphics/composer3/Lut.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
#include <aidl/android/hardware/graphics/composer3/RefreshRateChangedDebugData.h>
@@ -178,6 +179,8 @@
[[nodiscard]] virtual hal::Error getClientTargetProperty(
aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness*
outClientTargetProperty) = 0;
+ [[nodiscard]] virtual hal::Error getDisplayLuts(
+ std::vector<aidl::android::hardware::graphics::composer3::Lut>* outLuts) = 0;
[[nodiscard]] virtual hal::Error getDisplayDecorationSupport(
std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
support) = 0;
@@ -261,6 +264,8 @@
hal::Error getClientTargetProperty(
aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness*
outClientTargetProperty) override;
+ hal::Error getDisplayLuts(
+ std::vector<aidl::android::hardware::graphics::composer3::Lut>* outLuts) override;
hal::Error getDisplayDecorationSupport(
std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
support) override;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index c5008d8..ec2a3ec 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -46,6 +46,7 @@
using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
using aidl::android::hardware::graphics::composer3::DimmingStage;
using aidl::android::hardware::graphics::composer3::DisplayCapability;
+using aidl::android::hardware::graphics::composer3::Lut;
using aidl::android::hardware::graphics::composer3::OverlayProperties;
namespace android {
@@ -209,7 +210,7 @@
if (!buffer || buffer->initCheck() != ::android::OK) {
return nullptr;
}
- return std::move(buffer);
+ return buffer;
}
} // anonymous namespace
@@ -1408,6 +1409,10 @@
return Error::NONE;
}
+Error HidlComposer::getDisplayLuts(Display, std::vector<Lut>*) {
+ return Error::NONE;
+}
+
Error HidlComposer::setLayerBrightness(Display, Layer, float) {
return Error::NONE;
}
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index d78bfb7..8bca5ad 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -351,6 +351,8 @@
Hdr*) override;
Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
Error notifyExpectedPresent(Display, nsecs_t, int32_t) override;
+ Error getDisplayLuts(Display,
+ std::vector<aidl::android::hardware::graphics::composer3::Lut>*) override;
private:
class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 4b5a68c..384f7b2 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -22,6 +22,7 @@
#include <cinttypes>
+#include <com_android_graphics_libgui_flags.h>
#include <ftl/enum.h>
#include <ftl/flags.h>
#include <gui/BufferItem.h>
@@ -51,7 +52,11 @@
const sp<IGraphicBufferProducer>& bqProducer,
const sp<IGraphicBufferConsumer>& bqConsumer,
const std::string& name)
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ : ConsumerBase(bqProducer, bqConsumer),
+#else
: ConsumerBase(bqConsumer),
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
mHwc(hwc),
mDisplayId(displayId),
mDisplayName(name),
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index f4335f3..d709530 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -18,6 +18,8 @@
#undef LOG_TAG
#define LOG_TAG "SurfaceFlinger"
+#include <android-base/logging.h>
+
#include "LayerHierarchy.h"
#include "LayerLog.h"
#include "SwapErase.h"
@@ -485,6 +487,55 @@
return it->second;
}
+void LayerHierarchyBuilder::logSampledChildren(const LayerHierarchy& hierarchy) const {
+ LOG(ERROR) << "Dumping random sampling of child layers.";
+ int sampleSize = static_cast<int>(hierarchy.mChildren.size() / 100 + 1);
+ for (const auto& [child, variant] : hierarchy.mChildren) {
+ if (rand() % sampleSize == 0) {
+ LOG(ERROR) << "Child Layer: " << *(child->mLayer);
+ }
+ }
+}
+
+void LayerHierarchyBuilder::dumpLayerSample(const LayerHierarchy& root) const {
+ LOG(ERROR) << "Dumping layer keeping > 20 children alive:";
+ // If mLayer is nullptr, it will be skipped while traversing.
+ if (!root.mLayer && root.mChildren.size() > 20) {
+ LOG(ERROR) << "ROOT has " << root.mChildren.size() << " children";
+ logSampledChildren(root);
+ }
+ root.traverse([&](const LayerHierarchy& hierarchy, const auto&) -> bool {
+ if (hierarchy.mChildren.size() <= 20) {
+ return true;
+ }
+ // mLayer is ensured to be non-null. See LayerHierarchy::traverse.
+ const auto* layer = hierarchy.mLayer;
+ const auto childrenCount = hierarchy.mChildren.size();
+ LOG(ERROR) << "Layer " << *layer << " has " << childrenCount << " children";
+
+ const auto* parent = hierarchy.mParent;
+ while (parent != nullptr) {
+ if (!parent->mLayer) break;
+ LOG(ERROR) << "Parent Layer: " << *(parent->mLayer);
+ parent = parent->mParent;
+ }
+
+ logSampledChildren(hierarchy);
+ // Stop traversing.
+ return false;
+ });
+ LOG(ERROR) << "Dumping random sampled layers.";
+ size_t numLayers = 0;
+ root.traverse([&](const LayerHierarchy& hierarchy, const auto&) -> bool {
+ if (hierarchy.mLayer) numLayers++;
+ if ((rand() % 20 == 13) && hierarchy.mLayer) {
+ LOG(ERROR) << "Layer: " << *(hierarchy.mLayer);
+ }
+ return true;
+ });
+ LOG(ERROR) << "Total layer count: " << numLayers;
+}
+
const LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::ROOT =
{.id = UNASSIGNED_LAYER_ID, .variant = LayerHierarchy::Attached};
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index d023f9e..47d0041 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -211,8 +211,11 @@
const LayerHierarchy& getHierarchy() const;
const LayerHierarchy& getOffscreenHierarchy() const;
std::string getDebugString(uint32_t layerId, uint32_t depth = 0) const;
+ void dumpLayerSample(const LayerHierarchy& layerHierarchy) const;
private:
+ void logSampledChildren(const LayerHierarchy& hierarchy) const;
+
void onLayerAdded(RequestedLayerState* layer);
void attachToParent(LayerHierarchy*);
void detachFromParent(LayerHierarchy*);
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 4e09381..ac15b92 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -320,11 +320,8 @@
void updateMetadataAndGameMode(LayerSnapshot& snapshot, const RequestedLayerState& requested,
const LayerSnapshotBuilder::Args& args,
const LayerSnapshot& parentSnapshot) {
- if (snapshot.changes.test(RequestedLayerState::Changes::GameMode)) {
- snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE)
- ? requested.gameMode
- : parentSnapshot.gameMode;
- }
+ snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE) ? requested.gameMode
+ : parentSnapshot.gameMode;
updateMetadata(snapshot, requested, args);
if (args.includeMetadata) {
snapshot.layerMetadata = parentSnapshot.layerMetadata;
@@ -564,6 +561,7 @@
updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath);
}
+ bool childHasValidFrameRate = false;
for (auto& [childHierarchy, variant] : hierarchy.mChildren) {
LayerHierarchy::ScopedAddToTraversalPath addChildToPath(traversalPath,
childHierarchy->getLayer()->id,
@@ -571,7 +569,8 @@
const LayerSnapshot& childSnapshot =
updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot,
depth + 1);
- updateFrameRateFromChildSnapshot(*snapshot, childSnapshot, args);
+ updateFrameRateFromChildSnapshot(*snapshot, childSnapshot, *childHierarchy->getLayer(),
+ args, &childHasValidFrameRate);
}
return *snapshot;
@@ -678,9 +677,10 @@
}
}
-void LayerSnapshotBuilder::updateFrameRateFromChildSnapshot(LayerSnapshot& snapshot,
- const LayerSnapshot& childSnapshot,
- const Args& args) {
+void LayerSnapshotBuilder::updateFrameRateFromChildSnapshot(
+ LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot,
+ const RequestedLayerState& /* requestedChildState */, const Args& args,
+ bool* outChildHasValidFrameRate) {
if (args.forceUpdate == ForceUpdateFlags::NONE &&
!args.layerLifecycleManager.getGlobalChanges().any(
RequestedLayerState::Changes::Hierarchy) &&
@@ -690,7 +690,7 @@
}
using FrameRateCompatibility = scheduler::FrameRateCompatibility;
- if (snapshot.frameRate.isValid()) {
+ if (snapshot.inheritedFrameRate.isValid() || *outChildHasValidFrameRate) {
// we already have a valid framerate.
return;
}
@@ -707,13 +707,18 @@
const auto layerVotedWithExactCompatibility = childSnapshot.frameRate.vote.rate.isValid() &&
childSnapshot.frameRate.vote.type == FrameRateCompatibility::Exact;
- bool childHasValidFrameRate = layerVotedWithDefaultCompatibility || layerVotedWithNoVote ||
+ *outChildHasValidFrameRate |= layerVotedWithDefaultCompatibility || layerVotedWithNoVote ||
layerVotedWithCategory || layerVotedWithExactCompatibility;
// If we don't have a valid frame rate, but the children do, we set this
// layer as NoVote to allow the children to control the refresh rate
- if (childHasValidFrameRate) {
- snapshot.frameRate = scheduler::LayerInfo::FrameRate(Fps(), FrameRateCompatibility::NoVote);
+ static const auto noVote =
+ scheduler::LayerInfo::FrameRate(Fps(), FrameRateCompatibility::NoVote);
+ if (*outChildHasValidFrameRate) {
+ snapshot.frameRate = noVote;
+ snapshot.changes |= RequestedLayerState::Changes::FrameRate;
+ } else if (snapshot.frameRate != snapshot.inheritedFrameRate) {
+ snapshot.frameRate = snapshot.inheritedFrameRate;
snapshot.changes |= RequestedLayerState::Changes::FrameRate;
}
}
@@ -780,7 +785,8 @@
}
if (forceUpdate ||
(args.includeMetadata &&
- snapshot.changes.test(RequestedLayerState::Changes::Metadata))) {
+ snapshot.changes.any(RequestedLayerState::Changes::Metadata |
+ RequestedLayerState::Changes::Geometry))) {
updateMetadataAndGameMode(snapshot, requested, args, parentSnapshot);
}
return;
@@ -848,7 +854,9 @@
}
}
- if (forceUpdate || snapshot.changes.test(RequestedLayerState::Changes::Metadata)) {
+ if (forceUpdate ||
+ snapshot.changes.any(RequestedLayerState::Changes::Metadata |
+ RequestedLayerState::Changes::Hierarchy)) {
updateMetadataAndGameMode(snapshot, requested, args, parentSnapshot);
}
@@ -1257,6 +1265,12 @@
}
}
+void LayerSnapshotBuilder::forEachSnapshot(const ConstVisitor& visitor) const {
+ for (auto& snapshot : mSnapshots) {
+ visitor(*snapshot);
+ }
+}
+
void LayerSnapshotBuilder::forEachInputSnapshot(const ConstVisitor& visitor) const {
for (int i = mNumInterestingSnapshots - 1; i >= 0; i--) {
LayerSnapshot& snapshot = *mSnapshots[(size_t)i];
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index f3c56a4..486cb33 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -91,6 +91,9 @@
// snapshots in z-order
void forEachSnapshot(const Visitor& visitor, const ConstPredicate& predicate);
+ // Visit each snapshot
+ void forEachSnapshot(const ConstVisitor& visitor) const;
+
// Visit each snapshot interesting to input reverse z-order
void forEachInputSnapshot(const ConstVisitor& visitor) const;
@@ -130,7 +133,9 @@
const RequestedLayerState& layer,
const LayerSnapshot& parentSnapshot);
void updateFrameRateFromChildSnapshot(LayerSnapshot& snapshot,
- const LayerSnapshot& childSnapshot, const Args& args);
+ const LayerSnapshot& childSnapshot,
+ const RequestedLayerState& requestedCHildState,
+ const Args& args, bool* outChildHasValidFrameRate);
void updateTouchableRegionCrop(const Args& args);
std::unordered_map<LayerHierarchy::TraversalPath, LayerSnapshot*,
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.cpp b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
index dfb1c1e..2088635 100644
--- a/services/surfaceflinger/HdrSdrRatioOverlay.cpp
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
@@ -114,7 +114,7 @@
ALOGE("%s: Failed to create buffer state layer", __func__);
return;
}
- SurfaceComposerClient::Transaction()
+ createTransaction()
.setLayer(mSurfaceControl->get(), INT32_MAX - 2)
.setTrustedOverlay(mSurfaceControl->get(), true)
.apply();
@@ -130,7 +130,7 @@
}
void HdrSdrRatioOverlay::setLayerStack(ui::LayerStack stack) {
- SurfaceComposerClient::Transaction().setLayerStack(mSurfaceControl->get(), stack).apply();
+ createTransaction().setLayerStack(mSurfaceControl->get(), stack).apply();
}
void HdrSdrRatioOverlay::setViewport(ui::Size viewport) {
@@ -141,7 +141,7 @@
// set the ratio frame to the top right of the screen
frame.offsetBy(viewport.width - frame.width(), height >> 4);
- SurfaceComposerClient::Transaction()
+ createTransaction()
.setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
.setPosition(mSurfaceControl->get(), frame.left, frame.top)
@@ -167,7 +167,7 @@
}
}();
- SurfaceComposerClient::Transaction().setTransform(mSurfaceControl->get(), transform).apply();
+ createTransaction().setTransform(mSurfaceControl->get(), transform).apply();
constexpr SkColor kMinRatioColor = SK_ColorBLUE;
constexpr SkColor kMaxRatioColor = SK_ColorGREEN;
@@ -194,9 +194,21 @@
void HdrSdrRatioOverlay::animate() {
if (!std::isfinite(mCurrentHdrSdrRatio) || mCurrentHdrSdrRatio < 1.0f) return;
- SurfaceComposerClient::Transaction()
+ createTransaction()
.setBuffer(mSurfaceControl->get(), getOrCreateBuffers(mCurrentHdrSdrRatio))
.apply();
}
+SurfaceComposerClient::Transaction HdrSdrRatioOverlay::createTransaction() const {
+ constexpr float kFrameRate = 0.f;
+ constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE;
+ constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS;
+
+ const sp<SurfaceControl>& surface = mSurfaceControl->get();
+
+ SurfaceComposerClient::Transaction transaction;
+ transaction.setFrameRate(surface, kFrameRate, kCompatibility, kSeamlessness);
+ return transaction;
+}
+
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.h b/services/surfaceflinger/HdrSdrRatioOverlay.h
index 72d401d..ba88252 100644
--- a/services/surfaceflinger/HdrSdrRatioOverlay.h
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.h
@@ -53,5 +53,7 @@
size_t mIndex = 0;
std::array<sp<GraphicBuffer>, 2> mRingBuffer;
+
+ SurfaceComposerClient::Transaction createTransaction() const;
};
} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 0eb6cc3..636f7bd 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -316,8 +316,7 @@
void Layer::addToCurrentState() {
if (mRemovedFromDrawingState) {
mRemovedFromDrawingState = false;
- mFlinger->mScheduler->registerLayer(this);
- mFlinger->removeFromOffscreenLayers(this);
+ mFlinger->mScheduler->registerLayer(this, FrameRateCompatibility::Default);
}
for (const auto& child : mCurrentChildren) {
@@ -1100,42 +1099,6 @@
return true;
}
-bool Layer::setFrameRateSelectionPriority(int32_t priority) {
- if (mDrawingState.frameRateSelectionPriority == priority) return false;
- mDrawingState.frameRateSelectionPriority = priority;
- mDrawingState.sequence++;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-int32_t Layer::getFrameRateSelectionPriority() const {
- // Check if layer has priority set.
- if (mDrawingState.frameRateSelectionPriority != PRIORITY_UNSET) {
- return mDrawingState.frameRateSelectionPriority;
- }
- // If not, search whether its parents have it set.
- sp<Layer> parent = getParent();
- if (parent != nullptr) {
- return parent->getFrameRateSelectionPriority();
- }
-
- return Layer::PRIORITY_UNSET;
-}
-
-bool Layer::setDefaultFrameRateCompatibility(FrameRateCompatibility compatibility) {
- if (mDrawingState.defaultFrameRateCompatibility == compatibility) return false;
- mDrawingState.defaultFrameRateCompatibility = compatibility;
- mDrawingState.modified = true;
- mFlinger->mScheduler->setDefaultFrameRateCompatibility(sequence, compatibility);
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-scheduler::FrameRateCompatibility Layer::getDefaultFrameRateCompatibility() const {
- return mDrawingState.defaultFrameRateCompatibility;
-}
-
bool Layer::isLayerFocusedBasedOnPriority(int32_t priority) {
return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE;
};
@@ -1202,119 +1165,8 @@
return StretchEffect{};
}
-bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren,
- bool* transactionNeeded) {
- // Gets the frame rate to propagate to children.
- const auto frameRate = [&] {
- if (overrideChildren && parentFrameRate.isValid()) {
- return parentFrameRate;
- }
-
- if (mDrawingState.frameRate.isValid()) {
- return mDrawingState.frameRate;
- }
-
- return parentFrameRate;
- }();
-
- auto now = systemTime();
- *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate, now);
-
- // The frame rate is propagated to the children by default, but some properties may override it.
- bool childrenHaveFrameRate = false;
- const bool overrideChildrenFrameRate = overrideChildren || shouldOverrideChildrenFrameRate();
- const bool canPropagateFrameRate = shouldPropagateFrameRate() || overrideChildrenFrameRate;
- for (const sp<Layer>& child : mCurrentChildren) {
- childrenHaveFrameRate |=
- child->propagateFrameRateForLayerTree(canPropagateFrameRate ? frameRate
- : FrameRate(),
- overrideChildrenFrameRate, transactionNeeded);
- }
-
- // If we don't have a valid frame rate specification, but the children do, we set this
- // layer as NoVote to allow the children to control the refresh rate
- if (!frameRate.isValid() && childrenHaveFrameRate) {
- *transactionNeeded |=
- setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote),
- now);
- }
-
- // We return whether this layer or its children has a vote. We ignore ExactOrMultiple votes for
- // the same reason we are allowing touch boost for those layers. See
- // RefreshRateSelector::rankFrameRates for details.
- const auto layerVotedWithDefaultCompatibility =
- frameRate.vote.rate.isValid() && frameRate.vote.type == FrameRateCompatibility::Default;
- const auto layerVotedWithNoVote = frameRate.vote.type == FrameRateCompatibility::NoVote;
- const auto layerVotedWithCategory = frameRate.category != FrameRateCategory::Default;
- const auto layerVotedWithExactCompatibility =
- frameRate.vote.rate.isValid() && frameRate.vote.type == FrameRateCompatibility::Exact;
- return layerVotedWithDefaultCompatibility || layerVotedWithNoVote || layerVotedWithCategory ||
- layerVotedWithExactCompatibility || childrenHaveFrameRate;
-}
-
-void Layer::updateTreeHasFrameRateVote() {
- const auto root = [&]() -> sp<Layer> {
- sp<Layer> layer = sp<Layer>::fromExisting(this);
- while (auto parent = layer->getParent()) {
- layer = parent;
- }
- return layer;
- }();
-
- bool transactionNeeded = false;
- root->propagateFrameRateForLayerTree({}, false, &transactionNeeded);
-
- // TODO(b/195668952): we probably don't need eTraversalNeeded here
- if (transactionNeeded) {
- mFlinger->setTransactionFlags(eTraversalNeeded);
- }
-}
-
-bool Layer::setFrameRate(FrameRate::FrameRateVote frameRateVote) {
- if (mDrawingState.frameRate.vote == frameRateVote) {
- return false;
- }
-
- mDrawingState.sequence++;
- mDrawingState.frameRate.vote = frameRateVote;
- mDrawingState.modified = true;
-
- updateTreeHasFrameRateVote();
-
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setFrameRateCategory(FrameRateCategory category, bool smoothSwitchOnly) {
- if (mDrawingState.frameRate.category == category &&
- mDrawingState.frameRate.categorySmoothSwitchOnly == smoothSwitchOnly) {
- return false;
- }
-
- mDrawingState.sequence++;
- mDrawingState.frameRate.category = category;
- mDrawingState.frameRate.categorySmoothSwitchOnly = smoothSwitchOnly;
- mDrawingState.modified = true;
-
- updateTreeHasFrameRateVote();
-
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setFrameRateSelectionStrategy(FrameRateSelectionStrategy strategy) {
- if (mDrawingState.frameRateSelectionStrategy == strategy) return false;
- mDrawingState.frameRateSelectionStrategy = strategy;
- mDrawingState.sequence++;
- mDrawingState.modified = true;
-
- updateTreeHasFrameRateVote();
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
void Layer::setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info,
- nsecs_t postTime) {
+ nsecs_t postTime, gui::GameMode gameMode) {
mDrawingState.postTime = postTime;
// Check if one of the bufferlessSurfaceFramesTX contains the same vsyncId. This can happen if
@@ -1330,14 +1182,15 @@
mDrawingState.bufferSurfaceFrameTX->setActualQueueTime(postTime);
} else {
mDrawingState.bufferSurfaceFrameTX =
- createSurfaceFrameForBuffer(info, postTime, mTransactionName);
+ createSurfaceFrameForBuffer(info, postTime, mTransactionName, gameMode);
}
- setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName);
+ setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName, gameMode);
}
void Layer::setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info,
- nsecs_t postTime) {
+ nsecs_t postTime,
+ gui::GameMode gameMode) {
mDrawingState.frameTimelineInfo = info;
mDrawingState.postTime = postTime;
mDrawingState.modified = true;
@@ -1356,17 +1209,17 @@
// targeting different vsyncs).
auto it = mDrawingState.bufferlessSurfaceFramesTX.find(info.vsyncId);
if (it == mDrawingState.bufferlessSurfaceFramesTX.end()) {
- auto surfaceFrame = createSurfaceFrameForTransaction(info, postTime);
+ auto surfaceFrame = createSurfaceFrameForTransaction(info, postTime, gameMode);
mDrawingState.bufferlessSurfaceFramesTX[info.vsyncId] = surfaceFrame;
} else {
if (it->second->getPresentState() == PresentState::Presented) {
// If the SurfaceFrame was already presented, its safe to overwrite it since it must
// have been from previous vsync.
- it->second = createSurfaceFrameForTransaction(info, postTime);
+ it->second = createSurfaceFrameForTransaction(info, postTime, gameMode);
}
}
- setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName);
+ setFrameTimelineVsyncForSkippedFrames(info, postTime, mTransactionName, gameMode);
}
void Layer::addSurfaceFrameDroppedForBuffer(
@@ -1386,12 +1239,12 @@
}
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction(
- const FrameTimelineInfo& info, nsecs_t postTime) {
+ const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode) {
auto surfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
getSequence(), mName,
mTransactionName,
- /*isBuffer*/ false, getGameMode());
+ /*isBuffer*/ false, gameMode);
surfaceFrame->setActualStartTime(info.startTimeNanos);
// For Transactions, the post time is considered to be both queue and acquire fence time.
surfaceFrame->setActualQueueTime(postTime);
@@ -1404,11 +1257,12 @@
}
std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer(
- const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName) {
+ const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName,
+ gui::GameMode gameMode) {
auto surfaceFrame =
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
getSequence(), mName, debugName,
- /*isBuffer*/ true, getGameMode());
+ /*isBuffer*/ true, gameMode);
surfaceFrame->setActualStartTime(info.startTimeNanos);
// For buffers, acquire fence time will set during latch.
surfaceFrame->setActualQueueTime(queueTime);
@@ -1420,7 +1274,7 @@
}
void Layer::setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, nsecs_t postTime,
- std::string debugName) {
+ std::string debugName, gui::GameMode gameMode) {
if (info.skippedFrameVsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
return;
}
@@ -1432,7 +1286,7 @@
mFlinger->mFrameTimeline->createSurfaceFrameForToken(skippedFrameTimelineInfo,
mOwnerPid, mOwnerUid,
getSequence(), mName, debugName,
- /*isBuffer*/ false, getGameMode());
+ /*isBuffer*/ false, gameMode);
surfaceFrame->setActualStartTime(skippedFrameTimelineInfo.skippedFrameStartTimeNanos);
// For Transactions, the post time is considered to be both queue and acquire fence time.
surfaceFrame->setActualQueueTime(postTime);
@@ -1444,25 +1298,6 @@
addSurfaceFrameDroppedForBuffer(surfaceFrame, postTime);
}
-bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate, nsecs_t now) {
- if (mDrawingState.frameRateForLayerTree == frameRate) {
- return false;
- }
-
- mDrawingState.frameRateForLayerTree = frameRate;
-
- // TODO(b/195668952): we probably don't need to dirty visible regions here
- // or even store frameRateForLayerTree in mDrawingState
- mDrawingState.sequence++;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
-
- mFlinger->mScheduler
- ->recordLayerHistory(sequence, getLayerProps(), now, now,
- scheduler::LayerHistory::LayerUpdateType::SetFrameRate);
- return true;
-}
-
bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps,
nsecs_t now) {
if (mDrawingState.frameRateForLayerTree == frameRate) {
@@ -1514,14 +1349,6 @@
return usage;
}
-void Layer::updateTransformHint(ui::Transform::RotationFlags transformHint) {
- if (mFlinger->mDebugDisableTransformHint || transformHint & ui::Transform::ROT_INVALID) {
- transformHint = ui::Transform::ROT_0;
- }
-
- setTransformHintLegacy(transformHint);
-}
-
// ----------------------------------------------------------------------------
// debugging
// ----------------------------------------------------------------------------
@@ -1541,57 +1368,6 @@
result.append("\n");
}
-void Layer::miniDumpLegacy(std::string& result, const DisplayDevice& display) const {
- const auto outputLayer = findOutputLayerForDisplay(&display);
- if (!outputLayer) {
- return;
- }
-
- std::string name;
- if (mName.length() > 77) {
- std::string shortened;
- shortened.append(mName, 0, 36);
- shortened.append("[...]");
- shortened.append(mName, mName.length() - 36);
- name = std::move(shortened);
- } else {
- name = mName;
- }
-
- StringAppendF(&result, " %s\n", name.c_str());
-
- const State& layerState(getDrawingState());
- const auto& outputLayerState = outputLayer->getState();
-
- if (layerState.zOrderRelativeOf != nullptr || mDrawingParent != nullptr) {
- StringAppendF(&result, " rel %6d | ", layerState.z);
- } else {
- StringAppendF(&result, " %10d | ", layerState.z);
- }
- StringAppendF(&result, " %10d | ", mWindowType);
- StringAppendF(&result, "%10s | ", toString(getCompositionType(display)).c_str());
- StringAppendF(&result, "%10s | ", toString(outputLayerState.bufferTransform).c_str());
- const Rect& frame = outputLayerState.displayFrame;
- StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom);
- const FloatRect& crop = outputLayerState.sourceCrop;
- StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right,
- crop.bottom);
- const auto frameRate = getFrameRateForLayerTree();
- if (frameRate.vote.rate.isValid() || frameRate.vote.type != FrameRateCompatibility::Default) {
- StringAppendF(&result, "%s %15s %17s", to_string(frameRate.vote.rate).c_str(),
- ftl::enum_string(frameRate.vote.type).c_str(),
- ftl::enum_string(frameRate.vote.seamlessness).c_str());
- } else {
- result.append(41, ' ');
- }
-
- const auto focused = isLayerFocusedBasedOnPriority(getFrameRateSelectionPriority());
- StringAppendF(&result, " [%s]\n", focused ? "*" : " ");
-
- result.append(kDumpTableRowLength, '-');
- result.append("\n");
-}
-
void Layer::miniDump(std::string& result, const frontend::LayerSnapshot& snapshot,
const DisplayDevice& display) const {
const auto outputLayer = findOutputLayerForDisplay(&display, snapshot.path);
@@ -1671,26 +1447,12 @@
return count;
}
-void Layer::setGameModeForTree(GameMode gameMode) {
- const auto& currentState = getDrawingState();
- if (currentState.metadata.has(gui::METADATA_GAME_MODE)) {
- gameMode =
- static_cast<GameMode>(currentState.metadata.getInt32(gui::METADATA_GAME_MODE, 0));
- }
- setGameMode(gameMode);
- for (const sp<Layer>& child : mCurrentChildren) {
- child->setGameModeForTree(gameMode);
- }
-}
-
void Layer::addChild(const sp<Layer>& layer) {
mFlinger->mSomeChildrenChanged = true;
setTransactionFlags(eTransactionNeeded);
mCurrentChildren.add(layer);
layer->setParent(sp<Layer>::fromExisting(this));
- layer->setGameModeForTree(mGameMode);
- updateTreeHasFrameRateVote();
}
ssize_t Layer::removeChild(const sp<Layer>& layer) {
@@ -1700,10 +1462,6 @@
layer->setParent(nullptr);
const auto removeResult = mCurrentChildren.remove(layer);
- updateTreeHasFrameRateVote();
- layer->setGameModeForTree(GameMode::Unsupported);
- layer->updateTreeHasFrameRateVote();
-
return removeResult;
}
@@ -3082,7 +2840,7 @@
bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
- bool isAutoTimestamp, const FrameTimelineInfo& info) {
+ bool isAutoTimestamp, const FrameTimelineInfo& info, gui::GameMode gameMode) {
SFTRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
const bool frameNumberChanged =
@@ -3108,12 +2866,12 @@
resetDrawingStateBufferInfo();
setTransactionFlags(eTransactionNeeded);
mDrawingState.bufferSurfaceFrameTX = nullptr;
- setFrameTimelineVsyncForBufferlessTransaction(info, postTime);
+ setFrameTimelineVsyncForBufferlessTransaction(info, postTime, gameMode);
return true;
} else {
// release sideband stream if it exists and a non null buffer is being set
if (mDrawingState.sidebandStream != nullptr) {
- setSidebandStream(nullptr, info, postTime);
+ setSidebandStream(nullptr, info, postTime, gameMode);
}
}
@@ -3152,9 +2910,9 @@
const int32_t layerId = getSequence();
mFlinger->mTimeStats->setPostTime(layerId, mDrawingState.frameNumber, getName().c_str(),
- mOwnerUid, postTime, getGameMode());
+ mOwnerUid, postTime, gameMode);
- setFrameTimelineVsyncForBufferTransaction(info, postTime);
+ setFrameTimelineVsyncForBufferTransaction(info, postTime, gameMode);
if (bufferData.dequeueTime > 0) {
const uint64_t bufferId = mDrawingState.buffer->getId();
@@ -3309,7 +3067,7 @@
}
bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream, const FrameTimelineInfo& info,
- nsecs_t postTime) {
+ nsecs_t postTime, gui::GameMode gameMode) {
if (mDrawingState.sidebandStream == sidebandStream) return false;
if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) {
@@ -3324,7 +3082,7 @@
releasePreviousBuffer();
resetDrawingStateBufferInfo();
mDrawingState.bufferSurfaceFrameTX = nullptr;
- setFrameTimelineVsyncForBufferlessTransaction(info, postTime);
+ setFrameTimelineVsyncForBufferlessTransaction(info, postTime, gameMode);
}
setTransactionFlags(eTransactionNeeded);
if (!mSidebandStreamChanged.exchange(true)) {
@@ -3982,7 +3740,8 @@
void Layer::onCompositionPresented(const DisplayDevice* display,
const std::shared_ptr<FenceTime>& glDoneFence,
const std::shared_ptr<FenceTime>& presentFence,
- const CompositorTiming& compositorTiming) {
+ const CompositorTiming& compositorTiming,
+ gui::GameMode gameMode) {
// mFrameLatencyNeeded is true when a new frame was latched for the
// composition.
if (!mBufferInfo.mFrameLatencyNeeded) return;
@@ -4024,12 +3783,12 @@
}
if (display) {
- const Fps refreshRate = display->refreshRateSelector().getActiveMode().fps;
+ const auto activeMode = display->refreshRateSelector().getActiveMode();
+ const Fps refreshRate = activeMode.fps;
const std::optional<Fps> renderRate =
mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
const auto vote = frameRateToSetFrameRateVotePayload(getFrameRateForLayerTree());
- const auto gameMode = getGameMode();
if (presentFence->isValid()) {
mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
@@ -4045,7 +3804,12 @@
mFlinger->getHwComposer().getPresentTimestamp(*displayId);
const nsecs_t now = systemTime(CLOCK_MONOTONIC);
- const nsecs_t vsyncPeriod = display->getVsyncPeriodFromHWC();
+ const nsecs_t vsyncPeriod =
+ mFlinger->getHwComposer()
+ .getDisplayVsyncPeriod(*displayId)
+ .value_opt()
+ .value_or(activeMode.modePtr->getVsyncRate().getPeriodNsecs());
+
const nsecs_t actualPresentTime = now - ((now - presentTimestamp) % vsyncPeriod);
mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
@@ -4228,13 +3992,6 @@
return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
}
-void Layer::setTransformHintLegacy(ui::Transform::RotationFlags displayTransformHint) {
- mTransformHintLegacy = getFixedTransformHint();
- if (mTransformHintLegacy == ui::Transform::ROT_INVALID) {
- mTransformHintLegacy = displayTransformHint;
- }
-}
-
const std::shared_ptr<renderengine::ExternalTexture>& Layer::getExternalTexture() const {
return mBufferInfo.mBuffer;
}
@@ -4313,38 +4070,6 @@
}
}
-void Layer::updateMetadataSnapshot(const LayerMetadata& parentMetadata) {
- mSnapshot->layerMetadata = parentMetadata;
- mSnapshot->layerMetadata.merge(mDrawingState.metadata);
- for (const sp<Layer>& child : mDrawingChildren) {
- child->updateMetadataSnapshot(mSnapshot->layerMetadata);
- }
-}
-
-void Layer::updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
- std::unordered_set<Layer*>& visited) {
- if (visited.find(this) != visited.end()) {
- ALOGW("Cycle containing layer %s detected in z-order relatives", getDebugName());
- return;
- }
- visited.insert(this);
-
- mSnapshot->relativeLayerMetadata = relativeLayerMetadata;
-
- if (mDrawingState.zOrderRelatives.empty()) {
- return;
- }
- LayerMetadata childRelativeLayerMetadata = mSnapshot->relativeLayerMetadata;
- childRelativeLayerMetadata.merge(mSnapshot->layerMetadata);
- for (wp<Layer> weakRelative : mDrawingState.zOrderRelatives) {
- sp<Layer> relative = weakRelative.promote();
- if (!relative) {
- continue;
- }
- relative->updateRelativeMetadataSnapshot(childRelativeLayerMetadata, visited);
- }
-}
-
bool Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
TrustedPresentationListener const& listener) {
bool hadTrustedPresentationListener = hasTrustedPresentationListener();
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 52f169e..f6eed63 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -312,7 +312,7 @@
bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */,
const BufferData& /* bufferData */, nsecs_t /* postTime */,
nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
- const FrameTimelineInfo& /*info*/);
+ const FrameTimelineInfo& /*info*/, gui::GameMode gameMode);
void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/);
bool setDataspace(ui::Dataspace /*dataspace*/);
bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio);
@@ -322,25 +322,18 @@
bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/);
bool setApi(int32_t /*api*/);
bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/,
- const FrameTimelineInfo& /* info*/, nsecs_t /* postTime */);
+ const FrameTimelineInfo& /* info*/, nsecs_t /* postTime */,
+ gui::GameMode gameMode);
bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& /*handles*/,
bool willPresent);
virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace)
REQUIRES(mFlinger->mStateLock);
virtual bool setColorSpaceAgnostic(const bool agnostic);
virtual bool setDimmingEnabled(const bool dimmingEnabled);
- virtual bool setDefaultFrameRateCompatibility(FrameRateCompatibility compatibility);
- virtual bool setFrameRateSelectionPriority(int32_t priority);
virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint);
void setAutoRefresh(bool /* autoRefresh */);
bool setDropInputMode(gui::DropInputMode);
- // If the variable is not set on the layer, it traverses up the tree to inherit the frame
- // rate priority from its parent.
- virtual int32_t getFrameRateSelectionPriority() const;
- //
- virtual FrameRateCompatibility getDefaultFrameRateCompatibility() const;
- //
ui::Dataspace getDataSpace() const;
virtual bool isFrontBuffered() const;
@@ -439,7 +432,7 @@
void onCompositionPresented(const DisplayDevice*,
const std::shared_ptr<FenceTime>& /*glDoneFence*/,
const std::shared_ptr<FenceTime>& /*presentFence*/,
- const CompositorTiming&);
+ const CompositorTiming&, gui::GameMode gameMode);
// If a buffer was replaced this frame, release the former buffer
void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/);
@@ -694,14 +687,9 @@
*/
void addToCurrentState();
- /*
- * Sets display transform hint on BufferLayerConsumer.
- */
- void updateTransformHint(ui::Transform::RotationFlags);
inline const State& getDrawingState() const { return mDrawingState; }
inline State& getDrawingState() { return mDrawingState; }
- void miniDumpLegacy(std::string& result, const DisplayDevice&) const;
void miniDump(std::string& result, const frontend::LayerSnapshot&, const DisplayDevice&) const;
void dumpFrameStats(std::string& result) const;
void dumpOffscreenDebugInfo(std::string& result) const;
@@ -788,15 +776,11 @@
*/
Rect getCroppedBufferSize(const Layer::State& s) const;
- bool setFrameRate(FrameRate::FrameRateVote);
- bool setFrameRateCategory(FrameRateCategory, bool smoothSwitchOnly);
-
- bool setFrameRateSelectionStrategy(FrameRateSelectionStrategy);
-
virtual void setFrameTimelineInfoForBuffer(const FrameTimelineInfo& /*info*/) {}
- void setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, nsecs_t postTime);
+ void setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, nsecs_t postTime,
+ gui::GameMode gameMode);
void setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info,
- nsecs_t postTime);
+ nsecs_t postTime, gui::GameMode gameMode);
void addSurfaceFrameDroppedForBuffer(std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame,
nsecs_t dropTime);
@@ -805,11 +789,12 @@
nsecs_t currentLatchTime);
std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForTransaction(
- const FrameTimelineInfo& info, nsecs_t postTime);
+ const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode);
std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForBuffer(
- const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName);
+ const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName,
+ gui::GameMode gameMode);
void setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, nsecs_t postTime,
- std::string debugName);
+ std::string debugName, gui::GameMode gameMode);
bool setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
TrustedPresentationListener const& listener);
@@ -832,13 +817,6 @@
*/
bool hasInputInfo() const;
- // Sets the gui::GameMode for the tree rooted at this layer. A layer in the tree inherits this
- // gui::GameMode unless it (or an ancestor) has GAME_MODE_METADATA.
- void setGameModeForTree(gui::GameMode);
-
- void setGameMode(gui::GameMode gameMode) { mGameMode = gameMode; }
- gui::GameMode getGameMode() const { return mGameMode; }
-
virtual uid_t getOwnerUid() const { return mOwnerUid; }
pid_t getOwnerPid() { return mOwnerPid; }
@@ -899,9 +877,6 @@
// CompositionEngine to create a single path for composing layers.
void updateSnapshot(bool updateGeometry);
void updateChildrenSnapshots(bool updateGeometry);
- void updateMetadataSnapshot(const LayerMetadata& parentMetadata);
- void updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
- std::unordered_set<Layer*>& visited);
sp<Layer> getClonedFrom() const {
return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr;
}
@@ -912,19 +887,9 @@
void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
const sp<GraphicBuffer>& buffer, uint64_t framenumber,
const sp<Fence>& releaseFence);
- 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(),
- .bounds = getBounds(),
- .transform = getTransform(),
- .setFrameRateVote = getFrameRateForLayerTree(),
- .frameRateSelectionPriority = getFrameRateSelectionPriority(),
- .isSmallDirty = mSmallDirty,
- .isFrontBuffered = isFrontBuffered()};
- };
bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; }
void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) {
mTransformHint = transformHint;
@@ -1135,7 +1100,6 @@
LayerVector makeChildrenTraversalList(LayerVector::StateSet,
const std::vector<Layer*>& layersInTree);
- void updateTreeHasFrameRateVote();
bool propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren,
bool* transactionNeeded);
void setZOrderRelativeOf(const wp<Layer>& relativeOf);
@@ -1244,13 +1208,11 @@
bool findInHierarchy(const sp<Layer>&);
- void setTransformHintLegacy(ui::Transform::RotationFlags);
void releasePreviousBuffer();
void resetDrawingStateBufferInfo();
// Transform hint provided to the producer. This must be accessed holding
// the mStateLock.
- ui::Transform::RotationFlags mTransformHintLegacy = ui::Transform::ROT_0;
std::optional<ui::Transform::RotationFlags> mTransformHint = std::nullopt;
ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
index f430526..2b9e88c 100644
--- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -28,7 +28,6 @@
virtual void requestHardwareVsync(PhysicalDisplayId, bool enabled) = 0;
virtual void requestDisplayModes(std::vector<display::DisplayModeRequest>) = 0;
virtual void kernelTimerChanged(bool expired) = 0;
- virtual void triggerOnFrameRateOverridesChanged() = 0;
virtual void onChoreographerAttached() = 0;
virtual void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>,
Fps renderRate) = 0;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 4fd4c0e..64b85c0 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -109,12 +109,12 @@
LayerHistory::~LayerHistory() = default;
-void LayerHistory::registerLayer(Layer* layer, bool contentDetectionEnabled) {
+void LayerHistory::registerLayer(Layer* layer, bool contentDetectionEnabled,
+ FrameRateCompatibility frameRateCompatibility) {
std::lock_guard lock(mLock);
LOG_ALWAYS_FATAL_IF(findLayer(layer->getSequence()).first != LayerStatus::NotFound,
"%s already registered", layer->getName().c_str());
- LayerVoteType type =
- getVoteType(layer->getDefaultFrameRateCompatibility(), contentDetectionEnabled);
+ LayerVoteType type = getVoteType(frameRateCompatibility, contentDetectionEnabled);
auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
// The layer can be placed on either map, it is assumed that partitionLayers() will be called
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index c09f148..e3babba 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -51,7 +51,8 @@
~LayerHistory();
// Layers are unregistered when the weak reference expires.
- void registerLayer(Layer*, bool contentDetectionEnabled);
+ void registerLayer(Layer*, bool contentDetectionEnabled,
+ FrameRateCompatibility frameRateCompatibility);
// Sets the display size. Client is responsible for synchronization.
void setDisplayArea(uint32_t displayArea) { mDisplayArea = displayArea; }
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index a1a60e3..dbc458c 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -595,6 +595,12 @@
return true;
}
+ if (FlagManager::getInstance().view_set_requested_frame_rate_mrr() &&
+ category == FrameRateCategory::NoPreference && vote.rate.isValid() &&
+ vote.type == FrameRateCompatibility::ExactOrMultiple) {
+ return true;
+ }
+
return false;
}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 6a67ac5..2e1f938 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -188,12 +188,13 @@
postMessage(sp<ConfigureHandler>::make(mCompositor));
}
-void MessageQueue::scheduleFrame() {
+void MessageQueue::scheduleFrame(Duration workDurationSlack) {
SFTRACE_CALL();
std::lock_guard lock(mVsync.mutex);
+ const auto workDuration = Duration(mVsync.workDuration.get() - workDurationSlack);
mVsync.scheduledFrameTimeOpt =
- mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
+ mVsync.registration->schedule({.workDuration = workDuration.ns(),
.readyDuration = 0,
.lastVsync = mVsync.lastCallbackTime.ns()});
}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index c5fc371..ba1efbe 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -74,7 +74,7 @@
virtual void postMessage(sp<MessageHandler>&&) = 0;
virtual void postMessageDelayed(sp<MessageHandler>&&, nsecs_t uptimeDelay) = 0;
virtual void scheduleConfigure() = 0;
- virtual void scheduleFrame() = 0;
+ virtual void scheduleFrame(Duration workDurationSlack = Duration::fromNs(0)) = 0;
virtual std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const = 0;
};
@@ -149,7 +149,7 @@
void postMessageDelayed(sp<MessageHandler>&&, nsecs_t uptimeDelay) override;
void scheduleConfigure() override;
- void scheduleFrame() override;
+ void scheduleFrame(Duration workDurationSlack = Duration::fromNs(0)) override;
std::optional<scheduler::ScheduleResult> getScheduledFrameResult() const override;
};
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 6f9c146..a398c01 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -210,6 +210,8 @@
// within the timeout of DisplayPowerTimer.
bool powerOnImminent = false;
+ bool shouldEmitEvent() const { return !idle; }
+
bool operator==(GlobalSignals other) const {
return touch == other.touch && idle == other.idle &&
powerOnImminent == other.powerOnImminent;
@@ -321,7 +323,10 @@
RefreshRateSelector(const RefreshRateSelector&) = delete;
RefreshRateSelector& operator=(const RefreshRateSelector&) = delete;
- const DisplayModes& displayModes() const { return mDisplayModes; }
+ DisplayModes displayModes() const {
+ std::lock_guard lock(mLock);
+ return mDisplayModes;
+ }
// Returns whether switching modes (refresh rate or resolution) is possible.
// TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 0cf7bdc..566bb8e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -122,6 +122,12 @@
demotePacesetterDisplay(kPromotionParams);
promotePacesetterDisplay(pacesetterId, kPromotionParams);
+
+ // Cancel the pending refresh rate change, if any, before updating the phase configuration.
+ mVsyncModulator->cancelRefreshRateChange();
+
+ mVsyncConfiguration->reset();
+ updatePhaseConfiguration(pacesetterSelectorPtr()->getActiveMode().fps);
}
void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
@@ -403,14 +409,20 @@
eventThreadFor(Cycle::Render).enableSyntheticVsync(enable);
}
-void Scheduler::onFrameRateOverridesChanged(Cycle cycle, PhysicalDisplayId displayId) {
- const bool supportsFrameRateOverrideByContent =
- pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent();
+void Scheduler::onFrameRateOverridesChanged() {
+ const auto [pacesetterId, supportsFrameRateOverrideByContent] = [this] {
+ std::scoped_lock lock(mDisplayLock);
+ const auto pacesetterOpt = pacesetterDisplayLocked();
+ LOG_ALWAYS_FATAL_IF(!pacesetterOpt);
+ const Display& pacesetter = *pacesetterOpt;
+ return std::make_pair(FTL_FAKE_GUARD(kMainThreadContext, *mPacesetterDisplayId),
+ pacesetter.selectorPtr->supportsAppFrameRateOverrideByContent());
+ }();
std::vector<FrameRateOverride> overrides =
mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent);
- eventThreadFor(cycle).onFrameRateOverridesChanged(displayId, std::move(overrides));
+ eventThreadFor(Cycle::Render).onFrameRateOverridesChanged(pacesetterId, std::move(overrides));
}
void Scheduler::onHdcpLevelsChanged(Cycle cycle, PhysicalDisplayId displayId,
@@ -418,50 +430,49 @@
eventThreadFor(cycle).onHdcpLevelsChanged(displayId, connectedLevel, maxLevel);
}
-void Scheduler::onPrimaryDisplayModeChanged(Cycle cycle, const FrameRateMode& mode) {
- {
+bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode) {
+ const bool isPacesetter =
+ FTL_FAKE_GUARD(kMainThreadContext,
+ (std::scoped_lock(mDisplayLock), displayId == mPacesetterDisplayId));
+
+ if (isPacesetter) {
std::lock_guard<std::mutex> lock(mPolicyLock);
- // Cache the last reported modes for primary display.
- mPolicy.cachedModeChangedParams = {cycle, mode};
+ mPolicy.emittedModeOpt = mode;
// Invalidate content based refresh rate selection so it could be calculated
// again for the new refresh rate.
mPolicy.contentRequirements.clear();
}
- onNonPrimaryDisplayModeChanged(cycle, mode);
-}
-void Scheduler::dispatchCachedReportedMode() {
- // Check optional fields first.
- if (!mPolicy.modeOpt) {
- ALOGW("No mode ID found, not dispatching cached mode.");
- return;
- }
- if (!mPolicy.cachedModeChangedParams) {
- ALOGW("No mode changed params found, not dispatching cached mode.");
- return;
- }
-
- // If the mode is not the current mode, this means that a
- // mode change is in progress. In that case we shouldn't dispatch an event
- // as it will be dispatched when the current mode changes.
- if (pacesetterSelectorPtr()->getActiveMode() != mPolicy.modeOpt) {
- return;
- }
-
- // If there is no change from cached mode, there is no need to dispatch an event
- if (*mPolicy.modeOpt == mPolicy.cachedModeChangedParams->mode) {
- return;
- }
-
- mPolicy.cachedModeChangedParams->mode = *mPolicy.modeOpt;
- onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->cycle,
- mPolicy.cachedModeChangedParams->mode);
-}
-
-void Scheduler::onNonPrimaryDisplayModeChanged(Cycle cycle, const FrameRateMode& mode) {
if (hasEventThreads()) {
- eventThreadFor(cycle).onModeChanged(mode);
+ eventThreadFor(Cycle::Render).onModeChanged(mode);
+ }
+
+ return isPacesetter;
+}
+
+void Scheduler::emitModeChangeIfNeeded() {
+ if (!mPolicy.modeOpt || !mPolicy.emittedModeOpt) {
+ ALOGW("No mode change to emit");
+ return;
+ }
+
+ const auto& mode = *mPolicy.modeOpt;
+
+ if (mode != pacesetterSelectorPtr()->getActiveMode()) {
+ // A mode change is pending. The event will be emitted when the mode becomes active.
+ return;
+ }
+
+ if (mode == *mPolicy.emittedModeOpt) {
+ // The event was already emitted.
+ return;
+ }
+
+ mPolicy.emittedModeOpt = mode;
+
+ if (hasEventThreads()) {
+ eventThreadFor(Cycle::Render).onModeChanged(mode);
}
}
@@ -483,14 +494,6 @@
refreshRate.getPeriod());
}
-void Scheduler::resetPhaseConfiguration(Fps refreshRate) {
- // Cancel the pending refresh rate change, if any, before updating the phase configuration.
- mVsyncModulator->cancelRefreshRateChange();
-
- mVsyncConfiguration->reset();
- updatePhaseConfiguration(refreshRate);
-}
-
void Scheduler::setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode powerMode) {
mRefreshRateStats->setPowerMode(powerMode);
}
@@ -659,11 +662,12 @@
}
}
-void Scheduler::registerLayer(Layer* layer) {
+void Scheduler::registerLayer(Layer* layer, FrameRateCompatibility frameRateCompatibility) {
// If the content detection feature is off, we still keep the layer history,
// since we use it for other features (like Frame Rate API), so layers
// still need to be registered.
- mLayerHistory.registerLayer(layer, mFeatures.test(Feature::kContentDetection));
+ mLayerHistory.registerLayer(layer, mFeatures.test(Feature::kContentDetection),
+ frameRateCompatibility);
}
void Scheduler::deregisterLayer(Layer* layer) {
@@ -871,22 +875,19 @@
mRefreshRateStats->dump(dumper.out());
dumper.eol();
- {
- utils::Dumper::Section section(dumper, "Frame Targeting"sv);
+ std::scoped_lock lock(mDisplayLock);
+ ftl::FakeGuard guard(kMainThreadContext);
- std::scoped_lock lock(mDisplayLock);
- ftl::FakeGuard guard(kMainThreadContext);
+ for (const auto& [id, display] : mDisplays) {
+ utils::Dumper::Section
+ section(dumper,
+ id == mPacesetterDisplayId
+ ? ftl::Concat("Pacesetter Display ", id.value).c_str()
+ : ftl::Concat("Follower Display ", id.value).c_str());
- for (const auto& [id, display] : mDisplays) {
- utils::Dumper::Section
- section(dumper,
- id == mPacesetterDisplayId
- ? ftl::Concat("Pacesetter Display ", id.value).c_str()
- : ftl::Concat("Follower Display ", id.value).c_str());
-
- display.targeterPtr->dump(dumper);
- dumper.eol();
- }
+ display.selectorPtr->dump(dumper);
+ display.targeterPtr->dump(dumper);
+ dumper.eol();
}
}
@@ -907,9 +908,13 @@
}
}
-bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
- std::scoped_lock lock(mPolicyLock);
- return updateFrameRateOverridesLocked(consideredSignals, displayRefreshRate);
+void Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
+ const bool changed = (std::scoped_lock(mPolicyLock),
+ updateFrameRateOverridesLocked(consideredSignals, displayRefreshRate));
+
+ if (changed) {
+ onFrameRateOverridesChanged();
+ }
}
bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals,
@@ -1129,7 +1134,8 @@
for (auto& [id, choice] : modeChoices) {
modeRequests.emplace_back(
display::DisplayModeRequest{.mode = std::move(choice.mode),
- .emitEvent = !choice.consideredSignals.idle});
+ .emitEvent = choice.consideredSignals
+ .shouldEmitEvent()});
}
if (!FlagManager::getInstance().vrr_bugfix_dropped_frame()) {
@@ -1139,12 +1145,10 @@
if (mPolicy.modeOpt != modeOpt) {
mPolicy.modeOpt = modeOpt;
refreshRateChanged = true;
- } else {
- // We don't need to change the display mode, but we might need to send an event
- // about a mode change, since it was suppressed if previously considered idle.
- if (!consideredSignals.idle) {
- dispatchCachedReportedMode();
- }
+ } else if (consideredSignals.shouldEmitEvent()) {
+ // The mode did not change, but we may need to emit if DisplayModeRequest::emitEvent was
+ // previously false.
+ emitModeChangeIfNeeded();
}
}
if (refreshRateChanged) {
@@ -1157,7 +1161,7 @@
updateFrameRateOverridesLocked(consideredSignals, mPolicy.modeOpt->fps);
}
if (frameRateOverridesChanged) {
- mSchedulerCallback.triggerOnFrameRateOverridesChanged();
+ onFrameRateOverridesChanged();
}
return consideredSignals;
}
@@ -1257,6 +1261,8 @@
} else {
mFrameRateOverrideMappings.setGameModeRefreshRateForUid(frameRateOverride);
}
+
+ onFrameRateOverridesChanged();
}
void Scheduler::setGameDefaultFrameRateForUid(FrameRateOverride frameRateOverride) {
@@ -1275,6 +1281,7 @@
}
mFrameRateOverrideMappings.setPreferredRefreshRateForUid(frameRateOverride);
+ onFrameRateOverridesChanged();
}
void Scheduler::updateSmallAreaDetection(
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 94583db..88f0e94 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -154,13 +154,11 @@
void dispatchHotplugError(int32_t errorCode);
- void onPrimaryDisplayModeChanged(Cycle, const FrameRateMode&) EXCLUDES(mPolicyLock);
- void onNonPrimaryDisplayModeChanged(Cycle, const FrameRateMode&);
+ // Returns true if the PhysicalDisplayId is the pacesetter.
+ bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&) EXCLUDES(mPolicyLock);
void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext);
- void onFrameRateOverridesChanged(Cycle, PhysicalDisplayId);
-
void onHdcpLevelsChanged(Cycle, PhysicalDisplayId, int32_t, int32_t);
// Modifies work duration in the event thread.
@@ -190,7 +188,6 @@
}
void updatePhaseConfiguration(Fps);
- void resetPhaseConfiguration(Fps) REQUIRES(kMainThreadContext);
const VsyncConfiguration& getVsyncConfiguration() const { return *mVsyncConfiguration; }
@@ -222,7 +219,7 @@
REQUIRES(kMainThreadContext);
// Layers are registered on creation, and unregistered when the weak reference expires.
- void registerLayer(Layer*);
+ void registerLayer(Layer*, FrameRateCompatibility);
void recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
nsecs_t now, LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
void setModeChangePending(bool pending);
@@ -326,7 +323,7 @@
return mLayerHistory.getLayerFramerate(now, id);
}
- bool updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) EXCLUDES(mPolicyLock);
+ void updateFrameRateOverrides(GlobalSignals, Fps displayRefreshRate) EXCLUDES(mPolicyLock);
// Returns true if the small dirty detection is enabled for the appId.
bool supportSmallDirtyDetection(int32_t appId) {
@@ -450,6 +447,9 @@
bool updateFrameRateOverridesLocked(GlobalSignals, Fps displayRefreshRate)
REQUIRES(mPolicyLock);
+
+ void onFrameRateOverridesChanged();
+
void updateAttachedChoreographers(const surfaceflinger::frontend::LayerHierarchy&,
Fps displayRefreshRate);
int updateAttachedChoreographersInternal(const surfaceflinger::frontend::LayerHierarchy&,
@@ -457,7 +457,7 @@
void updateAttachedChoreographersFrameRate(const surfaceflinger::frontend::RequestedLayerState&,
Fps fps) EXCLUDES(mChoreographerLock);
- void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
+ void emitModeChangeIfNeeded() REQUIRES(mPolicyLock) EXCLUDES(mDisplayLock);
// IEventThreadCallback overrides
bool throttleVsync(TimePoint, uid_t) override;
@@ -583,13 +583,8 @@
// Chosen display mode.
ftl::Optional<FrameRateMode> modeOpt;
- struct ModeChangedParams {
- Cycle cycle;
- FrameRateMode mode;
- };
-
- // Parameters for latest dispatch of mode change event.
- std::optional<ModeChangedParams> cachedModeChangedParams;
+ // Display mode of latest emitted event.
+ std::optional<FrameRateMode> emittedModeOpt;
} mPolicy GUARDED_BY(mPolicyLock);
std::mutex mChoreographerLock;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index ee7eda1..4a7cff5 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -89,7 +89,9 @@
}
bool VSyncPredictor::validate(nsecs_t timestamp) const {
+ SFTRACE_CALL();
if (mLastTimestampIndex < 0 || mTimestamps.empty()) {
+ SFTRACE_INSTANT("timestamp valid (first)");
return true;
}
@@ -98,7 +100,11 @@
(timestamp - aValidTimestamp) % idealPeriod() * kMaxPercent / idealPeriod();
if (percent >= kOutlierTolerancePercent &&
percent <= (kMaxPercent - kOutlierTolerancePercent)) {
- SFTRACE_FORMAT_INSTANT("timestamp is not aligned with model");
+ SFTRACE_FORMAT_INSTANT("timestamp not aligned with model. aValidTimestamp %.2fms ago"
+ ", timestamp %.2fms ago, idealPeriod=%.2 percent=%d",
+ (mClock->now() - aValidTimestamp) / 1e6f,
+ (mClock->now() - timestamp) / 1e6f,
+ idealPeriod() / 1e6f, percent);
return false;
}
@@ -148,7 +154,10 @@
// Add the timestamp to mTimestamps before clearing it so we could
// update mKnownTimestamp based on the new timestamp.
mTimestamps.push_back(timestamp);
- clearTimestamps();
+
+ // Do not clear timelines as we don't want to break the phase while
+ // we are still learning.
+ clearTimestamps(/* clearTimelines */ false);
} else if (!mTimestamps.empty()) {
mKnownTimestamp =
std::max(timestamp, *std::max_element(mTimestamps.begin(), mTimestamps.end()));
@@ -235,7 +244,7 @@
if (CC_UNLIKELY(bottom == 0)) {
it->second = {idealPeriod(), 0};
- clearTimestamps();
+ clearTimestamps(/* clearTimelines */ true);
return false;
}
@@ -245,7 +254,7 @@
auto const percent = std::abs(anticipatedPeriod - idealPeriod()) * kMaxPercent / idealPeriod();
if (percent >= kOutlierTolerancePercent) {
it->second = {idealPeriod(), 0};
- clearTimestamps();
+ clearTimestamps(/* clearTimelines */ true);
return false;
}
@@ -425,6 +434,9 @@
timeout ? std::to_string(timeout->timeoutNs).c_str() : "N/A");
std::lock_guard lock(mMutex);
+ // do not clear the timelines on VRR displays if we didn't change the mode
+ const bool isVrr = modePtr->getVrrConfig().has_value();
+ const bool clearTimelines = !isVrr || mDisplayModePtr->getId() != modePtr->getId();
mDisplayModePtr = modePtr;
mNumVsyncsForFrame = numVsyncsPerFrame(mDisplayModePtr);
traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs());
@@ -438,8 +450,10 @@
mRateMap[idealPeriod()] = {idealPeriod(), 0};
}
- mTimelines.clear();
- clearTimestamps();
+ if (clearTimelines) {
+ mTimelines.clear();
+ }
+ clearTimestamps(clearTimelines);
}
Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
@@ -452,7 +466,7 @@
const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
const auto threshold = currentPeriod / 2;
- const auto minFramePeriod = minFramePeriodLocked().ns();
+ const auto minFramePeriod = minFramePeriodLocked();
auto prev = lastConfirmedPresentTime.ns();
for (auto& current : mPastExpectedPresentTimes) {
@@ -463,10 +477,10 @@
1e6f);
}
- const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod;
+ const auto minPeriodViolation = current.ns() - prev + threshold < minFramePeriod.ns();
if (minPeriodViolation) {
SFTRACE_NAME("minPeriodViolation");
- current = TimePoint::fromNs(prev + minFramePeriod);
+ current = TimePoint::fromNs(prev + minFramePeriod.ns());
prev = current.ns();
} else {
break;
@@ -477,7 +491,7 @@
const auto phase = Duration(mPastExpectedPresentTimes.back() - expectedPresentTime);
if (phase > 0ns) {
for (auto& timeline : mTimelines) {
- timeline.shiftVsyncSequence(phase);
+ timeline.shiftVsyncSequence(phase, minFramePeriod);
}
mPastExpectedPresentTimes.clear();
return phase;
@@ -556,15 +570,19 @@
return mRateMap.find(idealPeriod())->second;
}
-void VSyncPredictor::clearTimestamps() {
- SFTRACE_CALL();
+void VSyncPredictor::clearTimestamps(bool clearTimelines) {
+ SFTRACE_FORMAT("%s: clearTimelines=%d", __func__, clearTimelines);
if (!mTimestamps.empty()) {
auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end());
if (mKnownTimestamp) {
mKnownTimestamp = std::max(*mKnownTimestamp, maxRb);
+ SFTRACE_FORMAT_INSTANT("mKnownTimestamp was %.2fms ago",
+ (mClock->now() - *mKnownTimestamp) / 1e6f);
} else {
mKnownTimestamp = maxRb;
+ SFTRACE_FORMAT_INSTANT("mKnownTimestamp (maxRb) was %.2fms ago",
+ (mClock->now() - *mKnownTimestamp) / 1e6f);
}
mTimestamps.clear();
@@ -575,7 +593,7 @@
if (mTimelines.empty()) {
mLastCommittedVsync = TimePoint::fromNs(0);
mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
- } else {
+ } else if (clearTimelines) {
while (mTimelines.size() > 1) {
mTimelines.pop_front();
}
@@ -596,9 +614,10 @@
}
void VSyncPredictor::resetModel() {
+ SFTRACE_CALL();
std::lock_guard lock(mMutex);
mRateMap[idealPeriod()] = {idealPeriod(), 0};
- clearTimestamps();
+ clearTimestamps(/* clearTimelines */ true);
}
void VSyncPredictor::dump(std::string& result) const {
@@ -778,8 +797,15 @@
return vsyncSequence.seq % divisor == 0;
}
-void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase) {
+void VSyncPredictor::VsyncTimeline::shiftVsyncSequence(Duration phase, Period minFramePeriod) {
if (mLastVsyncSequence) {
+ const auto renderRate = mRenderRateOpt.value_or(Fps::fromPeriodNsecs(mIdealPeriod.ns()));
+ const auto threshold = mIdealPeriod.ns() / 2;
+ if (renderRate.getPeriodNsecs() - phase.ns() + threshold >= minFramePeriod.ns()) {
+ SFTRACE_FORMAT_INSTANT("Not-Adjusting vsync by %.2f",
+ static_cast<float>(phase.ns()) / 1e6f);
+ return;
+ }
SFTRACE_FORMAT_INSTANT("adjusting vsync by %.2f", static_cast<float>(phase.ns()) / 1e6f);
mLastVsyncSequence->vsyncTime += phase.ns();
}
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 9e1c90b..2df3d04 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -104,7 +104,7 @@
void freeze(TimePoint lastVsync);
std::optional<TimePoint> validUntil() const { return mValidUntil; }
bool isVSyncInPhase(Model, nsecs_t vsync, Fps frameRate);
- void shiftVsyncSequence(Duration phase);
+ void shiftVsyncSequence(Duration phase, Period minFramePeriod);
void setRenderRate(std::optional<Fps> renderRateOpt) { mRenderRateOpt = renderRateOpt; }
enum class VsyncOnTimeline {
@@ -127,7 +127,7 @@
VSyncPredictor(VSyncPredictor const&) = delete;
VSyncPredictor& operator=(VSyncPredictor const&) = delete;
- void clearTimestamps() REQUIRES(mMutex);
+ void clearTimestamps(bool clearTimelines) REQUIRES(mMutex);
const std::unique_ptr<Clock> mClock;
const PhysicalDisplayId mId;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 2455822..b974cd2 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -140,7 +140,9 @@
std::lock_guard lock(mMutex);
mLastHwVsync.reset();
- if (!mSupportKernelIdleTimer && mTracker.isCurrentMode(modePtr) && !force) {
+ // kernel idle timer is not applicable for VRR
+ const bool supportKernelIdleTimer = mSupportKernelIdleTimer && !modePtr->getVrrConfig();
+ if (!supportKernelIdleTimer && mTracker.isCurrentMode(modePtr) && !force) {
endPeriodTransition();
setIgnorePresentFencesInternal(false);
mMoreSamplesNeeded = false;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index 38cb446..2185bb0 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -55,15 +55,11 @@
std::optional<TimePoint> earliestPresentTime() const { return mEarliestPresentTime; }
- // The time of the VSYNC that preceded this frame. See `presentFenceForPastVsync` for details.
- TimePoint pastVsyncTime(Period minFramePeriod) const;
-
- // Equivalent to `presentFenceForPastVsync` unless running N VSYNCs ahead.
- const FenceTimePtr& presentFenceForPreviousFrame() const {
- return mPresentFences.front().fenceTime;
- }
+ // Equivalent to `expectedSignaledPresentFence` unless running N VSYNCs ahead.
+ const FenceTimePtr& presentFenceForPreviousFrame() const;
bool isFramePending() const { return mFramePending; }
+ bool wouldBackpressureHwc() const { return mWouldBackpressureHwc; }
bool didMissFrame() const { return mFrameMissed; }
bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; }
FrameTime lastSignaledFrameTime() const { return mLastSignaledFrameTime; }
@@ -72,7 +68,7 @@
explicit FrameTarget(const std::string& displayLabel);
~FrameTarget() = default;
- bool wouldPresentEarly(Period minFramePeriod) const;
+ bool wouldPresentEarly(Period vsyncPeriod, Period minFramePeriod) const;
// Equivalent to `pastVsyncTime` unless running N VSYNCs ahead.
TimePoint previousFrameVsyncTime(Period minFramePeriod) const {
@@ -81,8 +77,7 @@
void addFence(sp<Fence> presentFence, FenceTimePtr presentFenceTime,
TimePoint expectedPresentTime) {
- mFenceWithFenceTimes.next() = {std::move(presentFence), presentFenceTime,
- expectedPresentTime};
+ mPresentFences.next() = {std::move(presentFence), presentFenceTime, expectedPresentTime};
}
VsyncId mVsyncId;
@@ -94,8 +89,9 @@
TracedOrdinal<bool> mFrameMissed;
TracedOrdinal<bool> mHwcFrameMissed;
TracedOrdinal<bool> mGpuFrameMissed;
+ bool mWouldBackpressureHwc = false;
- struct FenceWithFenceTime {
+ struct PresentFence {
sp<Fence> fence = Fence::NO_FENCE;
FenceTimePtr fenceTime = FenceTime::NO_FENCE;
TimePoint expectedPresentTime = TimePoint();
@@ -106,9 +102,10 @@
// VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the
// `presentFenceForPreviousFrame` if running N VSYNCs ahead, but the one that should have been
// signaled by now (unless that frame missed).
- FenceWithFenceTime presentFenceForPastVsync(Period minFramePeriod) const;
- std::array<FenceWithFenceTime, 2> mPresentFences;
- utils::RingBuffer<FenceWithFenceTime, 5> mFenceWithFenceTimes;
+ std::pair<bool /* wouldBackpressure */, PresentFence> expectedSignaledPresentFence(
+ Period vsyncPeriod, Period minFramePeriod) const;
+ std::array<PresentFence, 2> mPresentFencesLegacy;
+ utils::RingBuffer<PresentFence, 5> mPresentFences;
FrameTime mLastSignaledFrameTime;
@@ -120,19 +117,6 @@
static_assert(N > 1);
return expectedFrameDuration() > (N - 1) * minFramePeriod;
}
-
- FenceWithFenceTime pastVsyncTimePtr() const {
- FenceWithFenceTime pastFenceWithFenceTime;
- for (size_t i = 0; i < mFenceWithFenceTimes.size(); i++) {
- const auto& fenceWithFenceTime = mFenceWithFenceTimes[i];
- // TODO(b/354007767) Fix the below condition to avoid frame drop
- if (fenceWithFenceTime.expectedPresentTime > mFrameBeginTime) {
- return pastFenceWithFenceTime;
- }
- pastFenceWithFenceTime = fenceWithFenceTime;
- }
- return pastFenceWithFenceTime;
- }
};
// Computes a display's per-frame metrics about past/upcoming targeting of present deadlines.
@@ -155,7 +139,7 @@
void beginFrame(const BeginFrameArgs&, const IVsyncSource&);
- std::optional<TimePoint> computeEarliestPresentTime(Period minFramePeriod,
+ std::optional<TimePoint> computeEarliestPresentTime(Period vsyncPeriod, Period minFramePeriod,
Duration hwcMinWorkDuration);
// TODO(b/241285191): Merge with FrameTargeter::endFrame.
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index 1d248fb..3ee1e54 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -18,8 +18,10 @@
#include <common/trace.h>
#include <scheduler/FrameTargeter.h>
#include <scheduler/IVsyncSource.h>
+#include <utils/Log.h>
namespace android::scheduler {
+using namespace std::chrono_literals;
FrameTarget::FrameTarget(const std::string& displayLabel)
: mFramePending("PrevFramePending " + displayLabel, false),
@@ -27,32 +29,53 @@
mHwcFrameMissed("PrevHwcFrameMissed " + displayLabel, false),
mGpuFrameMissed("PrevGpuFrameMissed " + displayLabel, false) {}
-TimePoint FrameTarget::pastVsyncTime(Period minFramePeriod) const {
- // TODO(b/267315508): Generalize to N VSYNCs.
- const int shift = static_cast<int>(targetsVsyncsAhead<2>(minFramePeriod));
- return mExpectedPresentTime - Period::fromNs(minFramePeriod.ns() << shift);
-}
-
-FrameTarget::FenceWithFenceTime FrameTarget::presentFenceForPastVsync(Period minFramePeriod) const {
- if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
- return pastVsyncTimePtr();
+std::pair<bool /* wouldBackpressure */, FrameTarget::PresentFence>
+FrameTarget::expectedSignaledPresentFence(Period vsyncPeriod, Period minFramePeriod) const {
+ if (!FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
+ const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod));
+ return {true, mPresentFencesLegacy[i]};
}
- const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod));
- return mPresentFences[i];
+
+ bool wouldBackpressure = true;
+ auto expectedPresentTime = mExpectedPresentTime;
+ for (size_t i = mPresentFences.size(); i != 0; --i) {
+ const auto& fence = mPresentFences[i - 1];
+
+ if (fence.expectedPresentTime + minFramePeriod < expectedPresentTime - vsyncPeriod / 2) {
+ wouldBackpressure = false;
+ }
+
+ if (fence.expectedPresentTime <= mFrameBeginTime) {
+ return {wouldBackpressure, fence};
+ }
+
+ expectedPresentTime = fence.expectedPresentTime;
+ }
+ return {wouldBackpressure, PresentFence{}};
}
-bool FrameTarget::wouldPresentEarly(Period minFramePeriod) const {
- // TODO(b/241285475): Since this is called during `composite`, the calls to `targetsVsyncsAhead`
- // should use `TimePoint::now()` in case of delays since `mFrameBeginTime`.
-
- // TODO(b/267315508): Generalize to N VSYNCs.
- const bool allowNVsyncs = FlagManager::getInstance().allow_n_vsyncs_in_targeter();
- if (!allowNVsyncs && targetsVsyncsAhead<3>(minFramePeriod)) {
+bool FrameTarget::wouldPresentEarly(Period vsyncPeriod, Period minFramePeriod) const {
+ if (targetsVsyncsAhead<3>(minFramePeriod)) {
return true;
}
- const auto fence = presentFenceForPastVsync(minFramePeriod).fenceTime;
- return fence->isValid() && fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+ const auto [wouldBackpressure, fence] =
+ expectedSignaledPresentFence(vsyncPeriod, minFramePeriod);
+
+ return !wouldBackpressure ||
+ (fence.fenceTime->isValid() &&
+ fence.fenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING);
+}
+
+const FenceTimePtr& FrameTarget::presentFenceForPreviousFrame() const {
+ if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
+ if (mPresentFences.size() > 0) {
+ return mPresentFences.back().fenceTime;
+ }
+ return FenceTime::NO_FENCE;
+ }
+
+ return mPresentFencesLegacy.front().fenceTime;
}
void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& vsyncSource) {
@@ -86,27 +109,39 @@
}
if (!mSupportsExpectedPresentTime) {
- mEarliestPresentTime = computeEarliestPresentTime(minFramePeriod, args.hwcMinWorkDuration);
+ mEarliestPresentTime =
+ computeEarliestPresentTime(vsyncPeriod, minFramePeriod, args.hwcMinWorkDuration);
}
SFTRACE_FORMAT("%s %" PRId64 " vsyncIn %.2fms%s", __func__, ftl::to_underlying(args.vsyncId),
ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()),
mExpectedPresentTime == args.expectedVsyncTime ? "" : " (adjusted)");
- FenceWithFenceTime pastPresentFence = presentFenceForPastVsync(minFramePeriod);
+ const auto [wouldBackpressure, fence] =
+ expectedSignaledPresentFence(vsyncPeriod, minFramePeriod);
// In cases where the present fence is about to fire, give it a small grace period instead of
// giving up on the frame.
- //
- // TODO(b/280667110): The grace period should depend on `sfWorkDuration` and `vsyncPeriod` being
- // approximately equal, not whether backpressure propagation is enabled.
- const int graceTimeForPresentFenceMs = static_cast<int>(
- mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu));
+ const int graceTimeForPresentFenceMs = [&] {
+ const bool considerBackpressure =
+ mBackpressureGpuComposition || !mCompositionCoverage.test(CompositionCoverage::Gpu);
+
+ if (!FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
+ return static_cast<int>(considerBackpressure);
+ }
+
+ if (!wouldBackpressure || !considerBackpressure) {
+ return 0;
+ }
+
+ return static_cast<int>((std::abs(fence.expectedPresentTime.ns() - mFrameBeginTime.ns()) <=
+ Duration(1ms).ns()));
+ }();
// Pending frames may trigger backpressure propagation.
const auto& isFencePending = *isFencePendingFuncPtr;
- mFramePending = pastPresentFence.fenceTime != FenceTime::NO_FENCE &&
- isFencePending(pastPresentFence.fenceTime, graceTimeForPresentFenceMs);
+ mFramePending = fence.fenceTime != FenceTime::NO_FENCE &&
+ isFencePending(fence.fenceTime, graceTimeForPresentFenceMs);
// A frame is missed if the prior frame is still pending. If no longer pending, then we still
// count the frame as missed if the predicted present time was further in the past than when the
@@ -114,10 +149,10 @@
// than a typical frame duration, but should not be so small that it reports reasonable drift as
// a missed frame.
mFrameMissed = mFramePending || [&] {
- const nsecs_t pastPresentTime = pastPresentFence.fenceTime->getSignalTime();
+ const nsecs_t pastPresentTime = fence.fenceTime->getSignalTime();
if (pastPresentTime < 0) return false;
mLastSignaledFrameTime = {.signalTime = TimePoint::fromNs(pastPresentTime),
- .expectedPresentTime = pastPresentFence.expectedPresentTime};
+ .expectedPresentTime = fence.expectedPresentTime};
const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
return lastScheduledPresentTime.ns() < pastPresentTime - frameMissedSlop;
}();
@@ -128,11 +163,14 @@
if (mFrameMissed) mFrameMissedCount++;
if (mHwcFrameMissed) mHwcFrameMissedCount++;
if (mGpuFrameMissed) mGpuFrameMissedCount++;
+
+ mWouldBackpressureHwc = mFramePending && wouldBackpressure;
}
-std::optional<TimePoint> FrameTargeter::computeEarliestPresentTime(Period minFramePeriod,
+std::optional<TimePoint> FrameTargeter::computeEarliestPresentTime(Period vsyncPeriod,
+ Period minFramePeriod,
Duration hwcMinWorkDuration) {
- if (wouldPresentEarly(minFramePeriod)) {
+ if (wouldPresentEarly(vsyncPeriod, minFramePeriod)) {
return previousFrameVsyncTime(minFramePeriod) - hwcMinWorkDuration;
}
return {};
@@ -151,8 +189,8 @@
if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
addFence(std::move(presentFence), presentFenceTime, mExpectedPresentTime);
} else {
- mPresentFences[1] = mPresentFences[0];
- mPresentFences[0] = {std::move(presentFence), presentFenceTime, mExpectedPresentTime};
+ mPresentFencesLegacy[1] = mPresentFencesLegacy[0];
+ mPresentFencesLegacy[0] = {std::move(presentFence), presentFenceTime, mExpectedPresentTime};
}
return presentFenceTime;
}
diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
index 190d062..6f4e1f1 100644
--- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
@@ -53,12 +53,13 @@
const auto& target() const { return mTargeter.target(); }
- bool wouldPresentEarly(Period minFramePeriod) const {
- return target().wouldPresentEarly(minFramePeriod);
+ bool wouldPresentEarly(Period vsyncPeriod, Period minFramePeriod) const {
+ return target().wouldPresentEarly(vsyncPeriod, minFramePeriod);
}
- FrameTarget::FenceWithFenceTime presentFenceForPastVsync(Period minFramePeriod) const {
- return target().presentFenceForPastVsync(minFramePeriod);
+ std::pair<bool /*wouldBackpressure*/, FrameTarget::PresentFence> expectedSignaledPresentFence(
+ Period vsyncPeriod, Period minFramePeriod) const {
+ return target().expectedSignaledPresentFence(vsyncPeriod, minFramePeriod);
}
struct Frame {
@@ -173,7 +174,6 @@
}
TEST_F(FrameTargeterTest, recallsPastVsync) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{111};
TimePoint frameBeginTime(1000ms);
constexpr Fps kRefreshRate = 60_Hz;
@@ -181,16 +181,72 @@
constexpr Duration kFrameDuration = 13ms;
for (int n = 5; n-- > 0;) {
- Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
- const auto fence = frame.end();
+ FenceTimePtr fence;
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate,
+ kRefreshRate);
+ fence = frame.end();
+ }
- EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - kPeriod);
- EXPECT_EQ(presentFenceForPastVsync(kPeriod).fenceTime, fence);
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ const auto [wouldBackpressure, presentFence] =
+ expectedSignaledPresentFence(kPeriod, kPeriod);
+ ASSERT_TRUE(wouldBackpressure);
+ EXPECT_EQ(presentFence.fenceTime, fence);
+ }
+}
+
+TEST_F(FrameTargeterTest, wouldBackpressureAfterTime) {
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
+ VsyncId vsyncId{111};
+ TimePoint frameBeginTime(1000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Duration kFrameDuration = 13ms;
+
+ { Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); }
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+
+ const auto [wouldBackpressure, presentFence] =
+ expectedSignaledPresentFence(kPeriod, kPeriod);
+ EXPECT_TRUE(wouldBackpressure);
+ }
+ {
+ frameBeginTime += kPeriod;
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ const auto [wouldBackpressure, presentFence] =
+ expectedSignaledPresentFence(kPeriod, kPeriod);
+ EXPECT_FALSE(wouldBackpressure);
+ }
+}
+
+TEST_F(FrameTargeterTest, wouldBackpressureAfterTimeLegacy) {
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
+ VsyncId vsyncId{111};
+ TimePoint frameBeginTime(1000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Duration kFrameDuration = 13ms;
+
+ { Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate); }
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+
+ const auto [wouldBackpressure, presentFence] =
+ expectedSignaledPresentFence(kPeriod, kPeriod);
+ EXPECT_TRUE(wouldBackpressure);
+ }
+ {
+ frameBeginTime += kPeriod;
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ const auto [wouldBackpressure, presentFence] =
+ expectedSignaledPresentFence(kPeriod, kPeriod);
+ EXPECT_TRUE(wouldBackpressure);
}
}
TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAhead) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{222};
TimePoint frameBeginTime(2000ms);
constexpr Fps kRefreshRate = 120_Hz;
@@ -198,101 +254,66 @@
constexpr Duration kFrameDuration = 10ms;
FenceTimePtr previousFence = FenceTime::NO_FENCE;
-
+ FenceTimePtr currentFence = FenceTime::NO_FENCE;
for (int n = 5; n-- > 0;) {
Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
- const auto fence = frame.end();
-
- EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
- EXPECT_EQ(presentFenceForPastVsync(kPeriod).fenceTime, previousFence);
-
- previousFence = fence;
+ EXPECT_EQ(expectedSignaledPresentFence(kPeriod, kPeriod).second.fenceTime, previousFence);
+ previousFence = currentFence;
+ currentFence = frame.end();
}
}
-TEST_F(FrameTargeterTest, recallsPastNVsyncTwoVsyncsAhead) {
+TEST_F(FrameTargeterTest, recallsPastVsyncFiveVsyncsAhead) {
SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
+
VsyncId vsyncId{222};
TimePoint frameBeginTime(2000ms);
constexpr Fps kRefreshRate = 120_Hz;
constexpr Period kPeriod = kRefreshRate.getPeriod();
- constexpr Duration kFrameDuration = 10ms;
+ constexpr Duration kFrameDuration = 40ms;
- FenceTimePtr previousFence = FenceTime::NO_FENCE;
-
+ FenceTimePtr firstFence = FenceTime::NO_FENCE;
for (int n = 5; n-- > 0;) {
Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
const auto fence = frame.end();
-
- const auto pastVsyncTime = frameBeginTime + kFrameDuration - 2 * kPeriod;
- EXPECT_EQ(target().pastVsyncTime(kPeriod), pastVsyncTime);
- EXPECT_EQ(presentFenceForPastVsync(kFrameDuration).fenceTime, previousFence);
-
- frameBeginTime += kPeriod;
- previousFence = fence;
+ if (firstFence == FenceTime::NO_FENCE) {
+ firstFence = fence;
+ }
}
+
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ EXPECT_EQ(expectedSignaledPresentFence(kPeriod, kPeriod).second.fenceTime, firstFence);
}
TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAheadVrr) {
SET_FLAG_FOR_TEST(flags::vrr_config, true);
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{222};
TimePoint frameBeginTime(2000ms);
constexpr Fps kRefreshRate = 120_Hz;
- constexpr Fps kPeakRefreshRate = 240_Hz;
+ constexpr Fps kVsyncRate = 240_Hz;
constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Period kVsyncPeriod = kVsyncRate.getPeriod();
constexpr Duration kFrameDuration = 10ms;
FenceTimePtr previousFence = FenceTime::NO_FENCE;
-
+ FenceTimePtr currentFence = FenceTime::NO_FENCE;
for (int n = 5; n-- > 0;) {
- Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate,
- kPeakRefreshRate);
- const auto fence = frame.end();
-
- EXPECT_EQ(target().pastVsyncTime(kPeriod), frameBeginTime + kFrameDuration - 2 * kPeriod);
- EXPECT_EQ(presentFenceForPastVsync(kPeriod).fenceTime, previousFence);
-
- previousFence = fence;
- }
-}
-
-TEST_F(FrameTargeterTest, recallsPastNVsyncTwoVsyncsAheadVrr) {
- SET_FLAG_FOR_TEST(flags::vrr_config, true);
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
-
- VsyncId vsyncId{222};
- TimePoint frameBeginTime(2000ms);
- constexpr Fps kRefreshRate = 120_Hz;
- constexpr Fps kPeakRefreshRate = 240_Hz;
- constexpr Period kPeriod = kRefreshRate.getPeriod();
- constexpr Duration kFrameDuration = 10ms;
-
- FenceTimePtr previousFence = FenceTime::NO_FENCE;
-
- for (int n = 5; n-- > 0;) {
- Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate,
- kPeakRefreshRate);
- const auto fence = frame.end();
-
- const auto pastVsyncTime = frameBeginTime + kFrameDuration - 2 * kPeriod;
- EXPECT_EQ(target().pastVsyncTime(kPeriod), pastVsyncTime);
- EXPECT_EQ(presentFenceForPastVsync(kFrameDuration).fenceTime, previousFence);
-
- frameBeginTime += kPeriod;
- previousFence = fence;
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ EXPECT_EQ(expectedSignaledPresentFence(kVsyncPeriod, kPeriod).second.fenceTime,
+ previousFence);
+ previousFence = currentFence;
+ currentFence = frame.end();
}
}
TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) {
constexpr Period kPeriod = (60_Hz).getPeriod();
- EXPECT_EQ(presentFenceForPastVsync(kPeriod).fenceTime, FenceTime::NO_FENCE);
- EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ EXPECT_EQ(expectedSignaledPresentFence(kPeriod, kPeriod).second.fenceTime, FenceTime::NO_FENCE);
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
}
TEST_F(FrameTargeterTest, detectsEarlyPresent) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{333};
TimePoint frameBeginTime(3000ms);
constexpr Fps kRefreshRate = 60_Hz;
@@ -300,20 +321,57 @@
// The target is not early while past present fences are pending.
for (int n = 3; n-- > 0;) {
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ }
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
EXPECT_FALSE(target().earliestPresentTime());
}
// The target is early if the past present fence was signaled.
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- const auto fence = frame.end();
- fence->signalForTest(frameBeginTime.ns());
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+ }
Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
// `finalFrame` would present early, so it has an earliest present time.
- EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod));
+ ASSERT_NE(std::nullopt, target().earliestPresentTime());
+ EXPECT_EQ(*target().earliestPresentTime(),
+ target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
+}
+
+TEST_F(FrameTargeterTest, detectsEarlyPresentAfterLongPeriod) {
+ VsyncId vsyncId{333};
+ TimePoint frameBeginTime(3000ms);
+ constexpr Fps kRefreshRate = 60_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+
+ // The target is not early while past present fences are pending.
+ for (int n = 3; n-- > 0;) {
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ }
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
+ EXPECT_FALSE(target().earliestPresentTime());
+ }
+
+ // The target is early if the past present fence was signaled.
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+ }
+
+ frameBeginTime += 10 * kPeriod;
+
+ Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+
+ // `finalFrame` would present early, so it has an earliest present time.
+ EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod));
ASSERT_NE(std::nullopt, target().earliestPresentTime());
EXPECT_EQ(*target().earliestPresentTime(),
target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
@@ -322,7 +380,6 @@
// Same as `detectsEarlyPresent`, above, but verifies that we do not set an earliest present time
// when there is expected present time support.
TEST_F(FrameTargeterWithExpectedPresentSupportTest, detectsEarlyPresent) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{333};
TimePoint frameBeginTime(3000ms);
constexpr Fps kRefreshRate = 60_Hz;
@@ -330,26 +387,30 @@
// The target is not early while past present fences are pending.
for (int n = 3; n-- > 0;) {
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ }
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
EXPECT_FALSE(target().earliestPresentTime());
}
// The target is early if the past present fence was signaled.
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- const auto fence = frame.end();
- fence->signalForTest(frameBeginTime.ns());
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+ }
Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
// `finalFrame` would present early, but we have expected present time support, so it has no
// earliest present time.
- EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod));
ASSERT_EQ(std::nullopt, target().earliestPresentTime());
}
TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{444};
TimePoint frameBeginTime(4000ms);
constexpr Fps kRefreshRate = 120_Hz;
@@ -357,17 +418,21 @@
// The target is not early while past present fences are pending.
for (int n = 3; n-- > 0;) {
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
+ }
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
EXPECT_FALSE(target().earliestPresentTime());
}
+ {
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
- const auto fence = frame.end();
- fence->signalForTest(frameBeginTime.ns());
+ const auto fence = frame.end();
+ fence->signalForTest(frameBeginTime.ns());
+ }
// The target is two VSYNCs ahead, so the past present fence is still pending.
- EXPECT_FALSE(wouldPresentEarly(kPeriod));
+ EXPECT_FALSE(wouldPresentEarly(kPeriod, kPeriod));
EXPECT_FALSE(target().earliestPresentTime());
{ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate); }
@@ -375,66 +440,21 @@
Frame finalFrame(this, vsyncId++, frameBeginTime, 10ms, kRefreshRate, kRefreshRate);
// The target is early if the past present fence was signaled.
- EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod));
ASSERT_NE(std::nullopt, target().earliestPresentTime());
EXPECT_EQ(*target().earliestPresentTime(),
target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
}
-TEST_F(FrameTargeterTest, detectsEarlyPresentNVsyncsAhead) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
- VsyncId vsyncId{444};
- TimePoint frameBeginTime(4000ms);
- Fps refreshRate = 120_Hz;
- Period period = refreshRate.getPeriod();
-
- // The target is not early while past present fences are pending.
- for (int n = 5; n-- > 0;) {
- const Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate);
- EXPECT_FALSE(wouldPresentEarly(period));
- EXPECT_FALSE(target().earliestPresentTime());
- }
-
- Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate);
- auto fence = frame.end();
- frameBeginTime += period;
- fence->signalForTest(frameBeginTime.ns());
-
- // The target is two VSYNCs ahead, so the past present fence is still pending.
- EXPECT_FALSE(wouldPresentEarly(period));
- EXPECT_FALSE(target().earliestPresentTime());
-
- { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate); }
-
- Frame oneEarlyPresentFrame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate);
- // The target is early if the past present fence was signaled.
- EXPECT_TRUE(wouldPresentEarly(period));
- ASSERT_NE(std::nullopt, target().earliestPresentTime());
- EXPECT_EQ(*target().earliestPresentTime(),
- target().expectedPresentTime() - period - kHwcMinWorkDuration);
-
- fence = oneEarlyPresentFrame.end();
- frameBeginTime += period;
- fence->signalForTest(frameBeginTime.ns());
-
- // Change rate to track frame more than 2 vsyncs ahead
- refreshRate = 144_Hz;
- period = refreshRate.getPeriod();
- Frame onePresentEarlyFrame(this, vsyncId++, frameBeginTime, 16ms, refreshRate, refreshRate);
- // The target is not early as last frame as the past frame is tracked for pending.
- EXPECT_FALSE(wouldPresentEarly(period));
-}
-
TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) {
- SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
TimePoint frameBeginTime(5000ms);
constexpr Fps kRefreshRate = 144_Hz;
constexpr Period kPeriod = kRefreshRate.getPeriod();
- const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate, kRefreshRate);
+ { const Frame frame(this, VsyncId{555}, frameBeginTime, 16ms, kRefreshRate, kRefreshRate); }
// The target is more than two VSYNCs ahead, but present fences are not tracked that far back.
- EXPECT_TRUE(wouldPresentEarly(kPeriod));
+ EXPECT_TRUE(wouldPresentEarly(kPeriod, kPeriod));
EXPECT_TRUE(target().earliestPresentTime());
EXPECT_EQ(*target().earliestPresentTime(),
target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1bee32b..0aad282 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -40,6 +40,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
+#include <com_android_graphics_libgui_flags.h>
#include <com_android_graphics_surfaceflinger_flags.h>
#include <common/FlagManager.h>
#include <common/trace.h>
@@ -64,7 +65,7 @@
#include <ftl/fake_guard.h>
#include <ftl/future.h>
#include <ftl/unit.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/BufferQueue.h>
#include <gui/DebugEGLImageTracker.h>
#include <gui/IProducerListener.h>
@@ -73,7 +74,7 @@
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <hidl/ServiceManagement.h>
-#include <layerproto/LayerProtoParser.h>
+#include <layerproto/LayerProtoHeader.h>
#include <linux/sched/types.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
@@ -205,8 +206,6 @@
using ui::DisplayPrimaries;
using ui::RenderIntent;
-using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController;
-
namespace hal = android::hardware::graphics::composer::hal;
namespace {
@@ -374,8 +373,6 @@
const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW");
const String16 sWakeupSurfaceFlinger("android.permission.WAKEUP_SURFACE_FLINGER");
-const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
-
// ---------------------------------------------------------------------------
int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
bool SurfaceFlinger::useHwcForRgbToYuv;
@@ -853,6 +850,8 @@
auto const algorithm = base::GetProperty(PROPERTY_DEBUG_RENDERENGINE_BLUR_ALGORITHM, "");
if (algorithm == "gaussian") {
return renderengine::RenderEngine::BlurAlgorithm::GAUSSIAN;
+ } else if (algorithm == "kawase2") {
+ return renderengine::RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER;
} else {
return renderengine::RenderEngine::BlurAlgorithm::KAWASE;
}
@@ -911,9 +910,11 @@
LOG_ALWAYS_FATAL_IF(!configureLocked(),
"Initial display configuration failed: HWC did not hotplug");
+ mActiveDisplayId = getPrimaryDisplayIdLocked();
+
// Commit primary display.
sp<const DisplayDevice> display;
- if (const auto indexOpt = mCurrentState.getDisplayIndex(getPrimaryDisplayIdLocked())) {
+ if (const auto indexOpt = mCurrentState.getDisplayIndex(mActiveDisplayId)) {
const auto& displays = mCurrentState.displays;
const auto& token = displays.keyAt(*indexOpt);
@@ -1354,7 +1355,7 @@
}
if (emitEvent) {
- dispatchDisplayModeChangeEvent(displayId, mode);
+ mScheduler->onDisplayModeChanged(displayId, mode);
}
break;
case DesiredModeAction::None:
@@ -1451,7 +1452,7 @@
}
if (pendingModeOpt->emitEvent) {
- dispatchDisplayModeChangeEvent(displayId, activeMode);
+ mScheduler->onDisplayModeChanged(displayId, activeMode);
}
}
@@ -2161,12 +2162,12 @@
return mScheduler->createDisplayEventConnection(cycle, eventRegistration, layerHandle);
}
-void SurfaceFlinger::scheduleCommit(FrameHint hint) {
+void SurfaceFlinger::scheduleCommit(FrameHint hint, Duration workDurationSlack) {
if (hint == FrameHint::kActive) {
mScheduler->resetIdleTimer();
}
mPowerAdvisor->notifyDisplayUpdateImminentAndCpuReset();
- mScheduler->scheduleFrame();
+ mScheduler->scheduleFrame(workDurationSlack);
}
void SurfaceFlinger::scheduleComposite(FrameHint hint) {
@@ -2183,14 +2184,6 @@
static_cast<void>(mScheduler->schedule([this] { sample(); }));
}
-nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const {
- if (const auto display = getDefaultDisplayDeviceLocked()) {
- return display->getVsyncPeriodFromHWC();
- }
-
- return 0;
-}
-
void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
if (FlagManager::getInstance().connected_display() && timestamp < 0 &&
@@ -2306,37 +2299,6 @@
}
}
-bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs,
- bool flushTransactions,
- bool& outTransactionsAreEmpty) {
- SFTRACE_CALL();
- frontend::Update update;
- if (flushTransactions) {
- update = flushLifecycleUpdates();
- if (mTransactionTracing) {
- mTransactionTracing->addCommittedTransactions(ftl::to_underlying(vsyncId), frameTimeNs,
- update, mFrontEndDisplayInfos,
- mFrontEndDisplayInfosChanged);
- }
- }
-
- bool needsTraversal = false;
- if (flushTransactions) {
- needsTraversal |= commitMirrorDisplays(vsyncId);
- needsTraversal |= commitCreatedLayers(vsyncId, update.layerCreatedStates);
- needsTraversal |= applyTransactions(update.transactions, vsyncId);
- }
- outTransactionsAreEmpty = !needsTraversal;
- const bool shouldCommit = (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal;
- if (shouldCommit) {
- commitTransactionsLegacy();
- }
-
- bool mustComposite = latchBuffers() || shouldCommit;
- updateLayerGeometry();
- return mustComposite;
-}
-
void SurfaceFlinger::updateLayerHistory(nsecs_t now) {
for (const auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
using Changes = frontend::RequestedLayerState::Changes;
@@ -2552,17 +2514,25 @@
it->second->latchBufferImpl(unused, latchTime, bgColorOnly);
newDataLatched = true;
- mLayersWithQueuedFrames.emplace(it->second);
+ frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(it->second->sequence);
+ gui::GameMode gameMode = (snapshot) ? snapshot->gameMode : gui::GameMode::Unsupported;
+ mLayersWithQueuedFrames.emplace(it->second, gameMode);
mLayersIdsWithQueuedFrames.emplace(it->second->sequence);
}
updateLayerHistory(latchTime);
- mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) {
- if (mLayersIdsWithQueuedFrames.find(snapshot.path.id) == mLayersIdsWithQueuedFrames.end())
- return;
- Region visibleReg;
- visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion);
- invalidateLayerStack(snapshot.outputFilter, visibleReg);
+ mLayerSnapshotBuilder.forEachSnapshot([&](const frontend::LayerSnapshot& snapshot) {
+ // update output dirty region if we have a queued buffer that is visible or a snapshot
+ // recently became invisible
+ // TODO(b/360050020) investigate if we need to update dirty region when layer color changes
+ if ((snapshot.isVisible &&
+ (mLayersIdsWithQueuedFrames.find(snapshot.path.id) !=
+ mLayersIdsWithQueuedFrames.end())) ||
+ (!snapshot.isVisible && snapshot.changes.test(Changes::Visibility))) {
+ Region visibleReg;
+ visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion);
+ invalidateLayerStack(snapshot.outputFilter, visibleReg);
+ }
});
for (auto& destroyedLayer : mLayerLifecycleManager.getDestroyedLayers()) {
@@ -2620,13 +2590,16 @@
}
}
- if (pacesetterFrameTarget.isFramePending()) {
+ if (pacesetterFrameTarget.wouldBackpressureHwc()) {
if (mBackpressureGpuComposition || pacesetterFrameTarget.didMissHwcFrame()) {
if (FlagManager::getInstance().vrr_config()) {
mScheduler->getVsyncSchedule()->getTracker().onFrameMissed(
pacesetterFrameTarget.expectedPresentTime());
}
- scheduleCommit(FrameHint::kNone);
+ const Duration slack = FlagManager::getInstance().allow_n_vsyncs_in_targeter()
+ ? TimePoint::now() - pacesetterFrameTarget.frameBeginTime()
+ : Duration::fromNs(0);
+ scheduleCommit(FrameHint::kNone, slack);
return false;
}
}
@@ -2778,16 +2751,12 @@
const bool updateTaskMetadata = mCompositionEngine->getFeatureFlags().test(
compositionengine::Feature::kSnapshotLayerMetadata);
- if (updateTaskMetadata && (mVisibleRegionsDirty || mLayerMetadataSnapshotNeeded)) {
- updateLayerMetadataSnapshot();
- mLayerMetadataSnapshotNeeded = false;
- }
refreshArgs.bufferIdsToUncache = std::move(mBufferIdsToUncache);
if (!FlagManager::getInstance().ce_fence_promise()) {
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
- for (auto& layer : mLayersWithQueuedFrames) {
+ for (auto& [layer, _] : mLayersWithQueuedFrames) {
if (const auto& layerFE = layer->getCompositionEngineLayerFE())
refreshArgs.layersWithQueuedFrames.push_back(layerFE);
}
@@ -2863,7 +2832,7 @@
}
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
- for (auto& layer : mLayersWithQueuedFrames) {
+ for (auto& [layer, _] : mLayersWithQueuedFrames) {
if (const auto& layerFE = layer->getCompositionEngineLayerFE()) {
refreshArgs.layersWithQueuedFrames.push_back(layerFE);
// Some layers are not displayed and do not yet have a future release fence
@@ -2913,6 +2882,9 @@
// Now that the current frame has been presented above, PowerAdvisor needs the present time
// of the previous frame (whose fence is signaled by now) to determine how long the HWC had
// waited on that fence to retire before presenting.
+ // TODO(b/355238809) `presentFenceForPreviousFrame` might not always be signaled (e.g. on
+ // devices
+ // where HWC does not block on the previous present fence). Revise this assumtion.
const auto& previousPresentFence = pacesetterTarget.presentFenceForPreviousFrame();
mPowerAdvisor->setSfPresentTiming(TimePoint::fromNs(previousPresentFence->getSignalTime()),
@@ -3000,21 +2972,6 @@
return resultsPerDisplay;
}
-void SurfaceFlinger::updateLayerGeometry() {
- SFTRACE_CALL();
-
- if (mVisibleRegionsDirty) {
- computeLayerBounds();
- }
-
- for (auto& layer : mLayersPendingRefresh) {
- Region visibleReg;
- visibleReg.set(layer->getScreenBounds());
- invalidateLayerStack(layer->getOutputFilter(), visibleReg);
- }
- mLayersPendingRefresh.clear();
-}
-
bool SurfaceFlinger::isHdrLayer(const frontend::LayerSnapshot& snapshot) const {
// Even though the camera layer may be using an HDR transfer function or otherwise be "HDR"
// the device may need to avoid boosting the brightness as a result of these layers to
@@ -3181,10 +3138,10 @@
}
mLayersWithBuffersRemoved.clear();
- for (const auto& layer: mLayersWithQueuedFrames) {
+ for (const auto& [layer, gameMode] : mLayersWithQueuedFrames) {
layer->onCompositionPresented(pacesetterDisplay.get(),
pacesetterGpuCompositionDoneFenceTime,
- pacesetterPresentFenceTime, compositorTiming);
+ pacesetterPresentFenceTime, compositorTiming, gameMode);
layer->releasePendingBuffer(presentTime.ns());
}
@@ -3327,36 +3284,6 @@
logFrameStats(presentTime);
}
-FloatRect SurfaceFlinger::getMaxDisplayBounds() {
- const ui::Size maxSize = [this] {
- ftl::FakeGuard guard(mStateLock);
-
- // The LayerTraceGenerator tool runs without displays.
- if (mDisplays.empty()) return ui::Size{5000, 5000};
-
- return std::accumulate(mDisplays.begin(), mDisplays.end(), ui::kEmptySize,
- [](ui::Size size, const auto& pair) -> ui::Size {
- const auto& display = pair.second;
- return {std::max(size.getWidth(), display->getWidth()),
- std::max(size.getHeight(), display->getHeight())};
- });
- }();
-
- // Ignore display bounds for now since they will be computed later. Use a large Rect bound
- // to ensure it's bigger than an actual display will be.
- const float xMax = maxSize.getWidth() * 10.f;
- const float yMax = maxSize.getHeight() * 10.f;
-
- return {-xMax, -yMax, xMax, yMax};
-}
-
-void SurfaceFlinger::computeLayerBounds() {
- const FloatRect maxBounds = getMaxDisplayBounds();
- for (const auto& layer : mDrawingState.layersSortedByZ) {
- layer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */);
- }
-}
-
void SurfaceFlinger::commitTransactions() {
SFTRACE_CALL();
mDebugInTransaction = systemTime();
@@ -3370,28 +3297,6 @@
mDebugInTransaction = 0;
}
-void SurfaceFlinger::commitTransactionsLegacy() {
- SFTRACE_CALL();
-
- // Keep a copy of the drawing state (that is going to be overwritten
- // by commitTransactionsLocked) outside of mStateLock so that the side
- // effects of the State assignment don't happen with mStateLock held,
- // which can cause deadlocks.
- State drawingState(mDrawingState);
-
- Mutex::Autolock lock(mStateLock);
- mDebugInTransaction = systemTime();
-
- // Here we're guaranteed that some transaction flags are set
- // so we can call commitTransactionsLocked unconditionally.
- // We clear the flags with mStateLock held to guarantee that
- // mCurrentState won't change until the transaction is committed.
- mScheduler->modulateVsync({}, &VsyncModulator::onTransactionCommit);
- commitTransactionsLocked(clearTransactionFlags(eTransactionMask));
-
- mDebugInTransaction = 0;
-}
-
std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes(
PhysicalDisplayId displayId) const {
std::vector<HWComposer::HWCDisplayMode> hwcModes;
@@ -3679,16 +3584,6 @@
mPhysicalDisplays.erase(displayId);
}
-void SurfaceFlinger::dispatchDisplayModeChangeEvent(PhysicalDisplayId displayId,
- const scheduler::FrameRateMode& mode) {
- // TODO(b/255635821): Merge code paths and move to Scheduler.
- const auto onDisplayModeChanged = displayId == mActiveDisplayId
- ? &scheduler::Scheduler::onPrimaryDisplayModeChanged
- : &scheduler::Scheduler::onNonPrimaryDisplayModeChanged;
-
- ((*mScheduler).*onDisplayModeChanged)(scheduler::Cycle::Render, mode);
-}
-
sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
const wp<IBinder>& displayToken,
std::shared_ptr<compositionengine::Display> compositionDisplay,
@@ -3834,11 +3729,20 @@
state.surface.get());
const auto displayId = PhysicalDisplayId::tryCast(compositionDisplay->getId());
LOG_FATAL_IF(!displayId);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ const auto frameBufferSurface =
+ sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqProducer, bqConsumer,
+ state.physical->activeMode->getResolution(),
+ ui::Size(maxGraphicsWidth, maxGraphicsHeight));
+ displaySurface = frameBufferSurface;
+ producer = frameBufferSurface->getSurface()->getIGraphicBufferProducer();
+#else
displaySurface =
sp<FramebufferSurface>::make(getHwComposer(), *displayId, bqConsumer,
state.physical->activeMode->getResolution(),
ui::Size(maxGraphicsWidth, maxGraphicsHeight));
producer = bqProducer;
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
}
LOG_FATAL_IF(!displaySurface);
@@ -3942,11 +3846,8 @@
setPowerModeInternal(display, hal::PowerMode::ON);
}
- // TODO(b/175678251) Call a listener instead.
- if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) {
- const Fps refreshRate =
- mDisplayModeController.getActiveMode(display->getPhysicalId()).fps;
- mScheduler->resetPhaseConfiguration(refreshRate);
+ if (display->getPhysicalId() == mActiveDisplayId) {
+ onActiveDisplayChangedLocked(nullptr, *display);
}
}
return;
@@ -4038,51 +3939,6 @@
mUpdateInputInfo = true;
}
- // Update transform hint.
- if (transactionFlags & (eTransformHintUpdateNeeded | eDisplayTransactionNeeded)) {
- // Layers and/or displays have changed, so update the transform hint for each layer.
- //
- // NOTE: we do this here, rather than when presenting the display so that
- // the hint is set before we acquire a buffer from the surface texture.
- //
- // NOTE: layer transactions have taken place already, so we use their
- // drawing state. However, SurfaceFlinger's own transaction has not
- // happened yet, so we must use the current state layer list
- // (soon to become the drawing state list).
- //
- sp<const DisplayDevice> hintDisplay;
- ui::LayerStack layerStack;
-
- mCurrentState.traverse([&](Layer* layer) REQUIRES(mStateLock) {
- // NOTE: we rely on the fact that layers are sorted by
- // layerStack first (so we don't have to traverse the list
- // of displays for every layer).
- if (const auto filter = layer->getOutputFilter(); layerStack != filter.layerStack) {
- layerStack = filter.layerStack;
- hintDisplay = nullptr;
-
- // Find the display that includes the layer.
- for (const auto& [token, display] : mDisplays) {
- if (!display->getCompositionDisplay()->includesLayer(filter)) {
- continue;
- }
-
- // Pick the primary display if another display mirrors the layer.
- if (hintDisplay) {
- hintDisplay = nullptr;
- break;
- }
-
- hintDisplay = display;
- }
- }
-
- if (hintDisplay) {
- layer->updateTransformHint(hintDisplay->getTransformHint());
- }
- });
- }
-
if (mLayersAdded) {
mLayersAdded = false;
// Layers have been added.
@@ -4096,14 +3952,6 @@
mLayersRemoved = false;
mVisibleRegionsDirty = true;
mUpdateInputInfo = true;
- mDrawingState.traverseInZOrder([&](Layer* layer) {
- if (mLayersPendingRemoval.indexOf(sp<Layer>::fromExisting(layer)) >= 0) {
- // this layer is not visible anymore
- Region visibleReg;
- visibleReg.set(layer->getScreenBounds());
- invalidateLayerStack(layer->getOutputFilter(), visibleReg);
- }
- });
}
if (transactionFlags & eInputInfoUpdateNeeded) {
@@ -4272,15 +4120,6 @@
}
}
-void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
- PhysicalDisplayId displayId = [&]() {
- ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
- return getDefaultDisplayDeviceLocked()->getPhysicalId();
- }();
-
- mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId);
-}
-
void SurfaceFlinger::notifyCpuLoadUp() {
mPowerAdvisor->notifyCpuLoadUp();
}
@@ -4530,13 +4369,6 @@
l->setIsAtRoot(false);
mCurrentState.layersSortedByZ.remove(l);
}
-
- // If the layer has been removed and has no parent, then it will not be reachable
- // when traversing layers on screen. Add the layer to the offscreenLayers set to
- // ensure we can copy its current to drawing state.
- if (!l->getParent()) {
- mOffscreenLayers.emplace(l.get());
- }
}
mLayersPendingRemoval.clear();
}
@@ -4550,7 +4382,6 @@
}
}
- commitOffscreenLayers();
if (mLayerMirrorRoots.size() > 0) {
std::deque<Layer*> pendingUpdates;
pendingUpdates.insert(pendingUpdates.end(), mLayerMirrorRoots.begin(),
@@ -4572,17 +4403,6 @@
}
}
-void SurfaceFlinger::commitOffscreenLayers() {
- for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) {
- if (layer->clearTransactionFlags(eTransactionNeeded)) {
- layer->doTransaction(0);
- layer->commitChildList();
- }
- });
- }
-}
-
void SurfaceFlinger::invalidateLayerStack(const ui::LayerFilter& layerFilter, const Region& dirty) {
for (const auto& [token, displayDevice] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
auto display = displayDevice->getCompositionDisplay();
@@ -4592,160 +4412,30 @@
}
}
-bool SurfaceFlinger::latchBuffers() {
- SFTRACE_CALL();
-
- const nsecs_t latchTime = systemTime();
-
- bool visibleRegions = false;
- bool frameQueued = false;
- bool newDataLatched = false;
-
- // Store the set of layers that need updates. This set must not change as
- // buffers are being latched, as this could result in a deadlock.
- // Example: Two producers share the same command stream and:
- // 1.) Layer 0 is latched
- // 2.) Layer 0 gets a new frame
- // 2.) Layer 1 gets a new frame
- // 3.) Layer 1 is latched.
- // Display is now waiting on Layer 1's frame, which is behind layer 0's
- // second frame. But layer 0's second frame could be waiting on display.
- mDrawingState.traverse([&](Layer* layer) {
- if (layer->clearTransactionFlags(eTransactionNeeded) || mForceTransactionDisplayChange) {
- const uint32_t flags = layer->doTransaction(0);
- if (flags & Layer::eVisibleRegion) {
- mVisibleRegionsDirty = true;
- }
- }
-
- if (layer->hasReadyFrame() || layer->willReleaseBufferOnLatch()) {
- frameQueued = true;
- mLayersWithQueuedFrames.emplace(sp<Layer>::fromExisting(layer));
- } else {
- layer->useEmptyDamage();
- if (!layer->hasBuffer()) {
- // The last latch time is used to classify a missed frame as buffer stuffing
- // instead of a missed frame. This is used to identify scenarios where we
- // could not latch a buffer or apply a transaction due to backpressure.
- // We only update the latch time for buffer less layers here, the latch time
- // is updated for buffer layers when the buffer is latched.
- layer->updateLastLatchTime(latchTime);
- }
- }
- });
- mForceTransactionDisplayChange = false;
-
- // The client can continue submitting buffers for offscreen layers, but they will not
- // be shown on screen. Therefore, we need to latch and release buffers of offscreen
- // layers to ensure dequeueBuffer doesn't block indefinitely.
- for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* l) { l->latchAndReleaseBuffer(); });
- }
-
- if (!mLayersWithQueuedFrames.empty()) {
- // mStateLock is needed for latchBuffer as LayerRejecter::reject()
- // writes to Layer current state. See also b/119481871
- Mutex::Autolock lock(mStateLock);
-
- for (const auto& layer : mLayersWithQueuedFrames) {
- if (layer->willReleaseBufferOnLatch()) {
- mLayersWithBuffersRemoved.emplace(layer);
- }
- if (layer->latchBuffer(visibleRegions, latchTime)) {
- mLayersPendingRefresh.push_back(layer);
- newDataLatched = true;
- }
- layer->useSurfaceDamage();
- }
- }
-
- mVisibleRegionsDirty |= visibleRegions;
-
- // If we will need to wake up at some time in the future to deal with a
- // queued frame that shouldn't be displayed during this vsync period, wake
- // up during the next vsync period to check again.
- if (frameQueued && (mLayersWithQueuedFrames.empty() || !newDataLatched)) {
- scheduleCommit(FrameHint::kNone);
- }
-
- // enter boot animation on first buffer latch
- if (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER && newDataLatched)) {
- ALOGI("Enter boot animation");
- mBootStage = BootStage::BOOTANIMATION;
- }
-
- if (mLayerMirrorRoots.size() > 0) {
- mDrawingState.traverse([&](Layer* layer) { layer->updateCloneBufferInfo(); });
- }
-
- // Only continue with the refresh if there is actually new work to do
- return !mLayersWithQueuedFrames.empty() && newDataLatched;
-}
-
status_t SurfaceFlinger::addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle,
const sp<Layer>& layer, const wp<Layer>& parent,
uint32_t* outTransformHint) {
if (mNumLayers >= MAX_LAYERS) {
+ static std::atomic<nsecs_t> lasttime{0};
+ nsecs_t now = systemTime();
+ if (lasttime != 0 && ns2s(now - lasttime.load()) < 10) {
+ ALOGE("AddClientLayer already dumped 10s before");
+ return NO_MEMORY;
+ } else {
+ lasttime = now;
+ }
+
ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
MAX_LAYERS);
- static_cast<void>(mScheduler->schedule([=, this] {
- ALOGE("Dumping layer keeping > 20 children alive:");
- bool leakingParentLayerFound = false;
- mDrawingState.traverse([&](Layer* layer) {
- if (leakingParentLayerFound) {
- return;
- }
- if (layer->getChildrenCount() > 20) {
- leakingParentLayerFound = true;
- sp<Layer> parent = sp<Layer>::fromExisting(layer);
- while (parent) {
- ALOGE("Parent Layer: %s%s", parent->getName().c_str(),
- (parent->isHandleAlive() ? "handleAlive" : ""));
- parent = parent->getParent();
- }
- // Sample up to 100 layers
- ALOGE("Dumping random sampling of child layers total(%zu): ",
- layer->getChildrenCount());
- int sampleSize = (layer->getChildrenCount() / 100) + 1;
- layer->traverseChildren([&](Layer* layer) {
- if (rand() % sampleSize == 0) {
- ALOGE("Child Layer: %s%s", layer->getName().c_str(),
- (layer->isHandleAlive() ? "handleAlive" : ""));
- }
- });
- }
- });
-
- int numLayers = 0;
- mDrawingState.traverse([&](Layer* layer) { numLayers++; });
-
- ALOGE("Dumping random sampling of on-screen layers total(%u):", numLayers);
- mDrawingState.traverse([&](Layer* layer) {
- // Aim to dump about 200 layers to avoid totally trashing
- // logcat. On the other hand, if there really are 4096 layers
- // something has gone totally wrong its probably the most
- // useful information in logcat.
- if (rand() % 20 == 13) {
- ALOGE("Layer: %s%s", layer->getName().c_str(),
- (layer->isHandleAlive() ? "handleAlive" : ""));
- std::this_thread::sleep_for(std::chrono::milliseconds(5));
- }
- });
- ALOGE("Dumping random sampling of off-screen layers total(%zu): ",
- mOffscreenLayers.size());
- for (Layer* offscreenLayer : mOffscreenLayers) {
- if (rand() % 20 == 13) {
- ALOGE("Offscreen-layer: %s%s", offscreenLayer->getName().c_str(),
- (offscreenLayer->isHandleAlive() ? "handleAlive" : ""));
- std::this_thread::sleep_for(std::chrono::milliseconds(5));
- }
- }
+ static_cast<void>(mScheduler->schedule([&]() FTL_FAKE_GUARD(kMainThreadContext) {
+ ALOGE("Dumping on-screen layers.");
+ mLayerHierarchyBuilder.dumpLayerSample(mLayerHierarchyBuilder.getHierarchy());
+ ALOGE("Dumping off-screen layers.");
+ mLayerHierarchyBuilder.dumpLayerSample(mLayerHierarchyBuilder.getOffscreenHierarchy());
}));
return NO_MEMORY;
}
- layer->updateTransformHint(mActiveDisplayTransformHint);
if (outTransformHint) {
*outTransformHint = mActiveDisplayTransformHint;
}
@@ -5040,7 +4730,7 @@
status_t SurfaceFlinger::setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp,
const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
@@ -5055,7 +4745,7 @@
composerState.state.sanitize(permissions);
}
- for (DisplayState display : displays) {
+ for (DisplayState& display : displays) {
display.sanitize(permissions);
}
@@ -5322,364 +5012,6 @@
return true;
}
-uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo,
- ResolvedComposerState& composerState,
- int64_t desiredPresentTime, bool isAutoTimestamp,
- int64_t postTime, uint64_t transactionId) {
- layer_state_t& s = composerState.state;
-
- std::vector<ListenerCallbacks> filteredListeners;
- for (auto& listener : s.listeners) {
- // Starts a registration but separates the callback ids according to callback type. This
- // allows the callback invoker to send on latch callbacks earlier.
- // note that startRegistration will not re-register if the listener has
- // already be registered for a prior surface control
-
- ListenerCallbacks onCommitCallbacks = listener.filter(CallbackId::Type::ON_COMMIT);
- if (!onCommitCallbacks.callbackIds.empty()) {
- filteredListeners.push_back(onCommitCallbacks);
- }
-
- ListenerCallbacks onCompleteCallbacks = listener.filter(CallbackId::Type::ON_COMPLETE);
- if (!onCompleteCallbacks.callbackIds.empty()) {
- filteredListeners.push_back(onCompleteCallbacks);
- }
- }
-
- const uint64_t what = s.what;
- uint32_t flags = 0;
- sp<Layer> layer = nullptr;
- if (s.surface) {
- layer = LayerHandle::getLayer(s.surface);
- } else {
- // The client may provide us a null handle. Treat it as if the layer was removed.
- ALOGW("Attempt to set client state with a null layer handle");
- }
- if (layer == nullptr) {
- for (auto& [listener, callbackIds] : s.listeners) {
- mTransactionCallbackInvoker.addCallbackHandle(
- sp<CallbackHandle>::make(listener, callbackIds, s.surface));
- }
- return 0;
- }
- MUTEX_ALIAS(mStateLock, layer->mFlinger->mStateLock);
-
- ui::LayerStack oldLayerStack = layer->getLayerStack(LayerVector::StateSet::Current);
-
- // Only set by BLAST adapter layers
- if (what & layer_state_t::eProducerDisconnect) {
- layer->onDisconnect();
- }
-
- if (what & layer_state_t::ePositionChanged) {
- if (layer->setPosition(s.x, s.y)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eLayerChanged) {
- // NOTE: index needs to be calculated before we update the state
- const auto& p = layer->getParent();
- if (p == nullptr) {
- ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
- if (layer->setLayer(s.z) && idx >= 0) {
- mCurrentState.layersSortedByZ.removeAt(idx);
- mCurrentState.layersSortedByZ.add(layer);
- // we need traversal (state changed)
- // AND transaction (list changed)
- flags |= eTransactionNeeded|eTraversalNeeded;
- }
- } else {
- if (p->setChildLayer(layer, s.z)) {
- flags |= eTransactionNeeded|eTraversalNeeded;
- }
- }
- }
- if (what & layer_state_t::eRelativeLayerChanged) {
- // NOTE: index needs to be calculated before we update the state
- const auto& p = layer->getParent();
- const auto& relativeHandle = s.relativeLayerSurfaceControl ?
- s.relativeLayerSurfaceControl->getHandle() : nullptr;
- if (p == nullptr) {
- ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
- if (layer->setRelativeLayer(relativeHandle, s.z) &&
- idx >= 0) {
- mCurrentState.layersSortedByZ.removeAt(idx);
- mCurrentState.layersSortedByZ.add(layer);
- // we need traversal (state changed)
- // AND transaction (list changed)
- flags |= eTransactionNeeded|eTraversalNeeded;
- }
- } else {
- if (p->setChildRelativeLayer(layer, relativeHandle, s.z)) {
- flags |= eTransactionNeeded|eTraversalNeeded;
- }
- }
- }
- if (what & layer_state_t::eAlphaChanged) {
- if (layer->setAlpha(s.color.a)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eColorChanged) {
- if (layer->setColor(s.color.rgb)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eColorTransformChanged) {
- if (layer->setColorTransform(s.colorTransform)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eBackgroundColorChanged) {
- if (layer->setBackgroundColor(s.bgColor.rgb, s.bgColor.a, s.bgColorDataspace)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eMatrixChanged) {
- if (layer->setMatrix(s.matrix)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eTransparentRegionChanged) {
- if (layer->setTransparentRegionHint(s.transparentRegion))
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eFlagsChanged) {
- if (layer->setFlags(s.flags, s.mask)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eCornerRadiusChanged) {
- if (layer->setCornerRadius(s.cornerRadius))
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eBackgroundBlurRadiusChanged && mSupportsBlur) {
- if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eBlurRegionsChanged) {
- if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eLayerStackChanged) {
- ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
- // We only allow setting layer stacks for top level layers,
- // everything else inherits layer stack from its parent.
- if (layer->hasParent()) {
- ALOGE("Attempt to set layer stack on layer with parent (%s) is invalid",
- layer->getDebugName());
- } else if (idx < 0) {
- ALOGE("Attempt to set layer stack on layer without parent (%s) that "
- "that also does not appear in the top level layer list. Something"
- " has gone wrong.",
- layer->getDebugName());
- } else if (layer->setLayerStack(s.layerStack)) {
- mCurrentState.layersSortedByZ.removeAt(idx);
- mCurrentState.layersSortedByZ.add(layer);
- // we need traversal (state changed)
- // AND transaction (list changed)
- flags |= eTransactionNeeded | eTraversalNeeded | eTransformHintUpdateNeeded;
- }
- }
- if (what & layer_state_t::eBufferTransformChanged) {
- if (layer->setTransform(s.bufferTransform)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eTransformToDisplayInverseChanged) {
- if (layer->setTransformToDisplayInverse(s.transformToDisplayInverse))
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eCropChanged) {
- if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eDataspaceChanged) {
- if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eSurfaceDamageRegionChanged) {
- if (layer->setSurfaceDamageRegion(s.surfaceDamageRegion)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eApiChanged) {
- if (layer->setApi(s.api)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eSidebandStreamChanged) {
- if (layer->setSidebandStream(s.sidebandStream, frameTimelineInfo, postTime))
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eInputInfoChanged) {
- layer->setInputInfo(*s.windowInfoHandle->getInfo());
- flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eMetadataChanged) {
- if (const int32_t gameMode = s.metadata.getInt32(gui::METADATA_GAME_MODE, -1);
- gameMode != -1) {
- // The transaction will be received on the Task layer and needs to be applied to all
- // child layers. Child layers that are added at a later point will obtain the game mode
- // info through addChild().
- layer->setGameModeForTree(static_cast<GameMode>(gameMode));
- }
-
- if (layer->setMetadata(s.metadata)) {
- flags |= eTraversalNeeded;
- mLayerMetadataSnapshotNeeded = true;
- }
- }
- if (what & layer_state_t::eColorSpaceAgnosticChanged) {
- if (layer->setColorSpaceAgnostic(s.colorSpaceAgnostic)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eShadowRadiusChanged) {
- if (layer->setShadowRadius(s.shadowRadius)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
- const auto compatibility =
- Layer::FrameRate::convertCompatibility(s.defaultFrameRateCompatibility);
-
- if (layer->setDefaultFrameRateCompatibility(compatibility)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eFrameRateSelectionPriority) {
- if (layer->setFrameRateSelectionPriority(s.frameRateSelectionPriority)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eFrameRateChanged) {
- const auto compatibility =
- Layer::FrameRate::convertCompatibility(s.frameRateCompatibility);
- const auto strategy =
- Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy);
-
- if (layer->setFrameRate(Layer::FrameRate::FrameRateVote(Fps::fromValue(s.frameRate),
- compatibility, strategy))) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eFrameRateCategoryChanged) {
- const FrameRateCategory category = Layer::FrameRate::convertCategory(s.frameRateCategory);
- if (layer->setFrameRateCategory(category, s.frameRateCategorySmoothSwitchOnly)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eFrameRateSelectionStrategyChanged) {
- const scheduler::LayerInfo::FrameRateSelectionStrategy strategy =
- scheduler::LayerInfo::convertFrameRateSelectionStrategy(
- s.frameRateSelectionStrategy);
- if (layer->setFrameRateSelectionStrategy(strategy)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eFixedTransformHintChanged) {
- if (layer->setFixedTransformHint(s.fixedTransformHint)) {
- flags |= eTraversalNeeded | eTransformHintUpdateNeeded;
- }
- }
- if (what & layer_state_t::eAutoRefreshChanged) {
- layer->setAutoRefresh(s.autoRefresh);
- }
- if (what & layer_state_t::eDimmingEnabledChanged) {
- if (layer->setDimmingEnabled(s.dimmingEnabled)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eExtendedRangeBrightnessChanged) {
- if (layer->setExtendedRangeBrightness(s.currentHdrSdrRatio, s.desiredHdrSdrRatio)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eDesiredHdrHeadroomChanged) {
- if (layer->setDesiredHdrHeadroom(s.desiredHdrSdrRatio)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eCachingHintChanged) {
- if (layer->setCachingHint(s.cachingHint)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eHdrMetadataChanged) {
- if (layer->setHdrMetadata(s.hdrMetadata)) flags |= eTraversalNeeded;
- }
- if (what & layer_state_t::eTrustedOverlayChanged) {
- if (layer->setTrustedOverlay(s.trustedOverlay == gui::TrustedOverlay::ENABLED)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eStretchChanged) {
- if (layer->setStretchEffect(s.stretchEffect)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eBufferCropChanged) {
- if (layer->setBufferCrop(s.bufferCrop)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eDestinationFrameChanged) {
- if (layer->setDestinationFrame(s.destinationFrame)) {
- flags |= eTraversalNeeded;
- }
- }
- if (what & layer_state_t::eDropInputModeChanged) {
- if (layer->setDropInputMode(s.dropInputMode)) {
- flags |= eTraversalNeeded;
- mUpdateInputInfo = true;
- }
- }
- // This has to happen after we reparent children because when we reparent to null we remove
- // child layers from current state and remove its relative z. If the children are reparented in
- // the same transaction, then we have to make sure we reparent the children first so we do not
- // lose its relative z order.
- if (what & layer_state_t::eReparent) {
- bool hadParent = layer->hasParent();
- auto parentHandle = (s.parentSurfaceControlForChild)
- ? s.parentSurfaceControlForChild->getHandle()
- : nullptr;
- if (layer->reparent(parentHandle)) {
- if (!hadParent) {
- layer->setIsAtRoot(false);
- mCurrentState.layersSortedByZ.remove(layer);
- }
- flags |= eTransactionNeeded | eTraversalNeeded;
- }
- }
- std::vector<sp<CallbackHandle>> callbackHandles;
- if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
- for (auto& [listener, callbackIds] : filteredListeners) {
- callbackHandles.emplace_back(
- sp<CallbackHandle>::make(listener, callbackIds, s.surface));
- }
- }
-
- if (what & layer_state_t::eBufferChanged) {
- if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
- desiredPresentTime, isAutoTimestamp, frameTimelineInfo)) {
- flags |= eTraversalNeeded;
- }
- } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
- layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
- }
-
- if ((what & layer_state_t::eBufferChanged) == 0) {
- layer->setDesiredPresentTime(desiredPresentTime, isAutoTimestamp);
- }
-
- if (what & layer_state_t::eTrustedPresentationInfoChanged) {
- if (layer->setTrustedPresentationInfo(s.trustedPresentationThresholds,
- s.trustedPresentationListener)) {
- flags |= eTraversalNeeded;
- }
- }
-
- if (what & layer_state_t::eFlushJankData) {
- // Do nothing. Processing the transaction completed listeners currently cause the flush.
- }
-
- if (layer->setTransactionCompletedListeners(callbackHandles,
- layer->willPresentCurrentTransaction() ||
- layer->willReleaseBufferOnLatch())) {
- flags |= eTraversalNeeded;
- }
-
- // Do not put anything that updates layer state or modifies flags after
- // setTransactionCompletedListener
-
- // if the layer has been parented on to a new display, update its transform hint.
- if (((flags & eTransformHintUpdateNeeded) == 0) &&
- oldLayerStack != layer->getLayerStack(LayerVector::StateSet::Current)) {
- flags |= eTransformHintUpdateNeeded;
- }
-
- return flags;
-}
-
uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& frameTimelineInfo,
ResolvedComposerState& composerState,
int64_t desiredPresentTime,
@@ -5732,6 +5064,17 @@
sp<CallbackHandle>::make(listener, callbackIds, s.surface));
}
}
+
+ frontend::LayerSnapshot* snapshot = nullptr;
+ gui::GameMode gameMode = gui::GameMode::Unsupported;
+ if (what & (layer_state_t::eSidebandStreamChanged | layer_state_t::eBufferChanged) ||
+ frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
+ snapshot = mLayerSnapshotBuilder.getSnapshot(layer->sequence);
+ if (snapshot) {
+ gameMode = snapshot->gameMode;
+ }
+ }
+
// TODO(b/238781169) remove after screenshot refactor, currently screenshots
// requires to read drawing state from binder thread. So we need to fix that
// before removing this.
@@ -5746,7 +5089,7 @@
if (layer->setCrop(s.crop)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eSidebandStreamChanged) {
- if (layer->setSidebandStream(s.sidebandStream, frameTimelineInfo, postTime))
+ if (layer->setSidebandStream(s.sidebandStream, frameTimelineInfo, postTime, gameMode))
flags |= eTraversalNeeded;
}
if (what & layer_state_t::eDataspaceChanged) {
@@ -5764,18 +5107,17 @@
}
if (what & layer_state_t::eBufferChanged) {
std::optional<ui::Transform::RotationFlags> transformHint = std::nullopt;
- frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(layer->sequence);
if (snapshot) {
transformHint = snapshot->transformHint;
}
layer->setTransformHint(transformHint);
if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
- desiredPresentTime, isAutoTimestamp, frameTimelineInfo)) {
+ desiredPresentTime, isAutoTimestamp, frameTimelineInfo, gameMode)) {
flags |= eTraversalNeeded;
}
- mLayersWithQueuedFrames.emplace(layer);
+ mLayersWithQueuedFrames.emplace(layer, gameMode);
} else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
- layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
+ layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime, gameMode);
}
if ((what & layer_state_t::eBufferChanged) == 0) {
@@ -6272,7 +5614,7 @@
}
void SurfaceFlinger::dumpStats(const DumpArgs& args, std::string& result) const {
- StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriodFromHWC());
+ StringAppendF(&result, "%" PRId64 "\n", mScheduler->getPacesetterVsyncPeriod().ns());
if (args.size() < 2) return;
const auto name = String8(args[1]);
@@ -6308,7 +5650,7 @@
sTimestamp = now;
SFTRACE_CALL();
- mDrawingState.traverse([&](Layer* layer) { layer->logFrameStats(); });
+ traverseLegacyLayers([&](Layer* layer) { layer->logFrameStats(); });
}
void SurfaceFlinger::appendSfConfigString(std::string& result) const {
@@ -6332,11 +5674,6 @@
// TODO(b/241285876): Move to DisplayModeController.
dumper.dump("debugDisplayModeSetByBackdoor"sv, mDebugDisplayModeSetByBackdoor);
dumper.eol();
-
- StringAppendF(&result,
- " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64
- " ns\n\n",
- dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
}
void SurfaceFlinger::dumpEvents(std::string& result) const {
@@ -6616,22 +5953,8 @@
.get();
}
-void SurfaceFlinger::dumpOffscreenLayers(std::string& result) {
- auto future = mScheduler->schedule([this] {
- std::string result;
- for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* layer) { layer->dumpOffscreenDebugInfo(result); });
- }
- return result;
- });
-
- result.append("Offscreen Layers:\n");
- result.append(future.get());
-}
-
void SurfaceFlinger::dumpHwcLayersMinidump(std::string& result) const {
- for (const auto& [token, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) {
+ for (const auto& [token, display] : mDisplays) {
const auto displayId = HalDisplayId::tryCast(display->getId());
if (!displayId) {
continue;
@@ -7302,15 +6625,9 @@
return NO_ERROR;
}
case 1039: {
- PhysicalDisplayId displayId = [&]() {
- Mutex::Autolock lock(mStateLock);
- return getDefaultDisplayDeviceLocked()->getPhysicalId();
- }();
-
- auto inUid = static_cast<uid_t>(data.readInt32());
+ const auto uid = static_cast<uid_t>(data.readInt32());
const auto refreshRate = data.readFloat();
- mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{inUid, refreshRate});
- mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId);
+ mScheduler->setPreferredRefreshRateForUid(FrameRateOverride{uid, refreshRate});
return NO_ERROR;
}
// Toggle caching feature
@@ -7499,7 +6816,7 @@
// Update the overlay on the main thread to avoid race conditions with
// RefreshRateSelector::getActiveMode
- static_cast<void>(mScheduler->schedule([=, this] {
+ static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
if (!display) {
ALOGW("%s: default display is null", __func__);
@@ -7507,15 +6824,9 @@
}
if (!display->isRefreshRateOverlayEnabled()) return;
- const auto desiredModeIdOpt =
- mDisplayModeController.getDesiredMode(display->getPhysicalId())
- .transform([](const display::DisplayModeRequest& request) {
- return request.mode.modePtr->getId();
- });
+ const auto state = mDisplayModeController.getKernelIdleTimerState(display->getPhysicalId());
- const bool timerExpired = mKernelIdleTimerEnabled && expired;
-
- if (display->onKernelTimerChanged(desiredModeIdOpt, timerExpired)) {
+ if (display->onKernelTimerChanged(state.desiredModeIdOpt, state.isEnabled && expired)) {
mScheduler->scheduleFrame();
}
}));
@@ -7537,8 +6848,8 @@
}));
}
-std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds>
-SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId) {
+auto SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId)
+ -> std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds> {
const bool isKernelIdleTimerHwcSupported = getHwComposer().getComposer()->isSupported(
android::Hwc2::Composer::OptionalFeature::KernelIdleTimer);
const auto timeout = getIdleTimerTimeout(displayId);
@@ -7562,63 +6873,6 @@
return {std::nullopt, timeout};
}
-void SurfaceFlinger::updateKernelIdleTimer(std::chrono::milliseconds timeout,
- KernelIdleTimerController controller,
- PhysicalDisplayId displayId) {
- switch (controller) {
- case KernelIdleTimerController::HwcApi: {
- getHwComposer().setIdleTimerEnabled(displayId, timeout);
- break;
- }
- case KernelIdleTimerController::Sysprop: {
- base::SetProperty(KERNEL_IDLE_TIMER_PROP, timeout > 0ms ? "true" : "false");
- break;
- }
- }
-}
-
-void SurfaceFlinger::toggleKernelIdleTimer() {
- using KernelIdleTimerAction = scheduler::RefreshRateSelector::KernelIdleTimerAction;
-
- const auto display = getDefaultDisplayDeviceLocked();
- if (!display) {
- ALOGW("%s: default display is null", __func__);
- return;
- }
-
- // If the support for kernel idle timer is disabled for the active display,
- // don't do anything.
- const std::optional<KernelIdleTimerController> kernelIdleTimerController =
- display->refreshRateSelector().kernelIdleTimerController();
- if (!kernelIdleTimerController.has_value()) {
- return;
- }
-
- const KernelIdleTimerAction action = display->refreshRateSelector().getIdleTimerAction();
-
- switch (action) {
- case KernelIdleTimerAction::TurnOff:
- if (mKernelIdleTimerEnabled) {
- SFTRACE_INT("KernelIdleTimer", 0);
- std::chrono::milliseconds constexpr kTimerDisabledTimeout = 0ms;
- updateKernelIdleTimer(kTimerDisabledTimeout, kernelIdleTimerController.value(),
- display->getPhysicalId());
- mKernelIdleTimerEnabled = false;
- }
- break;
- case KernelIdleTimerAction::TurnOn:
- if (!mKernelIdleTimerEnabled) {
- SFTRACE_INT("KernelIdleTimer", 1);
- const std::chrono::milliseconds timeout =
- display->refreshRateSelector().getIdleTimerTimeout();
- updateKernelIdleTimer(timeout, kernelIdleTimerController.value(),
- display->getPhysicalId());
- mKernelIdleTimerEnabled = true;
- }
- break;
- }
-}
-
// A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
class WindowDisconnector {
public:
@@ -7739,7 +6993,8 @@
const sp<IScreenCaptureListener>& captureListener) {
SFTRACE_CALL();
- status_t validate = validateScreenshotPermissions(args);
+ const auto& captureArgs = args.captureArgs;
+ status_t validate = validateScreenshotPermissions(captureArgs);
if (validate != OK) {
ALOGD("Permission denied to captureDisplay");
invokeScreenCaptureError(validate, captureListener);
@@ -7752,7 +7007,7 @@
return;
}
- if (args.captureSecureLayers && !hasCaptureBlackoutContentPermission()) {
+ if (captureArgs.captureSecureLayers && !hasCaptureBlackoutContentPermission()) {
ALOGD("Attempting to capture secure layers without CAPTURE_BLACKOUT_CONTENT");
invokeScreenCaptureError(PERMISSION_DENIED, captureListener);
return;
@@ -7778,7 +7033,7 @@
reqSize = display->getLayerStackSpaceRect().getSize();
}
- for (const auto& handle : args.excludeHandles) {
+ for (const auto& handle : captureArgs.excludeHandles) {
uint32_t excludeLayer = LayerHandle::getLayerId(handle);
if (excludeLayer != UNASSIGNED_LAYER_ID) {
excludeLayerIds.emplace(excludeLayer);
@@ -7791,17 +7046,21 @@
}
GetLayerSnapshotsFunction getLayerSnapshotsFn =
- getLayerSnapshotsForScreenshots(layerStack, args.uid, std::move(excludeLayerIds));
+ getLayerSnapshotsForScreenshots(layerStack, captureArgs.uid,
+ std::move(excludeLayerIds));
ftl::Flags<RenderArea::Options> options;
- if (args.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS;
- if (args.hintForSeamlessTransition)
+ if (captureArgs.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS;
+ if (captureArgs.hintForSeamlessTransition)
options |= RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION;
captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>,
- args.sourceCrop, reqSize, args.dataspace,
+ gui::aidl_utils::fromARect(captureArgs.sourceCrop),
+ reqSize,
+ static_cast<ui::Dataspace>(captureArgs.dataspace),
displayWeak, options),
- getLayerSnapshotsFn, reqSize, args.pixelFormat, args.allowProtected,
- args.grayscale, captureListener);
+ getLayerSnapshotsFn, reqSize,
+ static_cast<ui::PixelFormat>(captureArgs.pixelFormat),
+ captureArgs.allowProtected, captureArgs.grayscale, captureListener);
}
void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args,
@@ -7854,10 +7113,11 @@
if (args.hintForSeamlessTransition)
options |= RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION;
captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>,
- Rect(), size, args.dataspace, displayWeak,
- options),
- getLayerSnapshotsFn, size, args.pixelFormat, kAllowProtected, kGrayscale,
- captureListener);
+ Rect(), size,
+ static_cast<ui::Dataspace>(args.dataspace),
+ displayWeak, options),
+ getLayerSnapshotsFn, size, static_cast<ui::PixelFormat>(args.pixelFormat),
+ kAllowProtected, kGrayscale, captureListener);
}
ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) {
@@ -7870,20 +7130,23 @@
const sp<IScreenCaptureListener>& captureListener) {
SFTRACE_CALL();
- status_t validate = validateScreenshotPermissions(args);
+ const auto& captureArgs = args.captureArgs;
+
+ status_t validate = validateScreenshotPermissions(captureArgs);
if (validate != OK) {
ALOGD("Permission denied to captureLayers");
invokeScreenCaptureError(validate, captureListener);
return;
}
+ auto crop = gui::aidl_utils::fromARect(captureArgs.sourceCrop);
+
ui::Size reqSize;
sp<Layer> parent;
- Rect crop(args.sourceCrop);
std::unordered_set<uint32_t> excludeLayerIds;
- ui::Dataspace dataspace = args.dataspace;
+ ui::Dataspace dataspace = static_cast<ui::Dataspace>(captureArgs.dataspace);
- if (args.captureSecureLayers && !hasCaptureBlackoutContentPermission()) {
+ if (captureArgs.captureSecureLayers && !hasCaptureBlackoutContentPermission()) {
ALOGD("Attempting to capture secure layers without CAPTURE_BLACKOUT_CONTENT");
invokeScreenCaptureError(PERMISSION_DENIED, captureListener);
return;
@@ -7900,26 +7163,27 @@
}
Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getDrawingState());
- if (args.sourceCrop.width() <= 0) {
+ if (crop.width() <= 0) {
crop.left = 0;
crop.right = parentSourceBounds.getWidth();
}
- if (args.sourceCrop.height() <= 0) {
+ if (crop.height() <= 0) {
crop.top = 0;
crop.bottom = parentSourceBounds.getHeight();
}
- if (crop.isEmpty() || args.frameScaleX <= 0.0f || args.frameScaleY <= 0.0f) {
+ if (crop.isEmpty() || captureArgs.frameScaleX <= 0.0f || captureArgs.frameScaleY <= 0.0f) {
// Error out if the layer has no source bounds (i.e. they are boundless) and a source
// crop was not specified, or an invalid frame scale was provided.
ALOGD("Boundless layer, unspecified crop, or invalid frame scale to captureLayers");
invokeScreenCaptureError(BAD_VALUE, captureListener);
return;
}
- reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY);
+ reqSize = ui::Size(crop.width() * captureArgs.frameScaleX,
+ crop.height() * captureArgs.frameScaleY);
- for (const auto& handle : args.excludeHandles) {
+ for (const auto& handle : captureArgs.excludeHandles) {
uint32_t excludeLayer = LayerHandle::getLayerId(handle);
if (excludeLayer != UNASSIGNED_LAYER_ID) {
excludeLayerIds.emplace(excludeLayer);
@@ -7945,8 +7209,9 @@
}
GetLayerSnapshotsFunction getLayerSnapshotsFn =
- getLayerSnapshotsForScreenshots(parent->sequence, args.uid, std::move(excludeLayerIds),
- args.childrenOnly, parentCrop);
+ getLayerSnapshotsForScreenshots(parent->sequence, captureArgs.uid,
+ std::move(excludeLayerIds), args.childrenOnly,
+ parentCrop);
if (captureListener == nullptr) {
ALOGD("capture screen must provide a capture listener callback");
@@ -7955,14 +7220,15 @@
}
ftl::Flags<RenderArea::Options> options;
- if (args.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS;
- if (args.hintForSeamlessTransition)
+ if (captureArgs.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS;
+ if (captureArgs.hintForSeamlessTransition)
options |= RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION;
captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<LayerRenderAreaBuilder>, crop,
reqSize, dataspace, parent, args.childrenOnly,
options),
- getLayerSnapshotsFn, reqSize, args.pixelFormat, args.allowProtected,
- args.grayscale, captureListener);
+ getLayerSnapshotsFn, reqSize,
+ static_cast<ui::PixelFormat>(captureArgs.pixelFormat),
+ captureArgs.allowProtected, captureArgs.grayscale, captureListener);
}
// Creates a Future release fence for a layer and keeps track of it in a list to
@@ -8503,13 +7769,9 @@
const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy();
ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
- // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
- // be depending in this callback.
- if (const auto activeMode = selector.getActiveMode(); displayId == mActiveDisplayId) {
- mScheduler->onPrimaryDisplayModeChanged(scheduler::Cycle::Render, activeMode);
- toggleKernelIdleTimer();
- } else {
- mScheduler->onNonPrimaryDisplayModeChanged(scheduler::Cycle::Render, activeMode);
+ if (const bool isPacesetter =
+ mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode())) {
+ mDisplayModeController.updateKernelIdleTimer(displayId);
}
auto preferredModeOpt = getPreferredDisplayMode(displayId, currentPolicy.defaultMode);
@@ -8533,10 +7795,7 @@
setDesiredMode({std::move(preferredMode), .emitEvent = true});
// Update the frameRateOverride list as the display render rate might have changed
- if (mScheduler->updateFrameRateOverrides(scheduler::GlobalSignals{}, preferredFps)) {
- triggerOnFrameRateOverridesChanged();
- }
-
+ mScheduler->updateFrameRateOverrides(scheduler::GlobalSignals{}, preferredFps);
return NO_ERROR;
}
@@ -8629,13 +7888,12 @@
void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
mNumLayers++;
if (!layer->isRemovedFromCurrentState()) {
- mScheduler->registerLayer(layer);
+ mScheduler->registerLayer(layer, scheduler::FrameRateCompatibility::Default);
}
}
void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
mNumLayers--;
- removeHierarchyFromOffscreenLayers(layer);
if (!layer->isRemovedFromCurrentState()) {
mScheduler->deregisterLayer(layer);
}
@@ -8649,24 +7907,6 @@
scheduleCommit(FrameHint::kActive);
}
-// WARNING: ONLY CALL THIS FROM LAYER DTOR
-// Here we add children in the current state to offscreen layers and remove the
-// layer itself from the offscreen layer list. Since
-// this is the dtor, it is safe to access the current state. This keeps us
-// from dangling children layers such that they are not reachable from the
-// Drawing state nor the offscreen layer list
-// See b/141111965
-void SurfaceFlinger::removeHierarchyFromOffscreenLayers(Layer* layer) {
- for (auto& child : layer->getCurrentChildren()) {
- mOffscreenLayers.emplace(child.get());
- }
- mOffscreenLayers.erase(layer);
-}
-
-void SurfaceFlinger::removeFromOffscreenLayers(Layer* layer) {
- mOffscreenLayers.erase(layer);
-}
-
status_t SurfaceFlinger::setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
float lightPosY, float lightPosZ,
float lightRadius) {
@@ -8696,13 +7936,7 @@
}
status_t SurfaceFlinger::setGameModeFrameRateOverride(uid_t uid, float frameRate) {
- PhysicalDisplayId displayId = [&]() {
- Mutex::Autolock lock(mStateLock);
- return getDefaultDisplayDeviceLocked()->getPhysicalId();
- }();
-
- mScheduler->setGameModeFrameRateForUid(FrameRateOverride{static_cast<uid_t>(uid), frameRate});
- mScheduler->onFrameRateOverridesChanged(scheduler::Cycle::Render, displayId);
+ mScheduler->setGameModeFrameRateForUid(FrameRateOverride{uid, frameRate});
return NO_ERROR;
}
@@ -8844,20 +8078,6 @@
} else {
parent->addChild(layer);
}
-
- ui::LayerStack layerStack = layer->getLayerStack(LayerVector::StateSet::Current);
- sp<const DisplayDevice> hintDisplay;
- // Find the display that includes the layer.
- for (const auto& [token, display] : mDisplays) {
- if (display->getLayerStack() == layerStack) {
- hintDisplay = display;
- break;
- }
- }
-
- if (hintDisplay) {
- layer->updateTransformHint(hintDisplay->getTransformHint());
- }
}
void SurfaceFlinger::sample() {
@@ -8902,8 +8122,6 @@
mActiveDisplayId = activeDisplay.getPhysicalId();
activeDisplay.getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
- mScheduler->resetPhaseConfiguration(mDisplayModeController.getActiveMode(mActiveDisplayId).fps);
-
// TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
@@ -9027,92 +8245,6 @@
return nullptr;
}
-bool SurfaceFlinger::commitMirrorDisplays(VsyncId vsyncId) {
- std::vector<MirrorDisplayState> mirrorDisplays;
- {
- std::scoped_lock<std::mutex> lock(mMirrorDisplayLock);
- mirrorDisplays = std::move(mMirrorDisplays);
- mMirrorDisplays.clear();
- if (mirrorDisplays.size() == 0) {
- return false;
- }
- }
-
- sp<IBinder> unused;
- for (const auto& mirrorDisplay : mirrorDisplays) {
- // Set mirror layer's default layer stack to -1 so it doesn't end up rendered on a display
- // accidentally.
- sp<Layer> rootMirrorLayer = LayerHandle::getLayer(mirrorDisplay.rootHandle);
- ssize_t idx = mCurrentState.layersSortedByZ.indexOf(rootMirrorLayer);
- bool ret = rootMirrorLayer->setLayerStack(ui::LayerStack::fromValue(-1));
- if (idx >= 0 && ret) {
- mCurrentState.layersSortedByZ.removeAt(idx);
- mCurrentState.layersSortedByZ.add(rootMirrorLayer);
- }
-
- for (const auto& layer : mDrawingState.layersSortedByZ) {
- if (layer->getLayerStack() != mirrorDisplay.layerStack ||
- layer->isInternalDisplayOverlay()) {
- continue;
- }
-
- LayerCreationArgs mirrorArgs(this, mirrorDisplay.client, "MirrorLayerParent",
- ISurfaceComposerClient::eNoColorFill,
- gui::LayerMetadata());
- sp<Layer> childMirror;
- {
- Mutex::Autolock lock(mStateLock);
- createEffectLayer(mirrorArgs, &unused, &childMirror);
- MUTEX_ALIAS(mStateLock, childMirror->mFlinger->mStateLock);
- childMirror->setClonedChild(layer->createClone());
- childMirror->reparent(mirrorDisplay.rootHandle);
- }
- // lock on mStateLock needs to be released before binder handle gets destroyed
- unused.clear();
- }
- }
- return true;
-}
-
-bool SurfaceFlinger::commitCreatedLayers(VsyncId vsyncId,
- std::vector<LayerCreatedState>& createdLayers) {
- if (createdLayers.size() == 0) {
- return false;
- }
-
- Mutex::Autolock _l(mStateLock);
- for (const auto& createdLayer : createdLayers) {
- handleLayerCreatedLocked(createdLayer, vsyncId);
- }
- mLayersAdded = true;
- return mLayersAdded;
-}
-
-void SurfaceFlinger::updateLayerMetadataSnapshot() {
- LayerMetadata parentMetadata;
- for (const auto& layer : mDrawingState.layersSortedByZ) {
- layer->updateMetadataSnapshot(parentMetadata);
- }
-
- std::unordered_set<Layer*> visited;
- mDrawingState.traverse([&visited](Layer* layer) {
- if (visited.find(layer) != visited.end()) {
- return;
- }
-
- // If the layer isRelativeOf, then either it's relative metadata will be set
- // recursively when updateRelativeMetadataSnapshot is called on its relative parent or
- // it's relative parent has been deleted. Clear the layer's relativeLayerMetadata to ensure
- // that layers with deleted relative parents don't hold stale relativeLayerMetadata.
- if (layer->getDrawingState().isRelativeOf) {
- layer->editLayerSnapshot()->relativeLayerMetadata = {};
- return;
- }
-
- layer->updateRelativeMetadataSnapshot({}, visited);
- });
-}
-
void SurfaceFlinger::moveSnapshotsFromCompositionArgs(
compositionengine::CompositionRefreshArgs& refreshArgs,
const std::vector<std::pair<Layer*, LayerFE*>>& layers) {
@@ -9285,33 +8417,6 @@
};
}
-frontend::Update SurfaceFlinger::flushLifecycleUpdates() {
- frontend::Update update;
- SFTRACE_NAME("TransactionHandler:flushTransactions");
- // Locking:
- // 1. to prevent onHandleDestroyed from being called while the state lock is held,
- // we must keep a copy of the transactions (specifically the composer
- // states) around outside the scope of the lock.
- // 2. Transactions and created layers do not share a lock. To prevent applying
- // transactions with layers still in the createdLayer queue, flush the transactions
- // before committing the created layers.
- mTransactionHandler.collectTransactions();
- update.transactions = mTransactionHandler.flushTransactions();
- {
- // TODO(b/238781169) lockless queue this and keep order.
- std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
- update.layerCreatedStates = std::move(mCreatedLayers);
- mCreatedLayers.clear();
- update.newLayers = std::move(mNewLayers);
- mNewLayers.clear();
- update.layerCreationArgs = std::move(mNewLayerArgs);
- mNewLayerArgs.clear();
- update.destroyedHandles = std::move(mDestroyedHandles);
- mDestroyedHandles.clear();
- }
- return update;
-}
-
void SurfaceFlinger::doActiveLayersTracingIfNeeded(bool isCompositionComputed,
bool visibleRegionDirty, TimePoint time,
VsyncId vsyncId) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 9f85a9f..282c8cf 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -273,7 +273,7 @@
enum class FrameHint { kNone, kActive };
// Schedule commit of transactions on the main thread ahead of the next VSYNC.
- void scheduleCommit(FrameHint);
+ void scheduleCommit(FrameHint, Duration workDurationSlack = Duration::fromNs(0));
// As above, but also force composite regardless if transactions were committed.
void scheduleComposite(FrameHint);
// As above, but also force dirty geometry to repaint.
@@ -292,9 +292,6 @@
void onLayerDestroyed(Layer*);
void onLayerUpdate();
- void removeHierarchyFromOffscreenLayers(Layer* layer);
- void removeFromOffscreenLayers(Layer* layer);
-
// Called when all clients have released all their references to
// this layer. The layer may still be kept alive by its parents but
// the client can no longer modify this layer directly.
@@ -313,7 +310,6 @@
// Disables expensive rendering for all displays
// This is scheduled on the main thread
void disableExpensiveRendering();
- FloatRect getMaxDisplayBounds();
// If set, composition engine tries to predict the composition strategy provided by HWC
// based on the previous frame. If the strategy can be predicted, gpu composition will
@@ -560,7 +556,7 @@
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const;
status_t setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
@@ -698,7 +694,6 @@
void requestHardwareVsync(PhysicalDisplayId, bool) override;
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override;
void kernelTimerChanged(bool expired) override;
- void triggerOnFrameRateOverridesChanged() override;
void onChoreographerAttached() override;
void onExpectedPresentTimePosted(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>,
Fps renderRate) override;
@@ -709,22 +704,13 @@
// ICEPowerCallback overrides:
void notifyCpuLoadUp() override;
- // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
- void toggleKernelIdleTimer() REQUIRES(mStateLock);
-
using KernelIdleTimerController = scheduler::RefreshRateSelector::KernelIdleTimerController;
// Get the controller and timeout that will help decide how the kernel idle timer will be
// configured and what value to use as the timeout.
std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds>
getKernelIdleTimerProperties(PhysicalDisplayId) REQUIRES(mStateLock);
- // Updates the kernel idle timer either through HWC or through sysprop
- // depending on which controller is provided
- void updateKernelIdleTimer(std::chrono::milliseconds timeoutMs, KernelIdleTimerController,
- PhysicalDisplayId) REQUIRES(mStateLock);
- // Keeps track of whether the kernel idle timer is currently enabled, so we don't have to
- // make calls to sys prop each time.
- bool mKernelIdleTimerEnabled = false;
+
// Show spinner with refresh rate overlay
bool mRefreshRateOverlaySpinner = false;
// Show render rate with refresh rate overlay
@@ -764,17 +750,11 @@
const scheduler::RefreshRateSelector&)
REQUIRES(mStateLock, kMainThreadContext);
- void commitTransactionsLegacy() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
void commitTransactions() REQUIRES(kMainThreadContext, mStateLock);
void commitTransactionsLocked(uint32_t transactionFlags)
REQUIRES(mStateLock, kMainThreadContext);
void doCommitTransactions() REQUIRES(mStateLock);
- // Returns whether a new buffer has been latched.
- bool latchBuffers();
-
- void updateLayerGeometry();
- void updateLayerMetadataSnapshot();
std::vector<std::pair<Layer*, LayerFE*>> moveSnapshotsToCompositionArgs(
compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly)
REQUIRES(kMainThreadContext);
@@ -782,13 +762,9 @@
const std::vector<std::pair<Layer*, LayerFE*>>& layers)
REQUIRES(kMainThreadContext);
// Return true if we must composite this frame
- bool updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
- bool& out) REQUIRES(kMainThreadContext);
- // Return true if we must composite this frame
bool updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
bool& out) REQUIRES(kMainThreadContext);
void updateLayerHistory(nsecs_t now) REQUIRES(kMainThreadContext);
- frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
void updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) REQUIRES(kMainThreadContext);
void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
@@ -831,9 +807,6 @@
const TransactionHandler::TransactionFlushState& flushState)
REQUIRES(kMainThreadContext);
- uint32_t setClientStateLocked(const FrameTimelineInfo&, ResolvedComposerState&,
- int64_t desiredPresentTime, bool isAutoTimestamp,
- int64_t postTime, uint64_t transactionId) REQUIRES(mStateLock);
uint32_t updateLayerCallbacksAndStats(const FrameTimelineInfo&, ResolvedComposerState&,
int64_t desiredPresentTime, bool isAutoTimestamp,
int64_t postTime, uint64_t transactionId)
@@ -848,8 +821,6 @@
// Clears and returns the masked bits.
uint32_t clearTransactionFlags(uint32_t mask);
- void commitOffscreenLayers();
-
static LatchUnsignaledConfig getLatchUnsignaledConfig();
bool shouldLatchUnsignaled(const layer_state_t&, size_t numStates, bool firstTransaction) const;
bool applyTransactionsLocked(std::vector<TransactionState>& transactions, VsyncId)
@@ -883,9 +854,6 @@
const sp<Layer>& layer, const wp<Layer>& parentLayer,
uint32_t* outTransformHint);
- // Traverse through all the layers and compute and cache its bounds.
- void computeLayerBounds();
-
// Creates a promise for a future release fence for a layer. This allows for
// the layer to keep track of when its buffer can be released.
void attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE, ui::LayerStack layerStack);
@@ -980,18 +948,12 @@
return nullptr;
}
- // Returns the primary display or (for foldables) the active display, assuming that the inner
- // and outer displays have mutually exclusive power states.
+ // Returns the primary display or (for foldables) the active display.
sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const REQUIRES(mStateLock) {
return const_cast<SurfaceFlinger*>(this)->getDefaultDisplayDeviceLocked();
}
sp<DisplayDevice> getDefaultDisplayDeviceLocked() REQUIRES(mStateLock) {
- if (const auto display = getDisplayDeviceLocked(mActiveDisplayId)) {
- return display;
- }
- // The active display is outdated, so fall back to the primary display.
- mActiveDisplayId = getPrimaryDisplayIdLocked();
return getDisplayDeviceLocked(mActiveDisplayId);
}
@@ -1091,13 +1053,6 @@
const DisplayDeviceState& drawingState)
REQUIRES(mStateLock, kMainThreadContext);
- void dispatchDisplayModeChangeEvent(PhysicalDisplayId, const scheduler::FrameRateMode&);
-
- /*
- * VSYNC
- */
- nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock);
-
/*
* Display identification
*/
@@ -1187,7 +1142,6 @@
void dumpHwc(std::string& result) const;
perfetto::protos::LayersProto dumpProtoFromMainThread(
uint32_t traceFlags = LayerTracing::TRACE_ALL) EXCLUDES(mStateLock);
- void dumpOffscreenLayers(std::string& result) EXCLUDES(mStateLock);
void dumpPlannerInfo(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
status_t doDump(int fd, const DumpArgs& args, bool asProto);
@@ -1291,18 +1245,20 @@
bool mForceTransactionDisplayChange = false;
bool mUpdateAttachedChoreographer = false;
- // Set if LayerMetadata has changed since the last LayerMetadata snapshot.
- bool mLayerMetadataSnapshotNeeded = false;
+ struct LayerIntHash {
+ size_t operator()(const std::pair<sp<Layer>, gui::GameMode>& k) const {
+ return std::hash<Layer*>()(k.first.get()) ^
+ std::hash<int32_t>()(static_cast<int32_t>(k.second));
+ }
+ };
// TODO(b/238781169) validate these on composition
// Tracks layers that have pending frames which are candidates for being
// latched.
- std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithQueuedFrames;
+ std::unordered_set<std::pair<sp<Layer>, gui::GameMode>, LayerIntHash> mLayersWithQueuedFrames;
std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithBuffersRemoved;
std::unordered_set<uint32_t> mLayersIdsWithQueuedFrames;
- // Tracks layers that need to update a display's dirty region.
- std::vector<sp<Layer>> mLayersPendingRefresh;
// Sorted list of layers that were composed during previous frame. This is used to
// avoid an expensive traversal of the layer hierarchy when there are no
// visible region changes. Because this is a list of strong pointers, this will
@@ -1330,7 +1286,7 @@
display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
- // The inner or outer display for foldables, assuming they have mutually exclusive power states.
+ // The inner or outer display for foldables, while unfolded or folded, respectively.
std::atomic<PhysicalDisplayId> mActiveDisplayId;
display::DisplayModeController mDisplayModeController;
@@ -1431,12 +1387,6 @@
// Flag used to set override desired display mode from backdoor
bool mDebugDisplayModeSetByBackdoor = false;
- // A set of layers that have no parent so they are not drawn on screen.
- // Should only be accessed by the main thread.
- // The Layer pointer is removed from the set when the destructor is called so there shouldn't
- // be any issues with a raw pointer referencing an invalid object.
- std::unordered_set<Layer*> mOffscreenLayers;
-
BufferCountTracker mBufferCountTracker;
std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
@@ -1447,22 +1397,8 @@
// A temporay pool that store the created layers and will be added to current state in main
// thread.
std::vector<LayerCreatedState> mCreatedLayers GUARDED_BY(mCreatedLayersLock);
- bool commitCreatedLayers(VsyncId, std::vector<LayerCreatedState>& createdLayers);
void handleLayerCreatedLocked(const LayerCreatedState&, VsyncId) REQUIRES(mStateLock);
- mutable std::mutex mMirrorDisplayLock;
- struct MirrorDisplayState {
- MirrorDisplayState(ui::LayerStack layerStack, sp<IBinder>& rootHandle,
- const sp<Client>& client)
- : layerStack(layerStack), rootHandle(rootHandle), client(client) {}
-
- ui::LayerStack layerStack;
- sp<IBinder> rootHandle;
- const sp<Client> client;
- };
- std::vector<MirrorDisplayState> mMirrorDisplays GUARDED_BY(mMirrorDisplayLock);
- bool commitMirrorDisplays(VsyncId);
-
std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint;
// Must only be accessed on the main thread.
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 0bafb71..b189598 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -62,12 +62,12 @@
proto.mutable_layer_changes()->Reserve(static_cast<int32_t>(t.states.size()));
for (auto& layerState : t.states) {
- proto.mutable_layer_changes()->Add(std::move(toProto(layerState)));
+ proto.mutable_layer_changes()->Add(toProto(layerState));
}
proto.mutable_display_changes()->Reserve(static_cast<int32_t>(t.displays.size()));
for (auto& displayState : t.displays) {
- proto.mutable_display_changes()->Add(std::move(toProto(displayState)));
+ proto.mutable_display_changes()->Add(toProto(displayState));
}
proto.mutable_merged_transaction_ids()->Reserve(
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
index 696f348..bc9f809 100644
--- a/services/surfaceflinger/Tracing/TransactionTracing.cpp
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -244,7 +244,7 @@
static_cast<int32_t>(update.createdLayers.size()));
for (const auto& args : update.createdLayers) {
- entryProto.mutable_added_layers()->Add(std::move(mProtoParser.toProto(args)));
+ entryProto.mutable_added_layers()->Add(mProtoParser.toProto(args));
}
entryProto.mutable_destroyed_layers()->Reserve(
@@ -276,7 +276,7 @@
static_cast<int32_t>(update.displayInfos.size()));
for (auto& [layerStack, displayInfo] : update.displayInfos) {
entryProto.mutable_displays()->Add(
- std::move(mProtoParser.toProto(displayInfo, layerStack.id)));
+ mProtoParser.toProto(displayInfo, layerStack.id));
}
}
diff --git a/services/surfaceflinger/Utils/RingBuffer.h b/services/surfaceflinger/Utils/RingBuffer.h
index 198e7b2..215472b 100644
--- a/services/surfaceflinger/Utils/RingBuffer.h
+++ b/services/surfaceflinger/Utils/RingBuffer.h
@@ -43,8 +43,10 @@
}
T& front() { return (*this)[0]; }
+ const T& front() const { return (*this)[0]; }
T& back() { return (*this)[size() - 1]; }
+ const T& back() const { return (*this)[size() - 1]; }
T& operator[](size_t index) {
return mBuffer[(static_cast<size_t>(mHead + 1) + index) % mCount];
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index a56bb51..07c720f 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -119,6 +119,7 @@
DUMP_READ_ONLY_FLAG(connected_display);
DUMP_READ_ONLY_FLAG(enable_small_area_detection);
DUMP_READ_ONLY_FLAG(frame_rate_category_mrr);
+ DUMP_READ_ONLY_FLAG(view_set_requested_frame_rate_mrr);
DUMP_READ_ONLY_FLAG(misc1);
DUMP_READ_ONLY_FLAG(vrr_config);
DUMP_READ_ONLY_FLAG(hotplug2);
@@ -222,6 +223,8 @@
FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "")
FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "")
FLAG_MANAGER_READ_ONLY_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr")
+FLAG_MANAGER_READ_ONLY_FLAG(view_set_requested_frame_rate_mrr,
+ "debug.sf.view_set_requested_frame_rate_mrr")
FLAG_MANAGER_READ_ONLY_FLAG(misc1, "")
FLAG_MANAGER_READ_ONLY_FLAG(vrr_config, "debug.sf.enable_vrr_config")
FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 8799295..a4b4a2b 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -56,6 +56,7 @@
/// Trunk stable readonly flags ///
bool connected_display() const;
bool frame_rate_category_mrr() const;
+ bool view_set_requested_frame_rate_mrr() const;
bool enable_small_area_detection() const;
bool misc1() const;
bool vrr_config() const;
diff --git a/services/surfaceflinger/common/include/common/trace.h b/services/surfaceflinger/common/include/common/trace.h
index 0d7ac9b..dc5716b 100644
--- a/services/surfaceflinger/common/include/common/trace.h
+++ b/services/surfaceflinger/common/include/common/trace.h
@@ -48,7 +48,7 @@
::tracing_perfetto::traceAsyncBegin(ATRACE_TAG, name, cookie)
#define SFTRACE_ASYNC_END(name, cookie) ::tracing_perfetto::traceAsyncEnd(ATRACE_TAG, name, cookie)
#define SFTRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie) \
- ::tracing_perfetto::traceAsyncBeginForTrack(ATRACE_TAG, track_name, name, cookie)
+ ::tracing_perfetto::traceAsyncBeginForTrack(ATRACE_TAG, name, track_name, cookie)
#define SFTRACE_ASYNC_FOR_TRACK_END(track_name, cookie) \
::tracing_perfetto::traceAsyncEndForTrack(ATRACE_TAG, track_name, cookie)
#define SFTRACE_INSTANT(name) ::tracing_perfetto::traceInstant(ATRACE_TAG, name)
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
index f77b137..0a69a72 100644
--- a/services/surfaceflinger/layerproto/Android.bp
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -8,14 +8,9 @@
default_team: "trendy_team_android_core_graphics_stack",
}
-cc_library {
- name: "liblayers_proto",
+cc_defaults {
+ name: "libsurfaceflinger_proto_deps",
export_include_dirs: ["include"],
-
- srcs: [
- "LayerProtoParser.cpp",
- ],
-
static_libs: [
"libperfetto_client_experimental",
],
@@ -31,24 +26,15 @@
],
shared_libs: [
- "android.hardware.graphics.common@1.1",
- "libgui",
- "libui",
"libprotobuf-cpp-lite",
- "libbase",
],
- cppflags: [
- "-Werror",
- "-Wno-unused-parameter",
- "-Wno-format",
- "-Wno-c++98-compat-pedantic",
- "-Wno-float-conversion",
- "-Wno-disabled-macro-expansion",
- "-Wno-float-equal",
- "-Wno-sign-conversion",
- "-Wno-padded",
- "-Wno-old-style-cast",
- "-Wno-undef",
+ header_libs: [
+ "libsurfaceflinger_proto_headers",
],
}
+
+cc_library_headers {
+ name: "libsurfaceflinger_proto_headers",
+ export_include_dirs: ["include"],
+}
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
deleted file mode 100644
index c3d0a40..0000000
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright (C) 2017 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 <android-base/stringprintf.h>
-#include <layerproto/LayerProtoParser.h>
-#include <ui/DebugUtils.h>
-
-using android::base::StringAppendF;
-using android::base::StringPrintf;
-
-namespace android {
-namespace surfaceflinger {
-
-bool sortLayers(LayerProtoParser::Layer* lhs, const LayerProtoParser::Layer* rhs) {
- uint32_t ls = lhs->layerStack;
- uint32_t rs = rhs->layerStack;
- if (ls != rs) return ls < rs;
-
- int32_t lz = lhs->z;
- int32_t rz = rhs->z;
- if (lz != rz) {
- return lz < rz;
- }
-
- return lhs->id < rhs->id;
-}
-
-LayerProtoParser::LayerTree LayerProtoParser::generateLayerTree(
- const perfetto::protos::LayersProto& layersProto) {
- LayerTree layerTree;
- layerTree.allLayers = generateLayerList(layersProto);
-
- // find and sort the top-level layers
- for (Layer& layer : layerTree.allLayers) {
- if (layer.parent == nullptr) {
- layerTree.topLevelLayers.push_back(&layer);
- }
- }
- std::sort(layerTree.topLevelLayers.begin(), layerTree.topLevelLayers.end(), sortLayers);
-
- return layerTree;
-}
-
-std::vector<LayerProtoParser::Layer> LayerProtoParser::generateLayerList(
- const perfetto::protos::LayersProto& layersProto) {
- std::vector<Layer> layerList;
- std::unordered_map<int32_t, Layer*> layerMap;
-
- // build the layer list and the layer map
- layerList.reserve(layersProto.layers_size());
- layerMap.reserve(layersProto.layers_size());
- for (int i = 0; i < layersProto.layers_size(); i++) {
- layerList.emplace_back(generateLayer(layersProto.layers(i)));
- // this works because layerList never changes capacity
- layerMap[layerList.back().id] = &layerList.back();
- }
-
- // fix up children and relatives
- for (int i = 0; i < layersProto.layers_size(); i++) {
- updateChildrenAndRelative(layersProto.layers(i), layerMap);
- }
-
- return layerList;
-}
-
-LayerProtoParser::Layer LayerProtoParser::generateLayer(
- const perfetto::protos::LayerProto& layerProto) {
- Layer layer;
- layer.id = layerProto.id();
- layer.name = layerProto.name();
- layer.type = layerProto.type();
- layer.transparentRegion = generateRegion(layerProto.transparent_region());
- layer.visibleRegion = generateRegion(layerProto.visible_region());
- layer.damageRegion = generateRegion(layerProto.damage_region());
- layer.layerStack = layerProto.layer_stack();
- layer.z = layerProto.z();
- layer.position = {layerProto.position().x(), layerProto.position().y()};
- layer.requestedPosition = {layerProto.requested_position().x(),
- layerProto.requested_position().y()};
- layer.size = {layerProto.size().w(), layerProto.size().h()};
- layer.crop = generateRect(layerProto.crop());
- layer.isOpaque = layerProto.is_opaque();
- layer.invalidate = layerProto.invalidate();
- layer.dataspace = layerProto.dataspace();
- layer.pixelFormat = layerProto.pixel_format();
- layer.color = {layerProto.color().r(), layerProto.color().g(), layerProto.color().b(),
- layerProto.color().a()};
- layer.requestedColor = {layerProto.requested_color().r(), layerProto.requested_color().g(),
- layerProto.requested_color().b(), layerProto.requested_color().a()};
- layer.flags = layerProto.flags();
- layer.transform = generateTransform(layerProto.transform());
- layer.requestedTransform = generateTransform(layerProto.requested_transform());
- layer.activeBuffer = generateActiveBuffer(layerProto.active_buffer());
- layer.bufferTransform = generateTransform(layerProto.buffer_transform());
- layer.queuedFrames = layerProto.queued_frames();
- layer.refreshPending = layerProto.refresh_pending();
- layer.isProtected = layerProto.is_protected();
- layer.isTrustedOverlay = layerProto.is_trusted_overlay();
- layer.cornerRadius = layerProto.corner_radius();
- layer.backgroundBlurRadius = layerProto.background_blur_radius();
- for (const auto& entry : layerProto.metadata()) {
- const std::string& dataStr = entry.second;
- std::vector<uint8_t>& outData = layer.metadata.mMap[entry.first];
- outData.resize(dataStr.size());
- memcpy(outData.data(), dataStr.data(), dataStr.size());
- }
- layer.cornerRadiusCrop = generateFloatRect(layerProto.corner_radius_crop());
- layer.shadowRadius = layerProto.shadow_radius();
- layer.ownerUid = layerProto.owner_uid();
- return layer;
-}
-
-LayerProtoParser::Region LayerProtoParser::generateRegion(
- const perfetto::protos::RegionProto& regionProto) {
- LayerProtoParser::Region region;
- for (int i = 0; i < regionProto.rect_size(); i++) {
- const perfetto::protos::RectProto& rectProto = regionProto.rect(i);
- region.rects.push_back(generateRect(rectProto));
- }
-
- return region;
-}
-
-LayerProtoParser::Rect LayerProtoParser::generateRect(
- const perfetto::protos::RectProto& rectProto) {
- LayerProtoParser::Rect rect;
- rect.left = rectProto.left();
- rect.top = rectProto.top();
- rect.right = rectProto.right();
- rect.bottom = rectProto.bottom();
-
- return rect;
-}
-
-LayerProtoParser::FloatRect LayerProtoParser::generateFloatRect(
- const perfetto::protos::FloatRectProto& rectProto) {
- LayerProtoParser::FloatRect rect;
- rect.left = rectProto.left();
- rect.top = rectProto.top();
- rect.right = rectProto.right();
- rect.bottom = rectProto.bottom();
-
- return rect;
-}
-
-LayerProtoParser::Transform LayerProtoParser::generateTransform(
- const perfetto::protos::TransformProto& transformProto) {
- LayerProtoParser::Transform transform;
- transform.dsdx = transformProto.dsdx();
- transform.dtdx = transformProto.dtdx();
- transform.dsdy = transformProto.dsdy();
- transform.dtdy = transformProto.dtdy();
-
- return transform;
-}
-
-LayerProtoParser::ActiveBuffer LayerProtoParser::generateActiveBuffer(
- const perfetto::protos::ActiveBufferProto& activeBufferProto) {
- LayerProtoParser::ActiveBuffer activeBuffer;
- activeBuffer.width = activeBufferProto.width();
- activeBuffer.height = activeBufferProto.height();
- activeBuffer.stride = activeBufferProto.stride();
- activeBuffer.format = activeBufferProto.format();
-
- return activeBuffer;
-}
-
-void LayerProtoParser::updateChildrenAndRelative(const perfetto::protos::LayerProto& layerProto,
- std::unordered_map<int32_t, Layer*>& layerMap) {
- auto currLayer = layerMap[layerProto.id()];
-
- for (int i = 0; i < layerProto.children_size(); i++) {
- if (layerMap.count(layerProto.children(i)) > 0) {
- currLayer->children.push_back(layerMap[layerProto.children(i)]);
- }
- }
-
- for (int i = 0; i < layerProto.relatives_size(); i++) {
- if (layerMap.count(layerProto.relatives(i)) > 0) {
- currLayer->relatives.push_back(layerMap[layerProto.relatives(i)]);
- }
- }
-
- if (layerProto.has_parent()) {
- if (layerMap.count(layerProto.parent()) > 0) {
- currLayer->parent = layerMap[layerProto.parent()];
- }
- }
-
- if (layerProto.has_z_order_relative_of()) {
- if (layerMap.count(layerProto.z_order_relative_of()) > 0) {
- currLayer->zOrderRelativeOf = layerMap[layerProto.z_order_relative_of()];
- }
- }
-}
-
-std::string LayerProtoParser::layerTreeToString(const LayerTree& layerTree) {
- std::string result;
- for (const LayerProtoParser::Layer* layer : layerTree.topLevelLayers) {
- if (layer->zOrderRelativeOf != nullptr) {
- continue;
- }
- result.append(layerToString(layer));
- }
-
- return result;
-}
-
-std::string LayerProtoParser::layerToString(const LayerProtoParser::Layer* layer) {
- std::string result;
-
- std::vector<Layer*> traverse(layer->relatives);
- for (LayerProtoParser::Layer* child : layer->children) {
- if (child->zOrderRelativeOf != nullptr) {
- continue;
- }
-
- traverse.push_back(child);
- }
-
- std::sort(traverse.begin(), traverse.end(), sortLayers);
-
- size_t i = 0;
- for (; i < traverse.size(); i++) {
- auto& relative = traverse[i];
- if (relative->z >= 0) {
- break;
- }
- result.append(layerToString(relative));
- }
- result.append(layer->to_string());
- result.append("\n");
- for (; i < traverse.size(); i++) {
- auto& relative = traverse[i];
- result.append(layerToString(relative));
- }
-
- return result;
-}
-
-std::string LayerProtoParser::ActiveBuffer::to_string() const {
- return StringPrintf("[%4ux%4u:%4u,%s]", width, height, stride,
- decodePixelFormat(format).c_str());
-}
-
-std::string LayerProtoParser::Transform::to_string() const {
- return StringPrintf("[%.2f, %.2f][%.2f, %.2f]", static_cast<double>(dsdx),
- static_cast<double>(dtdx), static_cast<double>(dsdy),
- static_cast<double>(dtdy));
-}
-
-std::string LayerProtoParser::Rect::to_string() const {
- return StringPrintf("[%3d, %3d, %3d, %3d]", left, top, right, bottom);
-}
-
-std::string LayerProtoParser::FloatRect::to_string() const {
- return StringPrintf("[%.2f, %.2f, %.2f, %.2f]", left, top, right, bottom);
-}
-
-std::string LayerProtoParser::Region::to_string(const char* what) const {
- std::string result =
- StringPrintf(" Region %s (this=%lx count=%d)\n", what, static_cast<unsigned long>(id),
- static_cast<int>(rects.size()));
-
- for (auto& rect : rects) {
- StringAppendF(&result, " %s\n", rect.to_string().c_str());
- }
-
- return result;
-}
-
-std::string LayerProtoParser::Layer::to_string() const {
- std::string result;
- StringAppendF(&result, "+ %s (%s) uid=%d\n", type.c_str(), name.c_str(), ownerUid);
- result.append(transparentRegion.to_string("TransparentRegion").c_str());
- result.append(visibleRegion.to_string("VisibleRegion").c_str());
- result.append(damageRegion.to_string("SurfaceDamageRegion").c_str());
-
- StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ", layerStack,
- z, static_cast<double>(position.x), static_cast<double>(position.y), size.x,
- size.y);
-
- StringAppendF(&result, "crop=%s, ", crop.to_string().c_str());
- StringAppendF(&result, "cornerRadius=%f, ", cornerRadius);
- StringAppendF(&result, "isProtected=%1d, ", isProtected);
- StringAppendF(&result, "isTrustedOverlay=%1d, ", isTrustedOverlay);
- StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate);
- StringAppendF(&result, "dataspace=%s, ", dataspace.c_str());
- StringAppendF(&result, "defaultPixelFormat=%s, ", pixelFormat.c_str());
- StringAppendF(&result, "backgroundBlurRadius=%1d, ", backgroundBlurRadius);
- StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ",
- static_cast<double>(color.r), static_cast<double>(color.g),
- static_cast<double>(color.b), static_cast<double>(color.a), flags);
- StringAppendF(&result, "tr=%s", transform.to_string().c_str());
- result.append("\n");
- StringAppendF(&result, " parent=%s\n", parent == nullptr ? "none" : parent->name.c_str());
- StringAppendF(&result, " zOrderRelativeOf=%s\n",
- zOrderRelativeOf == nullptr ? "none" : zOrderRelativeOf->name.c_str());
- StringAppendF(&result, " activeBuffer=%s,", activeBuffer.to_string().c_str());
- StringAppendF(&result, " tr=%s", bufferTransform.to_string().c_str());
- StringAppendF(&result, " queued-frames=%d", queuedFrames);
- StringAppendF(&result, " metadata={");
- bool first = true;
- for (const auto& entry : metadata.mMap) {
- if (!first) result.append(", ");
- first = false;
- result.append(metadata.itemToString(entry.first, ":"));
- }
- result.append("},");
- StringAppendF(&result, " cornerRadiusCrop=%s, ", cornerRadiusCrop.to_string().c_str());
- StringAppendF(&result, " shadowRadius=%.3f, ", shadowRadius);
- return result;
-}
-
-} // namespace surfaceflinger
-} // namespace android
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
deleted file mode 100644
index 79c3982..0000000
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2017 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 <layerproto/LayerProtoHeader.h>
-
-#include <gui/LayerMetadata.h>
-#include <math/vec4.h>
-
-#include <memory>
-#include <unordered_map>
-#include <vector>
-
-using android::gui::LayerMetadata;
-
-namespace android {
-namespace surfaceflinger {
-
-class LayerProtoParser {
-public:
- class ActiveBuffer {
- public:
- uint32_t width;
- uint32_t height;
- uint32_t stride;
- int32_t format;
-
- std::string to_string() const;
- };
-
- class Transform {
- public:
- float dsdx;
- float dtdx;
- float dsdy;
- float dtdy;
-
- std::string to_string() const;
- };
-
- class Rect {
- public:
- int32_t left;
- int32_t top;
- int32_t right;
- int32_t bottom;
-
- std::string to_string() const;
- };
-
- class FloatRect {
- public:
- float left;
- float top;
- float right;
- float bottom;
-
- std::string to_string() const;
- };
-
- class Region {
- public:
- uint64_t id;
- std::vector<Rect> rects;
-
- std::string to_string(const char* what) const;
- };
-
- class Layer {
- public:
- int32_t id;
- std::string name;
- std::vector<Layer*> children;
- std::vector<Layer*> relatives;
- std::string type;
- LayerProtoParser::Region transparentRegion;
- LayerProtoParser::Region visibleRegion;
- LayerProtoParser::Region damageRegion;
- uint32_t layerStack;
- int32_t z;
- float2 position;
- float2 requestedPosition;
- int2 size;
- LayerProtoParser::Rect crop;
- bool isOpaque;
- bool invalidate;
- std::string dataspace;
- std::string pixelFormat;
- half4 color;
- half4 requestedColor;
- uint32_t flags;
- Transform transform;
- Transform requestedTransform;
- Layer* parent = 0;
- Layer* zOrderRelativeOf = 0;
- LayerProtoParser::ActiveBuffer activeBuffer;
- Transform bufferTransform;
- int32_t queuedFrames;
- bool refreshPending;
- bool isProtected;
- bool isTrustedOverlay;
- float cornerRadius;
- int backgroundBlurRadius;
- LayerMetadata metadata;
- LayerProtoParser::FloatRect cornerRadiusCrop;
- float shadowRadius;
- uid_t ownerUid;
-
- std::string to_string() const;
- };
-
- class LayerTree {
- public:
- // all layers in LayersProto and in the original order
- std::vector<Layer> allLayers;
-
- // pointers to top-level layers in allLayers
- std::vector<Layer*> topLevelLayers;
- };
-
- static LayerTree generateLayerTree(const perfetto::protos::LayersProto& layersProto);
- static std::string layerTreeToString(const LayerTree& layerTree);
-
-private:
- static std::vector<Layer> generateLayerList(const perfetto::protos::LayersProto& layersProto);
- static LayerProtoParser::Layer generateLayer(const perfetto::protos::LayerProto& layerProto);
- static LayerProtoParser::Region generateRegion(
- const perfetto::protos::RegionProto& regionProto);
- static LayerProtoParser::Rect generateRect(const perfetto::protos::RectProto& rectProto);
- static LayerProtoParser::FloatRect generateFloatRect(
- const perfetto::protos::FloatRectProto& rectProto);
- static LayerProtoParser::Transform generateTransform(
- const perfetto::protos::TransformProto& transformProto);
- static LayerProtoParser::ActiveBuffer generateActiveBuffer(
- const perfetto::protos::ActiveBufferProto& activeBufferProto);
- static void updateChildrenAndRelative(const perfetto::protos::LayerProto& layerProto,
- std::unordered_map<int32_t, Layer*>& layerMap);
-
- static std::string layerToString(const LayerProtoParser::Layer* layer);
-};
-
-} // namespace surfaceflinger
-} // namespace android
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index 919ec17..886167e 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -126,6 +126,14 @@
} # override_trusted_overlay
flag {
+ name: "view_set_requested_frame_rate_mrr"
+ namespace: "core_graphics"
+ description: "Enable to use frame rate category NoPreference with fixed frame rate vote on MRR devices"
+ bug: "352206100"
+ is_fixed_read_only: true
+} # view_set_requested_frame_rate_mrr
+
+flag {
name: "vrr_bugfix_24q4"
namespace: "core_graphics"
description: "bug fixes for VRR"
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 38fc977..4d5c0fd 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -28,6 +28,7 @@
"android.hardware.graphics.common-ndk_shared",
"surfaceflinger_defaults",
"libsurfaceflinger_common_test_deps",
+ "libsurfaceflinger_proto_deps",
],
test_suites: ["device-tests"],
srcs: [
@@ -58,14 +59,12 @@
"ScreenCapture_test.cpp",
"SetFrameRate_test.cpp",
"SetGeometry_test.cpp",
- "Stress_test.cpp",
"TextureFiltering_test.cpp",
"VirtualDisplay_test.cpp",
"WindowInfosListener_test.cpp",
],
data: ["SurfaceFlinger_test.filter"],
static_libs: [
- "liblayers_proto",
"android.hardware.graphics.composer@2.1",
"libsurfaceflinger_common",
],
@@ -121,7 +120,6 @@
"libEGL",
"libGLESv2",
"libgui",
- "liblayers_proto",
"liblog",
"libprotobuf-cpp-full",
"libui",
diff --git a/services/surfaceflinger/tests/BootDisplayMode_test.cpp b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
index 4f41a81..222642f 100644
--- a/services/surfaceflinger/tests/BootDisplayMode_test.cpp
+++ b/services/surfaceflinger/tests/BootDisplayMode_test.cpp
@@ -18,7 +18,7 @@
#include <gtest/gtest.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
#include <private/gui/ComposerServiceAIDL.h>
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
index d74bd55..efab7b8 100644
--- a/services/surfaceflinger/tests/BufferGenerator.cpp
+++ b/services/surfaceflinger/tests/BufferGenerator.cpp
@@ -42,8 +42,13 @@
* through saved callback. */
class BufferListener : public ConsumerBase::FrameAvailableListener {
public:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ BufferListener(sp<BufferItemConsumer> consumer, BufferCallback callback)
+#else
BufferListener(sp<IGraphicBufferConsumer> consumer, BufferCallback callback)
- : mConsumer(consumer), mCallback(callback) {}
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ : mConsumer(consumer), mCallback(callback) {
+ }
void onFrameAvailable(const BufferItem& /*item*/) {
BufferItem item;
@@ -55,7 +60,11 @@
}
private:
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<BufferItemConsumer> mConsumer;
+#else
sp<IGraphicBufferConsumer> mConsumer;
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
BufferCallback mCallback;
};
@@ -63,6 +72,16 @@
* queue. */
void initialize(uint32_t width, uint32_t height, android_pixel_format_t format,
BufferCallback callback) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mBufferItemConsumer = sp<BufferItemConsumer>::make(GraphicBuffer::USAGE_HW_TEXTURE);
+ mBufferItemConsumer->setDefaultBufferSize(width, height);
+ mBufferItemConsumer->setDefaultBufferFormat(format);
+
+ mListener = sp<BufferListener>::make(mBufferItemConsumer, callback);
+ mBufferItemConsumer->setFrameAvailableListener(mListener);
+
+ mSurface = mBufferItemConsumer->getSurface();
+#else
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
@@ -77,6 +96,7 @@
mBufferItemConsumer->setFrameAvailableListener(mListener);
mSurface = sp<Surface>::make(producer, true);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
}
/* Used by Egl manager. The surface is never displayed. */
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index ebe11fb..e6fed63 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -20,12 +20,13 @@
#include <android/gui/ISurfaceComposer.h>
#include <gtest/gtest.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <private/android_filesystem_config.h>
#include <private/gui/ComposerServiceAIDL.h>
#include <ui/DisplayMode.h>
+#include <ui/DisplayState.h>
#include <ui/DynamicDisplayInfo.h>
#include <utils/String8.h>
#include <functional>
@@ -276,10 +277,10 @@
TEST_F(CredentialsTest, CaptureLayersTest) {
setupBackgroundSurface();
sp<GraphicBuffer> outBuffer;
- std::function<status_t()> condition = [=]() {
+ std::function<status_t()> condition = [=, this]() {
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mBGSurfaceControl->getHandle();
- captureArgs.sourceCrop = {0, 0, 1, 1};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(0, 0, 1, 1);
ScreenCaptureResults captureResults;
return ScreenCapture::captureLayers(captureArgs, captureResults);
@@ -396,6 +397,56 @@
}
}
+TEST_F(CredentialsTest, DisplayTransactionPermissionTest) {
+ const auto display = getFirstDisplayToken();
+
+ ui::DisplayState displayState;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ const ui::Rotation initialOrientation = displayState.orientation;
+
+ // Set display orientation from an untrusted process. This should fail silently.
+ {
+ UIDFaker f{AID_BIN};
+ Transaction transaction;
+ Rect layerStackRect;
+ Rect displayRect;
+ transaction.setDisplayProjection(display, initialOrientation + ui::ROTATION_90,
+ layerStackRect, displayRect);
+ transaction.apply(/*synchronous=*/true);
+ }
+
+ // Verify that the display orientation did not change.
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ ASSERT_EQ(initialOrientation, displayState.orientation);
+
+ // Set display orientation from a trusted process.
+ {
+ UIDFaker f{AID_SYSTEM};
+ Transaction transaction;
+ Rect layerStackRect;
+ Rect displayRect;
+ transaction.setDisplayProjection(display, initialOrientation + ui::ROTATION_90,
+ layerStackRect, displayRect);
+ transaction.apply(/*synchronous=*/true);
+ }
+
+ // Verify that the display orientation did change.
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ ASSERT_EQ(initialOrientation + ui::ROTATION_90, displayState.orientation);
+
+ // Reset orientation
+ {
+ UIDFaker f{AID_SYSTEM};
+ Transaction transaction;
+ Rect layerStackRect;
+ Rect displayRect;
+ transaction.setDisplayProjection(display, initialOrientation, layerStackRect, displayRect);
+ transaction.apply(/*synchronous=*/true);
+ }
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayState(display, &displayState));
+ ASSERT_EQ(initialOrientation, displayState.orientation);
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp
index 15a98df..cc57e11 100644
--- a/services/surfaceflinger/tests/LayerState_test.cpp
+++ b/services/surfaceflinger/tests/LayerState_test.cpp
@@ -28,66 +28,6 @@
namespace test {
-TEST(LayerStateTest, ParcellingDisplayCaptureArgs) {
- DisplayCaptureArgs args;
- args.pixelFormat = ui::PixelFormat::RGB_565;
- args.sourceCrop = Rect(0, 0, 500, 200);
- args.frameScaleX = 2;
- args.frameScaleY = 4;
- args.captureSecureLayers = true;
- args.displayToken = sp<BBinder>::make();
- args.width = 10;
- args.height = 20;
- args.grayscale = true;
-
- Parcel p;
- args.writeToParcel(&p);
- p.setDataPosition(0);
-
- DisplayCaptureArgs args2;
- args2.readFromParcel(&p);
-
- ASSERT_EQ(args.pixelFormat, args2.pixelFormat);
- ASSERT_EQ(args.sourceCrop, args2.sourceCrop);
- ASSERT_EQ(args.frameScaleX, args2.frameScaleX);
- ASSERT_EQ(args.frameScaleY, args2.frameScaleY);
- ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers);
- ASSERT_EQ(args.displayToken, args2.displayToken);
- ASSERT_EQ(args.width, args2.width);
- ASSERT_EQ(args.height, args2.height);
- ASSERT_EQ(args.grayscale, args2.grayscale);
-}
-
-TEST(LayerStateTest, ParcellingLayerCaptureArgs) {
- LayerCaptureArgs args;
- args.pixelFormat = ui::PixelFormat::RGB_565;
- args.sourceCrop = Rect(0, 0, 500, 200);
- args.frameScaleX = 2;
- args.frameScaleY = 4;
- args.captureSecureLayers = true;
- args.layerHandle = sp<BBinder>::make();
- args.excludeHandles = {sp<BBinder>::make(), sp<BBinder>::make()};
- args.childrenOnly = false;
- args.grayscale = true;
-
- Parcel p;
- args.writeToParcel(&p);
- p.setDataPosition(0);
-
- LayerCaptureArgs args2;
- args2.readFromParcel(&p);
-
- ASSERT_EQ(args.pixelFormat, args2.pixelFormat);
- ASSERT_EQ(args.sourceCrop, args2.sourceCrop);
- ASSERT_EQ(args.frameScaleX, args2.frameScaleX);
- ASSERT_EQ(args.frameScaleY, args2.frameScaleY);
- ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers);
- ASSERT_EQ(args.layerHandle, args2.layerHandle);
- ASSERT_EQ(args.excludeHandles, args2.excludeHandles);
- ASSERT_EQ(args.childrenOnly, args2.childrenOnly);
- ASSERT_EQ(args.grayscale, args2.grayscale);
-}
-
TEST(LayerStateTest, ParcellingScreenCaptureResultsWithFence) {
ScreenCaptureResults results;
results.buffer = sp<GraphicBuffer>::make(100u, 200u, PIXEL_FORMAT_RGBA_8888, 1u, 0u);
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 5b056d0..03f9005 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -23,7 +23,7 @@
#include <cutils/properties.h>
#include <gtest/gtest.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <private/gui/ComposerService.h>
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
index f9b4bba..76bae41 100644
--- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -18,6 +18,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#include <gui/AidlUtil.h>
#include <gui/BufferItemConsumer.h>
#include <private/android_filesystem_config.h>
#include "TransactionTestHarnesses.h"
@@ -64,7 +65,7 @@
// only layerB is in this range
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = parent->getHandle();
- captureArgs.sourceCrop = {0, 0, 32, 32};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(32, 32);
ScreenCapture::captureLayers(&screenshot, captureArgs);
screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
}
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index d97d433..6cc1c51 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -20,6 +20,7 @@
#include <android-base/properties.h>
#include <common/FlagManager.h>
+#include <gui/AidlUtil.h>
#include <private/android_filesystem_config.h>
#include "LayerTransactionTest.h"
#include "utils/TransactionUtils.h"
@@ -350,7 +351,7 @@
// Capture just the mirror layer and child.
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mirrorParent->getHandle();
- captureArgs.sourceCrop = childBounds;
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(childBounds);
std::unique_ptr<ScreenCapture> shot;
ScreenCapture::captureLayers(&shot, captureArgs);
shot->expectSize(childBounds.width(), childBounds.height());
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 9a78550..c62f493 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -20,6 +20,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#include <gui/AidlUtil.h>
#include <private/android_filesystem_config.h>
#include <ui/DisplayState.h>
@@ -65,7 +66,7 @@
.show(mFGSurfaceControl);
});
- mCaptureArgs.sourceCrop = mDisplayRect;
+ mCaptureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(mDisplayRect);
mCaptureArgs.layerHandle = mRootSurfaceControl->getHandle();
}
@@ -112,7 +113,7 @@
shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
}
- mCaptureArgs.captureSecureLayers = true;
+ mCaptureArgs.captureArgs.captureSecureLayers = true;
// AID_SYSTEM is allowed to capture secure content.
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
@@ -164,7 +165,7 @@
// Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
// to receive them...we are expected to take care with the results.
- mCaptureArgs.captureSecureLayers = true;
+ mCaptureArgs.captureArgs.captureSecureLayers = true;
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
@@ -198,8 +199,8 @@
.apply();
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = childLayer->getHandle();
- captureArgs.sourceCrop = size;
- captureArgs.captureSecureLayers = false;
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(size);
+ captureArgs.captureArgs.captureSecureLayers = false;
{
SCOPED_TRACE("parent hidden");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -208,7 +209,7 @@
sc.expectColor(size, Color::BLACK);
}
- captureArgs.captureSecureLayers = true;
+ captureArgs.captureArgs.captureSecureLayers = true;
{
SCOPED_TRACE("capture secure parent not visible");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -218,7 +219,7 @@
}
Transaction().show(parentLayer).apply();
- captureArgs.captureSecureLayers = false;
+ captureArgs.captureArgs.captureSecureLayers = false;
{
SCOPED_TRACE("parent visible");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -227,7 +228,7 @@
sc.expectColor(size, Color::BLACK);
}
- captureArgs.captureSecureLayers = true;
+ captureArgs.captureArgs.captureSecureLayers = true;
{
SCOPED_TRACE("capture secure parent visible");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -259,8 +260,8 @@
.apply();
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = childLayer->getHandle();
- captureArgs.sourceCrop = size;
- captureArgs.captureSecureLayers = false;
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(size);
+ captureArgs.captureArgs.captureSecureLayers = false;
{
SCOPED_TRACE("parent hidden");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -269,7 +270,7 @@
sc.expectColor(size, Color::BLACK);
}
- captureArgs.captureSecureLayers = true;
+ captureArgs.captureArgs.captureSecureLayers = true;
{
SCOPED_TRACE("capture secure parent not visible");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -279,7 +280,7 @@
}
Transaction().show(parentLayer).apply();
- captureArgs.captureSecureLayers = false;
+ captureArgs.captureArgs.captureSecureLayers = false;
{
SCOPED_TRACE("parent visible");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -288,7 +289,7 @@
sc.expectColor(size, Color::BLACK);
}
- captureArgs.captureSecureLayers = true;
+ captureArgs.captureArgs.captureSecureLayers = true;
{
SCOPED_TRACE("capture secure parent visible");
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(captureArgs, mCaptureResults));
@@ -361,14 +362,14 @@
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = fgHandle;
captureArgs.childrenOnly = true;
- captureArgs.excludeHandles = {child2->getHandle()};
+ captureArgs.captureArgs.excludeHandles = {child2->getHandle()};
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->checkPixel(10, 10, 0, 0, 0);
mCapture->checkPixel(0, 0, 200, 200, 200);
}
TEST_F(ScreenCaptureTest, CaptureLayerExcludeThroughDisplayArgs) {
- mCaptureArgs.excludeHandles = {mFGSurfaceControl->getHandle()};
+ mCaptureArgs.captureArgs.excludeHandles = {mFGSurfaceControl->getHandle()};
ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
mCapture->expectBGColor(0, 0);
// Doesn't capture FG layer which is at 64, 64
@@ -401,7 +402,7 @@
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = fgHandle;
captureArgs.childrenOnly = true;
- captureArgs.excludeHandles = {child2->getHandle()};
+ captureArgs.captureArgs.excludeHandles = {child2->getHandle()};
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->checkPixel(10, 10, 0, 0, 0);
mCapture->checkPixel(0, 0, 200, 200, 200);
@@ -418,7 +419,7 @@
// Captures child
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = child->getHandle();
- captureArgs.sourceCrop = {0, 0, 10, 20};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(10, 20);
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255});
// Area outside of child's bounds is transparent.
@@ -481,7 +482,7 @@
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = child->getHandle();
- captureArgs.sourceCrop = {0, 0, 10, 10};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(10, 10);
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect(0, 0, 9, 9), Color::RED);
@@ -623,7 +624,7 @@
// red area to the right of the blue area
mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
- captureArgs.sourceCrop = {0, 0, 30, 30};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(30, 30);
ScreenCapture::captureLayers(&mCapture, captureArgs);
// Capturing the cropped screen, cropping out the shown red area, should leave only the blue
// area visible.
@@ -658,8 +659,8 @@
// red area to the right of the blue area
mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
- captureArgs.frameScaleX = 0.5f;
- captureArgs.frameScaleY = 0.5f;
+ captureArgs.captureArgs.frameScaleX = 0.5f;
+ captureArgs.captureArgs.frameScaleY = 0.5f;
sleep(1);
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -689,8 +690,8 @@
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = redLayer->getHandle();
- captureArgs.frameScaleX = INT32_MAX / 60;
- captureArgs.frameScaleY = INT32_MAX / 60;
+ captureArgs.captureArgs.frameScaleX = INT32_MAX / 60;
+ captureArgs.captureArgs.frameScaleY = INT32_MAX / 60;
ScreenCaptureResults captureResults;
ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(captureArgs, captureResults));
@@ -736,7 +737,7 @@
mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
// Passing flag secure so the blue layer should be screenshot too.
- args.captureSecureLayers = true;
+ args.captureArgs.captureSecureLayers = true;
ScreenCapture::captureLayers(&mCapture, args);
mCapture->expectColor(Rect(0, 0, 30, 30), Color::BLUE);
mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
@@ -780,7 +781,7 @@
// Reading color data will expectedly result in crash, only check usage bit
// b/309965549 Checking that the usage bit is protected does not work for
// devices that do not support usage protected.
- mCaptureArgs.allowProtected = true;
+ mCaptureArgs.captureArgs.allowProtected = true;
ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, captureResults));
// ASSERT_EQ(GRALLOC_USAGE_PROTECTED, GRALLOC_USAGE_PROTECTED &
// captureResults.buffer->getUsage());
@@ -898,7 +899,7 @@
// Make screenshot request with current uid set. No layers were created with the current
// uid so screenshot will be black.
- captureArgs.uid = fakeUid;
+ captureArgs.captureArgs.uid = fakeUid;
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect(0, 0, 32, 32), Color::TRANSPARENT);
mCapture->expectBorder(Rect(0, 0, 32, 32), Color::TRANSPARENT);
@@ -935,7 +936,7 @@
mCapture->expectBorder(Rect(128, 128, 160, 160), Color::TRANSPARENT);
// Screenshot from the fakeUid caller with no uid requested allows everything to be screenshot.
- captureArgs.uid = -1;
+ captureArgs.captureArgs.uid = -1;
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect(128, 128, 160, 160), Color::RED);
mCapture->expectBorder(Rect(128, 128, 160, 160), {63, 63, 195, 255});
@@ -955,7 +956,7 @@
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
- captureArgs.grayscale = true;
+ captureArgs.captureArgs.grayscale = true;
const uint8_t tolerance = 1;
@@ -1052,7 +1053,7 @@
LayerCaptureArgs captureArgs;
captureArgs.layerHandle = mirroredLayer->getHandle();
- captureArgs.sourceCrop = Rect(0, 0, 1, 1);
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(1, 1);
// Screenshot path should only use the children of the layer hierarchy so
// that it will not create a new snapshot. A snapshot would otherwise be
diff --git a/services/surfaceflinger/tests/Stress_test.cpp b/services/surfaceflinger/tests/Stress_test.cpp
deleted file mode 100644
index b30df5e..0000000
--- a/services/surfaceflinger/tests/Stress_test.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include <gtest/gtest.h>
-
-#include <gui/SurfaceComposerClient.h>
-
-#include <utils/String8.h>
-
-#include <thread>
-#include <functional>
-#include <layerproto/LayerProtoParser.h>
-
-namespace android {
-
-TEST(SurfaceFlingerStress, create_and_destroy) {
- auto do_stress = []() {
- sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make();
- ASSERT_EQ(NO_ERROR, client->initCheck());
- for (int j = 0; j < 1000; j++) {
- auto surf = client->createSurface(String8("t"), 100, 100,
- PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(surf != nullptr);
- surf.clear();
- }
- };
-
- std::vector<std::thread> threads;
- for (int i = 0; i < 10; i++) {
- threads.push_back(std::thread(do_stress));
- }
- for (auto& thread : threads) {
- thread.join();
- }
-}
-
-perfetto::protos::LayersProto generateLayerProto() {
- perfetto::protos::LayersProto layersProto;
- std::array<perfetto::protos::LayerProto*, 10> layers = {};
- for (size_t i = 0; i < layers.size(); ++i) {
- layers[i] = layersProto.add_layers();
- layers[i]->set_id(i);
- }
-
- layers[0]->add_children(1);
- layers[1]->set_parent(0);
- layers[0]->add_children(2);
- layers[2]->set_parent(0);
- layers[0]->add_children(3);
- layers[3]->set_parent(0);
- layers[2]->add_children(4);
- layers[4]->set_parent(2);
- layers[3]->add_children(5);
- layers[5]->set_parent(3);
- layers[5]->add_children(6);
- layers[6]->set_parent(5);
- layers[5]->add_children(7);
- layers[7]->set_parent(5);
- layers[6]->add_children(8);
- layers[8]->set_parent(6);
-
- layers[4]->set_z_order_relative_of(3);
- layers[3]->add_relatives(4);
- layers[8]->set_z_order_relative_of(9);
- layers[9]->add_relatives(8);
- layers[3]->set_z_order_relative_of(1);
- layers[1]->add_relatives(3);
-
-/* ----------------------------
- * - 0 - - 9 -
- * / | \
- * 1 2 3(1)
- * | |
- * 4(3) 5
- * / \
- * 6 7
- * |
- * 8(9)
- * -------------------------- */
-
- return layersProto;
-}
-
-TEST(LayerProtoStress, mem_info) {
- std::string cmd = "dumpsys meminfo ";
- cmd += std::to_string(getpid());
- system(cmd.c_str());
- for (int i = 0; i < 100000; i++) {
- perfetto::protos::LayersProto layersProto = generateLayerProto();
- auto layerTree = surfaceflinger::LayerProtoParser::generateLayerTree(layersProto);
- surfaceflinger::LayerProtoParser::layerTreeToString(layerTree);
- }
- system(cmd.c_str());
-}
-
-}
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/TextureFiltering_test.cpp b/services/surfaceflinger/tests/TextureFiltering_test.cpp
index c5d118c..3f39cf6 100644
--- a/services/surfaceflinger/tests/TextureFiltering_test.cpp
+++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp
@@ -14,9 +14,10 @@
* limitations under the License.
*/
+#include <android/gui/DisplayCaptureArgs.h>
#include <android/gui/ISurfaceComposerClient.h>
#include <gtest/gtest.h>
-#include <gui/DisplayCaptureArgs.h>
+#include <gui/AidlUtil.h>
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
@@ -84,7 +85,7 @@
};
TEST_F(TextureFilteringTest, NoFiltering) {
- captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(100, 100);
captureArgs.layerHandle = mParent->getHandle();
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -93,7 +94,7 @@
}
TEST_F(TextureFilteringTest, BufferCropNoFiltering) {
- captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(100, 100);
captureArgs.layerHandle = mParent->getHandle();
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -105,7 +106,7 @@
TEST_F(TextureFilteringTest, BufferCropIsFiltered) {
Transaction().setBufferCrop(mLayer, Rect{25, 25, 75, 75}).apply();
- captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(100, 100);
captureArgs.layerHandle = mParent->getHandle();
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -114,9 +115,9 @@
// Expect filtering because the output source crop is stretched to the output buffer's size.
TEST_F(TextureFilteringTest, OutputSourceCropIsFiltered) {
- captureArgs.frameScaleX = 2;
- captureArgs.frameScaleY = 2;
- captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+ captureArgs.captureArgs.frameScaleX = 2;
+ captureArgs.captureArgs.frameScaleY = 2;
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(25, 25, 75, 75);
captureArgs.layerHandle = mParent->getHandle();
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -127,9 +128,9 @@
// buffer's size.
TEST_F(TextureFilteringTest, LayerCropOutputSourceCropIsFiltered) {
Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
- captureArgs.frameScaleX = 2;
- captureArgs.frameScaleY = 2;
- captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+ captureArgs.captureArgs.frameScaleX = 2;
+ captureArgs.captureArgs.frameScaleY = 2;
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(25, 25, 75, 75);
captureArgs.layerHandle = mParent->getHandle();
ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -139,8 +140,8 @@
// Expect filtering because the layer is scaled up.
TEST_F(TextureFilteringTest, LayerCaptureWithScalingIsFiltered) {
captureArgs.layerHandle = mLayer->getHandle();
- captureArgs.frameScaleX = 2;
- captureArgs.frameScaleY = 2;
+ captureArgs.captureArgs.frameScaleX = 2;
+ captureArgs.captureArgs.frameScaleY = 2;
ScreenCapture::captureLayers(&mCapture, captureArgs);
expectFiltered({0, 0, 100, 200}, {100, 0, 200, 200});
@@ -149,7 +150,7 @@
// Expect no filtering because the output buffer's size matches the source crop.
TEST_F(TextureFilteringTest, LayerCaptureOutputSourceCropNoFiltering) {
captureArgs.layerHandle = mLayer->getHandle();
- captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(25, 25, 75, 75);
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
@@ -162,7 +163,7 @@
Transaction().setCrop(mLayer, Rect{10, 10, 90, 90}).apply();
captureArgs.layerHandle = mLayer->getHandle();
- captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(25, 25, 75, 75);
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
@@ -172,7 +173,7 @@
// Expect no filtering because the output source crop and output buffer are the same size.
TEST_F(TextureFilteringTest, OutputSourceCropDisplayFrameMatchNoFiltering) {
captureArgs.layerHandle = mLayer->getHandle();
- captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(25, 25, 75, 75);
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
@@ -206,7 +207,7 @@
Transaction().setPosition(mParent, 100, 100).apply();
captureArgs.layerHandle = mParent->getHandle();
- captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+ captureArgs.captureArgs.sourceCrop = gui::aidl_utils::toARect(100, 100);
ScreenCapture::captureLayers(&mCapture, captureArgs);
mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index af3cb9a..67a5247 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -16,6 +16,7 @@
#ifndef ANDROID_TRANSACTION_TEST_HARNESSES
#define ANDROID_TRANSACTION_TEST_HARNESSES
+#include <com_android_graphics_libgui_flags.h>
#include <common/FlagManager.h>
#include <ui/DisplayState.h>
@@ -51,6 +52,16 @@
const ui::Size& resolution = displayMode.resolution;
sp<IBinder> vDisplay;
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ sp<BufferItemConsumer> itemConsumer = sp<BufferItemConsumer>::make(
+ // Sample usage bits from screenrecord
+ GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_SW_READ_OFTEN);
+ sp<BufferListener> listener = sp<BufferListener>::make(this);
+ itemConsumer->setFrameAvailableListener(listener);
+ itemConsumer->setName(String8("Virtual disp consumer"));
+ itemConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
+#else
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
sp<BufferItemConsumer> itemConsumer;
@@ -65,6 +76,7 @@
GRALLOC_USAGE_SW_READ_OFTEN);
sp<BufferListener> listener = sp<BufferListener>::make(this);
itemConsumer->setFrameAvailableListener(listener);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
static const std::string kDisplayName("VirtualDisplay");
vDisplay = SurfaceComposerClient::createVirtualDisplay(kDisplayName,
@@ -76,7 +88,12 @@
SurfaceComposerClient::getDefault()->mirrorDisplay(displayId);
SurfaceComposerClient::Transaction t;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ t.setDisplaySurface(vDisplay,
+ itemConsumer->getSurface()->getIGraphicBufferProducer());
+#else
t.setDisplaySurface(vDisplay, producer);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
t.setDisplayProjection(vDisplay, displayState.orientation,
Rect(displayState.layerStackSpaceRect), Rect(resolution));
if (FlagManager::getInstance().ce_fence_promise()) {
diff --git a/services/surfaceflinger/tests/VirtualDisplay_test.cpp b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
index cd66dd2..d69378c 100644
--- a/services/surfaceflinger/tests/VirtualDisplay_test.cpp
+++ b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
@@ -27,6 +27,12 @@
class VirtualDisplayTest : public ::testing::Test {
protected:
void SetUp() override {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ mGLConsumer = sp<GLConsumer>::make(GLConsumer::TEXTURE_EXTERNAL, true, false, false);
+ mGLConsumer->setName(String8("Virtual disp consumer"));
+ mGLConsumer->setDefaultBufferSize(100, 100);
+ mProducer = mGLConsumer->getSurface()->getIGraphicBufferProducer();
+#else
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&mProducer, &consumer);
@@ -34,6 +40,7 @@
consumer->setDefaultBufferSize(100, 100);
mGLConsumer = sp<GLConsumer>::make(consumer, GLConsumer::TEXTURE_EXTERNAL, true, false);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
}
sp<IGraphicBufferProducer> mProducer;
diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
index 2e5913b..3104dd4 100644
--- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
+++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
@@ -25,6 +25,7 @@
#include <FrontEnd/LayerHierarchy.h>
#include <FrontEnd/LayerLifecycleManager.h>
#include <FrontEnd/LayerSnapshotBuilder.h>
+#include <Layer.h> // needed for framerate
namespace android::surfaceflinger::frontend {
@@ -84,6 +85,15 @@
mLifecycleManager.addLayers(std::move(layers));
}
+ void createRootLayerWithUid(uint32_t id, gui::Uid uid) {
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ auto args = createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID,
+ /*mirror=*/UNASSIGNED_LAYER_ID);
+ args.ownerUid = uid.val();
+ layers.emplace_back(std::make_unique<RequestedLayerState>(args));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
void createDisplayMirrorLayer(uint32_t id, ui::LayerStack layerStack) {
std::vector<std::unique_ptr<RequestedLayerState>> layers;
layers.emplace_back(std::make_unique<RequestedLayerState>(
@@ -321,6 +331,19 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setFrameRate(uint32_t id, Layer::FrameRate framerate) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eFrameRateChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.frameRate = framerate.vote.rate.getValue();
+ transactions.back().states.front().state.frameRateCompatibility = 0;
+ transactions.back().states.front().state.changeFrameRateStrategy = 0;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void setFrameRateCategory(uint32_t id, int8_t frameRateCategory) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
@@ -393,6 +416,14 @@
GRALLOC_USAGE_PROTECTED /*usage*/));
}
+ void setFrontBuffer(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 | AHARDWAREBUFFER_USAGE_FRONT_BUFFER /*usage*/));
+ }
+
void setBufferCrop(uint32_t id, const Rect& bufferCrop) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
@@ -472,6 +503,18 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setGameMode(uint32_t id, gui::GameMode gameMode) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
+ transactions.back().states.front().state.metadata = LayerMetadata();
+ transactions.back().states.front().state.metadata.setInt32(METADATA_GAME_MODE,
+ static_cast<int32_t>(gameMode));
+ transactions.back().states.front().layerId = id;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
private:
LayerLifecycleManager& mLifecycleManager;
};
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 9ebef8c..f1bd87c 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -80,21 +80,16 @@
"FpsTest.cpp",
"FramebufferSurfaceTest.cpp",
"FrameRateOverrideMappingsTest.cpp",
- "FrameRateSelectionPriorityTest.cpp",
- "FrameRateSelectionStrategyTest.cpp",
"FrameTimelineTest.cpp",
- "GameModeTest.cpp",
"HWComposerTest.cpp",
"JankTrackerTest.cpp",
"OneShotTimerTest.cpp",
- "LayerHistoryTest.cpp",
"LayerHistoryIntegrationTest.cpp",
"LayerInfoTest.cpp",
"LayerMetadataTest.cpp",
"LayerHierarchyTest.cpp",
"LayerLifecycleManagerTest.cpp",
"LayerSnapshotTest.cpp",
- "LayerTest.cpp",
"LayerTestUtils.cpp",
"MessageQueueTest.cpp",
"PowerAdvisorTest.cpp",
@@ -117,9 +112,7 @@
"SurfaceFlinger_SetDisplayStateTest.cpp",
"SurfaceFlinger_SetPowerModeInternalTest.cpp",
"SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
- "SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp",
"SchedulerTest.cpp",
- "SetFrameRateTest.cpp",
"RefreshRateSelectorTest.cpp",
"RefreshRateStatsTest.cpp",
"RegionSamplingTest.cpp",
@@ -153,6 +146,7 @@
"android.hardware.power-ndk_static",
"librenderengine_deps",
"libsurfaceflinger_common_test_deps",
+ "libsurfaceflinger_proto_deps",
],
static_libs: [
"android.hardware.common-V2-ndk",
@@ -171,7 +165,6 @@
"libframetimeline",
"libgmock",
"libgui_mocks",
- "liblayers_proto",
"libperfetto_client_experimental",
"librenderengine",
"librenderengine_mocks",
diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
deleted file mode 100644
index d30d5b8..0000000
--- a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright 2020 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 "LibSurfaceFlingerUnittests"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/LayerMetadata.h>
-
-#include "Layer.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockComposer.h"
-
-namespace android {
-
-using testing::_;
-using testing::DoAll;
-using testing::Mock;
-using testing::Return;
-using testing::SetArgPointee;
-
-using android::Hwc2::IComposer;
-using android::Hwc2::IComposerClient;
-
-/**
- * This class covers all the test that are related to refresh rate selection.
- */
-class RefreshRateSelectionTest : public testing::Test {
-public:
- RefreshRateSelectionTest();
- ~RefreshRateSelectionTest() override;
-
-protected:
- static constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
- static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
- static constexpr uint32_t WIDTH = 100;
- static constexpr uint32_t HEIGHT = 100;
- static constexpr uint32_t LAYER_FLAGS = 0;
- static constexpr int32_t PRIORITY_UNSET = -1;
-
- sp<Layer> createBufferStateLayer();
- sp<Layer> createEffectLayer();
-
- void setParent(Layer* child, Layer* parent);
- void commitTransaction(Layer* layer);
-
- TestableSurfaceFlinger mFlinger;
-
- sp<Client> mClient;
- sp<Layer> mParent;
- sp<Layer> mChild;
- sp<Layer> mGrandChild;
-};
-
-RefreshRateSelectionTest::RefreshRateSelectionTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-
- mFlinger.setupMockScheduler();
- mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
-}
-
-RefreshRateSelectionTest::~RefreshRateSelectionTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
-}
-
-sp<Layer> RefreshRateSelectionTest::createBufferStateLayer() {
- sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", LAYER_FLAGS,
- LayerMetadata());
- return sp<Layer>::make(args);
-}
-
-sp<Layer> RefreshRateSelectionTest::createEffectLayer() {
- sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata());
- return sp<Layer>::make(args);
-}
-
-void RefreshRateSelectionTest::setParent(Layer* child, Layer* parent) {
- child->setParent(sp<Layer>::fromExisting(parent));
-}
-
-void RefreshRateSelectionTest::commitTransaction(Layer* layer) {
- layer->commitTransaction();
-}
-
-namespace {
-
-TEST_F(RefreshRateSelectionTest, testPriorityOnBufferStateLayers) {
- mParent = createBufferStateLayer();
- mChild = createBufferStateLayer();
- setParent(mChild.get(), mParent.get());
- mGrandChild = createBufferStateLayer();
- setParent(mGrandChild.get(), mChild.get());
-
- ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
-
- // Child has its own priority.
- mGrandChild->setFrameRateSelectionPriority(1);
- commitTransaction(mGrandChild.get());
- ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
- // Child inherits from his parent.
- mChild->setFrameRateSelectionPriority(1);
- commitTransaction(mChild.get());
- mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
- commitTransaction(mGrandChild.get());
- ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
- // Grandchild inherits from his grand parent.
- mParent->setFrameRateSelectionPriority(1);
- commitTransaction(mParent.get());
- mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
- commitTransaction(mChild.get());
- mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
- commitTransaction(mGrandChild.get());
- ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-}
-
-TEST_F(RefreshRateSelectionTest, testPriorityOnEffectLayers) {
- mParent = createEffectLayer();
- mChild = createEffectLayer();
- setParent(mChild.get(), mParent.get());
- mGrandChild = createEffectLayer();
- setParent(mGrandChild.get(), mChild.get());
-
- ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
-
- // Child has its own priority.
- mGrandChild->setFrameRateSelectionPriority(1);
- commitTransaction(mGrandChild.get());
- ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
- // Child inherits from his parent.
- mChild->setFrameRateSelectionPriority(1);
- commitTransaction(mChild.get());
- mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
- commitTransaction(mGrandChild.get());
- ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-
- // Grandchild inherits from his grand parent.
- mParent->setFrameRateSelectionPriority(1);
- commitTransaction(mParent.get());
- mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
- commitTransaction(mChild.get());
- mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
- commitTransaction(mGrandChild.get());
- ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
- ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
deleted file mode 100644
index 5c742d7..0000000
--- a/services/surfaceflinger/tests/unittests/FrameRateSelectionStrategyTest.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * 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 "LibSurfaceFlingerUnittests"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/LayerMetadata.h>
-
-#include "Layer.h"
-#include "LayerTestUtils.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockComposer.h"
-
-namespace android {
-
-using testing::DoAll;
-using testing::Mock;
-using testing::SetArgPointee;
-
-using android::Hwc2::IComposer;
-using android::Hwc2::IComposerClient;
-
-using scheduler::LayerHistory;
-
-using FrameRate = Layer::FrameRate;
-using FrameRateCompatibility = Layer::FrameRateCompatibility;
-using FrameRateSelectionStrategy = scheduler::LayerInfo::FrameRateSelectionStrategy;
-
-/**
- * This class tests the behaviour of Layer::setFrameRateSelectionStrategy.
- */
-class FrameRateSelectionStrategyTest : public BaseLayerTest {
-protected:
- const FrameRate FRAME_RATE_VOTE1 = FrameRate(11_Hz, FrameRateCompatibility::Default);
- const FrameRate FRAME_RATE_VOTE2 = FrameRate(22_Hz, FrameRateCompatibility::Default);
- const FrameRate FRAME_RATE_VOTE3 = FrameRate(33_Hz, FrameRateCompatibility::Default);
- const FrameRate FRAME_RATE_DEFAULT = FrameRate(Fps(), FrameRateCompatibility::Default);
- const FrameRate FRAME_RATE_TREE = FrameRate(Fps(), FrameRateCompatibility::NoVote);
-
- FrameRateSelectionStrategyTest();
-
- void addChild(sp<Layer> layer, sp<Layer> child);
- void removeChild(sp<Layer> layer, sp<Layer> child);
- void commitTransaction();
-
- std::vector<sp<Layer>> mLayers;
-};
-
-FrameRateSelectionStrategyTest::FrameRateSelectionStrategyTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-
- mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
-}
-
-void FrameRateSelectionStrategyTest::addChild(sp<Layer> layer, sp<Layer> child) {
- layer->addChild(child);
-}
-
-void FrameRateSelectionStrategyTest::removeChild(sp<Layer> layer, sp<Layer> child) {
- layer->removeChild(child);
-}
-
-void FrameRateSelectionStrategyTest::commitTransaction() {
- for (auto layer : mLayers) {
- layer->commitTransaction();
- }
-}
-
-namespace {
-
-INSTANTIATE_TEST_SUITE_P(PerLayerType, FrameRateSelectionStrategyTest,
- testing::Values(std::make_shared<BufferStateLayerFactory>(),
- std::make_shared<EffectLayerFactory>()),
- PrintToStringParamName);
-
-TEST_P(FrameRateSelectionStrategyTest, SetAndGet) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
- auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- layer->setFrameRate(FRAME_RATE_VOTE1.vote);
- layer->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE1, layer->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
- layer->getDrawingState().frameRateSelectionStrategy);
-}
-
-TEST_P(FrameRateSelectionStrategyTest, SetChildOverrideChildren) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- addChild(parent, child1);
- addChild(child1, child2);
-
- child2->setFrameRate(FRAME_RATE_VOTE1.vote);
- child2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- parent->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- child1->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
- child2->getDrawingState().frameRateSelectionStrategy);
-}
-
-TEST_P(FrameRateSelectionStrategyTest, SetParentOverrideChildren) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
- auto layer1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto layer2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto layer3 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- addChild(layer1, layer2);
- addChild(layer2, layer3);
-
- layer1->setFrameRate(FRAME_RATE_VOTE1.vote);
- layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
- layer2->setFrameRate(FRAME_RATE_VOTE2.vote);
- layer2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
- layer3->setFrameRate(FRAME_RATE_VOTE3.vote);
- commitTransaction();
-
- EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
- layer1->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE1, layer2->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
- layer2->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE1, layer3->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- layer3->getDrawingState().frameRateSelectionStrategy);
-
- layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::Propagate);
- commitTransaction();
-
- EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- layer1->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
- layer2->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE2, layer3->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- layer3->getDrawingState().frameRateSelectionStrategy);
-}
-
-TEST_P(FrameRateSelectionStrategyTest, OverrideChildrenAndSelf) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
- auto layer1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto layer2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto layer3 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- addChild(layer1, layer2);
- addChild(layer2, layer3);
-
- layer1->setFrameRate(FRAME_RATE_VOTE1.vote);
- layer2->setFrameRate(FRAME_RATE_VOTE2.vote);
- layer2->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::Self);
- commitTransaction();
-
- EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- layer1->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Self,
- layer2->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_DEFAULT, layer3->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- layer3->getDrawingState().frameRateSelectionStrategy);
-
- layer1->setFrameRateSelectionStrategy(FrameRateSelectionStrategy::OverrideChildren);
- commitTransaction();
-
- EXPECT_EQ(FRAME_RATE_VOTE1, layer1->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
- layer1->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE1, layer2->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Self,
- layer2->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE1, layer3->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- layer3->getDrawingState().frameRateSelectionStrategy);
-
- layer1->setFrameRate(FRAME_RATE_DEFAULT.vote);
- commitTransaction();
-
- EXPECT_EQ(FRAME_RATE_TREE, layer1->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::OverrideChildren,
- layer1->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE2, layer2->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Self,
- layer2->getDrawingState().frameRateSelectionStrategy);
- EXPECT_EQ(FRAME_RATE_VOTE2, layer3->getFrameRateForLayerTree());
- EXPECT_EQ(FrameRateSelectionStrategy::Propagate,
- layer3->getDrawingState().frameRateSelectionStrategy);
-}
-
-} // namespace
-} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
deleted file mode 100644
index 1b5c6e7..0000000
--- a/services/surfaceflinger/tests/unittests/GameModeTest.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/LayerMetadata.h>
-#include <gui/SurfaceComposerClient.h>
-#include <log/log.h>
-
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockComposer.h"
-
-namespace android {
-
-using testing::_;
-using testing::Mock;
-using testing::Return;
-
-using gui::GameMode;
-using gui::LayerMetadata;
-
-class GameModeTest : public testing::Test {
-public:
- GameModeTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- mFlinger.setupMockScheduler();
- setupComposer();
- }
-
- ~GameModeTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
- }
-
- sp<Layer> createLayer() {
- sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "layer", 0, LayerMetadata());
- return sp<Layer>::make(args);
- }
-
- void setupComposer() {
- mComposer = new Hwc2::mock::Composer();
- mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
-
- Mock::VerifyAndClear(mComposer);
- }
-
- // Mocks the behavior of applying a transaction from WMShell
- void setGameModeMetadata(sp<Layer> layer, GameMode gameMode) {
- mLayerMetadata.setInt32(gui::METADATA_GAME_MODE, static_cast<int32_t>(gameMode));
- layer->setMetadata(mLayerMetadata);
- layer->setGameModeForTree(gameMode);
- }
-
- TestableSurfaceFlinger mFlinger;
- Hwc2::mock::Composer* mComposer = nullptr;
- client_cache_t mClientCache;
- LayerMetadata mLayerMetadata;
-};
-
-TEST_F(GameModeTest, SetGameModeSetsForAllCurrentChildren) {
- sp<Layer> rootLayer = createLayer();
- sp<Layer> childLayer1 = createLayer();
- sp<Layer> childLayer2 = createLayer();
- rootLayer->addChild(childLayer1);
- rootLayer->addChild(childLayer2);
- rootLayer->setGameModeForTree(GameMode::Performance);
-
- EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance);
- EXPECT_EQ(childLayer1->getGameMode(), GameMode::Performance);
- EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance);
-}
-
-TEST_F(GameModeTest, AddChildAppliesGameModeFromParent) {
- sp<Layer> rootLayer = createLayer();
- sp<Layer> childLayer = createLayer();
- rootLayer->setGameModeForTree(GameMode::Performance);
- rootLayer->addChild(childLayer);
-
- EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance);
- EXPECT_EQ(childLayer->getGameMode(), GameMode::Performance);
-}
-
-TEST_F(GameModeTest, RemoveChildResetsGameMode) {
- sp<Layer> rootLayer = createLayer();
- sp<Layer> childLayer = createLayer();
- rootLayer->setGameModeForTree(GameMode::Performance);
- rootLayer->addChild(childLayer);
-
- EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance);
- EXPECT_EQ(childLayer->getGameMode(), GameMode::Performance);
-
- rootLayer->removeChild(childLayer);
- EXPECT_EQ(childLayer->getGameMode(), GameMode::Unsupported);
-}
-
-TEST_F(GameModeTest, ReparentingDoesNotOverrideMetadata) {
- sp<Layer> rootLayer = createLayer();
- sp<Layer> childLayer1 = createLayer();
- sp<Layer> childLayer2 = createLayer();
- rootLayer->setGameModeForTree(GameMode::Standard);
- rootLayer->addChild(childLayer1);
-
- setGameModeMetadata(childLayer2, GameMode::Performance);
- rootLayer->addChild(childLayer2);
-
- EXPECT_EQ(rootLayer->getGameMode(), GameMode::Standard);
- EXPECT_EQ(childLayer1->getGameMode(), GameMode::Standard);
- EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance);
-
- rootLayer->removeChild(childLayer2);
- EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance);
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index a61fa1e..7e84408 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -38,6 +38,7 @@
namespace android::scheduler {
using android::mock::createDisplayMode;
+using android::mock::createVrrDisplayMode;
using namespace com::android::graphics::surfaceflinger;
class LayerHistoryIntegrationTest : public surfaceflinger::frontend::LayerSnapshotTestBase {
@@ -53,6 +54,8 @@
static constexpr Fps HI_FPS = 90_Hz;
static constexpr auto HI_FPS_PERIOD = HI_FPS.getPeriodNsecs();
+ static constexpr auto kVrrModeId = DisplayModeId(2);
+
LayerHistoryIntegrationTest() : LayerSnapshotTestBase() {
mFlinger.resetScheduler(mScheduler);
mLifecycleManager = {};
@@ -71,6 +74,13 @@
updateLayerSnapshotsAndLayerHistory(time);
}
+ void setFrontBufferWithPresentTime(sp<Layer>& layer, nsecs_t time) {
+ uint32_t sequence = static_cast<uint32_t>(layer->sequence);
+ setFrontBuffer(sequence);
+ layer->setDesiredPresentTime(time, false /*autotimestamp*/);
+ updateLayerSnapshotsAndLayerHistory(time);
+ }
+
LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
@@ -135,6 +145,21 @@
return layer;
}
+ auto createLegacyAndFrontedEndLayerWithUid(uint32_t sequence, gui::Uid uid) {
+ std::string layerName = "test layer:" + std::to_string(sequence);
+ auto args = LayerCreationArgs{mFlinger.flinger(),
+ nullptr,
+ layerName,
+ 0,
+ {},
+ std::make_optional<uint32_t>(sequence)};
+ args.ownerUid = uid.val();
+ const auto layer = sp<Layer>::make(args);
+ mFlinger.injectLegacyLayer(layer);
+ createRootLayerWithUid(sequence, uid);
+ return layer;
+ }
+
auto destroyLayer(sp<Layer>& layer) {
uint32_t sequence = static_cast<uint32_t>(layer->sequence);
mFlinger.releaseLegacyLayer(sequence);
@@ -157,12 +182,13 @@
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));
+ std::shared_ptr<RefreshRateSelector> mSelector = std::make_shared<RefreshRateSelector>(
+ makeModes(createDisplayMode(DisplayModeId(0), LO_FPS),
+ createDisplayMode(DisplayModeId(1), HI_FPS),
+ createVrrDisplayMode(kVrrModeId, HI_FPS,
+ hal::VrrConfig{.minFrameIntervalNs =
+ HI_FPS.getPeriodNsecs()})),
+ DisplayModeId(0));
mock::SchedulerCallback mSchedulerCallback;
TestableSurfaceFlinger mFlinger;
@@ -214,6 +240,146 @@
EXPECT_EQ(1u, activeLayerCount());
}
+TEST_F(LayerHistoryIntegrationTest, oneLayer) {
+ 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());
+
+ // Max returned if active layers have insufficient history.
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+ setBuffer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ time += LO_FPS_PERIOD;
+ }
+
+ // Max is returned since we have enough history but there is no timestamp votes.
+ for (size_t i = 0; i < 10; i++) {
+ setBuffer(1);
+ updateLayerSnapshotsAndLayerHistory(time);
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ time += LO_FPS_PERIOD;
+ }
+}
+
+TEST_F(LayerHistoryIntegrationTest, gameFrameRateOverrideMapping) {
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+
+ history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 60.0f}));
+
+ auto overridePair = history().getGameFrameRateOverride(0);
+ EXPECT_EQ(0_Hz, overridePair.first);
+ EXPECT_EQ(60_Hz, overridePair.second);
+
+ history().updateGameModeFrameRateOverride(FrameRateOverride({0, 40.0f}));
+ history().updateGameModeFrameRateOverride(FrameRateOverride({1, 120.0f}));
+
+ overridePair = history().getGameFrameRateOverride(0);
+ EXPECT_EQ(40_Hz, overridePair.first);
+ EXPECT_EQ(60_Hz, overridePair.second);
+
+ overridePair = history().getGameFrameRateOverride(1);
+ EXPECT_EQ(120_Hz, overridePair.first);
+ EXPECT_EQ(0_Hz, overridePair.second);
+
+ history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 0.0f}));
+ history().updateGameModeFrameRateOverride(FrameRateOverride({1, 0.0f}));
+
+ overridePair = history().getGameFrameRateOverride(0);
+ EXPECT_EQ(40_Hz, overridePair.first);
+ EXPECT_EQ(0_Hz, overridePair.second);
+
+ overridePair = history().getGameFrameRateOverride(1);
+ EXPECT_EQ(0_Hz, overridePair.first);
+ EXPECT_EQ(0_Hz, overridePair.second);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerGameFrameRateOverride) {
+ SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
+
+ const uid_t uid = 0;
+ const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
+ const Fps gameModeFrameRate = Fps::fromValue(60.0f);
+
+ auto layer = createLegacyAndFrontedEndLayerWithUid(1, gui::Uid(uid));
+ showLayer(1);
+
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ // update game default frame rate override
+ history().updateGameDefaultFrameRateOverride(
+ FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
+
+ LayerHistory::Summary summary;
+ scheduler::LayerProps layerProps = {
+ .visible = true,
+ .bounds = {0, 0, 100, 100},
+ .transform = {},
+ .setFrameRateVote = {},
+ .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+ .isSmallDirty = false,
+ .isFrontBuffered = false,
+ };
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += gameDefaultFrameRate.getPeriodNsecs();
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+ ASSERT_EQ(30.0_Hz, summary[0].desiredRefreshRate);
+
+ // test against setFrameRate vote
+ setFrameRate(1,
+ Layer::FrameRate(Fps::fromValue(120.0f), Layer::FrameRateCompatibility::Default));
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ const Fps setFrameRate = Fps::fromValue(120.0f);
+ layerProps.setFrameRateVote =
+ Layer::FrameRate(setFrameRate, Layer::FrameRateCompatibility::Default);
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += setFrameRate.getPeriodNsecs();
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+ ASSERT_EQ(120.0_Hz, summary[0].desiredRefreshRate);
+
+ // update game mode frame rate override
+ history().updateGameModeFrameRateOverride(
+ FrameRateOverride({uid, gameModeFrameRate.getValue()}));
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += gameModeFrameRate.getPeriodNsecs();
+ summary = summarizeLayerHistory(time);
+ }
+
+ ASSERT_EQ(1u, summary.size());
+ ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
+ ASSERT_EQ(60.0_Hz, summary[0].desiredRefreshRate);
+}
+
TEST_F(LayerHistoryIntegrationTest, oneInvisibleLayer) {
createLegacyAndFrontedEndLayer(1);
nsecs_t time = systemTime();
@@ -238,6 +404,110 @@
EXPECT_EQ(0u, activeLayerCount());
}
+TEST_F(LayerHistoryIntegrationTest, explicitTimestamp) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += LO_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(LO_FPS, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerNoVote) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerMinVote) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerMaxVote) {
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Max);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += LO_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_TRUE(summarizeLayerHistory(time).empty());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVote) {
createLegacyAndFrontedEndLayer(1);
setFrameRate(1, 73.4f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
@@ -273,6 +543,335 @@
EXPECT_EQ(1, frequentLayerCount(time));
}
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitExactVote2) {
+ auto layer = 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);
+
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ 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));
+
+ // layer became infrequent, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ 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(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitGte_vrr) {
+ // Set the test to be on a vrr mode.
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_GTE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, 0);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+
+ // layer became inactive, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+// Test for MRR device with VRR features enabled.
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitGte_nonVrr) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+ // The vrr_config flag is explicitly not set false because this test for an MRR device
+ // should still work in a VRR-capable world.
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (33_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_GTE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, 0);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+
+ // layer became infrequent, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategory_vrrFeatureOff) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (73.4_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH);
+
+ // Set default to Min so it is obvious that the vote reset triggered.
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ // There is only 1 LayerRequirement due to the disabled flag frame_rate_category_mrr.
+ ASSERT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+// This test case should be the same as oneLayerNoVote except instead of layer vote is NoVote,
+// the category is NoPreference.
+TEST_F(LayerHistoryIntegrationTest, oneLayerCategoryNoPreference) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (0_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+
+ // layer became infrequent
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ EXPECT_EQ(1u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithCategory) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (73.4_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ // There are 2 LayerRequirement's due to the frame rate category.
+ ASSERT_EQ(2u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ // First LayerRequirement is the layer's category 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);
+
+ // Second LayerRequirement is the frame rate specification
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[1].vote);
+ EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[1].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[1].frameRateCategory);
+
+ // layer became infrequent, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(2u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ 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, oneLayerExplicitVoteWithCategoryNotVisibleDoesNotVote) {
+ SET_FLAG_FOR_TEST(flags::misc1, true);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ hideLayer(1);
+ setFrameRate(1, (12.34_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ // Layer is not visible, so the layer is moved to inactive, infrequent, and it will not have
+ // votes to consider for refresh rate selection.
+ ASSERT_EQ(0u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(0u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, invisibleExplicitLayer) {
+ SET_FLAG_FOR_TEST(flags::misc1, false);
+
+ auto explicitVisiblelayer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (60_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0);
+
+ auto explicitInvisiblelayer = createLegacyAndFrontedEndLayer(2);
+ hideLayer(2);
+ setFrameRate(2, (90_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0);
+
+ nsecs_t time = systemTime();
+
+ // Post a buffer to the layers to make them active
+ setBuffer(1);
+ setBuffer(2);
+ updateLayerSnapshotsAndLayerHistory(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, invisibleExplicitLayerDoesNotVote) {
+ SET_FLAG_FOR_TEST(flags::misc1, true);
+
+ auto explicitVisiblelayer = createLegacyAndFrontedEndLayer(1);
+ showLayer(1);
+ setFrameRate(1, (60_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0);
+
+ auto explicitInvisiblelayer = createLegacyAndFrontedEndLayer(2);
+ hideLayer(2);
+ setFrameRate(2, (90_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, 0);
+
+ nsecs_t time = systemTime();
+
+ // Post a buffer to the layers to make them active
+ setBuffer(1);
+ setBuffer(2);
+ updateLayerSnapshotsAndLayerHistory(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(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryIntegrationTest, frontBufferedLayerVotesMax) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ setFrontBuffer(1);
+ showLayer(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++) {
+ setFrontBufferWithPresentTime(layer, time);
+ 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(0, animatingLayerCount(time));
+
+ // Layer still active due to front buffering, but it's infrequent.
+ time += MAX_ACTIVE_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(0, animatingLayerCount(time));
+}
+
TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitCategory) {
SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
@@ -293,6 +892,50 @@
EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
}
+TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithFixedSourceAndNoPreferenceCategory) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
+ SET_FLAG_FOR_TEST(flags::view_set_requested_frame_rate_mrr, true);
+
+ auto layer = createLegacyAndFrontedEndLayer(1);
+ setFrameRate(1, (45.6_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+ setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+
+ EXPECT_EQ(1u, layerCount());
+ EXPECT_EQ(0u, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ updateLayerSnapshotsAndLayerHistory(time);
+ for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ setBufferWithPresentTime(layer, time);
+ time += HI_FPS_PERIOD;
+ }
+
+ // There are 2 LayerRequirement's due to the frame rate category.
+ ASSERT_EQ(2u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ // First LayerRequirement is the layer's category specification
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::NoPreference, summarizeLayerHistory(time)[0].frameRateCategory);
+
+ // Second LayerRequirement is the frame rate specification
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+ summarizeLayerHistory(time)[1].vote);
+ EXPECT_EQ(45.6_Hz, summarizeLayerHistory(time)[1].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[1].frameRateCategory);
+
+ // layer became infrequent, but the vote stays
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(2u, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1u, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitCategory, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::NoPreference, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
TEST_F(LayerHistoryIntegrationTest, multipleLayers) {
auto layer1 = createLegacyAndFrontedEndLayer(1);
auto layer2 = createLegacyAndFrontedEndLayer(2);
@@ -805,7 +1448,15 @@
// layer is active but infrequent.
for (size_t i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- auto props = layer->getLayerProps();
+ scheduler::LayerProps props = {
+ .visible = false,
+ .bounds = {0, 0, 100, 100},
+ .transform = {},
+ .setFrameRateVote = {},
+ .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+ .isSmallDirty = false,
+ .isFrontBuffered = false,
+ };
if (i % 3 == 0) {
props.isSmallDirty = false;
} else {
@@ -838,8 +1489,15 @@
// 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;
+ scheduler::LayerProps props = {
+ .visible = false,
+ .bounds = {0, 0, 100, 100},
+ .transform = {},
+ .setFrameRateVote = {},
+ .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+ .isSmallDirty = true,
+ .isFrontBuffered = false,
+ };
setBuffer(1);
uiLayer->setDesiredPresentTime(0, false /*autotimestamp*/);
updateLayerSnapshotsAndLayerHistory(time);
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
deleted file mode 100644
index 088d0d2..0000000
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ /dev/null
@@ -1,1573 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wextra"
-
-#undef LOG_TAG
-#define LOG_TAG "LayerHistoryTest"
-
-#include <Layer.h>
-#include <com_android_graphics_surfaceflinger_flags.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <log/log.h>
-
-#include <common/test/FlagUtils.h>
-#include "FpsOps.h"
-#include "Scheduler/LayerHistory.h"
-#include "Scheduler/LayerInfo.h"
-#include "TestableScheduler.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockDisplayMode.h"
-#include "mock/MockLayer.h"
-#include "mock/MockSchedulerCallback.h"
-
-using testing::_;
-using testing::Return;
-using testing::ReturnRef;
-
-namespace android::scheduler {
-
-using MockLayer = android::mock::MockLayer;
-
-using android::mock::createDisplayMode;
-using android::mock::createVrrDisplayMode;
-
-// 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;
- 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();
-
- LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
-
- 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 createLayer() { return sp<MockLayer>::make(mFlinger.flinger()); }
- auto createLayer(std::string name) {
- return sp<MockLayer>::make(mFlinger.flinger(), std::move(name));
- }
- auto createLayer(std::string name, uint32_t uid) {
- return sp<MockLayer>::make(mFlinger.flinger(), std::move(name), std::move(uid));
- }
-
- void recordFramesAndExpect(const sp<MockLayer>& layer, nsecs_t& time, Fps frameRate,
- Fps desiredRefreshRate, int numFrames) {
- LayerHistory::Summary summary;
- for (int i = 0; i < numFrames; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += frameRate.getPeriodNsecs();
-
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate);
- }
-
- static constexpr auto kVrrModeId = DisplayModeId(2);
- std::shared_ptr<RefreshRateSelector> mSelector = std::make_shared<RefreshRateSelector>(
- makeModes(createDisplayMode(DisplayModeId(0), LO_FPS),
- createDisplayMode(DisplayModeId(1), HI_FPS),
- createVrrDisplayMode(kVrrModeId, HI_FPS,
- hal::VrrConfig{.minFrameIntervalNs =
- HI_FPS.getPeriodNsecs()})),
- DisplayModeId(0));
-
- mock::SchedulerCallback mSchedulerCallback;
- TestableSurfaceFlinger mFlinger;
- TestableScheduler* mScheduler = new TestableScheduler(mSelector, mFlinger, mSchedulerCallback);
-};
-
-namespace {
-
-using namespace com::android::graphics::surfaceflinger;
-
-TEST_F(LayerHistoryTest, singleLayerNoVoteDefaultCompatibility) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
- EXPECT_CALL(*layer, getDefaultFrameRateCompatibility())
- .WillOnce(Return(FrameRateCompatibility::NoVote));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
-
- // No layers returned if no layers are active.
- EXPECT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(0, activeLayerCount());
-
- history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
- LayerHistory::LayerUpdateType::Buffer);
- history().setDefaultFrameRateCompatibility(layer->getSequence(),
-
- layer->getDefaultFrameRateCompatibility(),
- true /* contentDetectionEnabled */);
-
- EXPECT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(1, activeLayerCount());
-}
-
-TEST_F(LayerHistoryTest, singleLayerMinVoteDefaultCompatibility) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
- EXPECT_CALL(*layer, getDefaultFrameRateCompatibility())
- .WillOnce(Return(FrameRateCompatibility::Min));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
-
- EXPECT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(0, activeLayerCount());
-
- history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
- LayerHistory::LayerUpdateType::Buffer);
- history().setDefaultFrameRateCompatibility(layer->getSequence(),
- layer->getDefaultFrameRateCompatibility(),
- true /* contentDetectionEnabled */);
-
- auto summary = summarizeLayerHistory(time);
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
-
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
-}
-
-TEST_F(LayerHistoryTest, oneLayer) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- // history().registerLayer(layer, LayerHistory::LayerVoteType::Max);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
-
- // No layers returned if no layers are active.
- EXPECT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(0, activeLayerCount());
-
- // Max returned if active layers have insufficient history.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
- LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- time += LO_FPS_PERIOD;
- }
-
- // Max is returned since we have enough history but there is no timestamp votes.
- for (int i = 0; i < 10; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
- LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- time += LO_FPS_PERIOD;
- }
-}
-
-TEST_F(LayerHistoryTest, gameFrameRateOverrideMapping) {
- SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
-
- history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 60.0f}));
-
- auto overridePair = history().getGameFrameRateOverride(0);
- EXPECT_EQ(0_Hz, overridePair.first);
- EXPECT_EQ(60_Hz, overridePair.second);
-
- history().updateGameModeFrameRateOverride(FrameRateOverride({0, 40.0f}));
- history().updateGameModeFrameRateOverride(FrameRateOverride({1, 120.0f}));
-
- overridePair = history().getGameFrameRateOverride(0);
- EXPECT_EQ(40_Hz, overridePair.first);
- EXPECT_EQ(60_Hz, overridePair.second);
-
- overridePair = history().getGameFrameRateOverride(1);
- EXPECT_EQ(120_Hz, overridePair.first);
- EXPECT_EQ(0_Hz, overridePair.second);
-
- history().updateGameDefaultFrameRateOverride(FrameRateOverride({0, 0.0f}));
- history().updateGameModeFrameRateOverride(FrameRateOverride({1, 0.0f}));
-
- overridePair = history().getGameFrameRateOverride(0);
- EXPECT_EQ(40_Hz, overridePair.first);
- EXPECT_EQ(0_Hz, overridePair.second);
-
- overridePair = history().getGameFrameRateOverride(1);
- EXPECT_EQ(0_Hz, overridePair.first);
- EXPECT_EQ(0_Hz, overridePair.second);
-}
-
-TEST_F(LayerHistoryTest, oneLayerGameFrameRateOverride) {
- SET_FLAG_FOR_TEST(flags::game_default_frame_rate, true);
-
- const uid_t uid = 0;
- const Fps gameDefaultFrameRate = Fps::fromValue(30.0f);
- const Fps gameModeFrameRate = Fps::fromValue(60.0f);
- const auto layer = createLayer("GameFrameRateLayer", uid);
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
- EXPECT_CALL(*layer, getOwnerUid()).WillRepeatedly(Return(uid));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- // update game default frame rate override
- history().updateGameDefaultFrameRateOverride(
- FrameRateOverride({uid, gameDefaultFrameRate.getValue()}));
-
- nsecs_t time = systemTime();
- LayerHistory::Summary summary;
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += gameDefaultFrameRate.getPeriodNsecs();
-
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
- ASSERT_EQ(30.0_Hz, summary[0].desiredRefreshRate);
-
- // test against setFrameRate vote
- const Fps setFrameRate = Fps::fromValue(120.0f);
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(setFrameRate, Layer::FrameRateCompatibility::Default)));
-
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += setFrameRate.getPeriodNsecs();
-
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
- ASSERT_EQ(120.0_Hz, summary[0].desiredRefreshRate);
-
- // update game mode frame rate override
- history().updateGameModeFrameRateOverride(
- FrameRateOverride({uid, gameModeFrameRate.getValue()}));
-
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += gameModeFrameRate.getPeriodNsecs();
-
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
- ASSERT_EQ(60.0_Hz, summary[0].desiredRefreshRate);
-}
-
-TEST_F(LayerHistoryTest, oneInvisibleLayer) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
-
- history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
- LayerHistory::LayerUpdateType::Buffer);
- auto summary = summarizeLayerHistory(time);
- ASSERT_EQ(1, 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(1, activeLayerCount());
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
- history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
- LayerHistory::LayerUpdateType::Buffer);
-
- summary = summarizeLayerHistory(time);
- EXPECT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(0, activeLayerCount());
-}
-
-TEST_F(LayerHistoryTest, explicitTimestamp) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(LO_FPS, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerNoVote) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerMinVote) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerMaxVote) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Max);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became inactive
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_TRUE(summarizeLayerHistory(time).empty());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitVote) {
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became infrequent, but the vote stays
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitExactVote) {
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
- summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became infrequent, but the vote stays
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
- summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitGte_vrr) {
- // Set the test to be on a vrr mode.
- SET_FLAG_FOR_TEST(flags::vrr_config, true);
- mSelector->setActiveMode(kVrrModeId, HI_FPS);
-
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(Layer::FrameRate(33_Hz, Layer::FrameRateCompatibility::Gte,
- Seamlessness::OnlySeamless,
- FrameRateCategory::Default)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
-
- // layer became inactive, but the vote stays
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
-}
-
-// Test for MRR device with VRR features enabled.
-TEST_F(LayerHistoryTest, oneLayerExplicitGte_nonVrr) {
- SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
- // The vrr_config flag is explicitly not set false because this test for an MRR device
- // should still work in a VRR-capable world.
-
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(Layer::FrameRate(33_Hz, Layer::FrameRateCompatibility::Gte,
- Seamlessness::OnlySeamless,
- FrameRateCategory::Default)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
-
- // layer became infrequent, but the vote stays
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategory_vrrFeatureOff) {
- SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
-
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default,
- Seamlessness::OnlySeamless, FrameRateCategory::High)));
-
- // Set default to Min so it is obvious that the vote reset triggered.
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- // There is only 1 LayerRequirement due to the disabled flag frame_rate_category_mrr.
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitCategory) {
- SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
-
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(0_Hz, Layer::FrameRateCompatibility::Default,
- Seamlessness::OnlySeamless, FrameRateCategory::High)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, 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);
-
- // layer became infrequent, but the vote stays
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- 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);
-}
-
-// This test case should be the same as oneLayerNoVote except instead of layer vote is NoVote,
-// the category is NoPreference.
-TEST_F(LayerHistoryTest, oneLayerCategoryNoPreference) {
- SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
-
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(Layer::FrameRate(0_Hz, Layer::FrameRateCompatibility::Default,
- Seamlessness::OnlySeamless,
- FrameRateCategory::NoPreference)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- EXPECT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer became infrequent
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- EXPECT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategory) {
- SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
-
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default,
- Seamlessness::OnlySeamless, FrameRateCategory::High)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- // There are 2 LayerRequirement's due to the frame rate category.
- ASSERT_EQ(2, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- // First LayerRequirement is the layer's category 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);
-
- // Second LayerRequirement is the frame rate specification
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[1].vote);
- EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[1].desiredRefreshRate);
- EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[1].frameRateCategory);
-
- // layer became infrequent, but the vote stays
- setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(2, summarizeLayerHistory(time).size());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- 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(LayerHistoryTest, oneLayerExplicitVoteWithCategoryNotVisibleDoesNotVote) {
- SET_FLAG_FOR_TEST(flags::misc1, true);
-
- auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
- EXPECT_CALL(*layer, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(12.34_Hz, Layer::FrameRateCompatibility::Default,
- Seamlessness::OnlySeamless, FrameRateCategory::High)));
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
-
- nsecs_t time = systemTime();
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- }
-
- // Layer is not visible, so the layer is moved to inactive, infrequent, and it will not have
- // votes to consider for refresh rate selection.
- ASSERT_EQ(0, summarizeLayerHistory(time).size());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, multipleLayers) {
- auto layer1 = createLayer("A");
- auto layer2 = createLayer("B");
- auto layer3 = createLayer("C");
-
- EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(3, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- LayerHistory::Summary summary;
-
- // layer1 is active but infrequent.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer1->getSequence(), layer1->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- // layer2 is frequent and has high refresh rate.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = summarizeLayerHistory(time);
- }
-
- // layer1 is still active but infrequent.
- history().record(layer1->getSequence(), layer1->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
-
- ASSERT_EQ(2, 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(2, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer1 is no longer active.
- // layer2 is frequent and has low refresh rate.
- for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(1, 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 (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- if (i % RATIO == 0) {
- history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- }
-
- history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(2, 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(2, activeLayerCount());
- EXPECT_EQ(2, frequentLayerCount(time));
-
- // layer3 becomes recently active.
- history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- summary = summarizeLayerHistory(time);
- ASSERT_EQ(2, 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(2, activeLayerCount());
- EXPECT_EQ(2, frequentLayerCount(time));
-
- // layer1 expires.
- layer1.clear();
- summary = summarizeLayerHistory(time);
- ASSERT_EQ(2, 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(2, layerCount());
- EXPECT_EQ(2, activeLayerCount());
- EXPECT_EQ(2, frequentLayerCount(time));
-
- // layer2 still has low refresh rate.
- // layer3 becomes inactive.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += LO_FPS_PERIOD;
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_EQ(LO_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer2 expires.
- layer2.clear();
- summary = summarizeLayerHistory(time);
- EXPECT_TRUE(summary.empty());
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- // layer3 becomes active and has high refresh rate.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
- history().record(layer3->getSequence(), layer3->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_EQ(HI_FPS, summary[0].desiredRefreshRate);
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-
- // layer3 expires.
- layer3.clear();
- summary = summarizeLayerHistory(time);
- EXPECT_TRUE(summary.empty());
- EXPECT_EQ(0, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, inactiveLayers) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- // the very first updates makes the layer frequent
- for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- }
-
- // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, 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 (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- }
-
- // More quick frames will get us to frequent again
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, invisibleExplicitLayer) {
- SET_FLAG_FOR_TEST(flags::misc1, false);
-
- auto explicitVisiblelayer = createLayer();
- auto explicitInvisiblelayer = createLayer();
-
- EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(60_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
- EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(90_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- nsecs_t time = systemTime();
-
- // Post a buffer to the layers to make them active
- history().record(explicitVisiblelayer->getSequence(), explicitVisiblelayer->getLayerProps(),
- time, time, LayerHistory::LayerUpdateType::Buffer);
- history().record(explicitInvisiblelayer->getSequence(), explicitInvisiblelayer->getLayerProps(),
- time, time, LayerHistory::LayerUpdateType::Buffer);
-
- EXPECT_EQ(2, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
- summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(2, activeLayerCount());
- EXPECT_EQ(2, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, invisibleExplicitLayerDoesNotVote) {
- SET_FLAG_FOR_TEST(flags::misc1, true);
-
- auto explicitVisiblelayer = createLayer();
- auto explicitInvisiblelayer = createLayer();
-
- EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(60_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
- EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(
- Layer::FrameRate(90_Hz, Layer::FrameRateCompatibility::ExactOrMultiple)));
-
- nsecs_t time = systemTime();
-
- // Post a buffer to the layers to make them active
- history().record(explicitVisiblelayer->getSequence(), explicitVisiblelayer->getLayerProps(),
- time, time, LayerHistory::LayerUpdateType::Buffer);
- history().record(explicitInvisiblelayer->getSequence(), explicitInvisiblelayer->getLayerProps(),
- time, time, LayerHistory::LayerUpdateType::Buffer);
-
- EXPECT_EQ(2, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
- summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, infrequentAnimatingLayer) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // layer is active but infrequent.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // another update with the same cadence keep in infrequent
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // an update as animation will immediately vote for Max
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::AnimationTX);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(1, animatingLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, frontBufferedLayerVotesMax) {
- SET_FLAG_FOR_TEST(flags::vrr_config, true);
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
- EXPECT_CALL(*layer, isFrontBuffered()).WillRepeatedly(Return(true));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // layer is active but infrequent.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
- }
-
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // Layer still active due to front buffering, but it's infrequent.
- time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // Fill up the window with frequent updates
- for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += (60_Hz).getPeriodNsecs();
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- }
-
- // posting a buffer after long inactivity should retain the layer as active
- time += std::chrono::nanoseconds(3s).count();
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(0, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
- EXPECT_EQ(1, 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();
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(0, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // posting another buffer should keep the layer infrequent
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(0, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, 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
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(1, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, 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();
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(0, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // posting another buffer should keep the layer frequent
- time += (60_Hz).getPeriodNsecs();
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(0, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, inconclusiveLayerBecomingFrequent) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // Fill up the window with frequent updates
- for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += (60_Hz).getPeriodNsecs();
-
- EXPECT_EQ(1, layerCount());
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, 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();
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count();
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(0, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, 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
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- EXPECT_EQ(1, clearLayerHistoryCount(time));
- ASSERT_EQ(1, summarizeLayerHistory(time).size());
- EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
- EXPECT_EQ(1, activeLayerCount());
- EXPECT_EQ(1, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-}
-
-TEST_F(LayerHistoryTest, getFramerate) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
- EXPECT_EQ(0, animatingLayerCount(time));
-
- // layer is active but infrequent.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer->getSequence(), layer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- 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(LayerHistoryTest, heuristicLayer60Hz) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- 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(LayerHistoryTest, heuristicLayer60_30Hz) {
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- 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(LayerHistoryTest, heuristicLayerNotOscillating) {
- SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, false);
-
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- 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(LayerHistoryTest, heuristicLayerNotOscillating_useKnownRefreshRate) {
- SET_FLAG_FOR_TEST(flags::use_known_refresh_rate_for_fps_consistency, true);
-
- const auto layer = createLayer();
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- 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, 24_Hz, PRESENT_TIME_HISTORY_SIZE);
- recordFramesAndExpect(layer, time, 27.1_Hz, 30_Hz, PRESENT_TIME_HISTORY_SIZE);
-}
-
-TEST_F(LayerHistoryTest, smallDirtyLayer) {
- auto layer = createLayer();
-
- EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(1, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- LayerHistory::Summary summary;
-
- // layer is active but infrequent.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- auto props = layer->getLayerProps();
- if (i % 3 == 0) {
- props.isSmallDirty = false;
- } else {
- props.isSmallDirty = true;
- }
-
- history().record(layer->getSequence(), props, time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
- EXPECT_GE(HI_FPS, summary[0].desiredRefreshRate);
-}
-
-TEST_F(LayerHistoryTest, smallDirtyInMultiLayer) {
- auto layer1 = createLayer("UI");
- auto layer2 = createLayer("Video");
-
- EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
-
- EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer2, getFrameRateForLayerTree())
- .WillRepeatedly(
- Return(Layer::FrameRate(30_Hz, Layer::FrameRateCompatibility::Default)));
-
- nsecs_t time = systemTime();
-
- EXPECT_EQ(2, layerCount());
- EXPECT_EQ(0, activeLayerCount());
- EXPECT_EQ(0, frequentLayerCount(time));
-
- LayerHistory::Summary summary;
-
- // layer1 is updating small dirty.
- for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
- auto props = layer1->getLayerProps();
- props.isSmallDirty = true;
- history().record(layer1->getSequence(), props, 0 /*presentTime*/, time,
- LayerHistory::LayerUpdateType::Buffer);
- history().record(layer2->getSequence(), layer2->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
- time += HI_FPS_PERIOD;
- summary = summarizeLayerHistory(time);
- }
-
- ASSERT_EQ(1, summary.size());
- ASSERT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summary[0].vote);
- ASSERT_EQ(30_Hz, summary[0].desiredRefreshRate);
-}
-
-class LayerHistoryTestParameterized : public LayerHistoryTest,
- public testing::WithParamInterface<std::chrono::nanoseconds> {
-};
-
-TEST_P(LayerHistoryTestParameterized, HeuristicLayerWithInfrequentLayer) {
- std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
- auto heuristicLayer = createLayer("HeuristicLayer");
-
- EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(Layer::FrameRate()));
-
- auto infrequentLayer = createLayer("InfrequentLayer");
- EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree())
- .WillRepeatedly(Return(Layer::FrameRate()));
-
- const nsecs_t startTime = systemTime();
-
- const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
- history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), startTime,
- startTime, LayerHistory::LayerUpdateType::Buffer);
- history().record(infrequentLayer->getSequence(), heuristicLayer->getLayerProps(), startTime,
- startTime, LayerHistory::LayerUpdateType::Buffer);
-
- nsecs_t time = startTime;
- nsecs_t lastInfrequentUpdate = startTime;
- const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
- int infrequentLayerUpdates = 0;
- while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
- time += heuristicUpdateDelta.count();
- history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), time, time,
- LayerHistory::LayerUpdateType::Buffer);
-
- if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
- ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
- totalInfrequentLayerUpdates);
- lastInfrequentUpdate = time;
- history().record(infrequentLayer->getSequence(), infrequentLayer->getLayerProps(), time,
- time, LayerHistory::LayerUpdateType::Buffer);
- infrequentLayerUpdates++;
- }
-
- if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
- ASSERT_NE(0, summarizeLayerHistory(time).size());
- ASSERT_GE(2, 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, LayerHistoryTestParameterized,
- ::testing::Values(1s, 2s, 3s, 4s, 5s));
-
-} // namespace
-} // namespace android::scheduler
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 54d4659..2860345 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -56,6 +56,17 @@
class LayerSnapshotTest : public LayerSnapshotTestBase {
protected:
+ const Layer::FrameRate FRAME_RATE_VOTE1 =
+ Layer::FrameRate(67_Hz, scheduler::FrameRateCompatibility::Default);
+ const Layer::FrameRate FRAME_RATE_VOTE2 =
+ Layer::FrameRate(14_Hz, scheduler::FrameRateCompatibility::Default);
+ const Layer::FrameRate FRAME_RATE_VOTE3 =
+ Layer::FrameRate(99_Hz, scheduler::FrameRateCompatibility::Default);
+ const Layer::FrameRate FRAME_RATE_TREE =
+ Layer::FrameRate(Fps(), scheduler::FrameRateCompatibility::NoVote);
+ const Layer::FrameRate FRAME_RATE_NO_VOTE =
+ Layer::FrameRate(Fps(), scheduler::FrameRateCompatibility::Default);
+
LayerSnapshotTest() : LayerSnapshotTestBase() {
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
}
@@ -281,22 +292,40 @@
EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eColorChanged);
}
-TEST_F(LayerSnapshotTest, GameMode) {
- std::vector<TransactionState> transactions;
- transactions.emplace_back();
- transactions.back().states.push_back({});
- transactions.back().states.front().state.what = layer_state_t::eMetadataChanged;
- transactions.back().states.front().state.metadata = LayerMetadata();
- transactions.back().states.front().state.metadata.setInt32(METADATA_GAME_MODE, 42);
- transactions.back().states.front().layerId = 1;
- transactions.back().states.front().state.layerId = static_cast<int32_t>(1);
- mLifecycleManager.applyTransactions(transactions);
+TEST_F(LayerSnapshotTest, ChildrenInheritGameMode) {
+ setGameMode(1, gui::GameMode::Performance);
EXPECT_EQ(mLifecycleManager.getGlobalChanges(),
RequestedLayerState::Changes::GameMode | RequestedLayerState::Changes::Metadata);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged);
- EXPECT_EQ(static_cast<int32_t>(getSnapshot(1)->gameMode), 42);
- EXPECT_EQ(static_cast<int32_t>(getSnapshot(11)->gameMode), 42);
+ EXPECT_EQ(getSnapshot(1)->gameMode, gui::GameMode::Performance);
+ EXPECT_EQ(getSnapshot(11)->gameMode, gui::GameMode::Performance);
+}
+
+TEST_F(LayerSnapshotTest, ChildrenCanOverrideGameMode) {
+ setGameMode(1, gui::GameMode::Performance);
+ setGameMode(11, gui::GameMode::Battery);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(),
+ RequestedLayerState::Changes::GameMode | RequestedLayerState::Changes::Metadata);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged);
+ EXPECT_EQ(getSnapshot(1)->gameMode, gui::GameMode::Performance);
+ EXPECT_EQ(getSnapshot(11)->gameMode, gui::GameMode::Battery);
+}
+
+TEST_F(LayerSnapshotTest, ReparentingUpdatesGameMode) {
+ setGameMode(1, gui::GameMode::Performance);
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges(),
+ RequestedLayerState::Changes::GameMode | RequestedLayerState::Changes::Metadata);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eMetadataChanged);
+ EXPECT_EQ(getSnapshot(1)->gameMode, gui::GameMode::Performance);
+ EXPECT_EQ(getSnapshot(2)->gameMode, gui::GameMode::Unsupported);
+
+ reparentLayer(2, 1);
+ setZ(2, 2);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(2)->gameMode, gui::GameMode::Performance);
}
TEST_F(LayerSnapshotTest, UpdateMetadata) {
@@ -769,6 +798,155 @@
scheduler::FrameRateCompatibility::Default);
}
+TEST_F(LayerSnapshotTest, frameRateSetAndGet) {
+ setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ // verify parent is gets no vote
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetParent) {
+ setFrameRate(111, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_TREE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ setFrameRate(111, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetParentAllVote) {
+ setFrameRate(1, FRAME_RATE_VOTE3.vote.rate.getValue(), 0, 0);
+ setFrameRate(11, FRAME_RATE_VOTE2.vote.rate.getValue(), 0, 0);
+ setFrameRate(111, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ setFrameRate(111, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE2);
+
+ setFrameRate(11, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE3);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE3);
+
+ setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetChild) {
+ setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetChildAllVote) {
+ setFrameRate(1, FRAME_RATE_VOTE3.vote.rate.getValue(), 0, 0);
+ setFrameRate(11, FRAME_RATE_VOTE2.vote.rate.getValue(), 0, 0);
+ setFrameRate(111, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE3);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE2);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ setFrameRate(11, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_TREE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ setFrameRate(111, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetChildAddAfterVote) {
+ setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ reparentLayer(111, 2);
+ std::vector<uint32_t> traversalOrder = {1, 11, 12, 121, 122, 1221, 13, 2, 111};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, traversalOrder);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+
+ reparentLayer(111, 11);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateSetAndGetChildRemoveAfterVote) {
+ setFrameRate(1, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_VOTE1);
+
+ reparentLayer(111, 2);
+ std::vector<uint32_t> traversalOrder = {1, 11, 12, 121, 122, 1221, 13, 2, 111};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, traversalOrder);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+
+ setFrameRate(1, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, traversalOrder);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 111})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
+TEST_F(LayerSnapshotTest, frameRateAddChildForParentWithTreeVote) {
+ setFrameRate(11, FRAME_RATE_VOTE1.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_TREE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_VOTE1);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRate, FRAME_RATE_NO_VOTE);
+
+ setFrameRate(11, FRAME_RATE_NO_VOTE.vote.rate.getValue(), 0, 0);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 11})->frameRate, FRAME_RATE_NO_VOTE);
+ EXPECT_EQ(getSnapshot({.id = 12})->frameRate, FRAME_RATE_NO_VOTE);
+}
+
TEST_F(LayerSnapshotTest, translateDataspace) {
setDataspace(1, ui::Dataspace::UNKNOWN);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
@@ -947,7 +1125,7 @@
EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
scheduler::FrameRateCompatibility::NoVote);
- EXPECT_FALSE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+ EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer 12 and all descendants (121, 122, 1221) get the requested vote
EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
@@ -1038,7 +1216,7 @@
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
scheduler::FrameRateCompatibility::NoVote);
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.category, FrameRateCategory::Default);
- EXPECT_FALSE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+ EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer 12 and all descendants (121, 122, 1221) get the requested vote
EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
@@ -1539,4 +1717,48 @@
gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
}
+static constexpr const FloatRect LARGE_FLOAT_RECT{std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::max(),
+ std::numeric_limits<float>::max()};
+TEST_F(LayerSnapshotTest, layerVisibleByDefault) {
+ DisplayInfo info;
+ info.info.logicalHeight = 1000000;
+ info.info.logicalWidth = 1000000;
+ mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_FALSE(getSnapshot(1)->isHiddenByPolicy());
+}
+
+TEST_F(LayerSnapshotTest, hideLayerWithZeroMatrix) {
+ DisplayInfo info;
+ info.info.logicalHeight = 1000000;
+ info.info.logicalWidth = 1000000;
+ mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info);
+ setMatrix(1, 0.f, 0.f, 0.f, 0.f);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {2});
+ EXPECT_TRUE(getSnapshot(1)->isHiddenByPolicy());
+}
+
+TEST_F(LayerSnapshotTest, hideLayerWithInfMatrix) {
+ DisplayInfo info;
+ info.info.logicalHeight = 1000000;
+ info.info.logicalWidth = 1000000;
+ mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info);
+ setMatrix(1, std::numeric_limits<float>::infinity(), 0.f, 0.f,
+ std::numeric_limits<float>::infinity());
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {2});
+ EXPECT_TRUE(getSnapshot(1)->isHiddenByPolicy());
+}
+
+TEST_F(LayerSnapshotTest, hideLayerWithNanMatrix) {
+ DisplayInfo info;
+ info.info.logicalHeight = 1000000;
+ info.info.logicalWidth = 1000000;
+ mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info);
+ setMatrix(1, std::numeric_limits<float>::quiet_NaN(), 0.f, 0.f,
+ std::numeric_limits<float>::quiet_NaN());
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {2});
+ EXPECT_TRUE(getSnapshot(1)->isHiddenByPolicy());
+}
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerTest.cpp b/services/surfaceflinger/tests/unittests/LayerTest.cpp
deleted file mode 100644
index 95e54f6..0000000
--- a/services/surfaceflinger/tests/unittests/LayerTest.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2022 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 "LibSurfaceFlingerUnittests"
-
-#include <gtest/gtest.h>
-#include <ui/FloatRect.h>
-#include <ui/Transform.h>
-#include <limits>
-
-#include "LayerTestUtils.h"
-#include "TestableSurfaceFlinger.h"
-
-namespace android {
-namespace {
-
-class LayerTest : public BaseLayerTest {
-protected:
- static constexpr const float MIN_FLOAT = std::numeric_limits<float>::min();
- static constexpr const float MAX_FLOAT = std::numeric_limits<float>::max();
- static constexpr const FloatRect LARGE_FLOAT_RECT{MIN_FLOAT, MIN_FLOAT, MAX_FLOAT, MAX_FLOAT};
-};
-
-INSTANTIATE_TEST_SUITE_P(PerLayerType, LayerTest,
- testing::Values(std::make_shared<BufferStateLayerFactory>(),
- std::make_shared<EffectLayerFactory>()),
- PrintToStringParamName);
-
-TEST_P(LayerTest, layerVisibleByDefault) {
- sp<Layer> layer = GetParam()->createLayer(mFlinger);
- layer->updateGeometry();
- layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
- ASSERT_FALSE(layer->isHiddenByPolicy());
-}
-
-TEST_P(LayerTest, hideLayerWithZeroMatrix) {
- sp<Layer> layer = GetParam()->createLayer(mFlinger);
-
- layer_state_t::matrix22_t matrix{0, 0, 0, 0};
- layer->setMatrix(matrix);
- layer->updateGeometry();
- layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
-
- ASSERT_TRUE(layer->isHiddenByPolicy());
-}
-
-TEST_P(LayerTest, hideLayerWithInfMatrix) {
- sp<Layer> layer = GetParam()->createLayer(mFlinger);
-
- constexpr const float INF = std::numeric_limits<float>::infinity();
- layer_state_t::matrix22_t matrix{INF, 0, 0, INF};
- layer->setMatrix(matrix);
- layer->updateGeometry();
- layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
-
- ASSERT_TRUE(layer->isHiddenByPolicy());
-}
-
-TEST_P(LayerTest, hideLayerWithNanMatrix) {
- sp<Layer> layer = GetParam()->createLayer(mFlinger);
-
- constexpr const float QUIET_NAN = std::numeric_limits<float>::quiet_NaN();
- layer_state_t::matrix22_t matrix{QUIET_NAN, 0, 0, QUIET_NAN};
- layer->setMatrix(matrix);
- layer->updateGeometry();
- layer->computeBounds(LARGE_FLOAT_RECT, ui::Transform(), 0.f);
-
- ASSERT_TRUE(layer->isHiddenByPolicy());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 3df724a..358f6b0 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -78,6 +78,8 @@
SchedulerTest();
+ static constexpr RefreshRateSelector::LayerRequirement kLayer = {.weight = 1.f};
+
static constexpr PhysicalDisplayId kDisplayId1 = PhysicalDisplayId::fromPort(255u);
static inline const ftl::NonNull<DisplayModePtr> kDisplay1Mode60 =
ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(0), 60_Hz));
@@ -85,6 +87,9 @@
ftl::as_non_null(createDisplayMode(kDisplayId1, DisplayModeId(1), 120_Hz));
static inline const DisplayModes kDisplay1Modes = makeModes(kDisplay1Mode60, kDisplay1Mode120);
+ static inline FrameRateMode kDisplay1Mode60_60{60_Hz, kDisplay1Mode60};
+ static inline FrameRateMode kDisplay1Mode120_120{120_Hz, kDisplay1Mode120};
+
static constexpr PhysicalDisplayId kDisplayId2 = PhysicalDisplayId::fromPort(254u);
static inline const ftl::NonNull<DisplayModePtr> kDisplay2Mode60 =
ftl::as_non_null(createDisplayMode(kDisplayId2, DisplayModeId(0), 60_Hz));
@@ -123,6 +128,7 @@
.WillRepeatedly(Return(mEventThreadConnection));
mScheduler->setEventThread(Cycle::Render, std::move(eventThread));
+ mScheduler->setEventThread(Cycle::LastComposite, std::make_unique<MockEventThread>());
mFlinger.resetScheduler(mScheduler);
}
@@ -162,7 +168,17 @@
// recordLayerHistory should be a noop
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
- mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, 0,
+ scheduler::LayerProps layerProps = {
+ .visible = true,
+ .bounds = {0, 0, 100, 100},
+ .transform = {},
+ .setFrameRateVote = {},
+ .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+ .isSmallDirty = false,
+ .isFrontBuffered = false,
+ };
+
+ mScheduler->recordLayerHistory(layer->getSequence(), layerProps, 0, 0,
LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
@@ -188,16 +204,53 @@
kDisplay1Mode60->getId()));
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
- mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, 0,
+ scheduler::LayerProps layerProps = {
+ .visible = true,
+ .bounds = {0, 0, 100, 100},
+ .transform = {},
+ .setFrameRateVote = {},
+ .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+ .isSmallDirty = false,
+ .isFrontBuffered = false,
+ };
+ mScheduler->recordLayerHistory(layer->getSequence(), layerProps, 0, 0,
LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1u, mScheduler->getNumActiveLayers());
}
-TEST_F(SchedulerTest, dispatchCachedReportedMode) {
- mScheduler->clearCachedReportedMode();
+TEST_F(SchedulerTest, emitModeChangeEvent) {
+ const auto selectorPtr =
+ std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode120->getId());
+ mScheduler->registerDisplay(kDisplayId1, selectorPtr);
+ mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120);
+ mScheduler->setContentRequirements({kLayer});
+
+ // No event is emitted in response to idle.
EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
- EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode());
+
+ using TimerState = TestableScheduler::TimerState;
+
+ mScheduler->idleTimerCallback(TimerState::Expired);
+ selectorPtr->setActiveMode(kDisplay1Mode60->getId(), 60_Hz);
+
+ auto layer = kLayer;
+ layer.vote = RefreshRateSelector::LayerVoteType::ExplicitExact;
+ layer.desiredRefreshRate = 60_Hz;
+ mScheduler->setContentRequirements({layer});
+
+ // An event is emitted implicitly despite choosing the same mode as when idle.
+ EXPECT_CALL(*mEventThread, onModeChanged(kDisplay1Mode60_60)).Times(1);
+
+ mScheduler->idleTimerCallback(TimerState::Reset);
+
+ mScheduler->setContentRequirements({kLayer});
+
+ // An event is emitted explicitly for the mode change.
+ EXPECT_CALL(*mEventThread, onModeChanged(kDisplay1Mode120_120)).Times(1);
+
+ mScheduler->touchTimerCallback(TimerState::Reset);
+ mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120);
}
TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) {
@@ -225,9 +278,16 @@
kDisplay1Mode60->getId()));
const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
- EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true));
-
- mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, systemTime(),
+ scheduler::LayerProps layerProps = {
+ .visible = true,
+ .bounds = {0, 0, 0, 0},
+ .transform = {},
+ .setFrameRateVote = {},
+ .frameRateSelectionPriority = Layer::PRIORITY_UNSET,
+ .isSmallDirty = false,
+ .isFrontBuffered = false,
+ };
+ mScheduler->recordLayerHistory(layer->getSequence(), layerProps, 0, systemTime(),
LayerHistory::LayerUpdateType::Buffer);
constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
@@ -246,14 +306,12 @@
/*updateAttachedChoreographer*/ false);
}
-TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) {
+TEST_F(SchedulerTest, chooseDisplayModes) {
mScheduler->registerDisplay(kDisplayId1,
std::make_shared<RefreshRateSelector>(kDisplay1Modes,
kDisplay1Mode60->getId()));
- std::vector<RefreshRateSelector::LayerRequirement> layers =
- std::vector<RefreshRateSelector::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}});
- mScheduler->setContentRequirements(layers);
+ mScheduler->setContentRequirements({kLayer, kLayer});
GlobalSignals globalSignals = {.idle = true};
mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
@@ -288,15 +346,14 @@
EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals));
}
-TEST_F(SchedulerTest, chooseDisplayModesSingleDisplayHighHintTouchSignal) {
+TEST_F(SchedulerTest, chooseDisplayModesHighHintTouchSignal) {
mScheduler->registerDisplay(kDisplayId1,
std::make_shared<RefreshRateSelector>(kDisplay1Modes,
kDisplay1Mode60->getId()));
using DisplayModeChoice = TestableScheduler::DisplayModeChoice;
- std::vector<RefreshRateSelector::LayerRequirement> layers =
- std::vector<RefreshRateSelector::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}});
+ std::vector<RefreshRateSelector::LayerRequirement> layers = {kLayer, kLayer};
auto& lr1 = layers[0];
auto& lr2 = layers[1];
@@ -371,9 +428,7 @@
kDisplay2Mode60},
GlobalSignals{});
- std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f},
- {.weight = 1.f}};
- mScheduler->setContentRequirements(layers);
+ mScheduler->setContentRequirements({kLayer, kLayer});
mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
const auto actualChoices = mScheduler->chooseDisplayModes();
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
deleted file mode 100644
index 9899d42..0000000
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * Copyright 2020 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 "LibSurfaceFlingerUnittests"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/FrameRateUtils.h>
-#include <gui/LayerMetadata.h>
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include "Layer.h"
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
-#include "FpsOps.h"
-#include "LayerTestUtils.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockVsyncController.h"
-
-namespace android {
-
-using testing::DoAll;
-using testing::Mock;
-using testing::SetArgPointee;
-
-using android::Hwc2::IComposer;
-using android::Hwc2::IComposerClient;
-
-using scheduler::LayerHistory;
-
-using FrameRate = Layer::FrameRate;
-using FrameRateCompatibility = Layer::FrameRateCompatibility;
-
-/**
- * This class tests the behaviour of Layer::SetFrameRate and Layer::GetFrameRate
- */
-class SetFrameRateTest : public BaseLayerTest {
-protected:
- const FrameRate FRAME_RATE_VOTE1 = FrameRate(67_Hz, FrameRateCompatibility::Default);
- const FrameRate FRAME_RATE_VOTE2 = FrameRate(14_Hz, FrameRateCompatibility::ExactOrMultiple);
- const FrameRate FRAME_RATE_VOTE3 = FrameRate(99_Hz, FrameRateCompatibility::NoVote);
- const FrameRate FRAME_RATE_TREE = FrameRate(Fps(), FrameRateCompatibility::NoVote);
- const FrameRate FRAME_RATE_NO_VOTE = FrameRate(Fps(), FrameRateCompatibility::Default);
-
- SetFrameRateTest();
-
- void addChild(sp<Layer> layer, sp<Layer> child);
- void removeChild(sp<Layer> layer, sp<Layer> child);
- void commitTransaction();
-
- std::vector<sp<Layer>> mLayers;
-};
-
-SetFrameRateTest::SetFrameRateTest() {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
-
- mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
-}
-
-void SetFrameRateTest::addChild(sp<Layer> layer, sp<Layer> child) {
- layer->addChild(child);
-}
-
-void SetFrameRateTest::removeChild(sp<Layer> layer, sp<Layer> child) {
- layer->removeChild(child);
-}
-
-void SetFrameRateTest::commitTransaction() {
- for (auto layer : mLayers) {
- layer->commitTransaction();
- }
-}
-
-namespace {
-
-TEST_P(SetFrameRateTest, SetAndGet) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- layer->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE1, layer->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetParent) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, child2);
-
- child2->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetParentAllVote) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, child2);
-
- child2->setFrameRate(FRAME_RATE_VOTE1.vote);
- child1->setFrameRate(FRAME_RATE_VOTE2.vote);
- parent->setFrameRate(FRAME_RATE_VOTE3.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE2, child2->getFrameRateForLayerTree());
-
- child1->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE3, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE3, child2->getFrameRateForLayerTree());
-
- parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetChild) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, child2);
-
- parent->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetChildAllVote) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, child2);
-
- child2->setFrameRate(FRAME_RATE_VOTE1.vote);
- child1->setFrameRate(FRAME_RATE_VOTE2.vote);
- parent->setFrameRate(FRAME_RATE_VOTE3.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- child1->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetChildAddAfterVote) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
-
- parent->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-
- addChild(child1, child2);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetChildRemoveAfterVote) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, child2);
-
- parent->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
-
- removeChild(child1, child2);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-
- parent->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-TEST_P(SetFrameRateTest, SetAndGetParentNotInTree) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- auto child2_1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, child2);
- addChild(child1, child2_1);
-
- child2->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
-
- child2->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
-}
-
-INSTANTIATE_TEST_SUITE_P(PerLayerType, SetFrameRateTest,
- testing::Values(std::make_shared<BufferStateLayerFactory>(),
- std::make_shared<EffectLayerFactory>()),
- PrintToStringParamName);
-
-TEST_P(SetFrameRateTest, SetOnParentActivatesTree) {
- const auto& layerFactory = GetParam();
-
- auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- auto child = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- addChild(parent, child);
-
- parent->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
-
- auto& history = mFlinger.mutableScheduler().mutableLayerHistory();
- history.record(parent->getSequence(), parent->getLayerProps(), 0, 0,
- LayerHistory::LayerUpdateType::Buffer);
- history.record(child->getSequence(), child->getLayerProps(), 0, 0,
- LayerHistory::LayerUpdateType::Buffer);
-
- const auto selectorPtr = mFlinger.mutableScheduler().refreshRateSelector();
- const auto summary = history.summarize(*selectorPtr, 0);
-
- ASSERT_EQ(2u, summary.size());
- EXPECT_EQ(FRAME_RATE_VOTE1.vote.rate, summary[0].desiredRefreshRate);
- EXPECT_EQ(FRAME_RATE_VOTE1.vote.rate, summary[1].desiredRefreshRate);
-}
-
-TEST_P(SetFrameRateTest, addChildForParentWithTreeVote) {
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
-
- const auto& layerFactory = GetParam();
-
- const auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- const auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- const auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
- const auto childOfChild1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
-
- addChild(parent, child1);
- addChild(child1, childOfChild1);
-
- childOfChild1->setFrameRate(FRAME_RATE_VOTE1.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, childOfChild1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-
- addChild(parent, child2);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_VOTE1, childOfChild1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-
- childOfChild1->setFrameRate(FRAME_RATE_NO_VOTE.vote);
- commitTransaction();
- EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, childOfChild1->getFrameRateForLayerTree());
- EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index e5f2a91..2d3ebb4 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -97,7 +97,7 @@
// Cleanup conditions
// Creating the display commits a display transaction.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
}
TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) {
@@ -129,7 +129,7 @@
// Cleanup conditions
// Creating the display commits a display transaction.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
}
TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForUniqueId) {
@@ -159,7 +159,7 @@
// Cleanup conditions
// Creating the display commits a display transaction.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
}
// Requesting 0 tells SF not to do anything, i.e., default to refresh as physical displays
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
index f8ad8e1..df8f68f 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
@@ -38,7 +38,7 @@
// Call Expectations
// Destroying the display commits a display transaction.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
// --------------------------------------------------------------------
// Invocation
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
index db6df22..4bc134f 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp
@@ -18,7 +18,7 @@
#define LOG_TAG "LibSurfaceFlingerUnittests"
#include <gtest/gtest.h>
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <private/gui/ComposerService.h>
#include <private/gui/ComposerServiceAIDL.h>
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index 897f9a0..aef467a 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -48,7 +48,7 @@
TEST_F(HotplugTest, schedulesFrameToCommitDisplayTransaction) {
EXPECT_CALL(*mFlinger.scheduler(), scheduleConfigure()).Times(1);
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
constexpr HWDisplayId displayId1 = 456;
mFlinger.onComposerHalHotplugEvent(displayId1, DisplayHotplugEvent::DISCONNECTED);
@@ -73,7 +73,7 @@
.WillOnce(Return(Error::NONE));
// A single commit should be scheduled for both configure calls.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
mFlinger.configure();
@@ -116,7 +116,7 @@
setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
.WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
mFlinger.configure();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
index eaf4684..5231965 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp
@@ -28,7 +28,7 @@
TEST_F(InitializeDisplaysTest, initializesDisplays) {
// Scheduled by the display transaction, and by powering on each display.
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(3);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(3);
EXPECT_CALL(static_cast<mock::VSyncTracker&>(
mFlinger.scheduler()->getVsyncSchedule()->getTracker()),
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 83e2f98..fed7b2e 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -271,7 +271,7 @@
}
static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame(_)).Times(1);
}
static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
deleted file mode 100644
index 0e5f1ea..0000000
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp
+++ /dev/null
@@ -1,194 +0,0 @@
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/LayerMetadata.h>
-
-#include "TestableSurfaceFlinger.h"
-
-namespace android {
-
-using testing::_;
-using testing::Return;
-
-class SurfaceFlingerUpdateLayerMetadataSnapshotTest : public testing::Test {
-public:
- SurfaceFlingerUpdateLayerMetadataSnapshotTest() { mFlinger.setupMockScheduler(); }
-
-protected:
- sp<Layer> createLayer(const char* name, LayerMetadata& inOutlayerMetadata) {
- LayerCreationArgs args =
- LayerCreationArgs{mFlinger.flinger(), nullptr, name, 0, inOutlayerMetadata};
- inOutlayerMetadata = args.metadata;
- return sp<Layer>::make(args);
- }
-
- TestableSurfaceFlinger mFlinger;
-};
-
-class LayerMetadataBuilder {
-public:
- LayerMetadataBuilder(LayerMetadata layerMetadata = {}) : mLayerMetadata(layerMetadata) {}
-
- LayerMetadataBuilder& setInt32(uint32_t key, int32_t value) {
- mLayerMetadata.setInt32(key, value);
- return *this;
- }
-
- LayerMetadata build() { return mLayerMetadata; }
-
-private:
- LayerMetadata mLayerMetadata;
-};
-
-bool operator==(const LayerMetadata& lhs, const LayerMetadata& rhs) {
- return lhs.mMap == rhs.mMap;
-}
-
-std::ostream& operator<<(std::ostream& stream, const LayerMetadata& layerMetadata) {
- stream << "LayerMetadata{";
- for (auto it = layerMetadata.mMap.cbegin(); it != layerMetadata.mMap.cend(); it++) {
- if (it != layerMetadata.mMap.cbegin()) {
- stream << ", ";
- }
- stream << layerMetadata.itemToString(it->first, ":");
- }
- return stream << "}";
-}
-
-// Test that the snapshot's layer metadata is set.
-TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesSnapshotMetadata) {
- auto layerMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build();
- auto layer = createLayer("layer", layerMetadata);
- mFlinger.mutableDrawingState().layersSortedByZ.add(layer);
-
- mFlinger.updateLayerMetadataSnapshot();
-
- EXPECT_EQ(layer->getLayerSnapshot()->layerMetadata, layerMetadata);
-}
-
-// Test that snapshot layer metadata is set by merging the child's metadata on top of its
-// parent's metadata.
-TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, mergesSnapshotMetadata) {
- auto layerAMetadata = LayerMetadataBuilder()
- .setInt32(METADATA_OWNER_UID, 1)
- .setInt32(METADATA_TASK_ID, 2)
- .build();
- auto layerA = createLayer("parent", layerAMetadata);
- auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 3).build();
- auto layerB = createLayer("child", layerBMetadata);
- layerA->addChild(layerB);
- layerA->commitChildList();
- mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
-
- mFlinger.updateLayerMetadataSnapshot();
-
- EXPECT_EQ(layerA->getLayerSnapshot()->layerMetadata, layerAMetadata);
- auto expectedChildMetadata =
- LayerMetadataBuilder(layerAMetadata).setInt32(METADATA_TASK_ID, 3).build();
- EXPECT_EQ(layerB->getLayerSnapshot()->layerMetadata, expectedChildMetadata);
-}
-
-// Test that snapshot relative layer metadata is set to the parent's layer metadata merged on top of
-// that parent's relative layer metadata.
-TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadata) {
- auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 1).build();
- auto layerA = createLayer("relative-parent", layerAMetadata);
- auto layerAHandle = layerA->getHandle();
- auto layerBMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 2).build();
- auto layerB = createLayer("relative-child", layerBMetadata);
- layerB->setRelativeLayer(layerAHandle, 1);
- mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
- mFlinger.mutableDrawingState().layersSortedByZ.add(layerB);
-
- mFlinger.updateLayerMetadataSnapshot();
-
- EXPECT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
- EXPECT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
-}
-
-// Test that snapshot relative layer metadata is set correctly when a layer is interleaved within
-// two other layers.
-//
-// Layer
-// A
-// / \
-// B D
-// /
-// C
-//
-// Z-order Relatives
-// B <- D <- C
-TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest, updatesRelativeMetadataInterleaved) {
- auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build();
- auto layerA = createLayer("layer-a", layerAMetadata);
- auto layerBMetadata = LayerMetadataBuilder()
- .setInt32(METADATA_TASK_ID, 2)
- .setInt32(METADATA_OWNER_PID, 3)
- .build();
- auto layerB = createLayer("layer-b", layerBMetadata);
- auto layerBHandle = layerB->getHandle();
- LayerMetadata layerCMetadata;
- auto layerC = createLayer("layer-c", layerCMetadata);
- auto layerDMetadata = LayerMetadataBuilder().setInt32(METADATA_TASK_ID, 4).build();
- auto layerD = createLayer("layer-d", layerDMetadata);
- auto layerDHandle = layerD->getHandle();
- layerB->addChild(layerC);
- layerA->addChild(layerB);
- layerA->addChild(layerD);
- layerC->setRelativeLayer(layerDHandle, 1);
- layerD->setRelativeLayer(layerBHandle, 1);
- layerA->commitChildList();
- mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
-
- mFlinger.updateLayerMetadataSnapshot();
-
- auto expectedLayerDRelativeMetadata =
- LayerMetadataBuilder()
- // From layer A, parent of relative parent
- .setInt32(METADATA_OWNER_UID, 1)
- // From layer B, relative parent
- .setInt32(METADATA_TASK_ID, 2)
- .setInt32(METADATA_OWNER_PID, 3)
- // added by layer creation args
- .setInt32(gui::METADATA_CALLING_UID,
- layerDMetadata.getInt32(gui::METADATA_CALLING_UID, 0))
- .build();
- EXPECT_EQ(layerD->getLayerSnapshot()->relativeLayerMetadata, expectedLayerDRelativeMetadata);
- auto expectedLayerCRelativeMetadata =
- LayerMetadataBuilder()
- // From layer A, parent of relative parent
- .setInt32(METADATA_OWNER_UID, 1)
- // From layer B, relative parent of relative parent
- .setInt32(METADATA_OWNER_PID, 3)
- // From layer D, relative parent
- .setInt32(METADATA_TASK_ID, 4)
- // added by layer creation args
- .setInt32(gui::METADATA_CALLING_UID,
- layerDMetadata.getInt32(gui::METADATA_CALLING_UID, 0))
- .build();
- EXPECT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, expectedLayerCRelativeMetadata);
-}
-
-TEST_F(SurfaceFlingerUpdateLayerMetadataSnapshotTest,
- updatesRelativeMetadataMultipleRelativeChildren) {
- auto layerAMetadata = LayerMetadataBuilder().setInt32(METADATA_OWNER_UID, 1).build();
- auto layerA = createLayer("layer-a", layerAMetadata);
- auto layerAHandle = layerA->getHandle();
- LayerMetadata layerBMetadata;
- auto layerB = createLayer("layer-b", layerBMetadata);
- LayerMetadata layerCMetadata;
- auto layerC = createLayer("layer-c", layerCMetadata);
- layerB->setRelativeLayer(layerAHandle, 1);
- layerC->setRelativeLayer(layerAHandle, 2);
- layerA->commitChildList();
- mFlinger.mutableDrawingState().layersSortedByZ.add(layerA);
- mFlinger.mutableDrawingState().layersSortedByZ.add(layerB);
- mFlinger.mutableDrawingState().layersSortedByZ.add(layerC);
-
- mFlinger.updateLayerMetadataSnapshot();
-
- EXPECT_EQ(layerA->getLayerSnapshot()->relativeLayerMetadata, LayerMetadata{});
- EXPECT_EQ(layerB->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
- EXPECT_EQ(layerC->getLayerSnapshot()->relativeLayerMetadata, layerAMetadata);
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index f063809..df16b2e 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -62,7 +62,7 @@
}
MOCK_METHOD(void, scheduleConfigure, (), (override));
- MOCK_METHOD(void, scheduleFrame, (), (override));
+ MOCK_METHOD(void, scheduleFrame, (Duration), (override));
MOCK_METHOD(void, postMessage, (sp<MessageHandler>&&), (override));
void doFrameSignal(ICompositor& compositor, VsyncId vsyncId) {
@@ -176,6 +176,11 @@
mPolicy.idleTimer = globalSignals.idle ? TimerState::Expired : TimerState::Reset;
}
+ using Scheduler::TimerState;
+
+ using Scheduler::idleTimerCallback;
+ using Scheduler::touchTimerCallback;
+
void setContentRequirements(std::vector<RefreshRateSelector::LayerRequirement> layers) {
std::lock_guard<std::mutex> lock(mPolicyLock);
mPolicy.contentRequirements = std::move(layers);
@@ -188,15 +193,7 @@
return Scheduler::chooseDisplayModes();
}
- void dispatchCachedReportedMode() {
- std::lock_guard<std::mutex> lock(mPolicyLock);
- Scheduler::dispatchCachedReportedMode();
- }
-
- void clearCachedReportedMode() {
- std::lock_guard<std::mutex> lock(mPolicyLock);
- mPolicy.cachedModeChangedParams.reset();
- }
+ using Scheduler::onDisplayModeChanged;
void setInitialHwVsyncEnabled(PhysicalDisplayId id, bool enabled) {
auto schedule = getVsyncSchedule(id);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index e3b2af1..a6a2758 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -528,7 +528,7 @@
auto setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
bool hasListenerCallbacks, std::vector<ListenerCallbacks>& listenerCallbacks,
@@ -584,8 +584,6 @@
return mFlinger->mirrorLayer(args, mirrorFromHandle, outResult);
}
- void updateLayerMetadataSnapshot() { mFlinger->updateLayerMetadataSnapshot(); }
-
void getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
ui::DynamicDisplayInfo* dynamicDisplayInfo) {
mFlinger->getDynamicDisplayInfoFromToken(displayToken, dynamicDisplayInfo);
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index e13fe49..fab1f6d 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -105,7 +105,7 @@
void NotPlacedOnTransactionQueue(uint32_t flags) {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
TransactionInfo transaction;
setupSingle(transaction, flags,
/*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
@@ -129,7 +129,7 @@
void PlaceOnTransactionQueue(uint32_t flags) {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
// first check will see desired present time has not passed,
// but afterwards it will look like the desired present time has passed
@@ -155,7 +155,7 @@
void BlockedByPriorTransaction(uint32_t flags) {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
nsecs_t time = systemTime();
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(2);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(2);
// transaction that should go on the pending thread
TransactionInfo transactionA;
@@ -217,7 +217,7 @@
TEST_F(TransactionApplicationTest, AddToPendingQueue) {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
@@ -238,7 +238,7 @@
TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
- EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index 85b61f8..abfab9a 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -94,7 +94,7 @@
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
layer->setBuffer(externalTexture, bufferData, postTime, /*desiredPresentTime*/ 30, false,
- FrameTimelineInfo{});
+ FrameTimelineInfo{}, gui::GameMode::Unsupported);
commitTransaction(layer.get());
nsecs_t latchTime = 25;
@@ -112,7 +112,8 @@
EXPECT_CALL(*mFlinger.getFrameTracer(),
traceFence(layerId, bufferId, frameNumber, presentFence,
FrameTracer::FrameEvent::PRESENT_FENCE, /*startTime*/ 0));
- layer->onCompositionPresented(nullptr, glDoneFence, presentFence, compositorTiming);
+ layer->onCompositionPresented(nullptr, glDoneFence, presentFence, compositorTiming,
+ gui::GameMode::Unsupported);
}
};
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 0745f87..9a68d75 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -72,7 +72,8 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10);
+ layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10,
+ gui::GameMode::Unsupported);
EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_TRUE(layer->mDrawingState.bufferSurfaceFrameTX == nullptr);
const auto surfaceFrame = layer->mDrawingState.bufferlessSurfaceFramesTX.at(/*token*/ 1);
@@ -99,7 +100,8 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo,
+ gui::GameMode::Unsupported);
acquireFence->signalForTest(12);
commitTransaction(layer.get());
@@ -134,7 +136,8 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo,
+ gui::GameMode::Unsupported);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -151,7 +154,8 @@
2ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfo);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfo,
+ gui::GameMode::Unsupported);
nsecs_t end = systemTime();
acquireFence2->signalForTest(12);
@@ -180,7 +184,8 @@
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10);
+ layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10,
+ gui::GameMode::Unsupported);
EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_EQ(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -197,7 +202,8 @@
1ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo,
+ gui::GameMode::Unsupported);
acquireFence->signalForTest(12);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
@@ -232,11 +238,13 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo,
+ gui::GameMode::Unsupported);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
- layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10);
+ layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10,
+ gui::GameMode::Unsupported);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
}
@@ -246,7 +254,8 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10);
+ layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo, 10,
+ gui::GameMode::Unsupported);
EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_EQ(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto bufferlessSurfaceFrame1 =
@@ -255,7 +264,8 @@
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 4;
ftInfo2.inputEventId = 0;
- layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10);
+ layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10,
+ gui::GameMode::Unsupported);
EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_EQ(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto bufferlessSurfaceFrame2 = layer->mDrawingState.bufferlessSurfaceFramesTX[4];
@@ -275,7 +285,8 @@
FrameTimelineInfo ftInfo3;
ftInfo3.vsyncId = 3;
ftInfo3.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo3);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo3,
+ gui::GameMode::Unsupported);
EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -320,7 +331,8 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo,
+ gui::GameMode::Unsupported);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -340,7 +352,8 @@
FrameTimelineInfo ftInfoInv;
ftInfoInv.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
ftInfoInv.inputEventId = 0;
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfoInv);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfoInv,
+ gui::GameMode::Unsupported);
auto dropEndTime1 = systemTime();
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -361,7 +374,8 @@
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 2;
ftInfo2.inputEventId = 0;
- layer->setBuffer(externalTexture3, bufferData, 10, 20, false, ftInfo2);
+ layer->setBuffer(externalTexture3, bufferData, 10, 20, false, ftInfo2,
+ gui::GameMode::Unsupported);
auto dropEndTime2 = systemTime();
acquireFence3->signalForTest(12);
@@ -409,11 +423,13 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo,
+ gui::GameMode::Unsupported);
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 2;
ftInfo2.inputEventId = 0;
- layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10);
+ layer->setFrameTimelineVsyncForBufferlessTransaction(ftInfo2, 10,
+ gui::GameMode::Unsupported);
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
EXPECT_EQ(1u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
index fb4ef70..7bf1674 100644
--- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -131,14 +131,14 @@
// add layers and add some layer transaction
{
frontend::Update update;
- update.layerCreationArgs.emplace_back(std::move(
+ update.layerCreationArgs.emplace_back(
getLayerCreationArgs(mParentLayerId, /*parentId=*/UNASSIGNED_LAYER_ID,
/*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/123,
- /*addToRoot=*/true)));
- update.layerCreationArgs.emplace_back(std::move(
+ /*addToRoot=*/true));
+ update.layerCreationArgs.emplace_back(
getLayerCreationArgs(mChildLayerId, mParentLayerId,
/*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/456,
- /*addToRoot=*/true)));
+ /*addToRoot=*/true));
TransactionState transaction;
transaction.id = 50;
ResolvedComposerState layerState;
diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
index 1cf14ae..9f6065b 100644
--- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
@@ -127,7 +127,7 @@
sp<NativeHandle> stream =
NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
false);
- layer->setSidebandStream(stream, FrameTimelineInfo{}, 20);
+ layer->setSidebandStream(stream, FrameTimelineInfo{}, 20, gui::GameMode::Unsupported);
mFlinger.mutableCurrentState().layersSortedByZ.add(layer);
mTunnelModeEnabledReporter->updateTunnelModeStatus();
mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener);
@@ -151,7 +151,8 @@
sp<NativeHandle> stream =
NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
false);
- layerWithSidebandStream->setSidebandStream(stream, FrameTimelineInfo{}, 20);
+ layerWithSidebandStream->setSidebandStream(stream, FrameTimelineInfo{}, 20,
+ gui::GameMode::Unsupported);
mFlinger.mutableCurrentState().layersSortedByZ.add(simpleLayer);
mFlinger.mutableCurrentState().layersSortedByZ.add(layerWithSidebandStream);
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 8690dba..7c678bd 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -1055,6 +1055,36 @@
EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1400, 1000));
EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2000, 1000));
}
+
+TEST_F(VSyncPredictorTest, adjustsOnlyMinFrameViolatingVrrTimeline) {
+ const auto refreshRate = Fps::fromPeriodNsecs(500);
+ auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig{.minFrameIntervalNs =
+ static_cast<int32_t>(minFrameRate.getPeriodNsecs())};
+ ftl::NonNull<DisplayModePtr> mode =
+ ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), refreshRate, vrrConfig));
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), mode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+ vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+
+ EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000));
+ auto lastConfirmedSignalTime = TimePoint::fromNs(1500);
+ auto lastConfirmedExpectedPresentTime = TimePoint::fromNs(1000);
+ vrrTracker.onFrameBegin(TimePoint::fromNs(2000),
+ {lastConfirmedSignalTime, lastConfirmedExpectedPresentTime});
+ EXPECT_EQ(3500, vrrTracker.nextAnticipatedVSyncTimeFrom(3000, 1500));
+
+ minFrameRate = Fps::fromPeriodNsecs(2000);
+ vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false);
+ lastConfirmedSignalTime = TimePoint::fromNs(2500);
+ lastConfirmedExpectedPresentTime = TimePoint::fromNs(2500);
+ vrrTracker.onFrameBegin(TimePoint::fromNs(3000),
+ {lastConfirmedSignalTime, lastConfirmedExpectedPresentTime});
+ // Enough time without adjusting vsync to present with new rate on time, no need of adjustment
+ EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 3500));
+}
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 184dada..e380e19 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -180,6 +180,8 @@
MOCK_METHOD1(onHotplugDisconnect, void(Display));
MOCK_METHOD(Error, setRefreshRateChangedCallbackDebugEnabled, (Display, bool));
MOCK_METHOD(Error, notifyExpectedPresent, (Display, nsecs_t, int32_t));
+ MOCK_METHOD(Error, getDisplayLuts,
+ (Display, std::vector<aidl::android::hardware::graphics::composer3::Lut>*));
};
} // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 602bdfc..1eda358 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -109,6 +109,8 @@
MOCK_METHOD(hal::Error, getOverlaySupport,
(aidl::android::hardware::graphics::composer3::OverlayProperties *),
(const override));
+ MOCK_METHOD(hal::Error, getDisplayLuts,
+ (std::vector<aidl::android::hardware::graphics::composer3::Lut>*), (override));
};
class Layer : public HWC2::Layer {
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 002fa9f..fdb6f4d 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -24,16 +24,10 @@
class MockLayer : public Layer {
public:
MockLayer(SurfaceFlinger* flinger, std::string name)
- : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {
- EXPECT_CALL(*this, getDefaultFrameRateCompatibility())
- .WillOnce(testing::Return(scheduler::FrameRateCompatibility::Default));
- }
+ : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {}
MockLayer(SurfaceFlinger* flinger, std::string name, std::optional<uint32_t> uid)
- : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, uid)) {
- EXPECT_CALL(*this, getDefaultFrameRateCompatibility())
- .WillOnce(testing::Return(scheduler::FrameRateCompatibility::Default));
- }
+ : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {}, uid)) {}
explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 8f21cdb..d45cc66 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -26,7 +26,6 @@
MOCK_METHOD(void, requestHardwareVsync, (PhysicalDisplayId, bool), (override));
MOCK_METHOD(void, requestDisplayModes, (std::vector<display::DisplayModeRequest>), (override));
MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
- MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
MOCK_METHOD(void, onChoreographerAttached, (), (override));
MOCK_METHOD(void, onExpectedPresentTimePosted, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps),
(override));
@@ -38,7 +37,6 @@
void requestHardwareVsync(PhysicalDisplayId, bool) override {}
void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {}
void kernelTimerChanged(bool) override {}
- void triggerOnFrameRateOverridesChanged() override {}
void onChoreographerAttached() override {}
void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {}
void onCommitNotComposited(PhysicalDisplayId) override {}
diff --git a/services/surfaceflinger/tests/utils/ColorUtils.h b/services/surfaceflinger/tests/utils/ColorUtils.h
index 07916b6..253bad7 100644
--- a/services/surfaceflinger/tests/utils/ColorUtils.h
+++ b/services/surfaceflinger/tests/utils/ColorUtils.h
@@ -74,8 +74,8 @@
static void applyMatrix(half3& color, const mat3& mat) {
half3 ret = half3(0);
- for (int i = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++) {
+ for (size_t i = 0; i < 3; i++) {
+ for (size_t j = 0; j < 3; j++) {
ret[i] = ret[i] + color[j] * mat[j][i];
}
}
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index 1675584..0bedcd1 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -15,7 +15,7 @@
*/
#pragma once
-#include <gui/AidlStatusUtil.h>
+#include <gui/AidlUtil.h>
#include <gui/SyncScreenCaptureListener.h>
#include <private/gui/ComposerServiceAIDL.h>
#include <ui/FenceResult.h>
@@ -39,7 +39,7 @@
const auto sf = ComposerServiceAIDL::getComposerService();
SurfaceComposerClient::Transaction().apply(true);
- captureArgs.dataspace = ui::Dataspace::V0_SRGB;
+ captureArgs.captureArgs.dataspace = static_cast<int32_t>(ui::Dataspace::V0_SRGB);
const sp<SyncScreenCaptureListener> captureListener = sp<SyncScreenCaptureListener>::make();
binder::Status status = sf->captureDisplay(captureArgs, captureListener);
status_t err = statusTFromBinderStatus(status);
@@ -77,7 +77,7 @@
const auto sf = ComposerServiceAIDL::getComposerService();
SurfaceComposerClient::Transaction().apply(true);
- captureArgs.dataspace = ui::Dataspace::V0_SRGB;
+ captureArgs.captureArgs.dataspace = static_cast<int32_t>(ui::Dataspace::V0_SRGB);
const sp<SyncScreenCaptureListener> captureListener = sp<SyncScreenCaptureListener>::make();
binder::Status status = sf->captureLayers(captureArgs, captureListener);
status_t err = statusTFromBinderStatus(status);
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 3f89960..ef213f0 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -339,10 +339,13 @@
ALOGD("Unload builtin Vulkan driver.");
- // Close the opened device
- int err = hal_.dev_->common.close(
- const_cast<struct hw_device_t*>(&hal_.dev_->common));
- ALOG_ASSERT(!err, "hw_device_t::close() failed.");
+ if (hal_.dev_->common.close != nullptr)
+ {
+ // Close the opened device
+ int err = hal_.dev_->common.close(
+ const_cast<struct hw_device_t*>(&hal_.dev_->common));
+ ALOG_ASSERT(!err, "hw_device_t::close() failed.");
+ }
// Close the opened shared library in the hw_module_t
android_unload_sphal_library(hal_.dev_->common.module->dso);