Merge "Check for buffer changes explicitly instead of relying on acquire fence changes" into sc-dev
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index 17ea903..a27fd10 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -86,7 +86,7 @@
bool generate_compact_dex,
bool use_jitzygote_image,
const char* compilation_reason) {
- PrepareBootImageAndBootClasspathFlags(use_jitzygote_image);
+ PrepareBootImageFlags(use_jitzygote_image);
PrepareInputFileFlags(output_oat, output_vdex, output_image, input_dex, input_vdex,
dex_metadata, profile, swap_fd, class_loader_context,
@@ -112,7 +112,7 @@
RunDex2Oat::~RunDex2Oat() {}
-void RunDex2Oat::PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image) {
+void RunDex2Oat::PrepareBootImageFlags(bool use_jitzygote_image) {
std::string boot_image;
if (use_jitzygote_image) {
boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
@@ -120,23 +120,6 @@
boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
}
AddArg(boot_image);
-
- // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query
- // BOOTCLASSPATH.
- char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");
- if (dex2oat_bootclasspath != nullptr) {
- AddRuntimeArg(StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath));
- }
-
- std::string updatable_bcp_packages =
- MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file",
- "--updatable-bcp-packages-file=%s");
- if (updatable_bcp_packages.empty()) {
- // Make dex2oat fail by providing non-existent file name.
- updatable_bcp_packages =
- "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt";
- }
- AddArg(updatable_bcp_packages);
}
void RunDex2Oat::PrepareInputFileFlags(const UniqueFile& output_oat,
diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h
index 325a3a2..475e124 100644
--- a/cmds/installd/run_dex2oat.h
+++ b/cmds/installd/run_dex2oat.h
@@ -56,7 +56,7 @@
void Exec(int exit_code);
protected:
- void PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image);
+ void PrepareBootImageFlags(bool use_jitzygote_image);
void PrepareInputFileFlags(const UniqueFile& output_oat,
const UniqueFile& output_vdex,
const UniqueFile& output_image,
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
index 3813cf7..0a638cd 100644
--- a/cmds/installd/run_dex2oat_test.cpp
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -175,8 +175,6 @@
default_expected_flags_["--swap-fd"] = FLAG_UNUSED;
default_expected_flags_["--class-loader-context"] = FLAG_UNUSED;
default_expected_flags_["--class-loader-context-fds"] = FLAG_UNUSED;
- default_expected_flags_["--updatable-bcp-packages-file"] =
- "=/nonx/updatable-bcp-packages.txt";
// Arch
default_expected_flags_["--instruction-set"] = "=arm64";
@@ -320,28 +318,6 @@
VerifyExpectedFlags();
}
-TEST_F(RunDex2OatTest, DEX2OATBOOTCLASSPATH) {
- ASSERT_EQ(nullptr, getenv("DEX2OATBOOTCLASSPATH"));
- ASSERT_EQ(0, setenv("DEX2OATBOOTCLASSPATH", "foobar", /*override=*/ false))
- << "Failed to setenv: " << strerror(errno);
-
- CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
-
- SetExpectedFlagUsed("-Xbootclasspath", ":foobar");
- VerifyExpectedFlags();
-
- ASSERT_EQ(0, unsetenv("DEX2OATBOOTCLASSPATH"))
- << "Failed to setenv: " << strerror(errno);
-}
-
-TEST_F(RunDex2OatTest, UpdatableBootClassPath) {
- setSystemProperty("dalvik.vm.dex2oat-updatable-bcp-packages-file", "/path/to/file");
- CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs());
-
- SetExpectedFlagUsed("--updatable-bcp-packages-file", "=/path/to/file");
- VerifyExpectedFlags();
-}
-
TEST_F(RunDex2OatTest, DoNotGenerateCompactDex) {
auto args = RunDex2OatArgs::MakeDefaultTestArgs();
args->generate_compact_dex = false;
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 91726cd..ee75957 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -455,10 +455,10 @@
__INTRODUCED_IN(29);
/**
- * Same as ASurfaceTransaction_setFrameRateWithSeamlessness(transaction, surface_control,
- * frameRate, compatibility, true).
+ * Same as ASurfaceTransaction_setFrameRateWithChangeStrategy(transaction, surface_control,
+ * frameRate, compatibility, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS).
*
- * See ASurfaceTransaction_setFrameRateWithSeamlessness().
+ * See ASurfaceTransaction_setFrameRateWithChangeStrategy().
*
* Available since API level 30.
*/
@@ -486,17 +486,15 @@
* influence the system's choice of display frame rate. To specify a compatibility use the
* ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum.
*
- * \param shouldBeSeamless Whether display refresh rate transitions should be seamless. A
- * seamless transition is one that doesn't have any visual interruptions, such as a black
- * screen for a second or two. True indicates that any frame rate changes caused by this
- * request should be seamless. False indicates that non-seamless refresh rates are also
- * acceptable.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless.
+ * A seamless transition is one that doesn't have any visual interruptions, such as a black
+ * screen for a second or two. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values.
*
* Available since API level 31.
*/
-void ASurfaceTransaction_setFrameRateWithSeamlessness(ASurfaceTransaction* transaction,
+void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, float frameRate,
- int8_t compatibility, bool shouldBeSeamless)
+ int8_t compatibility, int8_t changeFrameRateStrategy)
__INTRODUCED_IN(31);
__END_DECLS
diff --git a/include/input/Input.h b/include/input/Input.h
index f9fe6b9..bb5ca0e 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -266,6 +266,20 @@
const char* motionClassificationToString(MotionClassification classification);
/**
+ * Portion of FrameMetrics timeline of interest to input code.
+ */
+enum GraphicsTimeline : size_t {
+ /** Time when the app sent the buffer to SurfaceFlinger. */
+ GPU_COMPLETED_TIME = 0,
+
+ /** Time when the frame was presented on the display */
+ PRESENT_TIME = 1,
+
+ /** Total size of the 'GraphicsTimeline' array. Must always be last. */
+ SIZE = 2
+};
+
+/**
* Generator of unique numbers used to identify input events.
*
* Layout of ID:
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 3e5674e..898d1a9 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -71,6 +71,7 @@
FOCUS,
CAPTURE,
DRAG,
+ TIMELINE,
};
struct Header {
@@ -195,6 +196,14 @@
inline size_t size() const { return sizeof(Drag); }
} drag;
+
+ struct Timeline {
+ int32_t eventId;
+ uint32_t empty;
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+
+ inline size_t size() const { return sizeof(Timeline); }
+ } timeline;
} __attribute__((aligned(8))) body;
bool isValid(size_t actualSize) const;
@@ -381,10 +390,25 @@
nsecs_t consumeTime;
};
- /* Receives the finished signal from the consumer in reply to the original dispatch signal.
- * If a signal was received, returns a Finished object.
+ struct Timeline {
+ int32_t inputEventId;
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+ };
+
+ typedef std::variant<Finished, Timeline> ConsumerResponse;
+ /* Receive a signal from the consumer in reply to the original dispatch signal.
+ * If a signal was received, returns a Finished or a Timeline object.
+ * The InputConsumer should return a Finished object for every InputMessage that it is sent
+ * to confirm that it has been processed and that the InputConsumer is responsive.
+ * If several InputMessages are sent to InputConsumer, it's possible to receive Finished
+ * events out of order for those messages.
*
- * The returned sequence number is never 0 unless the operation failed.
+ * The Timeline object is returned whenever the receiving end has processed a graphical frame
+ * and is returning the timeline of the frame. Not all input events will cause a Timeline
+ * object to be returned, and there is not guarantee about when it will arrive.
+ *
+ * If an object of Finished is returned, the returned sequence number is never 0 unless the
+ * operation failed.
*
* Returned error codes:
* OK on success.
@@ -392,7 +416,7 @@
* DEAD_OBJECT if the channel's peer has been closed.
* Other errors probably indicate that the channel is broken.
*/
- android::base::Result<Finished> receiveFinishedSignal();
+ android::base::Result<ConsumerResponse> receiveConsumerResponse();
private:
std::shared_ptr<InputChannel> mChannel;
@@ -448,6 +472,9 @@
*/
status_t sendFinishedSignal(uint32_t seq, bool handled);
+ status_t sendTimeline(int32_t inputEventId,
+ std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
+
/* Returns true if there is a deferred event waiting.
*
* Should be called after calling consume() to determine whether the consumer
diff --git a/libs/binder/RpcConnection.cpp b/libs/binder/RpcConnection.cpp
index 83a1618..dab3246 100644
--- a/libs/binder/RpcConnection.cpp
+++ b/libs/binder/RpcConnection.cpp
@@ -20,6 +20,7 @@
#include <binder/Parcel.h>
#include <binder/Stability.h>
+#include <utils/String8.h>
#include "RpcState.h"
#include "RpcWireFormat.h"
@@ -29,14 +30,20 @@
#include <sys/un.h>
#include <unistd.h>
-#if defined(__GLIBC__)
+#ifdef __GLIBC__
extern "C" pid_t gettid();
#endif
+#ifdef __BIONIC__
+#include <linux/vm_sockets.h>
+#endif
+
namespace android {
using base::unique_fd;
+RpcConnection::SocketAddress::~SocketAddress() {}
+
RpcConnection::RpcConnection() {
LOG_RPC_DETAIL("RpcConnection created %p", this);
@@ -50,65 +57,68 @@
return new RpcConnection;
}
+class UnixSocketAddress : public RpcConnection::SocketAddress {
+public:
+ explicit UnixSocketAddress(const char* path) : mAddr({.sun_family = AF_UNIX}) {
+ unsigned int pathLen = strlen(path) + 1;
+ LOG_ALWAYS_FATAL_IF(pathLen > sizeof(mAddr.sun_path), "%u %s", pathLen, path);
+ memcpy(mAddr.sun_path, path, pathLen);
+ }
+ virtual ~UnixSocketAddress() {}
+ std::string toString() const override {
+ return String8::format("path '%.*s'", static_cast<int>(sizeof(mAddr.sun_path)),
+ mAddr.sun_path)
+ .c_str();
+ }
+ const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
+ size_t addrSize() const override { return sizeof(mAddr); }
+
+private:
+ sockaddr_un mAddr;
+};
+
bool RpcConnection::setupUnixDomainServer(const char* path) {
- LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Only supports one server now");
-
- unique_fd serverFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
- if (serverFd == -1) {
- ALOGE("Could not create socket at %s: %s", path, strerror(errno));
- return false;
- }
-
- struct sockaddr_un addr = {
- .sun_family = AF_UNIX,
- };
-
- unsigned int pathLen = strlen(path) + 1;
- LOG_ALWAYS_FATAL_IF(pathLen > sizeof(addr.sun_path), "%u", pathLen);
- memcpy(addr.sun_path, path, pathLen);
-
- if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), (struct sockaddr*)&addr, sizeof(addr)))) {
- ALOGE("Could not bind socket at %s: %s", path, strerror(errno));
- return false;
- }
-
- if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
- ALOGE("Could not listen socket at %s: %s", path, strerror(errno));
- return false;
- }
-
- mServer = std::move(serverFd);
- return true;
+ return addServer(UnixSocketAddress(path));
}
bool RpcConnection::addUnixDomainClient(const char* path) {
- LOG_RPC_DETAIL("Connecting on path: %s", path);
-
- unique_fd serverFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
- if (serverFd == -1) {
- ALOGE("Could not create socket at %s: %s", path, strerror(errno));
- return false;
- }
-
- struct sockaddr_un addr = {
- .sun_family = AF_UNIX,
- };
-
- unsigned int pathLen = strlen(path) + 1;
- LOG_ALWAYS_FATAL_IF(pathLen > sizeof(addr.sun_path), "%u", pathLen);
- memcpy(addr.sun_path, path, pathLen);
-
- if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), (struct sockaddr*)&addr, sizeof(addr)))) {
- ALOGE("Could not connect socket at %s: %s", path, strerror(errno));
- return false;
- }
-
- LOG_RPC_DETAIL("Unix domain client with fd %d", serverFd.get());
-
- addClient(std::move(serverFd));
- return true;
+ return addClient(UnixSocketAddress(path));
}
+#ifdef __BIONIC__
+
+class VsockSocketAddress : public RpcConnection::SocketAddress {
+public:
+ VsockSocketAddress(unsigned int cid, unsigned int port)
+ : mAddr({
+ .svm_family = AF_VSOCK,
+ .svm_port = port,
+ .svm_cid = cid,
+ }) {}
+ virtual ~VsockSocketAddress() {}
+ std::string toString() const override {
+ return String8::format("cid %du port %du", mAddr.svm_cid, mAddr.svm_port).c_str();
+ }
+ const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
+ size_t addrSize() const override { return sizeof(mAddr); }
+
+private:
+ sockaddr_vm mAddr;
+};
+
+bool RpcConnection::setupVsockServer(unsigned int port) {
+ // realizing value w/ this type at compile time to avoid ubsan abort
+ constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
+
+ return addServer(VsockSocketAddress(kAnyCid, port));
+}
+
+bool RpcConnection::addVsockClient(unsigned int cid, unsigned int port) {
+ return addClient(VsockSocketAddress(cid, port));
+}
+
+#endif // __BIONIC__
+
sp<IBinder> RpcConnection::getRootObject() {
ExclusiveSocket socket(this, SocketUse::CLIENT);
return state()->getRootObject(socket.fd(), this);
@@ -130,11 +140,8 @@
void RpcConnection::join() {
// establish a connection
{
- struct sockaddr_un clientSa;
- socklen_t clientSaLen = sizeof(clientSa);
-
- unique_fd clientFd(TEMP_FAILURE_RETRY(
- accept4(mServer.get(), (struct sockaddr*)&clientSa, &clientSaLen, SOCK_CLOEXEC)));
+ unique_fd clientFd(
+ TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, 0 /*length*/, SOCK_CLOEXEC)));
if (clientFd < 0) {
// If this log becomes confusing, should save more state from setupUnixDomainServer
// in order to output here.
@@ -144,7 +151,7 @@
LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
- addServer(std::move(clientFd));
+ assignServerToThisThread(std::move(clientFd));
}
// We may not use the connection we just established (two threads might
@@ -170,14 +177,57 @@
return mForServer;
}
-void RpcConnection::addClient(base::unique_fd&& fd) {
- std::lock_guard<std::mutex> _l(mSocketMutex);
- sp<ConnectionSocket> connection = new ConnectionSocket();
- connection->fd = std::move(fd);
- mClients.push_back(connection);
+bool RpcConnection::addServer(const SocketAddress& addr) {
+ LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Each RpcConnection can only have one server.");
+
+ unique_fd serverFd(
+ TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ if (serverFd == -1) {
+ ALOGE("Could not create socket: %s", strerror(errno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), addr.addr(), addr.addrSize()))) {
+ int savedErrno = errno;
+ ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
+ int savedErrno = errno;
+ ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ mServer = std::move(serverFd);
+ return true;
}
-void RpcConnection::addServer(base::unique_fd&& fd) {
+bool RpcConnection::addClient(const SocketAddress& addr) {
+ unique_fd serverFd(
+ TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ if (serverFd == -1) {
+ int savedErrno = errno;
+ ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
+ int savedErrno = errno;
+ ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
+
+ std::lock_guard<std::mutex> _l(mSocketMutex);
+ sp<ConnectionSocket> connection = new ConnectionSocket();
+ connection->fd = std::move(serverFd);
+ mClients.push_back(connection);
+ return true;
+}
+
+void RpcConnection::assignServerToThisThread(base::unique_fd&& fd) {
std::lock_guard<std::mutex> _l(mSocketMutex);
sp<ConnectionSocket> connection = new ConnectionSocket();
connection->fd = std::move(fd);
diff --git a/libs/binder/include/binder/RpcConnection.h b/libs/binder/include/binder/RpcConnection.h
index 65c5232..a300575 100644
--- a/libs/binder/include/binder/RpcConnection.h
+++ b/libs/binder/include/binder/RpcConnection.h
@@ -61,6 +61,18 @@
*/
[[nodiscard]] bool addUnixDomainClient(const char* path);
+#ifdef __BIONIC__
+ /**
+ * Creates an RPC server at the current port.
+ */
+ [[nodiscard]] bool setupVsockServer(unsigned int port);
+
+ /**
+ * Connects to an RPC server at the CVD & port.
+ */
+ [[nodiscard]] bool addVsockClient(unsigned int cvd, unsigned int port);
+#endif // __BIONIC__
+
/**
* Query the other side of the connection for the root object hosted by that
* process's RpcServer (if one exists)
@@ -85,11 +97,20 @@
// internal only
const std::unique_ptr<RpcState>& state() { return mState; }
+ class SocketAddress {
+ public:
+ virtual ~SocketAddress();
+ virtual std::string toString() const = 0;
+ virtual const sockaddr* addr() const = 0;
+ virtual size_t addrSize() const = 0;
+ };
+
private:
RpcConnection();
- void addServer(base::unique_fd&& fd);
- void addClient(base::unique_fd&& fd);
+ bool addServer(const SocketAddress& address);
+ bool addClient(const SocketAddress& address);
+ void assignServerToThisThread(base::unique_fd&& fd);
struct ConnectionSocket : public RefBase {
base::unique_fd fd;
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index a44cddf..2a3658e 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -122,6 +122,7 @@
],
test_suites: ["general-tests"],
require_root: true,
+ host_supported: true,
}
cc_test {
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 6fa5333..936ee5e 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -14,14 +14,6 @@
* limitations under the License.
*/
-#include <sys/prctl.h>
-#include <unistd.h>
-
-#include <chrono>
-#include <cstdlib>
-#include <iostream>
-#include <thread>
-
#include <BnBinderRpcSession.h>
#include <BnBinderRpcTest.h>
#include <android-base/logging.h>
@@ -33,6 +25,18 @@
#include <binder/RpcServer.h>
#include <gtest/gtest.h>
+#include <chrono>
+#include <cstdlib>
+#include <iostream>
+#include <thread>
+
+#ifdef __BIONIC__
+#include <linux/vm_sockets.h>
+#endif //__BIONIC__
+
+#include <sys/prctl.h>
+#include <unistd.h>
+
#include "../RpcState.h" // for debugging
namespace android {
@@ -185,8 +189,13 @@
static std::string allocateSocketAddress() {
static size_t id = 0;
+ static bool gUseTmp = access("/tmp/", F_OK) != -1;
- return "/dev/binderRpcTest_" + std::to_string(id++);
+ if (gUseTmp) {
+ return "/tmp/binderRpcTest_" + std::to_string(id++);
+ } else {
+ return "/dev/binderRpcTest_" + std::to_string(id++);
+ }
};
struct ProcessConnection {
@@ -214,58 +223,6 @@
}
};
-// This creates a new process serving an interface on a certain number of
-// threads.
-ProcessConnection createRpcTestSocketServerProcess(
- size_t numThreads,
- const std::function<void(const sp<RpcServer>&, const sp<RpcConnection>&)>& configure) {
- CHECK_GT(numThreads, 0);
-
- std::string addr = allocateSocketAddress();
- unlink(addr.c_str());
-
- auto ret = ProcessConnection{
- .host = Process([&] {
- sp<RpcServer> server = RpcServer::make();
-
- server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
-
- // server supporting one client on one socket
- sp<RpcConnection> connection = server->addClientConnection();
- CHECK(connection->setupUnixDomainServer(addr.c_str())) << addr;
-
- configure(server, connection);
-
- // accept 'numThreads' connections
- std::vector<std::thread> pool;
- for (size_t i = 0; i + 1 < numThreads; i++) {
- pool.push_back(std::thread([=] { connection->join(); }));
- }
- connection->join();
- for (auto& t : pool) t.join();
- }),
- .connection = RpcConnection::make(),
- };
-
- // wait up to 1s for sockets to be created
- constexpr useconds_t kMaxWaitUs = 1000000;
- constexpr useconds_t kWaitDivision = 100;
- for (size_t i = 0; i < kWaitDivision && 0 != access(addr.c_str(), F_OK); i++) {
- usleep(kMaxWaitUs / kWaitDivision);
- }
-
- // create remainder of connections
- for (size_t i = 0; i < numThreads; i++) {
- // Connection refused sometimes after file created but before listening.
- CHECK(ret.connection->addUnixDomainClient(addr.c_str()) ||
- (usleep(10000), ret.connection->addUnixDomainClient(addr.c_str())))
- << i;
- }
-
- ret.rootBinder = ret.connection->getRootObject();
- return ret;
-}
-
// Process connection where the process hosts IBinderRpcTest, the server used
// for most testing here
struct BinderRpcTestProcessConnection {
@@ -290,26 +247,122 @@
}
};
-BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads) {
- BinderRpcTestProcessConnection ret{
- .proc = createRpcTestSocketServerProcess(numThreads,
- [&](const sp<RpcServer>& server,
- const sp<RpcConnection>& connection) {
- sp<MyBinderRpcTest> service =
- new MyBinderRpcTest;
- server->setRootObject(service);
- service->connection =
- connection; // for testing only
- }),
- };
-
- ret.rootBinder = ret.proc.rootBinder;
- ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
-
- return ret;
+enum class SocketType {
+ UNIX,
+#ifdef __BIONIC__
+ VSOCK,
+#endif // __BIONIC__
+};
+static inline std::string PrintSocketType(const testing::TestParamInfo<SocketType>& info) {
+ switch (info.param) {
+ case SocketType::UNIX:
+ return "unix_domain_socket";
+#ifdef __BIONIC__
+ case SocketType::VSOCK:
+ return "vm_socket";
+#endif // __BIONIC__
+ default:
+ LOG_ALWAYS_FATAL("Unknown socket type");
+ return "";
+ }
}
+class BinderRpc : public ::testing::TestWithParam<SocketType> {
+public:
+ // This creates a new process serving an interface on a certain number of
+ // threads.
+ ProcessConnection createRpcTestSocketServerProcess(
+ size_t numThreads,
+ const std::function<void(const sp<RpcServer>&, const sp<RpcConnection>&)>& configure) {
+ CHECK_GT(numThreads, 0);
-TEST(BinderRpc, RootObjectIsNull) {
+ SocketType socketType = GetParam();
+
+ std::string addr = allocateSocketAddress();
+ unlink(addr.c_str());
+ static unsigned int port = 3456;
+ port++;
+
+ auto ret = ProcessConnection{
+ .host = Process([&] {
+ sp<RpcServer> server = RpcServer::make();
+
+ server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction();
+
+ // server supporting one client on one socket
+ sp<RpcConnection> connection = server->addClientConnection();
+
+ switch (socketType) {
+ case SocketType::UNIX:
+ CHECK(connection->setupUnixDomainServer(addr.c_str())) << addr;
+ break;
+#ifdef __BIONIC__
+ case SocketType::VSOCK:
+ CHECK(connection->setupVsockServer(port));
+ break;
+#endif // __BIONIC__
+ default:
+ LOG_ALWAYS_FATAL("Unknown socket type");
+ }
+
+ configure(server, connection);
+
+ // accept 'numThreads' connections
+ std::vector<std::thread> pool;
+ for (size_t i = 0; i + 1 < numThreads; i++) {
+ pool.push_back(std::thread([=] { connection->join(); }));
+ }
+ connection->join();
+ for (auto& t : pool) t.join();
+ }),
+ .connection = RpcConnection::make(),
+ };
+
+ // create remainder of connections
+ for (size_t i = 0; i < numThreads; i++) {
+ for (size_t tries = 0; tries < 5; tries++) {
+ usleep(10000);
+ switch (socketType) {
+ case SocketType::UNIX:
+ if (ret.connection->addUnixDomainClient(addr.c_str())) goto success;
+ break;
+#ifdef __BIONIC__
+ case SocketType::VSOCK:
+ if (ret.connection->addVsockClient(VMADDR_CID_LOCAL, port)) goto success;
+ break;
+#endif // __BIONIC__
+ default:
+ LOG_ALWAYS_FATAL("Unknown socket type");
+ }
+ }
+ LOG_ALWAYS_FATAL("Could not connect");
+ success:;
+ }
+
+ ret.rootBinder = ret.connection->getRootObject();
+ return ret;
+ }
+
+ BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads) {
+ BinderRpcTestProcessConnection ret{
+ .proc = createRpcTestSocketServerProcess(numThreads,
+ [&](const sp<RpcServer>& server,
+ const sp<RpcConnection>& connection) {
+ sp<MyBinderRpcTest> service =
+ new MyBinderRpcTest;
+ server->setRootObject(service);
+ service->connection =
+ connection; // for testing only
+ }),
+ };
+
+ ret.rootBinder = ret.proc.rootBinder;
+ ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder);
+
+ return ret;
+ }
+};
+
+TEST_P(BinderRpc, RootObjectIsNull) {
auto proc = createRpcTestSocketServerProcess(1,
[](const sp<RpcServer>& server,
const sp<RpcConnection>&) {
@@ -324,20 +377,20 @@
EXPECT_EQ(nullptr, proc.connection->getRootObject());
}
-TEST(BinderRpc, Ping) {
+TEST_P(BinderRpc, Ping) {
auto proc = createRpcTestSocketServerProcess(1);
ASSERT_NE(proc.rootBinder, nullptr);
EXPECT_EQ(OK, proc.rootBinder->pingBinder());
}
-TEST(BinderRpc, TransactionsMustBeMarkedRpc) {
+TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) {
auto proc = createRpcTestSocketServerProcess(1);
Parcel data;
Parcel reply;
EXPECT_EQ(BAD_TYPE, proc.rootBinder->transact(IBinder::PING_TRANSACTION, data, &reply, 0));
}
-TEST(BinderRpc, UnknownTransaction) {
+TEST_P(BinderRpc, UnknownTransaction) {
auto proc = createRpcTestSocketServerProcess(1);
Parcel data;
data.markForBinder(proc.rootBinder);
@@ -345,19 +398,19 @@
EXPECT_EQ(UNKNOWN_TRANSACTION, proc.rootBinder->transact(1337, data, &reply, 0));
}
-TEST(BinderRpc, SendSomethingOneway) {
+TEST_P(BinderRpc, SendSomethingOneway) {
auto proc = createRpcTestSocketServerProcess(1);
EXPECT_OK(proc.rootIface->sendString("asdf"));
}
-TEST(BinderRpc, SendAndGetResultBack) {
+TEST_P(BinderRpc, SendAndGetResultBack) {
auto proc = createRpcTestSocketServerProcess(1);
std::string doubled;
EXPECT_OK(proc.rootIface->doubleString("cool ", &doubled));
EXPECT_EQ("cool cool ", doubled);
}
-TEST(BinderRpc, SendAndGetResultBackBig) {
+TEST_P(BinderRpc, SendAndGetResultBackBig) {
auto proc = createRpcTestSocketServerProcess(1);
std::string single = std::string(1024, 'a');
std::string doubled;
@@ -365,7 +418,7 @@
EXPECT_EQ(single + single, doubled);
}
-TEST(BinderRpc, CallMeBack) {
+TEST_P(BinderRpc, CallMeBack) {
auto proc = createRpcTestSocketServerProcess(1);
int32_t pingResult;
@@ -375,7 +428,7 @@
EXPECT_EQ(0, MyBinderRpcSession::gNum);
}
-TEST(BinderRpc, RepeatBinder) {
+TEST_P(BinderRpc, RepeatBinder) {
auto proc = createRpcTestSocketServerProcess(1);
sp<IBinder> inBinder = new MyBinderRpcSession("foo");
@@ -397,7 +450,7 @@
EXPECT_EQ(0, MyBinderRpcSession::gNum);
}
-TEST(BinderRpc, RepeatTheirBinder) {
+TEST_P(BinderRpc, RepeatTheirBinder) {
auto proc = createRpcTestSocketServerProcess(1);
sp<IBinderRpcSession> session;
@@ -421,7 +474,7 @@
EXPECT_EQ(nullptr, weak.promote());
}
-TEST(BinderRpc, RepeatBinderNull) {
+TEST_P(BinderRpc, RepeatBinderNull) {
auto proc = createRpcTestSocketServerProcess(1);
sp<IBinder> outBinder;
@@ -429,7 +482,7 @@
EXPECT_EQ(nullptr, outBinder);
}
-TEST(BinderRpc, HoldBinder) {
+TEST_P(BinderRpc, HoldBinder) {
auto proc = createRpcTestSocketServerProcess(1);
IBinder* ptr = nullptr;
@@ -455,7 +508,7 @@
// These are behavioral differences form regular binder, where certain usecases
// aren't supported.
-TEST(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) {
+TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) {
auto proc1 = createRpcTestSocketServerProcess(1);
auto proc2 = createRpcTestSocketServerProcess(1);
@@ -464,7 +517,7 @@
proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError());
}
-TEST(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
+TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) {
auto proc = createRpcTestSocketServerProcess(1);
sp<IBinder> someRealBinder = IInterface::asBinder(defaultServiceManager());
@@ -473,7 +526,7 @@
proc.rootIface->repeatBinder(someRealBinder, &outBinder).transactionError());
}
-TEST(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
+TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) {
auto proc = createRpcTestSocketServerProcess(1);
// for historical reasons, IServiceManager interface only returns the
@@ -484,7 +537,7 @@
// END TESTS FOR LIMITATIONS OF SOCKET BINDER
-TEST(BinderRpc, RepeatRootObject) {
+TEST_P(BinderRpc, RepeatRootObject) {
auto proc = createRpcTestSocketServerProcess(1);
sp<IBinder> outBinder;
@@ -492,7 +545,7 @@
EXPECT_EQ(proc.rootBinder, outBinder);
}
-TEST(BinderRpc, NestedTransactions) {
+TEST_P(BinderRpc, NestedTransactions) {
auto proc = createRpcTestSocketServerProcess(1);
auto nastyNester = sp<MyBinderRpcTest>::make();
@@ -503,7 +556,7 @@
EXPECT_EQ(nullptr, weak.promote());
}
-TEST(BinderRpc, SameBinderEquality) {
+TEST_P(BinderRpc, SameBinderEquality) {
auto proc = createRpcTestSocketServerProcess(1);
sp<IBinder> a;
@@ -515,7 +568,7 @@
EXPECT_EQ(a, b);
}
-TEST(BinderRpc, SameBinderEqualityWeak) {
+TEST_P(BinderRpc, SameBinderEqualityWeak) {
auto proc = createRpcTestSocketServerProcess(1);
sp<IBinder> a;
@@ -547,7 +600,7 @@
EXPECT_EQ(expected, session); \
} while (false)
-TEST(BinderRpc, SingleSession) {
+TEST_P(BinderRpc, SingleSession) {
auto proc = createRpcTestSocketServerProcess(1);
sp<IBinderRpcSession> session;
@@ -561,7 +614,7 @@
expectSessions(0, proc.rootIface);
}
-TEST(BinderRpc, ManySessions) {
+TEST_P(BinderRpc, ManySessions) {
auto proc = createRpcTestSocketServerProcess(1);
std::vector<sp<IBinderRpcSession>> sessions;
@@ -595,7 +648,7 @@
return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
}
-TEST(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
+TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) {
constexpr size_t kNumThreads = 10;
auto proc = createRpcTestSocketServerProcess(kNumThreads);
@@ -627,7 +680,7 @@
for (auto& t : ts) t.join();
}
-TEST(BinderRpc, ThreadPoolOverSaturated) {
+TEST_P(BinderRpc, ThreadPoolOverSaturated) {
constexpr size_t kNumThreads = 10;
constexpr size_t kNumCalls = kNumThreads + 3;
constexpr size_t kSleepMs = 500;
@@ -651,7 +704,7 @@
EXPECT_LE(epochMsAfter, epochMsBefore + 3 * kSleepMs);
}
-TEST(BinderRpc, ThreadingStressTest) {
+TEST_P(BinderRpc, ThreadingStressTest) {
constexpr size_t kNumClientThreads = 10;
constexpr size_t kNumServerThreads = 10;
constexpr size_t kNumCalls = 100;
@@ -672,7 +725,7 @@
for (auto& t : threads) t.join();
}
-TEST(BinderRpc, OnewayCallDoesNotWait) {
+TEST_P(BinderRpc, OnewayCallDoesNotWait) {
constexpr size_t kReallyLongTimeMs = 100;
constexpr size_t kSleepMs = kReallyLongTimeMs * 5;
@@ -687,7 +740,7 @@
EXPECT_LT(epochMsAfter, epochMsBefore + kReallyLongTimeMs);
}
-TEST(BinderRpc, OnewayCallQueueing) {
+TEST_P(BinderRpc, OnewayCallQueueing) {
constexpr size_t kNumSleeps = 10;
constexpr size_t kNumExtraServerThreads = 4;
constexpr size_t kSleepMs = 50;
@@ -711,7 +764,7 @@
EXPECT_GT(epochMsAfter, epochMsBefore + kSleepMs * kNumSleeps);
}
-TEST(BinderRpc, Die) {
+TEST_P(BinderRpc, Die) {
// TODO(b/183141167): handle this in library
signal(SIGPIPE, SIG_IGN);
@@ -743,7 +796,7 @@
return ret;
}
-TEST(BinderRpc, Fds) {
+TEST_P(BinderRpc, Fds) {
ssize_t beforeFds = countFds();
ASSERT_GE(beforeFds, 0);
{
@@ -753,10 +806,19 @@
ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?");
}
-extern "C" int main(int argc, char** argv) {
+INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc,
+ ::testing::Values(SocketType::UNIX
+#ifdef __BIONIC__
+ ,
+ SocketType::VSOCK
+#endif // __BIONIC__
+ ),
+ PrintSocketType);
+
+} // namespace android
+
+int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
return RUN_ALL_TESTS();
}
-
-} // namespace android
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 1976b49..bcdd06b 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -555,11 +555,13 @@
}).detach();
}
- status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) override {
- if (!ValidateFrameRate(frameRate, compatibility, "BBQSurface::setFrameRate")) {
+ status_t setFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) override {
+ if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+ "BBQSurface::setFrameRate")) {
return BAD_VALUE;
}
- return mBbq->setFrameRate(frameRate, compatibility, shouldBeSeamless);
+ return mBbq->setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
}
status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 97f8f47..c1e3385 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -1033,39 +1033,15 @@
}
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility, bool shouldBeSeamless) override {
+ int8_t compatibility, int8_t changeFrameRateStrategy) override {
Parcel data, reply;
- status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed writing interface token: %s (%d)", strerror(-err), -err);
- return err;
- }
+ SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+ SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(surface));
+ SAFE_PARCEL(data.writeFloat, frameRate);
+ SAFE_PARCEL(data.writeByte, compatibility);
+ SAFE_PARCEL(data.writeByte, changeFrameRateStrategy);
- err = data.writeStrongBinder(IInterface::asBinder(surface));
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed writing strong binder: %s (%d)", strerror(-err), -err);
- return err;
- }
-
- err = data.writeFloat(frameRate);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed writing float: %s (%d)", strerror(-err), -err);
- return err;
- }
-
- err = data.writeByte(compatibility);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed writing byte: %s (%d)", strerror(-err), -err);
- return err;
- }
-
- err = data.writeBool(shouldBeSeamless);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed writing bool: %s (%d)", strerror(-err), -err);
- return err;
- }
-
- err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
+ status_t err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
if (err != NO_ERROR) {
ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err);
return err;
@@ -1894,36 +1870,24 @@
case SET_FRAME_RATE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> binder;
- status_t err = data.readStrongBinder(&binder);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed to read strong binder: %s (%d)", strerror(-err), -err);
- return err;
- }
+ SAFE_PARCEL(data.readStrongBinder, &binder);
+
sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(binder);
if (!surface) {
- ALOGE("setFrameRate: failed to cast to IGraphicBufferProducer: %s (%d)",
- strerror(-err), -err);
- return err;
+ ALOGE("setFrameRate: failed to cast to IGraphicBufferProducer");
+ return BAD_VALUE;
}
float frameRate;
- err = data.readFloat(&frameRate);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed to read float: %s (%d)", strerror(-err), -err);
- return err;
- }
+ SAFE_PARCEL(data.readFloat, &frameRate);
+
int8_t compatibility;
- err = data.readByte(&compatibility);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed to read byte: %s (%d)", strerror(-err), -err);
- return err;
- }
- bool shouldBeSeamless;
- err = data.readBool(&shouldBeSeamless);
- if (err != NO_ERROR) {
- ALOGE("setFrameRate: failed to read bool: %s (%d)", strerror(-err), -err);
- return err;
- }
- status_t result = setFrameRate(surface, frameRate, compatibility, shouldBeSeamless);
+ SAFE_PARCEL(data.readByte, &compatibility);
+
+ int8_t changeFrameRateStrategy;
+ SAFE_PARCEL(data.readByte, &changeFrameRateStrategy);
+
+ status_t result =
+ setFrameRate(surface, frameRate, compatibility, changeFrameRateStrategy);
reply->writeInt32(result);
return NO_ERROR;
}
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 467dcda..2a9a97e 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -19,6 +19,7 @@
#include <apex/window.h>
#include <inttypes.h>
+#include <android/native_window.h>
#include <binder/Parcel.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposerClient.h>
@@ -60,7 +61,7 @@
frameRateSelectionPriority(-1),
frameRate(0.0f),
frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
- shouldBeSeamless(true),
+ changeFrameRateStrategy(ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS),
fixedTransformHint(ui::Transform::ROT_INVALID),
frameNumber(0),
autoRefresh(false),
@@ -148,7 +149,7 @@
SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority);
SAFE_PARCEL(output.writeFloat, frameRate);
SAFE_PARCEL(output.writeByte, frameRateCompatibility);
- SAFE_PARCEL(output.writeBool, shouldBeSeamless);
+ SAFE_PARCEL(output.writeByte, changeFrameRateStrategy);
SAFE_PARCEL(output.writeUint32, fixedTransformHint);
SAFE_PARCEL(output.writeUint64, frameNumber);
SAFE_PARCEL(output.writeBool, autoRefresh);
@@ -269,7 +270,7 @@
SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority);
SAFE_PARCEL(input.readFloat, &frameRate);
SAFE_PARCEL(input.readByte, &frameRateCompatibility);
- SAFE_PARCEL(input.readBool, &shouldBeSeamless);
+ SAFE_PARCEL(input.readByte, &changeFrameRateStrategy);
SAFE_PARCEL(input.readUint32, &tmpUint32);
fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
SAFE_PARCEL(input.readUint64, &frameNumber);
@@ -531,7 +532,7 @@
what |= eFrameRateChanged;
frameRate = other.frameRate;
frameRateCompatibility = other.frameRateCompatibility;
- shouldBeSeamless = other.shouldBeSeamless;
+ changeFrameRateStrategy = other.changeFrameRateStrategy;
}
if (other.what & eFixedTransformHintChanged) {
what |= eFixedTransformHintChanged;
@@ -628,8 +629,8 @@
return NO_ERROR;
}
-bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunctionName,
- bool privileged) {
+bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy,
+ const char* inFunctionName, bool privileged) {
const char* functionName = inFunctionName != nullptr ? inFunctionName : "call";
int floatClassification = std::fpclassify(frameRate);
if (frameRate < 0 || floatClassification == FP_INFINITE || floatClassification == FP_NAN) {
@@ -645,6 +646,12 @@
return false;
}
+ if (changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS &&
+ changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS) {
+ ALOGE("%s failed - invalid change frame rate strategy value %d", functionName,
+ changeFrameRateStrategy);
+ }
+
return true;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 6de3e97..2fc9d47 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1729,8 +1729,8 @@
int Surface::dispatchSetFrameRate(va_list args) {
float frameRate = static_cast<float>(va_arg(args, double));
int8_t compatibility = static_cast<int8_t>(va_arg(args, int));
- bool shouldBeSeamless = static_cast<bool>(va_arg(args, int));
- return setFrameRate(frameRate, compatibility, shouldBeSeamless);
+ int8_t changeFrameRateStrategy = static_cast<int8_t>(va_arg(args, int));
+ return setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
}
int Surface::dispatchAddCancelInterceptor(va_list args) {
@@ -2575,16 +2575,18 @@
mSurfaceListener->onBuffersDiscarded(discardedBufs);
}
-status_t Surface::setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless) {
+status_t Surface::setFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy) {
ATRACE_CALL();
ALOGV("Surface::setFrameRate");
- if (!ValidateFrameRate(frameRate, compatibility, "Surface::setFrameRate")) {
+ if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+ "Surface::setFrameRate")) {
return BAD_VALUE;
}
return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility,
- shouldBeSeamless);
+ changeFrameRateStrategy);
}
status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 0b01084..cb8028b 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1583,7 +1583,7 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate(
const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility,
- bool shouldBeSeamless) {
+ int8_t changeFrameRateStrategy) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -1591,7 +1591,8 @@
}
// Allow privileged values as well here, those will be ignored by SF if
// the caller is not privileged
- if (!ValidateFrameRate(frameRate, compatibility, "Transaction::setFrameRate",
+ if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+ "Transaction::setFrameRate",
/*privileged=*/true)) {
mStatus = BAD_VALUE;
return *this;
@@ -1599,7 +1600,7 @@
s->what |= layer_state_t::eFrameRateChanged;
s->frameRate = frameRate;
s->frameRateCompatibility = compatibility;
- s->shouldBeSeamless = shouldBeSeamless;
+ s->changeFrameRateStrategy = changeFrameRateStrategy;
return *this;
}
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 9f9ca74..d933514 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -466,7 +466,7 @@
* Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info.
*/
virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility, bool shouldBeSeamless) = 0;
+ int8_t compatibility, int8_t changeFrameRateStrategy) = 0;
/*
* Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 11f0961..6c265c8 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -208,7 +208,7 @@
// Layer frame rate and compatibility. See ANativeWindow_setFrameRate().
float frameRate;
int8_t frameRateCompatibility;
- bool shouldBeSeamless;
+ int8_t changeFrameRateStrategy;
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
@@ -308,10 +308,11 @@
//
// @param frameRate the frame rate in Hz
// @param compatibility a ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_*
+// @param changeFrameRateStrategy a ANATIVEWINDOW_CHANGE_FRAME_RATE_*
// @param functionName calling function or nullptr. Used for logging
// @param privileged whether caller has unscoped surfaceflinger access
-bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* functionName,
- bool privileged = false);
+bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy,
+ const char* functionName, bool privileged = false);
struct CaptureArgs {
const static int32_t UNSET_UID = -1;
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 5881221..d22bdaa 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -187,7 +187,8 @@
status_t getUniqueId(uint64_t* outId) const;
status_t getConsumerUsage(uint64_t* outUsage) const;
- virtual status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
+ virtual status_t setFrameRate(float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy);
virtual status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
virtual status_t getExtraBufferCount(int* extraBuffers) const;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index c38375c..35f57a2 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -515,7 +515,7 @@
Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius);
Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
- int8_t compatibility, bool shouldBeSeamless);
+ int8_t compatibility, int8_t changeFrameRateStrategy);
// Set by window manager indicating the layer and all its children are
// in a different orientation than the display. The hint suggests that
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 5ac3f19..8876033 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -859,7 +859,7 @@
}
status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/,
- int8_t /*compatibility*/, bool /*shouldBeSeamless*/) override {
+ int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) override {
return NO_ERROR;
}
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index c2a3cf1..f7962e0 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -110,6 +110,12 @@
return true;
case Type::DRAG:
return true;
+ case Type::TIMELINE:
+ const nsecs_t gpuCompletedTime =
+ body.timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+ const nsecs_t presentTime =
+ body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+ return presentTime > gpuCompletedTime;
}
}
return false;
@@ -129,6 +135,8 @@
return sizeof(Header) + body.capture.size();
case Type::DRAG:
return sizeof(Header) + body.drag.size();
+ case Type::TIMELINE:
+ return sizeof(Header) + body.timeline.size();
}
return sizeof(Header);
}
@@ -260,6 +268,11 @@
msg->body.drag.isExiting = body.drag.isExiting;
break;
}
+ case InputMessage::Type::TIMELINE: {
+ msg->body.timeline.eventId = body.timeline.eventId;
+ msg->body.timeline.graphicsTimeline = body.timeline.graphicsTimeline;
+ break;
+ }
}
}
@@ -629,7 +642,7 @@
return mChannel->sendMessage(&msg);
}
-android::base::Result<InputPublisher::Finished> InputPublisher::receiveFinishedSignal() {
+android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() {
if (DEBUG_TRANSPORT_ACTIONS) {
ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__);
}
@@ -639,16 +652,24 @@
if (result) {
return android::base::Error(result);
}
- if (msg.header.type != InputMessage::Type::FINISHED) {
- ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer",
- mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
- return android::base::Error(UNKNOWN_ERROR);
+ if (msg.header.type == InputMessage::Type::FINISHED) {
+ return Finished{
+ .seq = msg.header.seq,
+ .handled = msg.body.finished.handled,
+ .consumeTime = msg.body.finished.consumeTime,
+ };
}
- return Finished{
- .seq = msg.header.seq,
- .handled = msg.body.finished.handled,
- .consumeTime = msg.body.finished.consumeTime,
- };
+
+ if (msg.header.type == InputMessage::Type::TIMELINE) {
+ return Timeline{
+ .inputEventId = msg.body.timeline.eventId,
+ .graphicsTimeline = msg.body.timeline.graphicsTimeline,
+ };
+ }
+
+ ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer",
+ mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str());
+ return android::base::Error(UNKNOWN_ERROR);
}
// --- InputConsumer ---
@@ -785,7 +806,8 @@
break;
}
- case InputMessage::Type::FINISHED: {
+ case InputMessage::Type::FINISHED:
+ case InputMessage::Type::TIMELINE: {
LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by "
"InputConsumer!",
NamedEnum::string(mMsg.header.type).c_str());
@@ -1193,6 +1215,24 @@
return sendUnchainedFinishedSignal(seq, handled);
}
+status_t InputConsumer::sendTimeline(int32_t inputEventId,
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) {
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32
+ ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64,
+ mChannel->getName().c_str(), inputEventId,
+ graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME],
+ graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
+ }
+
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::TIMELINE;
+ msg.header.seq = 0;
+ msg.body.timeline.eventId = inputEventId;
+ msg.body.timeline.graphicsTimeline = std::move(graphicsTimeline);
+ return mChannel->sendMessage(&msg);
+}
+
nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const {
auto it = mConsumeTimes.find(seq);
// Consume time will be missing if either 'finishInputEvent' is called twice, or if it was
@@ -1399,6 +1439,19 @@
toString(msg.body.drag.isExiting));
break;
}
+ case InputMessage::Type::TIMELINE: {
+ const nsecs_t gpuCompletedTime =
+ msg.body.timeline
+ .graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+ const nsecs_t presentTime =
+ msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+ out += android::base::StringPrintf("inputEventId=%" PRId32
+ ", gpuCompletedTime=%" PRId64
+ ", presentTime=%" PRId64,
+ msg.body.timeline.eventId, gpuCompletedTime,
+ presentTime);
+ break;
+ }
}
out += "\n";
}
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index fc31715..088e00b 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -124,13 +124,15 @@
ASSERT_EQ(OK, status)
<< "consumer sendFinishedSignal should return OK";
- Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
- ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, result->seq)
- << "receiveFinishedSignal should have returned the original sequence number";
- ASSERT_TRUE(result->handled)
- << "receiveFinishedSignal should have set handled to consumer's reply";
- ASSERT_GE(result->consumeTime, publishTime)
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_TRUE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -264,13 +266,15 @@
ASSERT_EQ(OK, status)
<< "consumer sendFinishedSignal should return OK";
- Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
- ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, result->seq)
- << "receiveFinishedSignal should have returned the original sequence number";
- ASSERT_FALSE(result->handled)
- << "receiveFinishedSignal should have set handled to consumer's reply";
- ASSERT_GE(result->consumeTime, publishTime)
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_FALSE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -304,14 +308,16 @@
status = mConsumer->sendFinishedSignal(seq, true);
ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
- Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
- ASSERT_TRUE(result.ok()) << "receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, result->seq)
- << "receiveFinishedSignal should have returned the original sequence number";
- ASSERT_TRUE(result->handled)
- << "receiveFinishedSignal should have set handled to consumer's reply";
- ASSERT_GE(result->consumeTime, publishTime)
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_TRUE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -343,13 +349,15 @@
status = mConsumer->sendFinishedSignal(seq, true);
ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
- android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
- ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, result->seq)
- << "receiveFinishedSignal should have returned the original sequence number";
- ASSERT_TRUE(result->handled)
- << "receiveFinishedSignal should have set handled to consumer's reply";
- ASSERT_GE(result->consumeTime, publishTime)
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_TRUE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
@@ -385,16 +393,34 @@
status = mConsumer->sendFinishedSignal(seq, true);
ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
- android::base::Result<InputPublisher::Finished> result = mPublisher->receiveFinishedSignal();
- ASSERT_TRUE(result.ok()) << "publisher receiveFinishedSignal should return OK";
- ASSERT_EQ(seq, result->seq)
- << "publisher receiveFinishedSignal should have returned the original sequence number";
- ASSERT_TRUE(result->handled)
- << "publisher receiveFinishedSignal should have set handled to consumer's reply";
- ASSERT_GE(result->consumeTime, publishTime)
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_TRUE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
<< "finished signal's consume time should be greater than publish time";
}
+TEST_F(InputPublisherAndConsumerTest, SendTimeline) {
+ const int32_t inputEventId = 20;
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+ graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 30;
+ graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 40;
+ status_t status = mConsumer->sendTimeline(inputEventId, graphicsTimeline);
+ ASSERT_EQ(OK, status);
+
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Timeline>(*result));
+ const InputPublisher::Timeline& timeline = std::get<InputPublisher::Timeline>(*result);
+ ASSERT_EQ(inputEventId, timeline.inputEventId);
+ ASSERT_EQ(graphicsTimeline, timeline.graphicsTimeline);
+}
+
TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
}
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 3d80b38..585779e 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -96,6 +96,10 @@
CHECK_OFFSET(InputMessage::Body::Finished, handled, 0);
CHECK_OFFSET(InputMessage::Body::Finished, empty, 1);
CHECK_OFFSET(InputMessage::Body::Finished, consumeTime, 8);
+
+ CHECK_OFFSET(InputMessage::Body::Timeline, eventId, 0);
+ CHECK_OFFSET(InputMessage::Body::Timeline, empty, 4);
+ CHECK_OFFSET(InputMessage::Body::Timeline, graphicsTimeline, 8);
}
void TestHeaderSize() {
@@ -117,6 +121,9 @@
static_assert(sizeof(InputMessage::Body::Focus) == 8);
static_assert(sizeof(InputMessage::Body::Capture) == 8);
static_assert(sizeof(InputMessage::Body::Drag) == 16);
+ // Timeline
+ static_assert(GraphicsTimeline::SIZE == 2);
+ static_assert(sizeof(InputMessage::Body::Timeline) == 24);
}
// --- VerifiedInputEvent ---
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index b406a9c..ada689a 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -159,8 +159,8 @@
}
int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) {
- return ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility,
- /*shouldBeSeamless*/ true);
+ return ANativeWindow_setFrameRateWithChangeStrategy(window, frameRate, compatibility,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
}
void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) {
@@ -170,12 +170,12 @@
window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
}
-int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate,
- int8_t compatibility, bool shouldBeSeamless) {
+int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, float frameRate,
+ int8_t compatibility, int8_t changeFrameRateStrategy) {
if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
return -EINVAL;
}
- return native_window_set_frame_rate(window, frameRate, compatibility, shouldBeSeamless);
+ return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy);
}
/**************************************************************************************************
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 285f2fb..50e9d53 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -247,9 +247,10 @@
};
/**
- * Same as ANativeWindow_setFrameRateWithSeamlessness(window, frameRate, compatibility, true).
+ * Same as ANativeWindow_setFrameRateWithChangeStrategy(window, frameRate, compatibility,
+ * ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS).
*
- * See ANativeWindow_setFrameRateWithSeamlessness().
+ * See ANativeWindow_setFrameRateWithChangeStrategy().
*
* Available since API level 30.
*/
@@ -267,6 +268,19 @@
*/
void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) __INTRODUCED_IN(30);
+/** Change frame rate strategy value for ANativeWindow_setFrameRate. */
+enum ANativeWindow_ChangeFrameRateStrategy {
+ /**
+ * Change the frame rate only if the transition is going to be seamless.
+ */
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS = 0,
+ /**
+ * Change the frame rate even if the transition is going to be non-seamless,
+ * i.e. with visual interruptions for the user.
+ */
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS = 1
+} __INTRODUCED_IN(31);
+
/**
* Sets the intended frame rate for this window.
*
@@ -296,17 +310,16 @@
* compatibility value may influence the system's choice of display refresh
* rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info.
*
- * \param shouldBeSeamless Whether display refresh rate transitions should be seamless. A
- * seamless transition is one that doesn't have any visual interruptions, such as a black
- * screen for a second or two. True indicates that any frame rate changes caused by this
- * request should be seamless. False indicates that non-seamless refresh rates are also
- * acceptable.
+ * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless.
+ * A seamless transition is one that doesn't have any visual interruptions, such as a black
+ * screen for a second or two. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values.
*
* \return 0 for success, -EINVAL if the window, frame rate, or compatibility
* value are invalid.
*/
-int32_t ANativeWindow_setFrameRateWithSeamlessness(ANativeWindow* window, float frameRate,
- int8_t compatibility, bool shouldBeSeamless) __INTRODUCED_IN(31);
+int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, float frameRate,
+ int8_t compatibility, int8_t changeFrameRateStrategy)
+ __INTRODUCED_IN(31);
#ifdef __cplusplus
};
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 7aa2cf4..cc82bb4 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1019,9 +1019,9 @@
}
static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate,
- int8_t compatibility, bool shouldBeSeamless) {
+ int8_t compatibility, int8_t changeFrameRateStrategy) {
return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate,
- (int)compatibility, (int)shouldBeSeamless);
+ (int)compatibility, (int)changeFrameRateStrategy);
}
static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window,
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 24d0e3b..988132c 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -47,7 +47,7 @@
ANativeWindow_setBuffersTransform;
ANativeWindow_setDequeueTimeout; # apex # introduced=30
ANativeWindow_setFrameRate; # introduced=30
- ANativeWindow_setFrameRateWithSeamlessness; # introduced=31
+ ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31
ANativeWindow_setSharedBufferMode; # llndk
ANativeWindow_setSwapInterval; # llndk
ANativeWindow_setUsage; # llndk
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 6977396..073455a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3312,15 +3312,21 @@
bool gotOne = false;
status_t status = OK;
for (;;) {
- Result<InputPublisher::Finished> result =
- connection->inputPublisher.receiveFinishedSignal();
+ Result<InputPublisher::ConsumerResponse> result =
+ connection->inputPublisher.receiveConsumerResponse();
if (!result.ok()) {
status = result.error().code();
break;
}
- const InputPublisher::Finished& finished = *result;
- d->finishDispatchCycleLocked(currentTime, connection, finished.seq,
- finished.handled, finished.consumeTime);
+
+ if (std::holds_alternative<InputPublisher::Finished>(*result)) {
+ const InputPublisher::Finished& finish =
+ std::get<InputPublisher::Finished>(*result);
+ d->finishDispatchCycleLocked(currentTime, connection, finish.seq,
+ finish.handled, finish.consumeTime);
+ } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) {
+ // TODO(b/167947340): Report this data to LatencyTracker
+ }
gotOne = true;
}
if (gotOne) {
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 76f6e74..b62fce4 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -742,6 +742,11 @@
ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
}
+ void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
+ const status_t status = mConsumer->sendTimeline(inputEventId, timeline);
+ ASSERT_EQ(OK, status);
+ }
+
void consumeEvent(int32_t expectedEventType, int32_t expectedAction,
std::optional<int32_t> expectedDisplayId,
std::optional<int32_t> expectedFlags) {
@@ -1054,6 +1059,11 @@
mInputReceiver->finishEvent(sequenceNum);
}
+ void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
+ ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver";
+ mInputReceiver->sendTimeline(inputEventId, timeline);
+ }
+
InputEvent* consume() {
if (mInputReceiver == nullptr) {
return nullptr;
@@ -1970,6 +1980,21 @@
secondWindow->assertNoEvents();
}
+TEST_F(InputDispatcherTest, SendTimeline_DoesNotCrashDispatcher) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+ graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2;
+ graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3;
+
+ window->sendTimeline(1 /*inputEventId*/, graphicsTimeline);
+ window->assertNoEvents();
+ mDispatcher->waitForIdle();
+}
+
class FakeMonitorReceiver {
public:
FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 8f1aef0..be9bce0 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -329,6 +329,37 @@
mRefreshPending = false;
return hasReadyFrame();
}
+namespace {
+TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
+ using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
+ using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
+ const auto frameRateCompatibility = [frameRate] {
+ switch (frameRate.type) {
+ case Layer::FrameRateCompatibility::Default:
+ return FrameRateCompatibility::Default;
+ case Layer::FrameRateCompatibility::ExactOrMultiple:
+ return FrameRateCompatibility::ExactOrMultiple;
+ default:
+ return FrameRateCompatibility::Undefined;
+ }
+ }();
+
+ const auto seamlessness = [frameRate] {
+ switch (frameRate.seamlessness) {
+ case scheduler::Seamlessness::OnlySeamless:
+ return Seamlessness::ShouldBeSeamless;
+ case scheduler::Seamlessness::SeamedAndSeamless:
+ return Seamlessness::NotRequired;
+ default:
+ return Seamlessness::Undefined;
+ }
+ }();
+
+ return TimeStats::SetFrameRateVote{.frameRate = frameRate.rate.getValue(),
+ .frameRateCompatibility = frameRateCompatibility,
+ .seamlessness = seamlessness};
+}
+} // namespace
bool BufferLayer::onPostComposition(const DisplayDevice* display,
const std::shared_ptr<FenceTime>& glDoneFence,
@@ -381,7 +412,9 @@
const std::optional<Fps> renderRate = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
if (presentFence->isValid()) {
mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
- refreshRate, renderRate);
+ refreshRate, renderRate,
+ frameRateToSetFrameRateVotePayload(
+ mDrawingState.frameRate));
mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
presentFence, FrameTracer::FrameEvent::PRESENT_FENCE);
mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
@@ -393,7 +426,9 @@
// timestamp instead.
const nsecs_t actualPresentTime = display->getRefreshTimestamp();
mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
- refreshRate, renderRate);
+ refreshRate, renderRate,
+ frameRateToSetFrameRateVotePayload(
+ mDrawingState.frameRate));
mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
actualPresentTime,
FrameTracer::FrameEvent::PRESENT_FENCE);
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 2ae49fa..44b33ef 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -48,7 +48,7 @@
auto bufItr = processBuffers.find(id);
if (bufItr == processBuffers.end()) {
- ALOGE("failed to get buffer, invalid buffer id");
+ ALOGV("failed to get buffer, invalid buffer id");
return false;
}
@@ -150,7 +150,7 @@
ClientCacheBuffer* buf = nullptr;
if (!getBuffer(cacheId, &buf)) {
- ALOGE("failed to register erased recipient, could not retrieve buffer");
+ ALOGV("failed to register erased recipient, could not retrieve buffer");
return false;
}
buf->recipients.insert(recipient);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index a56f28a..cfb4962 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -16,6 +16,8 @@
#pragma once
+#include <compositionengine/Output.h>
+#include <compositionengine/ProjectionSpace.h>
#include <compositionengine/impl/planner/LayerState.h>
#include <renderengine/RenderEngine.h>
@@ -92,8 +94,8 @@
}
void incrementAge() { ++mAge; }
- // Renders the cached set with the supplied output dataspace.
- void render(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
+ // Renders the cached set with the supplied output composition state.
+ void render(renderengine::RenderEngine& re, const OutputCompositionState& outputState);
void dump(std::string& result) const;
@@ -133,6 +135,7 @@
Texture mTexture;
sp<Fence> mDrawFence;
ui::Dataspace mOutputDataspace;
+ ui::Transform::RotationFlags mOrientation = ui::Transform::ROT_0;
static const bool sDebugHighlighLayers;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 2bd3249..e21b5bc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -42,8 +42,9 @@
NonBufferHash flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash,
std::chrono::steady_clock::time_point now);
- // Renders the newest cached sets with the supplied output dataspace
- void renderCachedSets(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
+ // Renders the newest cached sets with the supplied output composition state
+ void renderCachedSets(renderengine::RenderEngine& re,
+ const OutputCompositionState& outputState);
void dump(std::string& result) const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index 89de34d..e6d2b63 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -59,7 +59,8 @@
compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers);
// The planner will call to the Flattener to render any pending cached set
- void renderCachedSets(renderengine::RenderEngine&, ui::Dataspace outputDataspace);
+ void renderCachedSets(renderengine::RenderEngine& re,
+ const OutputCompositionState& outputState);
void dump(const Vector<String16>& args, std::string&);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index ded2dcc..aed3be9 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1252,7 +1252,7 @@
void Output::renderCachedSets() {
if (mPlanner) {
- mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState().dataspace);
+ mPlanner->renderCachedSets(getCompositionEngine().getRenderEngine(), getState());
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index b364649..e662491 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -354,6 +354,7 @@
Rect displayFrame = outputDependentState.displayFrame;
FloatRect sourceCrop = outputDependentState.sourceCrop;
+
if (outputDependentState.overrideInfo.buffer != nullptr) { // adyabr
displayFrame = outputDependentState.overrideInfo.displayFrame;
sourceCrop = displayFrame.toFloatRect();
@@ -431,11 +432,13 @@
outputDependentState.outputSpaceVisibleRegion.dump(LOG_TAG);
}
- if (auto error = hwcLayer->setDataspace(outputDependentState.dataspace);
- error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(),
- outputDependentState.dataspace, to_string(error).c_str(),
- static_cast<int32_t>(error));
+ const auto dataspace = outputDependentState.overrideInfo.buffer
+ ? outputDependentState.overrideInfo.dataspace
+ : outputDependentState.dataspace;
+
+ if (auto error = hwcLayer->setDataspace(dataspace); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(), dataspace,
+ to_string(error).c_str(), static_cast<int32_t>(error));
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 4d3036a..e8c6de0 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -19,6 +19,7 @@
// #define LOG_NDEBUG 0
#include <android-base/properties.h>
+#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/planner/CachedSet.h>
#include <math/HashCombine.h>
#include <renderengine/DisplaySettings.h>
@@ -84,6 +85,7 @@
size_t hash = 0;
android::hashCombineSingle(hash, mBounds);
android::hashCombineSingle(hash, mOutputDataspace);
+ android::hashCombineSingle(hash, mOrientation);
return hash;
}
@@ -148,10 +150,15 @@
}
}
-void CachedSet::render(renderengine::RenderEngine& renderEngine, ui::Dataspace outputDataspace) {
+void CachedSet::render(renderengine::RenderEngine& renderEngine,
+ const OutputCompositionState& outputState) {
+ const ui::Dataspace& outputDataspace = outputState.dataspace;
+ const ui::Transform::RotationFlags orientation =
+ ui::Transform::toRotationFlags(outputState.displaySpace.orientation);
renderengine::DisplaySettings displaySettings{
.physicalDisplay = Rect(0, 0, mBounds.getWidth(), mBounds.getHeight()),
.clip = mBounds,
+ .orientation = orientation,
.outputDataspace = outputDataspace,
};
@@ -217,6 +224,7 @@
mTexture.setBuffer(buffer, &renderEngine);
mDrawFence = new Fence(drawFence.release());
mOutputDataspace = outputDataspace;
+ mOrientation = orientation;
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 60ebbb2..06f7e7a 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -52,12 +52,12 @@
}
void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine,
- ui::Dataspace outputDataspace) {
+ const OutputCompositionState& outputState) {
if (!mNewCachedSet) {
return;
}
- mNewCachedSet->render(renderEngine, outputDataspace);
+ mNewCachedSet->render(renderEngine, outputState);
}
void Flattener::dump(std::string& result) const {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index 87721c7..ad75557 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -134,8 +134,8 @@
}
void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine,
- ui::Dataspace outputDataspace) {
- mFlattener.renderCachedSets(renderEngine, outputDataspace);
+ const OutputCompositionState& outputState) {
+ mFlattener.renderCachedSets(renderEngine, outputState);
}
void Planner::dump(const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 9dd199d..83fb9e3 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -694,6 +694,7 @@
static_cast<Hwc2::IComposerClient::BlendMode>(41);
static constexpr float kAlpha = 51.f;
static constexpr ui::Dataspace kDataspace = static_cast<ui::Dataspace>(71);
+ static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72);
static constexpr int kSupportedPerFrameMetadata = 101;
static constexpr int kExpectedHwcSlot = 0;
static constexpr bool kLayerGenericMetadata1Mandatory = true;
@@ -701,13 +702,16 @@
static const half4 kColor;
static const Rect kDisplayFrame;
+ static const Rect kOverrideDisplayFrame;
static const Region kOutputSpaceVisibleRegion;
static const mat4 kColorTransform;
static const Region kSurfaceDamage;
static const HdrMetadata kHdrMetadata;
static native_handle_t* kSidebandStreamHandle;
static const sp<GraphicBuffer> kBuffer;
+ static const sp<GraphicBuffer> kOverrideBuffer;
static const sp<Fence> kFence;
+ static const sp<Fence> kOverrideFence;
static const std::string kLayerGenericMetadata1Key;
static const std::vector<uint8_t> kLayerGenericMetadata1Value;
static const std::string kLayerGenericMetadata2Key;
@@ -751,9 +755,19 @@
kLayerGenericMetadata2Value};
}
- void expectGeometryCommonCalls() {
- EXPECT_CALL(*mHwcLayer, setDisplayFrame(kDisplayFrame)).WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setSourceCrop(kSourceCrop)).WillOnce(Return(kError));
+ void includeOverrideInfo() {
+ auto& overrideInfo = mOutputLayer.editState().overrideInfo;
+
+ overrideInfo.buffer = kOverrideBuffer;
+ overrideInfo.acquireFence = kOverrideFence;
+ overrideInfo.displayFrame = kOverrideDisplayFrame;
+ overrideInfo.dataspace = kOverrideDataspace;
+ }
+
+ void expectGeometryCommonCalls(Rect displayFrame = kDisplayFrame,
+ FloatRect sourceCrop = kSourceCrop) {
+ EXPECT_CALL(*mHwcLayer, setDisplayFrame(displayFrame)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setSourceCrop(sourceCrop)).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setZOrder(kZOrder)).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setTransform(kBufferTransform)).WillOnce(Return(kError));
@@ -761,10 +775,11 @@
EXPECT_CALL(*mHwcLayer, setPlaneAlpha(kAlpha)).WillOnce(Return(kError));
}
- void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None) {
+ void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None,
+ ui::Dataspace dataspace = kDataspace) {
EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(kOutputSpaceVisibleRegion)))
.WillOnce(Return(kError));
- EXPECT_CALL(*mHwcLayer, setDataspace(kDataspace)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setDataspace(dataspace)).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setColorTransform(kColorTransform))
.WillOnce(Return(unsupported == SimulateUnsupported::ColorTransform
? hal::Error::UNSUPPORTED
@@ -793,9 +808,10 @@
EXPECT_CALL(*mHwcLayer, setSidebandStream(kSidebandStreamHandle));
}
- void expectSetHdrMetadataAndBufferCalls() {
+ void expectSetHdrMetadataAndBufferCalls(sp<GraphicBuffer> buffer = kBuffer,
+ sp<Fence> fence = kFence) {
EXPECT_CALL(*mHwcLayer, setPerFrameMetadata(kSupportedPerFrameMetadata, kHdrMetadata));
- EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, kBuffer, kFence));
+ EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, buffer, fence));
}
void expectGenericLayerMetadataCalls() {
@@ -817,6 +833,7 @@
const half4 OutputLayerWriteStateToHWCTest::kColor{81.f / 255.f, 82.f / 255.f, 83.f / 255.f,
84.f / 255.f};
const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044};
+const Rect OutputLayerWriteStateToHWCTest::kOverrideDisplayFrame{1002, 1003, 1004, 20044};
const Region OutputLayerWriteStateToHWCTest::kOutputSpaceVisibleRegion{
Rect{1005, 1006, 1007, 1008}};
const mat4 OutputLayerWriteStateToHWCTest::kColorTransform{
@@ -828,7 +845,9 @@
native_handle_t* OutputLayerWriteStateToHWCTest::kSidebandStreamHandle =
reinterpret_cast<native_handle_t*>(1031);
const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer;
+const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kOverrideBuffer = new GraphicBuffer();
const sp<Fence> OutputLayerWriteStateToHWCTest::kFence;
+const sp<Fence> OutputLayerWriteStateToHWCTest::kOverrideFence = new Fence();
const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Key =
"com.example.metadata.1";
const std::vector<uint8_t> OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Value{{1, 2, 3}};
@@ -983,6 +1002,18 @@
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false);
}
+TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) {
+ mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ includeOverrideInfo();
+
+ expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideDisplayFrame.toFloatRect());
+ expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace);
+ expectSetHdrMetadataAndBufferCalls(kOverrideBuffer, kOverrideFence);
+ expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false);
+}
+
/*
* OutputLayer::writeCursorPositionToHWC()
*/
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 7842efb..0e7ef71 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/planner/CachedSet.h>
#include <compositionengine/impl/planner/LayerState.h>
#include <compositionengine/mock/LayerFE.h>
@@ -59,6 +60,7 @@
static constexpr size_t kNumLayers = 5;
std::vector<std::unique_ptr<TestLayer>> mTestLayers;
+ impl::OutputCompositionState mOutputState;
android::renderengine::mock::RenderEngine mRenderEngine;
};
@@ -87,6 +89,10 @@
std::make_unique<CachedSet::Layer>(testLayer->layerState.get(), kStartTime);
mTestLayers.emplace_back(std::move(testLayer));
+
+ // set up minimium params needed for rendering
+ mOutputState.dataspace = ui::Dataspace::SRGB;
+ mOutputState.displaySpace.orientation = ui::ROTATION_0;
}
}
@@ -300,7 +306,7 @@
EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2));
EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers));
EXPECT_CALL(mRenderEngine, cacheExternalTextureBuffer(_));
- cachedSet.render(mRenderEngine, ui::Dataspace::SRGB);
+ cachedSet.render(mRenderEngine, mOutputState);
expectReadyBuffer(cachedSet);
// Now check that appending a new cached set properly cleans up RenderEngine resources.
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index bd77559..211b4dc 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <compositionengine/impl/OutputCompositionState.h>
#include <compositionengine/impl/planner/CachedSet.h>
#include <compositionengine/impl/planner/Flattener.h>
#include <compositionengine/impl/planner/LayerState.h>
@@ -76,6 +77,7 @@
static constexpr size_t kNumLayers = 5;
std::vector<std::unique_ptr<TestLayer>> mTestLayers;
+ impl::OutputCompositionState mOutputState;
};
void FlattenerTest::SetUp() {
@@ -120,6 +122,10 @@
testLayer->layerState->incrementFramesSinceBufferUpdate();
mTestLayers.emplace_back(std::move(testLayer));
+
+ // set up minimium params needed for rendering
+ mOutputState.dataspace = ui::Dataspace::SRGB;
+ mOutputState.displaySpace.orientation = ui::ROTATION_0;
}
}
@@ -134,13 +140,13 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
// same geometry, update the internal layer stack
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
}
void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) {
@@ -150,7 +156,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
for (const auto layer : layers) {
EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer);
@@ -160,7 +166,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer;
EXPECT_NE(nullptr, buffer);
@@ -195,7 +201,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
}
TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
@@ -241,7 +247,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -276,7 +282,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -313,7 +319,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -322,7 +328,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_NE(nullptr, overrideBuffer2);
@@ -335,7 +341,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_NE(nullptr, overrideBuffer2);
@@ -344,7 +350,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -386,7 +392,7 @@
initializeOverrideBuffer(layers);
EXPECT_EQ(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_EQ(nullptr, overrideBuffer1);
EXPECT_EQ(nullptr, overrideBuffer2);
@@ -399,7 +405,8 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mOutputState.displaySpace.orientation = ui::ROTATION_90;
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -411,7 +418,8 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mOutputState.displaySpace.orientation = ui::ROTATION_180;
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -426,7 +434,7 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
@@ -437,7 +445,8 @@
initializeOverrideBuffer(layers);
EXPECT_NE(getNonBufferHash(layers),
mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
- mFlattener->renderCachedSets(mRenderEngine, ui::Dataspace::SRGB);
+ mOutputState.displaySpace.orientation = ui::ROTATION_270;
+ mFlattener->renderCachedSets(mRenderEngine, mOutputState);
EXPECT_NE(nullptr, overrideBuffer1);
EXPECT_EQ(overrideBuffer1, overrideBuffer2);
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 2784861..6485265 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -575,13 +575,9 @@
}
} else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) {
// Finish late, Present late
- if (displayFrameJankType == JankType::None) {
- // Display frame is not janky, so purely app's fault
- mJankType |= JankType::AppDeadlineMissed;
- } else {
- // Propagate DisplayFrame's jankType if it is janky
- mJankType |= displayFrameJankType;
- }
+ mJankType |= JankType::AppDeadlineMissed;
+ // Propagate DisplayFrame's jankType if it is janky
+ mJankType |= displayFrameJankType;
}
}
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index cd3e8ad..b5410fe 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2867,6 +2867,18 @@
}
}
+scheduler::Seamlessness Layer::FrameRate::convertChangeFrameRateStrategy(int8_t strategy) {
+ switch (strategy) {
+ case ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS:
+ return Seamlessness::OnlySeamless;
+ case ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS:
+ return Seamlessness::SeamedAndSeamless;
+ default:
+ LOG_ALWAYS_FATAL("Invalid change frame sate strategy value %d", strategy);
+ return Seamlessness::Default;
+ }
+}
+
bool Layer::getPrimaryDisplayOnly() const {
const State& s(mDrawingState);
if (s.flags & layer_state_t::eLayerSkipScreenshot) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 1c5d6ec..421a107 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -168,8 +168,9 @@
: rate(0),
type(FrameRateCompatibility::Default),
seamlessness(Seamlessness::Default) {}
- FrameRate(Fps rate, FrameRateCompatibility type, bool shouldBeSeamless = true)
- : rate(rate), type(type), seamlessness(getSeamlessness(rate, shouldBeSeamless)) {}
+ FrameRate(Fps rate, FrameRateCompatibility type,
+ Seamlessness seamlessness = Seamlessness::OnlySeamless)
+ : rate(rate), type(type), seamlessness(getSeamlessness(rate, seamlessness)) {}
bool operator==(const FrameRate& other) const {
return rate.equalsWithMargin(other.rate) && type == other.type &&
@@ -181,18 +182,16 @@
// Convert an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value to a
// Layer::FrameRateCompatibility. Logs fatal if the compatibility value is invalid.
static FrameRateCompatibility convertCompatibility(int8_t compatibility);
+ static scheduler::Seamlessness convertChangeFrameRateStrategy(int8_t strategy);
private:
- static Seamlessness getSeamlessness(Fps rate, bool shouldBeSeamless) {
+ static Seamlessness getSeamlessness(Fps rate, Seamlessness seamlessness) {
if (!rate.isValid()) {
// Refresh rate of 0 is a special value which should reset the vote to
// its default value.
return Seamlessness::Default;
- } else if (shouldBeSeamless) {
- return Seamlessness::OnlySeamless;
- } else {
- return Seamlessness::SeamedAndSeamless;
}
+ return seamlessness;
}
};
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index a5cf797..b062acd 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -215,7 +215,7 @@
int explicitExactOrMultipleVoteLayers = 0;
int explicitExact = 0;
float maxExplicitWeight = 0;
- int seamedLayers = 0;
+ int seamedFocusedLayers = 0;
for (const auto& layer : layers) {
switch (layer.vote) {
case LayerVoteType::NoVote:
@@ -243,8 +243,8 @@
break;
}
- if (layer.seamlessness == Seamlessness::SeamedAndSeamless) {
- seamedLayers++;
+ if (layer.seamlessness == Seamlessness::SeamedAndSeamless && layer.focused) {
+ seamedFocusedLayers++;
}
}
@@ -329,12 +329,11 @@
// mode group otherwise. In second case, if the current mode group is different
// from the default, this means a layer with seamlessness=SeamedAndSeamless has just
// disappeared.
- const bool isInPolicyForDefault = seamedLayers > 0
+ const bool isInPolicyForDefault = seamedFocusedLayers > 0
? scores[i].refreshRate->getModeGroup() == mCurrentRefreshRate->getModeGroup()
: scores[i].refreshRate->getModeGroup() == defaultMode->getModeGroup();
- if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault &&
- !layer.focused) {
+ if (layer.seamlessness == Seamlessness::Default && !isInPolicyForDefault) {
ALOGV("%s ignores %s. Current mode = %s", formatLayerInfo(layer, weight).c_str(),
scores[i].refreshRate->toString().c_str(),
mCurrentRefreshRate->toString().c_str());
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f7a2669..f7c9291 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3984,13 +3984,16 @@
}
}
if (what & layer_state_t::eFrameRateChanged) {
- if (ValidateFrameRate(s.frameRate, s.frameRateCompatibility,
- "SurfaceFlinger::setClientStateLocked", privileged) &&
- layer->setFrameRate(Layer::FrameRate(Fps(s.frameRate),
- Layer::FrameRate::convertCompatibility(
- s.frameRateCompatibility),
- s.shouldBeSeamless))) {
- flags |= eTraversalNeeded;
+ if (ValidateFrameRate(s.frameRate, s.frameRateCompatibility, s.changeFrameRateStrategy,
+ "SurfaceFlinger::setClientStateLocked", privileged)) {
+ const auto compatibility =
+ Layer::FrameRate::convertCompatibility(s.frameRateCompatibility);
+ const auto strategy =
+ Layer::FrameRate::convertChangeFrameRateStrategy(s.changeFrameRateStrategy);
+
+ if (layer->setFrameRate(Layer::FrameRate(Fps(s.frameRate), compatibility, strategy))) {
+ flags |= eTraversalNeeded;
+ }
}
}
if (what & layer_state_t::eFixedTransformHintChanged) {
@@ -6392,8 +6395,9 @@
}
status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility, bool shouldBeSeamless) {
- if (!ValidateFrameRate(frameRate, compatibility, "SurfaceFlinger::setFrameRate")) {
+ int8_t compatibility, int8_t changeFrameRateStrategy) {
+ if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy,
+ "SurfaceFlinger::setFrameRate")) {
return BAD_VALUE;
}
@@ -6405,10 +6409,12 @@
ALOGE("Attempt to set frame rate on a layer that no longer exists");
return BAD_VALUE;
}
+ const auto strategy =
+ Layer::FrameRate::convertChangeFrameRateStrategy(changeFrameRateStrategy);
if (layer->setFrameRate(
Layer::FrameRate(Fps{frameRate},
Layer::FrameRate::convertCompatibility(compatibility),
- shouldBeSeamless))) {
+ strategy))) {
setTransactionFlags(eTraversalNeeded);
}
} else {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e4ff6c9..c57b180 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -688,7 +688,7 @@
status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
float lightPosY, float lightPosZ, float lightRadius) override;
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
- int8_t compatibility, bool shouldBeSeamless) override;
+ int8_t compatibility, int8_t changeFrameRateStrategy) override;
status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 974ae84..2094972 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -89,12 +89,15 @@
return byteString;
}
-std::string frameRateVoteToProtoByteString(float refreshRate, int frameRateCompatibility,
- int seamlessness) {
+std::string frameRateVoteToProtoByteString(
+ float refreshRate,
+ TimeStats::SetFrameRateVote::FrameRateCompatibility frameRateCompatibility,
+ TimeStats::SetFrameRateVote::Seamlessness seamlessness) {
util::ProtoOutputStream proto;
proto.write(android::util::FIELD_TYPE_FLOAT | 1 /* field id */, refreshRate);
- proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */, frameRateCompatibility);
- proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, seamlessness);
+ proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */,
+ static_cast<int>(frameRateCompatibility));
+ proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, static_cast<int>(seamlessness));
std::string byteString;
proto.serializeToString(&byteString);
@@ -229,7 +232,10 @@
mStatsDelegate->statsEventWriteInt32(
event, layer->displayRefreshRateBucket); // display_refresh_rate_bucket
mStatsDelegate->statsEventWriteInt32(event, layer->renderRateBucket); // render_rate_bucket
- std::string frameRateVoteBytes = frameRateVoteToProtoByteString(0.0, 0, 0);
+ std::string frameRateVoteBytes =
+ frameRateVoteToProtoByteString(layer->setFrameRateVote.frameRate,
+ layer->setFrameRateVote.frameRateCompatibility,
+ layer->setFrameRateVote.seamlessness);
mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameRateVoteBytes.c_str(),
frameRateVoteBytes.size()); // set_frame_rate_vote
std::string appDeadlineMissedBytes =
@@ -468,8 +474,10 @@
}
void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
- std::optional<Fps> renderRate) {
+ std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) {
ATRACE_CALL();
+ ALOGV("[%d]-flushAvailableRecordsToStatsLocked", layerId);
LayerRecord& layerRecord = mTimeStatsTracker[layerId];
TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
@@ -501,6 +509,9 @@
displayStats.stats[layerKey].uid = uid;
displayStats.stats[layerKey].layerName = layerName;
}
+ if (frameRateVote.frameRate > 0.0f) {
+ displayStats.stats[layerKey].setFrameRateVote = frameRateVote;
+ }
TimeStatsHelper::TimeStatsLayer& timeStatsLayer = displayStats.stats[layerKey];
timeStatsLayer.totalFrames++;
timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
@@ -724,7 +735,8 @@
}
void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
- Fps displayRefreshRate, std::optional<Fps> renderRate) {
+ Fps displayRefreshRate, std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -743,12 +755,13 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate);
+ flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
}
void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence,
- Fps displayRefreshRate, std::optional<Fps> renderRate) {
+ Fps displayRefreshRate, std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -768,7 +781,7 @@
layerRecord.waitData++;
}
- flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate);
+ flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate, frameRateVote);
}
static const constexpr int32_t kValidJankyReason = JankType::DisplayHAL |
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index fd112b9..a87b7cb 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -50,6 +50,8 @@
class TimeStats {
public:
+ using SetFrameRateVote = TimeStatsHelper::SetFrameRateVote;
+
virtual ~TimeStats() = default;
// Called once boot has been finished to perform additional capabilities,
@@ -110,10 +112,12 @@
// SetPresent{Time, Fence} are not expected to be called in the critical
// rendering path, as they flush prior fences if those fences have fired.
virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
- Fps displayRefreshRate, std::optional<Fps> renderRate) = 0;
+ Fps displayRefreshRate, std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) = 0;
virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence,
- Fps displayRefreshRate, std::optional<Fps> renderRate) = 0;
+ Fps displayRefreshRate, std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) = 0;
// Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName}
// key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the
@@ -307,10 +311,11 @@
void setAcquireFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence) override;
void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
- Fps displayRefreshRate, std::optional<Fps> renderRate) override;
+ Fps displayRefreshRate, std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote) override;
void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
- std::optional<Fps> renderRate) override;
+ std::optional<Fps> renderRate, SetFrameRateVote frameRateVote) override;
void incrementJankyFrames(const JankyFramesInfo& info) override;
// Clean up the layer record
@@ -334,7 +339,8 @@
AStatsManager_PullAtomCallbackReturn populateLayerAtom(AStatsEventList* data);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
- std::optional<Fps> renderRate);
+ std::optional<Fps> renderRate,
+ SetFrameRateVote frameRateVote);
void flushPowerTimeLocked();
void flushAvailableGlobalRecordsToStatsLocked();
bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index d116b02..a7e7db2 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -91,6 +91,37 @@
return result;
}
+std::string TimeStatsHelper::SetFrameRateVote::toString(FrameRateCompatibility compatibility) {
+ switch (compatibility) {
+ case FrameRateCompatibility::Undefined:
+ return "Undefined";
+ case FrameRateCompatibility::Default:
+ return "Default";
+ case FrameRateCompatibility::ExactOrMultiple:
+ return "ExactOrMultiple";
+ }
+}
+
+std::string TimeStatsHelper::SetFrameRateVote::toString(Seamlessness seamlessness) {
+ switch (seamlessness) {
+ case Seamlessness::Undefined:
+ return "Undefined";
+ case Seamlessness::ShouldBeSeamless:
+ return "ShouldBeSeamless";
+ case Seamlessness::NotRequired:
+ return "NotRequired";
+ }
+}
+
+std::string TimeStatsHelper::SetFrameRateVote::toString() const {
+ std::string result;
+ StringAppendF(&result, "frameRate = %.2f\n", frameRate);
+ StringAppendF(&result, "frameRateCompatibility = %s\n",
+ toString(frameRateCompatibility).c_str());
+ StringAppendF(&result, "seamlessness = %s\n", toString(seamlessness).c_str());
+ return result;
+}
+
std::string TimeStatsHelper::TimeStatsLayer::toString() const {
std::string result = "\n";
StringAppendF(&result, "displayRefreshRate = %d fps\n", displayRefreshRateBucket);
@@ -104,6 +135,8 @@
StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
result.append("Jank payload for this layer:\n");
result.append(jankPayload.toString());
+ result.append("SetFrateRate vote for this layer:\n");
+ result.append(setFrameRateVote.toString());
const auto iter = deltas.find("present2present");
if (iter != deltas.end()) {
const float averageTime = iter->second.averageTime();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 5ee28ce..2b37ffe 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -55,6 +55,28 @@
std::string toString() const;
};
+ struct SetFrameRateVote {
+ float frameRate = 0;
+
+ // Needs to be in sync with atoms.proto
+ enum class FrameRateCompatibility {
+ Undefined = 0,
+ Default = 1,
+ ExactOrMultiple = 2,
+ } frameRateCompatibility = FrameRateCompatibility::Undefined;
+
+ // Needs to be in sync with atoms.proto
+ enum class Seamlessness {
+ Undefined = 0,
+ ShouldBeSeamless = 1,
+ NotRequired = 2,
+ } seamlessness = Seamlessness::Undefined;
+
+ static std::string toString(FrameRateCompatibility);
+ static std::string toString(Seamlessness);
+ std::string toString() const;
+ };
+
class TimeStatsLayer {
public:
uid_t uid;
@@ -67,6 +89,7 @@
int32_t lateAcquireFrames = 0;
int32_t badDesiredPresentFrames = 0;
JankPayload jankPayload;
+ SetFrameRateVote setFrameRateVote;
std::unordered_map<std::string, Histogram> deltas;
std::string toString() const;
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index d1385c0..982252f 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -668,9 +668,10 @@
Fps renderRate = Fps::fromPeriodNsecs(30);
EXPECT_CALL(*mTimeStats,
- incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne,
- sLayerNameOne, JankType::Unknown,
- -1, -1, 25}));
+ incrementJankyFrames(
+ TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne, sLayerNameOne,
+ JankType::Unknown | JankType::AppDeadlineMissed,
+ -1, -1, 25}));
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 60});
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({82, 90, 90});
@@ -697,7 +698,7 @@
EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::UnknownPresent);
EXPECT_EQ(surfaceFrame1->getActuals().presentTime, 90);
- EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown);
+ EXPECT_EQ(surfaceFrame1->getJankType(), JankType::Unknown | JankType::AppDeadlineMissed);
}
/*
@@ -1699,9 +1700,9 @@
}
TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) {
- // First frame - DisplayFrame is not janky. This should classify the SurfaceFrame as
+ // First frame - DisplayFrame is not janky. This should classify the SurfaceFrame as only
// AppDeadlineMissed. Second frame - DisplayFrame is janky. This should propagate DisplayFrame's
- // jank to the SurfaceFrame.
+ // jank to the SurfaceFrame along with AppDeadlineMissed.
EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2);
auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
@@ -1771,7 +1772,8 @@
EXPECT_EQ(actuals2.presentTime, 60);
EXPECT_EQ(presentedSurfaceFrame2.getFramePresentMetadata(), FramePresentMetadata::LatePresent);
EXPECT_EQ(presentedSurfaceFrame2.getFrameReadyMetadata(), FrameReadyMetadata::LateFinish);
- EXPECT_EQ(presentedSurfaceFrame2.getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed);
+ EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
+ JankType::SurfaceFlingerCpuDeadlineMissed | JankType::AppDeadlineMissed);
}
TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadlineMissed) {
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 15bd0e1..0b70f27 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1182,11 +1182,13 @@
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
}
-TEST_F(RefreshRateConfigsTest, groupSwitching) {
+TEST_F(RefreshRateConfigsTest, groupSwitchingNotAllowed) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
/*currentConfigId=*/HWC_CONFIG_ID_60);
+ // The default policy doesn't allow group switching. Verify that no
+ // group switches are performed.
auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
auto& layer = layers[0];
layer.vote = LayerVoteType::ExplicitDefault;
@@ -1198,64 +1200,203 @@
ASSERT_EQ(HWC_CONFIG_ID_60,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
+}
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayer) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
RefreshRateConfigs::Policy policy;
policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
policy.allowGroupSwitching = true;
ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
+ layer.desiredRefreshRate = Fps(90.0f);
+ layer.seamlessness = Seamlessness::SeamedAndSeamless;
+ layer.name = "90Hz ExplicitDefault";
+ layer.focused = true;
ASSERT_EQ(HWC_CONFIG_ID_90,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamless) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
// Verify that we won't change the group if seamless switch is required.
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
+ layer.desiredRefreshRate = Fps(90.0f);
layer.seamlessness = Seamlessness::OnlySeamless;
+ layer.name = "90Hz ExplicitDefault";
+ layer.focused = true;
ASSERT_EQ(HWC_CONFIG_ID_60,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerOnlySeamlessDefaultFps) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
// Verify that we won't do a seamless switch if we request the same mode as the default
- refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
layer.desiredRefreshRate = Fps(60.0f);
- layer.name = "60Hz ExplicitDefault";
layer.seamlessness = Seamlessness::OnlySeamless;
+ layer.name = "60Hz ExplicitDefault";
+ layer.focused = true;
ASSERT_EQ(HWC_CONFIG_ID_90,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithOneLayerDefaultSeamlessness) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
// Verify that if the current config is in another group and there are no layers with
// seamlessness=SeamedAndSeamless we'll go back to the default group.
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& layer = layers[0];
+ layer.vote = LayerVoteType::ExplicitDefault;
layer.desiredRefreshRate = Fps(60.0f);
- layer.name = "60Hz ExplicitDefault";
layer.seamlessness = Seamlessness::Default;
+ layer.name = "60Hz ExplicitDefault";
+ layer.focused = true;
+
ASSERT_EQ(HWC_CONFIG_ID_60,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersOnlySeamlessAndSeamed) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
// If there's a layer with seamlessness=SeamedAndSeamless, another layer with
// seamlessness=OnlySeamless can't change the mode group.
- refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
- layer.seamlessness = Seamlessness::OnlySeamless;
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].seamlessness = Seamlessness::OnlySeamless;
+ layers[0].name = "60Hz ExplicitDefault";
+ layers[0].focused = true;
layers.push_back(LayerRequirement{.weight = 0.5f});
- auto& layer2 = layers[layers.size() - 1];
- layer2.vote = LayerVoteType::ExplicitDefault;
- layer2.desiredRefreshRate = Fps(90.0f);
- layer2.name = "90Hz ExplicitDefault";
- layer2.seamlessness = Seamlessness::SeamedAndSeamless;
- layer2.focused = false;
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+ layers[1].desiredRefreshRate = Fps(90.0f);
+ layers[1].name = "90Hz ExplicitDefault";
+ layers[1].focused = false;
ASSERT_EQ(HWC_CONFIG_ID_90,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
+}
- // If there's a layer with seamlessness=SeamedAndSeamless, another layer with
- // seamlessness=Default can't change the mode group.
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultFocusedAndSeamed) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+
+ // If there's a focused layer with seamlessness=SeamedAndSeamless, another layer with
+ // seamlessness=Default can't change the mode group back to the group of the default
+ // mode.
+ // For example, this may happen when a video playback requests and gets a seamed switch,
+ // but another layer (with default seamlessness) starts animating. The animating layer
+ // should not cause a seamed switch.
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
layers[0].seamlessness = Seamlessness::Default;
+ layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].focused = true;
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].name = "60Hz ExplicitDefault";
+
+ layers.push_back(LayerRequirement{.weight = 0.1f});
+ layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+ layers[1].desiredRefreshRate = Fps(90.0f);
+ layers[1].focused = true;
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ layers[1].name = "90Hz ExplicitDefault";
+
ASSERT_EQ(HWC_CONFIG_ID_90,
refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
.getModeId());
}
+TEST_F(RefreshRateConfigsTest, groupSwitchingWithTwoLayersDefaultNotFocusedAndSeamed) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ RefreshRateConfigs::Policy policy;
+ policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode;
+ policy.allowGroupSwitching = true;
+ ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+
+ refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
+
+ // Layer with seamlessness=Default can change the mode group if there's a not
+ // focused layer with seamlessness=SeamedAndSeamless. This happens for example,
+ // when in split screen mode the user switches between the two visible applications.
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ layers[0].seamlessness = Seamlessness::Default;
+ layers[0].desiredRefreshRate = Fps(60.0f);
+ layers[0].focused = true;
+ layers[0].vote = LayerVoteType::ExplicitDefault;
+ layers[0].name = "60Hz ExplicitDefault";
+
+ layers.push_back(LayerRequirement{.weight = 0.7f});
+ layers[1].seamlessness = Seamlessness::SeamedAndSeamless;
+ layers[1].desiredRefreshRate = Fps(90.0f);
+ layers[1].focused = false;
+ layers[1].vote = LayerVoteType::ExplicitDefault;
+ layers[1].name = "90Hz ExplicitDefault";
+
+ ASSERT_EQ(HWC_CONFIG_ID_60,
+ refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+ .getModeId());
+}
+
TEST_F(RefreshRateConfigsTest, nonSeamlessVotePrefersSeamlessSwitches) {
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(m30_60Device,
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 5c8c2d8..7ef1f2b 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -475,18 +475,36 @@
PrintToStringParamName);
TEST_F(SetFrameRateTest, ValidateFrameRate) {
- EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
- EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
- EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, ""));
- EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, "", /*privileged=*/true));
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS, ""));
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_TRUE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, "",
+ /*privileged=*/true));
- EXPECT_FALSE(ValidateFrameRate(-1, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
- EXPECT_FALSE(
- ValidateFrameRate(1.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
- EXPECT_FALSE(
- ValidateFrameRate(0.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ""));
+ EXPECT_FALSE(ValidateFrameRate(-1, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_FALSE(ValidateFrameRate(1.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_FALSE(ValidateFrameRate(0.0f / 0.0f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
- EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, ""));
+ EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+
+ // Invalid compatibility
+ EXPECT_FALSE(
+ ValidateFrameRate(60.0f, -1, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+ EXPECT_FALSE(ValidateFrameRate(60.0f, 2, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, ""));
+
+ // Invalid change frame rate strategy
+ EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, -1, ""));
+ EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, 2, ""));
}
TEST_P(SetFrameRateTest, SetOnParentActivatesTree) {
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index a72ab42..c1f0c4e 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -142,15 +142,16 @@
std::string inputCommand(InputCommand cmd, bool useProto);
- void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts);
+ void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
+ TimeStats::SetFrameRateVote frameRateVote);
int32_t genRandomInt32(int32_t begin, int32_t end);
template <size_t N>
void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber,
- nsecs_t ts) {
+ nsecs_t ts, TimeStats::SetFrameRateVote frameRateVote = {}) {
for (size_t i = 0; i < N; i++, ts += 1000000) {
- setTimeStamp(sequence[i], id, frameNumber, ts);
+ setTimeStamp(sequence[i], id, frameNumber, ts, frameRateVote);
}
}
@@ -234,7 +235,8 @@
return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.example.fake#") + std::to_string(layerId);
}
-void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
+void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
+ TimeStats::SetFrameRateVote frameRateVote) {
switch (type) {
case TimeStamp::POST:
ASSERT_NO_FATAL_FAILURE(
@@ -254,13 +256,13 @@
ASSERT_NO_FATAL_FAILURE(mTimeStats->setDesiredTime(id, frameNumber, ts));
break;
case TimeStamp::PRESENT:
- ASSERT_NO_FATAL_FAILURE(
- mTimeStats->setPresentTime(id, frameNumber, ts, kRefreshRate0, kRenderRate0));
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentTime(id, frameNumber, ts, kRefreshRate0,
+ kRenderRate0, frameRateVote));
break;
case TimeStamp::PRESENT_FENCE:
- ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentFence(id, frameNumber,
- std::make_shared<FenceTime>(ts),
- kRefreshRate0, kRenderRate0));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFence(id, frameNumber, std::make_shared<FenceTime>(ts),
+ kRefreshRate0, kRenderRate0, frameRateVote));
break;
default:
ALOGD("Invalid timestamp type");
@@ -411,6 +413,96 @@
EXPECT_THAT(result, HasSubstr(expectedResult));
}
+TEST_F(TimeStatsTest, canCaptureSetFrameRateVote) {
+ // this stat is not in the proto so verify by checking the string dump
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
+ const auto frameRate60 = TimeStats::SetFrameRateVote{
+ .frameRate = 60.0f,
+ .frameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility::Default,
+ .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::ShouldBeSeamless,
+ };
+ const auto frameRate90 = TimeStats::SetFrameRateVote{
+ .frameRate = 90.0f,
+ .frameRateCompatibility =
+ TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+ .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+ };
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
+ std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ std::string expectedResult = "frameRate = 60.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = Default";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = ShouldBeSeamless";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, frameRate90);
+ result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+ expectedResult = "frameRate = 90.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = ExactOrMultiple";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = NotRequired";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+
+ insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 4, 4000000, frameRate60);
+ result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+ expectedResult = "frameRate = 60.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = Default";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = ShouldBeSeamless";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canCaptureSetFrameRateVoteAfterZeroForLayer) {
+ // this stat is not in the proto so verify by checking the string dump
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
+ const auto frameRate90 = TimeStats::SetFrameRateVote{
+ .frameRate = 90.0f,
+ .frameRateCompatibility =
+ TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+ .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+ };
+ const auto frameRateDefault = TimeStats::SetFrameRateVote{
+ .frameRate = 0.0f,
+ .frameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility::Default,
+ .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::ShouldBeSeamless,
+ };
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate90);
+ std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ std::string expectedResult = "frameRate = 90.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = ExactOrMultiple";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = NotRequired";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, frameRateDefault);
+ result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+ expectedResult = "frameRate = 90.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = ExactOrMultiple";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = NotRequired";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+
+ insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 4, 4000000, frameRateDefault);
+ result = inputCommand(InputCommand::DUMP_ALL, FMT_STRING);
+ expectedResult = "frameRate = 90.00";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "frameRateCompatibility = ExactOrMultiple";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "seamlessness = NotRequired";
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) {
// this stat is not in the proto so verify by checking the string dump
constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2;
@@ -936,12 +1028,15 @@
return byteString;
}
-std::string frameRateVoteToProtoByteString(float refreshRate, int frameRateCompatibility,
- int seamlessness) {
+std::string frameRateVoteToProtoByteString(
+ float refreshRate,
+ TimeStats::SetFrameRateVote::FrameRateCompatibility frameRateCompatibility,
+ TimeStats::SetFrameRateVote::Seamlessness seamlessness) {
util::ProtoOutputStream proto;
proto.write(android::util::FIELD_TYPE_FLOAT | 1 /* field id */, refreshRate);
- proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */, frameRateCompatibility);
- proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, seamlessness);
+ proto.write(android::util::FIELD_TYPE_ENUM | 2 /* field id */,
+ static_cast<int>(frameRateCompatibility));
+ proto.write(android::util::FIELD_TYPE_ENUM | 3 /* field id */, static_cast<int>(seamlessness));
std::string byteString;
proto.serializeToString(&byteString);
@@ -1149,7 +1244,13 @@
for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
}
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ const auto frameRate60 = TimeStats::SetFrameRateVote{
+ .frameRate = 60.0f,
+ .frameRateCompatibility =
+ TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
+ .seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
+ };
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60);
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3});
@@ -1181,7 +1282,10 @@
std::string expectedLatchToPresent = buildExpectedHistogramBytestring({2}, {1});
std::string expectedDesiredToPresent = buildExpectedHistogramBytestring({1}, {1});
std::string expectedPostToAcquire = buildExpectedHistogramBytestring({1}, {1});
- std::string expectedFrameRateOverride = frameRateVoteToProtoByteString(0.0, 0, 0);
+ std::string expectedFrameRateOverride =
+ frameRateVoteToProtoByteString(frameRate60.frameRate,
+ frameRate60.frameRateCompatibility,
+ frameRate60.seamlessness);
std::string expectedAppDeadlineMissed = buildExpectedHistogramBytestring({3, 2}, {4, 3});
{
InSequence seq;
@@ -1455,7 +1559,7 @@
TimeStamp type = static_cast<TimeStamp>(genRandomInt32(TIME_STAMP_BEGIN, TIME_STAMP_END));
const int32_t ts = genRandomInt32(1, 1000000000);
ALOGV("type[%d], layerId[%d], frameNumber[%d], ts[%d]", type, layerId, frameNumber, ts);
- setTimeStamp(type, layerId, frameNumber, ts);
+ setTimeStamp(type, layerId, frameNumber, ts, {});
}
}
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 3e4a0b8..37b74ed 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -48,10 +48,11 @@
MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
- MOCK_METHOD5(setPresentTime, void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>));
- MOCK_METHOD5(setPresentFence,
- void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps,
- std::optional<Fps>));
+ MOCK_METHOD6(setPresentTime,
+ void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote));
+ MOCK_METHOD6(setPresentFence,
+ void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>,
+ SetFrameRateVote));
MOCK_METHOD1(incrementJankyFrames, void(const JankyFramesInfo&));
MOCK_METHOD1(onDestroy, void(int32_t));
MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));