Merge changes I6902e052,I39a7f08a,I26771887
* changes:
Extract Gesture handling code and test it
Make assertPosition a method of FakePointerController
Extract HardwareState conversion into a class and test it
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index c0f3e30..6d64e1e 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -132,12 +132,21 @@
} else {
out << "\ttarget.ptr=" << btd->target.ptr;
}
- out << "\t (cookie " << btd->cookie << ")"
- << "\n"
+ out << "\t (cookie " << btd->cookie << ")\n"
<< "\tcode=" << TypeCode(btd->code) << ", flags=" << (void*)(uint64_t)btd->flags << "\n"
- << "\tdata=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size << " bytes)"
- << "\n"
- << "\toffsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size << " bytes)";
+ << "\tdata=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size << " bytes)\n"
+ << "\toffsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size << " bytes)\n";
+ return btd + 1;
+}
+
+static const void* printBinderTransactionDataSecCtx(std::ostream& out, const void* data) {
+ const binder_transaction_data_secctx* btd = (const binder_transaction_data_secctx*)data;
+
+ printBinderTransactionData(out, &btd->transaction_data);
+
+ char* secctx = (char*)btd->secctx;
+ out << "\tsecctx=" << secctx << "\n";
+
return btd+1;
}
@@ -156,6 +165,11 @@
out << "\t" << kReturnStrings[cmdIndex];
switch (code) {
+ case BR_TRANSACTION_SEC_CTX: {
+ out << ": ";
+ cmd = (const int32_t*)printBinderTransactionDataSecCtx(out, cmd);
+ } break;
+
case BR_TRANSACTION:
case BR_REPLY: {
out << ": ";
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 0820cd1..fedc1d9 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -70,11 +70,8 @@
return setupSocketServer(UnixSocketAddress(path));
}
-status_t RpcServer::setupVsockServer(unsigned int port) {
- // realizing value w/ this type at compile time to avoid ubsan abort
- constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
-
- return setupSocketServer(VsockSocketAddress(kAnyCid, port));
+status_t RpcServer::setupVsockServer(unsigned int bindCid, unsigned int port) {
+ return setupSocketServer(VsockSocketAddress(bindCid, port));
}
status_t RpcServer::setupInetServer(const char* address, unsigned int port,
@@ -157,6 +154,12 @@
mRootObjectFactory = std::move(makeObject);
}
+void RpcServer::setConnectionFilter(std::function<bool(const void*, size_t)>&& filter) {
+ RpcMutexLockGuard _l(mLock);
+ LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined");
+ mConnectionFilter = std::move(filter);
+}
+
sp<IBinder> RpcServer::getRootObject() {
RpcMutexLockGuard _l(mLock);
bool hasWeak = mRootObjectWeak.unsafe_get();
@@ -242,13 +245,19 @@
if (mAcceptFn(*this, &clientSocket) != OK) {
continue;
}
+
+ LOG_RPC_DETAIL("accept on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());
+
if (getpeername(clientSocket.fd.get(), reinterpret_cast<sockaddr*>(addr.data()),
&addrLen)) {
ALOGE("Could not getpeername socket: %s", strerror(errno));
continue;
}
- LOG_RPC_DETAIL("accept on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());
+ if (mConnectionFilter != nullptr && !mConnectionFilter(addr.data(), addrLen)) {
+ ALOGE("Dropped client connection fd %d", clientSocket.fd.get());
+ continue;
+ }
{
RpcMutexLockGuard _l(mLock);
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 4ad0a47..25193a3 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -81,9 +81,9 @@
[[nodiscard]] status_t setupRawSocketServer(base::unique_fd socket_fd);
/**
- * Creates an RPC server at the current port.
+ * Creates an RPC server binding to the given CID at the given port.
*/
- [[nodiscard]] status_t setupVsockServer(unsigned int port);
+ [[nodiscard]] status_t setupVsockServer(unsigned int bindCid, unsigned int port);
/**
* Creates an RPC server at the current port using IPv4.
@@ -171,6 +171,16 @@
sp<IBinder> getRootObject();
/**
+ * Set optional filter of incoming connections based on the peer's address.
+ *
+ * Takes one argument: a callable that is invoked on each accept()-ed
+ * connection and returns false if the connection should be dropped.
+ * See the description of setPerSessionRootObject() for details about
+ * the callable's arguments.
+ */
+ void setConnectionFilter(std::function<bool(const void*, size_t)>&& filter);
+
+ /**
* See RpcTransportCtx::getCertificate
*/
std::vector<uint8_t> getCertificate(RpcCertificateFormat);
@@ -253,6 +263,7 @@
sp<IBinder> mRootObject;
wp<IBinder> mRootObjectWeak;
std::function<sp<IBinder>(const void*, size_t)> mRootObjectFactory;
+ std::function<bool(const void*, size_t)> mConnectionFilter;
std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions;
std::unique_ptr<FdTrigger> mShutdownTrigger;
RpcConditionVariable mShutdownCv;
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index f08bde8..3ec049e 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -25,9 +25,13 @@
struct ARpcServer;
// Starts an RPC server on a given port and a given root IBinder object.
+// The server will only accept connections from the given CID.
+// Set `cid` to VMADDR_CID_ANY to accept connections from any client.
+// Set `cid` to VMADDR_CID_LOCAL to only bind to the local vsock interface.
// Returns an opaque handle to the running server instance, or null if the server
// could not be started.
-[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port);
+[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid,
+ unsigned int port);
// Starts a Unix domain RPC server with a given init-managed Unix domain `name`
// and a given root IBinder object.
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index f55c779..88f8c94 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -51,21 +51,26 @@
ref->decStrong(ref);
}
+static unsigned int cidFromStructAddr(const void* addr, size_t addrlen) {
+ LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated");
+ const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
+ LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock");
+ return vaddr->svm_cid;
+}
+
extern "C" {
bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
void* factoryContext, unsigned int port) {
auto server = RpcServer::make();
- if (status_t status = server->setupVsockServer(port); status != OK) {
+ if (status_t status = server->setupVsockServer(VMADDR_CID_ANY, port); status != OK) {
LOG(ERROR) << "Failed to set up vsock server with port " << port
<< " error: " << statusToString(status).c_str();
return false;
}
server->setPerSessionRootObject([=](const void* addr, size_t addrlen) {
- LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated");
- const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
- LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock");
- return AIBinder_toPlatformBinder(factory(vaddr->svm_cid, factoryContext));
+ unsigned int cid = cidFromStructAddr(addr, addrlen);
+ return AIBinder_toPlatformBinder(factory(cid, factoryContext));
});
server->join();
@@ -75,13 +80,30 @@
return true;
}
-ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port) {
+ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) {
auto server = RpcServer::make();
- if (status_t status = server->setupVsockServer(port); status != OK) {
+
+ unsigned int bindCid = VMADDR_CID_ANY; // bind to the remote interface
+ if (cid == VMADDR_CID_LOCAL) {
+ bindCid = VMADDR_CID_LOCAL; // bind to the local interface
+ cid = VMADDR_CID_ANY; // no need for a connection filter
+ }
+
+ if (status_t status = server->setupVsockServer(bindCid, port); status != OK) {
LOG(ERROR) << "Failed to set up vsock server with port " << port
<< " error: " << statusToString(status).c_str();
return nullptr;
}
+ if (cid != VMADDR_CID_ANY) {
+ server->setConnectionFilter([=](const void* addr, size_t addrlen) {
+ unsigned int remoteCid = cidFromStructAddr(addr, addrlen);
+ if (cid != remoteCid) {
+ LOG(ERROR) << "Rejected vsock connection from CID " << remoteCid;
+ return false;
+ }
+ return true;
+ });
+ }
server->setRootObject(AIBinder_toPlatformBinder(service));
return createRpcServerHandle(server);
}
diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs
index 42f5567..d5f1219 100644
--- a/libs/binder/rust/rpcbinder/src/server.rs
+++ b/libs/binder/rust/rpcbinder/src/server.rs
@@ -41,14 +41,19 @@
impl RpcServer {
/// Creates a binder RPC server, serving the supplied binder service implementation on the given
- /// vsock port.
- pub fn new_vsock(mut service: SpIBinder, port: u32) -> Result<RpcServer, Error> {
+ /// vsock port. Only connections from the given CID are accepted.
+ ///
+ // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client.
+ // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface.
+ pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> {
let service = service.as_native_mut();
// SAFETY: Service ownership is transferring to the server and won't be valid afterward.
// Plus the binder objects are threadsafe.
unsafe {
- Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(service, port))
+ Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(
+ service, cid, port,
+ ))
}
}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 02aa45f..739c217 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1342,7 +1342,7 @@
} break;
case SocketType::VSOCK: {
auto port = allocateVsockPort();
- auto status = rpcServer->setupVsockServer(port);
+ auto status = rpcServer->setupVsockServer(VMADDR_CID_LOCAL, port);
if (status != OK) {
return AssertionFailure() << "setupVsockServer: " << statusToString(status);
}
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index 995e761..cc9726b 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -58,7 +58,7 @@
CHECK_EQ(OK, server->setupRawSocketServer(std::move(socketFd)));
break;
case SocketType::VSOCK:
- CHECK_EQ(OK, server->setupVsockServer(serverConfig.vsockPort));
+ CHECK_EQ(OK, server->setupVsockServer(VMADDR_CID_LOCAL, serverConfig.vsockPort));
break;
case SocketType::INET: {
CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort));
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index d70a7f0..06a246e 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -103,7 +103,7 @@
// (sf vsync offset - debug.sf.early_phase_offset_ns). SurfaceFlinger will continue to be
// in the early configuration until it receives eEarlyWakeupEnd. These flags are
// expected to be used by WindowManager only and are guarded by
- // android.permission.ACCESS_SURFACE_FLINGER
+ // android.permission.WAKEUP_SURFACE_FLINGER
eEarlyWakeupStart = 0x08,
eEarlyWakeupEnd = 0x10,
eOneWay = 0x20
diff --git a/libs/jpegrecoverymap/tests/data/raw_yuv420_image.yuv420 b/libs/jpegrecoverymap/tests/data/raw_yuv420_image.yuv420
new file mode 100644
index 0000000..c043da6
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/raw_yuv420_image.yuv420
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
index 6dea27f..c3c6fd4 100644
--- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
@@ -22,9 +22,11 @@
#include <utils/Log.h>
#define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010"
-#define RAW_P010_IMAGE_WIDTH 1280
-#define RAW_P010_IMAGE_HEIGHT 720
+#define RAW_YUV420_IMAGE "/sdcard/Documents/raw_yuv420_image.yuv420"
#define JPEG_IMAGE "/sdcard/Documents/jpeg_image.jpg"
+#define TEST_IMAGE_WIDTH 1280
+#define TEST_IMAGE_HEIGHT 720
+#define DEFAULT_JPEG_QUALITY 90
#define SAVE_ENCODING_RESULT true
#define SAVE_DECODING_RESULT true
@@ -40,6 +42,7 @@
virtual void TearDown();
struct jpegr_uncompressed_struct mRawP010Image;
+ struct jpegr_uncompressed_struct mRawYuv420Image;
struct jpegr_compressed_struct mJpegImage;
};
@@ -49,6 +52,7 @@
void RecoveryMapTest::SetUp() {}
void RecoveryMapTest::TearDown() {
free(mRawP010Image.data);
+ free(mRawYuv420Image.data);
free(mJpegImage.data);
}
@@ -108,24 +112,88 @@
ASSERT_EQ(metadata_expected.rangeScalingFactor, metadata_read.rangeScalingFactor);
}
-TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) {
+
+/* Test Encode API-0 and decode */
+// TODO: enable when tonemapper is ready.
+//TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) {
+// int ret;
+//
+// // Load input files.
+// if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+// FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+// }
+// mRawP010Image.width = TEST_IMAGE_WIDTH;
+// mRawP010Image.height = TEST_IMAGE_HEIGHT;
+// mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+//
+// RecoveryMap recoveryMap;
+//
+// jpegr_compressed_struct jpegR;
+// jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
+// jpegR.data = malloc(jpegR.maxLength);
+// ret = recoveryMap.encodeJPEGR(
+// &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, 90, nullptr);
+// if (ret != OK) {
+// FAIL() << "Error code is " << ret;
+// }
+// if (SAVE_ENCODING_RESULT) {
+// // Output image data to file
+// std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+// std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+// if (!imageFile.is_open()) {
+// ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+// }
+// imageFile.write((const char*)jpegR.data, jpegR.length);
+// }
+//
+// jpegr_uncompressed_struct decodedJpegR;
+// int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+// decodedJpegR.data = malloc(decodedJpegRSize);
+// ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+// if (ret != OK) {
+// FAIL() << "Error code is " << ret;
+// }
+// if (SAVE_DECODING_RESULT) {
+// // Output image data to file
+// std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+// std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+// if (!imageFile.is_open()) {
+// ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+// }
+// imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+// }
+//
+// free(jpegR.data);
+// free(decodedJpegR.data);
+//}
+
+/* Test Encode API-1 and decode */
+TEST_F(RecoveryMapTest, encodeFromRawHdrAndSdrThenDecode) {
int ret;
// Load input files.
if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
}
- mRawP010Image.width = RAW_P010_IMAGE_WIDTH;
- mRawP010Image.height = RAW_P010_IMAGE_HEIGHT;
+ mRawP010Image.width = TEST_IMAGE_WIDTH;
+ mRawP010Image.height = TEST_IMAGE_HEIGHT;
mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+ if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawYuv420Image.width = TEST_IMAGE_WIDTH;
+ mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
+ mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+
RecoveryMap recoveryMap;
jpegr_compressed_struct jpegR;
- jpegR.maxLength = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * sizeof(uint8_t);
+ jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
jpegR.data = malloc(jpegR.maxLength);
ret = recoveryMap.encodeJPEGR(
- &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, 90, nullptr);
+ &mRawP010Image, &mRawYuv420Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR,
+ DEFAULT_JPEG_QUALITY, nullptr);
if (ret != OK) {
FAIL() << "Error code is " << ret;
}
@@ -140,7 +208,7 @@
}
jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * 4;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
decodedJpegR.data = malloc(decodedJpegRSize);
ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
if (ret != OK) {
@@ -160,6 +228,72 @@
free(decodedJpegR.data);
}
+/* Test Encode API-2 and decode */
+TEST_F(RecoveryMapTest, encodeFromRawHdrAndSdrAndJpegThenDecode) {
+ int ret;
+
+ // Load input files.
+ if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawP010Image.width = TEST_IMAGE_WIDTH;
+ mRawP010Image.height = TEST_IMAGE_HEIGHT;
+ mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+
+ if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawYuv420Image.width = TEST_IMAGE_WIDTH;
+ mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
+ mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+
+ if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
+ FAIL() << "Load file " << JPEG_IMAGE << " failed";
+ }
+ mJpegImage.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+
+ RecoveryMap recoveryMap;
+
+ jpegr_compressed_struct jpegR;
+ jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
+ jpegR.data = malloc(jpegR.maxLength);
+ ret = recoveryMap.encodeJPEGR(
+ &mRawP010Image, &mRawYuv420Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_ENCODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)jpegR.data, jpegR.length);
+ }
+
+ jpegr_uncompressed_struct decodedJpegR;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ decodedJpegR.data = malloc(decodedJpegRSize);
+ ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_DECODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+ }
+
+ free(jpegR.data);
+ free(decodedJpegR.data);
+}
+
+/* Test Encode API-3 and decode */
TEST_F(RecoveryMapTest, encodeFromJpegThenDecode) {
int ret;
@@ -167,8 +301,8 @@
if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
}
- mRawP010Image.width = RAW_P010_IMAGE_WIDTH;
- mRawP010Image.height = RAW_P010_IMAGE_HEIGHT;
+ mRawP010Image.width = TEST_IMAGE_WIDTH;
+ mRawP010Image.height = TEST_IMAGE_HEIGHT;
mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
@@ -179,7 +313,7 @@
RecoveryMap recoveryMap;
jpegr_compressed_struct jpegR;
- jpegR.maxLength = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * sizeof(uint8_t);
+ jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
jpegR.data = malloc(jpegR.maxLength);
ret = recoveryMap.encodeJPEGR(
&mRawP010Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
@@ -197,7 +331,7 @@
}
jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * 4;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
decodedJpegR.data = malloc(decodedJpegRSize);
ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
if (ret != OK) {
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 04e24ed..8d19c45 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -111,9 +111,23 @@
],
}
+// Used to consolidate and simplify pulling Skia & Skia deps into targets that depend on
+// librenderengine. This allows shared deps to be deduplicated (e.g. Perfetto), which doesn't seem
+// possible if libskia_renderengine is just pulled into librenderengine via whole_static_libs.
+cc_defaults {
+ name: "librenderengine_deps",
+ defaults: ["skia_renderengine_deps"],
+ static_libs: ["libskia_renderengine"],
+}
+
+// Note: if compilation fails when adding librenderengine as a dependency, try adding
+// librenderengine_deps to the defaults field of your dependent target.
cc_library_static {
name: "librenderengine",
- defaults: ["librenderengine_defaults"],
+ defaults: [
+ "librenderengine_defaults",
+ "librenderengine_deps",
+ ],
double_loadable: true,
cflags: [
"-fvisibility=hidden",
@@ -132,7 +146,6 @@
include_dirs: [
"external/skia/src/gpu",
],
- whole_static_libs: ["libskia_renderengine"],
lto: {
thin: true,
},
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
index afbe6cf..55c34cd 100644
--- a/libs/renderengine/benchmark/Android.bp
+++ b/libs/renderengine/benchmark/Android.bp
@@ -25,7 +25,7 @@
name: "librenderengine_bench",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
- "skia_deps",
+ "librenderengine_deps",
"surfaceflinger_defaults",
],
srcs: [
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index 6f328d7..50e166d 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -25,7 +25,7 @@
name: "librenderengine_test",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
- "skia_deps",
+ "librenderengine_deps",
"surfaceflinger_defaults",
],
test_suites: ["device-tests"],
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index b8a6dad..3e4db43 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -191,6 +191,9 @@
// The set of disabled input devices (disabledDevices) has changed.
CHANGE_ENABLED_STATE = 1 << 9,
+ // The device type has been updated.
+ CHANGE_DEVICE_TYPE = 1 << 10,
+
// All devices must be reopened.
CHANGE_MUST_REOPEN = 1 << 31,
};
@@ -212,6 +215,10 @@
// Used to determine which DisplayViewport should be tied to which InputDevice.
std::unordered_map<std::string, std::string> uniqueIdAssociations;
+ // The associations between input device ports device types.
+ // This is used to determine which device type and source should be tied to which InputDevice.
+ std::unordered_map<std::string, std::string> deviceTypeAssociations;
+
// The suggested display ID to show the cursor.
int32_t defaultPointerDisplayId;
@@ -326,7 +333,6 @@
std::optional<DisplayViewport> getDisplayViewportById(int32_t displayId) const;
void setDisplayViewports(const std::vector<DisplayViewport>& viewports);
-
void dump(std::string& dump) const;
void dumpViewport(std::string& dump, const DisplayViewport& viewport) const;
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 11b5209..6dfe5f5 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -56,6 +56,16 @@
InputDevice::~InputDevice() {}
+template <typename K, typename V>
+std::optional<V> getValueByKey(const std::unordered_map<K, V>& map, K key) {
+ auto it = map.find(key);
+ std::optional<V> value = std::nullopt;
+ if (it != map.end()) {
+ value = it->second;
+ }
+ return value;
+}
+
bool InputDevice::isEnabled() {
if (!hasEventHubDevices()) {
return false;
@@ -291,6 +301,9 @@
context.getConfiguration(&configuration);
mConfiguration.addAll(&configuration);
});
+
+ mAssociatedDeviceType =
+ getValueByKey(config->deviceTypeAssociations, mIdentifier.location);
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 6fa21e5..af59fe2 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -65,6 +65,9 @@
inline std::optional<std::string> getAssociatedDisplayUniqueId() const {
return mAssociatedDisplayUniqueId;
}
+ inline std::optional<std::string> getDeviceTypeAssociation() const {
+ return mAssociatedDeviceType;
+ }
inline std::optional<DisplayViewport> getAssociatedViewport() const {
return mAssociatedViewport;
}
@@ -180,6 +183,7 @@
bool mIsExternal;
std::optional<uint8_t> mAssociatedDisplayPort;
std::optional<std::string> mAssociatedDisplayUniqueId;
+ std::optional<std::string> mAssociatedDeviceType;
std::optional<DisplayViewport> mAssociatedViewport;
bool mHasMic;
bool mDropUntilNextSync;
@@ -408,6 +412,9 @@
inline std::optional<std::string> getAssociatedDisplayUniqueId() const {
return mDevice.getAssociatedDisplayUniqueId();
}
+ inline std::optional<std::string> getDeviceTypeAssociation() const {
+ return mDevice.getDeviceTypeAssociation();
+ }
inline std::optional<DisplayViewport> getAssociatedViewport() const {
return mDevice.getAssociatedViewport();
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index cefc44e..160f9eb 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -392,33 +392,10 @@
}
}
- if (getDeviceContext().hasInputProperty(INPUT_PROP_DIRECT)) {
- // The device is a touch screen.
- mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
- } else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) {
- // The device is a pointing device like a track pad.
- mParameters.deviceType = Parameters::DeviceType::POINTER;
- } else {
- // The device is a touch pad of unknown purpose.
- mParameters.deviceType = Parameters::DeviceType::POINTER;
- }
+ configureDeviceType();
mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD);
- std::string deviceTypeString;
- if (getDeviceContext().getConfiguration().tryGetProperty("touch.deviceType",
- deviceTypeString)) {
- if (deviceTypeString == "touchScreen") {
- mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
- } else if (deviceTypeString == "touchNavigation") {
- mParameters.deviceType = Parameters::DeviceType::TOUCH_NAVIGATION;
- } else if (deviceTypeString == "pointer") {
- mParameters.deviceType = Parameters::DeviceType::POINTER;
- } else if (deviceTypeString != "default") {
- ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.c_str());
- }
- }
-
mParameters.orientationAware = mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
getDeviceContext().getConfiguration().tryGetProperty("touch.orientationAware",
mParameters.orientationAware);
@@ -444,7 +421,9 @@
mParameters.associatedDisplayIsExternal = false;
if (mParameters.orientationAware ||
mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN ||
- mParameters.deviceType == Parameters::DeviceType::POINTER) {
+ mParameters.deviceType == Parameters::DeviceType::POINTER ||
+ (mParameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION &&
+ getDeviceContext().getAssociatedViewport())) {
mParameters.hasAssociatedDisplay = true;
if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal();
@@ -473,6 +452,34 @@
mParameters.enableForInactiveViewport);
}
+void TouchInputMapper::configureDeviceType() {
+ if (getDeviceContext().hasInputProperty(INPUT_PROP_DIRECT)) {
+ // The device is a touch screen.
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
+ } else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) {
+ // The device is a pointing device like a track pad.
+ mParameters.deviceType = Parameters::DeviceType::POINTER;
+ } else {
+ // The device is a touch pad of unknown purpose.
+ mParameters.deviceType = Parameters::DeviceType::POINTER;
+ }
+
+ // Type association takes precedence over the device type found in the idc file.
+ std::string deviceTypeString = getDeviceContext().getDeviceTypeAssociation().value_or("");
+ if (deviceTypeString.empty()) {
+ getDeviceContext().getConfiguration().tryGetProperty("touch.deviceType", deviceTypeString);
+ }
+ if (deviceTypeString == "touchScreen") {
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
+ } else if (deviceTypeString == "touchNavigation") {
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_NAVIGATION;
+ } else if (deviceTypeString == "pointer") {
+ mParameters.deviceType = Parameters::DeviceType::POINTER;
+ } else if (deviceTypeString != "default" && deviceTypeString != "") {
+ ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.c_str());
+ }
+}
+
void TouchInputMapper::dumpParameters(std::string& dump) {
dump += INDENT3 "Parameters:\n";
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 34ba625..50a7ea3 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -814,6 +814,8 @@
static void assignPointerIds(const RawState& last, RawState& current);
void rotateAndScale(float& x, float& y) const;
+
+ void configureDeviceType();
};
} // namespace android
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 3af4298..dc7e581 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -126,6 +126,11 @@
mConfig.portAssociations.insert({inputPort, displayPort});
}
+void FakeInputReaderPolicy::addDeviceTypeAssociation(const std::string& inputPort,
+ const std::string& type) {
+ mConfig.deviceTypeAssociations.insert({inputPort, type});
+}
+
void FakeInputReaderPolicy::addInputUniqueIdAssociation(const std::string& inputUniqueId,
const std::string& displayUniqueId) {
mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId});
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index c16cda4..faa9c01 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -55,6 +55,7 @@
bool updateViewport(const DisplayViewport& viewport);
void addExcludedDeviceName(const std::string& deviceName);
void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort);
+ void addDeviceTypeAssociation(const std::string& inputPort, const std::string& type);
void addInputUniqueIdAssociation(const std::string& inputUniqueId,
const std::string& displayUniqueId);
void addDisabledDevice(int32_t deviceId);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 41c174a..d2ff097 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -29,6 +29,7 @@
#include <sys/epoll.h>
#include <cinttypes>
+#include <compare>
#include <thread>
#include <unordered_set>
#include <vector>
@@ -56,6 +57,7 @@
static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
static constexpr int32_t SECOND_DISPLAY_ID = 1;
+static constexpr int32_t ACTION_OUTSIDE = AMOTION_EVENT_ACTION_OUTSIDE;
static constexpr int32_t POINTER_1_DOWN =
AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
static constexpr int32_t POINTER_2_DOWN =
@@ -84,6 +86,7 @@
struct PointF {
float x;
float y;
+ auto operator<=>(const PointF&) const = default;
};
/**
@@ -137,6 +140,24 @@
return arg.getSource() == source;
}
+MATCHER_P2(WithCoords, x, y, "MotionEvent with specified coordinates") {
+ if (arg.getPointerCount() != 1) {
+ *result_listener << "Expected 1 pointer, got " << arg.getPointerCount();
+ return false;
+ }
+ return arg.getX(0 /*pointerIndex*/) == x && arg.getY(0 /*pointerIndex*/) == y;
+}
+
+MATCHER_P(WithPointers, pointers, "MotionEvent with specified pointers") {
+ // Build a map for the received pointers, by pointer id
+ std::map<int32_t /*pointerId*/, PointF> actualPointers;
+ for (size_t pointerIndex = 0; pointerIndex < arg.getPointerCount(); pointerIndex++) {
+ const int32_t pointerId = arg.getPointerId(pointerIndex);
+ actualPointers[pointerId] = {arg.getX(pointerIndex), arg.getY(pointerIndex)};
+ }
+ return pointers == actualPointers;
+}
+
// --- FakeInputDispatcherPolicy ---
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
@@ -2536,6 +2557,39 @@
}
/**
+ * Two windows. First is a regular window. Second does not overlap with the first, and has
+ * WATCH_OUTSIDE_TOUCH.
+ * Both windows are owned by the same UID.
+ * Tap first window. Make sure that the second window receives ACTION_OUTSIDE with correct, non-zero
+ * coordinates. The coordinates are not zeroed out because both windows are owned by the same UID.
+ */
+TEST_F(InputDispatcherTest, ActionOutsideForOwnedWindowHasValidCoordinates) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "First Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect{0, 0, 100, 100});
+
+ sp<FakeWindowHandle> outsideWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
+ ADISPLAY_ID_DEFAULT);
+ outsideWindow->setFrame(Rect{100, 100, 200, 200});
+ outsideWindow->setWatchOutsideTouch(true);
+ // outsideWindow must be above 'window' to receive ACTION_OUTSIDE events when 'window' is tapped
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {outsideWindow, window}}});
+
+ // Tap on first window.
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {PointF{50, 50}});
+ mDispatcher->notifyMotion(&motionArgs);
+ window->consumeMotionDown();
+ // The coordinates of the tap in 'outsideWindow' are relative to its top left corner.
+ // Therefore, we should offset them by (100, 100) relative to the screen's top left corner.
+ outsideWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_OUTSIDE), WithCoords(-50, -50)));
+}
+
+/**
* This test documents the behavior of WATCH_OUTSIDE_TOUCH. The window will get ACTION_OUTSIDE when
* a another pointer causes ACTION_DOWN to be sent to another window for the first time. Only one
* ACTION_OUTSIDE event is sent per gesture.
@@ -2570,7 +2624,9 @@
motionArgs = generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{PointF{-10, -10}, PointF{105, 105}});
mDispatcher->notifyMotion(&motionArgs);
- window->consumeMotionOutside();
+ const std::map<int32_t, PointF> expectedPointers{{0, PointF{-10, -10}}, {1, PointF{105, 105}}};
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_OUTSIDE), WithPointers(expectedPointers)));
secondWindow->consumeMotionDown();
thirdWindow->assertNoEvents();
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 78b70aa..db6bf45 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -6505,6 +6505,17 @@
WithCoords(toDisplayX(100), toDisplayY(200)), WithButtonState(0))));
}
+TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsSetToTouchNavigation_setsCorrectType) {
+ mFakePolicy->addDeviceTypeAssociation(DEVICE_LOCATION, "touchNavigation");
+ prepareDisplay(ui::ROTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+
+ ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mapper.getSources());
+}
+
// --- TouchDisplayProjectionTest ---
class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 0a192c5..e0dcab5 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -26,8 +26,8 @@
name: "libsurfaceflinger_defaults",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
+ "librenderengine_deps",
"surfaceflinger_defaults",
- "skia_renderengine_deps",
],
cflags: [
"-DLOG_TAG=\"SurfaceFlinger\"",
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 30d34a5..f3a0186 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -11,6 +11,7 @@
name: "libcompositionengine_defaults",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
+ "librenderengine_deps",
"surfaceflinger_defaults",
],
cflags: [
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 46b857b..96ae77f 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -452,7 +452,8 @@
}
}
-auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) -> DesiredActiveModeAction {
+auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info, bool force)
+ -> DesiredActiveModeAction {
ATRACE_CALL();
LOG_ALWAYS_FATAL_IF(!info.modeOpt, "desired mode not provided");
@@ -473,7 +474,7 @@
const auto& desiredMode = *info.modeOpt->modePtr;
// Check if we are already at the desired mode
- if (refreshRateSelector().getActiveMode().modePtr->getId() == desiredMode.getId()) {
+ if (!force && refreshRateSelector().getActiveMode().modePtr->getId() == desiredMode.getId()) {
if (refreshRateSelector().getActiveMode() == info.modeOpt) {
return DesiredActiveModeAction::None;
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 8f9b2a1..d757673 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -210,7 +210,8 @@
InitiateDisplayModeSwitch,
InitiateRenderRateSwitch
};
- DesiredActiveModeAction setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock);
+ DesiredActiveModeAction setDesiredActiveMode(const ActiveModeInfo&, bool force = false)
+ EXCLUDES(mActiveModeLock);
std::optional<ActiveModeInfo> getDesiredActiveMode() const EXCLUDES(mActiveModeLock);
void clearDesiredActiveModeState() EXCLUDES(mActiveModeLock);
ActiveModeInfo getUpcomingActiveMode() const REQUIRES(kMainThreadContext) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 34f8df2..856fda0 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -815,18 +815,18 @@
.powerOnImminent = powerOnImminent};
}
-ftl::Optional<FrameRateMode> Scheduler::getPreferredDisplayMode() {
+FrameRateMode Scheduler::getPreferredDisplayMode() {
std::lock_guard<std::mutex> lock(mPolicyLock);
- // Make sure the stored mode is up to date.
- if (mPolicy.modeOpt) {
- const auto ranking =
- leaderSelectorPtr()
- ->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals())
- .ranking;
+ const auto frameRateMode =
+ leaderSelectorPtr()
+ ->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals())
+ .ranking.front()
+ .frameRateMode;
- mPolicy.modeOpt = ranking.front().frameRateMode;
- }
- return mPolicy.modeOpt;
+ // Make sure the stored mode is up to date.
+ mPolicy.modeOpt = frameRateMode;
+
+ return frameRateMode;
}
void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index cf2ffb8..f189426 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -209,8 +209,8 @@
void dump(ConnectionHandle, std::string&) const;
void dumpVsync(std::string&) const;
- // Get the appropriate refresh for current conditions.
- ftl::Optional<FrameRateMode> getPreferredDisplayMode();
+ // Returns the preferred refresh rate and frame rate for the leader display.
+ FrameRateMode getPreferredDisplayMode();
// Notifies the scheduler about a refresh rate timeline change.
void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 60000ff..b930477 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -299,6 +299,7 @@
const String16 sDump("android.permission.DUMP");
const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW");
+const String16 sWakeupSurfaceFlinger("android.permission.WAKEUP_SURFACE_FLINGER");
const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
@@ -332,20 +333,12 @@
}
}
-bool callingThreadHasRotateSurfaceFlingerAccess() {
+bool callingThreadHasPermission(const String16& permission) {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
- PermissionCache::checkPermission(sRotateSurfaceFlinger, pid, uid);
-}
-
-bool callingThreadHasInternalSystemWindowAccess() {
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int uid = ipc->getCallingUid();
- return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
- PermissionCache::checkPermission(sInternalSystemWindow, pid, uid);
+ PermissionCache::checkPermission(permission, pid, uid);
}
SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
@@ -1139,7 +1132,7 @@
return NO_ERROR;
}
-void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request) {
+void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request, bool force) {
ATRACE_CALL();
auto display = getDisplayDeviceLocked(request.mode.modePtr->getPhysicalDisplayId());
@@ -1151,7 +1144,8 @@
const auto mode = request.mode;
const bool emitEvent = request.emitEvent;
- switch (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)))) {
+ switch (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)),
+ force)) {
case DisplayDevice::DesiredActiveModeAction::InitiateDisplayModeSwitch:
scheduleComposite(FrameHint::kNone);
@@ -3464,10 +3458,21 @@
for (auto& request : modeRequests) {
const auto& modePtr = request.mode.modePtr;
- const auto display = getDisplayDeviceLocked(modePtr->getPhysicalDisplayId());
+
+ const auto displayId = modePtr->getPhysicalDisplayId();
+ const auto display = getDisplayDeviceLocked(displayId);
if (!display) continue;
+ const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
+ .transform(&PhysicalDisplay::isInternal)
+ .value_or(false);
+
+ if (isInternalDisplay && displayId != mActiveDisplayId) {
+ ALOGV("%s(%s): Inactive display", __func__, to_string(displayId).c_str());
+ continue;
+ }
+
if (display->refreshRateSelector().isModeAllowed(request.mode)) {
setDesiredActiveMode(std::move(request));
} else {
@@ -3989,18 +3994,23 @@
// Avoid checking for rotation permissions if the caller already has ACCESS_SURFACE_FLINGER
// permissions.
if ((permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) ||
- callingThreadHasRotateSurfaceFlingerAccess()) {
+ callingThreadHasPermission(sRotateSurfaceFlinger)) {
permissions |= layer_state_t::Permission::ROTATE_SURFACE_FLINGER;
}
- if (callingThreadHasInternalSystemWindowAccess()) {
+ if (callingThreadHasPermission(sInternalSystemWindow)) {
permissions |= layer_state_t::Permission::INTERNAL_SYSTEM_WINDOW;
}
- if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) &&
- (flags & (eEarlyWakeupStart | eEarlyWakeupEnd))) {
- ALOGE("Only WindowManager is allowed to use eEarlyWakeup[Start|End] flags");
- flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
+ if (flags & (eEarlyWakeupStart | eEarlyWakeupEnd)) {
+ const bool hasPermission =
+ (permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) ||
+ callingThreadHasPermission(sWakeupSurfaceFlinger);
+ if (!hasPermission) {
+ ALOGE("Caller needs permission android.permission.WAKEUP_SURFACE_FLINGER to use "
+ "eEarlyWakeup[Start|End] flags");
+ flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
+ }
}
const int64_t postTime = systemTime();
@@ -6727,7 +6737,7 @@
ftl::Optional<scheduler::FrameRateMode> SurfaceFlinger::getPreferredDisplayMode(
PhysicalDisplayId displayId, DisplayModeId defaultModeId) const {
if (const auto schedulerMode = mScheduler->getPreferredDisplayMode();
- schedulerMode && schedulerMode->modePtr->getPhysicalDisplayId() == displayId) {
+ schedulerMode.modePtr->getPhysicalDisplayId() == displayId) {
return schedulerMode;
}
@@ -6762,12 +6772,24 @@
case SetPolicyResult::Unchanged:
return NO_ERROR;
case SetPolicyResult::Changed:
- return applyRefreshRateSelectorPolicy(displayId, selector);
+ break;
}
+
+ const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
+ .transform(&PhysicalDisplay::isInternal)
+ .value_or(false);
+
+ if (isInternalDisplay && displayId != mActiveDisplayId) {
+ // The policy will be be applied when the display becomes active.
+ ALOGV("%s(%s): Inactive display", __func__, to_string(displayId).c_str());
+ return NO_ERROR;
+ }
+
+ return applyRefreshRateSelectorPolicy(displayId, selector);
}
status_t SurfaceFlinger::applyRefreshRateSelectorPolicy(
- PhysicalDisplayId displayId, const scheduler::RefreshRateSelector& selector) {
+ PhysicalDisplayId displayId, const scheduler::RefreshRateSelector& selector, bool force) {
const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy();
ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
@@ -6797,7 +6819,7 @@
return INVALID_OPERATION;
}
- setDesiredActiveMode({std::move(preferredMode), .emitEvent = true});
+ setDesiredActiveMode({std::move(preferredMode), .emitEvent = true}, force);
return NO_ERROR;
}
@@ -7101,7 +7123,8 @@
// case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In either
// case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode,
// and the kernel idle timer of the newly active display must be toggled.
- applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay->refreshRateSelector());
+ constexpr bool kForce = true;
+ applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay->refreshRateSelector(), kForce);
}
status_t SurfaceFlinger::addWindowInfosListener(
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index fa3a49f..7bb0514 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -649,7 +649,8 @@
// Show render rate with refresh rate overlay
bool mRefreshRateOverlayRenderRate = false;
- void setDesiredActiveMode(display::DisplayModeRequest&&) REQUIRES(mStateLock);
+ void setDesiredActiveMode(display::DisplayModeRequest&&, bool force = false)
+ REQUIRES(mStateLock);
status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId);
// Sets the active mode and a new refresh rate in SF.
@@ -678,7 +679,8 @@
// TODO(b/241285191): Look up RefreshRateSelector on Scheduler to remove redundant parameter.
status_t applyRefreshRateSelectorPolicy(PhysicalDisplayId,
- const scheduler::RefreshRateSelector&)
+ const scheduler::RefreshRateSelector&,
+ bool force = false)
REQUIRES(mStateLock, kMainThreadContext);
void commitTransactions() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
@@ -1187,7 +1189,9 @@
display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
// The inner or outer display for foldables, assuming they have mutually exclusive power states.
- PhysicalDisplayId mActiveDisplayId GUARDED_BY(mStateLock);
+ // Atomic because writes from onActiveDisplayChangedLocked are not always under mStateLock, but
+ // reads from ISchedulerCallback::requestDisplayModes may happen concurrently.
+ std::atomic<PhysicalDisplayId> mActiveDisplayId GUARDED_BY(mStateLock);
struct {
DisplayIdGenerator<GpuVirtualDisplayId> gpu;
diff --git a/services/surfaceflinger/Tracing/tools/Android.bp b/services/surfaceflinger/Tracing/tools/Android.bp
index e8fe734..b6435a8 100644
--- a/services/surfaceflinger/Tracing/tools/Android.bp
+++ b/services/surfaceflinger/Tracing/tools/Android.bp
@@ -25,8 +25,8 @@
name: "layertracegenerator",
defaults: [
"libsurfaceflinger_mocks_defaults",
+ "librenderengine_deps",
"surfaceflinger_defaults",
- "skia_renderengine_deps",
],
srcs: [
":libsurfaceflinger_sources",
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index bf22521..6d1b3fe 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -140,6 +140,7 @@
defaults: [
"android.hardware.graphics.common-ndk_static",
"android.hardware.graphics.composer3-ndk_static",
+ "librenderengine_deps",
],
static_libs: [
"android.hardware.common-V2-ndk",
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 074bf8c..ad3bd35 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#include "mock/MockDisplayModeSpecs.h"
-#include "mock/MockEventThread.h"
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
#include "DisplayTransactionTestHelpers.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockDisplayModeSpecs.h"
#include <ftl/fake_guard.h>
#include <scheduler/Fps.h>
@@ -42,8 +42,7 @@
PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
- DisplayModes modes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
- auto selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
+ auto selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(kModes, kModeId60);
setupScheduler(selectorPtr);
@@ -51,7 +50,7 @@
mFlinger.configureAndCommit();
mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
- .setDisplayModes(std::move(modes), kModeId60, std::move(selectorPtr))
+ .setDisplayModes(kModes, kModeId60, std::move(selectorPtr))
.inject();
// isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
@@ -78,6 +77,8 @@
static constexpr ui::Size kResolution4K{3840, 2160};
static inline const DisplayModePtr kMode90_4K =
createDisplayMode(kModeId90_4K, 90_Hz, 3, kResolution4K);
+
+ static inline const DisplayModes kModes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
};
void DisplayModeSwitchingTest::setupScheduler(
@@ -283,5 +284,114 @@
ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K);
}
+TEST_F(DisplayModeSwitchingTest, multiDisplay) {
+ ftl::FakeGuard guard(kMainThreadContext);
+
+ constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID;
+ constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1;
+
+ constexpr PhysicalDisplayId kOuterDisplayId = PhysicalDisplayId::fromPort(254u);
+
+ constexpr bool kIsPrimary = false;
+ TestableSurfaceFlinger::FakeHwcDisplayInjector(kOuterDisplayId, hal::DisplayType::PHYSICAL,
+ kIsPrimary)
+ .setHwcDisplayId(kOuterDisplayHwcId)
+ .inject(&mFlinger, mComposer);
+
+ const auto outerDisplay = mFakeDisplayInjector.injectInternalDisplay(
+ [&](FakeDisplayDeviceInjector& injector) {
+ injector.setDisplayModes(mock::cloneForDisplay(kOuterDisplayId, kModes),
+ kModeId120);
+ },
+ {.displayId = kOuterDisplayId,
+ .hwcDisplayId = kOuterDisplayHwcId,
+ .isPrimary = kIsPrimary});
+
+ const auto& innerDisplay = mDisplay;
+
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+
+ EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId120);
+
+ mFlinger.onActiveDisplayChanged(innerDisplay);
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90.value(),
+ false, 0.f, 120.f)));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId60.value(),
+ false, 0.f, 120.f)));
+
+ // Transition on the inner display.
+ ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+
+ // No transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kInnerDisplayHwcId,
+ hal::HWConfigId(kModeId90.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ // Transition on the inner display.
+ ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+
+ // No transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+
+ mFlinger.commit();
+
+ // Transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId90);
+
+ // No transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId120);
+
+ mFlinger.onActiveDisplayChanged(outerDisplay);
+
+ // No transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+
+ // Transition on the outer display.
+ ASSERT_TRUE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId60);
+
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kOuterDisplayHwcId,
+ hal::HWConfigId(kModeId60.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ // No transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+
+ // Transition on the outer display.
+ ASSERT_TRUE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId60);
+
+ mFlinger.commit();
+
+ // No transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId90);
+
+ // Transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId60);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
index c78b6bd..3b36361 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
@@ -38,4 +38,24 @@
return createDisplayMode(modeId, refreshRate, {}, {}, displayId);
}
+inline DisplayModePtr cloneForDisplay(PhysicalDisplayId displayId, const DisplayModePtr& modePtr) {
+ return DisplayMode::Builder(modePtr->getHwcId())
+ .setId(modePtr->getId())
+ .setPhysicalDisplayId(displayId)
+ .setVsyncPeriod(modePtr->getVsyncPeriod())
+ .setGroup(modePtr->getGroup())
+ .setResolution(modePtr->getResolution())
+ .build();
+}
+
+inline DisplayModes cloneForDisplay(PhysicalDisplayId displayId, const DisplayModes& modes) {
+ DisplayModes clones;
+
+ for (const auto& [id, modePtr] : modes) {
+ clones.try_emplace(id, cloneForDisplay(displayId, modePtr));
+ }
+
+ return clones;
+}
+
} // namespace android::mock