Merge "Add TFLite model for motion prediction."
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index cc038ae..695faf8 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -718,7 +718,8 @@
if (service.guaranteeClient) {
// we have no record of this client
if (!service.hasClients && !hasClients) {
- sendClientCallbackNotifications(serviceName, true);
+ sendClientCallbackNotifications(serviceName, true,
+ "service is guaranteed to be in use");
}
// guarantee is temporary
@@ -729,34 +730,41 @@
if (isCalledOnInterval) {
if (hasClients && !service.hasClients) {
// client was retrieved in some other way
- sendClientCallbackNotifications(serviceName, true);
+ sendClientCallbackNotifications(serviceName, true, "we now have a record of a client");
}
// there are no more clients, but the callback has not been called yet
if (!hasClients && service.hasClients) {
- sendClientCallbackNotifications(serviceName, false);
+ sendClientCallbackNotifications(serviceName, false,
+ "we now have no record of a client");
}
}
return count;
}
-void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName, bool hasClients) {
+void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName,
+ bool hasClients, const char* context) {
auto serviceIt = mNameToService.find(serviceName);
if (serviceIt == mNameToService.end()) {
- ALOGW("sendClientCallbackNotifications could not find service %s", serviceName.c_str());
+ ALOGW("sendClientCallbackNotifications could not find service %s when %s",
+ serviceName.c_str(), context);
return;
}
Service& service = serviceIt->second;
- CHECK(hasClients != service.hasClients) << "Record shows: " << service.hasClients
- << " so we can't tell clients again that we have client: " << hasClients;
+ CHECK(hasClients != service.hasClients)
+ << "Record shows: " << service.hasClients
+ << " so we can't tell clients again that we have client: " << hasClients
+ << " when: " << context;
- ALOGI("Notifying %s they have clients: %d", serviceName.c_str(), hasClients);
+ ALOGI("Notifying %s they %s have clients when %s", serviceName.c_str(),
+ hasClients ? "do" : "don't", context);
auto ccIt = mNameToClientCallback.find(serviceName);
CHECK(ccIt != mNameToClientCallback.end())
- << "sendClientCallbackNotifications could not find callbacks for service ";
+ << "sendClientCallbackNotifications could not find callbacks for service when "
+ << context;
for (const auto& callback : ccIt->second) {
callback->onClients(service.binder, hasClients);
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index b24c11c..f9d4f8f 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -92,8 +92,9 @@
ServiceCallbackMap::iterator* it,
bool* found);
ssize_t handleServiceClientCallback(const std::string& serviceName, bool isCalledOnInterval);
- // Also updates mHasClients (of what the last callback was)
- void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients);
+ // Also updates mHasClients (of what the last callback was)
+ void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients,
+ const char* context);
// removes a callback from mNameToClientCallback, deleting the entry if the vector is empty
// this updates the iterator to the next location
void removeClientCallback(const wp<IBinder>& who, ClientCallbackMap::iterator* it);
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 4a5bd5e..b494f89 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -37,6 +37,7 @@
#include <android/api-level.h>
#include <stdint.h>
+#include <unistd.h>
__BEGIN_DECLS
@@ -173,7 +174,7 @@
*/
int APerformanceHint_setThreads(
APerformanceHintSession* session,
- const int32_t* threadIds,
+ const pid_t* threadIds,
size_t size) __INTRODUCED_IN(__ANDROID_API_U__);
__END_DECLS
diff --git a/include/input/Input.h b/include/input/Input.h
index e281675..7573282 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -529,7 +529,7 @@
inline nsecs_t getEventTime() const { return mEventTime; }
static const char* getLabel(int32_t keyCode);
- static int32_t getKeyCodeFromLabel(const char* label);
+ static std::optional<int> getKeyCodeFromLabel(const char* label);
void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t flags, int32_t keyCode,
@@ -550,6 +550,8 @@
nsecs_t mEventTime;
};
+std::ostream& operator<<(std::ostream& out, const KeyEvent& event);
+
/*
* Motion events.
*/
@@ -840,7 +842,7 @@
}
static const char* getLabel(int32_t axis);
- static int32_t getAxisFromLabel(const char* label);
+ static std::optional<int> getAxisFromLabel(const char* label);
static std::string actionToString(int32_t action);
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index b4374ac..4668fce 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -35,22 +35,22 @@
class InputEventLookup {
public:
- static int lookupValueByLabel(const std::unordered_map<std::string, int>& map,
- const char* literal);
+ static std::optional<int> lookupValueByLabel(const std::unordered_map<std::string, int>& map,
+ const char* literal);
static const char* lookupLabelByValue(const std::vector<InputEventLabel>& vec, int value);
- static int32_t getKeyCodeByLabel(const char* label);
+ static std::optional<int> getKeyCodeByLabel(const char* label);
static const char* getLabelByKeyCode(int32_t keyCode);
- static uint32_t getKeyFlagByLabel(const char* label);
+ static std::optional<int> getKeyFlagByLabel(const char* label);
- static int32_t getAxisByLabel(const char* label);
+ static std::optional<int> getAxisByLabel(const char* label);
static const char* getAxisLabel(int32_t axisId);
- static int32_t getLedByLabel(const char* label);
+ static std::optional<int> getLedByLabel(const char* label);
private:
static const std::unordered_map<std::string, int> KEYCODES;
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 808b1ec..7617136 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -74,6 +74,9 @@
name: "libbinder_common_defaults",
host_supported: true,
+ // for vndbinder and binderRpcTest
+ vendor_available: true,
+
srcs: [
"Binder.cpp",
"BpBinder.cpp",
@@ -197,6 +200,7 @@
cc_library_headers {
name: "trusty_mock_headers",
+ vendor_available: true,
host_supported: true,
export_include_dirs: [
@@ -211,6 +215,8 @@
cc_defaults {
name: "trusty_mock_defaults",
+ vendor_available: true,
+ host_supported: true,
header_libs: [
"trusty_mock_headers",
@@ -295,8 +301,6 @@
version_script: "libbinder.map",
- // for vndbinder
- vendor_available: true,
vndk: {
enabled: true,
},
@@ -455,6 +459,7 @@
cc_library_static {
name: "libbinder_tls_static",
defaults: ["libbinder_tls_defaults"],
+ vendor_available: true,
visibility: [
":__subpackages__",
],
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 44ff62b..0aca163 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -375,6 +375,10 @@
return (mDataSize > mDataPos ? mDataSize : mDataPos);
}
+size_t Parcel::dataBufferSize() const {
+ return mDataSize;
+}
+
size_t Parcel::dataAvail() const
{
size_t result = dataSize() - dataPosition();
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
index 5406205..2e70304 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -123,8 +123,8 @@
static_cast<int32_t>(timestamp.tv_nsec),
0};
- t.mData.mInterfaceName = String8(interfaceName);
- if (interfaceName.size() != t.mData.mInterfaceName.bytes()) {
+ t.mData.mInterfaceName = std::string(String8(interfaceName).string());
+ if (interfaceName.size() != t.mData.mInterfaceName.size()) {
LOG(ERROR) << "Interface Name is not valid. Contains characters that aren't single byte "
"utf-8: "
<< interfaceName;
@@ -231,7 +231,8 @@
break;
}
case INTERFACE_NAME_CHUNK: {
- t.mData.mInterfaceName.setTo(reinterpret_cast<char*>(payloadMap), chunk.dataSize);
+ t.mData.mInterfaceName =
+ std::string(reinterpret_cast<char*>(payloadMap), chunk.dataSize);
break;
}
case DATA_PARCEL_CHUNK: {
@@ -308,7 +309,7 @@
}
if (NO_ERROR !=
writeChunk(fd, INTERFACE_NAME_CHUNK, mData.mInterfaceName.size() * sizeof(uint8_t),
- reinterpret_cast<const uint8_t*>(mData.mInterfaceName.string()))) {
+ reinterpret_cast<const uint8_t*>(mData.mInterfaceName.c_str()))) {
LOG(INFO) << "Failed to write Interface Name Chunk to fd " << fd.get();
return UNKNOWN_ERROR;
}
@@ -328,7 +329,7 @@
return NO_ERROR;
}
-const android::String8& RecordedTransaction::getInterfaceName() const {
+const std::string& RecordedTransaction::getInterfaceName() const {
return mData.mInterfaceName;
}
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 180c67c..04cb61f 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -99,5 +99,19 @@
{
"name": "binderLibTest"
}
+ ],
+ "kernel-presubmit": [
+ {
+ "name": "binderDriverInterfaceTest"
+ },
+ {
+ "name": "binderLibTest"
+ },
+ {
+ "name": "binderSafeInterfaceTest"
+ },
+ {
+ "name": "memunreachable_binder_test"
+ }
]
}
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index f730acb..162cd40 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -75,6 +75,7 @@
size_t dataAvail() const;
size_t dataPosition() const;
size_t dataCapacity() const;
+ size_t dataBufferSize() const;
status_t setDataSize(size_t size);
diff --git a/libs/binder/include/binder/RecordedTransaction.h b/libs/binder/include/binder/RecordedTransaction.h
index 4966330..eb765fe 100644
--- a/libs/binder/include/binder/RecordedTransaction.h
+++ b/libs/binder/include/binder/RecordedTransaction.h
@@ -41,7 +41,7 @@
[[nodiscard]] status_t dumpToFile(const android::base::unique_fd& fd) const;
- const String8& getInterfaceName() const;
+ const std::string& getInterfaceName() const;
uint32_t getCode() const;
uint32_t getFlags() const;
int32_t getReturnedStatus() const;
@@ -73,7 +73,7 @@
struct MovableData { // movable
TransactionHeader mHeader;
- String8 mInterfaceName;
+ std::string mInterfaceName;
};
MovableData mData;
Parcel mSent;
diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h
index 9b0d222..b0c7f6d 100644
--- a/libs/binder/ndk/include_cpp/android/binder_to_string.h
+++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h
@@ -139,30 +139,6 @@
};
template <typename _T>
-class ToEmptyString {
- template <typename _U>
- static std::enable_if_t<false
-#ifdef HAS_NDK_INTERFACE
- || std::is_base_of_v<::ndk::ICInterface, _U>
-#if __ANDROID_API__ >= 31
- || std::is_same_v<::ndk::AParcelableHolder, _U>
-#endif
-#endif // HAS_NDK_INTERFACE
-#ifdef HAS_CPP_INTERFACE
- || std::is_base_of_v<IInterface, _U> ||
- std::is_same_v<IBinder, _U>
-#endif
- ,
- std::true_type>
- _test(int);
- template <typename _U>
- static std::false_type _test(...);
-
- public:
- enum { value = decltype(_test<_T>(0))::value };
-};
-
-template <typename _T>
struct TypeDependentFalse {
enum { value = false };
};
@@ -171,9 +147,7 @@
template <typename _T>
std::string ToString(const _T& t) {
- if constexpr (details::ToEmptyString<_T>::value) {
- return "<unimplemented>";
- } else if constexpr (std::is_same_v<bool, _T>) {
+ if constexpr (std::is_same_v<bool, _T>) {
return t ? "true" : "false";
} else if constexpr (std::is_same_v<char16_t, _T>) {
// TODO(b/244494451): codecvt is deprecated in C++17 -- suppress the
@@ -193,6 +167,24 @@
return ss.str();
} else if constexpr (std::is_same_v<::ndk::ScopedFileDescriptor, _T>) {
return "fd:" + std::to_string(t.get());
+ } else if constexpr (std::is_base_of_v<::ndk::ICInterface, _T>) {
+ // TODO(b/266248339): this format is to make it easy to handle resolv_integration_test
+ // freezing the output format. We would like to print more info.
+ return "<interface>";
+#if __ANDROID_API__ >= 31
+ } else if constexpr (std::is_same_v<::ndk::AParcelableHolder, _T>) {
+ return "AParcelableHolder";
+#endif
+#endif // HAS_NDK_INTERFACE
+#ifdef HAS_CPP_INTERFACE
+ } else if constexpr (std::is_base_of_v<IInterface, _T>) {
+ std::stringstream ss;
+ ss << "interface:" << std::hex << &t;
+ return ss.str();
+ } else if constexpr (std::is_same_v<IBinder, _T>) {
+ std::stringstream ss;
+ ss << "binder:" << std::hex << &t;
+ return ss.str();
#endif
#ifdef HAS_STRING16
} else if constexpr (std::is_same_v<String16, _T>) {
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 94f72d9..b5a2e2f 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -695,7 +695,10 @@
if (parcel->get()->objectsCount()) {
return STATUS_INVALID_OPERATION;
}
- int32_t dataSize = AParcel_getDataSize(parcel);
+ // b/264739302 - getDataSize will return dataPos if it is greater than dataSize
+ // which will cause crashes in memcpy at later point. Instead compare with
+ // actual length of internal buffer
+ int32_t dataSize = parcel->get()->dataBufferSize();
if (len > static_cast<size_t>(dataSize) || start > static_cast<size_t>(dataSize) - len) {
return STATUS_BAD_VALUE;
}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index bab4e73..458dff3 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -138,6 +138,7 @@
aidl_interface {
name: "binderRpcTestIface",
+ vendor_available: true,
host_supported: true,
unstable: true,
srcs: [
@@ -158,6 +159,7 @@
cc_library_static {
name: "libbinder_tls_test_utils",
+ vendor_available: true,
host_supported: true,
target: {
darwin: {
@@ -211,6 +213,7 @@
defaults: [
"binderRpcTest_common_defaults",
],
+ vendor_available: true,
gtest: false,
auto_gen_config: false,
srcs: [
@@ -221,10 +224,18 @@
cc_defaults {
name: "binderRpcTest_defaults",
+ vendor_available: true,
target: {
android: {
test_suites: ["vts"],
},
+
+ vendor: {
+ shared_libs: [
+ "libbinder_trusty",
+ "libtrusty",
+ ],
+ },
},
defaults: [
"binderRpcTest_common_defaults",
@@ -267,6 +278,7 @@
name: "binderRpcTest_static_defaults",
shared_libs: [
+ "liblog",
"libutils",
// libcrypto_static is not visible to this module
"libcrypto",
@@ -274,7 +286,6 @@
static_libs: [
"libbase",
"libcutils",
- "liblog",
"libssl",
],
@@ -360,6 +371,30 @@
}
cc_test {
+ name: "binderRpcTest_on_trusty_mock",
+ defaults: [
+ "trusty_mock_defaults",
+ ],
+
+ srcs: [
+ "binderRpcUniversalTests.cpp",
+ "binderRpcTestCommon.cpp",
+ "binderRpcTestTrusty.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder_on_trusty_mock",
+ "libbase",
+ "libutils",
+ "libcutils",
+ ],
+
+ static_libs: [
+ "binderRpcTestIface-cpp",
+ ],
+}
+
+cc_test {
name: "binderRpcTest",
defaults: [
"binderRpcTest_defaults",
@@ -371,6 +406,7 @@
required: [
"libbinder_on_trusty_mock",
"binderRpcTestService_on_trusty_mock",
+ "binderRpcTest_on_trusty_mock",
],
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index f7498c4..955c650 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -1406,9 +1406,11 @@
ASSERT_TRUE(server != nullptr);
int32_t delay = 1000; // ms
data.writeInt32(delay);
+ // b/266537959 - must take before taking lock, since countdown is started in the remote
+ // process there.
+ size_t epochMsBefore = epochMillis();
EXPECT_THAT(server->transact(BINDER_LIB_TEST_PROCESS_TEMPORARY_LOCK, data, &reply), NO_ERROR);
std::vector<std::thread> ts;
- size_t epochMsBefore = epochMillis();
for (size_t i = 0; i < kKernelThreads + 1; i++) {
ts.push_back(std::thread([&] {
Parcel local_reply;
diff --git a/libs/binder/tests/binderRecordedTransactionTest.cpp b/libs/binder/tests/binderRecordedTransactionTest.cpp
index 2f5c8c6..30172cc 100644
--- a/libs/binder/tests/binderRecordedTransactionTest.cpp
+++ b/libs/binder/tests/binderRecordedTransactionTest.cpp
@@ -45,7 +45,7 @@
auto retrievedTransaction = RecordedTransaction::fromFile(fd);
- EXPECT_EQ(retrievedTransaction->getInterfaceName(), android::String8(interfaceName));
+ EXPECT_EQ(retrievedTransaction->getInterfaceName(), android::String8(interfaceName).c_str());
EXPECT_EQ(retrievedTransaction->getCode(), 1);
EXPECT_EQ(retrievedTransaction->getFlags(), 42);
EXPECT_EQ(retrievedTransaction->getTimestamp().tv_sec, ts.tv_sec);
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 36c8d8c..84c93dd 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -28,6 +28,11 @@
#include <sys/prctl.h>
#include <sys/socket.h>
+#ifdef __ANDROID_VENDOR__
+#include <binder/RpcTransportTipcAndroid.h>
+#include <trusty/tipc.h>
+#endif // __ANDROID_VENDOR__
+
#include "binderRpcTestCommon.h"
#include "binderRpcTestFixture.h"
@@ -45,6 +50,10 @@
constexpr bool kEnableSharedLibs = true;
#endif
+#ifdef __ANDROID_VENDOR__
+constexpr char kTrustyIpcDevice[] = "/dev/trusty-ipc-dev0";
+#endif
+
static std::string WaitStatusToString(int wstatus) {
if (WIFEXITED(wstatus)) {
return base::StringPrintf("exit status %d", WEXITSTATUS(wstatus));
@@ -270,6 +279,11 @@
auto ret = std::make_unique<LinuxProcessSession>(
Process([=](android::base::borrowed_fd writeEnd, android::base::borrowed_fd readEnd) {
+ if (socketType == SocketType::TIPC) {
+ // Trusty has a single persistent service
+ return;
+ }
+
auto writeFd = std::to_string(writeEnd.get());
auto readFd = std::to_string(readEnd.get());
execl(servicePath.c_str(), servicePath.c_str(), writeFd.c_str(), readFd.c_str(),
@@ -288,31 +302,47 @@
serverConfig.serverSupportedFileDescriptorTransportModes.push_back(
static_cast<int32_t>(mode));
}
- writeToFd(ret->host.writeEnd(), serverConfig);
+ if (socketType != SocketType::TIPC) {
+ writeToFd(ret->host.writeEnd(), serverConfig);
+ }
std::vector<sp<RpcSession>> sessions;
auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
for (size_t i = 0; i < options.numSessions; i++) {
- sessions.emplace_back(RpcSession::make(newFactory(rpcSecurity, certVerifier)));
+ std::unique_ptr<RpcTransportCtxFactory> factory;
+ if (socketType == SocketType::TIPC) {
+#ifdef __ANDROID_VENDOR__
+ factory = RpcTransportCtxFactoryTipcAndroid::make();
+#else
+ LOG_ALWAYS_FATAL("TIPC socket type only supported on vendor");
+#endif
+ } else {
+ factory = newFactory(rpcSecurity, certVerifier);
+ }
+ sessions.emplace_back(RpcSession::make(std::move(factory)));
}
- auto serverInfo = readFromFd<BinderRpcTestServerInfo>(ret->host.readEnd());
- BinderRpcTestClientInfo clientInfo;
- for (const auto& session : sessions) {
- auto& parcelableCert = clientInfo.certs.emplace_back();
- parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM);
- }
- writeToFd(ret->host.writeEnd(), clientInfo);
+ BinderRpcTestServerInfo serverInfo;
+ if (socketType != SocketType::TIPC) {
+ serverInfo = readFromFd<BinderRpcTestServerInfo>(ret->host.readEnd());
+ BinderRpcTestClientInfo clientInfo;
+ for (const auto& session : sessions) {
+ auto& parcelableCert = clientInfo.certs.emplace_back();
+ parcelableCert.data = session->getCertificate(RpcCertificateFormat::PEM);
+ }
+ writeToFd(ret->host.writeEnd(), clientInfo);
- CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max());
- if (socketType == SocketType::INET) {
- CHECK_NE(0, serverInfo.port);
- }
+ CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max());
+ if (socketType == SocketType::INET) {
+ CHECK_NE(0, serverInfo.port);
+ }
- if (rpcSecurity == RpcSecurity::TLS) {
- const auto& serverCert = serverInfo.cert.data;
- CHECK_EQ(OK,
- certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, serverCert));
+ if (rpcSecurity == RpcSecurity::TLS) {
+ const auto& serverCert = serverInfo.cert.data;
+ CHECK_EQ(OK,
+ certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
+ serverCert));
+ }
}
status_t status;
@@ -343,6 +373,19 @@
case SocketType::INET:
status = session->setupInetClient("127.0.0.1", serverInfo.port);
break;
+ case SocketType::TIPC:
+ status = session->setupPreconnectedClient({}, [=]() {
+#ifdef __ANDROID_VENDOR__
+ auto port = trustyIpcPort(serverVersion);
+ int tipcFd = tipc_connect(kTrustyIpcDevice, port.c_str());
+ return tipcFd >= 0 ? android::base::unique_fd(tipcFd)
+ : android::base::unique_fd();
+#else
+ LOG_ALWAYS_FATAL("Tried to connect to Trusty outside of vendor");
+ return android::base::unique_fd();
+#endif
+ });
+ break;
default:
LOG_ALWAYS_FATAL("Unknown socket type");
}
@@ -684,6 +727,10 @@
}
TEST_P(BinderRpc, DeathRecipientFailsWithoutIncoming) {
+ if (socketType() == SocketType::TIPC) {
+ // This should work, but Trusty takes too long to restart the service
+ GTEST_SKIP() << "Service death test not supported on Trusty";
+ }
class MyDeathRec : public IBinder::DeathRecipient {
public:
void binderDied(const wp<IBinder>& /* who */) override {}
@@ -725,6 +772,11 @@
}
TEST_P(BinderRpc, Die) {
+ if (socketType() == SocketType::TIPC) {
+ // This should work, but Trusty takes too long to restart the service
+ GTEST_SKIP() << "Service death test not supported on Trusty";
+ }
+
for (bool doDeathCleanup : {true, false}) {
auto proc = createRpcTestSocketServerProcess({});
@@ -777,6 +829,10 @@
}
TEST_P(BinderRpc, FileDescriptorTransportRejectNone) {
+ if (socketType() == SocketType::TIPC) {
+ GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+ }
+
auto proc = createRpcTestSocketServerProcess({
.clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE,
.serverSupportedFileDescriptorTransportModes =
@@ -793,6 +849,10 @@
}
TEST_P(BinderRpc, FileDescriptorTransportRejectUnix) {
+ if (socketType() == SocketType::TIPC) {
+ GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+ }
+
auto proc = createRpcTestSocketServerProcess({
.clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
.serverSupportedFileDescriptorTransportModes =
@@ -809,6 +869,10 @@
}
TEST_P(BinderRpc, FileDescriptorTransportOptionalUnix) {
+ if (socketType() == SocketType::TIPC) {
+ GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+ }
+
auto proc = createRpcTestSocketServerProcess({
.clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE,
.serverSupportedFileDescriptorTransportModes =
@@ -822,6 +886,10 @@
}
TEST_P(BinderRpc, ReceiveFile) {
+ if (socketType() == SocketType::TIPC) {
+ GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+ }
+
auto proc = createRpcTestSocketServerProcess({
.clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
.serverSupportedFileDescriptorTransportModes =
@@ -842,6 +910,10 @@
}
TEST_P(BinderRpc, SendFiles) {
+ if (socketType() == SocketType::TIPC) {
+ GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+ }
+
auto proc = createRpcTestSocketServerProcess({
.clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
.serverSupportedFileDescriptorTransportModes =
@@ -914,6 +986,10 @@
}
TEST_P(BinderRpc, AppendInvalidFd) {
+ if (socketType() == SocketType::TIPC) {
+ GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+ }
+
auto proc = createRpcTestSocketServerProcess({
.clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
.serverSupportedFileDescriptorTransportModes =
@@ -940,6 +1016,7 @@
ASSERT_EQ(-1, pRaw.readFileDescriptor());
}
+#ifndef __ANDROID_VENDOR__ // No AIBinder_fromPlatformBinder on vendor
TEST_P(BinderRpc, WorksWithLibbinderNdkPing) {
if constexpr (!kEnableSharedLibs) {
GTEST_SKIP() << "Test disabled because Binder was built as a static library";
@@ -971,6 +1048,7 @@
ASSERT_TRUE(status.isOk()) << status.getDescription();
ASSERT_EQ("aoeuaoeu", out);
}
+#endif // __ANDROID_VENDOR__
ssize_t countFds() {
DIR* dir = opendir("/proc/self/fd/");
@@ -986,6 +1064,9 @@
if (serverSingleThreaded()) {
GTEST_SKIP() << "This test requires multiple threads";
}
+ if (socketType() == SocketType::TIPC) {
+ GTEST_SKIP() << "File descriptor tests not supported on Trusty (yet)";
+ }
ssize_t beforeFds = countFds();
ASSERT_GE(beforeFds, 0);
@@ -1100,6 +1181,21 @@
return ret;
}
+static std::vector<SocketType> testTipcSocketTypes() {
+#ifdef __ANDROID_VENDOR__
+ auto port = trustyIpcPort(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL);
+ int tipcFd = tipc_connect(kTrustyIpcDevice, port.c_str());
+ if (tipcFd >= 0) {
+ close(tipcFd);
+ return {SocketType::TIPC};
+ }
+#endif // __ANDROID_VENDOR__
+
+ // TIPC is not supported on this device, most likely
+ // because /dev/trusty-ipc-dev0 is missing
+ return {};
+}
+
INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
::testing::Combine(::testing::ValuesIn(testSocketTypes()),
::testing::ValuesIn(RpcSecurityValues()),
@@ -1109,6 +1205,14 @@
::testing::Values(false, true)),
BinderRpc::PrintParamInfo);
+INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc,
+ ::testing::Combine(::testing::ValuesIn(testTipcSocketTypes()),
+ ::testing::Values(RpcSecurity::RAW),
+ ::testing::ValuesIn(testVersions()),
+ ::testing::ValuesIn(testVersions()),
+ ::testing::Values(true), ::testing::Values(true)),
+ BinderRpc::PrintParamInfo);
+
class BinderRpcServerRootObject
: public ::testing::TestWithParam<std::tuple<bool, bool, RpcSecurity>> {};
@@ -1360,7 +1464,10 @@
addr, port);
return base::unique_fd{};
};
- }
+ } break;
+ case SocketType::TIPC: {
+ LOG_ALWAYS_FATAL("RpcTransportTest should not be enabled for TIPC");
+ } break;
}
mFd = rpcServer->releaseServer();
if (!mFd.fd.ok()) return AssertionFailure() << "releaseServer returns invalid fd";
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index 262d7e4..a467ee3 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -90,6 +90,7 @@
UNIX_RAW,
VSOCK,
INET,
+ TIPC,
};
static inline std::string PrintToString(SocketType socketType) {
@@ -106,6 +107,8 @@
return "vm_socket";
case SocketType::INET:
return "inet_socket";
+ case SocketType::TIPC:
+ return "trusty_ipc";
default:
LOG_ALWAYS_FATAL("Unknown socket type");
return "";
diff --git a/libs/binder/tests/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h
index 5a78782..c99d68a 100644
--- a/libs/binder/tests/binderRpcTestFixture.h
+++ b/libs/binder/tests/binderRpcTestFixture.h
@@ -106,6 +106,10 @@
// Whether the test params support sending FDs in parcels.
bool supportsFdTransport() const {
+ if (socketType() == SocketType::TIPC) {
+ // Trusty does not support file descriptors yet
+ return false;
+ }
return clientVersion() >= 1 && serverVersion() >= 1 && rpcSecurity() != RpcSecurity::TLS &&
(socketType() == SocketType::PRECONNECTED || socketType() == SocketType::UNIX ||
socketType() == SocketType::UNIX_BOOTSTRAP ||
diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp
new file mode 100644
index 0000000..b3bb5eb
--- /dev/null
+++ b/libs/binder/tests/binderRpcTestTrusty.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "binderRpcTest"
+
+#include <android-base/stringprintf.h>
+#include <binder/RpcTransportTipcTrusty.h>
+#include <trusty-gtest.h>
+#include <trusty_ipc.h>
+
+#include "binderRpcTestFixture.h"
+
+namespace android {
+
+// Destructors need to be defined, even if pure virtual
+ProcessSession::~ProcessSession() {}
+
+class TrustyProcessSession : public ProcessSession {
+public:
+ ~TrustyProcessSession() override {}
+
+ void setCustomExitStatusCheck(std::function<void(int wstatus)> /*f*/) override {
+ LOG_ALWAYS_FATAL("setCustomExitStatusCheck() not supported");
+ }
+
+ void terminate() override { LOG_ALWAYS_FATAL("terminate() not supported"); }
+};
+
+std::string BinderRpc::PrintParamInfo(const testing::TestParamInfo<ParamType>& info) {
+ auto [type, security, clientVersion, serverVersion, singleThreaded, noKernel] = info.param;
+ auto ret = PrintToString(type) + "_clientV" + std::to_string(clientVersion) + "_serverV" +
+ std::to_string(serverVersion);
+ if (singleThreaded) {
+ ret += "_single_threaded";
+ }
+ if (noKernel) {
+ ret += "_no_kernel";
+ }
+ return ret;
+}
+
+// This creates a new process serving an interface on a certain number of
+// threads.
+std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc(
+ const BinderRpcOptions& options) {
+ LOG_ALWAYS_FATAL_IF(options.numIncomingConnections != 0,
+ "Non-zero incoming connections %zu on Trusty",
+ options.numIncomingConnections);
+
+ uint32_t clientVersion = std::get<2>(GetParam());
+ uint32_t serverVersion = std::get<3>(GetParam());
+
+ auto ret = std::make_unique<TrustyProcessSession>();
+
+ status_t status;
+ for (size_t i = 0; i < options.numSessions; i++) {
+ auto factory = android::RpcTransportCtxFactoryTipcTrusty::make();
+ auto session = android::RpcSession::make(std::move(factory));
+
+ EXPECT_TRUE(session->setProtocolVersion(clientVersion));
+ session->setMaxOutgoingThreads(options.numOutgoingConnections);
+ session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
+
+ status = session->setupPreconnectedClient({}, [&]() {
+ auto port = trustyIpcPort(serverVersion);
+ int rc = connect(port.c_str(), IPC_CONNECT_WAIT_FOR_PORT);
+ LOG_ALWAYS_FATAL_IF(rc < 0, "Failed to connect to service: %d", rc);
+ return base::unique_fd(rc);
+ });
+ if (options.allowConnectFailure && status != OK) {
+ ret->sessions.clear();
+ break;
+ }
+ LOG_ALWAYS_FATAL_IF(status != OK, "Failed to connect to service: %s",
+ statusToString(status).c_str());
+ ret->sessions.push_back({session, session->getRootObject()});
+ }
+
+ return ret;
+}
+
+INSTANTIATE_TEST_CASE_P(Trusty, BinderRpc,
+ ::testing::Combine(::testing::Values(SocketType::TIPC),
+ ::testing::Values(RpcSecurity::RAW),
+ ::testing::ValuesIn(testVersions()),
+ ::testing::ValuesIn(testVersions()),
+ ::testing::Values(false), ::testing::Values(true)),
+ BinderRpc::PrintParamInfo);
+
+} // namespace android
+
+PORT_GTEST(BinderRpcTest, "com.android.trusty.binderRpcTest");
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index 9cd8a82..11a22b0 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -113,6 +113,10 @@
}
TEST_P(BinderRpc, AppendSeparateFormats) {
+ if (socketType() == SocketType::TIPC) {
+ GTEST_SKIP() << "Trusty does not support multiple server processes";
+ }
+
auto proc1 = createRpcTestSocketServerProcess({});
auto proc2 = createRpcTestSocketServerProcess({});
@@ -155,7 +159,9 @@
TEST_P(BinderRpc, SendAndGetResultBackBig) {
auto proc = createRpcTestSocketServerProcess({});
- std::string single = std::string(1024, 'a');
+ // Trusty has a limit of 4096 bytes for the entire RPC Binder message
+ size_t singleLen = socketType() == SocketType::TIPC ? 512 : 4096;
+ std::string single = std::string(singleLen, 'a');
std::string doubled;
EXPECT_OK(proc.rootIface->doubleString(single, &doubled));
EXPECT_EQ(single + single, doubled);
@@ -259,6 +265,10 @@
// aren't supported.
TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) {
+ if (socketType() == SocketType::TIPC) {
+ GTEST_SKIP() << "Trusty does not support multiple server processes";
+ }
+
auto proc1 = createRpcTestSocketServerProcess({});
auto proc2 = createRpcTestSocketServerProcess({});
@@ -319,12 +329,16 @@
}
TEST_P(BinderRpc, NestedTransactions) {
+ auto fileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX;
+ if (socketType() == SocketType::TIPC) {
+ // TIPC does not support file descriptors yet
+ fileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::NONE;
+ }
auto proc = createRpcTestSocketServerProcess({
// Enable FD support because it uses more stack space and so represents
// something closer to a worst case scenario.
- .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX,
- .serverSupportedFileDescriptorTransportModes =
- {RpcSession::FileDescriptorTransportMode::UNIX},
+ .clientFileDescriptorTransportMode = fileDescriptorTransportMode,
+ .serverSupportedFileDescriptorTransportModes = {fileDescriptorTransportMode},
});
auto nastyNester = sp<MyBinderRpcTestDefault>::make();
@@ -372,11 +386,11 @@
EXPECT_EQ(b, weak.promote());
}
-#define expectSessions(expected, iface) \
+#define EXPECT_SESSIONS(expected, iface) \
do { \
int session; \
EXPECT_OK((iface)->getNumOpenSessions(&session)); \
- EXPECT_EQ(expected, session); \
+ EXPECT_EQ(static_cast<int>(expected), session); \
} while (false)
TEST_P(BinderRpc, SingleSession) {
@@ -388,9 +402,9 @@
EXPECT_OK(session->getName(&out));
EXPECT_EQ("aoeu", out);
- expectSessions(1, proc.rootIface);
+ EXPECT_SESSIONS(1, proc.rootIface);
session = nullptr;
- expectSessions(0, proc.rootIface);
+ EXPECT_SESSIONS(0, proc.rootIface);
}
TEST_P(BinderRpc, ManySessions) {
@@ -399,24 +413,24 @@
std::vector<sp<IBinderRpcSession>> sessions;
for (size_t i = 0; i < 15; i++) {
- expectSessions(i, proc.rootIface);
+ EXPECT_SESSIONS(i, proc.rootIface);
sp<IBinderRpcSession> session;
EXPECT_OK(proc.rootIface->openSession(std::to_string(i), &session));
sessions.push_back(session);
}
- expectSessions(sessions.size(), proc.rootIface);
+ EXPECT_SESSIONS(sessions.size(), proc.rootIface);
for (size_t i = 0; i < sessions.size(); i++) {
std::string out;
EXPECT_OK(sessions.at(i)->getName(&out));
EXPECT_EQ(std::to_string(i), out);
}
- expectSessions(sessions.size(), proc.rootIface);
+ EXPECT_SESSIONS(sessions.size(), proc.rootIface);
while (!sessions.empty()) {
sessions.pop_back();
- expectSessions(sessions.size(), proc.rootIface);
+ EXPECT_SESSIONS(sessions.size(), proc.rootIface);
}
- expectSessions(0, proc.rootIface);
+ EXPECT_SESSIONS(0, proc.rootIface);
}
TEST_P(BinderRpc, OnewayCallDoesNotWait) {
@@ -469,7 +483,7 @@
cb->mCv.wait_for(_l, 1s, [&] { return !cb->mValues.empty(); });
}
- EXPECT_EQ(cb->mValues.size(), 1)
+ EXPECT_EQ(cb->mValues.size(), 1UL)
<< "callIsOneway: " << callIsOneway
<< " callbackIsOneway: " << callbackIsOneway << " delayed: " << delayed;
if (cb->mValues.empty()) continue;
diff --git a/libs/binder/trusty/binderRpcTest/manifest.json b/libs/binder/trusty/binderRpcTest/manifest.json
new file mode 100644
index 0000000..d8b080f
--- /dev/null
+++ b/libs/binder/trusty/binderRpcTest/manifest.json
@@ -0,0 +1,6 @@
+{
+ "uuid": "9dbe9fb8-60fd-4bdd-af86-03e95d7ad78b",
+ "app_name": "binderRpcTest",
+ "min_heap": 163840,
+ "min_stack": 16384
+}
diff --git a/libs/binder/trusty/binderRpcTest/rules.mk b/libs/binder/trusty/binderRpcTest/rules.mk
new file mode 100644
index 0000000..ae39492
--- /dev/null
+++ b/libs/binder/trusty/binderRpcTest/rules.mk
@@ -0,0 +1,35 @@
+# 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_TESTS_DIR := frameworks/native/libs/binder/tests
+
+MODULE := $(LOCAL_DIR)
+
+MANIFEST := $(LOCAL_DIR)/manifest.json
+
+MODULE_SRCS += \
+ $(LIBBINDER_TESTS_DIR)/binderRpcUniversalTests.cpp \
+ $(LIBBINDER_TESTS_DIR)/binderRpcTestCommon.cpp \
+ $(LIBBINDER_TESTS_DIR)/binderRpcTestTrusty.cpp \
+
+MODULE_LIBRARY_DEPS += \
+ $(LOCAL_DIR)/aidl \
+ frameworks/native/libs/binder/trusty \
+ frameworks/native/libs/binder/trusty/ndk \
+ trusty/user/base/lib/googletest \
+ trusty/user/base/lib/libstdc++-trusty \
+
+include make/trusted_app.mk
diff --git a/libs/binder/trusty/build-config-usertests b/libs/binder/trusty/build-config-usertests
new file mode 100644
index 0000000..d0a1fbc
--- /dev/null
+++ b/libs/binder/trusty/build-config-usertests
@@ -0,0 +1,19 @@
+# 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.
+
+# This file lists userspace tests
+
+[
+ porttest("com.android.trusty.binderRpcTest"),
+]
diff --git a/libs/binder/trusty/include_mock/trusty-gtest.h b/libs/binder/trusty/include_mock/trusty-gtest.h
new file mode 100644
index 0000000..046b403
--- /dev/null
+++ b/libs/binder/trusty/include_mock/trusty-gtest.h
@@ -0,0 +1,21 @@
+/*
+ * 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
+
+#define PORT_GTEST(suite, port) \
+ int main(void) { \
+ return 0; \
+ }
diff --git a/libs/binder/trusty/include_mock/trusty_ipc.h b/libs/binder/trusty/include_mock/trusty_ipc.h
index 43ab84a..db044c2 100644
--- a/libs/binder/trusty/include_mock/trusty_ipc.h
+++ b/libs/binder/trusty/include_mock/trusty_ipc.h
@@ -27,6 +27,8 @@
#define IPC_PORT_ALLOW_TA_CONNECT 0x1
#define IPC_PORT_ALLOW_NS_CONNECT 0x2
+#define IPC_CONNECT_WAIT_FOR_PORT 0x1
+
#define IPC_HANDLE_POLL_HUP 0x1
#define IPC_HANDLE_POLL_MSG 0x2
#define IPC_HANDLE_POLL_SEND_UNBLOCKED 0x4
diff --git a/libs/binder/trusty/usertests-inc.mk b/libs/binder/trusty/usertests-inc.mk
index 2f5a7f4..1300121 100644
--- a/libs/binder/trusty/usertests-inc.mk
+++ b/libs/binder/trusty/usertests-inc.mk
@@ -14,4 +14,6 @@
#
TRUSTY_USER_TESTS += \
+ frameworks/native/libs/binder/trusty/binderRpcTest \
frameworks/native/libs/binder/trusty/binderRpcTest/service \
+
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 797d6ae..60603ba 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -587,9 +587,23 @@
t->setDesiredPresentTime(bufferItem.mTimestamp);
}
- if (!mNextFrameTimelineInfoQueue.empty()) {
- t->setFrameTimelineInfo(mNextFrameTimelineInfoQueue.front());
- mNextFrameTimelineInfoQueue.pop();
+ // Drop stale frame timeline infos
+ while (!mPendingFrameTimelines.empty() &&
+ mPendingFrameTimelines.front().first < bufferItem.mFrameNumber) {
+ ATRACE_FORMAT_INSTANT("dropping stale frameNumber: %" PRIu64 " vsyncId: %" PRId64,
+ mPendingFrameTimelines.front().first,
+ mPendingFrameTimelines.front().second.vsyncId);
+ mPendingFrameTimelines.pop();
+ }
+
+ if (!mPendingFrameTimelines.empty() &&
+ mPendingFrameTimelines.front().first == bufferItem.mFrameNumber) {
+ ATRACE_FORMAT_INSTANT("Transaction::setFrameTimelineInfo frameNumber: %" PRIu64
+ " vsyncId: %" PRId64,
+ bufferItem.mFrameNumber,
+ mPendingFrameTimelines.front().second.vsyncId);
+ t->setFrameTimelineInfo(mPendingFrameTimelines.front().second);
+ mPendingFrameTimelines.pop();
}
{
@@ -653,6 +667,7 @@
{
std::unique_lock _lock{mMutex};
BBQ_TRACE();
+
const bool syncTransactionSet = mTransactionReadyCallback != nullptr;
BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
@@ -847,12 +862,13 @@
return mBbq->setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
}
- status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
+ status_t setFrameTimelineInfo(uint64_t frameNumber,
+ const FrameTimelineInfo& frameTimelineInfo) override {
std::unique_lock _lock{mMutex};
if (mDestroyed) {
return DEAD_OBJECT;
}
- return mBbq->setFrameTimelineInfo(frameTimelineInfo);
+ return mBbq->setFrameTimelineInfo(frameNumber, frameTimelineInfo);
}
void destroy() override {
@@ -874,9 +890,12 @@
return t.setFrameRate(mSurfaceControl, frameRate, compatibility, shouldBeSeamless).apply();
}
-status_t BLASTBufferQueue::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) {
+status_t BLASTBufferQueue::setFrameTimelineInfo(uint64_t frameNumber,
+ const FrameTimelineInfo& frameTimelineInfo) {
+ ATRACE_FORMAT("%s(%s) frameNumber: %" PRIu64 " vsyncId: %" PRId64, __func__, mName.c_str(),
+ frameNumber, frameTimelineInfo.vsyncId);
std::unique_lock _lock{mMutex};
- mNextFrameTimelineInfoQueue.push(frameTimelineInfo);
+ mPendingFrameTimelines.push({frameNumber, frameTimelineInfo});
return OK;
}
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index a77ca04..cefb9a7 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -63,7 +63,8 @@
Vector<ComposerState>& state, const Vector<DisplayState>& displays,
uint32_t flags, const sp<IBinder>& applyToken,
const InputWindowCommands& commands, int64_t desiredPresentTime,
- bool isAutoTimestamp, const client_cache_t& uncacheBuffer,
+ bool isAutoTimestamp,
+ const std::vector<client_cache_t>& uncacheBuffers,
bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
uint64_t transactionId) override {
@@ -87,8 +88,11 @@
SAFE_PARCEL(commands.write, data);
SAFE_PARCEL(data.writeInt64, desiredPresentTime);
SAFE_PARCEL(data.writeBool, isAutoTimestamp);
- SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
- SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
+ SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(uncacheBuffers.size()));
+ for (const client_cache_t& uncacheBuffer : uncacheBuffers) {
+ SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
+ SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
+ }
SAFE_PARCEL(data.writeBool, hasListenerCallbacks);
SAFE_PARCEL(data.writeVectorSize, listenerCallbacks);
@@ -158,11 +162,14 @@
SAFE_PARCEL(data.readInt64, &desiredPresentTime);
SAFE_PARCEL(data.readBool, &isAutoTimestamp);
- client_cache_t uncachedBuffer;
+ SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
+ std::vector<client_cache_t> uncacheBuffers(count);
sp<IBinder> tmpBinder;
- SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder);
- uncachedBuffer.token = tmpBinder;
- SAFE_PARCEL(data.readUint64, &uncachedBuffer.id);
+ for (size_t i = 0; i < count; i++) {
+ SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder);
+ uncacheBuffers[i].token = tmpBinder;
+ SAFE_PARCEL(data.readUint64, &uncacheBuffers[i].id);
+ }
bool hasListenerCallbacks = false;
SAFE_PARCEL(data.readBool, &hasListenerCallbacks);
@@ -182,7 +189,7 @@
return setTransactionState(frameTimelineInfo, state, displays, stateFlags, applyToken,
inputWindowCommands, desiredPresentTime, isAutoTimestamp,
- uncachedBuffer, hasListenerCallbacks, listenerCallbacks,
+ uncacheBuffers, hasListenerCallbacks, listenerCallbacks,
transactionId);
}
default: {
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 2b25b61..985c549 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -33,7 +33,8 @@
ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
ON_RELEASE_BUFFER,
ON_TRANSACTION_QUEUE_STALLED,
- LAST = ON_TRANSACTION_QUEUE_STALLED,
+ ON_TRUSTED_PRESENTATION_CHANGED,
+ LAST = ON_TRUSTED_PRESENTATION_CHANGED,
};
} // Anonymous namespace
@@ -302,6 +303,11 @@
onTransactionQueueStalled)>(Tag::ON_TRANSACTION_QUEUE_STALLED,
reason);
}
+
+ void onTrustedPresentationChanged(int id, bool inTrustedPresentationState) override {
+ callRemoteAsync<decltype(&ITransactionCompletedListener::onTrustedPresentationChanged)>(
+ Tag::ON_TRUSTED_PRESENTATION_CHANGED, id, inTrustedPresentationState);
+ }
};
// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
@@ -325,6 +331,9 @@
case Tag::ON_TRANSACTION_QUEUE_STALLED:
return callLocalAsync(data, reply,
&ITransactionCompletedListener::onTransactionQueueStalled);
+ case Tag::ON_TRUSTED_PRESENTATION_CHANGED:
+ return callLocalAsync(data, reply,
+ &ITransactionCompletedListener::onTrustedPresentationChanged);
}
}
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 59b62fe..8372363 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -185,6 +185,10 @@
if (hasBufferData) {
SAFE_PARCEL(output.writeParcelable, *bufferData);
}
+ SAFE_PARCEL(output.writeParcelable, trustedPresentationThresholds);
+ SAFE_PARCEL(output.writeParcelable, trustedPresentationListener);
+ SAFE_PARCEL(output.writeFloat, currentSdrHdrRatio);
+ SAFE_PARCEL(output.writeFloat, desiredSdrHdrRatio);
return NO_ERROR;
}
@@ -315,6 +319,15 @@
} else {
bufferData = nullptr;
}
+
+ SAFE_PARCEL(input.readParcelable, &trustedPresentationThresholds);
+ SAFE_PARCEL(input.readParcelable, &trustedPresentationListener);
+
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ currentSdrHdrRatio = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ desiredSdrHdrRatio = tmpFloat;
+
return NO_ERROR;
}
@@ -553,10 +566,20 @@
what |= eBufferChanged;
bufferData = other.bufferData;
}
+ if (other.what & eTrustedPresentationInfoChanged) {
+ what |= eTrustedPresentationInfoChanged;
+ trustedPresentationListener = other.trustedPresentationListener;
+ trustedPresentationThresholds = other.trustedPresentationThresholds;
+ }
if (other.what & eDataspaceChanged) {
what |= eDataspaceChanged;
dataspace = other.dataspace;
}
+ if (other.what & eExtendedRangeBrightnessChanged) {
+ what |= eExtendedRangeBrightnessChanged;
+ desiredSdrHdrRatio = other.desiredSdrHdrRatio;
+ currentSdrHdrRatio = other.currentSdrHdrRatio;
+ }
if (other.what & eHdrMetadataChanged) {
what |= eHdrMetadataChanged;
hdrMetadata = other.hdrMetadata;
@@ -703,6 +726,8 @@
CHECK_DIFF(diff, eCropChanged, other, crop);
if (other.what & eBufferChanged) diff |= eBufferChanged;
CHECK_DIFF(diff, eDataspaceChanged, other, dataspace);
+ CHECK_DIFF2(diff, eExtendedRangeBrightnessChanged, other, currentSdrHdrRatio,
+ desiredSdrHdrRatio);
CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata);
if (other.what & eSurfaceDamageRegionChanged &&
(!surfaceDamageRegion.hasSameRects(other.surfaceDamageRegion))) {
@@ -998,4 +1023,20 @@
return NO_ERROR;
}
+status_t TrustedPresentationListener::writeToParcel(Parcel* parcel) const {
+ SAFE_PARCEL(parcel->writeStrongBinder, callbackInterface);
+ SAFE_PARCEL(parcel->writeInt32, callbackId);
+ return NO_ERROR;
+}
+
+status_t TrustedPresentationListener::readFromParcel(const Parcel* parcel) {
+ sp<IBinder> tmpBinder = nullptr;
+ SAFE_PARCEL(parcel->readNullableStrongBinder, &tmpBinder);
+ if (tmpBinder) {
+ callbackInterface = checked_interface_cast<ITransactionCompletedListener>(tmpBinder);
+ }
+ SAFE_PARCEL(parcel->readInt32, &callbackId);
+ return NO_ERROR;
+}
+
}; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index edb18a8..b18bf5b 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1863,6 +1863,7 @@
int Surface::dispatchSetFrameTimelineInfo(va_list args) {
ATRACE_CALL();
+ auto frameNumber = static_cast<uint64_t>(va_arg(args, uint64_t));
auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t));
auto startTimeNanos = static_cast<int64_t>(va_arg(args, int64_t));
@@ -1872,7 +1873,7 @@
ftlInfo.vsyncId = frameTimelineVsyncId;
ftlInfo.inputEventId = inputEventId;
ftlInfo.startTimeNanos = startTimeNanos;
- return setFrameTimelineInfo(ftlInfo);
+ return setFrameTimelineInfo(frameNumber, ftlInfo);
}
bool Surface::transformToDisplayInverse() const {
@@ -2641,7 +2642,8 @@
return NO_ERROR;
}
-status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& /*frameTimelineInfo*/) {
+status_t Surface::setFrameTimelineInfo(uint64_t /*frameNumber*/,
+ const FrameTimelineInfo& /*frameTimelineInfo*/) {
// ISurfaceComposer no longer supports setFrameTimelineInfo
return BAD_VALUE;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 92125ea..cf9828b 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -24,6 +24,7 @@
#include <android/gui/DisplayState.h>
#include <android/gui/ISurfaceComposerClient.h>
#include <android/gui/IWindowInfosListener.h>
+#include <android/gui/TrustedPresentationThresholds.h>
#include <android/os/IInputConstants.h>
#include <gui/TraceUtils.h>
#include <utils/Errors.h>
@@ -63,6 +64,7 @@
using aidl::android::hardware::graphics::common::DisplayDecorationSupport;
using gui::FocusRequest;
using gui::IRegionSamplingListener;
+using gui::TrustedPresentationThresholds;
using gui::WindowInfo;
using gui::WindowInfoHandle;
using gui::WindowInfosListener;
@@ -518,6 +520,45 @@
}
}
+SurfaceComposerClient::PresentationCallbackRAII::PresentationCallbackRAII(
+ TransactionCompletedListener* tcl, int id) {
+ mTcl = tcl;
+ mId = id;
+}
+
+SurfaceComposerClient::PresentationCallbackRAII::~PresentationCallbackRAII() {
+ mTcl->clearTrustedPresentationCallback(mId);
+}
+
+sp<SurfaceComposerClient::PresentationCallbackRAII>
+TransactionCompletedListener::addTrustedPresentationCallback(TrustedPresentationCallback tpc,
+ int id, void* context) {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ mTrustedPresentationCallbacks[id] =
+ std::tuple<TrustedPresentationCallback, void*>(tpc, context);
+ return new SurfaceComposerClient::PresentationCallbackRAII(this, id);
+}
+
+void TransactionCompletedListener::clearTrustedPresentationCallback(int id) {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ mTrustedPresentationCallbacks.erase(id);
+}
+
+void TransactionCompletedListener::onTrustedPresentationChanged(int id,
+ bool presentedWithinThresholds) {
+ TrustedPresentationCallback tpc;
+ void* context;
+ {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ auto it = mTrustedPresentationCallbacks.find(id);
+ if (it == mTrustedPresentationCallbacks.end()) {
+ return;
+ }
+ std::tie(tpc, context) = it->second;
+ }
+ tpc(context, presentedWithinThresholds);
+}
+
// ---------------------------------------------------------------------------
void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId);
@@ -565,11 +606,13 @@
return NO_ERROR;
}
- uint64_t cache(const sp<GraphicBuffer>& buffer) {
+ uint64_t cache(const sp<GraphicBuffer>& buffer,
+ std::optional<client_cache_t>& outUncacheBuffer) {
std::lock_guard<std::mutex> lock(mMutex);
if (mBuffers.size() >= BUFFER_CACHE_MAX_SIZE) {
- evictLeastRecentlyUsedBuffer();
+ outUncacheBuffer = findLeastRecentlyUsedBuffer();
+ mBuffers.erase(outUncacheBuffer->id);
}
buffer->addDeathCallback(removeDeadBufferCallback, nullptr);
@@ -580,16 +623,13 @@
void uncache(uint64_t cacheId) {
std::lock_guard<std::mutex> lock(mMutex);
- uncacheLocked(cacheId);
- }
-
- void uncacheLocked(uint64_t cacheId) REQUIRES(mMutex) {
- mBuffers.erase(cacheId);
- SurfaceComposerClient::doUncacheBufferTransaction(cacheId);
+ if (mBuffers.erase(cacheId)) {
+ SurfaceComposerClient::doUncacheBufferTransaction(cacheId);
+ }
}
private:
- void evictLeastRecentlyUsedBuffer() REQUIRES(mMutex) {
+ client_cache_t findLeastRecentlyUsedBuffer() REQUIRES(mMutex) {
auto itr = mBuffers.begin();
uint64_t minCounter = itr->second;
auto minBuffer = itr;
@@ -603,7 +643,8 @@
}
itr++;
}
- uncacheLocked(minBuffer->first);
+
+ return {.token = getToken(), .id = minBuffer->first};
}
uint64_t getCounter() REQUIRES(mMutex) {
@@ -741,6 +782,18 @@
InputWindowCommands inputWindowCommands;
inputWindowCommands.read(*parcel);
+ count = static_cast<size_t>(parcel->readUint32());
+ if (count > parcel->dataSize()) {
+ return BAD_VALUE;
+ }
+ std::vector<client_cache_t> uncacheBuffers(count);
+ for (size_t i = 0; i < count; i++) {
+ sp<IBinder> tmpBinder;
+ SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
+ uncacheBuffers[i].token = tmpBinder;
+ SAFE_PARCEL(parcel->readUint64, &uncacheBuffers[i].id);
+ }
+
// Parsing was successful. Update the object.
mId = transactionId;
mTransactionNestCount = transactionNestCount;
@@ -755,6 +808,7 @@
mComposerStates = composerStates;
mInputWindowCommands = inputWindowCommands;
mApplyToken = applyToken;
+ mUncacheBuffers = std::move(uncacheBuffers);
return NO_ERROR;
}
@@ -806,6 +860,13 @@
}
mInputWindowCommands.write(*parcel);
+
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mUncacheBuffers.size()));
+ for (const client_cache_t& uncacheBuffer : mUncacheBuffers) {
+ SAFE_PARCEL(parcel->writeStrongBinder, uncacheBuffer.token.promote());
+ SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id);
+ }
+
return NO_ERROR;
}
@@ -873,6 +934,10 @@
}
}
+ for (const auto& cacheId : other.mUncacheBuffers) {
+ mUncacheBuffers.push_back(cacheId);
+ }
+
mInputWindowCommands.merge(other.mInputWindowCommands);
mMayContainBuffer |= other.mMayContainBuffer;
@@ -891,6 +956,7 @@
mDisplayStates.clear();
mListenerCallbacks.clear();
mInputWindowCommands.clear();
+ mUncacheBuffers.clear();
mMayContainBuffer = false;
mTransactionNestCount = 0;
mAnimation = false;
@@ -913,10 +979,10 @@
uncacheBuffer.token = BufferCache::getInstance().getToken();
uncacheBuffer.id = cacheId;
Vector<ComposerState> composerStates;
- status_t status =
- sf->setTransactionState(FrameTimelineInfo{}, composerStates, {},
- ISurfaceComposer::eOneWay, Transaction::getDefaultApplyToken(),
- {}, systemTime(), true, uncacheBuffer, false, {}, generateId());
+ status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, {},
+ ISurfaceComposer::eOneWay,
+ Transaction::getDefaultApplyToken(), {}, systemTime(),
+ true, {uncacheBuffer}, false, {}, generateId());
if (status != NO_ERROR) {
ALOGE_AND_TRACE("SurfaceComposerClient::doUncacheBufferTransaction - %s",
strerror(-status));
@@ -954,7 +1020,11 @@
s->bufferData->buffer = nullptr;
} else {
// Cache-miss. Include the buffer and send the new cacheId.
- cacheId = BufferCache::getInstance().cache(s->bufferData->buffer);
+ std::optional<client_cache_t> uncacheBuffer;
+ cacheId = BufferCache::getInstance().cache(s->bufferData->buffer, uncacheBuffer);
+ if (uncacheBuffer) {
+ mUncacheBuffers.push_back(*uncacheBuffer);
+ }
}
s->bufferData->flags |= BufferData::BufferDataChange::cachedBufferChanged;
s->bufferData->cachedBuffer.token = BufferCache::getInstance().getToken();
@@ -1087,8 +1157,7 @@
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,
mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
- {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
- hasListenerCallbacks, listenerCallbacks, mId);
+ mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId);
mId = generateId();
// Clear the current states and flags
@@ -1604,6 +1673,21 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setExtendedRangeBrightness(
+ const sp<SurfaceControl>& sc, float currentBufferRatio, float desiredRatio) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eExtendedRangeBrightnessChanged;
+ s->currentSdrHdrRatio = currentBufferRatio;
+ s->desiredSdrHdrRatio = desiredRatio;
+
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setHdrMetadata(
const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata) {
layer_state_t* s = getLayerState(sc);
@@ -2098,6 +2182,45 @@
t.startTimeNanos = 0;
}
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::setTrustedPresentationCallback(
+ const sp<SurfaceControl>& sc, TrustedPresentationCallback cb,
+ const TrustedPresentationThresholds& thresholds, void* context,
+ sp<SurfaceComposerClient::PresentationCallbackRAII>& outCallbackRef) {
+ auto listener = TransactionCompletedListener::getInstance();
+ outCallbackRef = listener->addTrustedPresentationCallback(cb, sc->getLayerId(), context);
+
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eTrustedPresentationInfoChanged;
+ s->trustedPresentationThresholds = thresholds;
+ s->trustedPresentationListener.callbackInterface = TransactionCompletedListener::getIInstance();
+ s->trustedPresentationListener.callbackId = sc->getLayerId();
+
+ return *this;
+}
+
+SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::clearTrustedPresentationCallback(const sp<SurfaceControl>& sc) {
+ auto listener = TransactionCompletedListener::getInstance();
+ listener->clearTrustedPresentationCallback(sc->getLayerId());
+
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eTrustedPresentationInfoChanged;
+ s->trustedPresentationThresholds = TrustedPresentationThresholds();
+ s->trustedPresentationListener.callbackInterface = nullptr;
+ s->trustedPresentationListener.callbackId = -1;
+
+ return *this;
+}
+
// ---------------------------------------------------------------------------
SurfaceComposerClient::SurfaceComposerClient() : mStatus(NO_INIT) {}
diff --git a/libs/gui/aidl/android/gui/IHdrConversionConstants.aidl b/libs/gui/aidl/android/gui/IHdrConversionConstants.aidl
new file mode 100644
index 0000000..7697f29
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IHdrConversionConstants.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.gui;
+
+/** @hide */
+interface IHdrConversionConstants
+{
+ /** HDR Conversion Mode when there is no conversion being done */
+ const int HdrConversionModePassthrough = 1;
+
+ /** HDR Conversion Mode when HDR conversion is decided by the system or implementation */
+ const int HdrConversionModeAuto = 2;
+
+ /** HDR Conversion Mode when the output HDR types is selected by the user or framework */
+ const int HdrConversionModeForce = 3;
+}
\ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/OverlayProperties.aidl b/libs/gui/aidl/android/gui/OverlayProperties.aidl
index 1af5746..5fb1a83 100644
--- a/libs/gui/aidl/android/gui/OverlayProperties.aidl
+++ b/libs/gui/aidl/android/gui/OverlayProperties.aidl
@@ -20,7 +20,9 @@
parcelable OverlayProperties {
parcelable SupportedBufferCombinations {
int[] pixelFormats;
- int[] dataspaces;
+ int[] standards;
+ int[] transfers;
+ int[] ranges;
}
SupportedBufferCombinations[] combinations;
diff --git a/libs/gui/aidl/android/gui/TrustedPresentationThresholds.aidl b/libs/gui/aidl/android/gui/TrustedPresentationThresholds.aidl
new file mode 100644
index 0000000..1eea5b4
--- /dev/null
+++ b/libs/gui/aidl/android/gui/TrustedPresentationThresholds.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright 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.
+ */
+
+package android.gui;
+
+parcelable TrustedPresentationThresholds {
+ float minAlpha = -1.0f;
+ float minFractionRendered = -1.0f;
+
+ int stabilityRequirementMs = 0;
+}
diff --git a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
index 761f08f..17f4c63 100644
--- a/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
+++ b/libs/gui/fuzzer/libgui_bufferQueue_fuzzer.cpp
@@ -130,7 +130,7 @@
queue->setFrameRate(mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeIntegral<int8_t>(),
mFdp.ConsumeBool() /*shouldBeSeamless*/);
FrameTimelineInfo info;
- queue->setFrameTimelineInfo(info);
+ queue->setFrameTimelineInfo(mFdp.ConsumeIntegral<uint64_t>(), info);
ManageResourceHandle handle(&mFdp);
queue->setSidebandStream(handle.getStream());
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 001d8e5..c93ab86 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -106,7 +106,7 @@
void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format);
status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
- status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
+ status_t setFrameTimelineInfo(uint64_t frameNumber, const FrameTimelineInfo& info);
void setSidebandStream(const sp<NativeHandle>& stream);
@@ -231,7 +231,7 @@
std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
mPendingTransactions GUARDED_BY(mMutex);
- std::queue<FrameTimelineInfo> mNextFrameTimelineInfoQueue GUARDED_BY(mMutex);
+ std::queue<std::pair<uint64_t, FrameTimelineInfo>> mPendingFrameTimelines GUARDED_BY(mMutex);
// Tracks the last acquired frame number
uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0;
diff --git a/libs/gui/include/gui/DisplayCaptureArgs.h b/libs/gui/include/gui/DisplayCaptureArgs.h
index ec884cf..c826c17 100644
--- a/libs/gui/include/gui/DisplayCaptureArgs.h
+++ b/libs/gui/include/gui/DisplayCaptureArgs.h
@@ -24,6 +24,7 @@
#include <binder/Parcelable.h>
#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
namespace android::gui {
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 045cc2a..ae56f9f 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -113,8 +113,9 @@
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
- bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) = 0;
+ bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffer,
+ bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+ uint64_t transactionId) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 453e8f3..d593f56 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -196,6 +196,8 @@
uint32_t currentMaxAcquiredBufferCount) = 0;
virtual void onTransactionQueueStalled(const String8& name) = 0;
+
+ virtual void onTrustedPresentationChanged(int id, bool inTrustedPresentationState) = 0;
};
class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index ecde47f..b8bee72 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -22,6 +22,7 @@
#include <sys/types.h>
#include <android/gui/IWindowInfosReportedListener.h>
+#include <android/gui/TrustedPresentationThresholds.h>
#include <android/native_window.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/ITransactionCompletedListener.h>
@@ -56,6 +57,8 @@
using gui::ISurfaceComposerClient;
using gui::LayerMetadata;
+using gui::TrustedPresentationThresholds;
+
struct client_cache_t {
wp<IBinder> token = nullptr;
uint64_t id;
@@ -65,6 +68,19 @@
bool isValid() const { return token != nullptr; }
};
+class TrustedPresentationListener : public Parcelable {
+public:
+ sp<ITransactionCompletedListener> callbackInterface;
+ int callbackId = -1;
+
+ void invoke(bool presentedWithinThresholds) {
+ callbackInterface->onTrustedPresentationChanged(callbackId, presentedWithinThresholds);
+ }
+
+ status_t writeToParcel(Parcel* parcel) const;
+ status_t readFromParcel(const Parcel* parcel);
+};
+
class BufferData : public Parcelable {
public:
virtual ~BufferData() = default;
@@ -148,7 +164,7 @@
enum {
ePositionChanged = 0x00000001,
eLayerChanged = 0x00000002,
- /* unused = 0x00000004, */
+ eTrustedPresentationInfoChanged = 0x00000004,
eAlphaChanged = 0x00000008,
eMatrixChanged = 0x00000010,
eTransparentRegionChanged = 0x00000020,
@@ -193,7 +209,8 @@
eAutoRefreshChanged = 0x1000'00000000,
eStretchChanged = 0x2000'00000000,
eTrustedOverlayChanged = 0x4000'00000000,
- eDropInputModeChanged = 0x8000'00000000
+ eDropInputModeChanged = 0x8000'00000000,
+ eExtendedRangeBrightnessChanged = 0x10000'00000000
};
layer_state_t();
@@ -224,7 +241,8 @@
layer_state_t::eBufferTransformChanged | layer_state_t::eDataspaceChanged |
layer_state_t::eSidebandStreamChanged | layer_state_t::eSurfaceDamageRegionChanged |
layer_state_t::eTransformToDisplayInverseChanged |
- layer_state_t::eTransparentRegionChanged;
+ layer_state_t::eTransparentRegionChanged |
+ layer_state_t::eExtendedRangeBrightnessChanged;
// Content updates.
static constexpr uint64_t CONTENT_CHANGES = layer_state_t::BUFFER_CHANGES |
@@ -232,9 +250,9 @@
layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBackgroundColorChanged |
layer_state_t::eBlurRegionsChanged | layer_state_t::eColorChanged |
layer_state_t::eColorSpaceAgnosticChanged | layer_state_t::eColorTransformChanged |
- layer_state_t::eCornerRadiusChanged | layer_state_t::eHdrMetadataChanged |
- layer_state_t::eRenderBorderChanged | layer_state_t::eShadowRadiusChanged |
- layer_state_t::eStretchChanged;
+ layer_state_t::eCornerRadiusChanged | layer_state_t::eDimmingEnabledChanged |
+ layer_state_t::eHdrMetadataChanged | layer_state_t::eRenderBorderChanged |
+ layer_state_t::eShadowRadiusChanged | layer_state_t::eStretchChanged;
// Changes which invalidates the layer's visible region in CE.
static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES |
@@ -245,7 +263,17 @@
layer_state_t::HIERARCHY_CHANGES | layer_state_t::eAlphaChanged |
layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged |
layer_state_t::eFlagsChanged | layer_state_t::eLayerStackChanged |
- layer_state_t::eTrustedOverlayChanged;
+ layer_state_t::eTrustedOverlayChanged | layer_state_t::eFrameRateChanged |
+ layer_state_t::eFixedTransformHintChanged;
+
+ // Changes affecting data sent to input.
+ static constexpr uint64_t INPUT_CHANGES = layer_state_t::GEOMETRY_CHANGES |
+ layer_state_t::HIERARCHY_CHANGES | layer_state_t::eInputInfoChanged |
+ layer_state_t::eDropInputModeChanged | layer_state_t::eTrustedOverlayChanged;
+
+ // Changes that affect the visible region on a display.
+ static constexpr uint64_t VISIBLE_REGION_CHANGES =
+ layer_state_t::GEOMETRY_CHANGES | layer_state_t::HIERARCHY_CHANGES;
bool hasValidBuffer() const;
void sanitize(int32_t permissions);
@@ -359,6 +387,11 @@
gui::DropInputMode dropInputMode;
bool dimmingEnabled;
+ float currentSdrHdrRatio = 1.f;
+ float desiredSdrHdrRatio = 1.f;
+
+ TrustedPresentationThresholds trustedPresentationThresholds;
+ TrustedPresentationListener trustedPresentationListener;
};
class ComposerState {
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index b9ccdc9..39a59e4 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -213,7 +213,7 @@
virtual status_t setFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy);
- virtual status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
+ virtual status_t setFrameTimelineInfo(uint64_t frameNumber, const FrameTimelineInfo& info);
protected:
virtual ~Surface();
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2458a40..c5f59c8 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -58,6 +58,7 @@
class IGraphicBufferProducer;
class ITunnelModeEnabledListener;
class Region;
+class TransactionCompletedListener;
using gui::DisplayCaptureArgs;
using gui::IRegionSamplingListener;
@@ -106,6 +107,8 @@
const sp<Fence>& /*presentFence*/,
const SurfaceStats& /*stats*/)>;
+using TrustedPresentationCallback = std::function<void(void*, bool)>;
+
// ---------------------------------------------------------------------------
class ReleaseCallbackThread {
@@ -390,6 +393,13 @@
std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls;
};
+ struct PresentationCallbackRAII : public RefBase {
+ sp<TransactionCompletedListener> mTcl;
+ int mId;
+ PresentationCallbackRAII(TransactionCompletedListener* tcl, int id);
+ virtual ~PresentationCallbackRAII();
+ };
+
class Transaction : public Parcelable {
private:
static sp<IBinder> sApplyToken;
@@ -402,6 +412,7 @@
SortedVector<DisplayState> mDisplayStates;
std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
mListenerCallbacks;
+ std::vector<client_cache_t> mUncacheBuffers;
uint64_t mId;
@@ -548,6 +559,8 @@
Transaction& setBufferHasBarrier(const sp<SurfaceControl>& sc,
uint64_t barrierFrameNumber);
Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
+ Transaction& setExtendedRangeBrightness(const sp<SurfaceControl>& sc,
+ float currentBufferRatio, float desiredRatio);
Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc,
const Region& surfaceDamageRegion);
@@ -569,6 +582,59 @@
Transaction& addTransactionCommittedCallback(
TransactionCompletedCallbackTakesContext callback, void* callbackContext);
+ /**
+ * Set a callback to receive feedback about the presentation of a layer.
+ * When the layer is presented according to the passed in Thresholds,
+ * it is said to "enter the state", and receives the callback with true.
+ * When the conditions fall out of thresholds, it is then said to leave the
+ * state.
+ *
+ * There are a few simple thresholds:
+ * minAlpha: Lower bound on computed alpha
+ * minFractionRendered: Lower bounds on fraction of pixels that
+ * were rendered.
+ * stabilityThresholdMs: A time that alpha and fraction rendered
+ * must remain within bounds before we can "enter the state"
+ *
+ * The fraction of pixels rendered is a computation based on scale, crop
+ * and occlusion. The calculation may be somewhat counterintuitive, so we
+ * can work through an example. Imagine we have a layer with a 100x100 buffer
+ * which is occluded by (10x100) pixels on the left, and cropped by (100x10) pixels
+ * on the top. Furthermore imagine this layer is scaled by 0.9 in both dimensions.
+ * (c=crop,o=occluded,b=both,x=none
+ * b c c c
+ * o x x x
+ * o x x x
+ * o x x x
+ *
+ * We first start by computing fr=xscale*yscale=0.9*0.9=0.81, indicating
+ * that "81%" of the pixels were rendered. This corresponds to what was 100
+ * pixels being displayed in 81 pixels. This is somewhat of an abuse of
+ * language, as the information of merged pixels isn't totally lost, but
+ * we err on the conservative side.
+ *
+ * We then repeat a similar process for the crop and covered regions and
+ * accumulate the results: fr = fr * (fractionNotCropped) * (fractionNotCovered)
+ * So for this example we would get 0.9*0.9*0.9*0.9=0.65...
+ *
+ * Notice that this is not completely accurate, as we have double counted
+ * the region marked as b. However we only wanted a "lower bound" and so it
+ * is ok to err in this direction. Selection of the threshold will ultimately
+ * be somewhat arbitrary, and so there are some somewhat arbitrary decisions in
+ * this API as well.
+ *
+ * The caller must keep "PresentationCallbackRAII" alive, or the callback
+ * in SurfaceComposerClient will be unregistered.
+ */
+ Transaction& setTrustedPresentationCallback(const sp<SurfaceControl>& sc,
+ TrustedPresentationCallback callback,
+ const TrustedPresentationThresholds& thresholds,
+ void* context,
+ sp<PresentationCallbackRAII>& outCallbackOwner);
+
+ // Clear local memory in SCC
+ Transaction& clearTrustedPresentationCallback(const sp<SurfaceControl>& sc);
+
// ONLY FOR BLAST ADAPTER
Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
@@ -795,6 +861,9 @@
std::multimap<int32_t, SurfaceStatsCallbackEntry> mSurfaceStatsListeners;
std::unordered_map<void*, std::function<void(const std::string&)>> mQueueStallListeners;
+ std::unordered_map<int, std::tuple<TrustedPresentationCallback, void*>>
+ mTrustedPresentationCallbacks;
+
public:
static sp<TransactionCompletedListener> getInstance();
static sp<ITransactionCompletedListener> getIInstance();
@@ -814,6 +883,10 @@
void addQueueStallListener(std::function<void(const std::string&)> stallListener, void* id);
void removeQueueStallListener(void *id);
+ sp<SurfaceComposerClient::PresentationCallbackRAII> addTrustedPresentationCallback(
+ TrustedPresentationCallback tpc, int id, void* context);
+ void clearTrustedPresentationCallback(int id);
+
/*
* Adds a jank listener to be informed about SurfaceFlinger's jank classification for a specific
* surface. Jank classifications arrive as part of the transaction callbacks about previous
@@ -844,6 +917,8 @@
void onTransactionQueueStalled(const String8& reason) override;
+ void onTrustedPresentationChanged(int id, bool presentedWithinThresholds) override;
+
private:
ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&);
static sp<TransactionCompletedListener> sInstance;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 3014804..32d60cd 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -701,7 +701,7 @@
const sp<IBinder>& /*applyToken*/,
const InputWindowCommands& /*inputWindowCommands*/,
int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
- const client_cache_t& /*cachedBuffer*/,
+ const std::vector<client_cache_t>& /*cachedBuffer*/,
bool /*hasListenerCallbacks*/,
const std::vector<ListenerCallbacks>& /*listenerCallbacks*/,
uint64_t /*transactionId*/) override {
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index c796439..133b260 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -299,7 +299,7 @@
return InputEventLookup::getLabelByKeyCode(keyCode);
}
-int32_t KeyEvent::getKeyCodeFromLabel(const char* label) {
+std::optional<int> KeyEvent::getKeyCodeFromLabel(const char* label) {
return InputEventLookup::getKeyCodeByLabel(label);
}
@@ -343,6 +343,28 @@
return "UNKNOWN";
}
+std::ostream& operator<<(std::ostream& out, const KeyEvent& event) {
+ out << "KeyEvent { action=" << KeyEvent::actionToString(event.getAction());
+
+ out << ", keycode=" << event.getKeyCode() << "(" << KeyEvent::getLabel(event.getKeyCode())
+ << ")";
+
+ if (event.getMetaState() != 0) {
+ out << ", metaState=" << event.getMetaState();
+ }
+
+ out << ", eventTime=" << event.getEventTime();
+ out << ", downTime=" << event.getDownTime();
+ out << ", flags=" << std::hex << event.getFlags() << std::dec;
+ out << ", repeatCount=" << event.getRepeatCount();
+ out << ", deviceId=" << event.getDeviceId();
+ out << ", source=" << inputEventSourceToString(event.getSource());
+ out << ", displayId=" << event.getDisplayId();
+ out << ", eventId=" << event.getId();
+ out << "}";
+ return out;
+}
+
// --- PointerCoords ---
float PointerCoords::getAxisValue(int32_t axis) const {
@@ -869,7 +891,7 @@
return InputEventLookup::getAxisLabel(axis);
}
-int32_t MotionEvent::getAxisFromLabel(const char* label) {
+std::optional<int> MotionEvent::getAxisFromLabel(const char* label) {
return InputEventLookup::getAxisByLabel(label);
}
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index 7159e27..d97c1bb 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -438,11 +438,11 @@
const std::unordered_map<std::string, int> InputEventLookup::FLAGS = {FLAGS_SEQUENCE};
-int InputEventLookup::lookupValueByLabel(const std::unordered_map<std::string, int>& map,
- const char* literal) {
+std::optional<int> InputEventLookup::lookupValueByLabel(
+ const std::unordered_map<std::string, int>& map, const char* literal) {
std::string str(literal);
auto it = map.find(str);
- return it != map.end() ? it->second : 0;
+ return it != map.end() ? std::make_optional(it->second) : std::nullopt;
}
const char* InputEventLookup::lookupLabelByValue(const std::vector<InputEventLabel>& vec,
@@ -453,8 +453,8 @@
return nullptr;
}
-int32_t InputEventLookup::getKeyCodeByLabel(const char* label) {
- return int32_t(lookupValueByLabel(KEYCODES, label));
+std::optional<int> InputEventLookup::getKeyCodeByLabel(const char* label) {
+ return lookupValueByLabel(KEYCODES, label);
}
const char* InputEventLookup::getLabelByKeyCode(int32_t keyCode) {
@@ -464,20 +464,20 @@
return nullptr;
}
-uint32_t InputEventLookup::getKeyFlagByLabel(const char* label) {
- return uint32_t(lookupValueByLabel(FLAGS, label));
+std::optional<int> InputEventLookup::getKeyFlagByLabel(const char* label) {
+ return lookupValueByLabel(FLAGS, label);
}
-int32_t InputEventLookup::getAxisByLabel(const char* label) {
- return int32_t(lookupValueByLabel(AXES, label));
+std::optional<int> InputEventLookup::getAxisByLabel(const char* label) {
+ return lookupValueByLabel(AXES, label);
}
const char* InputEventLookup::getAxisLabel(int32_t axisId) {
return lookupLabelByValue(AXES_NAMES, axisId);
}
-int32_t InputEventLookup::getLedByLabel(const char* label) {
- return int32_t(lookupValueByLabel(LEDS, label));
+std::optional<int> InputEventLookup::getLedByLabel(const char* label) {
+ return lookupValueByLabel(LEDS, label);
}
} // namespace android
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 6bfac40..737bd15 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -999,7 +999,7 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
+ std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
if (!keyCode) {
ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
keyCodeToken.string());
@@ -1010,19 +1010,19 @@
ALOGD("Parsed map key %s: code=%d, keyCode=%d.",
mapUsage ? "usage" : "scan code", code, keyCode);
#endif
- map.insert_or_assign(code, keyCode);
+ map.insert_or_assign(code, *keyCode);
return NO_ERROR;
}
status_t KeyCharacterMap::Parser::parseKey() {
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
+ std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
if (!keyCode) {
ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
keyCodeToken.string());
return BAD_VALUE;
}
- if (mMap->mKeys.indexOfKey(keyCode) >= 0) {
+ if (mMap->mKeys.indexOfKey(*keyCode) >= 0) {
ALOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(),
keyCodeToken.string());
return BAD_VALUE;
@@ -1036,11 +1036,9 @@
return BAD_VALUE;
}
-#if DEBUG_PARSER
- ALOGD("Parsed beginning of key: keyCode=%d.", keyCode);
-#endif
- mKeyCode = keyCode;
- mMap->mKeys.add(keyCode, new Key());
+ ALOGD_IF(DEBUG_PARSER, "Parsed beginning of key: keyCode=%d.", *keyCode);
+ mKeyCode = *keyCode;
+ mMap->mKeys.add(*keyCode, new Key());
mState = STATE_KEY;
return NO_ERROR;
}
@@ -1136,7 +1134,7 @@
} else if (token == "fallback") {
mTokenizer->skipDelimiters(WHITESPACE);
token = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
+ std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
if (!keyCode) {
ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.",
mTokenizer->getLocation().string(),
@@ -1148,12 +1146,12 @@
mTokenizer->getLocation().string());
return BAD_VALUE;
}
- behavior.fallbackKeyCode = keyCode;
+ behavior.fallbackKeyCode = *keyCode;
haveFallback = true;
} else if (token == "replace") {
mTokenizer->skipDelimiters(WHITESPACE);
token = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
+ std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(token.string());
if (!keyCode) {
ALOGE("%s: Invalid key code label for replace, got '%s'.",
mTokenizer->getLocation().string(),
@@ -1170,7 +1168,7 @@
mTokenizer->getLocation().string());
return BAD_VALUE;
}
- behavior.replacementKeyCode = keyCode;
+ behavior.replacementKeyCode = *keyCode;
haveReplacement = true;
} else {
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index 7371033..a2649f6 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "KeyLayoutMap"
+#include <android-base/logging.h>
#include <android/keycodes.h>
#include <ftl/enum.h>
#include <input/InputEventLabels.h>
@@ -54,6 +55,21 @@
namespace android {
namespace {
+std::optional<int> parseInt(const char* str) {
+ char* end;
+ errno = 0;
+ const int value = strtol(str, &end, 0);
+ if (end == str) {
+ LOG(ERROR) << "Could not parse " << str;
+ return {};
+ }
+ if (errno == ERANGE) {
+ LOG(ERROR) << "Out of bounds: " << str;
+ return {};
+ }
+ return value;
+}
+
constexpr const char* WHITESPACE = " \t\r";
template <InputDeviceSensorType S>
@@ -336,16 +352,15 @@
codeToken = mTokenizer->nextToken(WHITESPACE);
}
- char* end;
- int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
- if (*end) {
+ std::optional<int> code = parseInt(codeToken.string());
+ if (!code) {
ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(),
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
}
std::unordered_map<int32_t, Key>& map =
mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
- if (map.find(code) != map.end()) {
+ if (map.find(*code) != map.end()) {
ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
@@ -353,7 +368,7 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE);
- int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
+ std::optional<int> keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string());
if (!keyCode) {
ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(),
keyCodeToken.string());
@@ -366,40 +381,39 @@
if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break;
String8 flagToken = mTokenizer->nextToken(WHITESPACE);
- uint32_t flag = InputEventLookup::getKeyFlagByLabel(flagToken.string());
+ std::optional<int> flag = InputEventLookup::getKeyFlagByLabel(flagToken.string());
if (!flag) {
ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(),
flagToken.string());
return BAD_VALUE;
}
- if (flags & flag) {
+ if (flags & *flag) {
ALOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(),
flagToken.string());
return BAD_VALUE;
}
- flags |= flag;
+ flags |= *flag;
}
ALOGD_IF(DEBUG_PARSER, "Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.",
- mapUsage ? "usage" : "scan code", code, keyCode, flags);
+ mapUsage ? "usage" : "scan code", *code, *keyCode, flags);
Key key;
- key.keyCode = keyCode;
+ key.keyCode = *keyCode;
key.flags = flags;
- map.insert({code, key});
+ map.insert({*code, key});
return NO_ERROR;
}
status_t KeyLayoutMap::Parser::parseAxis() {
String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE);
- char* end;
- int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0));
- if (*end) {
+ std::optional<int> scanCode = parseInt(scanCodeToken.string());
+ if (!scanCode) {
ALOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(),
scanCodeToken.string());
return BAD_VALUE;
}
- if (mMap->mAxes.find(scanCode) != mMap->mAxes.end()) {
+ if (mMap->mAxes.find(*scanCode) != mMap->mAxes.end()) {
ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(),
scanCodeToken.string());
return BAD_VALUE;
@@ -414,48 +428,53 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 axisToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.axis = InputEventLookup::getAxisByLabel(axisToken.string());
- if (axisInfo.axis < 0) {
+ std::optional<int> axis = InputEventLookup::getAxisByLabel(axisToken.string());
+ if (!axis) {
ALOGE("%s: Expected inverted axis label, got '%s'.",
mTokenizer->getLocation().string(), axisToken.string());
return BAD_VALUE;
}
+ axisInfo.axis = *axis;
} else if (token == "split") {
axisInfo.mode = AxisInfo::MODE_SPLIT;
mTokenizer->skipDelimiters(WHITESPACE);
String8 splitToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.splitValue = int32_t(strtol(splitToken.string(), &end, 0));
- if (*end) {
+ std::optional<int> splitValue = parseInt(splitToken.string());
+ if (!splitValue) {
ALOGE("%s: Expected split value, got '%s'.",
mTokenizer->getLocation().string(), splitToken.string());
return BAD_VALUE;
}
+ axisInfo.splitValue = *splitValue;
mTokenizer->skipDelimiters(WHITESPACE);
String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.axis = InputEventLookup::getAxisByLabel(lowAxisToken.string());
- if (axisInfo.axis < 0) {
+ std::optional<int> axis = InputEventLookup::getAxisByLabel(lowAxisToken.string());
+ if (!axis) {
ALOGE("%s: Expected low axis label, got '%s'.",
mTokenizer->getLocation().string(), lowAxisToken.string());
return BAD_VALUE;
}
+ axisInfo.axis = *axis;
mTokenizer->skipDelimiters(WHITESPACE);
String8 highAxisToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.highAxis = InputEventLookup::getAxisByLabel(highAxisToken.string());
- if (axisInfo.highAxis < 0) {
+ std::optional<int> highAxis = InputEventLookup::getAxisByLabel(highAxisToken.string());
+ if (!highAxis) {
ALOGE("%s: Expected high axis label, got '%s'.",
mTokenizer->getLocation().string(), highAxisToken.string());
return BAD_VALUE;
}
+ axisInfo.highAxis = *highAxis;
} else {
- axisInfo.axis = InputEventLookup::getAxisByLabel(token.string());
- if (axisInfo.axis < 0) {
+ std::optional<int> axis = InputEventLookup::getAxisByLabel(token.string());
+ if (!axis) {
ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.",
mTokenizer->getLocation().string(), token.string());
return BAD_VALUE;
}
+ axisInfo.axis = *axis;
}
for (;;) {
@@ -467,12 +486,13 @@
if (keywordToken == "flat") {
mTokenizer->skipDelimiters(WHITESPACE);
String8 flatToken = mTokenizer->nextToken(WHITESPACE);
- axisInfo.flatOverride = int32_t(strtol(flatToken.string(), &end, 0));
- if (*end) {
+ std::optional<int> flatOverride = parseInt(flatToken.string());
+ if (!flatOverride) {
ALOGE("%s: Expected flat value, got '%s'.",
mTokenizer->getLocation().string(), flatToken.string());
return BAD_VALUE;
}
+ axisInfo.flatOverride = *flatOverride;
} else {
ALOGE("%s: Expected keyword 'flat', got '%s'.",
mTokenizer->getLocation().string(), keywordToken.string());
@@ -483,9 +503,9 @@
ALOGD_IF(DEBUG_PARSER,
"Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, "
"splitValue=%d, flatOverride=%d.",
- scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue,
+ *scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue,
axisInfo.flatOverride);
- mMap->mAxes.insert({scanCode, axisInfo});
+ mMap->mAxes.insert({*scanCode, axisInfo});
return NO_ERROR;
}
@@ -497,9 +517,8 @@
mTokenizer->skipDelimiters(WHITESPACE);
codeToken = mTokenizer->nextToken(WHITESPACE);
}
- char* end;
- int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
- if (*end) {
+ std::optional<int> code = parseInt(codeToken.string());
+ if (!code) {
ALOGE("%s: Expected led %s number, got '%s'.", mTokenizer->getLocation().string(),
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
@@ -507,7 +526,7 @@
std::unordered_map<int32_t, Led>& map =
mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode;
- if (map.find(code) != map.end()) {
+ if (map.find(*code) != map.end()) {
ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(),
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
@@ -515,19 +534,19 @@
mTokenizer->skipDelimiters(WHITESPACE);
String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE);
- int32_t ledCode = InputEventLookup::getLedByLabel(ledCodeToken.string());
- if (ledCode < 0) {
+ std::optional<int> ledCode = InputEventLookup::getLedByLabel(ledCodeToken.string());
+ if (!ledCode) {
ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(),
ledCodeToken.string());
return BAD_VALUE;
}
ALOGD_IF(DEBUG_PARSER, "Parsed led %s: code=%d, ledCode=%d.", mapUsage ? "usage" : "scan code",
- code, ledCode);
+ *code, *ledCode);
Led led;
- led.ledCode = ledCode;
- map.insert({code, led});
+ led.ledCode = *ledCode;
+ map.insert({*code, led});
return NO_ERROR;
}
@@ -565,16 +584,15 @@
// sensor 0x05 GYROSCOPE Z
status_t KeyLayoutMap::Parser::parseSensor() {
String8 codeToken = mTokenizer->nextToken(WHITESPACE);
- char* end;
- int32_t code = int32_t(strtol(codeToken.string(), &end, 0));
- if (*end) {
+ std::optional<int> code = parseInt(codeToken.string());
+ if (!code) {
ALOGE("%s: Expected sensor %s number, got '%s'.", mTokenizer->getLocation().string(),
"abs code", codeToken.string());
return BAD_VALUE;
}
std::unordered_map<int32_t, Sensor>& map = mMap->mSensorsByAbsCode;
- if (map.find(code) != map.end()) {
+ if (map.find(*code) != map.end()) {
ALOGE("%s: Duplicate entry for sensor %s '%s'.", mTokenizer->getLocation().string(),
"abs code", codeToken.string());
return BAD_VALUE;
@@ -599,13 +617,13 @@
}
int32_t sensorDataIndex = indexOpt.value();
- ALOGD_IF(DEBUG_PARSER, "Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code,
+ ALOGD_IF(DEBUG_PARSER, "Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", *code,
ftl::enum_string(sensorType).c_str(), sensorDataIndex);
Sensor sensor;
sensor.sensorType = sensorType;
sensor.sensorDataIndex = sensorDataIndex;
- map.emplace(code, sensor);
+ map.emplace(*code, sensor);
return NO_ERROR;
}
diff --git a/libs/input/android/os/InputEventInjectionResult.aidl b/libs/input/android/os/InputEventInjectionResult.aidl
index 3bc7068..e80c2a5 100644
--- a/libs/input/android/os/InputEventInjectionResult.aidl
+++ b/libs/input/android/os/InputEventInjectionResult.aidl
@@ -37,4 +37,7 @@
/* Injection failed due to a timeout. */
TIMED_OUT = 3,
+
+ ftl_first=PENDING,
+ ftl_last=TIMED_OUT,
}
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp
index 2344463..ee961f0 100644
--- a/libs/input/tests/InputDevice_test.cpp
+++ b/libs/input/tests/InputDevice_test.cpp
@@ -133,6 +133,20 @@
ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap);
}
+TEST_F(InputDeviceKeyMapTest, keyCharacteMapBadAxisLabel) {
+ std::string klPath = base::GetExecutableDirectory() + "/data/bad_axis_label.kl";
+
+ base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
+ ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath;
+}
+
+TEST_F(InputDeviceKeyMapTest, keyCharacteMapBadLedLabel) {
+ std::string klPath = base::GetExecutableDirectory() + "/data/bad_led_label.kl";
+
+ base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath);
+ ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath;
+}
+
TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) {
#if !defined(__ANDROID__)
GTEST_SKIP() << "Can't check kernel configs on host";
diff --git a/libs/input/tests/data/bad_axis_label.kl b/libs/input/tests/data/bad_axis_label.kl
new file mode 100644
index 0000000..6897380
--- /dev/null
+++ b/libs/input/tests/data/bad_axis_label.kl
@@ -0,0 +1,17 @@
+# Copyright (C) 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.
+
+# This KL should not be loaded because the axis label is not valid
+
+axis 0 DEFINITELY_NOT_AXIS_LABEL
diff --git a/libs/input/tests/data/bad_led_label.kl b/libs/input/tests/data/bad_led_label.kl
new file mode 100644
index 0000000..293c0d2
--- /dev/null
+++ b/libs/input/tests/data/bad_led_label.kl
@@ -0,0 +1,17 @@
+# Copyright (C) 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.
+
+# This KL should not be loaded because the led label is invalid
+
+led 0 ABSOLUTELY_NOT_LED_LABEL
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
index d0de48f..419b63d 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegdecoder.h
@@ -47,7 +47,7 @@
*/
void* getDecompressedImagePtr();
/*
- * Returns the decompressed raw image buffer size. This mgit ethod must be called only after
+ * Returns the decompressed raw image buffer size. This method must be called only after
* calling decompressImage().
*/
size_t getDecompressedImageSize();
@@ -92,10 +92,6 @@
size_t* pWidth, size_t* pHeight,
std::vector<uint8_t>* iccData,
std::vector<uint8_t>* exifData);
- /*
- * Extracts EXIF package and updates the EXIF position / length without decoding the image.
- */
- bool extractEXIF(const void* image, int length);
private:
bool decode(const void* image, int length, bool decodeToRGBA);
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
index ae15d24..696be1b 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
@@ -321,13 +321,17 @@
jr_compressed_ptr dest);
/*
- * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image
- * and the compressed recovery map as input, and update the XMP metadata with the end of JPEG
- * marker, and append the compressed gian map after the JPEG.
+ * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image,
+ * the compressed recovery map and optionally the exif package as inputs, and generate the XMP
+ * metadata, and finally append everything in the order of:
+ * SOI, APP2(EXIF) (if EXIF is from outside), APP2(XMP), primary image, recovery map
+ * Note that EXIF package is only available for encoding API-0 and API-1. For encoding API-2 and
+ * API-3 this parameter is null, but the primary image in JPEG/R may still have EXIF as long as
+ * the input JPEG has EXIF.
*
* @param compressed_jpeg_image compressed 8-bit JPEG image
* @param compress_recovery_map compressed recover map
- * @param exif EXIF package
+ * @param (nullable) exif EXIF package
* @param metadata JPEG/R metadata to encode in XMP of the jpeg
* @param dest compressed JPEGR image
* @return NO_ERROR if calculation succeeds, error code if error occurs.
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
index 8b2672f..c36a363 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
@@ -45,7 +45,6 @@
* @return status of succeed or error code.
*/
status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position);
-status_t Write(jr_exif_ptr destination, const void* source, size_t length, int &position);
/*
@@ -105,83 +104,6 @@
* @return XMP metadata in type of string
*/
std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
-
-/*
- * Add J R entry to existing exif, or create a new one with J R entry if it's null.
- * EXIF syntax / change:
- * ori:
- * FF E1 - APP1
- * 01 FC - size of APP1 (to be calculated)
- * -----------------------------------------------------
- * 45 78 69 66 00 00 - Exif\0\0 "Exif header"
- * 49 49 2A 00 - TIFF Header
- * 08 00 00 00 - offset to the IFD (image file directory)
- * 06 00 - 6 entries
- * 00 01 - Width Tag
- * 03 00 - 'Short' type
- * 01 00 00 00 - 1 component
- * 00 05 00 00 - image with 0x500
- *--------------------------------------------------------------------------
- * new:
- * FF E1 - APP1
- * 02 08 - new size, equals to old size + EXIF_J_R_ENTRY_LENGTH (12)
- *-----------------------------------------------------
- * 45 78 69 66 00 00 - Exif\0\0 "Exif header"
- * 49 49 2A 00 - TIFF Header
- * 08 00 00 00 - offset to the IFD (image file directory)
- * 07 00 - +1 entry
- * 4A 52 Custom ('J''R') Tag
- * 07 00 - Unknown type
- * 01 00 00 00 - 1 component
- * 00 00 00 00 - empty data
- * 00 01 - Width Tag
- * 03 00 - 'Short' type
- * 01 00 00 00 - 1 component
- * 00 05 00 00 - image with 0x500
- */
-status_t updateExif(jr_exif_ptr exif, jr_exif_ptr dest);
-
-/*
- * Modify offsets in EXIF in place.
- *
- * Each tag has the following structure:
- *
- * 00 01 - Tag
- * 03 00 - data format
- * 01 00 00 00 - number of components
- * 00 05 00 00 - value
- *
- * The value means offset if
- * (1) num_of_components * bytes_per_component > 4 bytes, or
- * (2) tag == 0x8769 (ExifOffset).
- * In both cases, the method will add EXIF_J_R_ENTRY_LENGTH (12) to the offsets.
- */
-void updateExifOffsets(jr_exif_ptr exif, int pos, bool use_big_endian);
-void updateExifOffsets(jr_exif_ptr exif, int pos, int num_entry, bool use_big_endian);
-
-/*
- * Read data from the target position and target length in bytes;
- */
-int readValue(uint8_t* data, int pos, int length, bool use_big_endian);
-
-/*
- * Returns the length of data format in bytes
- *
- * ----------------------------------------------------------------------------------------------
- * | value | 1 | 2 | 3 | 4 |
- * | format | unsigned byte | ascii strings | unsigned short | unsigned long |
- * | bytes/component | 1 | 1 | 2 | 4 |
- * ----------------------------------------------------------------------------------------------
- * | value | 5 | 6 | 7 | 8 |
- * | format |unsigned rational| signed byte | undefined | signed short |
- * | bytes/component | 8 | 1 | 1 | 2 |
- * ----------------------------------------------------------------------------------------------
- * | value | 9 | 10 | 11 | 12 |
- * | format | signed long | signed rational | single float | double float |
- * | bytes/component | 4 | 8 | 4 | 8 |
- * ----------------------------------------------------------------------------------------------
- */
-int findFormatLengthInBytes(int data_format);
}
#endif //ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
diff --git a/libs/jpegrecoverymap/jpegdecoder.cpp b/libs/jpegrecoverymap/jpegdecoder.cpp
index 6fbc6b0..1bf609a 100644
--- a/libs/jpegrecoverymap/jpegdecoder.cpp
+++ b/libs/jpegrecoverymap/jpegdecoder.cpp
@@ -248,60 +248,6 @@
return true;
}
-// TODO (Fyodor/Dichen): merge this method with getCompressedImageParameters() since they have
-// similar functionality. Yet Dichen is not familiar with who's calling
-// getCompressedImageParameters(), looks like it's used by some pending CLs.
-bool JpegDecoder::extractEXIF(const void* image, int length) {
- jpeg_decompress_struct cinfo;
- jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
- jpegrerror_mgr myerr;
-
- cinfo.err = jpeg_std_error(&myerr.pub);
- myerr.pub.error_exit = jpegrerror_exit;
-
- if (setjmp(myerr.setjmp_buffer)) {
- jpeg_destroy_decompress(&cinfo);
- return false;
- }
- jpeg_create_decompress(&cinfo);
-
- jpeg_save_markers(&cinfo, kAPP0Marker, 0xFFFF);
- jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
- jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
-
- cinfo.src = &mgr;
- jpeg_read_header(&cinfo, TRUE);
-
- bool exifAppears = false;
- size_t pos = 2; // position after SOI
- for (jpeg_marker_struct* marker = cinfo.marker_list;
- marker && !exifAppears;
- marker = marker->next) {
-
- pos += 4;
- pos += marker->original_length;
-
- if (marker->marker != kAPP1Marker) {
- continue;
- }
-
- const unsigned int len = marker->data_length;
- if (!exifAppears &&
- len > kExifIdCode.size() &&
- !strncmp(reinterpret_cast<const char*>(marker->data),
- kExifIdCode.c_str(),
- kExifIdCode.size())) {
- mEXIFBuffer.resize(len, 0);
- memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
- exifAppears = true;
- mExifPos = pos - marker->original_length;
- }
- }
-
- jpeg_destroy_decompress(&cinfo);
- return true;
-}
-
bool JpegDecoder::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest,
bool isSingleChannel) {
if (isSingleChannel) {
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index 22289de..30aa846 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -175,18 +175,7 @@
jpeg.data = jpeg_encoder.getCompressedImagePtr();
jpeg.length = jpeg_encoder.getCompressedImageSize();
- jpegr_exif_struct new_exif;
- if (exif == nullptr || exif->data == nullptr) {
- new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
- } else {
- new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
- }
- new_exif.data = new uint8_t[new_exif.length];
- std::unique_ptr<uint8_t[]> new_exif_data;
- new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
- JPEGR_CHECK(updateExif(exif, &new_exif));
-
- JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &new_exif, &metadata, dest));
+ JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, exif, &metadata, dest));
return NO_ERROR;
}
@@ -250,19 +239,7 @@
jpeg.data = jpeg_encoder.getCompressedImagePtr();
jpeg.length = jpeg_encoder.getCompressedImageSize();
- jpegr_exif_struct new_exif;
- if (exif == nullptr || exif->data == nullptr) {
- new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
- } else {
- new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
- }
-
- new_exif.data = new uint8_t[new_exif.length];
- std::unique_ptr<uint8_t[]> new_exif_data;
- new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
- JPEGR_CHECK(updateExif(exif, &new_exif));
-
- JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &new_exif, &metadata, dest));
+ JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, exif, &metadata, dest));
return NO_ERROR;
}
@@ -311,47 +288,7 @@
compressed_map.data = compressed_map_data.get();
JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
- // Extract EXIF from JPEG without decoding.
- JpegDecoder jpeg_decoder;
- if (!jpeg_decoder.extractEXIF(compressed_jpeg_image->data, compressed_jpeg_image->length)) {
- return ERROR_JPEGR_DECODE_ERROR;
- }
-
- // Update exif.
- jpegr_exif_struct exif;
- exif.data = nullptr;
- exif.length = 0;
- jpegr_compressed_struct new_jpeg_image;
- new_jpeg_image.data = nullptr;
- new_jpeg_image.length = 0;
- if (jpeg_decoder.getEXIFPos() != 0) {
- copyJpegWithoutExif(&new_jpeg_image,
- compressed_jpeg_image,
- jpeg_decoder.getEXIFPos(),
- jpeg_decoder.getEXIFSize());
- exif.data = jpeg_decoder.getEXIFPtr();
- exif.length = jpeg_decoder.getEXIFSize();
- }
-
- jpegr_exif_struct new_exif;
- if (exif.data == nullptr) {
- new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
- } else {
- new_exif.length = exif.length + EXIF_J_R_ENTRY_LENGTH;
- }
-
- new_exif.data = new uint8_t[new_exif.length];
- std::unique_ptr<uint8_t[]> new_exif_data;
- new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
- JPEGR_CHECK(updateExif(&exif, &new_exif));
-
- JPEGR_CHECK(appendRecoveryMap(
- new_jpeg_image.data == nullptr ? compressed_jpeg_image : &new_jpeg_image,
- &compressed_map, &new_exif, &metadata, dest));
-
- if (new_jpeg_image.data != nullptr) {
- free(new_jpeg_image.data);
- }
+ JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, nullptr, &metadata, dest));
return NO_ERROR;
}
@@ -384,33 +321,6 @@
uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut;
- // Update exif.
- jpegr_exif_struct exif;
- exif.data = nullptr;
- exif.length = 0;
- jpegr_compressed_struct new_jpeg_image;
- new_jpeg_image.data = nullptr;
- new_jpeg_image.length = 0;
- if (jpeg_decoder.getEXIFPos() != 0) {
- copyJpegWithoutExif(&new_jpeg_image,
- compressed_jpeg_image,
- jpeg_decoder.getEXIFPos(),
- jpeg_decoder.getEXIFSize());
- exif.data = jpeg_decoder.getEXIFPtr();
- exif.length = jpeg_decoder.getEXIFSize();
- }
-
- jpegr_exif_struct new_exif;
- if (exif.data == nullptr) {
- new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
- } else {
- new_exif.length = exif.length + EXIF_J_R_ENTRY_LENGTH;
- }
- new_exif.data = new uint8_t[new_exif.length];
- std::unique_ptr<uint8_t[]> new_exif_data;
- new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
- JPEGR_CHECK(updateExif(&exif, &new_exif));
-
if (uncompressed_p010_image->width != uncompressed_yuv_420_image.width
|| uncompressed_p010_image->height != uncompressed_yuv_420_image.height) {
return ERROR_JPEGR_RESOLUTION_MISMATCH;
@@ -435,13 +345,7 @@
compressed_map.data = compressed_map_data.get();
JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
- JPEGR_CHECK(appendRecoveryMap(
- new_jpeg_image.data == nullptr ? compressed_jpeg_image : &new_jpeg_image,
- &compressed_map, &new_exif, &metadata, dest));
-
- if (new_jpeg_image.data != nullptr) {
- free(new_jpeg_image.data);
- }
+ JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, nullptr, &metadata, dest));
return NO_ERROR;
}
@@ -967,15 +871,20 @@
// JPEG/R structure:
// SOI (ff d8)
+//
+// (Optional, only if EXIF package is from outside)
// APP1 (ff e1)
// 2 bytes of length (2 + length of exif package)
// EXIF package (this includes the first two bytes representing the package length)
-// APP1 (ff e1)
+//
+// (Required, XMP package) APP1 (ff e1)
// 2 bytes of length (2 + 29 + length of xmp package)
// name space ("http://ns.adobe.com/xap/1.0/\0")
// xmp
-// primary image (without the first two bytes (SOI) and without EXIF, may have other packages)
-// secondary image (the recovery map)
+//
+// (Required) primary image (without the first two bytes (SOI), may have other packages)
+//
+// (Required) secondary image (the recovery map)
//
// Metadata versions we are using:
// ECMA TR-98 for JFIF marker
@@ -989,7 +898,6 @@
jr_compressed_ptr dest) {
if (compressed_jpeg_image == nullptr
|| compressed_recovery_map == nullptr
- || exif == nullptr
|| metadata == nullptr
|| dest == nullptr) {
return ERROR_JPEGR_INVALID_NULL_PTR;
@@ -1002,7 +910,7 @@
JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
// Write EXIF
- {
+ if (exif != nullptr) {
const int length = 2 + exif->length;
const uint8_t lengthH = ((length >> 8) & 0xff);
const uint8_t lengthL = (length & 0xff);
diff --git a/libs/jpegrecoverymap/recoverymaputils.cpp b/libs/jpegrecoverymap/recoverymaputils.cpp
index d5ad9a5..1617b8b 100644
--- a/libs/jpegrecoverymap/recoverymaputils.cpp
+++ b/libs/jpegrecoverymap/recoverymaputils.cpp
@@ -22,8 +22,6 @@
#include <image_io/xml/xml_handler.h>
#include <image_io/xml/xml_rule.h>
-#include <utils/Log.h>
-
using namespace photos_editing_formats::image_io;
using namespace std;
@@ -55,12 +53,6 @@
return NO_ERROR;
}
-status_t Write(jr_exif_ptr destination, const void* source, size_t length, int &position) {
- memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
- position += length;
- return NO_ERROR;
-}
-
// Extremely simple XML Handler - just searches for interesting elements
class XMPXmlHandler : public XmlHandler {
public:
@@ -344,185 +336,4 @@
return ss.str();
}
-/*
- * Helper function
- * Add J R entry to existing exif, or create a new one with J R entry if it's null.
- */
-status_t updateExif(jr_exif_ptr exif, jr_exif_ptr dest) {
- if (exif == nullptr || exif->data == nullptr) {
- uint8_t data[PSEUDO_EXIF_PACKAGE_LENGTH] = {
- 0x45, 0x78, 0x69, 0x66, 0x00, 0x00,
- 0x49, 0x49, 0x2A, 0x00,
- 0x08, 0x00, 0x00, 0x00,
- 0x01, 0x00,
- 0x4A, 0x52,
- 0x07, 0x00,
- 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00};
- int pos = 0;
- Write(dest, data, PSEUDO_EXIF_PACKAGE_LENGTH, pos);
- return NO_ERROR;
- }
-
- int num_entry = 0;
- uint8_t num_entry_low = 0;
- uint8_t num_entry_high = 0;
- bool use_big_endian = false;
- if (reinterpret_cast<uint16_t*>(exif->data)[3] == 0x4949) {
- num_entry_low = reinterpret_cast<uint8_t*>(exif->data)[14];
- num_entry_high = reinterpret_cast<uint8_t*>(exif->data)[15];
- } else if (reinterpret_cast<uint16_t*>(exif->data)[3] == 0x4d4d) {
- use_big_endian = true;
- num_entry_high = reinterpret_cast<uint8_t*>(exif->data)[14];
- num_entry_low = reinterpret_cast<uint8_t*>(exif->data)[15];
- } else {
- return ERROR_JPEGR_METADATA_ERROR;
- }
- num_entry = (num_entry_high << 8) | num_entry_low;
- num_entry += 1;
- num_entry_low = num_entry & 0xff;
- num_entry_high = (num_entry >> 8) & 0xff;
-
- int pos = 0;
- Write(dest, (uint8_t*)exif->data, 14, pos);
-
- if (use_big_endian) {
- Write(dest, &num_entry_high, 1, pos);
- Write(dest, &num_entry_low, 1, pos);
- uint8_t data[EXIF_J_R_ENTRY_LENGTH] = {
- 0x4A, 0x52,
- 0x00, 0x07,
- 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00};
- Write(dest, data, EXIF_J_R_ENTRY_LENGTH, pos);
- } else {
- Write(dest, &num_entry_low, 1, pos);
- Write(dest, &num_entry_high, 1, pos);
- uint8_t data[EXIF_J_R_ENTRY_LENGTH] = {
- 0x4A, 0x52,
- 0x07, 0x00,
- 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00};
- Write(dest, data, EXIF_J_R_ENTRY_LENGTH, pos);
- }
-
- Write(dest, (uint8_t*)exif->data + 16, exif->length - 16, pos);
-
- updateExifOffsets(dest,
- 28, // start from the second tag, skip the "JR" tag
- num_entry - 1,
- use_big_endian);
-
- return NO_ERROR;
-}
-
-/*
- * Helper function
- * Modify offsets in EXIF in place.
- */
-void updateExifOffsets(jr_exif_ptr exif, int pos, bool use_big_endian) {
- int num_entry = readValue(reinterpret_cast<uint8_t*>(exif->data), pos, 2, use_big_endian);
- updateExifOffsets(exif, pos + 2, num_entry, use_big_endian);
-}
-
-void updateExifOffsets(jr_exif_ptr exif, int pos, int num_entry, bool use_big_endian) {
- for (int i = 0; i < num_entry; pos += EXIF_J_R_ENTRY_LENGTH, i++) {
- int tag = readValue(reinterpret_cast<uint8_t*>(exif->data), pos, 2, use_big_endian);
- bool need_to_update_offset = false;
- if (tag == 0x8769) {
- need_to_update_offset = true;
- int sub_ifd_offset =
- readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 8, 4, use_big_endian)
- + 6 // "Exif\0\0";
- + EXIF_J_R_ENTRY_LENGTH;
- updateExifOffsets(exif, sub_ifd_offset, use_big_endian);
- } else {
- int data_format =
- readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 2, 2, use_big_endian);
- int num_of_components =
- readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 4, 4, use_big_endian);
- int data_length = findFormatLengthInBytes(data_format) * num_of_components;
- if (data_length > 4) {
- need_to_update_offset = true;
- }
- }
-
- if (!need_to_update_offset) {
- continue;
- }
-
- int offset = readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 8, 4, use_big_endian);
-
- offset += EXIF_J_R_ENTRY_LENGTH;
-
- if (use_big_endian) {
- reinterpret_cast<uint8_t*>(exif->data)[pos + 11] = offset & 0xff;
- reinterpret_cast<uint8_t*>(exif->data)[pos + 10] = (offset >> 8) & 0xff;
- reinterpret_cast<uint8_t*>(exif->data)[pos + 9] = (offset >> 16) & 0xff;
- reinterpret_cast<uint8_t*>(exif->data)[pos + 8] = (offset >> 24) & 0xff;
- } else {
- reinterpret_cast<uint8_t*>(exif->data)[pos + 8] = offset & 0xff;
- reinterpret_cast<uint8_t*>(exif->data)[pos + 9] = (offset >> 8) & 0xff;
- reinterpret_cast<uint8_t*>(exif->data)[pos + 10] = (offset >> 16) & 0xff;
- reinterpret_cast<uint8_t*>(exif->data)[pos + 11] = (offset >> 24) & 0xff;
- }
- }
-}
-
-/*
- * Read data from the target position and target length in bytes;
- */
-int readValue(uint8_t* data, int pos, int length, bool use_big_endian) {
- if (length == 2) {
- if (use_big_endian) {
- return (data[pos] << 8) | data[pos + 1];
- } else {
- return (data[pos + 1] << 8) | data[pos];
- }
- } else if (length == 4) {
- if (use_big_endian) {
- return (data[pos] << 24) | (data[pos + 1] << 16) | (data[pos + 2] << 8) | data[pos + 3];
- } else {
- return (data[pos + 3] << 24) | (data[pos + 2] << 16) | (data[pos + 1] << 8) | data[pos];
- }
- } else {
- // Not support for now.
- ALOGE("Error in readValue(): pos=%d, length=%d", pos, length);
- return -1;
- }
-}
-
-/*
- * Helper function
- * Returns the length of data format in bytes
- */
-int findFormatLengthInBytes(int data_format) {
- switch (data_format) {
- case 1: // unsigned byte
- case 2: // ascii strings
- case 6: // signed byte
- case 7: // undefined
- return 1;
-
- case 3: // unsigned short
- case 8: // signed short
- return 2;
-
- case 4: // unsigned long
- case 9: // signed long
- case 11: // single float
- return 4;
-
- case 5: // unsigned rational
- case 10: // signed rational
- case 12: // double float
- return 8;
-
- default:
- // should not hit here
- ALOGE("Error in findFormatLengthInBytes(): data_format=%d", data_format);
- return -1;
- }
-}
-
-} // namespace android::recoverymap
\ No newline at end of file
+} // namespace android::recoverymap
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index c7745e6..6c54635 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1067,11 +1067,12 @@
}
static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window,
+ uint64_t frameNumber,
int64_t frameTimelineVsyncId,
int32_t inputEventId,
int64_t startTimeNanos) {
- return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameTimelineVsyncId,
- inputEventId, startTimeNanos);
+ return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameNumber,
+ frameTimelineVsyncId, inputEventId, startTimeNanos);
}
// ------------------------------------------------------------------------------------------------
diff --git a/services/batteryservice/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h
index 178bc29..a2e4115 100644
--- a/services/batteryservice/include/batteryservice/BatteryService.h
+++ b/services/batteryservice/include/batteryservice/BatteryService.h
@@ -34,6 +34,9 @@
BATTERY_PROP_CAPACITY = 4, // equals BATTERY_PROPERTY_CAPACITY
BATTERY_PROP_ENERGY_COUNTER = 5, // equals BATTERY_PROPERTY_ENERGY_COUNTER
BATTERY_PROP_BATTERY_STATUS = 6, // equals BATTERY_PROPERTY_BATTERY_STATUS
+ BATTERY_PROP_CHARGING_POLICY = 7, // equals BATTERY_PROPERTY_CHARGING_POLICY
+ BATTERY_PROP_MANUFACTURING_DATE = 8, // equals BATTERY_PROPERTY_MANUFACTURING_DATE
+ BATTERY_PROP_FIRST_USAGE_DATE = 9, // equals BATTERY_PROPERTY_FIRST_USAGE_DATE
};
struct BatteryProperties {
diff --git a/services/batteryservice/include/batteryservice/BatteryServiceConstants.h b/services/batteryservice/include/batteryservice/BatteryServiceConstants.h
index 8a90a12..2d7072d 100644
--- a/services/batteryservice/include/batteryservice/BatteryServiceConstants.h
+++ b/services/batteryservice/include/batteryservice/BatteryServiceConstants.h
@@ -1,7 +1,5 @@
-// This file is autogenerated by hidl-gen. Do not edit manually.
-
-#ifndef HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
-#define HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+#ifndef AIDL_android_hardware_health_V2_EXPORTED_CONSTANTS_H_
+#define AIDL_android_hardware_health_V2_EXPORTED_CONSTANTS_H_
#ifdef __cplusplus
extern "C" {
@@ -15,6 +13,8 @@
BATTERY_STATUS_FULL = 5,
};
+// must be kept in sync with definitions in
+// hardware/interfaces/health/aidl/android/hardware/health/BatteryHealth.aidl
enum {
BATTERY_HEALTH_UNKNOWN = 1,
BATTERY_HEALTH_GOOD = 2,
@@ -23,10 +23,23 @@
BATTERY_HEALTH_OVER_VOLTAGE = 5,
BATTERY_HEALTH_UNSPECIFIED_FAILURE = 6,
BATTERY_HEALTH_COLD = 7,
+ BATTERY_HEALTH_FAIR = 8,
+ BATTERY_HEALTH_NOT_AVAILABLE = 11,
+ BATTERY_HEALTH_INCONSISTENT = 12,
+};
+
+// must be kept in sync with definitions in
+// hardware/interfaces/health/aidl/android/hardware/health/BatteryChargingState.aidl
+enum {
+ BATTERY_STATUS_NORMAL = 1,
+ BATTERY_STATUS_TOO_COLD = 2,
+ BATTERY_STATUS_TOO_HOT = 3,
+ BATTERY_STATUS_LONG_LIFE = 4,
+ BATTERY_STATUS_ADAPTIVE = 5,
};
#ifdef __cplusplus
}
#endif
-#endif // HIDL_GENERATED_android_hardware_health_V1_0_EXPORTED_CONSTANTS_H_
+#endif // AIDL_android_hardware_health_V2_EXPORTED_CONSTANTS_H_
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index a864cf8..2450235 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -73,6 +73,9 @@
if (changes & CHANGE_ENABLED_STATE) {
result += "ENABLED_STATE | ";
}
+ if (changes & CHANGE_TOUCHPAD_SETTINGS) {
+ result += "TOUCHPAD_SETTINGS | ";
+ }
if (changes & CHANGE_MUST_REOPEN) {
result += "MUST_REOPEN | ";
}
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 3d7242e..cacf30b 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -61,6 +61,7 @@
"name": "FrameworksCoreTests",
"options": [
{
+ "include-filter": "android.hardware.input",
"include-filter": "android.view.VerifiedKeyEventTest",
"include-filter": "android.view.VerifiedMotionEventTest"
}
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 7bbfb95..ce7c882 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -331,4 +331,28 @@
return seq;
}
+std::ostream& operator<<(std::ostream& out, const DispatchEntry& entry) {
+ out << "DispatchEntry{resolvedAction=";
+ switch (entry.eventEntry->type) {
+ case EventEntry::Type::KEY: {
+ out << KeyEvent::actionToString(entry.resolvedAction);
+ break;
+ }
+ case EventEntry::Type::MOTION: {
+ out << MotionEvent::actionToString(entry.resolvedAction);
+ break;
+ }
+ default: {
+ out << "<invalid, not a key or a motion>";
+ break;
+ }
+ }
+ std::string transform;
+ entry.transform.dump(transform, "transform");
+ out << ", resolvedFlags=" << entry.resolvedFlags
+ << ", targetFlags=" << entry.targetFlags.string() << ", transform=" << transform
+ << "} original =" << entry.eventEntry->getDescription();
+ return out;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 3799814..8dc2a2a 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -254,6 +254,8 @@
static uint32_t nextSeq();
};
+std::ostream& operator<<(std::ostream& out, const DispatchEntry& entry);
+
VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry);
VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry,
const ui::Transform& rawTransform);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 37a451b..dc9f02a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -20,6 +20,7 @@
#define LOG_NDEBUG 1
#include <android-base/chrono_utils.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android/os/IInputConstants.h>
@@ -616,6 +617,12 @@
return out;
}
+template <typename T>
+std::vector<T>& operator+=(std::vector<T>& left, const std::vector<T>& right) {
+ left.insert(left.end(), right.begin(), right.end());
+ return left;
+}
+
} // namespace
// --- InputDispatcher ---
@@ -1018,8 +1025,7 @@
const auto [x, y] = resolveTouchedPosition(motionEntry);
const bool isStylus = isPointerFromStylus(motionEntry, 0 /*pointerIndex*/);
- sp<WindowInfoHandle> touchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, nullptr, isStylus);
+ auto [touchedWindowHandle, _] = findTouchedWindowAtLocked(displayId, x, y, isStylus);
if (touchedWindowHandle != nullptr &&
touchedWindowHandle->getApplicationToken() !=
mAwaitedFocusedApplication->getApplicationToken()) {
@@ -1142,15 +1148,11 @@
}
}
-sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
- int32_t y, TouchState* touchState,
- bool isStylus,
- bool addOutsideTargets,
- bool ignoreDragWindow) const {
- if (addOutsideTargets && touchState == nullptr) {
- LOG_ALWAYS_FATAL("Must provide a valid touch state if adding outside targets");
- }
+std::pair<sp<WindowInfoHandle>, std::vector<InputTarget>>
+InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, bool isStylus,
+ bool ignoreDragWindow) const {
// Traverse windows from front to back to find touched window.
+ std::vector<InputTarget> outsideTargets;
const auto& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
@@ -1159,16 +1161,16 @@
const WindowInfo& info = *windowHandle->getInfo();
if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
- return windowHandle;
+ return {windowHandle, outsideTargets};
}
- if (addOutsideTargets &&
- info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
- touchState->addOrUpdateWindow(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE,
- BitSet32(0));
+ if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
+ addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE,
+ BitSet32(0), /*firstDownTimeInTarget=*/std::nullopt,
+ outsideTargets);
}
}
- return nullptr;
+ return {nullptr, {}};
}
std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(
@@ -1756,16 +1758,11 @@
pilferPointersLocked(mDragState->dragWindow->getToken());
}
- std::vector<TouchedWindow> touchedWindows =
+ inputTargets =
findTouchedWindowTargetsLocked(currentTime, *entry, &conflictingPointerActions,
/*byref*/ injectionResult);
- for (const TouchedWindow& touchedWindow : touchedWindows) {
- LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED,
- "Shouldn't be adding window if the injection didn't succeed.");
- addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
- touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
- inputTargets);
- }
+ LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED &&
+ !inputTargets.empty());
} else {
// Non touch event. (eg. trackball)
sp<WindowInfoHandle> focusedWindow =
@@ -2137,12 +2134,12 @@
return true;
}
-std::vector<TouchedWindow> InputDispatcher::findTouchedWindowTargetsLocked(
+std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
InputEventInjectionResult& outInjectionResult) {
ATRACE_CALL();
- std::vector<TouchedWindow> touchedWindows;
+ std::vector<InputTarget> targets;
// For security reasons, we defer updating the touch state until we are sure that
// event injection will be allowed.
const int32_t displayId = entry.displayId;
@@ -2169,8 +2166,12 @@
const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
- const bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
- maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
+ // A DOWN could be generated from POINTER_DOWN if the initial pointers did not land into any
+ // touchable windows.
+ const bool wasDown = oldState != nullptr && oldState->isDown();
+ const bool isDown = (maskedAction == AMOTION_EVENT_ACTION_DOWN) ||
+ (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN && !wasDown);
+ const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction;
const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
if (newGesture) {
@@ -2181,7 +2182,7 @@
displayId);
// TODO: test multiple simultaneous input streams.
outInjectionResult = InputEventInjectionResult::FAILED;
- return touchedWindows; // wrong device
+ return {}; // wrong device
}
tempTouchState.clearWindowsWithoutPointers();
tempTouchState.deviceId = entry.deviceId;
@@ -2193,7 +2194,7 @@
displayId);
// TODO: test multiple simultaneous input streams.
outInjectionResult = InputEventInjectionResult::FAILED;
- return touchedWindows; // wrong device
+ return {}; // wrong device
}
if (isHoverAction) {
@@ -2206,12 +2207,15 @@
/* Case 1: New splittable pointer going down, or need target for hover or scroll. */
const auto [x, y] = resolveTouchedPosition(entry);
const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
- const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
+ // Outside targets should be added upon first dispatched DOWN event. That means, this should
+ // be a pointer that would generate ACTION_DOWN, *and* touch should not already be down.
const bool isStylus = isPointerFromStylus(entry, pointerIndex);
- sp<WindowInfoHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus,
- isDown /*addOutsideTargets*/);
+ auto [newTouchedWindowHandle, outsideTargets] =
+ findTouchedWindowAtLocked(displayId, x, y, isStylus);
+ if (isDown) {
+ targets += outsideTargets;
+ }
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
ALOGD("No new touched window at (%" PRId32 ", %" PRId32 ") in display %" PRId32, x, y,
@@ -2225,7 +2229,7 @@
ALOGW("Dropping injected touch event: %s", (*err).c_str());
outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
newTouchedWindowHandle = nullptr;
- goto Failed;
+ return {};
}
// Figure out whether splitting will be allowed for this window.
@@ -2256,7 +2260,7 @@
ALOGI("Dropping event because there is no touchable window at (%d, %d) on display %d.",
x, y, displayId);
outInjectionResult = InputEventInjectionResult::FAILED;
- goto Failed;
+ return {};
}
for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
@@ -2347,7 +2351,7 @@
"dropped the pointer down event in display %" PRId32 ": %s",
displayId, entry.getDescription().c_str());
outInjectionResult = InputEventInjectionResult::FAILED;
- goto Failed;
+ return {};
}
addDragEventLocked(entry);
@@ -2359,15 +2363,13 @@
const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/);
sp<WindowInfoHandle> oldTouchedWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
- sp<WindowInfoHandle> newTouchedWindowHandle =
- findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus);
+ auto [newTouchedWindowHandle, _] = findTouchedWindowAtLocked(displayId, x, y, isStylus);
// Verify targeted injection.
if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
ALOGW("Dropping injected event: %s", (*err).c_str());
outInjectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
- newTouchedWindowHandle = nullptr;
- goto Failed;
+ return {};
}
// Drop touch events if requested by input feature
@@ -2384,9 +2386,15 @@
newTouchedWindowHandle->getName().c_str(), displayId);
}
// Make a slippery exit from the old window.
- tempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
- InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
- BitSet32(0));
+ BitSet32 pointerIds;
+ const int32_t pointerId = entry.pointerProperties[0].id;
+ pointerIds.markBit(pointerId);
+
+ const TouchedWindow& touchedWindow =
+ tempTouchState.getTouchedWindow(oldTouchedWindowHandle);
+ addWindowTargetLocked(oldTouchedWindowHandle,
+ InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT, pointerIds,
+ touchedWindow.firstDownTimeInTarget, targets);
// Make a slippery entrance into the new window.
if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
@@ -2407,14 +2415,13 @@
targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
}
- BitSet32 pointerIds;
- pointerIds.markBit(entry.pointerProperties[0].id);
tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds,
entry.eventTime);
// Check if the wallpaper window should deliver the corresponding event.
slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle,
- tempTouchState, pointerIds);
+ tempTouchState, pointerId, targets);
+ tempTouchState.removeTouchedPointerFromWindow(pointerId, oldTouchedWindowHandle);
}
}
@@ -2437,7 +2444,11 @@
{
std::vector<TouchedWindow> hoveringWindows =
getHoveringWindowsLocked(oldState, tempTouchState, entry);
- touchedWindows.insert(touchedWindows.end(), hoveringWindows.begin(), hoveringWindows.end());
+ for (const TouchedWindow& touchedWindow : hoveringWindows) {
+ addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
+ touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
+ targets);
+ }
}
// Ensure that we have at least one foreground window or at least one window that cannot be a
// foreground target. If we only have windows that are not receiving foreground touches (e.g. we
@@ -2452,7 +2463,7 @@
ALOGI("Dropping event because there is no touched window on display %d to receive it: %s",
displayId, entry.getDescription().c_str());
outInjectionResult = InputEventInjectionResult::FAILED;
- goto Failed;
+ return {};
}
// Ensure that all touched windows are valid for injection.
@@ -2472,34 +2483,35 @@
"%d:%s",
*entry.injectionState->targetUid, errs.c_str());
outInjectionResult = InputEventInjectionResult::TARGET_MISMATCH;
- goto Failed;
+ return {};
}
}
- // Check whether windows listening for outside touches are owned by the same UID. If it is
- // set the policy flag that we will not reveal coordinate information to this window.
+ // Check whether windows listening for outside touches are owned by the same UID. If the owner
+ // has a different UID, then we will not reveal coordinate information to this window.
if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
sp<WindowInfoHandle> foregroundWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
if (foregroundWindowHandle) {
const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
- for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (touchedWindow.targetFlags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
- sp<WindowInfoHandle> windowInfoHandle = touchedWindow.windowHandle;
- if (windowInfoHandle->getInfo()->ownerUid != foregroundWindowUid) {
- tempTouchState.addOrUpdateWindow(windowInfoHandle,
- InputTarget::Flags::ZERO_COORDS,
- BitSet32(0));
+ for (InputTarget& target : targets) {
+ if (target.flags.test(InputTarget::Flags::DISPATCH_AS_OUTSIDE)) {
+ sp<WindowInfoHandle> targetWindow =
+ getWindowHandleLocked(target.inputChannel->getConnectionToken());
+ if (targetWindow->getInfo()->ownerUid != foregroundWindowUid) {
+ target.flags |= InputTarget::Flags::ZERO_COORDS;
}
}
}
}
}
- // Success! Output targets for everything except hovers.
- if (!isHoverAction) {
- touchedWindows.insert(touchedWindows.end(), tempTouchState.windows.begin(),
- tempTouchState.windows.end());
+ // Success! Output targets from the touch state.
+ tempTouchState.clearWindowsWithoutPointers();
+ for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
+ addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
+ touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
+ targets);
}
outInjectionResult = InputEventInjectionResult::SUCCEEDED;
@@ -2507,7 +2519,6 @@
// in the next iteration.
tempTouchState.filterNonAsIsTouchWindows();
-Failed:
// Update final pieces of touch state if the injector had permission.
if (switchedDevice) {
if (DEBUG_FOCUS) {
@@ -2571,7 +2582,7 @@
mTouchStatesByDisplay.erase(displayId);
}
- return touchedWindows;
+ return targets;
}
void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) {
@@ -2579,9 +2590,8 @@
// have an explicit reason to support it.
constexpr bool isStylus = false;
- const sp<WindowInfoHandle> dropWindow =
- findTouchedWindowAtLocked(displayId, x, y, nullptr /*touchState*/, isStylus,
- false /*addOutsideTargets*/, true /*ignoreDragWindow*/);
+ auto [dropWindow, _] =
+ findTouchedWindowAtLocked(displayId, x, y, isStylus, true /*ignoreDragWindow*/);
if (dropWindow) {
vec2 local = dropWindow->getInfo()->transform.transform(x, y);
sendDropWindowCommandLocked(dropWindow->getToken(), local.x, local.y);
@@ -2637,10 +2647,8 @@
// until we have an explicit reason to support it.
constexpr bool isStylus = false;
- const sp<WindowInfoHandle> hoverWindowHandle =
- findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/,
- isStylus, false /*addOutsideTargets*/,
- true /*ignoreDragWindow*/);
+ auto [hoverWindowHandle, _] = findTouchedWindowAtLocked(entry.displayId, x, y, isStylus,
+ true /*ignoreDragWindow*/);
// enqueue drag exit if needed.
if (hoverWindowHandle != mDragState->dragHoverWindowHandle &&
!haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) {
@@ -3382,6 +3390,10 @@
case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
std::array<uint8_t, 32> hmac = getSignature(keyEntry, *dispatchEntry);
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ LOG(DEBUG) << "Publishing " << *dispatchEntry << " to "
+ << connection->getInputChannelName();
+ }
// Publish the key event.
status = connection->inputPublisher
@@ -3397,6 +3409,10 @@
}
case EventEntry::Type::MOTION: {
+ if (DEBUG_OUTBOUND_EVENT_DETAILS) {
+ LOG(DEBUG) << "Publishing " << *dispatchEntry << " to "
+ << connection->getInputChannelName();
+ }
status = publishMotionEvent(*connection, *dispatchEntry);
break;
}
@@ -4448,6 +4464,9 @@
bool needWake = false;
while (!injectedEntries.empty()) {
+ if (DEBUG_INJECTION) {
+ LOG(DEBUG) << "Injecting " << injectedEntries.front()->getDescription();
+ }
needWake |= enqueueInboundEventLocked(std::move(injectedEntries.front()));
injectedEntries.pop();
}
@@ -4510,7 +4529,8 @@
} // release lock
if (DEBUG_INJECTION) {
- ALOGD("injectInputEvent - Finished with result %d.", injectionResult);
+ LOG(DEBUG) << "injectInputEvent - Finished with result "
+ << ftl::enum_string(injectionResult);
}
return injectionResult;
@@ -4554,7 +4574,8 @@
InjectionState* injectionState = entry.injectionState;
if (injectionState) {
if (DEBUG_INJECTION) {
- ALOGD("Setting input event injection result to %d.", injectionResult);
+ LOG(DEBUG) << "Setting input event injection result to "
+ << ftl::enum_string(injectionResult);
}
if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
@@ -6521,7 +6542,10 @@
void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<WindowInfoHandle>& oldWindowHandle,
const sp<WindowInfoHandle>& newWindowHandle,
- TouchState& state, const BitSet32& pointerIds) {
+ TouchState& state, int32_t pointerId,
+ std::vector<InputTarget>& targets) {
+ BitSet32 pointerIds;
+ pointerIds.markBit(pointerId);
const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
const bool newHasWallpaper = targetFlags.test(InputTarget::Flags::FOREGROUND) &&
@@ -6536,8 +6560,12 @@
}
if (oldWallpaper != nullptr) {
- state.addOrUpdateWindow(oldWallpaper, InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
- BitSet32(0));
+ const TouchedWindow& oldTouchedWindow = state.getTouchedWindow(oldWallpaper);
+ addWindowTargetLocked(oldWallpaper,
+ oldTouchedWindow.targetFlags |
+ InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT,
+ pointerIds, oldTouchedWindow.firstDownTimeInTarget, targets);
+ state.removeTouchedPointerFromWindow(pointerId, oldWallpaper);
}
if (newWallpaper != nullptr) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 91ca2db..81f8de8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -237,9 +237,9 @@
// to transfer focus to a new application.
std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
- sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
- int32_t displayId, int32_t x, int32_t y, TouchState* touchState, bool isStylus = false,
- bool addOutsideTargets = false, bool ignoreDragWindow = false) const REQUIRES(mLock);
+ std::pair<sp<android::gui::WindowInfoHandle>, std::vector<InputTarget>>
+ findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, bool isStylus = false,
+ bool ignoreDragWindow = false) const REQUIRES(mLock);
std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
int32_t displayId, int32_t x, int32_t y, bool isStylus) const REQUIRES(mLock);
@@ -543,7 +543,7 @@
sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked(
nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime,
android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
- std::vector<TouchedWindow> findTouchedWindowTargetsLocked(
+ std::vector<InputTarget> findTouchedWindowTargetsLocked(
nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
std::vector<Monitor> selectResponsiveMonitorsLocked(
@@ -697,7 +697,8 @@
void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
const sp<android::gui::WindowInfoHandle>& newWindowHandle,
- TouchState& state, const BitSet32& pointerIds) REQUIRES(mLock);
+ TouchState& state, int32_t pointerId, std::vector<InputTarget>& targets)
+ REQUIRES(mLock);
void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags,
ftl::Flags<InputTarget::Flags> newTargetFlags,
const sp<android::gui::WindowInfoHandle> fromWindowHandle,
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index f120fc9..ad37d02 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -37,6 +37,16 @@
}
}
+void TouchState::removeTouchedPointerFromWindow(
+ int32_t pointerId, const sp<android::gui::WindowInfoHandle>& windowHandle) {
+ for (TouchedWindow& touchedWindow : windows) {
+ if (touchedWindow.windowHandle == windowHandle) {
+ touchedWindow.pointerIds.clearBit(pointerId);
+ return;
+ }
+ }
+}
+
void TouchState::clearHoveringPointers() {
for (TouchedWindow& touchedWindow : windows) {
touchedWindow.clearHoveringPointers();
@@ -70,7 +80,6 @@
return;
}
}
-
TouchedWindow touchedWindow;
touchedWindow.windowHandle = windowHandle;
touchedWindow.targetFlags = targetFlags;
@@ -175,6 +184,13 @@
return nullptr;
}
+const TouchedWindow& TouchState::getTouchedWindow(const sp<WindowInfoHandle>& windowHandle) const {
+ auto it = std::find_if(windows.begin(), windows.end(),
+ [&](const TouchedWindow& w) { return w.windowHandle == windowHandle; });
+ LOG_ALWAYS_FATAL_IF(it == windows.end(), "Could not find %s", windowHandle->getName().c_str());
+ return *it;
+}
+
bool TouchState::isDown() const {
return std::any_of(windows.begin(), windows.end(),
[](const TouchedWindow& window) { return !window.pointerIds.isEmpty(); });
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index b75e6ef..4025db8 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -43,6 +43,8 @@
void clearWindowsWithoutPointers();
void removeTouchedPointer(int32_t pointerId);
+ void removeTouchedPointerFromWindow(int32_t pointerId,
+ const sp<android::gui::WindowInfoHandle>& windowHandle);
void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds,
std::optional<nsecs_t> eventTime = std::nullopt);
@@ -62,6 +64,8 @@
sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const;
bool isSlippery() const;
sp<android::gui::WindowInfoHandle> getWallpaperWindow() const;
+ const TouchedWindow& getTouchedWindow(
+ const sp<android::gui::WindowInfoHandle>& windowHandle) const;
// Whether any of the windows are currently being touched
bool isDown() const;
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index d55ab28..2173117 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -161,7 +161,7 @@
struct InputReaderConfiguration {
// Describes changes that have occurred.
enum {
- // The pointer speed changed.
+ // The mouse pointer speed changed.
CHANGE_POINTER_SPEED = 1 << 0,
// The pointer gesture control changed.
@@ -200,6 +200,9 @@
// The stylus button reporting configurations has changed.
CHANGE_STYLUS_BUTTON_REPORTING = 1 << 12,
+ // The touchpad settings changed.
+ CHANGE_TOUCHPAD_SETTINGS = 1 << 13,
+
// All devices must be reopened.
CHANGE_MUST_REOPEN = 1 << 31,
};
@@ -309,6 +312,20 @@
// The latest request to enable or disable Pointer Capture.
PointerCaptureRequest pointerCaptureRequest;
+ // The touchpad pointer speed, as a number from -7 (slowest) to 7 (fastest).
+ int32_t touchpadPointerSpeed;
+
+ // True to invert the touchpad scrolling direction, so that moving two fingers downwards on the
+ // touchpad scrolls the content upwards.
+ bool touchpadNaturalScrollingEnabled;
+
+ // True to enable tap-to-click on touchpads.
+ bool touchpadTapToClickEnabled;
+
+ // 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;
+
// The set of currently disabled input devices.
std::set<int32_t> disabledDevices;
@@ -337,6 +354,10 @@
pointerGestureZoomSpeedRatio(0.3f),
showTouches(false),
pointerCaptureRequest(),
+ touchpadPointerSpeed(0),
+ touchpadNaturalScrollingEnabled(true),
+ touchpadTapToClickEnabled(true),
+ touchpadRightClickZoneEnabled(false),
stylusButtonMotionEventsEnabled(true) {}
static std::string changesToString(uint32_t changes);
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 9bd50f7..c598c0a 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -209,9 +209,15 @@
// Touchscreens and touchpad devices.
static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY =
- sysprop::InputProperties::enable_touchpad_gestures_library().value_or(false);
+ sysprop::InputProperties::enable_touchpad_gestures_library().value_or(true);
+ // TODO(b/246587538): Fix the new touchpad stack for Sony DualShock 4 (5c4, 9cc) and DualSense
+ // (ce6) touchpads, or at least load this setting from the IDC file.
+ const InputDeviceIdentifier identifier = contextPtr->getDeviceIdentifier();
+ const bool isSonyGamepadTouchpad = identifier.vendor == 0x054c &&
+ (identifier.product == 0x05c4 || identifier.product == 0x09cc ||
+ identifier.product == 0x0ce6);
if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
- classes.test(InputDeviceClass::TOUCH_MT)) {
+ classes.test(InputDeviceClass::TOUCH_MT) && !isSonyGamepadTouchpad) {
mappers.push_back(std::make_unique<TouchpadInputMapper>(*contextPtr));
} else if (classes.test(InputDeviceClass::TOUCH_MT)) {
mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index ddddca2..0c57628 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -884,8 +884,6 @@
mDeviceMode = DeviceMode::POINTER;
if (hasStylus()) {
mSource |= AINPUT_SOURCE_STYLUS;
- } else {
- mSource |= AINPUT_SOURCE_TOUCHPAD;
}
} else if (isTouchScreen()) {
mSource = AINPUT_SOURCE_TOUCHSCREEN;
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index b6313a1..9f32311 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -146,6 +146,18 @@
}
mGestureConverter.setOrientation(orientation);
}
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCHPAD_SETTINGS)) {
+ // TODO(b/265798483): load an Android-specific acceleration curve instead of mapping to one
+ // of five ChromeOS curves.
+ const int pointerSensitivity = (config->touchpadPointerSpeed + 7) / 3 + 1;
+ mPropertyProvider.getProperty("Pointer Sensitivity").setIntValues({pointerSensitivity});
+ mPropertyProvider.getProperty("Invert Scrolling")
+ .setBoolValues({config->touchpadNaturalScrollingEnabled});
+ mPropertyProvider.getProperty("Tap Enable")
+ .setBoolValues({config->touchpadTapToClickEnabled});
+ mPropertyProvider.getProperty("Button Right Click Zone Enable")
+ .setBoolValues({config->touchpadRightClickZoneEnabled});
+ }
return {};
}
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 561b1f8..d636d44 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -219,11 +219,11 @@
float deltaY = gesture.details.scroll.dy;
rotateDelta(mOrientation, &deltaX, &deltaY);
- coords.setAxisValue(AMOTION_EVENT_AXIS_X, coords.getAxisValue(AMOTION_EVENT_AXIS_X) - deltaX);
- coords.setAxisValue(AMOTION_EVENT_AXIS_Y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y) - deltaY);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, coords.getAxisValue(AMOTION_EVENT_AXIS_X) + deltaX);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y) + deltaY);
// TODO(b/262876643): set AXIS_GESTURE_{X,Y}_OFFSET.
- coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, gesture.details.scroll.dx);
- coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, gesture.details.scroll.dy);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, -gesture.details.scroll.dx);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, -gesture.details.scroll.dy);
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 36a39bb..9c624ba 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -244,7 +244,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 10);
+ Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture);
ASSERT_EQ(2u, args.size());
@@ -261,7 +261,7 @@
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
- Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 5);
+ Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
ASSERT_EQ(1u, args.size());
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
@@ -289,7 +289,7 @@
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setOrientation(ui::ROTATION_90);
- Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 10);
+ Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args = converter.handleGesture(downTime, READ_TIME, startGesture);
ASSERT_EQ(2u, args.size());
@@ -306,7 +306,7 @@
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
- Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 5);
+ Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
ASSERT_EQ(1u, args.size());
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
@@ -332,10 +332,10 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 10);
+ Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
- Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 5);
+ Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, continueGesture);
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 95d35f4..feda191 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -5036,7 +5036,7 @@
prepareAxes(POSITION);
SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
- ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
}
TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchScreen_ReturnsTouchScreen) {
@@ -8865,8 +8865,8 @@
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
- // Check source is a touchpad that would obtain the PointerController.
- ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
+ // Check source is mouse that would obtain the PointerController.
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
NotifyMotionArgs motionArgs;
processPosition(mapper, 100, 100);
@@ -9936,11 +9936,11 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
- // A non captured touchpad should have a mouse and touchpad source.
+ // non captured touchpad should be a mouse source
mFakePolicy->setPointerCapture(false);
configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
}
TEST_F(MultiTouchInputMapperTest, Process_UnCapturedTouchpadPointer) {
@@ -9999,10 +9999,10 @@
mFakePolicy->setPointerCapture(false);
MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
- // An uncaptured touchpad should be a pointer device, with additional touchpad source.
- ASSERT_EQ(AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
+ // uncaptured touchpad should be a pointer device
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
- // A captured touchpad should just have a touchpad source.
+ // captured touchpad should be a touchpad device
mFakePolicy->setPointerCapture(true);
configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
diff --git a/services/sensorservice/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp
index 9b03344..b7aecdf 100644
--- a/services/sensorservice/aidl/SensorManager.cpp
+++ b/services/sensorservice/aidl/SensorManager.cpp
@@ -185,12 +185,8 @@
}
::android::SensorManager& SensorManagerAidl::getInternalManager() {
- std::lock_guard<std::mutex> lock(mInternalManagerMutex);
- if (mInternalManager == nullptr) {
- mInternalManager = &::android::SensorManager::getInstanceForPackage(
- String16(ISensorManager::descriptor));
- }
- return *mInternalManager;
+ return ::android::SensorManager::getInstanceForPackage(
+ String16(ISensorManager::descriptor));
}
/* One global looper for all event queues created from this SensorManager. */
diff --git a/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h
index c77ee88..83496f6 100644
--- a/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h
+++ b/services/sensorservice/aidl/include/sensorserviceaidl/SensorManagerAidl.h
@@ -57,8 +57,6 @@
::android::SensorManager& getInternalManager();
sp<Looper> getLooper();
- std::mutex mInternalManagerMutex;
- ::android::SensorManager* mInternalManager = nullptr; // does not own
sp<Looper> mLooper;
volatile bool mStopThread;
diff --git a/services/sensorservice/hidl/SensorManager.cpp b/services/sensorservice/hidl/SensorManager.cpp
index 9380600..f04712c 100644
--- a/services/sensorservice/hidl/SensorManager.cpp
+++ b/services/sensorservice/hidl/SensorManager.cpp
@@ -193,12 +193,8 @@
}
::android::SensorManager& SensorManager::getInternalManager() {
- std::lock_guard<std::mutex> lock(mInternalManagerMutex);
- if (mInternalManager == nullptr) {
- mInternalManager = &::android::SensorManager::getInstanceForPackage(
- String16(ISensorManager::descriptor));
- }
- return *mInternalManager;
+ return ::android::SensorManager::getInstanceForPackage(
+ String16(ISensorManager::descriptor));
}
Return<void> SensorManager::createEventQueue(
diff --git a/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h b/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h
index 8d7a05b..1b085ac 100644
--- a/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h
+++ b/services/sensorservice/hidl/include/sensorservicehidl/SensorManager.h
@@ -58,8 +58,6 @@
::android::SensorManager& getInternalManager();
sp<Looper> getLooper();
- std::mutex mInternalManagerMutex;
- ::android::SensorManager* mInternalManager = nullptr; // does not own
sp<Looper> mLooper;
volatile bool mStopThread;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index ad98e93..5bb2497 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -209,6 +209,9 @@
// The dimming flag
bool dimmingEnabled{true};
+ float currentSdrHdrRatio = 1.f;
+ float desiredSdrHdrRatio = 1.f;
+
virtual ~LayerFECompositionState();
// Debugging
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index a405c4d..531b659 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -121,6 +121,10 @@
dumpVal(out, "dataspace", toString(dataspace), dataspace);
dumpVal(out, "hdr metadata types", hdrMetadata.validTypes);
dumpVal(out, "dimming enabled", dimmingEnabled);
+ if (currentSdrHdrRatio > 1.01f || desiredSdrHdrRatio > 1.01f) {
+ dumpVal(out, "current sdr/hdr ratio", currentSdrHdrRatio);
+ dumpVal(out, "desired sdr/hdr ratio", desiredSdrHdrRatio);
+ }
dumpVal(out, "colorTransform", colorTransform);
out.append("\n");
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 60a2c83..6b69ce7 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -340,10 +340,17 @@
state.dimmingRatio = 1.f;
state.whitePointNits = getOutput().getState().displayBrightnessNits;
} else {
- state.dimmingRatio = std::clamp(getOutput().getState().sdrWhitePointNits /
- getOutput().getState().displayBrightnessNits,
- 0.f, 1.f);
- state.whitePointNits = getOutput().getState().sdrWhitePointNits;
+ float layerBrightnessNits = getOutput().getState().sdrWhitePointNits;
+ // RANGE_EXTENDED can "self-promote" to HDR, but is still rendered for a particular
+ // range that we may need to re-adjust to the current display conditions
+ if ((state.dataspace & HAL_DATASPACE_RANGE_MASK) == HAL_DATASPACE_RANGE_EXTENDED &&
+ layerFEState->currentSdrHdrRatio > 1.01f) {
+ layerBrightnessNits *= layerFEState->currentSdrHdrRatio;
+ }
+ state.dimmingRatio =
+ std::clamp(layerBrightnessNits / getOutput().getState().displayBrightnessNits, 0.f,
+ 1.f);
+ state.whitePointNits = layerBrightnessNits;
}
// These are evaluated every frame as they can potentially change at any
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 2b6a519..c61f7d8 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -166,7 +166,8 @@
.receivesInput = receivesInput(),
.isSecure = isSecure(),
.isPrimary = isPrimary(),
- .rotationFlags = ui::Transform::toRotationFlags(mOrientation)};
+ .rotationFlags = ui::Transform::toRotationFlags(mOrientation),
+ .transformHint = getTransformHint()};
}
void DisplayDevice::setPowerMode(hal::PowerMode mode) {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 27a099c..925f111 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -1095,6 +1095,12 @@
}
void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const {
+ if (mSurfaceFrames.empty()) {
+ // We don't want to trace display frames without any surface frames updates as this cannot
+ // be janky
+ return;
+ }
+
if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID) {
// DisplayFrame should not have an invalid token.
ALOGE("Cannot trace DisplayFrame with invalid token");
diff --git a/services/surfaceflinger/FrontEnd/DisplayInfo.h b/services/surfaceflinger/FrontEnd/DisplayInfo.h
index 0c7b24a..6b9d7a2 100644
--- a/services/surfaceflinger/FrontEnd/DisplayInfo.h
+++ b/services/surfaceflinger/FrontEnd/DisplayInfo.h
@@ -16,6 +16,8 @@
#pragma once
+#include <sstream>
+
#include <gui/DisplayInfo.h>
namespace android::surfaceflinger::frontend {
@@ -29,6 +31,16 @@
// TODO(b/238781169) can eliminate once sPrimaryDisplayRotationFlags is removed.
bool isPrimary;
ui::Transform::RotationFlags rotationFlags;
+ ui::Transform::RotationFlags transformHint;
+ std::string getDebugString() const {
+ std::stringstream debug;
+ debug << "DisplayInfo {displayId=" << info.displayId << " lw=" << info.logicalWidth
+ << " lh=" << info.logicalHeight << " transform={" << transform.dsdx() << " ,"
+ << transform.dsdy() << " ," << transform.dtdx() << " ," << transform.dtdy()
+ << "} isSecure=" << isSecure << " isPrimary=" << isPrimary
+ << " rotationFlags=" << rotationFlags << " transformHint=" << transformHint << "}";
+ return debug.str();
+ }
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
index 7b5a157..9d2aaab 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -18,10 +18,12 @@
#include <binder/Binder.h>
#include <gui/LayerMetadata.h>
+#include <ui/LayerStack.h>
#include <utils/StrongPointer.h>
#include <cstdint>
#include <limits>
#include <optional>
+
constexpr uint32_t UNASSIGNED_LAYER_ID = std::numeric_limits<uint32_t>::max();
namespace android {
@@ -51,6 +53,7 @@
bool addToRoot = true;
wp<IBinder> parentHandle = nullptr;
wp<IBinder> mirrorLayerHandle = nullptr;
+ ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
};
} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index 514a642..a4fac1c 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -28,8 +28,8 @@
const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& rhs) {
auto lhsLayer = lhs.first->getLayer();
auto rhsLayer = rhs.first->getLayer();
- if (lhsLayer->layerStack != rhsLayer->layerStack) {
- return lhsLayer->layerStack.id < rhsLayer->layerStack.id;
+ if (lhsLayer->layerStack.id != rhsLayer->layerStack.id) {
+ return lhsLayer->layerStack.id > rhsLayer->layerStack.id;
}
if (lhsLayer->z != rhsLayer->z) {
return lhsLayer->z < rhsLayer->z;
@@ -75,11 +75,11 @@
for (auto it = mChildren.begin(); it < mChildren.end(); it++) {
auto& [child, childVariant] = *it;
if (traverseThisLayer && child->getLayer()->z >= 0) {
+ traverseThisLayer = false;
bool breakTraversal = !visitor(*this, traversalPath);
if (breakTraversal) {
return;
}
- traverseThisLayer = false;
}
if (childVariant == LayerHierarchy::Variant::Detached) {
continue;
@@ -252,8 +252,8 @@
attachToParent(hierarchy);
attachToRelativeParent(hierarchy);
- if (layer->mirrorId != UNASSIGNED_LAYER_ID) {
- LayerHierarchy* mirror = getHierarchyFromId(layer->mirrorId);
+ for (uint32_t mirrorId : layer->mirrorIds) {
+ LayerHierarchy* mirror = getHierarchyFromId(mirrorId);
hierarchy->addChild(mirror, LayerHierarchy::Variant::Mirror);
}
}
@@ -292,14 +292,14 @@
auto it = hierarchy->mChildren.begin();
while (it != hierarchy->mChildren.end()) {
if (it->second == LayerHierarchy::Variant::Mirror) {
- hierarchy->mChildren.erase(it);
- break;
+ it = hierarchy->mChildren.erase(it);
+ } else {
+ it++;
}
- it++;
}
- if (layer->mirrorId != UNASSIGNED_LAYER_ID) {
- hierarchy->addChild(getHierarchyFromId(layer->mirrorId), LayerHierarchy::Variant::Mirror);
+ for (uint32_t mirrorId : layer->mirrorIds) {
+ hierarchy->addChild(getHierarchyFromId(mirrorId), LayerHierarchy::Variant::Mirror);
}
}
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index 8cdc240..ca8d301 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -41,11 +41,13 @@
// states.
class LayerHierarchy {
public:
- enum Variant {
+ enum Variant : uint32_t {
Attached,
Detached,
Relative,
Mirror,
+ ftl_first = Attached,
+ ftl_last = Mirror,
};
// Represents a unique path to a node.
struct TraversalPath {
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 5514c06..547a852 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -44,9 +44,28 @@
layer.parentId = linkLayer(layer.parentId, layer.id);
layer.relativeParentId = linkLayer(layer.relativeParentId, layer.id);
- layer.mirrorId = linkLayer(layer.mirrorId, layer.id);
+ if (layer.layerStackToMirror != ui::INVALID_LAYER_STACK) {
+ // if this layer is mirroring a display, then walk though all the existing root layers
+ // for the layer stack and add them as children to be mirrored.
+ mDisplayMirroringLayers.emplace_back(layer.id);
+ for (auto& rootLayer : mLayers) {
+ if (rootLayer->isRoot() && rootLayer->layerStack == layer.layerStackToMirror) {
+ layer.mirrorIds.emplace_back(rootLayer->id);
+ linkLayer(rootLayer->id, layer.id);
+ }
+ }
+ } else {
+ // Check if we are mirroring a single layer, and if so add it to the list of children
+ // to be mirrored.
+ layer.layerIdToMirror = linkLayer(layer.layerIdToMirror, layer.id);
+ if (layer.layerIdToMirror != UNASSIGNED_LAYER_ID) {
+ layer.mirrorIds.emplace_back(layer.layerIdToMirror);
+ }
+ }
layer.touchCropId = linkLayer(layer.touchCropId, layer.id);
-
+ if (layer.isRoot()) {
+ updateDisplayMirrorLayers(layer);
+ }
mLayers.emplace_back(std::move(newLayer));
}
}
@@ -85,7 +104,14 @@
layer.parentId = unlinkLayer(layer.parentId, layer.id);
layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id);
- layer.mirrorId = unlinkLayer(layer.mirrorId, layer.id);
+ if (layer.layerStackToMirror != ui::INVALID_LAYER_STACK) {
+ layer.mirrorIds = unlinkLayers(layer.mirrorIds, layer.id);
+ swapErase(mDisplayMirroringLayers, layer.id);
+ } else {
+ layer.layerIdToMirror = unlinkLayer(layer.layerIdToMirror, layer.id);
+ layer.mirrorIds.clear();
+ }
+
layer.touchCropId = unlinkLayer(layer.touchCropId, layer.id);
auto& references = it->second.references;
@@ -106,8 +132,8 @@
if (linkedLayer->relativeParentId == layer.id) {
linkedLayer->relativeParentId = UNASSIGNED_LAYER_ID;
}
- if (linkedLayer->mirrorId == layer.id) {
- linkedLayer->mirrorId = UNASSIGNED_LAYER_ID;
+ if (swapErase(linkedLayer->mirrorIds, layer.id)) {
+ linkedLayer->changes |= RequestedLayerState::Changes::Mirror;
}
if (linkedLayer->touchCropId == layer.id) {
linkedLayer->touchCropId = UNASSIGNED_LAYER_ID;
@@ -200,6 +226,12 @@
if (oldParentId != layer->parentId) {
unlinkLayer(oldParentId, layer->id);
layer->parentId = linkLayer(layer->parentId, layer->id);
+ if (oldParentId == UNASSIGNED_LAYER_ID) {
+ updateDisplayMirrorLayers(*layer);
+ }
+ }
+ if (layer->what & layer_state_t::eLayerStackChanged && layer->isRoot()) {
+ updateDisplayMirrorLayers(*layer);
}
if (oldRelativeParentId != layer->relativeParentId) {
unlinkLayer(oldRelativeParentId, layer->id);
@@ -308,6 +340,14 @@
return UNASSIGNED_LAYER_ID;
}
+std::vector<uint32_t> LayerLifecycleManager::unlinkLayers(const std::vector<uint32_t>& layerIds,
+ uint32_t linkedLayer) {
+ for (uint32_t layerId : layerIds) {
+ unlinkLayer(layerId, linkedLayer);
+ }
+ return {};
+}
+
std::string LayerLifecycleManager::References::getDebugString() const {
std::string debugInfo = owner.name + "[" + std::to_string(owner.id) + "] refs:";
std::for_each(references.begin(), references.end(),
@@ -329,4 +369,30 @@
mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
}
+// Some layers mirror the entire display stack. Since we don't have a single root layer per display
+// we have to track all these layers and update what they mirror when the list of root layers
+// on a display changes. This function walks through the list of display mirroring layers
+// and updates its list of layers that its mirroring. This function should be called when a new
+// root layer is added, removed or moved to another display.
+void LayerLifecycleManager::updateDisplayMirrorLayers(RequestedLayerState& rootLayer) {
+ for (uint32_t mirrorLayerId : mDisplayMirroringLayers) {
+ RequestedLayerState* mirrorLayer = getLayerFromId(mirrorLayerId);
+ bool canBeMirrored =
+ rootLayer.isRoot() && rootLayer.layerStack == mirrorLayer->layerStackToMirror;
+ bool currentlyMirrored =
+ std::find(mirrorLayer->mirrorIds.begin(), mirrorLayer->mirrorIds.end(),
+ rootLayer.id) != mirrorLayer->mirrorIds.end();
+
+ if (canBeMirrored && !currentlyMirrored) {
+ mirrorLayer->mirrorIds.emplace_back(rootLayer.id);
+ linkLayer(rootLayer.id, mirrorLayer->id);
+ mirrorLayer->changes |= RequestedLayerState::Changes::Mirror;
+ } else if (!canBeMirrored && currentlyMirrored) {
+ swapErase(mirrorLayer->mirrorIds, rootLayer.id);
+ unlinkLayer(rootLayer.id, mirrorLayer->id);
+ mirrorLayer->changes |= RequestedLayerState::Changes::Mirror;
+ }
+ }
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index 63a7afc..25d27ee 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -80,6 +80,9 @@
std::vector<uint32_t>* getLinkedLayersFromId(uint32_t);
uint32_t linkLayer(uint32_t layerId, uint32_t layerToLink);
uint32_t unlinkLayer(uint32_t layerId, uint32_t linkedLayer);
+ std::vector<uint32_t> unlinkLayers(const std::vector<uint32_t>& layerIds, uint32_t linkedLayer);
+
+ void updateDisplayMirrorLayers(RequestedLayerState& rootLayer);
struct References {
// Lifetime tied to mLayers
@@ -90,6 +93,8 @@
std::unordered_map<uint32_t, References> mIdToLayer;
// Listeners are invoked once changes are committed.
std::vector<std::shared_ptr<ILifecycleListener>> mListeners;
+ // Layers that mirror a display stack (see updateDisplayMirrorLayers)
+ std::vector<uint32_t> mDisplayMirroringLayers;
// Aggregation of changes since last commit.
ftl::Flags<RequestedLayerState::Changes> mGlobalChanges;
diff --git a/services/surfaceflinger/FrontEnd/LayerLog.h b/services/surfaceflinger/FrontEnd/LayerLog.h
new file mode 100644
index 0000000..47e1e30
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerLog.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 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
+
+// Uncomment to trace layer updates for a single layer
+// #define LOG_LAYER 1
+
+#ifdef LOG_LAYER
+#define LLOGV(LAYER_ID, x, ...) \
+ ALOGV_IF(((LAYER_ID) == LOG_LAYER), "[%d] %s " x, LOG_LAYER, __func__, ##__VA_ARGS__);
+#else
+#define LLOGV(LAYER_ID, x, ...) ALOGV("[%d] %s " x, (LAYER_ID), __func__, ##__VA_ARGS__);
+#endif
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index d483a99..4e69565 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -61,7 +61,7 @@
}
bool LayerSnapshot::hasBufferOrSidebandStream() const {
- return ((sidebandStream != nullptr) || (buffer != nullptr));
+ return ((sidebandStream != nullptr) || (externalTexture != nullptr));
}
bool LayerSnapshot::drawShadows() const {
@@ -99,7 +99,7 @@
// If the buffer has no alpha channel, then we are opaque
if (hasBufferOrSidebandStream() &&
- isOpaqueFormat(buffer ? buffer->getPixelFormat() : PIXEL_FORMAT_NONE)) {
+ isOpaqueFormat(externalTexture ? externalTexture->getPixelFormat() : PIXEL_FORMAT_NONE)) {
return true;
}
@@ -108,14 +108,14 @@
}
bool LayerSnapshot::isHiddenByPolicy() const {
- if (CC_UNLIKELY(invalidTransform)) {
- ALOGW("Hide layer %s because it has invalid transformation.", name.c_str());
- return true;
- }
- return isHiddenByPolicyFromParent || isHiddenByPolicyFromRelativeParent;
+ return invalidTransform || isHiddenByPolicyFromParent || isHiddenByPolicyFromRelativeParent;
}
bool LayerSnapshot::getIsVisible() const {
+ if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) {
+ return false;
+ }
+
if (!hasSomethingToDraw()) {
return false;
}
@@ -128,19 +128,23 @@
}
std::string LayerSnapshot::getIsVisibleReason() const {
- if (!hasSomethingToDraw()) {
- return "!hasSomethingToDraw";
- }
+ // not visible
+ if (handleSkipScreenshotFlag & outputFilter.toInternalDisplay) return "eLayerSkipScreenshot";
+ if (!hasSomethingToDraw()) return "!hasSomethingToDraw";
+ if (invalidTransform) return "invalidTransform";
+ if (isHiddenByPolicyFromParent) return "hidden by parent or layer flag";
+ if (isHiddenByPolicyFromRelativeParent) return "hidden by relative parent";
+ if (color.a == 0.0f && !hasBlur()) return "alpha = 0 and no blur";
- if (isHiddenByPolicy()) {
- return "isHiddenByPolicy";
- }
-
- if (color.a > 0.0f || hasBlur()) {
- return "";
- }
-
- return "alpha = 0 and !hasBlur";
+ // visible
+ std::stringstream reason;
+ if (sidebandStream != nullptr) reason << " sidebandStream";
+ if (externalTexture != nullptr) reason << " buffer";
+ if (fillsColor() || color.a > 0.0f) reason << " color{" << color << "}";
+ if (drawShadows()) reason << " shadowSettings.length=" << shadowSettings.length;
+ if (backgroundBlurRadius > 0) reason << " backgroundBlurRadius=" << backgroundBlurRadius;
+ if (blurRegions.size() > 0) reason << " blurRegions.size()=" << blurRegions.size();
+ return reason.str();
}
bool LayerSnapshot::canReceiveInput() const {
@@ -152,11 +156,16 @@
return transformDet != 0 && !isinf(transformDet) && !isnan(transformDet);
}
+bool LayerSnapshot::hasInputInfo() const {
+ return inputInfo.token != nullptr ||
+ inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+}
+
std::string LayerSnapshot::getDebugString() const {
- return "Snapshot(" + base::StringPrintf("%p", this) + "){" + path.toString() + name +
- " isHidden=" + std::to_string(isHiddenByPolicyFromParent) +
- " isHiddenRelative=" + std::to_string(isHiddenByPolicyFromRelativeParent) +
- " isVisible=" + std::to_string(isVisible) + " " + getIsVisibleReason() + "}";
+ std::stringstream debug;
+ debug << "Snapshot{" << path.toString() << name << " isVisible=" << isVisible << " {"
+ << getIsVisibleReason() << "} changes=" << changes.string() << "}";
+ return debug.str();
}
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index d14bd3a..159410f 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -20,6 +20,7 @@
#include <renderengine/LayerSettings.h>
#include "LayerHierarchy.h"
#include "RequestedLayerState.h"
+#include "Scheduler/LayerInfo.h"
#include "android-base/stringprintf.h"
namespace android::surfaceflinger::frontend {
@@ -39,6 +40,10 @@
}
};
+struct ChildState {
+ bool hasValidFrameRate = false;
+};
+
// LayerSnapshot stores Layer state used by CompositionEngine and RenderEngine. Composition
// Engine uses a pointer to LayerSnapshot (as LayerFECompositionState*) and the LayerSettings
// passed to Render Engine are created using properties stored on this struct.
@@ -59,6 +64,7 @@
bool layerOpaqueFlagSet;
RoundedCornerState roundedCorner;
FloatRect transformedBounds;
+ Rect transformedBoundsWithoutTransparentRegion;
renderengine::ShadowSettings shadowSettings;
bool premultipliedAlpha;
bool isHdrY410;
@@ -75,6 +81,11 @@
ui::Transform localTransform;
gui::DropInputMode dropInputMode;
bool isTrustedOverlay;
+ gui::GameMode gameMode;
+ scheduler::LayerInfo::FrameRate frameRate;
+ ui::Transform::RotationFlags fixedTransformHint;
+ bool handleSkipScreenshotFlag = false;
+ ChildState childState;
static bool isOpaqueFormat(PixelFormat format);
static bool isTransformValid(const ui::Transform& t);
@@ -91,6 +102,7 @@
bool isHiddenByPolicy() const;
std::string getDebugString() const;
std::string getIsVisibleReason() const;
+ bool hasInputInfo() const;
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index bff12d7..d0ffe61 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -24,6 +24,8 @@
#include <numeric>
#include "DisplayHardware/HWC2.h"
#include "DisplayHardware/Hal.h"
+#include "LayerLog.h"
+#include "TimeStats/TimeStats.h"
#include "ftl/small_map.h"
namespace android::surfaceflinger::frontend {
@@ -250,6 +252,64 @@
return blendMode;
}
+void updateSurfaceDamage(const RequestedLayerState& requested, bool hasReadyFrame,
+ bool forceFullDamage, Region& outSurfaceDamageRegion) {
+ if (!hasReadyFrame) {
+ outSurfaceDamageRegion.clear();
+ return;
+ }
+ if (forceFullDamage) {
+ outSurfaceDamageRegion = Region::INVALID_REGION;
+ } else {
+ outSurfaceDamageRegion = requested.surfaceDamageRegion;
+ }
+}
+
+void updateVisibility(LayerSnapshot& snapshot) {
+ snapshot.isVisible = snapshot.getIsVisible();
+
+ // TODO(b/238781169) we are ignoring this compat for now, since we will have
+ // to remove any optimization based on visibility.
+
+ // For compatibility reasons we let layers which can receive input
+ // receive input before they have actually submitted a buffer. Because
+ // of this we use canReceiveInput instead of isVisible to check the
+ // policy-visibility, ignoring the buffer state. However for layers with
+ // hasInputInfo()==false we can use the real visibility state.
+ // We are just using these layers for occlusion detection in
+ // InputDispatcher, and obviously if they aren't visible they can't occlude
+ // anything.
+ const bool visible =
+ (snapshot.inputInfo.token != nullptr) ? snapshot.canReceiveInput() : snapshot.isVisible;
+ snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visible);
+}
+
+bool needsInputInfo(const LayerSnapshot& snapshot, const RequestedLayerState& requested) {
+ if (requested.potentialCursor) {
+ return false;
+ }
+
+ if (snapshot.inputInfo.token != nullptr) {
+ return true;
+ }
+
+ if (snapshot.hasBufferOrSidebandStream()) {
+ return true;
+ }
+
+ return requested.windowInfoHandle &&
+ requested.windowInfoHandle->getInfo()->inputConfig.test(
+ gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+}
+
+void clearChanges(LayerSnapshot& snapshot) {
+ snapshot.changes.clear();
+ snapshot.contentDirty = false;
+ snapshot.hasReadyFrame = false;
+ snapshot.sidebandStreamHasFrame = false;
+ snapshot.surfaceDamage.clear();
+}
+
} // namespace
LayerSnapshot LayerSnapshotBuilder::getRootSnapshot() {
@@ -274,6 +334,9 @@
snapshot.inputInfo.touchOcclusionMode = gui::TouchOcclusionMode::BLOCK_UNTRUSTED;
snapshot.dropInputMode = gui::DropInputMode::NONE;
snapshot.isTrustedOverlay = false;
+ snapshot.gameMode = gui::GameMode::Unsupported;
+ snapshot.frameRate = {};
+ snapshot.fixedTransformHint = ui::Transform::ROT_INVALID;
return snapshot;
}
@@ -285,16 +348,15 @@
}
bool LayerSnapshotBuilder::tryFastUpdate(const Args& args) {
- if (args.forceUpdate) {
- // force update requested, so skip the fast path
+ if (args.forceUpdate || args.displayChanges) {
+ // force update requested, or we have display changes, so skip the fast path
return false;
}
if (args.layerLifecycleManager.getGlobalChanges().get() == 0) {
// there are no changes, so just clear the change flags from before.
for (auto& snapshot : mSnapshots) {
- snapshot->changes.clear();
- snapshot->contentDirty = false;
+ clearChanges(*snapshot);
}
return true;
}
@@ -320,14 +382,14 @@
// Walk through the snapshots, clearing previous change flags and updating the snapshots
// if needed.
for (auto& snapshot : mSnapshots) {
- snapshot->changes.clear();
- snapshot->contentDirty = false;
+ clearChanges(*snapshot);
auto it = layersWithChanges.find(snapshot->path.id);
if (it != layersWithChanges.end()) {
ALOGV("%s fast path snapshot changes = %s", __func__,
mRootSnapshot.changes.string().c_str());
LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT;
- updateSnapshot(*snapshot, args, *it->second, mRootSnapshot, root);
+ updateSnapshot(*snapshot, args, *it->second, mRootSnapshot, root,
+ /*newSnapshot=*/false);
}
}
return true;
@@ -335,7 +397,6 @@
void LayerSnapshotBuilder::updateSnapshots(const Args& args) {
ATRACE_NAME("UpdateSnapshots");
- ALOGV("%s updateSnapshots force = %s", __func__, std::to_string(args.forceUpdate).c_str());
if (args.forceUpdate || args.displayChanges) {
mRootSnapshot.geomLayerBounds = getMaxDisplayBounds(args.displays);
}
@@ -352,7 +413,7 @@
}
sortSnapshotsByZ(args);
- mRootSnapshot.changes.clear();
+ clearChanges(mRootSnapshot);
// Destroy unreachable snapshots
if (args.layerLifecycleManager.getDestroyedLayers().empty()) {
@@ -372,6 +433,7 @@
}
mIdToSnapshot.erase(traversalPath);
+ mSnapshots.back()->globalZ = it->get()->globalZ;
std::iter_swap(it, mSnapshots.end() - 1);
mSnapshots.erase(mSnapshots.end() - 1);
}
@@ -384,12 +446,15 @@
updateSnapshots(args);
}
-void LayerSnapshotBuilder::updateSnapshotsInHierarchy(const Args& args,
- const LayerHierarchy& hierarchy,
- LayerHierarchy::TraversalPath& traversalPath,
- const LayerSnapshot& parentSnapshot) {
+const LayerSnapshot& LayerSnapshotBuilder::updateSnapshotsInHierarchy(
+ const Args& args, const LayerHierarchy& hierarchy,
+ LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot) {
const RequestedLayerState* layer = hierarchy.getLayer();
- LayerSnapshot* snapshot = getOrCreateSnapshot(traversalPath, *layer);
+ LayerSnapshot* snapshot = getSnapshot(traversalPath);
+ const bool newSnapshot = snapshot == nullptr;
+ if (newSnapshot) {
+ snapshot = createSnapshot(traversalPath, *layer);
+ }
if (traversalPath.isRelative()) {
bool parentIsRelative = traversalPath.variant == LayerHierarchy::Variant::Relative;
updateRelativeState(*snapshot, parentSnapshot, parentIsRelative, args);
@@ -397,23 +462,18 @@
if (traversalPath.isAttached()) {
resetRelativeState(*snapshot);
}
- updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath);
- }
-
- // If layer is hidden by policy we can avoid update its children. If the visibility
- // changed this update, then we still need to set the visibility on all the children.
- if (snapshot->isHiddenByPolicy() &&
- (!snapshot->changes.any(RequestedLayerState::Changes::Visibility |
- RequestedLayerState::Changes::Hierarchy))) {
- return;
+ updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath, newSnapshot);
}
for (auto& [childHierarchy, variant] : hierarchy.mChildren) {
LayerHierarchy::ScopedAddToTraversalPath addChildToPath(traversalPath,
childHierarchy->getLayer()->id,
variant);
- updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot);
+ const LayerSnapshot& childSnapshot =
+ updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot);
+ updateChildState(*snapshot, childSnapshot, args);
}
+ return *snapshot;
}
LayerSnapshot* LayerSnapshotBuilder::getSnapshot(uint32_t layerId) const {
@@ -429,22 +489,17 @@
return it == mIdToSnapshot.end() ? nullptr : it->second;
}
-LayerSnapshot* LayerSnapshotBuilder::getOrCreateSnapshot(const LayerHierarchy::TraversalPath& id,
- const RequestedLayerState& layer) {
- auto snapshot = getSnapshot(id);
- if (snapshot) {
- return snapshot;
- }
-
+LayerSnapshot* LayerSnapshotBuilder::createSnapshot(const LayerHierarchy::TraversalPath& id,
+ const RequestedLayerState& layer) {
mSnapshots.emplace_back(std::make_unique<LayerSnapshot>(layer, id));
- snapshot = mSnapshots.back().get();
+ LayerSnapshot* snapshot = mSnapshots.back().get();
snapshot->globalZ = static_cast<size_t>(mSnapshots.size()) - 1;
mIdToSnapshot[id] = snapshot;
return snapshot;
}
void LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) {
- if (!args.forceUpdate &&
+ if (!mResortSnapshots && !args.forceUpdate &&
!args.layerLifecycleManager.getGlobalChanges().any(
RequestedLayerState::Changes::Hierarchy |
RequestedLayerState::Changes::Visibility)) {
@@ -453,6 +508,8 @@
return;
}
+ mResortSnapshots = false;
+
size_t globalZ = 0;
args.root.traverseInZOrder(
[this, &globalZ](const LayerHierarchy&,
@@ -467,7 +524,8 @@
return false;
}
- if (snapshot->isVisible) {
+ if (snapshot->getIsVisible() || snapshot->hasInputInfo()) {
+ updateVisibility(*snapshot);
size_t oldZ = snapshot->globalZ;
size_t newZ = globalZ++;
snapshot->globalZ = newZ;
@@ -475,16 +533,17 @@
return true;
}
mSnapshots[newZ]->globalZ = oldZ;
+ LLOGV(snapshot->sequence, "Made visible z=%zu -> %zu %s", oldZ, newZ,
+ snapshot->getDebugString().c_str());
std::iter_swap(mSnapshots.begin() + static_cast<ssize_t>(oldZ),
mSnapshots.begin() + static_cast<ssize_t>(newZ));
}
-
return true;
});
-
+ mNumInterestingSnapshots = (int)globalZ;
while (globalZ < mSnapshots.size()) {
mSnapshots[globalZ]->globalZ = globalZ;
- mSnapshots[globalZ]->isVisible = false;
+ updateVisibility(*mSnapshots[globalZ]);
globalZ++;
}
}
@@ -493,7 +552,8 @@
const LayerSnapshot& parentSnapshot,
bool parentIsRelative, const Args& args) {
if (parentIsRelative) {
- snapshot.isHiddenByPolicyFromRelativeParent = parentSnapshot.isHiddenByPolicyFromParent;
+ snapshot.isHiddenByPolicyFromRelativeParent =
+ parentSnapshot.isHiddenByPolicyFromParent || parentSnapshot.invalidTransform;
if (args.includeMetadata) {
snapshot.relativeLayerMetadata = parentSnapshot.layerMetadata;
}
@@ -507,6 +567,38 @@
snapshot.isVisible = snapshot.getIsVisible();
}
+void LayerSnapshotBuilder::updateChildState(LayerSnapshot& snapshot,
+ const LayerSnapshot& childSnapshot, const Args& args) {
+ if (snapshot.childState.hasValidFrameRate) {
+ return;
+ }
+ if (args.forceUpdate || childSnapshot.changes.test(RequestedLayerState::Changes::FrameRate)) {
+ // We return whether this layer ot 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.
+ using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
+ const auto layerVotedWithDefaultCompatibility = childSnapshot.frameRate.rate.isValid() &&
+ childSnapshot.frameRate.type == FrameRateCompatibility::Default;
+ const auto layerVotedWithNoVote =
+ childSnapshot.frameRate.type == FrameRateCompatibility::NoVote;
+ const auto layerVotedWithExactCompatibility = childSnapshot.frameRate.rate.isValid() &&
+ childSnapshot.frameRate.type == FrameRateCompatibility::Exact;
+
+ snapshot.childState.hasValidFrameRate |= layerVotedWithDefaultCompatibility ||
+ layerVotedWithNoVote || 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 (!snapshot.frameRate.rate.isValid() &&
+ snapshot.frameRate.type != FrameRateCompatibility::NoVote &&
+ snapshot.childState.hasValidFrameRate) {
+ snapshot.frameRate =
+ scheduler::LayerInfo::FrameRate(Fps(), FrameRateCompatibility::NoVote);
+ snapshot.changes |= childSnapshot.changes & RequestedLayerState::Changes::FrameRate;
+ }
+ }
+}
+
void LayerSnapshotBuilder::resetRelativeState(LayerSnapshot& snapshot) {
snapshot.isHiddenByPolicyFromRelativeParent = false;
snapshot.relativeLayerMetadata.mMap.clear();
@@ -523,27 +615,71 @@
void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& args,
const RequestedLayerState& requested,
const LayerSnapshot& parentSnapshot,
- const LayerHierarchy::TraversalPath& path) {
+ const LayerHierarchy::TraversalPath& path,
+ bool newSnapshot) {
// Always update flags and visibility
ftl::Flags<RequestedLayerState::Changes> parentChanges = parentSnapshot.changes &
(RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Metadata |
RequestedLayerState::Changes::AffectsChildren);
snapshot.changes = parentChanges | requested.changes;
- snapshot.isHiddenByPolicyFromParent =
- parentSnapshot.isHiddenByPolicyFromParent || requested.isHiddenByPolicy();
+ snapshot.isHiddenByPolicyFromParent = parentSnapshot.isHiddenByPolicyFromParent ||
+ parentSnapshot.invalidTransform || requested.isHiddenByPolicy();
snapshot.contentDirty = requested.what & layer_state_t::CONTENT_DIRTY;
- if (snapshot.isHiddenByPolicyFromParent) {
- snapshot.isVisible = false;
- return;
- }
+ // TODO(b/238781169) scope down the changes to only buffer updates.
+ snapshot.hasReadyFrame =
+ (snapshot.contentDirty || requested.autoRefresh) && (requested.externalTexture);
+ // TODO(b/238781169) how is this used? ag/15523870
+ snapshot.sidebandStreamHasFrame = false;
+ updateSurfaceDamage(requested, snapshot.hasReadyFrame, args.forceFullDamage,
+ snapshot.surfaceDamage);
+ const bool forceUpdate = newSnapshot || args.forceUpdate ||
+ snapshot.changes.any(RequestedLayerState::Changes::Visibility |
+ RequestedLayerState::Changes::Created);
uint32_t displayRotationFlags =
getDisplayRotationFlags(args.displays, snapshot.outputFilter.layerStack);
- const bool forceUpdate = args.forceUpdate ||
- snapshot.changes.any(RequestedLayerState::Changes::Visibility |
- RequestedLayerState::Changes::Created);
+ // always update the buffer regardless of visibility
+ if (forceUpdate || requested.what & layer_state_t::BUFFER_CHANGES) {
+ snapshot.acquireFence =
+ (requested.externalTexture &&
+ requested.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged))
+ ? requested.bufferData->acquireFence
+ : Fence::NO_FENCE;
+ snapshot.buffer =
+ requested.externalTexture ? requested.externalTexture->getBuffer() : nullptr;
+ snapshot.bufferSize = requested.getBufferSize(displayRotationFlags);
+ snapshot.geomBufferSize = snapshot.bufferSize;
+ snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize);
+ snapshot.dataspace = requested.dataspace;
+ snapshot.externalTexture = requested.externalTexture;
+ snapshot.frameNumber = (requested.bufferData) ? requested.bufferData->frameNumber : 0;
+ snapshot.geomBufferTransform = requested.bufferTransform;
+ snapshot.geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse;
+ snapshot.geomContentCrop = requested.getBufferCrop();
+ snapshot.geomUsesSourceCrop = snapshot.hasBufferOrSidebandStream();
+ snapshot.hasProtectedContent = requested.externalTexture &&
+ requested.externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED;
+ snapshot.isHdrY410 = requested.dataspace == ui::Dataspace::BT2020_ITU_PQ &&
+ requested.api == NATIVE_WINDOW_API_MEDIA &&
+ requested.bufferData->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102;
+ snapshot.sidebandStream = requested.sidebandStream;
+ snapshot.transparentRegionHint = requested.transparentRegion;
+ snapshot.color.rgb = requested.getColor().rgb;
+ snapshot.currentSdrHdrRatio = requested.currentSdrHdrRatio;
+ snapshot.desiredSdrHdrRatio = requested.desiredSdrHdrRatio;
+ }
+
+ if (snapshot.isHiddenByPolicyFromParent && !newSnapshot) {
+ if (forceUpdate ||
+ snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
+ RequestedLayerState::Changes::Geometry |
+ RequestedLayerState::Changes::Input)) {
+ updateInput(snapshot, requested, parentSnapshot, path, args);
+ }
+ return;
+ }
if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::AffectsChildren)) {
// If root layer, use the layer stack otherwise get the parent's layer stack.
@@ -567,6 +703,21 @@
snapshot.colorTransform = requested.colorTransform;
snapshot.colorTransformIsIdentity = !requested.hasColorTransform;
}
+ snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE)
+ ? requested.gameMode
+ : parentSnapshot.gameMode;
+ snapshot.frameRate = (requested.requestedFrameRate.rate.isValid() ||
+ (requested.requestedFrameRate.type ==
+ scheduler::LayerInfo::FrameRateCompatibility::NoVote))
+ ? requested.requestedFrameRate
+ : parentSnapshot.frameRate;
+ snapshot.fixedTransformHint = requested.fixedTransformHint != ui::Transform::ROT_INVALID
+ ? requested.fixedTransformHint
+ : parentSnapshot.fixedTransformHint;
+ // Display mirrors are always placed in a VirtualDisplay so we never want to capture layers
+ // marked as skip capture
+ snapshot.handleSkipScreenshotFlag = parentSnapshot.handleSkipScreenshotFlag ||
+ (requested.layerStackToMirror != ui::INVALID_LAYER_STACK);
}
if (forceUpdate || requested.changes.get() != 0) {
@@ -576,35 +727,11 @@
(requested.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
}
- if (forceUpdate || requested.what & layer_state_t::BUFFER_CHANGES) {
- snapshot.acquireFence =
- (requested.bufferData) ? requested.bufferData->acquireFence : Fence::NO_FENCE;
- snapshot.buffer =
- requested.externalTexture ? requested.externalTexture->getBuffer() : nullptr;
- snapshot.bufferSize = requested.getBufferSize(displayRotationFlags);
- snapshot.geomBufferSize = snapshot.bufferSize;
- snapshot.croppedBufferSize = requested.getCroppedBufferSize(snapshot.bufferSize);
- snapshot.dataspace = requested.dataspace;
- snapshot.externalTexture = requested.externalTexture;
- snapshot.frameNumber = (requested.bufferData) ? requested.bufferData->frameNumber : 0;
- snapshot.geomBufferTransform = requested.bufferTransform;
- snapshot.geomBufferUsesDisplayInverseTransform = requested.transformToDisplayInverse;
- snapshot.geomContentCrop = requested.getBufferCrop();
- snapshot.geomUsesSourceCrop = snapshot.hasBufferOrSidebandStream();
- snapshot.hasProtectedContent = requested.externalTexture &&
- requested.externalTexture->getUsage() & GRALLOC_USAGE_PROTECTED;
- snapshot.isHdrY410 = requested.dataspace == ui::Dataspace::BT2020_ITU_PQ &&
- requested.api == NATIVE_WINDOW_API_MEDIA &&
- requested.bufferData->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102;
- snapshot.sidebandStream = requested.sidebandStream;
- snapshot.surfaceDamage = requested.surfaceDamageRegion;
- snapshot.transparentRegionHint = requested.transparentRegion;
- }
-
if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Content)) {
snapshot.color.rgb = requested.getColor().rgb;
snapshot.isColorspaceAgnostic = requested.colorSpaceAgnostic;
- snapshot.backgroundBlurRadius = static_cast<int>(requested.backgroundBlurRadius);
+ snapshot.backgroundBlurRadius =
+ args.supportsBlur ? static_cast<int>(requested.backgroundBlurRadius) : 0;
snapshot.blurRegions = requested.blurRegions;
snapshot.hdrMetadata = requested.hdrMetadata;
}
@@ -620,12 +747,7 @@
snapshot.changes.any(RequestedLayerState::Changes::Hierarchy |
RequestedLayerState::Changes::Geometry |
RequestedLayerState::Changes::Input)) {
- static frontend::DisplayInfo sDefaultInfo = {.isSecure = false};
- const std::optional<frontend::DisplayInfo> displayInfo =
- args.displays.get(snapshot.outputFilter.layerStack);
- bool noValidDisplay = !displayInfo.has_value();
- updateInput(snapshot, requested, parentSnapshot, displayInfo.value_or(sDefaultInfo),
- noValidDisplay, path);
+ updateInput(snapshot, requested, parentSnapshot, path, args);
}
// computed snapshot properties
@@ -636,12 +758,14 @@
}
snapshot.forceClientComposition = snapshot.isHdrY410 || snapshot.shadowSettings.length > 0 ||
requested.blurRegions.size() > 0 || snapshot.stretchEffect.hasEffect();
- snapshot.isVisible = snapshot.getIsVisible();
snapshot.isOpaque = snapshot.isContentOpaque() && !snapshot.roundedCorner.hasRoundedCorners() &&
snapshot.color.a == 1.f;
snapshot.blendMode = getBlendMode(snapshot, requested);
-
- ALOGV("%supdated [%d]%s changes parent:%s global:%s local:%s requested:%s %s from parent %s",
+ // TODO(b/238781169) pass this from flinger
+ // snapshot.fps;
+ // snapshot.metadata;
+ LLOGV(snapshot.sequence,
+ "%supdated [%d]%s changes parent:%s global:%s local:%s requested:%s %s from parent %s",
args.forceUpdate ? "Force " : "", requested.id, requested.name.c_str(),
parentSnapshot.changes.string().c_str(), snapshot.changes.string().c_str(),
requested.changes.string().c_str(), std::to_string(requested.what).c_str(),
@@ -694,12 +818,35 @@
snapshot.localTransform = requested.getTransform(displayRotationFlags);
snapshot.localTransformInverse = snapshot.localTransform.inverse();
snapshot.geomLayerTransform = parentSnapshot.geomLayerTransform * snapshot.localTransform;
+ const bool transformWasInvalid = snapshot.invalidTransform;
snapshot.invalidTransform = !LayerSnapshot::isTransformValid(snapshot.geomLayerTransform);
if (snapshot.invalidTransform) {
- ALOGW("Resetting transform for %s because it has an invalid transformation.",
- requested.getDebugStringShort().c_str());
+ auto& t = snapshot.geomLayerTransform;
+ auto& requestedT = requested.requestedTransform;
+ std::string transformDebug =
+ base::StringPrintf(" transform={%f,%f,%f,%f} requestedTransform={%f,%f,%f,%f}",
+ t.dsdx(), t.dsdy(), t.dtdx(), t.dtdy(), requestedT.dsdx(),
+ requestedT.dsdy(), requestedT.dtdx(), requestedT.dtdy());
+ std::string bufferDebug;
+ if (requested.externalTexture) {
+ auto unRotBuffer = requested.getUnrotatedBufferSize(displayRotationFlags);
+ auto& destFrame = requested.destinationFrame;
+ bufferDebug = base::StringPrintf(" buffer={%d,%d} displayRot=%d"
+ " destFrame={%d,%d,%d,%d} unRotBuffer={%d,%d}",
+ requested.externalTexture->getWidth(),
+ requested.externalTexture->getHeight(),
+ displayRotationFlags, destFrame.left, destFrame.top,
+ destFrame.right, destFrame.bottom,
+ unRotBuffer.getHeight(), unRotBuffer.getWidth());
+ }
+ ALOGW("Resetting transform for %s because it is invalid.%s%s",
+ snapshot.getDebugString().c_str(), transformDebug.c_str(), bufferDebug.c_str());
snapshot.geomLayerTransform.reset();
}
+ if (transformWasInvalid != snapshot.invalidTransform) {
+ // If transform is invalid, the layer will be hidden.
+ mResortSnapshots = true;
+ }
snapshot.geomInverseLayerTransform = snapshot.geomLayerTransform.inverse();
FloatRect parentBounds = parentSnapshot.geomLayerBounds;
@@ -711,12 +858,19 @@
}
snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(parentBounds);
snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds);
+ const Rect geomLayerBoundsWithoutTransparentRegion =
+ RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
+ requested.transparentRegion);
+ snapshot.transformedBoundsWithoutTransparentRegion =
+ snapshot.geomLayerTransform.transform(geomLayerBoundsWithoutTransparentRegion);
snapshot.parentTransform = parentSnapshot.geomLayerTransform;
// Subtract the transparent region and snap to the bounds
- Rect bounds =
+ const Rect bounds =
RequestedLayerState::reduce(snapshot.croppedBufferSize, requested.transparentRegion);
- snapshot.cursorFrame = snapshot.geomLayerTransform.transform(bounds);
+ if (requested.potentialCursor) {
+ snapshot.cursorFrame = snapshot.geomLayerTransform.transform(bounds);
+ }
// TODO(b/238781169) use dest vs src
snapshot.bufferNeedsFiltering = snapshot.externalTexture &&
@@ -749,15 +903,28 @@
void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot,
const RequestedLayerState& requested,
const LayerSnapshot& parentSnapshot,
- const frontend::DisplayInfo& displayInfo,
- bool noValidDisplay,
- const LayerHierarchy::TraversalPath& path) {
+ const LayerHierarchy::TraversalPath& path,
+ const Args& args) {
+ if (requested.windowInfoHandle) {
+ snapshot.inputInfo = *requested.windowInfoHandle->getInfo();
+ } else {
+ snapshot.inputInfo = {};
+ }
snapshot.inputInfo.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id);
- if (!requested.hasInputInfo()) {
- snapshot.inputInfo.inputConfig = gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL;
+
+ if (!needsInputInfo(snapshot, requested)) {
return;
}
+ static frontend::DisplayInfo sDefaultInfo = {.isSecure = false};
+ const std::optional<frontend::DisplayInfo> displayInfoOpt =
+ args.displays.get(snapshot.outputFilter.layerStack);
+ bool noValidDisplay = !displayInfoOpt.has_value();
+ auto displayInfo = displayInfoOpt.value_or(sDefaultInfo);
+
+ if (!requested.windowInfoHandle) {
+ snapshot.inputInfo.inputConfig = gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL;
+ }
fillInputFrameInfo(snapshot.inputInfo, displayInfo.transform, snapshot);
if (noValidDisplay) {
@@ -766,17 +933,6 @@
snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::NOT_TOUCHABLE;
}
- // For compatibility reasons we let layers which can receive input
- // receive input before they have actually submitted a buffer. Because
- // of this we use canReceiveInput instead of isVisible to check the
- // policy-visibility, ignoring the buffer state. However for layers with
- // hasInputInfo()==false we can use the real visibility state.
- // We are just using these layers for occlusion detection in
- // InputDispatcher, and obviously if they aren't visible they can't occlude
- // anything.
- const bool visible = requested.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible;
- snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visible);
-
snapshot.inputInfo.alpha = snapshot.color.a;
snapshot.inputInfo.touchOcclusionMode = parentSnapshot.inputInfo.touchOcclusionMode;
if (requested.dropInputMode == gui::DropInputMode::ALL ||
@@ -830,4 +986,28 @@
return mSnapshots;
}
+void LayerSnapshotBuilder::forEachVisibleSnapshot(const ConstVisitor& visitor) const {
+ for (int i = 0; i < mNumInterestingSnapshots; i++) {
+ LayerSnapshot& snapshot = *mSnapshots[(size_t)i];
+ if (!snapshot.isVisible) continue;
+ visitor(snapshot);
+ }
+}
+
+void LayerSnapshotBuilder::forEachVisibleSnapshot(const Visitor& visitor) {
+ for (int i = 0; i < mNumInterestingSnapshots; i++) {
+ std::unique_ptr<LayerSnapshot>& snapshot = mSnapshots.at((size_t)i);
+ if (!snapshot->isVisible) continue;
+ visitor(snapshot);
+ }
+}
+
+void LayerSnapshotBuilder::forEachInputSnapshot(const ConstVisitor& visitor) const {
+ for (int i = mNumInterestingSnapshots - 1; i >= 0; i--) {
+ LayerSnapshot& snapshot = *mSnapshots[(size_t)i];
+ if (!snapshot.hasInputInfo()) continue;
+ visitor(snapshot);
+ }
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
index 33b250c..abb7e66 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h
@@ -44,6 +44,8 @@
// Set to true if there were display changes since last update.
bool displayChanges = false;
const renderengine::ShadowSettings& globalShadowSettings;
+ bool supportsBlur = true;
+ bool forceFullDamage = false;
};
LayerSnapshotBuilder();
@@ -56,10 +58,22 @@
// change flags.
void update(const Args&);
std::vector<std::unique_ptr<LayerSnapshot>>& getSnapshots();
+ LayerSnapshot* getSnapshot(uint32_t layerId) const;
+
+ typedef std::function<void(const LayerSnapshot& snapshot)> ConstVisitor;
+
+ // Visit each visible snapshot in z-order
+ void forEachVisibleSnapshot(const ConstVisitor& visitor) const;
+
+ typedef std::function<void(std::unique_ptr<LayerSnapshot>& snapshot)> Visitor;
+ // Visit each visible snapshot in z-order and move the snapshot if needed
+ void forEachVisibleSnapshot(const Visitor& visitor);
+
+ // Visit each snapshot interesting to input reverse z-order
+ void forEachInputSnapshot(const ConstVisitor& visitor) const;
private:
friend class LayerSnapshotTest;
- LayerSnapshot* getSnapshot(uint32_t layerId) const;
LayerSnapshot* getSnapshot(const LayerHierarchy::TraversalPath& id) const;
static LayerSnapshot getRootSnapshot();
@@ -69,28 +83,29 @@
void updateSnapshots(const Args& args);
- void updateSnapshotsInHierarchy(const Args&, const LayerHierarchy& hierarchy,
- LayerHierarchy::TraversalPath& traversalPath,
- const LayerSnapshot& parentSnapshot);
- void updateSnapshot(LayerSnapshot& snapshot, const Args& args, const RequestedLayerState&,
- const LayerSnapshot& parentSnapshot,
- const LayerHierarchy::TraversalPath& path);
+ const LayerSnapshot& updateSnapshotsInHierarchy(const Args&, const LayerHierarchy& hierarchy,
+ LayerHierarchy::TraversalPath& traversalPath,
+ const LayerSnapshot& parentSnapshot);
+ void updateSnapshot(LayerSnapshot&, const Args&, const RequestedLayerState&,
+ const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath&,
+ bool newSnapshot);
static void updateRelativeState(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot,
bool parentIsRelative, const Args& args);
static void resetRelativeState(LayerSnapshot& snapshot);
static void updateRoundedCorner(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
const LayerSnapshot& parentSnapshot);
- static void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
- const LayerSnapshot& parentSnapshot,
- uint32_t displayRotationFlags);
+ void updateLayerBounds(LayerSnapshot& snapshot, const RequestedLayerState& layerState,
+ const LayerSnapshot& parentSnapshot, uint32_t displayRotationFlags);
static void updateShadows(LayerSnapshot& snapshot, const RequestedLayerState& requested,
const renderengine::ShadowSettings& globalShadowSettings);
void updateInput(LayerSnapshot& snapshot, const RequestedLayerState& requested,
- const LayerSnapshot& parentSnapshot, const frontend::DisplayInfo& displayInfo,
- bool noValidDisplay, const LayerHierarchy::TraversalPath& path);
+ const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath& path,
+ const Args& args);
void sortSnapshotsByZ(const Args& args);
- LayerSnapshot* getOrCreateSnapshot(const LayerHierarchy::TraversalPath& id,
- const RequestedLayerState& layer);
+ LayerSnapshot* createSnapshot(const LayerHierarchy::TraversalPath& id,
+ const RequestedLayerState& layer);
+ void updateChildState(LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot,
+ const Args& args);
struct TraversalPathHash {
std::size_t operator()(const LayerHierarchy::TraversalPath& key) const {
@@ -105,6 +120,8 @@
mIdToSnapshot;
std::vector<std::unique_ptr<LayerSnapshot>> mSnapshots;
LayerSnapshot mRootSnapshot;
+ bool mResortSnapshots = false;
+ int mNumInterestingSnapshots = 0;
};
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index dcc16e8..d63b126 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -14,16 +14,18 @@
* limitations under the License.
*/
-#include "FrontEnd/LayerCreationArgs.h"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#undef LOG_TAG
#define LOG_TAG "RequestedLayerState"
+#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <sys/types.h>
#include "Layer.h"
+#include "LayerCreationArgs.h"
#include "LayerHandle.h"
+#include "LayerLog.h"
#include "RequestedLayerState.h"
namespace android::surfaceflinger::frontend {
@@ -43,11 +45,21 @@
return layerId == UNASSIGNED_LAYER_ID ? "none" : std::to_string(layerId);
}
+std::string layerIdsToString(const std::vector<uint32_t>& layerIds) {
+ std::stringstream stream;
+ stream << "{";
+ for (auto layerId : layerIds) {
+ stream << layerId << ",";
+ }
+ stream << "}";
+ return stream.str();
+}
+
} // namespace
RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args)
: id(args.sequence),
- name(args.name),
+ name(args.name + "#" + std::to_string(args.sequence)),
canBeRoot(args.addToRoot),
layerCreationFlags(args.flags),
textureName(args.textureName),
@@ -59,8 +71,14 @@
changes |= RequestedLayerState::Changes::Metadata;
handleAlive = true;
parentId = LayerHandle::getLayerId(args.parentHandle.promote());
- mirrorId = LayerHandle::getLayerId(args.mirrorLayerHandle.promote());
- if (mirrorId != UNASSIGNED_LAYER_ID) {
+ if (args.parentHandle != nullptr) {
+ canBeRoot = false;
+ }
+ layerIdToMirror = LayerHandle::getLayerId(args.mirrorLayerHandle.promote());
+ if (layerIdToMirror != UNASSIGNED_LAYER_ID) {
+ changes |= RequestedLayerState::Changes::Mirror;
+ } else if (args.layerStackToMirror != ui::INVALID_LAYER_STACK) {
+ layerStackToMirror = args.layerStackToMirror;
changes |= RequestedLayerState::Changes::Mirror;
}
@@ -83,6 +101,7 @@
} else {
color.rgb = {0.0_hf, 0.0_hf, 0.0_hf};
}
+ LLOGV(layerId, "Created %s flags=%d", getDebugString().c_str(), flags);
color.a = 1.0f;
crop.makeInvalid();
@@ -90,6 +109,8 @@
layerStack = ui::DEFAULT_LAYER_STACK;
transformToDisplayInverse = false;
dataspace = ui::Dataspace::UNKNOWN;
+ desiredSdrHdrRatio = 1.f;
+ currentSdrHdrRatio = 1.f;
dataspaceRequested = false;
hdrMetadata.validTypes = 0;
surfaceDamageRegion = Region::INVALID_REGION;
@@ -114,11 +135,14 @@
defaultFrameRateCompatibility =
static_cast<int8_t>(scheduler::LayerInfo::FrameRateCompatibility::Default);
dataspace = ui::Dataspace::V0_SRGB;
+ gameMode = gui::GameMode::Unsupported;
+ requestedFrameRate = {};
}
void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) {
- bool oldFlags = flags;
- Rect oldBufferSize = getBufferSize(0);
+ const uint32_t oldFlags = flags;
+ const half oldAlpha = color.a;
+ const bool hadBufferOrSideStream = hasValidBuffer() || sidebandStream != nullptr;
const layer_state_t& clientState = resolvedComposerState.state;
uint64_t clientChanges = what | layer_state_t::diff(clientState);
@@ -127,14 +151,28 @@
if (clientState.what & layer_state_t::eFlagsChanged) {
if ((oldFlags ^ flags) & layer_state_t::eLayerHidden) {
- changes |= RequestedLayerState::Changes::Visibility;
+ changes |= RequestedLayerState::Changes::Visibility |
+ RequestedLayerState::Changes::VisibleRegion;
}
if ((oldFlags ^ flags) & layer_state_t::eIgnoreDestinationFrame) {
changes |= RequestedLayerState::Changes::Geometry;
}
}
- if (clientState.what & layer_state_t::eBufferChanged && oldBufferSize != getBufferSize(0)) {
- changes |= RequestedLayerState::Changes::Geometry;
+ if (clientState.what &
+ (layer_state_t::eBufferChanged | layer_state_t::eSidebandStreamChanged)) {
+ const bool hasBufferOrSideStream = hasValidBuffer() || sidebandStream != nullptr;
+ if (hadBufferOrSideStream != hasBufferOrSideStream) {
+ changes |= RequestedLayerState::Changes::Geometry |
+ RequestedLayerState::Changes::VisibleRegion |
+ RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input |
+ RequestedLayerState::Changes::Buffer;
+ }
+ }
+ if (what & (layer_state_t::eAlphaChanged)) {
+ if (oldAlpha == 0 || color.a == 0) {
+ changes |= RequestedLayerState::Changes::Visibility |
+ RequestedLayerState::Changes::VisibleRegion;
+ }
}
if (clientChanges & layer_state_t::HIERARCHY_CHANGES)
changes |= RequestedLayerState::Changes::Hierarchy;
@@ -144,7 +182,10 @@
changes |= RequestedLayerState::Changes::Geometry;
if (clientChanges & layer_state_t::AFFECTS_CHILDREN)
changes |= RequestedLayerState::Changes::AffectsChildren;
-
+ if (clientChanges & layer_state_t::INPUT_CHANGES)
+ changes |= RequestedLayerState::Changes::Input;
+ if (clientChanges & layer_state_t::VISIBLE_REGION_CHANGES)
+ changes |= RequestedLayerState::Changes::VisibleRegion;
if (clientState.what & layer_state_t::eColorTransformChanged) {
static const mat4 identityMatrix = mat4();
hasColorTransform = colorTransform != identityMatrix;
@@ -183,7 +224,6 @@
wp<IBinder>& touchableRegionCropHandle =
windowInfoHandle->editInfo()->touchableRegionCropHandle;
touchCropId = LayerHandle::getLayerId(touchableRegionCropHandle.promote());
- changes |= RequestedLayerState::Changes::Input;
touchableRegionCropHandle.clear();
}
if (clientState.what & layer_state_t::eStretchChanged) {
@@ -205,6 +245,27 @@
if (clientState.what & layer_state_t::eMatrixChanged) {
requestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
}
+ if (clientState.what & layer_state_t::eMetadataChanged) {
+ const int32_t requestedGameMode =
+ clientState.metadata.getInt32(gui::METADATA_GAME_MODE, -1);
+ if (requestedGameMode != -1) {
+ // The transaction will be received on the Task layer and needs to be applied to all
+ // child layers.
+ if (static_cast<int32_t>(gameMode) != requestedGameMode) {
+ gameMode = static_cast<gui::GameMode>(requestedGameMode);
+ changes |= RequestedLayerState::Changes::AffectsChildren;
+ }
+ }
+ }
+ if (clientState.what & layer_state_t::eFrameRateChanged) {
+ const auto compatibility =
+ Layer::FrameRate::convertCompatibility(clientState.frameRateCompatibility);
+ const auto strategy = Layer::FrameRate::convertChangeFrameRateStrategy(
+ clientState.changeFrameRateStrategy);
+ requestedFrameRate =
+ Layer::FrameRate(Fps::fromValue(clientState.frameRate), compatibility, strategy);
+ changes |= RequestedLayerState::Changes::FrameRate;
+ }
}
ui::Size RequestedLayerState::getUnrotatedBufferSize(uint32_t displayRotationFlags) const {
@@ -261,7 +322,7 @@
return "[" + std::to_string(id) + "]" + name + ",parent=" + layerIdToString(parentId) +
",relativeParent=" + layerIdToString(relativeParentId) +
",isRelativeOf=" + std::to_string(isRelativeOf) +
- ",mirrorId=" + layerIdToString(mirrorId) +
+ ",mirrorIds=" + layerIdsToString(mirrorIds) +
",handleAlive=" + std::to_string(handleAlive) + ",z=" + std::to_string(z);
}
@@ -368,7 +429,8 @@
// If the relative parentid is unassigned, the layer will be considered relative but won't be
// reachable.
bool RequestedLayerState::hasValidRelativeParent() const {
- return isRelativeOf && parentId != relativeParentId;
+ return isRelativeOf &&
+ (parentId != relativeParentId || relativeParentId == UNASSIGNED_LAYER_ID);
}
bool RequestedLayerState::hasInputInfo() const {
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 95240d0..6317b95 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -20,6 +20,7 @@
#include <ftl/flags.h>
#include <gui/LayerState.h>
#include <renderengine/ExternalTexture.h>
+#include "Scheduler/LayerInfo.h"
#include "LayerCreationArgs.h"
#include "TransactionState.h"
@@ -48,6 +49,9 @@
Metadata = 1u << 10,
Visibility = 1u << 11,
AffectsChildren = 1u << 12,
+ FrameRate = 1u << 13,
+ VisibleRegion = 1u << 14,
+ Buffer = 1u << 15,
};
static Rect reduce(const Rect& win, const Region& exclude);
RequestedLayerState(const LayerCreationArgs&);
@@ -91,13 +95,17 @@
ui::Transform requestedTransform;
std::shared_ptr<FenceTime> acquireFenceTime;
std::shared_ptr<renderengine::ExternalTexture> externalTexture;
+ gui::GameMode gameMode;
+ scheduler::LayerInfo::FrameRate requestedFrameRate;
+ ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
+ uint32_t layerIdToMirror = UNASSIGNED_LAYER_ID;
// book keeping states
bool handleAlive = true;
bool isRelativeOf = false;
uint32_t parentId = UNASSIGNED_LAYER_ID;
uint32_t relativeParentId = UNASSIGNED_LAYER_ID;
- uint32_t mirrorId = UNASSIGNED_LAYER_ID;
+ std::vector<uint32_t> mirrorIds{};
uint32_t touchCropId = UNASSIGNED_LAYER_ID;
uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID;
ftl::Flags<RequestedLayerState::Changes> changes;
diff --git a/services/surfaceflinger/FrontEnd/SwapErase.h b/services/surfaceflinger/FrontEnd/SwapErase.h
index f672f99..0061c53 100644
--- a/services/surfaceflinger/FrontEnd/SwapErase.h
+++ b/services/surfaceflinger/FrontEnd/SwapErase.h
@@ -23,12 +23,15 @@
// remove an element from a vector that avoids relocating all the elements after the one
// that is erased.
template <typename T>
-void swapErase(std::vector<T>& vec, const T& value) {
+bool swapErase(std::vector<T>& vec, const T& value) {
+ bool found = false;
auto it = std::find(vec.begin(), vec.end(), value);
if (it != vec.end()) {
std::iter_swap(it, vec.end() - 1);
vec.erase(vec.end() - 1);
+ found = true;
}
+ return found;
}
// Similar to swapErase(std::vector<T>& vec, const T& value) but erases the first element
diff --git a/services/surfaceflinger/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
index 4ada2b6..9b70c16 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.h
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -33,6 +33,17 @@
int32_t maxW = 0;
int32_t maxH = 0;
int32_t flags = 0;
+ // Counter-intuitively a value of "1" means "as much as you can give me" due to "1" being
+ // the default value for all layers, so any HDR layer with a value of 1.f means no
+ // reduced maximum has been requested
+ // TODO: Should the max desired ratio have a better meaning for HLG/PQ so this can be
+ // eliminated? If we assume an SDR white point of even just 100 nits for those content
+ // then HLG could have a meaningful max ratio of 10.f and PQ of 100.f instead of needing
+ // to treat 1.f as "uncapped"
+ // With peak display brightnesses exceeding 1,000 nits currently, HLG's request could
+ // actually be satisfied in some ambient conditions such that limiting that max for that
+ // content in theory makes sense
+ float maxDesiredSdrHdrRatio = 0.f;
bool operator==(const HdrLayerInfo& other) const {
return numberOfHdrLayers == other.numberOfHdrLayers && maxW == other.maxW &&
@@ -40,6 +51,20 @@
}
bool operator!=(const HdrLayerInfo& other) const { return !(*this == other); }
+
+ void mergeDesiredRatio(float update) {
+ if (maxDesiredSdrHdrRatio == 0.f) {
+ // If nothing is set, take the incoming value
+ maxDesiredSdrHdrRatio = update;
+ } else if (update == 1.f) {
+ // If the request is to "go to max", then take it regardless
+ maxDesiredSdrHdrRatio = 1.f;
+ } else if (maxDesiredSdrHdrRatio != 1.f) {
+ // If we're not currently asked to "go to max", then take the max
+ // of the incoming requests
+ maxDesiredSdrHdrRatio = std::max(maxDesiredSdrHdrRatio, update);
+ }
+ }
};
HdrLayerInfoReporter() = default;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 0179d62..7a4b337 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -196,7 +196,7 @@
mDrawingState.color.b = -1.0_hf;
}
- mFrameTracker.setDisplayRefreshPeriod(args.flinger->mScheduler->getLeaderVsyncPeriod());
+ mFrameTracker.setDisplayRefreshPeriod(args.flinger->mScheduler->getLeaderVsyncPeriod().ns());
mOwnerUid = args.ownerUid;
mOwnerPid = args.ownerPid;
@@ -252,6 +252,10 @@
if (mHadClonedChild) {
mFlinger->mNumClones--;
}
+ if (hasTrustedPresentationListener()) {
+ mFlinger->mNumTrustedPresentationListeners--;
+ updateTrustedPresentationState(nullptr, -1 /* time_in_ms */, true /* leaveState*/);
+ }
}
// ---------------------------------------------------------------------------
@@ -281,6 +285,7 @@
mRemovedFromDrawingState = true;
mFlinger->mScheduler->deregisterLayer(this);
}
+ updateTrustedPresentationState(nullptr, -1 /* time_in_ms */, true /* leaveState*/);
mFlinger->markLayerPendingRemovalLocked(sp<Layer>::fromExisting(this));
}
@@ -378,6 +383,92 @@
return reduce(mBounds, activeTransparentRegion);
}
+// No early returns.
+void Layer::updateTrustedPresentationState(const DisplayDevice* display, int64_t time_in_ms,
+ bool leaveState) {
+ if (!hasTrustedPresentationListener()) {
+ return;
+ }
+ const bool lastState = mLastComputedTrustedPresentationState;
+ mLastComputedTrustedPresentationState = false;
+
+ if (!leaveState) {
+ const auto outputLayer = findOutputLayerForDisplay(display);
+ if (outputLayer != nullptr) {
+ mLastComputedTrustedPresentationState =
+ computeTrustedPresentationState(mBounds, mSourceBounds,
+ outputLayer->getState().coveredRegion,
+ mScreenBounds, getCompositionState()->alpha,
+ getCompositionState()->geomLayerTransform,
+ mTrustedPresentationThresholds);
+ }
+ }
+ const bool newState = mLastComputedTrustedPresentationState;
+ if (lastState && !newState) {
+ // We were in the trusted presentation state, but now we left it,
+ // emit the callback if needed
+ if (mLastReportedTrustedPresentationState) {
+ mLastReportedTrustedPresentationState = false;
+ mTrustedPresentationListener.invoke(false);
+ }
+ // Reset the timer
+ mEnteredTrustedPresentationStateTime = -1;
+ } else if (!lastState && newState) {
+ // We were not in the trusted presentation state, but we entered it, begin the timer
+ // and make sure this gets called at least once more!
+ mEnteredTrustedPresentationStateTime = time_in_ms;
+ mFlinger->forceFutureUpdate(mTrustedPresentationThresholds.stabilityRequirementMs * 1.5);
+ }
+
+ // Has the timer elapsed, but we are still in the state? Emit a callback if needed
+ if (!mLastReportedTrustedPresentationState && newState &&
+ (time_in_ms - mEnteredTrustedPresentationStateTime >
+ mTrustedPresentationThresholds.stabilityRequirementMs)) {
+ mLastReportedTrustedPresentationState = true;
+ mTrustedPresentationListener.invoke(true);
+ }
+}
+
+/**
+ * See SurfaceComposerClient.h: setTrustedPresentationCallback for discussion
+ * of how the parameters and thresholds are interpreted. The general spirit is
+ * to produce an upper bound on the amount of the buffer which was presented.
+ */
+bool Layer::computeTrustedPresentationState(const FloatRect& bounds, const FloatRect& sourceBounds,
+ const Region& coveredRegion,
+ const FloatRect& screenBounds, float alpha,
+ const ui::Transform& effectiveTransform,
+ const TrustedPresentationThresholds& thresholds) {
+ if (alpha < thresholds.minAlpha) {
+ return false;
+ }
+ if (sourceBounds.getWidth() == 0 || sourceBounds.getHeight() == 0) {
+ return false;
+ }
+ if (screenBounds.getWidth() == 0 || screenBounds.getHeight() == 0) {
+ return false;
+ }
+
+ const float sx = effectiveTransform.dsdx();
+ const float sy = effectiveTransform.dsdy();
+ float fractionRendered = std::min(sx * sy, 1.0f);
+
+ float boundsOverSourceW = bounds.getWidth() / (float)sourceBounds.getWidth();
+ float boundsOverSourceH = bounds.getHeight() / (float)sourceBounds.getHeight();
+ fractionRendered *= boundsOverSourceW * boundsOverSourceH;
+
+ Rect coveredBounds = coveredRegion.bounds();
+ fractionRendered *= (1 -
+ ((coveredBounds.width() / (float)screenBounds.getWidth()) *
+ coveredBounds.height() / (float)screenBounds.getHeight()));
+
+ if (fractionRendered < thresholds.minFractionRendered) {
+ return false;
+ }
+
+ return true;
+}
+
void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform,
float parentShadowRadius) {
const State& s(getDrawingState());
@@ -475,7 +566,8 @@
snapshot->geomLayerTransform = getTransform();
snapshot->geomInverseLayerTransform = snapshot->geomLayerTransform.inverse();
snapshot->transparentRegionHint = getActiveTransparentRegion(drawingState);
- snapshot->localTransformInverse = getActiveTransform(drawingState).inverse();
+ snapshot->localTransform = getActiveTransform(drawingState);
+ snapshot->localTransformInverse = snapshot->localTransform.inverse();
snapshot->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
snapshot->alpha = alpha;
snapshot->backgroundBlurRadius = drawingState.backgroundBlurRadius;
@@ -530,6 +622,8 @@
snapshot->surfaceDamage = surfaceDamageRegion;
snapshot->hasProtectedContent = isProtected();
snapshot->dimmingEnabled = isDimmingEnabled();
+ snapshot->currentSdrHdrRatio = getCurrentSdrHdrRatio();
+ snapshot->desiredSdrHdrRatio = getDesiredSdrHdrRatio();
const bool usesRoundedCorners = hasRoundedCorners();
@@ -2595,6 +2689,8 @@
mDrawingState = from->mDrawingState;
// Skip callback info since they are not applicable for cloned layers.
mDrawingState.releaseBufferListener = nullptr;
+ // TODO (b/238781169) currently broken for mirror layers because we do not
+ // track release fences for mirror layers composed on other displays
mDrawingState.callbackHandles = {};
}
@@ -2662,18 +2758,18 @@
void Layer::onSurfaceFrameCreated(
const std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) {
- if (!hasBufferOrSidebandStreamInDrawing()) {
- return;
- }
-
while (mPendingJankClassifications.size() >= kPendingClassificationMaxSurfaceFrames) {
// Too many SurfaceFrames pending classification. The front of the deque is probably not
// tracked by FrameTimeline and will never be presented. This will only result in a memory
// leak.
- ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
- mName.c_str());
- std::string miniDump = mPendingJankClassifications.front()->miniDump();
- ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str());
+ if (hasBufferOrSidebandStreamInDrawing()) {
+ // Only log for layers with a buffer, since we expect the jank data to be drained for
+ // these, while there may be no jank listeners for bufferless layers.
+ ALOGW("Removing the front of pending jank deque from layer - %s to prevent memory leak",
+ mName.c_str());
+ std::string miniDump = mPendingJankClassifications.front()->miniDump();
+ ALOGD("Head SurfaceFrame mini dump\n%s", miniDump.c_str());
+ }
mPendingJankClassifications.pop_front();
}
mPendingJankClassifications.emplace_back(surfaceFrame);
@@ -2947,6 +3043,17 @@
return true;
}
+bool Layer::setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio) {
+ if (mDrawingState.currentSdrHdrRatio == currentBufferRatio &&
+ mDrawingState.desiredSdrHdrRatio == desiredRatio)
+ return false;
+ mDrawingState.currentSdrHdrRatio = currentBufferRatio;
+ mDrawingState.desiredSdrHdrRatio = desiredRatio;
+ mDrawingState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
bool Layer::setHdrMetadata(const HdrMetadata& hdrMetadata) {
if (mDrawingState.hdrMetadata == hdrMetadata) return false;
mDrawingState.hdrMetadata = hdrMetadata;
@@ -3178,7 +3285,11 @@
auto lastDataspace = mBufferInfo.mDataspace;
mBufferInfo.mDataspace = translateDataspace(mDrawingState.dataspace);
if (lastDataspace != mBufferInfo.mDataspace) {
- mFlinger->mSomeDataspaceChanged = true;
+ mFlinger->mHdrLayerInfoChanged = true;
+ }
+ if (mBufferInfo.mDesiredSdrHdrRatio != mDrawingState.desiredSdrHdrRatio) {
+ mBufferInfo.mDesiredSdrHdrRatio = mDrawingState.desiredSdrHdrRatio;
+ mFlinger->mHdrLayerInfoChanged = true;
}
mBufferInfo.mCrop = computeBufferCrop(mDrawingState);
mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
@@ -3469,6 +3580,14 @@
}
}
+ if (s.what & layer_state_t::eExtendedRangeBrightnessChanged) {
+ if (mDrawingState.currentSdrHdrRatio != s.currentSdrHdrRatio ||
+ mDrawingState.desiredSdrHdrRatio != s.desiredSdrHdrRatio) {
+ ALOGV("%s: false [eDimmingEnabledChanged changed]", __func__);
+ return false;
+ }
+ }
+
ALOGV("%s: true", __func__);
return true;
}
@@ -3969,6 +4088,19 @@
return *this;
}
+void Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
+ TrustedPresentationListener const& listener) {
+ bool hadTrustedPresentationListener = hasTrustedPresentationListener();
+ mTrustedPresentationListener = listener;
+ mTrustedPresentationThresholds = thresholds;
+ bool haveTrustedPresentationListener = hasTrustedPresentationListener();
+ if (!hadTrustedPresentationListener && haveTrustedPresentationListener) {
+ mFlinger->mNumTrustedPresentationListeners++;
+ } else if (hadTrustedPresentationListener && !haveTrustedPresentationListener) {
+ mFlinger->mNumTrustedPresentationListeners--;
+ }
+}
+
// ---------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 8281140..3384e4a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -223,6 +223,8 @@
gui::DropInputMode dropInputMode;
bool autoRefresh = false;
bool dimmingEnabled = true;
+ float currentSdrHdrRatio = 1.f;
+ float desiredSdrHdrRatio = 1.f;
};
explicit Layer(const LayerCreationArgs& args);
@@ -289,7 +291,9 @@
virtual mat4 getColorTransform() const;
virtual bool hasColorTransform() const;
virtual bool isColorSpaceAgnostic() const { return mDrawingState.colorSpaceAgnostic; }
- virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; };
+ virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; }
+ float getDesiredSdrHdrRatio() const { return getDrawingState().desiredSdrHdrRatio; }
+ float getCurrentSdrHdrRatio() const { return getDrawingState().currentSdrHdrRatio; }
bool setTransform(uint32_t /*transform*/);
bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/);
@@ -298,6 +302,7 @@
nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/);
bool setDataspace(ui::Dataspace /*dataspace*/);
+ bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio);
bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/);
bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/);
bool setApi(int32_t /*api*/);
@@ -499,6 +504,7 @@
uint64_t mFrameNumber;
bool mFrameLatencyNeeded{false};
+ float mDesiredSdrHdrRatio = 1.f;
};
BufferInfo mBufferInfo;
@@ -527,6 +533,19 @@
uint32_t getTransactionFlags() const { return mTransactionFlags; }
+ static bool computeTrustedPresentationState(const FloatRect& bounds,
+ const FloatRect& sourceBounds,
+ const Region& coveredRegion,
+ const FloatRect& screenBounds, float,
+ const ui::Transform&,
+ const TrustedPresentationThresholds&);
+ void updateTrustedPresentationState(const DisplayDevice* display, int64_t time_in_ms,
+ bool leaveState);
+
+ inline bool hasTrustedPresentationListener() {
+ return mTrustedPresentationListener.callbackInterface != nullptr;
+ }
+
// Sets the masked bits.
void setTransactionFlags(uint32_t mask);
@@ -728,6 +747,9 @@
std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForBuffer(
const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName);
+ void setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
+ TrustedPresentationListener const& listener);
+
// Creates a new handle each time, so we only expect
// this to be called once.
sp<IBinder> getHandle();
@@ -885,6 +907,12 @@
// These are only accessed by the main thread or the tracing thread.
State mDrawingState;
+ TrustedPresentationThresholds mTrustedPresentationThresholds;
+ TrustedPresentationListener mTrustedPresentationListener;
+ bool mLastComputedTrustedPresentationState = false;
+ bool mLastReportedTrustedPresentationState = false;
+ int64_t mEnteredTrustedPresentationStateTime = -1;
+
uint32_t mTransactionFlags{0};
// Updated in doTransaction, used to track the last sequence number we
// committed. Currently this is really only used for updating visible
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 6e64e0a..839500f 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -344,12 +344,13 @@
renderengine::impl::ExternalTexture::Usage::
WRITEABLE);
}
+ auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
constexpr bool kRegionSampling = true;
constexpr bool kGrayscale = false;
if (const auto fenceResult =
- mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
+ mFlinger.captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, buffer,
kRegionSampling, kGrayscale, nullptr)
.get();
fenceResult.ok()) {
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index b62b15c..e8c891e 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -37,7 +37,6 @@
namespace android {
class Layer;
-class Scheduler;
class SurfaceFlinger;
struct SamplingOffsetCallback;
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 387364c..3c20e3b 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -34,6 +34,21 @@
mRotationFlags(rotation),
mLayerStackSpaceRect(layerStackRect) {}
+ static std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> fromTraverseLayersLambda(
+ std::function<void(const LayerVector::Visitor&)> traverseLayers) {
+ return [traverseLayers = std::move(traverseLayers)]() {
+ std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
+ traverseLayers([&](Layer* layer) {
+ // Layer::prepareClientComposition uses the layer's snapshot to populate the
+ // resulting LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings
+ // are generated with the layer's current buffer and geometry.
+ layer->updateSnapshot(true /* updateGeometry */);
+ layers.emplace_back(layer, layer->copyCompositionEngineLayerFE());
+ });
+ return layers;
+ };
+ }
+
virtual ~RenderArea() = default;
// Invoke drawLayers to render layers into the render area.
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index e827c12..9b04497 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -132,6 +132,10 @@
mLooper->sendMessage(handler, Message());
}
+void MessageQueue::postMessageDelayed(sp<MessageHandler>&& handler, nsecs_t uptimeDelay) {
+ mLooper->sendMessageDelayed(uptimeDelay, handler, Message());
+}
+
void MessageQueue::scheduleConfigure() {
struct ConfigureHandler : MessageHandler {
explicit ConfigureHandler(ICompositor& compositor) : compositor(compositor) {}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 71f8645..ad0ea72 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -79,6 +79,7 @@
virtual void setDuration(std::chrono::nanoseconds workDuration) = 0;
virtual void waitMessage() = 0;
virtual void postMessage(sp<MessageHandler>&&) = 0;
+ virtual void postMessageDelayed(sp<MessageHandler>&&, nsecs_t uptimeDelay) = 0;
virtual void scheduleConfigure() = 0;
virtual void scheduleFrame() = 0;
@@ -144,6 +145,7 @@
void waitMessage() override;
void postMessage(sp<MessageHandler>&&) override;
+ void postMessageDelayed(sp<MessageHandler>&&, nsecs_t uptimeDelay) override;
void scheduleConfigure() override;
void scheduleFrame() override;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 30821d8..1d27cfc 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -932,14 +932,22 @@
const char* const whence = __func__;
std::deque<ScoredFrameRate> ranking;
const auto rankFrameRate = [&](const FrameRateMode& frameRateMode) REQUIRES(mLock) {
+ using fps_approx_ops::operator<;
const auto& modePtr = frameRateMode.modePtr;
if (anchorGroupOpt && modePtr->getGroup() != anchorGroupOpt) {
return;
}
+ const bool ascending = (refreshRateOrder == RefreshRateOrder::Ascending);
+ if (ascending && frameRateMode.fps < getMinRefreshRateByPolicyLocked()->getFps()) {
+ // TODO(b/266481656): Once this bug is fixed, we can remove this workaround and actually
+ // use a lower frame rate when we want Ascending frame rates.
+ return;
+ }
+
float score = calculateDistanceScoreFromMax(frameRateMode.fps);
- const bool inverseScore = (refreshRateOrder == RefreshRateOrder::Ascending);
- if (inverseScore) {
+
+ if (ascending) {
score = 1.0f / score;
}
if (preferredDisplayModeOpt) {
@@ -951,6 +959,7 @@
constexpr float kNonPreferredModePenalty = 0.95f;
score *= kNonPreferredModePenalty;
}
+
ALOGV("%s(%s) %s (%s) scored %.2f", whence, ftl::enum_string(refreshRateOrder).c_str(),
to_string(frameRateMode.fps).c_str(), to_string(modePtr->getFps()).c_str(), score);
ranking.emplace_back(ScoredFrameRate{frameRateMode, score});
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index bc465ce..1fc1519 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -60,8 +60,12 @@
namespace android::scheduler {
-Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features)
- : impl::MessageQueue(compositor), mFeatures(features), mSchedulerCallback(callback) {}
+Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features,
+ sp<VsyncModulator> modulatorPtr)
+ : impl::MessageQueue(compositor),
+ mFeatures(features),
+ mVsyncModulator(std::move(modulatorPtr)),
+ mSchedulerCallback(callback) {}
Scheduler::~Scheduler() {
// MessageQueue depends on VsyncSchedule, so first destroy it.
@@ -186,17 +190,19 @@
};
}
-ConnectionHandle Scheduler::createConnection(const char* connectionName,
- frametimeline::TokenManager* tokenManager,
- std::chrono::nanoseconds workDuration,
- std::chrono::nanoseconds readyDuration) {
- auto throttleVsync = makeThrottleVsyncCallback();
- auto getVsyncPeriod = makeGetVsyncPeriodFunction();
- auto eventThread =
- std::make_unique<impl::EventThread>(connectionName, *mVsyncSchedule, tokenManager,
- std::move(throttleVsync), std::move(getVsyncPeriod),
- workDuration, readyDuration);
- return createConnection(std::move(eventThread));
+ConnectionHandle Scheduler::createEventThread(Cycle cycle,
+ frametimeline::TokenManager* tokenManager,
+ std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration) {
+ auto eventThread = std::make_unique<impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf",
+ *mVsyncSchedule, tokenManager,
+ makeThrottleVsyncCallback(),
+ makeGetVsyncPeriodFunction(),
+ workDuration, readyDuration);
+
+ auto& handle = cycle == Cycle::Render ? mAppConnectionHandle : mSfConnectionHandle;
+ handle = createConnection(std::move(eventThread));
+ return handle;
}
ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
@@ -356,6 +362,20 @@
thread->setDuration(workDuration, readyDuration);
}
+void Scheduler::setVsyncConfigSet(const VsyncConfigSet& configs, Period vsyncPeriod) {
+ setVsyncConfig(mVsyncModulator->setVsyncConfigSet(configs), vsyncPeriod);
+}
+
+void Scheduler::setVsyncConfig(const VsyncConfig& config, Period vsyncPeriod) {
+ setDuration(mAppConnectionHandle,
+ /* workDuration */ config.appWorkDuration,
+ /* readyDuration */ config.sfWorkDuration);
+ setDuration(mSfConnectionHandle,
+ /* workDuration */ vsyncPeriod,
+ /* readyDuration */ config.sfWorkDuration);
+ setDuration(config.sfWorkDuration);
+}
+
void Scheduler::enableHardwareVsync() {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 20221d1..ef7d0cf 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -36,6 +36,7 @@
#include <ftl/optional.h>
#include <scheduler/Features.h>
#include <scheduler/Time.h>
+#include <scheduler/VsyncConfig.h>
#include <ui/DisplayId.h>
#include "Display/DisplayMap.h"
@@ -47,6 +48,7 @@
#include "OneShotTimer.h"
#include "RefreshRateSelector.h"
#include "Utils/Dumper.h"
+#include "VsyncModulator.h"
#include "VsyncSchedule.h"
namespace android::scheduler {
@@ -104,7 +106,7 @@
using Impl = android::impl::MessageQueue;
public:
- Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags);
+ Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, sp<VsyncModulator>);
virtual ~Scheduler();
void startTimers();
@@ -139,9 +141,21 @@
return std::move(future);
}
- ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*,
- std::chrono::nanoseconds workDuration,
- std::chrono::nanoseconds readyDuration);
+ template <typename F, typename T = std::invoke_result_t<F>>
+ [[nodiscard]] std::future<T> scheduleDelayed(F&& f, nsecs_t uptimeDelay) {
+ auto [task, future] = makeTask(std::move(f));
+ postMessageDelayed(std::move(task), uptimeDelay);
+ return std::move(future);
+ }
+
+ enum class Cycle {
+ Render, // Surface rendering.
+ LastComposite // Ahead of display compositing by one refresh period.
+ };
+
+ ConnectionHandle createEventThread(Cycle, frametimeline::TokenManager*,
+ std::chrono::nanoseconds workDuration,
+ std::chrono::nanoseconds readyDuration);
sp<IDisplayEventConnection> createDisplayEventConnection(
ConnectionHandle, EventRegistrationFlags eventRegistration = {});
@@ -161,6 +175,18 @@
void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration);
+ const VsyncModulator& vsyncModulator() const { return *mVsyncModulator; }
+
+ template <typename... Args,
+ typename Handler = std::optional<VsyncConfig> (VsyncModulator::*)(Args...)>
+ void modulateVsync(Handler handler, Args... args) {
+ if (const auto config = (*mVsyncModulator.*handler)(args...)) {
+ setVsyncConfig(*config, getLeaderVsyncPeriod());
+ }
+ }
+
+ void setVsyncConfigSet(const VsyncConfigSet&, Period vsyncPeriod);
+
// Sets the render rate for the scheduler to run at.
void setRenderRate(Fps);
@@ -232,8 +258,8 @@
// Retrieves the overridden refresh rate for a given uid.
std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock);
- nsecs_t getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) {
- return leaderSelectorPtr()->getActiveMode().fps.getPeriodNsecs();
+ Period getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) {
+ return leaderSelectorPtr()->getActiveMode().fps.getPeriod();
}
// Returns the framerate of the layer with the given sequence ID
@@ -263,6 +289,7 @@
void displayPowerTimerCallback(TimerState);
void setVsyncPeriod(nsecs_t period);
+ void setVsyncConfig(const VsyncConfig&, Period vsyncPeriod);
// Chooses a leader among the registered displays, unless `leaderIdOpt` is specified. The new
// `mLeaderDisplayId` is never `std::nullopt`.
@@ -323,6 +350,9 @@
mutable std::mutex mConnectionsLock;
std::unordered_map<ConnectionHandle, Connection> mConnections GUARDED_BY(mConnectionsLock);
+ ConnectionHandle mAppConnectionHandle;
+ ConnectionHandle mSfConnectionHandle;
+
mutable std::mutex mHWVsyncLock;
bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock) = false;
bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock) = false;
@@ -332,6 +362,9 @@
const FeatureFlags mFeatures;
std::optional<VsyncSchedule> mVsyncSchedule;
+ // Shifts the VSYNC phase during certain transactions and refresh rate changes.
+ const sp<VsyncModulator> mVsyncModulator;
+
// Used to choose refresh rate if content detection is enabled.
LayerHistory mLayerHistory;
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
index ff31651..6ae10f3 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp
@@ -42,12 +42,12 @@
VsyncConfiguration::VsyncConfiguration(Fps currentFps) : mRefreshRateFps(currentFps) {}
-PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(Fps fps) const {
+VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(Fps fps) const {
std::lock_guard lock(mLock);
return getConfigsForRefreshRateLocked(fps);
}
-PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRateLocked(Fps fps) const {
+VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRateLocked(Fps fps) const {
if (const auto offsets = mOffsetsCache.get(fps)) {
return offsets->get();
}
@@ -134,7 +134,7 @@
mThresholdForNextVsync(thresholdForNextVsync),
mHwcMinWorkDuration(hwcMinWorkDuration) {}
-PhaseOffsets::VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const {
+VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const {
if (vsyncDuration < std::chrono::nanoseconds(15ms).count()) {
return getHighFpsOffsets(vsyncDuration);
} else {
@@ -158,7 +158,7 @@
}
} // namespace
-PhaseOffsets::VsyncConfigSet PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
+VsyncConfigSet PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
const auto earlySfOffset =
mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
@@ -196,7 +196,7 @@
};
}
-PhaseOffsets::VsyncConfigSet PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
+VsyncConfigSet PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
const auto earlySfOffset =
mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
? mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs)
@@ -286,7 +286,7 @@
}
} // namespace
-WorkDuration::VsyncConfigSet WorkDuration::constructOffsets(nsecs_t vsyncDuration) const {
+VsyncConfigSet WorkDuration::constructOffsets(nsecs_t vsyncDuration) const {
const auto sfDurationFixup = [vsyncDuration](nsecs_t duration) {
return duration == -1 ? std::chrono::nanoseconds(vsyncDuration) - 1ms
: std::chrono::nanoseconds(duration);
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
index 02ebd70..a24e43f 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.h
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -20,12 +20,12 @@
#include <optional>
#include <string>
+#include <android-base/thread_annotations.h>
#include <ftl/small_map.h>
#include <utils/Timers.h>
#include <scheduler/Fps.h>
-
-#include "VsyncModulator.h"
+#include <scheduler/VsyncConfig.h>
namespace android::scheduler {
@@ -37,8 +37,6 @@
*/
class VsyncConfiguration {
public:
- using VsyncConfigSet = VsyncModulator::VsyncConfigSet;
-
virtual ~VsyncConfiguration() = default;
virtual VsyncConfigSet getCurrentConfigs() const = 0;
virtual VsyncConfigSet getConfigsForRefreshRate(Fps fps) const = 0;
@@ -85,7 +83,7 @@
void dump(std::string& result) const override;
protected:
- virtual VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const = 0;
+ virtual VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const = 0;
VsyncConfigSet getConfigsForRefreshRateLocked(Fps fps) const REQUIRES(mLock);
@@ -115,7 +113,7 @@
nsecs_t hwcMinWorkDuration);
private:
- VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+ VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
VsyncConfigSet getDefaultOffsets(nsecs_t vsyncPeriod) const;
VsyncConfigSet getHighFpsOffsets(nsecs_t vsyncPeriod) const;
@@ -154,7 +152,7 @@
nsecs_t hwcMinWorkDuration);
private:
- VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
+ VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override;
const nsecs_t mSfDuration;
const nsecs_t mAppDuration;
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
index 138d8d6..c9af4c2 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -40,7 +40,7 @@
mNow(now),
mTraceDetailedInfo(base::GetBoolProperty("debug.sf.vsync_trace_detailed_info", false)) {}
-VsyncModulator::VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) {
+VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) {
std::lock_guard<std::mutex> lock(mMutex);
mVsyncConfigSet = config;
return updateVsyncConfigLocked();
@@ -129,7 +129,7 @@
return updateVsyncConfig();
}
-VsyncModulator::VsyncConfig VsyncModulator::getVsyncConfig() const {
+VsyncConfig VsyncModulator::getVsyncConfig() const {
std::lock_guard<std::mutex> lock(mMutex);
return mVsyncConfig;
}
@@ -147,7 +147,7 @@
}
}
-const VsyncModulator::VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
+const VsyncConfig& VsyncModulator::getNextVsyncConfig() const {
switch (getNextVsyncConfigType()) {
case VsyncConfigType::Early:
return mVsyncConfigSet.early;
@@ -158,12 +158,12 @@
}
}
-VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfig() {
+VsyncConfig VsyncModulator::updateVsyncConfig() {
std::lock_guard<std::mutex> lock(mMutex);
return updateVsyncConfigLocked();
}
-VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfigLocked() {
+VsyncConfig VsyncModulator::updateVsyncConfigLocked() {
const VsyncConfig& offsets = getNextVsyncConfig();
mVsyncConfig = offsets;
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index 537cae1..dc4dafd 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -25,19 +25,13 @@
#include <binder/IBinder.h>
#include <utils/Timers.h>
+#include <scheduler/TransactionSchedule.h>
+#include <scheduler/VsyncConfig.h>
+
#include "../WpHash.h"
namespace android::scheduler {
-// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
-// when a transaction is flagged EarlyStart or Early, lasting until an EarlyEnd transaction or a
-// fixed number of frames, respectively.
-enum class TransactionSchedule {
- Late, // Default.
- EarlyStart,
- EarlyEnd
-};
-
// Modulates VSYNC phase depending on transaction schedule and refresh rate changes.
class VsyncModulator : public IBinder::DeathRecipient {
public:
@@ -51,39 +45,8 @@
// This may keep early offsets for an extra frame, but avoids a race with transaction commit.
static const std::chrono::nanoseconds MIN_EARLY_TRANSACTION_TIME;
- // Phase offsets and work durations for SF and app deadlines from VSYNC.
- struct VsyncConfig {
- nsecs_t sfOffset;
- nsecs_t appOffset;
- std::chrono::nanoseconds sfWorkDuration;
- std::chrono::nanoseconds appWorkDuration;
-
- bool operator==(const VsyncConfig& other) const {
- return sfOffset == other.sfOffset && appOffset == other.appOffset &&
- sfWorkDuration == other.sfWorkDuration &&
- appWorkDuration == other.appWorkDuration;
- }
-
- bool operator!=(const VsyncConfig& other) const { return !(*this == other); }
- };
-
using VsyncConfigOpt = std::optional<VsyncConfig>;
- struct VsyncConfigSet {
- VsyncConfig early; // Used for early transactions, and during refresh rate change.
- VsyncConfig earlyGpu; // Used during GPU composition.
- VsyncConfig late; // Default.
- std::chrono::nanoseconds hwcMinWorkDuration; // Used for calculating the
- // earliest present time
-
- bool operator==(const VsyncConfigSet& other) const {
- return early == other.early && earlyGpu == other.earlyGpu && late == other.late &&
- hwcMinWorkDuration == other.hwcMinWorkDuration;
- }
-
- bool operator!=(const VsyncConfigSet& other) const { return !(*this == other); }
- };
-
using Clock = std::chrono::steady_clock;
using TimePoint = Clock::time_point;
using Now = TimePoint (*)();
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index 5522ff8..d6329e2 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -23,7 +23,7 @@
#include <type_traits>
#include <android-base/stringprintf.h>
-#include <utils/Timers.h>
+#include <scheduler/Time.h>
namespace android {
@@ -52,6 +52,7 @@
constexpr float getValue() const { return mFrequency; }
int getIntValue() const { return static_cast<int>(std::round(mFrequency)); }
+ constexpr Period getPeriod() const { return Period::fromNs(mPeriod); }
constexpr nsecs_t getPeriodNsecs() const { return mPeriod; }
private:
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/TransactionSchedule.h b/services/surfaceflinger/Scheduler/include/scheduler/TransactionSchedule.h
new file mode 100644
index 0000000..6fc44dd
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/TransactionSchedule.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android::scheduler {
+
+// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
+// when a transaction is flagged EarlyStart or Early, lasting until an EarlyEnd transaction or a
+// fixed number of frames, respectively.
+enum class TransactionSchedule {
+ Late, // Default.
+ EarlyStart,
+ EarlyEnd
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h b/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h
new file mode 100644
index 0000000..3b1985f
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/VsyncConfig.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+
+#include <utils/Timers.h>
+
+namespace android::scheduler {
+
+// Phase offsets and work durations for SF and app deadlines from VSYNC.
+struct VsyncConfig {
+ nsecs_t sfOffset;
+ nsecs_t appOffset;
+ std::chrono::nanoseconds sfWorkDuration;
+ std::chrono::nanoseconds appWorkDuration;
+
+ bool operator==(const VsyncConfig& other) const {
+ return sfOffset == other.sfOffset && appOffset == other.appOffset &&
+ sfWorkDuration == other.sfWorkDuration && appWorkDuration == other.appWorkDuration;
+ }
+
+ bool operator!=(const VsyncConfig& other) const { return !(*this == other); }
+};
+
+struct VsyncConfigSet {
+ VsyncConfig early; // Used for early transactions, and during refresh rate change.
+ VsyncConfig earlyGpu; // Used during GPU composition.
+ VsyncConfig late; // Default.
+ std::chrono::nanoseconds hwcMinWorkDuration; // Used for calculating the earliest present time.
+
+ bool operator==(const VsyncConfigSet& other) const {
+ return early == other.early && earlyGpu == other.earlyGpu && late == other.late &&
+ hwcMinWorkDuration == other.hwcMinWorkDuration;
+ }
+
+ bool operator!=(const VsyncConfigSet& other) const { return !(*this == other); }
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 40de4d6..7d0dc93 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -130,6 +130,7 @@
#include "FrameTracer/FrameTracer.h"
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerHandle.h"
+#include "FrontEnd/LayerSnapshot.h"
#include "HdrLayerInfoReporter.h"
#include "Layer.h"
#include "LayerProtoHelper.h"
@@ -142,6 +143,7 @@
#include "Scheduler/LayerHistory.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncConfiguration.h"
+#include "Scheduler/VsyncModulator.h"
#include "ScreenCaptureOutput.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlingerProperties.h"
@@ -189,6 +191,7 @@
using gui::LayerMetadata;
using gui::WindowInfo;
using gui::aidl_utils::binderStatusFromStatusT;
+using scheduler::VsyncModulator;
using ui::Dataspace;
using ui::DisplayPrimaries;
using ui::RenderIntent;
@@ -1163,7 +1166,7 @@
mScheduler->resyncToHardwareVsync(true, mode.modePtr->getFps());
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// VsyncController model is locked.
- modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
+ mScheduler->modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
updatePhaseConfiguration(mode.fps);
mScheduler->setModeChangePending(true);
@@ -1488,14 +1491,26 @@
std::transform(combination.pixelFormats.cbegin(), combination.pixelFormats.cend(),
std::back_inserter(pixelFormats),
[](const auto& val) { return static_cast<int32_t>(val); });
- std::vector<int32_t> dataspaces;
- dataspaces.reserve(combination.dataspaces.size());
- std::transform(combination.dataspaces.cbegin(), combination.dataspaces.cend(),
- std::back_inserter(dataspaces),
+ std::vector<int32_t> standards;
+ standards.reserve(combination.standards.size());
+ std::transform(combination.standards.cbegin(), combination.standards.cend(),
+ std::back_inserter(standards),
+ [](const auto& val) { return static_cast<int32_t>(val); });
+ std::vector<int32_t> transfers;
+ transfers.reserve(combination.transfers.size());
+ std::transform(combination.transfers.cbegin(), combination.transfers.cend(),
+ std::back_inserter(transfers),
+ [](const auto& val) { return static_cast<int32_t>(val); });
+ std::vector<int32_t> ranges;
+ ranges.reserve(combination.ranges.size());
+ std::transform(combination.ranges.cbegin(), combination.ranges.cend(),
+ std::back_inserter(ranges),
[](const auto& val) { return static_cast<int32_t>(val); });
gui::OverlayProperties::SupportedBufferCombinations outCombination;
outCombination.pixelFormats = std::move(pixelFormats);
- outCombination.dataspaces = std::move(dataspaces);
+ outCombination.standards = std::move(standards);
+ outCombination.transfers = std::move(transfers);
+ outCombination.ranges = std::move(ranges);
outProperties->combinations.emplace_back(outCombination);
}
outProperties->supportMixedColorSpaces = aidlProperties.supportMixedColorSpaces;
@@ -2027,7 +2042,7 @@
bool periodFlushed = false;
mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
if (periodFlushed) {
- modulateVsync(&VsyncModulator::onRefreshRateChangeCompleted);
+ mScheduler->modulateVsync(&VsyncModulator::onRefreshRateChangeCompleted);
}
}
@@ -2105,7 +2120,7 @@
const auto& schedule = mScheduler->getVsyncSchedule();
const TimePoint vsyncDeadline = schedule.vsyncDeadlineAfter(frameTime);
- if (mVsyncModulator->getVsyncConfig().sfOffset > 0) {
+ if (mScheduler->vsyncModulator().getVsyncConfig().sfOffset > 0) {
return vsyncDeadline;
}
@@ -2219,17 +2234,19 @@
mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay &&
activeDisplay->getPowerMode() == hal::PowerMode::ON;
if (mPowerHintSessionEnabled) {
- const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
- const Period vsyncPeriod = Period::fromNs(display->getActiveMode().fps.getPeriodNsecs());
mPowerAdvisor->setCommitStart(frameTime);
mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime);
// Frame delay is how long we should have minus how long we actually have.
- const Duration idealSfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration;
+ const Duration idealSfWorkDuration =
+ mScheduler->vsyncModulator().getVsyncConfig().sfWorkDuration;
const Duration frameDelay = idealSfWorkDuration - (mExpectedPresentTime - frameTime);
mPowerAdvisor->setFrameDelay(frameDelay);
mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration);
+
+ const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
+ const Period vsyncPeriod = display->getActiveMode().fps.getPeriod();
mPowerAdvisor->setTargetWorkDuration(vsyncPeriod);
// Send early hint here to make sure there's not another frame pending
@@ -2254,9 +2271,17 @@
bool needsTraversal = false;
if (clearTransactionFlags(eTransactionFlushNeeded)) {
+ // 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.
+ std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
needsTraversal |= commitMirrorDisplays(vsyncId);
needsTraversal |= commitCreatedLayers(vsyncId);
- needsTraversal |= flushTransactionQueues(vsyncId);
+ needsTraversal |= applyTransactions(transactions, vsyncId);
}
const bool shouldCommit =
@@ -2432,7 +2457,7 @@
scheduleComposite(FrameHint::kNone);
}
- postComposition();
+ postComposition(presentTime);
const bool prevFrameHadClientComposition = mHadClientComposition;
@@ -2456,7 +2481,7 @@
// TODO: b/160583065 Enable skip validation when SF caches all client composition layers
const bool usedGpuComposition = mHadClientComposition || mReusedClientComposition;
- modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
+ mScheduler->modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
mLayersWithQueuedFrames.clear();
if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
@@ -2464,7 +2489,7 @@
mLayerTracing.notify(mVisibleRegionsDirty, frameTime.ns(), vsyncId.value);
}
- mVisibleRegionsWereDirtyThisFrame = mVisibleRegionsDirty; // Cache value for use in post-comp
+ if (mVisibleRegionsDirty) mHdrLayerInfoChanged = true;
mVisibleRegionsDirty = false;
if (mCompositionEngine->needsAnotherUpdate()) {
@@ -2491,22 +2516,33 @@
mLayersPendingRefresh.clear();
}
-bool SurfaceFlinger::isHdrLayer(Layer* layer) const {
- // Treat all layers as non-HDR if:
- // 1. They do not have a valid HDR dataspace. Currently we treat those as PQ or HLG. and
- // 2. The layer is allowed to be dimmed. WindowManager may disable dimming in order to
- // keep animations invoking SDR screenshots of HDR layers seamless. Treat such tagged
- // layers as HDR so that DisplayManagerService does not try to change the screen brightness
- if (!isHdrDataspace(layer->getDataSpace()) && layer->isDimmingEnabled()) {
- return false;
- }
+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
+ // reduce power consumption during camera recording
if (mIgnoreHdrCameraLayers) {
- auto buffer = layer->getBuffer();
- if (buffer && (buffer->getUsage() & GRALLOC_USAGE_HW_CAMERA_WRITE) != 0) {
+ if (snapshot.externalTexture &&
+ (snapshot.externalTexture->getUsage() & GRALLOC_USAGE_HW_CAMERA_WRITE) != 0) {
return false;
}
}
- return true;
+ if (isHdrDataspace(snapshot.dataspace)) {
+ return true;
+ }
+ // If the layer is not allowed to be dimmed, treat it as HDR. WindowManager may disable
+ // dimming in order to keep animations invoking SDR screenshots of HDR layers seamless.
+ // Treat such tagged layers as HDR so that DisplayManagerService does not try to change
+ // the screen brightness
+ if (!snapshot.dimmingEnabled) {
+ return true;
+ }
+ // RANGE_EXTENDED layers may identify themselves as being "HDR" via a desired sdr/hdr ratio
+ if ((snapshot.dataspace & (int32_t)Dataspace::RANGE_MASK) ==
+ (int32_t)Dataspace::RANGE_EXTENDED &&
+ snapshot.desiredSdrHdrRatio > 1.01f) {
+ return true;
+ }
+ return false;
}
ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(DisplayId displayId,
@@ -2546,7 +2582,7 @@
return ui::ROTATION_0;
}
-void SurfaceFlinger::postComposition() {
+void SurfaceFlinger::postComposition(nsecs_t callTime) {
ATRACE_CALL();
ALOGV(__func__);
@@ -2625,18 +2661,20 @@
mAddingHDRLayerInfoListener = false;
}
- if (haveNewListeners || mSomeDataspaceChanged || mVisibleRegionsWereDirtyThisFrame) {
+ if (haveNewListeners || mHdrLayerInfoChanged) {
for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
HdrLayerInfoReporter::HdrLayerInfo info;
int32_t maxArea = 0;
mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
const auto layerFe = layer->getCompositionEngineLayerFE();
- if (layer->isVisible() &&
- compositionDisplay->includesLayer(layer->getOutputFilter())) {
- if (isHdrLayer(layer)) {
+ const frontend::LayerSnapshot& snapshot = *layer->getLayerSnapshot();
+ if (snapshot.isVisible &&
+ compositionDisplay->includesLayer(snapshot.outputFilter)) {
+ if (isHdrLayer(snapshot)) {
const auto* outputLayer =
compositionDisplay->getOutputLayerForLayer(layerFe);
if (outputLayer) {
+ info.mergeDesiredRatio(snapshot.desiredSdrHdrRatio);
info.numberOfHdrLayers++;
const auto displayFrame = outputLayer->getState().displayFrame;
const int32_t area = displayFrame.width() * displayFrame.height();
@@ -2653,8 +2691,7 @@
}
}
- mSomeDataspaceChanged = false;
- mVisibleRegionsWereDirtyThisFrame = false;
+ mHdrLayerInfoChanged = false;
mTransactionCallbackInvoker.addPresentFence(std::move(presentFence));
mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
@@ -2712,6 +2749,17 @@
}
}
+ if (mNumTrustedPresentationListeners > 0) {
+ // We avoid any reverse traversal upwards so this shouldn't be too expensive
+ mDrawingState.traverse([&](Layer* layer) {
+ if (!layer->hasTrustedPresentationListener()) {
+ return;
+ }
+ layer->updateTrustedPresentationState(display, nanoseconds_to_milliseconds(callTime),
+ false);
+ });
+ }
+
// Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
// side-effect of getTotalSize(), so we check that again here
if (ATRACE_ENABLED()) {
@@ -2768,7 +2816,7 @@
// 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.
- modulateVsync(&VsyncModulator::onTransactionCommit);
+ mScheduler->modulateVsync(&VsyncModulator::onTransactionCommit);
commitTransactionsLocked(clearTransactionFlags(eTransactionMask));
mDebugInTransaction = 0;
@@ -3569,19 +3617,18 @@
}
void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
+ using namespace scheduler;
+
LOG_ALWAYS_FATAL_IF(mScheduler);
const auto activeMode = display->refreshRateSelector().getActiveMode();
const Fps activeRefreshRate = activeMode.fps;
mRefreshRateStats =
- std::make_unique<scheduler::RefreshRateStats>(*mTimeStats, activeRefreshRate,
- hal::PowerMode::OFF);
+ std::make_unique<RefreshRateStats>(*mTimeStats, activeRefreshRate, hal::PowerMode::OFF);
mVsyncConfiguration = getFactory().createVsyncConfiguration(activeRefreshRate);
- mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
- using Feature = scheduler::Feature;
- scheduler::FeatureFlags features;
+ FeatureFlags features;
if (sysprop::use_content_detection_for_refresh_rate(false)) {
features |= Feature::kContentDetection;
@@ -3597,9 +3644,11 @@
features |= Feature::kKernelIdleTimer;
}
- mScheduler = std::make_unique<scheduler::Scheduler>(static_cast<ICompositor&>(*this),
- static_cast<ISchedulerCallback&>(*this),
- features);
+ auto modulatorPtr = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
+
+ mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this),
+ static_cast<ISchedulerCallback&>(*this), features,
+ std::move(modulatorPtr));
mScheduler->createVsyncSchedule(features);
mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
@@ -3607,15 +3656,17 @@
mScheduler->startTimers();
const auto configs = mVsyncConfiguration->getCurrentConfigs();
- const nsecs_t vsyncPeriod = activeRefreshRate.getPeriodNsecs();
+
mAppConnectionHandle =
- mScheduler->createConnection("app", mFrameTimeline->getTokenManager(),
- /*workDuration=*/configs.late.appWorkDuration,
- /*readyDuration=*/configs.late.sfWorkDuration);
+ mScheduler->createEventThread(Scheduler::Cycle::Render,
+ mFrameTimeline->getTokenManager(),
+ /* workDuration */ configs.late.appWorkDuration,
+ /* readyDuration */ configs.late.sfWorkDuration);
mSfConnectionHandle =
- mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(),
- /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
- /*readyDuration=*/configs.late.sfWorkDuration);
+ mScheduler->createEventThread(Scheduler::Cycle::LastComposite,
+ mFrameTimeline->getTokenManager(),
+ /* workDuration */ activeRefreshRate.getPeriod(),
+ /* readyDuration */ configs.late.sfWorkDuration);
mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(),
*mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration);
@@ -3626,21 +3677,10 @@
mFpsReporter = sp<FpsReporter>::make(*mFrameTimeline, *this);
}
-void SurfaceFlinger::updatePhaseConfiguration(const Fps& refreshRate) {
+void SurfaceFlinger::updatePhaseConfiguration(Fps refreshRate) {
mVsyncConfiguration->setRefreshRateFps(refreshRate);
- setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
- refreshRate.getPeriodNsecs());
-}
-
-void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config,
- nsecs_t vsyncPeriod) {
- mScheduler->setDuration(mAppConnectionHandle,
- /*workDuration=*/config.appWorkDuration,
- /*readyDuration=*/config.sfWorkDuration);
- mScheduler->setDuration(mSfConnectionHandle,
- /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
- /*readyDuration=*/config.sfWorkDuration);
- mScheduler->setDuration(config.sfWorkDuration);
+ mScheduler->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs(),
+ refreshRate.getPeriod());
}
void SurfaceFlinger::doCommitTransactions() {
@@ -3839,10 +3879,14 @@
void SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule schedule,
const sp<IBinder>& applyToken, FrameHint frameHint) {
- modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, applyToken);
+ mScheduler->modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, applyToken);
if (const bool scheduled = mTransactionFlags.fetch_or(mask) & mask; !scheduled) {
scheduleCommit(frameHint);
+ } else if (frameHint == FrameHint::kActive) {
+ // Even if the next frame is already scheduled, we should reset the idle timer
+ // as a new activity just happened.
+ mScheduler->resetIdleTimer();
}
}
@@ -3952,19 +3996,20 @@
std::bind(&SurfaceFlinger::transactionReadyBufferCheck, this, std::placeholders::_1));
}
+// For tests only
bool SurfaceFlinger::flushTransactionQueues(VsyncId vsyncId) {
- // to prevent onHandleDestroyed from being called while the lock is held,
- // we must keep a copy of the transactions (specifically the composer
- // states) around outside the scope of the lock
std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
- {
- Mutex::Autolock _l(mStateLock);
- return applyTransactions(transactions, vsyncId);
- }
+ return applyTransactions(transactions, vsyncId);
}
bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions,
VsyncId vsyncId) {
+ Mutex::Autolock _l(mStateLock);
+ return applyTransactionsLocked(transactions, vsyncId);
+}
+
+bool SurfaceFlinger::applyTransactionsLocked(std::vector<TransactionState>& transactions,
+ VsyncId vsyncId) {
bool needsTraversal = false;
// Now apply all transactions.
for (auto& transaction : transactions) {
@@ -3973,7 +4018,7 @@
transaction.displays, transaction.flags,
transaction.inputWindowCommands,
transaction.desiredPresentTime, transaction.isAutoTimestamp,
- transaction.buffer, transaction.postTime,
+ std::move(transaction.uncacheBufferIds), transaction.postTime,
transaction.permissions, transaction.hasListenerCallbacks,
transaction.listenerCallbacks, transaction.originPid,
transaction.originUid, transaction.id);
@@ -3999,7 +4044,7 @@
const auto predictedPresentTime = TimePoint::fromNs(prediction->presentTime);
// The duration for which SF can delay a frame if it is considered early based on the
- // VsyncModulator::VsyncConfig::appWorkDuration.
+ // VsyncConfig::appWorkDuration.
if (constexpr std::chrono::nanoseconds kEarlyLatchMaxThreshold = 100ms;
std::chrono::abs(predictedPresentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold) {
return false;
@@ -4040,7 +4085,7 @@
// We don't want to latch unsignaled if are in early / client composition
// as it leads to jank due to RenderEngine waiting for unsignaled buffer
// or window animations being slow.
- const auto isDefaultVsyncConfig = mVsyncModulator->isVsyncConfigDefault();
+ const auto isDefaultVsyncConfig = mScheduler->vsyncModulator().isVsyncConfigDefault();
if (!isDefaultVsyncConfig) {
ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; !isDefaultVsyncConfig)",
__func__);
@@ -4061,8 +4106,9 @@
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
- bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
+ bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
+ bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+ uint64_t transactionId) {
ATRACE_CALL();
uint32_t permissions =
@@ -4096,6 +4142,15 @@
const int originPid = ipc->getCallingPid();
const int originUid = ipc->getCallingUid();
+ std::vector<uint64_t> uncacheBufferIds;
+ uncacheBufferIds.reserve(uncacheBuffers.size());
+ for (const auto& uncacheBuffer : uncacheBuffers) {
+ sp<GraphicBuffer> buffer = ClientCache::getInstance().erase(uncacheBuffer);
+ if (buffer != nullptr) {
+ uncacheBufferIds.push_back(buffer->getId());
+ }
+ }
+
std::vector<ResolvedComposerState> resolvedStates;
resolvedStates.reserve(states.size());
for (auto& state : states) {
@@ -4113,14 +4168,22 @@
}
}
- TransactionState state{frameTimelineInfo, resolvedStates,
- displays, flags,
- applyToken, inputWindowCommands,
- desiredPresentTime, isAutoTimestamp,
- uncacheBuffer, postTime,
- permissions, hasListenerCallbacks,
- listenerCallbacks, originPid,
- originUid, transactionId};
+ TransactionState state{frameTimelineInfo,
+ resolvedStates,
+ displays,
+ flags,
+ applyToken,
+ inputWindowCommands,
+ desiredPresentTime,
+ isAutoTimestamp,
+ std::move(uncacheBufferIds),
+ postTime,
+ permissions,
+ hasListenerCallbacks,
+ listenerCallbacks,
+ originPid,
+ originUid,
+ transactionId};
if (mTransactionTracing) {
mTransactionTracing->addQueuedTransaction(state);
@@ -4144,7 +4207,7 @@
Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
- const client_cache_t& uncacheBuffer,
+ const std::vector<uint64_t>& uncacheBufferIds,
const int64_t postTime, uint32_t permissions,
bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
@@ -4185,11 +4248,8 @@
ALOGE("Only privileged callers are allowed to send input commands.");
}
- if (uncacheBuffer.isValid()) {
- sp<GraphicBuffer> buffer = ClientCache::getInstance().erase(uncacheBuffer);
- if (buffer != nullptr) {
- mBufferIdsToUncache.push_back(buffer->getId());
- }
+ for (uint64_t uncacheBufferId : uncacheBufferIds) {
+ mBufferIdsToUncache.push_back(uncacheBufferId);
}
// If a synchronous transaction is explicitly requested without any changes, force a transaction
@@ -4450,9 +4510,6 @@
if (what & layer_state_t::eDataspaceChanged) {
if (layer->setDataspace(s.dataspace)) flags |= eTraversalNeeded;
}
- if (what & layer_state_t::eHdrMetadataChanged) {
- if (layer->setHdrMetadata(s.hdrMetadata)) flags |= eTraversalNeeded;
- }
if (what & layer_state_t::eSurfaceDamageRegionChanged) {
if (layer->setSurfaceDamageRegion(s.surfaceDamageRegion)) flags |= eTraversalNeeded;
}
@@ -4526,6 +4583,14 @@
if (what & layer_state_t::eDimmingEnabledChanged) {
if (layer->setDimmingEnabled(s.dimmingEnabled)) flags |= eTraversalNeeded;
}
+ if (what & layer_state_t::eExtendedRangeBrightnessChanged) {
+ if (layer->setExtendedRangeBrightness(s.currentSdrHdrRatio, s.desiredSdrHdrRatio)) {
+ 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.isTrustedOverlay)) {
flags |= eTraversalNeeded;
@@ -4587,6 +4652,11 @@
layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime);
}
+ if (what & layer_state_t::eTrustedPresentationInfoChanged) {
+ layer->setTrustedPresentationInfo(s.trustedPresentationThresholds,
+ s.trustedPresentationListener);
+ }
+
if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
// Do not put anything that updates layer state or modifies flags after
// setTransactionCompletedListener
@@ -6350,8 +6420,9 @@
auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
traverseLayersInLayerStack(layerStack, args.uid, visitor);
};
+ auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
- auto future = captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
+ auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize,
args.pixelFormat, args.allowProtected, args.grayscale,
captureListener);
return fenceStatus(future.get());
@@ -6386,6 +6457,7 @@
auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
};
+ auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
if (captureListener == nullptr) {
ALOGE("capture screen must provide a capture listener callback");
@@ -6395,7 +6467,7 @@
constexpr bool kAllowProtected = false;
constexpr bool kGrayscale = false;
- auto future = captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
+ auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size,
ui::PixelFormat::RGBA_8888, kAllowProtected, kGrayscale,
captureListener);
return fenceStatus(future.get());
@@ -6413,7 +6485,7 @@
ui::Size reqSize;
sp<Layer> parent;
Rect crop(args.sourceCrop);
- std::unordered_set<sp<Layer>, SpHash<Layer>> excludeLayers;
+ std::unordered_set<uint32_t> excludeLayerIds;
ui::Dataspace dataspace;
// Call this before holding mStateLock to avoid any deadlocking.
@@ -6453,9 +6525,9 @@
reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY);
for (const auto& handle : args.excludeHandles) {
- sp<Layer> excludeLayer = LayerHandle::getLayer(handle);
- if (excludeLayer != nullptr) {
- excludeLayers.emplace(excludeLayer);
+ uint32_t excludeLayer = LayerHandle::getLayerId(handle);
+ if (excludeLayer != UNASSIGNED_LAYER_ID) {
+ excludeLayerIds.emplace(excludeLayer);
} else {
ALOGW("Invalid layer handle passed as excludeLayer to captureLayers");
return NAME_NOT_FOUND;
@@ -6484,7 +6556,7 @@
args.captureSecureLayers);
});
- auto traverseLayers = [parent, args, excludeLayers](const LayerVector::Visitor& visitor) {
+ auto traverseLayers = [parent, args, excludeLayerIds](const LayerVector::Visitor& visitor) {
parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
if (!layer->isVisible()) {
return;
@@ -6496,7 +6568,7 @@
auto p = sp<Layer>::fromExisting(layer);
while (p != nullptr) {
- if (excludeLayers.count(p) != 0) {
+ if (excludeLayerIds.count(p->sequence) != 0) {
return;
}
p = p->getParent();
@@ -6505,20 +6577,21 @@
visitor(layer);
});
};
+ auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
if (captureListener == nullptr) {
ALOGE("capture screen must provide a capture listener callback");
return BAD_VALUE;
}
- auto future = captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
+ auto future = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize,
args.pixelFormat, args.allowProtected, args.grayscale,
captureListener);
return fenceStatus(future.get());
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon(
- RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers,
+ RenderAreaFuture renderAreaFuture, GetLayerSnapshotsFunction getLayerSnapshots,
ui::Size bufferSize, ui::PixelFormat reqPixelFormat, bool allowProtected, bool grayscale,
const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
@@ -6537,15 +6610,18 @@
const bool supportsProtected = getRenderEngine().supportsProtectedContent();
bool hasProtectedLayer = false;
if (allowProtected && supportsProtected) {
- auto future = mScheduler->schedule([=]() {
- bool protectedLayerFound = false;
- traverseLayers([&](Layer* layer) {
- protectedLayerFound =
- protectedLayerFound || (layer->isVisible() && layer->isProtected());
- });
- return protectedLayerFound;
- });
- hasProtectedLayer = future.get();
+ hasProtectedLayer = mScheduler
+ ->schedule([=]() {
+ bool protectedLayerFound = false;
+ auto layers = getLayerSnapshots();
+ for (auto& [layer, layerFe] : layers) {
+ protectedLayerFound |=
+ (layerFe->mSnapshot->isVisible &&
+ layerFe->mSnapshot->hasProtectedContent);
+ }
+ return protectedLayerFound;
+ })
+ .get();
}
const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
@@ -6570,12 +6646,12 @@
renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
renderengine::impl::ExternalTexture::Usage::
WRITEABLE);
- return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, texture,
+ return captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, texture,
false /* regionSampling */, grayscale, captureListener);
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon(
- RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers,
+ RenderAreaFuture renderAreaFuture, GetLayerSnapshotsFunction getLayerSnapshots,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
bool grayscale, const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
@@ -6598,7 +6674,7 @@
ftl::SharedFuture<FenceResult> renderFuture;
renderArea->render([&]() FTL_FAKE_GUARD(kMainThreadContext) {
- renderFuture = renderScreenImpl(renderArea, traverseLayers, buffer,
+ renderFuture = renderScreenImpl(renderArea, getLayerSnapshots, buffer,
canCaptureBlackoutContent, regionSampling,
grayscale, captureResults);
});
@@ -6626,18 +6702,26 @@
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
- std::shared_ptr<const RenderArea> renderArea, TraverseLayersFunction traverseLayers,
+ std::shared_ptr<const RenderArea> renderArea, GetLayerSnapshotsFunction getLayerSnapshots,
const std::shared_ptr<renderengine::ExternalTexture>& buffer,
bool canCaptureBlackoutContent, bool regionSampling, bool grayscale,
ScreenCaptureResults& captureResults) {
ATRACE_CALL();
- size_t layerCount = 0;
- traverseLayers([&](Layer* layer) {
- layerCount++;
- captureResults.capturedSecureLayers =
- captureResults.capturedSecureLayers || (layer->isVisible() && layer->isSecure());
- });
+ const auto& display = renderArea->getDisplayDevice();
+ const auto& transform = renderArea->getTransform();
+ std::unordered_set<compositionengine::LayerFE*> filterForScreenshot;
+ auto layers = getLayerSnapshots();
+ for (auto& [layer, layerFE] : layers) {
+ frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get();
+ captureResults.capturedSecureLayers |= (snapshot->isVisible && snapshot->isSecure);
+ captureResults.capturedHdrLayers |= isHdrLayer(*snapshot);
+ layerFE->mSnapshot->geomLayerTransform =
+ renderArea->getTransform() * layerFE->mSnapshot->geomLayerTransform;
+ if (layer->needsFilteringForScreenshots(display.get(), transform)) {
+ filterForScreenshot.insert(layerFE.get());
+ }
+ }
// We allow the system server to take screenshots of secure layers for
// use in situations like the Screen-rotation animation and place
@@ -6671,31 +6755,6 @@
}
captureResults.capturedDataspace = dataspace;
- const auto transform = renderArea->getTransform();
- const auto display = renderArea->getDisplayDevice();
-
- std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
- layers.reserve(layerCount);
- std::unordered_set<compositionengine::LayerFE*> filterForScreenshot;
- traverseLayers([&](Layer* layer) {
- captureResults.capturedHdrLayers |= isHdrLayer(layer);
- // Layer::prepareClientComposition uses the layer's snapshot to populate the resulting
- // LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings are
- // generated with the layer's current buffer and geometry.
- layer->updateSnapshot(true /* updateGeometry */);
-
- layers.emplace_back(layer, layer->copyCompositionEngineLayerFE());
-
- sp<LayerFE>& layerFE = layers.back().second;
-
- layerFE->mSnapshot->geomLayerTransform =
- renderArea->getTransform() * layerFE->mSnapshot->geomLayerTransform;
-
- if (layer->needsFilteringForScreenshots(display.get(), transform)) {
- filterForScreenshot.insert(layerFE.get());
- }
- });
-
ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK};
if (!layers.empty()) {
const sp<LayerFE>& layerFE = layers.back().second;
@@ -8112,6 +8171,10 @@
return OK;
}
+void SurfaceFlinger::forceFutureUpdate(int delayInMs) {
+ static_cast<void>(mScheduler->scheduleDelayed([&]() { scheduleRepaint(); }, ms2ns(delayInMs)));
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 5457be8..207dfe2 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -56,6 +56,7 @@
#include <scheduler/Fps.h>
#include <scheduler/PresentLatencyTracker.h>
#include <scheduler/Time.h>
+#include <scheduler/TransactionSchedule.h>
#include <ui/FenceResult.h>
#include "Display/DisplayMap.h"
@@ -68,12 +69,12 @@
#include "FlagManager.h"
#include "FrontEnd/DisplayInfo.h"
#include "FrontEnd/LayerCreationArgs.h"
+#include "FrontEnd/LayerSnapshot.h"
#include "FrontEnd/TransactionHandler.h"
#include "LayerVector.h"
#include "Scheduler/RefreshRateSelector.h"
#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/Scheduler.h"
-#include "Scheduler/VsyncModulator.h"
#include "SurfaceFlingerFactory.h"
#include "ThreadContext.h"
#include "Tracing/LayerTracing.h"
@@ -313,6 +314,8 @@
// TODO(b/246793311): Clean up a temporary property
bool mIgnoreHwcPhysicalDisplayOrientation = false;
+ void forceFutureUpdate(int delayInMs);
+
protected:
// We're reference counted, never destroy SurfaceFlinger directly
virtual ~SurfaceFlinger();
@@ -344,15 +347,15 @@
friend class LayerRenderArea;
friend class LayerTracing;
friend class SurfaceComposerAIDL;
+ friend class DisplayRenderArea;
// For unit tests
friend class TestableSurfaceFlinger;
friend class TransactionApplicationTest;
friend class TunnelModeEnabledReporterTest;
- using VsyncModulator = scheduler::VsyncModulator;
using TransactionSchedule = scheduler::TransactionSchedule;
- using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
+ using GetLayerSnapshotsFunction = std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>;
using RenderAreaFuture = ftl::Future<std::unique_ptr<RenderArea>>;
using DumpArgs = Vector<String16>;
using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
@@ -468,14 +471,6 @@
return std::bind(dump, this, _1, _2, _3);
}
- template <typename... Args,
- typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)>
- void modulateVsync(Handler handler, Args... args) {
- if (const auto config = (*mVsyncModulator.*handler)(args...)) {
- setVsyncConfig(*config, mScheduler->getLeaderVsyncPeriod());
- }
- }
-
// Maximum allowed number of display frames that can be set through backdoor
static const int MAX_ALLOWED_DISPLAY_FRAMES = 2048;
@@ -501,7 +496,8 @@
uint32_t flags, const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands,
int64_t desiredPresentTime, bool isAutoTimestamp,
- const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ const std::vector<client_cache_t>& uncacheBuffers,
+ bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
uint64_t transactionId) override;
void bootFinished();
@@ -707,25 +703,25 @@
void updateCursorAsync();
void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock);
- void updatePhaseConfiguration(const Fps&) REQUIRES(mStateLock);
- void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod);
-
+ void updatePhaseConfiguration(Fps) REQUIRES(mStateLock);
/*
* Transactions
*/
- bool applyTransactionState(const FrameTimelineInfo& info,
- std::vector<ResolvedComposerState>& state,
- Vector<DisplayState>& displays, uint32_t flags,
- const InputWindowCommands& inputWindowCommands,
- const int64_t desiredPresentTime, bool isAutoTimestamp,
- const client_cache_t& uncacheBuffer, const int64_t postTime,
- uint32_t permissions, bool hasListenerCallbacks,
- const std::vector<ListenerCallbacks>& listenerCallbacks,
- int originPid, int originUid, uint64_t transactionId)
- REQUIRES(mStateLock);
+ bool applyTransactionState(
+ const FrameTimelineInfo& info, std::vector<ResolvedComposerState>& state,
+ Vector<DisplayState>& displays, uint32_t flags,
+ const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
+ bool isAutoTimestamp, const std::vector<uint64_t>& uncacheBufferIds,
+ const int64_t postTime, uint32_t permissions, bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks, int originPid, int originUid,
+ uint64_t transactionId) REQUIRES(mStateLock);
// Flush pending transactions that were presented after desiredPresentTime.
+ // For test only
bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext);
+
+ bool applyTransactions(std::vector<TransactionState>&, VsyncId) REQUIRES(kMainThreadContext);
+
// Returns true if there is at least one transaction that needs to be flushed
bool transactionFlushNeeded();
void addTransactionReadyFilters();
@@ -756,7 +752,7 @@
static LatchUnsignaledConfig getLatchUnsignaledConfig();
bool shouldLatchUnsignaled(const sp<Layer>& layer, const layer_state_t&, size_t numStates,
bool firstTransaction) const;
- bool applyTransactions(std::vector<TransactionState>& transactions, VsyncId)
+ bool applyTransactionsLocked(std::vector<TransactionState>& transactions, VsyncId)
REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
@@ -793,16 +789,16 @@
// Boot animation, on/off animations and screen capture
void startBootAnim();
- ftl::SharedFuture<FenceResult> captureScreenCommon(RenderAreaFuture, TraverseLayersFunction,
+ ftl::SharedFuture<FenceResult> captureScreenCommon(RenderAreaFuture, GetLayerSnapshotsFunction,
ui::Size bufferSize, ui::PixelFormat,
bool allowProtected, bool grayscale,
const sp<IScreenCaptureListener>&);
ftl::SharedFuture<FenceResult> captureScreenCommon(
- RenderAreaFuture, TraverseLayersFunction,
+ RenderAreaFuture, GetLayerSnapshotsFunction,
const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
bool grayscale, const sp<IScreenCaptureListener>&);
ftl::SharedFuture<FenceResult> renderScreenImpl(
- std::shared_ptr<const RenderArea>, TraverseLayersFunction,
+ std::shared_ptr<const RenderArea>, GetLayerSnapshotsFunction,
const std::shared_ptr<renderengine::ExternalTexture>&, bool canCaptureBlackoutContent,
bool regionSampling, bool grayscale, ScreenCaptureResults&) EXCLUDES(mStateLock)
REQUIRES(kMainThreadContext);
@@ -923,7 +919,7 @@
/*
* Compositing
*/
- void postComposition() REQUIRES(kMainThreadContext);
+ void postComposition(nsecs_t callTime) REQUIRES(kMainThreadContext);
/*
* Display management
@@ -1091,7 +1087,7 @@
void updateInternalDisplayVsyncLocked(const sp<DisplayDevice>& activeDisplay)
REQUIRES(mStateLock, kMainThreadContext);
- bool isHdrLayer(Layer* layer) const;
+ bool isHdrLayer(const frontend::LayerSnapshot& snapshot) const;
ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const
REQUIRES(mStateLock);
@@ -1139,9 +1135,8 @@
State mDrawingState{LayerVector::StateSet::Drawing};
bool mVisibleRegionsDirty = false;
- // VisibleRegions dirty is already cleared by postComp, but we need to track it to prevent
- // extra work in the HDR layer info listener.
- bool mVisibleRegionsWereDirtyThisFrame = false;
+ bool mHdrLayerInfoChanged = false;
+
// Used to ensure we omit a callback when HDR layer info listener is newly added but the
// scene hasn't changed
bool mAddingHDRLayerInfoListener = false;
@@ -1152,7 +1147,6 @@
// TODO: Also move visibleRegions over to a boolean system.
bool mUpdateInputInfo = false;
bool mSomeChildrenChanged;
- bool mSomeDataspaceChanged = false;
bool mForceTransactionDisplayChange = false;
// Set if LayerMetadata has changed since the last LayerMetadata snapshot.
@@ -1271,6 +1265,8 @@
float mDimmingRatio = -1.f;
std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+ std::atomic<int> mNumTrustedPresentationListeners = 0;
+
std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine;
// mMaxRenderTargetSize is only set once in init() so it doesn't need to be protected by
// any mutex.
@@ -1288,9 +1284,6 @@
// Stores phase offsets configured per refresh rate.
std::unique_ptr<scheduler::VsyncConfiguration> mVsyncConfiguration;
- // Optional to defer construction until PhaseConfiguration is created.
- sp<VsyncModulator> mVsyncModulator;
-
std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
scheduler::PresentLatencyTracker mPresentLatencyTracker GUARDED_BY(kMainThreadContext);
@@ -1339,6 +1332,7 @@
std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
GUARDED_BY(mStateLock);
+
mutable std::mutex mCreatedLayersLock;
struct LayerCreatedState {
LayerCreatedState(const wp<Layer>& layer, const wp<Layer> parent, bool addToRoot)
@@ -1494,6 +1488,7 @@
binder::Status removeHdrLayerInfoListener(
const sp<IBinder>& displayToken,
const sp<gui::IHdrLayerInfoListener>& listener) override;
+
binder::Status notifyPowerBoost(int boostId) override;
binder::Status setGlobalShadowSettings(const gui::Color& ambientColor,
const gui::Color& spotColor, float lightPosY,
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 366b09d..5025c49 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -43,7 +43,7 @@
const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
int64_t desiredPresentTime, bool isAutoTimestamp,
- const client_cache_t& uncacheBuffer, int64_t postTime, uint32_t permissions,
+ std::vector<uint64_t> uncacheBufferIds, int64_t postTime, uint32_t permissions,
bool hasListenerCallbacks, std::vector<ListenerCallbacks> listenerCallbacks,
int originPid, int originUid, uint64_t transactionId)
: frameTimelineInfo(frameTimelineInfo),
@@ -54,7 +54,7 @@
inputWindowCommands(inputWindowCommands),
desiredPresentTime(desiredPresentTime),
isAutoTimestamp(isAutoTimestamp),
- buffer(uncacheBuffer),
+ uncacheBufferIds(std::move(uncacheBufferIds)),
postTime(postTime),
permissions(permissions),
hasListenerCallbacks(hasListenerCallbacks),
@@ -109,7 +109,7 @@
InputWindowCommands inputWindowCommands;
int64_t desiredPresentTime;
bool isAutoTimestamp;
- client_cache_t buffer;
+ std::vector<uint64_t> uncacheBufferIds;
int64_t postTime;
uint32_t permissions;
bool hasListenerCallbacks;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index 81ca659..cdffbb4 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -157,6 +157,10 @@
return TimePoint::fromNs(fdp.ConsumeIntegral<nsecs_t>());
}
+inline Duration getFuzzedDuration(FuzzedDataProvider& fdp) {
+ return Duration::fromNs(fdp.ConsumeIntegral<nsecs_t>());
+}
+
inline FloatRect getFuzzedFloatRect(FuzzedDataProvider* fdp) {
return FloatRect(fdp->ConsumeFloatingPoint<float>() /*left*/,
fdp->ConsumeFloatingPoint<float>() /*right*/,
@@ -196,9 +200,11 @@
static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
static constexpr auto FAKE_DURATION_OFFSET_NS = std::chrono::nanoseconds(0);
- VsyncConfigSet getConfigsForRefreshRate(Fps) const override { return getCurrentConfigs(); }
+ scheduler::VsyncConfigSet getConfigsForRefreshRate(Fps) const override {
+ return getCurrentConfigs();
+ }
- VsyncConfigSet getCurrentConfigs() const override {
+ scheduler::VsyncConfigSet getCurrentConfigs() const override {
return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
FAKE_DURATION_OFFSET_NS},
{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS,
@@ -218,16 +224,16 @@
class TestableScheduler : public Scheduler, private ICompositor {
public:
TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr,
- ISchedulerCallback& callback)
+ sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
: TestableScheduler(std::make_unique<android::mock::VsyncController>(),
std::make_unique<android::mock::VSyncTracker>(), selectorPtr,
- callback) {}
+ std::move(modulatorPtr), callback) {}
TestableScheduler(std::unique_ptr<VsyncController> controller,
std::unique_ptr<VSyncTracker> tracker,
std::shared_ptr<RefreshRateSelector> selectorPtr,
- ISchedulerCallback& callback)
- : Scheduler(*this, callback, Feature::kContentDetection) {
+ sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
+ : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) {
mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
@@ -275,6 +281,8 @@
return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
}
+ using Scheduler::setVsyncConfig;
+
private:
// ICompositor overrides:
void configure() override {}
@@ -510,11 +518,6 @@
mFlinger->getDesiredDisplayModeSpecs(display, &_);
}
- void setVsyncConfig(FuzzedDataProvider *fdp) {
- const scheduler::VsyncModulator::VsyncConfig vsyncConfig{};
- mFlinger->setVsyncConfig(vsyncConfig, fdp->ConsumeIntegral<nsecs_t>());
- }
-
// TODO(b/248317436): extend to cover all displays for multi-display devices
static std::optional<PhysicalDisplayId> getFirstDisplayId() {
std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
@@ -592,14 +595,18 @@
mFlinger->updateInputFlinger();
mFlinger->updateCursorAsync();
- setVsyncConfig(&mFdp);
+ mutableScheduler().setVsyncConfig({.sfOffset = mFdp.ConsumeIntegral<nsecs_t>(),
+ .appOffset = mFdp.ConsumeIntegral<nsecs_t>(),
+ .sfWorkDuration = getFuzzedDuration(mFdp),
+ .appWorkDuration = getFuzzedDuration(mFdp)},
+ getFuzzedDuration(mFdp));
{
ftl::FakeGuard guard(kMainThreadContext);
mFlinger->commitTransactions();
mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp));
- mFlinger->postComposition();
+ mFlinger->postComposition(systemTime());
}
mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>());
@@ -658,15 +665,17 @@
mRefreshRateSelector = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
const auto fps = mRefreshRateSelector->getActiveMode().modePtr->getFps();
mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
- mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
- mFlinger->mVsyncConfiguration->getCurrentConfigs());
+
mFlinger->mRefreshRateStats =
std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps,
hal::PowerMode::OFF);
+ auto modulatorPtr = sp<scheduler::VsyncModulator>::make(
+ mFlinger->mVsyncConfiguration->getCurrentConfigs());
+
mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
std::move(vsyncTracker), mRefreshRateSelector,
- *(callback ?: this));
+ std::move(modulatorPtr), *(callback ?: this));
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
@@ -730,17 +739,18 @@
return mFlinger->mTransactionHandler.mPendingTransactionQueues;
}
- auto setTransactionState(const FrameTimelineInfo &frameTimelineInfo,
- Vector<ComposerState> &states, const Vector<DisplayState> &displays,
- uint32_t flags, const sp<IBinder> &applyToken,
- const InputWindowCommands &inputWindowCommands,
+ auto setTransactionState(const FrameTimelineInfo& frameTimelineInfo,
+ Vector<ComposerState>& states, const Vector<DisplayState>& displays,
+ uint32_t flags, const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands,
int64_t desiredPresentTime, bool isAutoTimestamp,
- const client_cache_t &uncacheBuffer, bool hasListenerCallbacks,
- std::vector<ListenerCallbacks> &listenerCallbacks,
+ const std::vector<client_cache_t>& uncacheBuffers,
+ bool hasListenerCallbacks,
+ std::vector<ListenerCallbacks>& listenerCallbacks,
uint64_t transactionId) {
return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
inputWindowCommands, desiredPresentTime,
- isAutoTimestamp, uncacheBuffer, hasListenerCallbacks,
+ isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
listenerCallbacks, transactionId);
}
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
index 0af3efa..44805db 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -280,17 +280,14 @@
};
using Schedule = scheduler::TransactionSchedule;
using nanos = std::chrono::nanoseconds;
- using VsyncModulator = scheduler::VsyncModulator;
using FuzzImplVsyncModulator = scheduler::FuzzImplVsyncModulator;
- const VsyncModulator::VsyncConfig early{SF_OFFSET_EARLY, APP_OFFSET_EARLY,
- nanos(SF_DURATION_LATE), nanos(APP_DURATION_LATE)};
- const VsyncModulator::VsyncConfig earlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
- nanos(SF_DURATION_EARLY), nanos(APP_DURATION_EARLY)};
- const VsyncModulator::VsyncConfig late{SF_OFFSET_LATE, APP_OFFSET_LATE,
- nanos(SF_DURATION_EARLY_GPU),
- nanos(APP_DURATION_EARLY_GPU)};
- const VsyncModulator::VsyncConfigSet offsets = {early, earlyGpu, late,
- nanos(HWC_MIN_WORK_DURATION)};
+ const scheduler::VsyncConfig early{SF_OFFSET_EARLY, APP_OFFSET_EARLY, nanos(SF_DURATION_LATE),
+ nanos(APP_DURATION_LATE)};
+ const scheduler::VsyncConfig earlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
+ nanos(SF_DURATION_EARLY), nanos(APP_DURATION_EARLY)};
+ const scheduler::VsyncConfig late{SF_OFFSET_LATE, APP_OFFSET_LATE, nanos(SF_DURATION_EARLY_GPU),
+ nanos(APP_DURATION_EARLY_GPU)};
+ const scheduler::VsyncConfigSet offsets = {early, earlyGpu, late, nanos(HWC_MIN_WORK_DURATION)};
sp<FuzzImplVsyncModulator> vSyncModulator =
sp<FuzzImplVsyncModulator>::make(offsets, scheduler::Now);
(void)vSyncModulator->setVsyncConfigSet(offsets);
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index bd6367d..6d12aa7 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -43,6 +43,7 @@
"LayerRenderTypeTransaction_test.cpp",
"LayerState_test.cpp",
"LayerTransaction_test.cpp",
+ "LayerTrustedPresentationListener_test.cpp",
"LayerTypeAndRenderTypeTransaction_test.cpp",
"LayerTypeTransaction_test.cpp",
"LayerUpdate_test.cpp",
@@ -56,6 +57,7 @@
"SetFrameRateOverride_test.cpp",
"SetGeometry_test.cpp",
"Stress_test.cpp",
+ "TextureFiltering_test.cpp",
"VirtualDisplay_test.cpp",
"WindowInfosListener_test.cpp",
],
diff --git a/services/surfaceflinger/tests/LayerTrustedPresentationListener_test.cpp b/services/surfaceflinger/tests/LayerTrustedPresentationListener_test.cpp
new file mode 100644
index 0000000..a843fd1
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerTrustedPresentationListener_test.cpp
@@ -0,0 +1,241 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <gui/BufferItemConsumer.h>
+#include <ui/Transform.h>
+#include <thread>
+#include "TransactionTestHarnesses.h"
+
+namespace android {
+struct PresentationCallbackHelper {
+ void callbackArrived(bool state) {
+ std::unique_lock l(mMutex);
+ mGotCallback = true;
+ mState = state;
+ mCondition.notify_all();
+ }
+ bool awaitCallback() {
+ std::unique_lock l(mMutex);
+ mGotCallback = false;
+ mCondition.wait_for(l, 5000ms);
+ EXPECT_TRUE(mGotCallback);
+ return mState;
+ }
+
+ bool mState;
+ bool mGotCallback;
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+};
+
+TrustedPresentationThresholds thresh() {
+ TrustedPresentationThresholds thresholds;
+ thresholds.minAlpha = 1.0;
+ thresholds.minFractionRendered = 1.0;
+ thresholds.stabilityRequirementMs = 100;
+ return thresholds;
+}
+
+class LayerTrustedPresentationListenerTest : public LayerTransactionTest {
+public:
+ void SetUp() override {
+ LayerTransactionTest::SetUp();
+ mainLayer = makeLayer();
+ thresholds = thresh();
+ }
+
+ void TearDown() override {
+ LayerTransactionTest::TearDown();
+ mCallback = nullptr;
+ t.reparent(mainLayer, nullptr).apply();
+ mainLayer = nullptr;
+ }
+
+ void thresholdsPrepared() {
+ t.show(mainLayer)
+ .setLayer(mainLayer, INT32_MAX)
+ .setTrustedPresentationCallback(
+ mainLayer,
+ [&](void* context, bool state) {
+ PresentationCallbackHelper* helper =
+ (PresentationCallbackHelper*)context;
+ helper->callbackArrived(state);
+ },
+ thresholds, &pch, mCallback)
+ .setPosition(mainLayer, 100, 100)
+ .apply();
+ }
+
+ sp<SurfaceControl> makeLayer() {
+ sp<SurfaceControl> layer =
+ createLayer("test", 100, 100, ISurfaceComposerClient::eFXSurfaceBufferState,
+ mBlackBgSurface.get());
+ fillBufferLayerColor(layer, Color::RED, 100, 100);
+ return layer;
+ }
+ sp<SurfaceControl> mainLayer;
+ PresentationCallbackHelper pch;
+ SurfaceComposerClient::Transaction t;
+ TrustedPresentationThresholds thresholds;
+ sp<SurfaceComposerClient::PresentationCallbackRAII> mCallback;
+};
+
+// The layer is fully presented with the default test setup.
+TEST_F(LayerTrustedPresentationListenerTest, callback_arrives) {
+ thresholdsPrepared();
+ EXPECT_TRUE(pch.awaitCallback());
+}
+
+// A hidden layer can't be considered presented!
+TEST_F(LayerTrustedPresentationListenerTest, hiding_layer_clears_state) {
+ thresholdsPrepared();
+ EXPECT_TRUE(pch.awaitCallback());
+ t.hide(mainLayer).apply();
+ EXPECT_FALSE(pch.awaitCallback());
+}
+
+// A fully obscured layer can't be considered presented!
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_clears_state) {
+ thresholdsPrepared();
+ EXPECT_TRUE(pch.awaitCallback());
+
+ auto otherLayer = makeLayer();
+ t.show(otherLayer)
+ .setPosition(otherLayer, 100, 100)
+ .setLayer(otherLayer, INT32_MAX)
+ .setLayer(mainLayer, INT32_MAX - 1)
+ .apply();
+ EXPECT_FALSE(pch.awaitCallback());
+}
+
+// Even if the layer obscuring us has an Alpha channel, we are still considered
+// obscured.
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_transparency_clears_state) {
+ thresholdsPrepared();
+ EXPECT_TRUE(pch.awaitCallback());
+
+ auto otherLayer = makeLayer();
+ t.show(otherLayer)
+ .setPosition(otherLayer, 100, 100)
+ .setLayer(otherLayer, INT32_MAX)
+ .setFlags(otherLayer, 0, layer_state_t::eLayerOpaque)
+ .setLayer(mainLayer, INT32_MAX - 1)
+ .apply();
+ EXPECT_FALSE(pch.awaitCallback());
+}
+
+// We can't be presented if our alpha is below the threshold.
+TEST_F(LayerTrustedPresentationListenerTest, alpha_below_threshold) {
+ thresholdsPrepared();
+ EXPECT_TRUE(pch.awaitCallback());
+ t.setAlpha(mainLayer, 0.9).apply();
+ EXPECT_FALSE(pch.awaitCallback());
+ t.setAlpha(mainLayer, 1.0).apply();
+ EXPECT_TRUE(pch.awaitCallback());
+}
+
+// Verify that the passed in threshold is actually respected!
+TEST_F(LayerTrustedPresentationListenerTest, alpha_below_other_threshold) {
+ thresholds.minAlpha = 0.8;
+ thresholdsPrepared();
+ EXPECT_TRUE(pch.awaitCallback());
+ t.setAlpha(mainLayer, 0.8).apply();
+ EXPECT_FALSE(pch.awaitCallback());
+ t.setAlpha(mainLayer, 0.9).apply();
+ EXPECT_TRUE(pch.awaitCallback());
+}
+
+// (86*86)/(100*100) = 0.73...so a crop of 86x86 is below the threshold
+// (87*87)/(100*100) = 0.76...so a crop of 87x87 is above the threshold!
+TEST_F(LayerTrustedPresentationListenerTest, crop_below_threshold) {
+ thresholds.minFractionRendered = 0.75;
+ thresholdsPrepared();
+ EXPECT_TRUE(pch.awaitCallback());
+ t.setCrop(mainLayer, Rect(0, 0, 86, 86)).apply();
+ EXPECT_FALSE(pch.awaitCallback());
+ t.setCrop(mainLayer, Rect(0, 0, 87, 87)).apply();
+ EXPECT_TRUE(pch.awaitCallback());
+}
+
+TEST_F(LayerTrustedPresentationListenerTest, scale_below_threshold) {
+ thresholds.minFractionRendered = 0.64;
+ thresholdsPrepared();
+ EXPECT_TRUE(pch.awaitCallback());
+ // 0.8 = sqrt(0.64)
+ t.setMatrix(mainLayer, 0.79, 0, 0, 0.79).apply();
+ EXPECT_FALSE(pch.awaitCallback());
+ t.setMatrix(mainLayer, 0.81, 0, 0, 0.81).apply();
+ EXPECT_TRUE(pch.awaitCallback());
+}
+
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_threshold_1) {
+ thresholds.minFractionRendered = 0.75;
+ thresholdsPrepared();
+ EXPECT_TRUE(pch.awaitCallback());
+
+ auto otherLayer = makeLayer();
+ t.show(otherLayer)
+ .setPosition(otherLayer, 100, 100)
+ .setLayer(otherLayer, INT32_MAX)
+ .setLayer(mainLayer, INT32_MAX - 1)
+ .apply();
+ EXPECT_FALSE(pch.awaitCallback());
+ t.setMatrix(otherLayer, 0.49, 0, 0, 0.49).apply();
+ EXPECT_TRUE(pch.awaitCallback());
+ t.setMatrix(otherLayer, 0.51, 0, 0, 0.51).apply();
+ EXPECT_FALSE(pch.awaitCallback());
+}
+
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_threshold_2) {
+ thresholds.minFractionRendered = 0.9;
+ thresholdsPrepared();
+ EXPECT_TRUE(pch.awaitCallback());
+
+ auto otherLayer = makeLayer();
+ t.show(otherLayer)
+ .setPosition(otherLayer, 100, 100)
+ .setLayer(otherLayer, INT32_MAX)
+ .setLayer(mainLayer, INT32_MAX - 1)
+ .apply();
+ EXPECT_FALSE(pch.awaitCallback());
+ t.setMatrix(otherLayer, 0.3, 0, 0, 0.3).apply();
+ EXPECT_TRUE(pch.awaitCallback());
+ t.setMatrix(otherLayer, 0.33, 0, 0, 0.33).apply();
+ EXPECT_FALSE(pch.awaitCallback());
+}
+
+TEST_F(LayerTrustedPresentationListenerTest, obscuring_with_alpha) {
+ thresholds.minFractionRendered = 0.9;
+ thresholdsPrepared();
+ EXPECT_TRUE(pch.awaitCallback());
+
+ auto otherLayer = makeLayer();
+ t.show(otherLayer)
+ .setPosition(otherLayer, 100, 100)
+ .setLayer(otherLayer, INT32_MAX)
+ .setLayer(mainLayer, INT32_MAX - 1)
+ .setAlpha(otherLayer, 0.01)
+ .apply();
+ EXPECT_FALSE(pch.awaitCallback());
+ t.setAlpha(otherLayer, 0.0).apply();
+ EXPECT_TRUE(pch.awaitCallback());
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/TextureFiltering_test.cpp b/services/surfaceflinger/tests/TextureFiltering_test.cpp
new file mode 100644
index 0000000..e9b1fbb
--- /dev/null
+++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/gui/ISurfaceComposerClient.h>
+#include <gtest/gtest.h>
+#include <gui/DisplayCaptureArgs.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+bool operator==(const Color& left, const Color& right) {
+ return left.a == right.a && left.r == right.r && left.g == right.g && left.b == right.b;
+}
+
+class TextureFilteringTest : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+
+ mParent = createLayer("test-parent", 100, 100,
+ gui::ISurfaceComposerClient::eFXSurfaceContainer);
+ mLayer = createLayer("test-child", 100, 100,
+ gui::ISurfaceComposerClient::eFXSurfaceBufferState, mParent.get());
+ sp<GraphicBuffer> buffer =
+ sp<GraphicBuffer>::make(static_cast<uint32_t>(100), static_cast<uint32_t>(100),
+ PIXEL_FORMAT_RGBA_8888, 1u,
+ BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY |
+ BufferUsage::GPU_TEXTURE,
+ "test");
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect{0, 0, 50, 100}, Color::RED);
+ TransactionUtils::fillGraphicBufferColor(buffer, Rect{50, 0, 100, 100}, Color::BLUE);
+ Transaction()
+ .setBuffer(mLayer, buffer)
+ .setDataspace(mLayer, ui::Dataspace::V0_SRGB)
+ .setLayer(mLayer, INT32_MAX)
+ .apply();
+ }
+
+ virtual void TearDown() { LayerTransactionTest::TearDown(); }
+
+ void expectFiltered(Rect redRect, Rect blueRect) {
+ // Check that at least some of the pixels in the red rectangle aren't solid red
+ int redPixels = 0;
+ for (int x = redRect.left; x < redRect.right; x++) {
+ for (int y = redRect.top; y < redRect.bottom; y++) {
+ redPixels += mCapture->getPixelColor(static_cast<uint32_t>(x),
+ static_cast<uint32_t>(y)) == Color::RED;
+ }
+ }
+ ASSERT_LT(redPixels, redRect.getWidth() * redRect.getHeight());
+
+ // Check that at least some of the pixels in the blue rectangle aren't solid blue
+ int bluePixels = 0;
+ for (int x = blueRect.left; x < blueRect.right; x++) {
+ for (int y = blueRect.top; y < blueRect.bottom; y++) {
+ bluePixels += mCapture->getPixelColor(static_cast<uint32_t>(x),
+ static_cast<uint32_t>(y)) == Color::BLUE;
+ }
+ }
+ ASSERT_LT(bluePixels, blueRect.getWidth() * blueRect.getHeight());
+ }
+
+ sp<SurfaceControl> mParent;
+ sp<SurfaceControl> mLayer;
+ std::unique_ptr<ScreenCapture> mCapture;
+};
+
+TEST_F(TextureFilteringTest, NoFiltering) {
+ gui::DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = mDisplay;
+ captureArgs.width = 100;
+ captureArgs.height = 100;
+ captureArgs.sourceCrop = Rect{100, 100};
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+ mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
+ mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
+}
+
+TEST_F(TextureFilteringTest, BufferCropNoFiltering) {
+ Transaction().setBufferCrop(mLayer, Rect{0, 0, 100, 100}).apply();
+
+ gui::DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = mDisplay;
+ captureArgs.width = 100;
+ captureArgs.height = 100;
+ captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+ mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
+ mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
+}
+
+// Expect filtering because the buffer is stretched to the layer's bounds.
+TEST_F(TextureFilteringTest, BufferCropIsFiltered) {
+ Transaction().setBufferCrop(mLayer, Rect{25, 25, 75, 75}).apply();
+
+ gui::DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = mDisplay;
+ captureArgs.width = 100;
+ captureArgs.height = 100;
+ captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+ expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
+}
+
+// Expect filtering because the output source crop is stretched to the output buffer's size.
+TEST_F(TextureFilteringTest, OutputSourceCropIsFiltered) {
+ gui::DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = mDisplay;
+ captureArgs.width = 100;
+ captureArgs.height = 100;
+ captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+ expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
+}
+
+// Expect filtering because the layer crop and output source crop are stretched to the output
+// buffer's size.
+TEST_F(TextureFilteringTest, LayerCropOutputSourceCropIsFiltered) {
+ Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
+
+ gui::DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = mDisplay;
+ captureArgs.width = 100;
+ captureArgs.height = 100;
+ captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+ expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
+}
+
+// Expect filtering because the layer is scaled up.
+TEST_F(TextureFilteringTest, LayerCaptureWithScalingIsFiltered) {
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mLayer->getHandle();
+ captureArgs.frameScaleX = 2;
+ captureArgs.frameScaleY = 2;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+ expectFiltered({0, 0, 100, 200}, {100, 0, 200, 200});
+}
+
+// Expect no filtering because the output buffer's size matches the source crop.
+TEST_F(TextureFilteringTest, LayerCaptureOutputSourceCropNoFiltering) {
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mLayer->getHandle();
+ captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+ mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
+ mCapture->expectColor(Rect{25, 0, 50, 50}, Color::BLUE);
+}
+
+// Expect no filtering because the output buffer's size matches the source crop (with a cropped
+// layer).
+TEST_F(TextureFilteringTest, LayerCaptureWithCropNoFiltering) {
+ Transaction().setCrop(mLayer, Rect{10, 10, 90, 90}).apply();
+
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mLayer->getHandle();
+ captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+
+ mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
+ mCapture->expectColor(Rect{25, 0, 50, 50}, Color::BLUE);
+}
+
+// Expect no filtering because the output source crop and output buffer are the same size.
+TEST_F(TextureFilteringTest, OutputSourceCropDisplayFrameMatchNoFiltering) {
+ // Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
+
+ gui::DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = mDisplay;
+ captureArgs.width = 50;
+ captureArgs.height = 50;
+ captureArgs.sourceCrop = Rect{25, 25, 75, 75};
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+ mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
+ mCapture->expectColor(Rect{25, 0, 50, 50}, Color::BLUE);
+}
+
+// Expect no filtering because the layer crop shouldn't scale the layer.
+TEST_F(TextureFilteringTest, LayerCropDisplayFrameMatchNoFiltering) {
+ Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
+
+ gui::DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = mDisplay;
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+ mCapture->expectColor(Rect{25, 25, 50, 75}, Color::RED);
+ mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
+}
+
+// Expect no filtering because the parent layer crop shouldn't scale the layer.
+TEST_F(TextureFilteringTest, ParentCropNoFiltering) {
+ Transaction().setCrop(mParent, Rect{25, 25, 75, 75}).apply();
+
+ gui::DisplayCaptureArgs captureArgs;
+ captureArgs.displayToken = mDisplay;
+ ScreenCapture::captureDisplay(&mCapture, captureArgs);
+
+ mCapture->expectColor(Rect{25, 25, 50, 75}, Color::RED);
+ mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index ba77600..0416e93 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -238,6 +238,8 @@
CaptureArgs::UNSET_UID, visitor);
};
+ auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
mCaptureScreenBuffer =
@@ -246,7 +248,7 @@
HAL_PIXEL_FORMAT_RGBA_8888, 1,
usage);
- auto future = mFlinger.renderScreenImpl(std::move(renderArea), traverseLayers,
+ auto future = mFlinger.renderScreenImpl(std::move(renderArea), getLayerSnapshots,
mCaptureScreenBuffer, forSystem, regionSampling);
ASSERT_TRUE(future.valid());
const auto fenceResult = future.get();
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index f47ac6d..abd7789 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -44,6 +44,17 @@
namespace android::frametimeline {
+static const std::string sLayerNameOne = "layer1";
+static const std::string sLayerNameTwo = "layer2";
+
+constexpr const uid_t sUidOne = 0;
+constexpr pid_t sPidOne = 10;
+constexpr pid_t sPidTwo = 20;
+constexpr int32_t sInputEventId = 5;
+constexpr int32_t sLayerIdOne = 1;
+constexpr int32_t sLayerIdTwo = 2;
+constexpr GameMode sGameMode = GameMode::Unsupported;
+
class FrameTimelineTest : public testing::Test {
public:
FrameTimelineTest() {
@@ -106,6 +117,14 @@
return packets;
}
+ void addEmptySurfaceFrame() {
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ false, sGameMode);
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame));
+ }
+
void addEmptyDisplayFrame() {
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
// Trigger a flushPresentFence by calling setSfPresent for the next frame
@@ -168,17 +187,6 @@
kStartThreshold};
};
-static const std::string sLayerNameOne = "layer1";
-static const std::string sLayerNameTwo = "layer2";
-
-constexpr const uid_t sUidOne = 0;
-constexpr pid_t sPidOne = 10;
-constexpr pid_t sPidTwo = 20;
-constexpr int32_t sInputEventId = 5;
-constexpr int32_t sLayerIdOne = 1;
-constexpr int32_t sLayerIdTwo = 2;
-constexpr GameMode sGameMode = GameMode::Unsupported;
-
TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
EXPECT_EQ(getPredictions().size(), 1u);
@@ -1054,6 +1062,9 @@
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
tracingSession->StartBlocking();
+
+ // Add an empty surface frame so that display frame would get traced.
+ addEmptySurfaceFrame();
int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 30, 30});
// Set up the display frame
@@ -1135,6 +1146,9 @@
// Flush the token so that it would expire
flushTokens();
+ // Add an empty surface frame so that display frame would get traced.
+ addEmptySurfaceFrame();
+
// Set up the display frame
mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11));
mFrameTimeline->setSfPresent(26, presentFence1);
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
index 33c9440..763426a 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -562,4 +562,148 @@
hierarchyBuilder.getHierarchy().traverseInZOrder(checkTraversalPathIdVisitor);
}
+TEST_F(LayerHierarchyTest, zorderRespectsLayerSequenceId) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ createRootLayer(1);
+ createRootLayer(2);
+ createRootLayer(4);
+ createRootLayer(5);
+ createLayer(11, 1);
+ createLayer(51, 5);
+ createLayer(53, 5);
+
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 4, 5, 51, 53};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // A new layer is added with a smaller sequence id. Make sure its sorted correctly. While
+ // sequence ids are always incremented, this scenario can happen when a layer is reparented.
+ createRootLayer(3);
+ createLayer(52, 5);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ expectedTraversalPath = {1, 11, 2, 3, 4, 5, 51, 52, 53};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, zorderRespectsLayerZ) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ createRootLayer(1);
+ createLayer(11, 1);
+ createLayer(12, 1);
+ createLayer(13, 1);
+ setZ(11, -1);
+ setZ(12, 2);
+ setZ(13, 1);
+
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 13, 12};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+
+ expectedTraversalPath = {11, 1, 13, 12};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, zorderRespectsLayerStack) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ createRootLayer(1);
+ createRootLayer(2);
+ createLayer(11, 1);
+ createLayer(21, 2);
+ setLayerStack(1, 20);
+ setLayerStack(2, 10);
+
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 21};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+
+ expectedTraversalPath = {1, 11, 2, 21};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, canMirrorDisplay) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+ createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+ setLayerStack(3, 1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expected = {3, 1, 11, 111, 12, 121, 122, 1221, 13, 2,
+ 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
+ expected = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expected);
+}
+
+TEST_F(LayerHierarchyTest, mirrorNonExistingDisplay) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+ createDisplayMirrorLayer(3, ui::LayerStack::fromValue(5));
+ setLayerStack(3, 1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expected = {3, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
+ expected = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expected);
+}
+
+TEST_F(LayerHierarchyTest, newRootLayerIsMirrored) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+ createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+ setLayerStack(3, 1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ createRootLayer(4);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expected = {3, 1, 11, 111, 12, 121, 122, 1221, 13, 2, 4,
+ 1, 11, 111, 12, 121, 122, 1221, 13, 2, 4};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
+ expected = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expected);
+}
+
+TEST_F(LayerHierarchyTest, removedRootLayerIsNoLongerMirrored) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+ createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+ setLayerStack(3, 1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentLayer(1, UNASSIGNED_LAYER_ID);
+ destroyLayerHandle(1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expected = {3, 2, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expected);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expected);
+ expected = {11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expected);
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 08727f2..852cb91 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -60,6 +60,14 @@
return args;
}
+ LayerCreationArgs createDisplayMirrorArgs(uint32_t id, ui::LayerStack layerStack) {
+ LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id));
+ args.addToRoot = true;
+ args.parentHandle.clear();
+ args.layerStackToMirror = layerStack;
+ return args;
+ }
+
std::vector<uint32_t> getTraversalPath(const LayerHierarchy& hierarchy) const {
std::vector<uint32_t> layerIds;
hierarchy.traverse([&layerIds = layerIds](const LayerHierarchy& hierarchy,
@@ -90,6 +98,15 @@
mLifecycleManager.addLayers(std::move(layers));
}
+ void createDisplayMirrorLayer(uint32_t id, ui::LayerStack layerStack) {
+ sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+ mHandles[id] = handle;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createDisplayMirrorArgs(/*id=*/id, layerStack)));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
virtual void createLayer(uint32_t id, uint32_t parentId) {
sp<LayerHandle> handle = sp<LayerHandle>::make(id);
mHandles[id] = handle;
@@ -245,6 +262,18 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setLayerStack(uint32_t id, int32_t layerStack) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eLayerStackChanged;
+ transactions.back().states.front().state.surface = mHandles[id];
+ transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+ transactions.back().states.front().state.layerStack = ui::LayerStack::fromValue(layerStack);
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
LayerLifecycleManager mLifecycleManager;
std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
};
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 2441c06..aa6a14e 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -38,6 +38,9 @@
namespace android::surfaceflinger::frontend {
+using ftl::Flags;
+using namespace ftl::flag_operators;
+
// To run test:
/**
mp :libsurfaceflinger_unittest && adb sync; adb shell \
@@ -88,14 +91,11 @@
ASSERT_TRUE(expectedBuilder.getSnapshots().size() > 0);
ASSERT_TRUE(actualBuilder.getSnapshots().size() > 0);
- std::vector<std::unique_ptr<LayerSnapshot>>& snapshots = actualBuilder.getSnapshots();
std::vector<uint32_t> actualVisibleLayerIdsInZOrder;
- for (auto& snapshot : snapshots) {
- if (!snapshot->isVisible) {
- break;
- }
- actualVisibleLayerIdsInZOrder.push_back(snapshot->path.id);
- }
+ actualBuilder.forEachVisibleSnapshot(
+ [&actualVisibleLayerIdsInZOrder](const LayerSnapshot& snapshot) {
+ actualVisibleLayerIdsInZOrder.push_back(snapshot.path.id);
+ });
EXPECT_EQ(expectedVisibleLayerIdsInZOrder, actualVisibleLayerIdsInZOrder);
}
@@ -103,7 +103,6 @@
LayerHierarchyBuilder mHierarchyBuilder{{}};
LayerSnapshotBuilder mSnapshotBuilder;
- std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
display::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
renderengine::ShadowSettings globalShadowSettings;
static const std::vector<uint32_t> STARTING_ZORDER;
@@ -257,4 +256,77 @@
EXPECT_EQ(getSnapshot(1)->changes, RequestedLayerState::Changes::Content);
}
+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().state.surface = mHandles[1];
+ transactions.back().states.front().state.layerId = static_cast<int32_t>(1);
+ mLifecycleManager.applyTransactions(transactions);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(static_cast<int32_t>(getSnapshot(1)->gameMode), 42);
+ EXPECT_EQ(static_cast<int32_t>(getSnapshot(11)->gameMode), 42);
+}
+
+TEST_F(LayerSnapshotTest, NoLayerVoteForParentWithChildVotes) {
+ // ROOT
+ // ├── 1
+ // │ ├── 11 (frame rate set)
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ ├── 121
+ // │ │ └── 122
+ // │ │ └── 1221
+ // │ └── 13
+ // └── 2
+
+ 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().state.frameRate = 90.0;
+ transactions.back().states.front().state.frameRateCompatibility =
+ ANATIVEWINDOW_FRAME_RATE_EXACT;
+ transactions.back().states.front().state.changeFrameRateStrategy =
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS;
+ transactions.back().states.front().state.surface = mHandles[11];
+ transactions.back().states.front().state.layerId = static_cast<int32_t>(11);
+ mLifecycleManager.applyTransactions(transactions);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_EQ(getSnapshot(11)->frameRate.rate.getIntValue(), 90);
+ EXPECT_EQ(getSnapshot(11)->frameRate.type, scheduler::LayerInfo::FrameRateCompatibility::Exact);
+ EXPECT_EQ(getSnapshot(111)->frameRate.rate.getIntValue(), 90);
+ EXPECT_EQ(getSnapshot(111)->frameRate.type,
+ scheduler::LayerInfo::FrameRateCompatibility::Exact);
+ EXPECT_EQ(getSnapshot(1)->frameRate.rate.getIntValue(), 0);
+ EXPECT_EQ(getSnapshot(1)->frameRate.type, scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+}
+
+// Display Mirroring Tests
+// tree with 3 levels of children
+// ROOT (DISPLAY 0)
+// ├── 1
+// │ ├── 11
+// │ │ └── 111
+// │ ├── 12 (has skip screenshot flag)
+// │ │ ├── 121
+// │ │ └── 122
+// │ │ └── 1221
+// │ └── 13
+// └── 2
+// ROOT (DISPLAY 1)
+// └── 3 (mirrors display 0)
+TEST_F(LayerSnapshotTest, displayMirrorRespects) {
+ setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot);
+ createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0));
+ setLayerStack(3, 1);
+
+ std::vector<uint32_t> expected = {3, 1, 11, 111, 13, 2, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 06f45f9..5fddda5 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -2973,5 +2973,15 @@
EXPECT_EQ(kMode1, selector.getMinRefreshRateByPolicy());
}
+// TODO(b/266481656): Once this bug is fixed, we can remove this test
+TEST_P(RefreshRateSelectorTest, noLowerFrameRateOnMinVote) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].name = "Test layer";
+ layers[0].vote = LayerVoteType::Min;
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode);
+}
+
} // namespace
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 0f53eb6..0cbfa63 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -37,12 +37,12 @@
TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback)
: TestableScheduler(std::make_unique<mock::VsyncController>(),
std::make_unique<mock::VSyncTracker>(), std::move(selectorPtr),
- callback) {}
+ /* modulatorPtr */ nullptr, callback) {}
TestableScheduler(std::unique_ptr<VsyncController> controller,
std::unique_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr,
- ISchedulerCallback& callback)
- : Scheduler(*this, callback, Feature::kContentDetection) {
+ sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback)
+ : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) {
mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker),
std::make_unique<mock::VSyncDispatch>(),
std::move(controller)));
@@ -99,6 +99,7 @@
Scheduler::setLeaderDisplay(displayId);
}
+ auto& mutableVsyncModulator() { return *mVsyncModulator; }
auto& mutableLayerHistory() { return mLayerHistory; }
size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 584d52c..68c738f 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -224,8 +224,6 @@
const auto fps = selectorPtr->getActiveMode().fps;
mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps);
- mFlinger->mVsyncModulator = sp<scheduler::VsyncModulator>::make(
- mFlinger->mVsyncConfiguration->getCurrentConfigs());
mFlinger->mRefreshRateStats =
std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, fps,
@@ -238,16 +236,21 @@
? static_cast<Callback&>(mNoOpSchedulerCallback)
: static_cast<Callback&>(mSchedulerCallback);
+ auto modulatorPtr = sp<scheduler::VsyncModulator>::make(
+ mFlinger->mVsyncConfiguration->getCurrentConfigs());
+
if (useNiceMock) {
mScheduler =
new testing::NiceMock<scheduler::TestableScheduler>(std::move(vsyncController),
std::move(vsyncTracker),
std::move(selectorPtr),
+ std::move(modulatorPtr),
callback);
} else {
mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
std::move(vsyncTracker),
- std::move(selectorPtr), callback);
+ std::move(selectorPtr),
+ std::move(modulatorPtr), callback);
}
mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(), *mTokenManager, 0ms);
@@ -262,8 +265,6 @@
scheduler::TestableScheduler& mutableScheduler() { return *mScheduler; }
scheduler::mock::SchedulerCallback& mockSchedulerCallback() { return mSchedulerCallback; }
- auto& mutableVsyncModulator() { return mFlinger->mVsyncModulator; }
-
using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
mFactory.mCreateBufferQueue = f;
@@ -406,7 +407,7 @@
}
auto renderScreenImpl(std::shared_ptr<const RenderArea> renderArea,
- SurfaceFlinger::TraverseLayersFunction traverseLayers,
+ SurfaceFlinger::GetLayerSnapshotsFunction traverseLayers,
const std::shared_ptr<renderengine::ExternalTexture>& buffer,
bool forSystem, bool regionSampling) {
ScreenCaptureResults captureResults;
@@ -439,12 +440,13 @@
uint32_t flags, const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands,
int64_t desiredPresentTime, bool isAutoTimestamp,
- const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+ const std::vector<client_cache_t>& uncacheBuffers,
+ bool hasListenerCallbacks,
std::vector<ListenerCallbacks>& listenerCallbacks,
uint64_t transactionId) {
return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
inputWindowCommands, desiredPresentTime,
- isAutoTimestamp, uncacheBuffer, hasListenerCallbacks,
+ isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
listenerCallbacks, transactionId);
}
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index d84698f..859f702 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -102,7 +102,7 @@
int64_t desiredPresentTime = 0;
bool isAutoTimestamp = true;
FrameTimelineInfo frameTimelineInfo;
- client_cache_t uncacheBuffer;
+ std::vector<client_cache_t> uncacheBuffers;
uint64_t id = static_cast<uint64_t>(-1);
static_assert(0xffffffffffffffff == static_cast<uint64_t>(-1));
};
@@ -138,7 +138,7 @@
transaction.displays, transaction.flags,
transaction.applyToken, transaction.inputWindowCommands,
transaction.desiredPresentTime, transaction.isAutoTimestamp,
- transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
transaction.id);
// If transaction is synchronous, SF applyTransactionState should time out (5s) wating for
@@ -165,7 +165,7 @@
transaction.displays, transaction.flags,
transaction.applyToken, transaction.inputWindowCommands,
transaction.desiredPresentTime, transaction.isAutoTimestamp,
- transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
transaction.id);
nsecs_t returnedTime = systemTime();
@@ -196,7 +196,7 @@
transactionA.displays, transactionA.flags,
transactionA.applyToken, transactionA.inputWindowCommands,
transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
- transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionA.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
transactionA.id);
// This thread should not have been blocked by the above transaction
@@ -211,7 +211,7 @@
transactionB.displays, transactionB.flags,
transactionB.applyToken, transactionB.inputWindowCommands,
transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
- transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionB.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
transactionB.id);
// this thread should have been blocked by the above transaction
@@ -228,6 +228,11 @@
EXPECT_EQ(0u, transactionQueue.size());
}
+ void modulateVsync() {
+ static_cast<void>(
+ mFlinger.mutableScheduler().mutableVsyncModulator().onRefreshRateChangeInitiated());
+ }
+
bool mHasListenerCallbacks = false;
std::vector<ListenerCallbacks> mCallbacks;
int mTransactionNumber = 0;
@@ -243,7 +248,7 @@
mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
transactionA.displays, transactionA.flags, transactionA.applyToken,
transactionA.inputWindowCommands, transactionA.desiredPresentTime,
- transactionA.isAutoTimestamp, transactionA.uncacheBuffer,
+ transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
mHasListenerCallbacks, mCallbacks, transactionA.id);
auto& transactionQueue = mFlinger.getTransactionQueue();
@@ -263,7 +268,7 @@
mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
transactionA.displays, transactionA.flags, transactionA.applyToken,
transactionA.inputWindowCommands, transactionA.desiredPresentTime,
- transactionA.isAutoTimestamp, transactionA.uncacheBuffer,
+ transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
mHasListenerCallbacks, mCallbacks, transactionA.id);
auto& transactionQueue = mFlinger.getTransactionQueue();
@@ -277,7 +282,7 @@
mFlinger.setTransactionState(empty.frameTimelineInfo, empty.states, empty.displays, empty.flags,
empty.applyToken, empty.inputWindowCommands,
empty.desiredPresentTime, empty.isAutoTimestamp,
- empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks, empty.id);
+ empty.uncacheBuffers, mHasListenerCallbacks, mCallbacks, empty.id);
// flush transaction queue should flush as desiredPresentTime has
// passed
@@ -374,8 +379,7 @@
transaction.applyToken,
transaction.inputWindowCommands,
transaction.desiredPresentTime,
- transaction.isAutoTimestamp,
- transaction.uncacheBuffer, systemTime(), 0,
+ transaction.isAutoTimestamp, {}, systemTime(), 0,
mHasListenerCallbacks, mCallbacks, getpid(),
static_cast<int>(getuid()), transaction.id);
mFlinger.setTransactionStateInternal(transactionState);
@@ -624,9 +628,7 @@
layer_state_t::eBufferChanged),
});
- // Get VsyncModulator out of the default config
- static_cast<void>(mFlinger.mutableVsyncModulator()->onRefreshRateChangeInitiated());
-
+ modulateVsync();
setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
}
@@ -986,9 +988,7 @@
layer_state_t::eBufferChanged),
});
- // Get VsyncModulator out of the default config
- static_cast<void>(mFlinger.mutableVsyncModulator()->onRefreshRateChangeInitiated());
-
+ modulateVsync();
setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
}
diff --git a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
index b519582..8acbd6f 100644
--- a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp
@@ -57,17 +57,14 @@
using Schedule = scheduler::TransactionSchedule;
using nanos = std::chrono::nanoseconds;
- const VsyncModulator::VsyncConfig kEarly{SF_OFFSET_EARLY, APP_OFFSET_EARLY,
- nanos(SF_DURATION_LATE), nanos(APP_DURATION_LATE)};
- const VsyncModulator::VsyncConfig kEarlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU,
- nanos(SF_DURATION_EARLY),
- nanos(APP_DURATION_EARLY)};
- const VsyncModulator::VsyncConfig kLate{SF_OFFSET_LATE, APP_OFFSET_LATE,
- nanos(SF_DURATION_EARLY_GPU),
- nanos(APP_DURATION_EARLY_GPU)};
+ const VsyncConfig kEarly{SF_OFFSET_EARLY, APP_OFFSET_EARLY, nanos(SF_DURATION_LATE),
+ nanos(APP_DURATION_LATE)};
+ const VsyncConfig kEarlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU, nanos(SF_DURATION_EARLY),
+ nanos(APP_DURATION_EARLY)};
+ const VsyncConfig kLate{SF_OFFSET_LATE, APP_OFFSET_LATE, nanos(SF_DURATION_EARLY_GPU),
+ nanos(APP_DURATION_EARLY_GPU)};
- const VsyncModulator::VsyncConfigSet mOffsets = {kEarly, kEarlyGpu, kLate,
- nanos(HWC_MIN_WORK_DURATION)};
+ const VsyncConfigSet mOffsets = {kEarly, kEarlyGpu, kLate, nanos(HWC_MIN_WORK_DURATION)};
sp<TestableVsyncModulator> mVsyncModulator = sp<TestableVsyncModulator>::make(mOffsets, Now);
void SetUp() override { EXPECT_EQ(kLate, mVsyncModulator->setVsyncConfigSet(mOffsets)); }