Merge "Make sendHint private in platform"
diff --git a/Android.bp b/Android.bp
index 615a7a8..3992f82 100644
--- a/Android.bp
+++ b/Android.bp
@@ -56,7 +56,8 @@
cc_library_headers {
name: "libandroid_sensor_headers",
- vendor: true,
+ vendor_available: true,
+ host_supported: true,
export_include_dirs: ["include_sensor"],
}
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 48d48ac..46859a2 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -62,7 +62,7 @@
using std::string;
-#define MAX_SYS_FILES 12
+#define MAX_SYS_FILES 13
const char* k_traceTagsProperty = "debug.atrace.tags.enableflags";
const char* k_userInitiatedTraceProperty = "debug.atrace.user_initiated";
@@ -189,6 +189,8 @@
{ OPT, "events/f2fs/f2fs_sync_file_exit/enable" },
{ OPT, "events/f2fs/f2fs_write_begin/enable" },
{ OPT, "events/f2fs/f2fs_write_end/enable" },
+ { OPT, "events/f2fs/f2fs_iostat/enable" },
+ { OPT, "events/f2fs/f2fs_iostat_latency/enable" },
{ OPT, "events/ext4/ext4_da_write_begin/enable" },
{ OPT, "events/ext4/ext4_da_write_end/enable" },
{ OPT, "events/ext4/ext4_sync_file_enter/enable" },
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index e5b5db2..d4ba321 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -829,6 +829,8 @@
AKEYCODE_STYLUS_BUTTON_TERTIARY = 310,
/** A button on the tail end of a stylus. */
AKEYCODE_STYLUS_BUTTON_TAIL = 311,
+ /** Key to open recent apps (a.k.a. Overview) */
+ AKEYCODE_RECENT_APPS = 312,
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/ftl/optional.h b/include/ftl/optional.h
index 7b02bac..a818128 100644
--- a/include/ftl/optional.h
+++ b/include/ftl/optional.h
@@ -95,6 +95,14 @@
if (has_value()) return std::invoke(std::forward<F>(f), std::move(value()));
return R();
}
+
+ // Delete new for this class. Its base doesn't have a virtual destructor, and
+ // if it got deleted via base class pointer, it would cause undefined
+ // behavior. There's not a good reason to allocate this object on the heap
+ // anyway.
+ static void* operator new(size_t) = delete;
+ static void* operator new[](size_t) = delete;
+
};
template <typename T, typename U>
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index dc928b8..867a089 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -125,14 +125,21 @@
bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
Vector<KeyEvent>& outEvents) const;
+ /* Maps an Android key code to another Android key code. This mapping is applied after scanCode
+ * and usageCodes are mapped to corresponding Android Keycode */
+ void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
+
/* Maps a scan code and usage code to a key code, in case this key map overrides
* the mapping in some way. */
status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const;
- /* Tries to find a replacement key code for a given key code and meta state
- * in character map. */
- void tryRemapKey(int32_t scanCode, int32_t metaState,
- int32_t* outKeyCode, int32_t* outMetaState) const;
+ /* Returns keycode after applying Android key code remapping defined in mKeyRemapping */
+ int32_t applyKeyRemapping(int32_t fromKeyCode) const;
+
+ /* Returns the <keyCode, metaState> pair after applying key behavior defined in the kcm file,
+ * that tries to find a replacement key code based on current meta state */
+ std::pair<int32_t /*keyCode*/, int32_t /*metaState*/> applyKeyBehavior(int32_t keyCode,
+ int32_t metaState) const;
#ifdef __linux__
/* Reads a key map from a parcel. */
@@ -227,8 +234,9 @@
std::string mLoadFileName;
bool mLayoutOverlayApplied;
- KeyedVector<int32_t, int32_t> mKeysByScanCode;
- KeyedVector<int32_t, int32_t> mKeysByUsageCode;
+ std::map<int32_t /* fromAndroidKeyCode */, int32_t /* toAndroidKeyCode */> mKeyRemapping;
+ std::map<int32_t /* fromScanCode */, int32_t /* toAndroidKeyCode */> mKeysByScanCode;
+ std::map<int32_t /* fromHidUsageCode */, int32_t /* toAndroidKeyCode */> mKeysByUsageCode;
KeyCharacterMap(const std::string& filename);
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 7770374..6d64e1e 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -132,12 +132,21 @@
} else {
out << "\ttarget.ptr=" << btd->target.ptr;
}
- out << "\t (cookie " << btd->cookie << ")"
- << "\n"
+ out << "\t (cookie " << btd->cookie << ")\n"
<< "\tcode=" << TypeCode(btd->code) << ", flags=" << (void*)(uint64_t)btd->flags << "\n"
- << "\tdata=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size << " bytes)"
- << "\n"
- << "\toffsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size << " bytes)";
+ << "\tdata=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size << " bytes)\n"
+ << "\toffsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size << " bytes)\n";
+ return btd + 1;
+}
+
+static const void* printBinderTransactionDataSecCtx(std::ostream& out, const void* data) {
+ const binder_transaction_data_secctx* btd = (const binder_transaction_data_secctx*)data;
+
+ printBinderTransactionData(out, &btd->transaction_data);
+
+ char* secctx = (char*)btd->secctx;
+ out << "\tsecctx=" << secctx << "\n";
+
return btd+1;
}
@@ -156,6 +165,11 @@
out << "\t" << kReturnStrings[cmdIndex];
switch (code) {
+ case BR_TRANSACTION_SEC_CTX: {
+ out << ": ";
+ cmd = (const int32_t*)printBinderTransactionDataSecCtx(out, cmd);
+ } break;
+
case BR_TRANSACTION:
case BR_REPLY: {
out << ": ";
@@ -1017,6 +1031,10 @@
if (!reply && !acquireResult) goto finish;
break;
+ case BR_TRANSACTION_PENDING_FROZEN:
+ ALOGW("Sending oneway calls to frozen process.");
+ goto finish;
+
case BR_DEAD_REPLY:
err = DEAD_OBJECT;
goto finish;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index a0c4334..2408307 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -444,7 +444,7 @@
bool declared;
if (Status status = mTheRealServiceManager->isDeclared(String8(name).c_str(), &declared);
!status.isOk()) {
- ALOGW("Failed to get isDeclard for %s: %s", String8(name).c_str(),
+ ALOGW("Failed to get isDeclared for %s: %s", String8(name).c_str(),
status.toString8().c_str());
return false;
}
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 1f311ac..254dda8 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -439,6 +439,10 @@
return mCurrentThreads;
}
+bool ProcessState::isThreadPoolStarted() const {
+ return mThreadPoolStarted;
+}
+
#define DRIVER_FEATURES_PATH "/dev/binderfs/features/"
bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) {
static const char* const names[] = {
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index 0820cd1..fedc1d9 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -70,11 +70,8 @@
return setupSocketServer(UnixSocketAddress(path));
}
-status_t RpcServer::setupVsockServer(unsigned int port) {
- // realizing value w/ this type at compile time to avoid ubsan abort
- constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
-
- return setupSocketServer(VsockSocketAddress(kAnyCid, port));
+status_t RpcServer::setupVsockServer(unsigned int bindCid, unsigned int port) {
+ return setupSocketServer(VsockSocketAddress(bindCid, port));
}
status_t RpcServer::setupInetServer(const char* address, unsigned int port,
@@ -157,6 +154,12 @@
mRootObjectFactory = std::move(makeObject);
}
+void RpcServer::setConnectionFilter(std::function<bool(const void*, size_t)>&& filter) {
+ RpcMutexLockGuard _l(mLock);
+ LOG_ALWAYS_FATAL_IF(mShutdownTrigger != nullptr, "Already joined");
+ mConnectionFilter = std::move(filter);
+}
+
sp<IBinder> RpcServer::getRootObject() {
RpcMutexLockGuard _l(mLock);
bool hasWeak = mRootObjectWeak.unsafe_get();
@@ -242,13 +245,19 @@
if (mAcceptFn(*this, &clientSocket) != OK) {
continue;
}
+
+ LOG_RPC_DETAIL("accept on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());
+
if (getpeername(clientSocket.fd.get(), reinterpret_cast<sockaddr*>(addr.data()),
&addrLen)) {
ALOGE("Could not getpeername socket: %s", strerror(errno));
continue;
}
- LOG_RPC_DETAIL("accept on fd %d yields fd %d", mServer.fd.get(), clientSocket.fd.get());
+ if (mConnectionFilter != nullptr && !mConnectionFilter(addr.data(), addrLen)) {
+ ALOGE("Dropped client connection fd %d", clientSocket.fd.get());
+ continue;
+ }
{
RpcMutexLockGuard _l(mLock);
diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp
index 453279c..8b3ddfb 100644
--- a/libs/binder/RpcTransportTipcAndroid.cpp
+++ b/libs/binder/RpcTransportTipcAndroid.cpp
@@ -63,12 +63,14 @@
if (pfd.revents & POLLERR) {
return DEAD_OBJECT;
}
+ if (pfd.revents & POLLIN) {
+ // Copied from FdTrigger.cpp: Even though POLLHUP may also be set,
+ // treat it as a success condition to ensure data is drained.
+ return OK;
+ }
if (pfd.revents & POLLHUP) {
return DEAD_OBJECT;
}
- if (pfd.revents & POLLIN) {
- return OK;
- }
return WOULD_BLOCK;
}
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index 793795e..eef07ae 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -100,4 +100,9 @@
#define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
#endif // BINDER_ENABLE_ONEWAY_SPAM_DETECTION
+#ifndef BR_TRANSACTION_PENDING_FROZEN
+// Temporary definition of BR_TRANSACTION_PENDING_FROZEN until UAPI binder.h includes it.
+#define BR_TRANSACTION_PENDING_FROZEN _IO('r', 20)
+#endif // BR_TRANSACTION_PENDING_FROZEN
+
#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index dc572ac..8cc8105 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -230,7 +230,6 @@
"android.graphicsenv.IGpuService",
"android.gui.IConsumerListener",
"android.gui.IGraphicBufferConsumer",
- "android.gui.ITransactionComposerListener",
"android.gui.SensorEventConnection",
"android.gui.SensorServer",
"android.hardware.ICamera",
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 65b77c6..d261c21 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -141,11 +141,13 @@
void restoreCallingIdentity(int64_t token);
bool hasExplicitIdentity();
+ // For main functions - dangerous for libraries to use
status_t setupPolling(int* fd);
status_t handlePolledCommands();
void flushCommands();
bool flushIfNeeded();
+ // For main functions - dangerous for libraries to use
void joinThreadPool(bool isMain = true);
// Stop the local process.
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 79e771f..2af512e 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -67,7 +67,8 @@
* a system property, or in the case of services in the VINTF manifest, it can be checked
* with isDeclared).
*/
- virtual sp<IBinder> getService( const String16& name) const = 0;
+ [[deprecated("this polls for 5s, prefer waitForService or checkService")]]
+ virtual sp<IBinder> getService(const String16& name) const = 0;
/**
* Retrieve an existing service, non-blocking.
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 9679a5f..bad8cb1 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -50,6 +50,7 @@
sp<IBinder> getContextObject(const sp<IBinder>& caller);
+ // For main functions - dangerous for libraries to use
void startThreadPool();
bool becomeContextManager();
@@ -57,8 +58,10 @@
sp<IBinder> getStrongProxyForHandle(int32_t handle);
void expungeHandle(int32_t handle, IBinder* binder);
+ // TODO: deprecate.
void spawnPooledThread(bool isMain);
+ // For main functions - dangerous for libraries to use
status_t setThreadPoolMaxThreadCount(size_t maxThreads);
status_t enableOnewaySpamDetection(bool enable);
void giveThreadPoolName();
@@ -94,6 +97,11 @@
*/
size_t getThreadPoolMaxTotalThreadCount() const;
+ /**
+ * Check to see if the thread pool has started.
+ */
+ bool isThreadPoolStarted() const;
+
enum class DriverFeature {
ONEWAY_SPAM_DETECTION,
EXTENDED_ERROR,
diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h
index 4ad0a47..25193a3 100644
--- a/libs/binder/include/binder/RpcServer.h
+++ b/libs/binder/include/binder/RpcServer.h
@@ -81,9 +81,9 @@
[[nodiscard]] status_t setupRawSocketServer(base::unique_fd socket_fd);
/**
- * Creates an RPC server at the current port.
+ * Creates an RPC server binding to the given CID at the given port.
*/
- [[nodiscard]] status_t setupVsockServer(unsigned int port);
+ [[nodiscard]] status_t setupVsockServer(unsigned int bindCid, unsigned int port);
/**
* Creates an RPC server at the current port using IPv4.
@@ -171,6 +171,16 @@
sp<IBinder> getRootObject();
/**
+ * Set optional filter of incoming connections based on the peer's address.
+ *
+ * Takes one argument: a callable that is invoked on each accept()-ed
+ * connection and returns false if the connection should be dropped.
+ * See the description of setPerSessionRootObject() for details about
+ * the callable's arguments.
+ */
+ void setConnectionFilter(std::function<bool(const void*, size_t)>&& filter);
+
+ /**
* See RpcTransportCtx::getCertificate
*/
std::vector<uint8_t> getCertificate(RpcCertificateFormat);
@@ -253,6 +263,7 @@
sp<IBinder> mRootObject;
wp<IBinder> mRootObjectWeak;
std::function<sp<IBinder>(const void*, size_t)> mRootObjectFactory;
+ std::function<bool(const void*, size_t)> mConnectionFilter;
std::map<std::vector<uint8_t>, sp<RpcSession>> mSessions;
std::unique_ptr<FdTrigger> mShutdownTrigger;
RpcConditionVariable mShutdownCv;
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index f08bde8..3ec049e 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -25,9 +25,13 @@
struct ARpcServer;
// Starts an RPC server on a given port and a given root IBinder object.
+// The server will only accept connections from the given CID.
+// Set `cid` to VMADDR_CID_ANY to accept connections from any client.
+// Set `cid` to VMADDR_CID_LOCAL to only bind to the local vsock interface.
// Returns an opaque handle to the running server instance, or null if the server
// could not be started.
-[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port);
+[[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid,
+ unsigned int port);
// Starts a Unix domain RPC server with a given init-managed Unix domain `name`
// and a given root IBinder object.
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index f55c779..88f8c94 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -51,21 +51,26 @@
ref->decStrong(ref);
}
+static unsigned int cidFromStructAddr(const void* addr, size_t addrlen) {
+ LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated");
+ const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
+ LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock");
+ return vaddr->svm_cid;
+}
+
extern "C" {
bool RunVsockRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
void* factoryContext, unsigned int port) {
auto server = RpcServer::make();
- if (status_t status = server->setupVsockServer(port); status != OK) {
+ if (status_t status = server->setupVsockServer(VMADDR_CID_ANY, port); status != OK) {
LOG(ERROR) << "Failed to set up vsock server with port " << port
<< " error: " << statusToString(status).c_str();
return false;
}
server->setPerSessionRootObject([=](const void* addr, size_t addrlen) {
- LOG_ALWAYS_FATAL_IF(addrlen < sizeof(sockaddr_vm), "sockaddr is truncated");
- const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
- LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock");
- return AIBinder_toPlatformBinder(factory(vaddr->svm_cid, factoryContext));
+ unsigned int cid = cidFromStructAddr(addr, addrlen);
+ return AIBinder_toPlatformBinder(factory(cid, factoryContext));
});
server->join();
@@ -75,13 +80,30 @@
return true;
}
-ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int port) {
+ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) {
auto server = RpcServer::make();
- if (status_t status = server->setupVsockServer(port); status != OK) {
+
+ unsigned int bindCid = VMADDR_CID_ANY; // bind to the remote interface
+ if (cid == VMADDR_CID_LOCAL) {
+ bindCid = VMADDR_CID_LOCAL; // bind to the local interface
+ cid = VMADDR_CID_ANY; // no need for a connection filter
+ }
+
+ if (status_t status = server->setupVsockServer(bindCid, port); status != OK) {
LOG(ERROR) << "Failed to set up vsock server with port " << port
<< " error: " << statusToString(status).c_str();
return nullptr;
}
+ if (cid != VMADDR_CID_ANY) {
+ server->setConnectionFilter([=](const void* addr, size_t addrlen) {
+ unsigned int remoteCid = cidFromStructAddr(addr, addrlen);
+ if (cid != remoteCid) {
+ LOG(ERROR) << "Rejected vsock connection from CID " << remoteCid;
+ return false;
+ }
+ return true;
+ });
+ }
server->setRootObject(AIBinder_toPlatformBinder(service));
return createRpcServerHandle(server);
}
diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h
index 6a25db2..2a00736 100644
--- a/libs/binder/ndk/include_cpp/android/binder_to_string.h
+++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h
@@ -160,7 +160,7 @@
template <typename _T>
std::string ToString(const _T& t) {
if constexpr (details::ToEmptyString<_T>::value) {
- return "";
+ return "<unimplemented>";
} else if constexpr (std::is_same_v<bool, _T>) {
return t ? "true" : "false";
} else if constexpr (std::is_same_v<char16_t, _T>) {
@@ -176,9 +176,11 @@
return t;
#ifdef HAS_NDK_INTERFACE
} else if constexpr (std::is_same_v<::ndk::SpAIBinder, _T>) {
- return (t.get() == nullptr) ? "(null)" : "";
+ std::stringstream ss;
+ ss << "binder:" << std::hex << t.get();
+ return ss.str();
} else if constexpr (std::is_same_v<::ndk::ScopedFileDescriptor, _T>) {
- return (t.get() == -1) ? "(null)" : "";
+ return "fd:" + std::to_string(t.get());
#endif
#ifdef HAS_STRING16
} else if constexpr (std::is_same_v<String16, _T>) {
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 4163897..1af2119 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -613,7 +613,8 @@
*
* 1. If the binder died, shortly after the call to onBinderDied.
* 2. If the binder is explicitly unlinked with AIBinder_unlinkToDeath or
- * AIBinder_DeathRecipient_delete.
+ * AIBinder_DeathRecipient_delete, after any pending onBinderDied calls
+ * finish.
* 3. During or shortly after the AIBinder_linkToDeath call if it returns an error.
*
* It is guaranteed that the callback is called exactly once for each call to linkToDeath unless the
diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h
index f408fad..ffcad55 100644
--- a/libs/binder/ndk/include_platform/android/binder_process.h
+++ b/libs/binder/ndk/include_platform/android/binder_process.h
@@ -28,17 +28,33 @@
*
* When using this, it is expected that ABinderProcess_setupPolling and
* ABinderProcess_handlePolledCommands are not used.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
*/
void ABinderProcess_startThreadPool();
/**
* This sets the maximum number of threads that can be started in the threadpool. By default, after
* startThreadPool is called, this is 15. If it is called additional times, it will only prevent
* the kernel from starting new threads and will not delete already existing threads.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
*/
bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads);
/**
+ * Check if the threadpool has already been started.
+ * This tells whether someone in the process has called ABinderProcess_startThreadPool. Usually,
+ * you should use this in a library to abort if the threadpool is not started.
+ * Programs should configure binder threadpools once at the beginning.
+ */
+bool ABinderProcess_isThreadPoolStarted();
+/**
* This adds the current thread to the threadpool. This may cause the threadpool to exceed the
* maximum size.
+ *
+ * Do not use this from a library. Apps setup their own threadpools, and otherwise, the main
+ * function should be responsible for configuring the threadpool for the entire application.
*/
void ABinderProcess_joinThreadPool();
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 683a433..c1f62e5 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -50,6 +50,15 @@
* requirements associated with that higher stability level. For instance, a
* VINTF stability binder is required to be in the VINTF manifest. This API
* can be called to use that same interface within the vendor partition.
+ *
+ * WARNING: you must hold on to a binder instance after this is set, while you
+ * are using it. If you get a binder (e.g. `...->asBinder().get()`), you must
+ * save this binder and then
+ * use it. For instance:
+ *
+ * auto binder = ...->asBinder();
+ * AIBinder_forceDowngradeToVendorStability(binder.get());
+ * doSomething(binder);
*/
void AIBinder_forceDowngradeToVendorStability(AIBinder* binder);
@@ -79,6 +88,15 @@
* requirements associated with that higher stability level. For instance, a
* VINTF stability binder is required to be in the VINTF manifest. This API
* can be called to use that same interface within the system partition.
+ *
+ * WARNING: you must hold on to a binder instance after this is set, while you
+ * are using it. If you get a binder (e.g. `...->asBinder().get()`), you must
+ * save this binder and then
+ * use it. For instance:
+ *
+ * auto binder = ...->asBinder();
+ * AIBinder_forceDowngradeToSystemStability(binder.get());
+ * doSomething(binder);
*/
void AIBinder_forceDowngradeToSystemStability(AIBinder* binder);
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 5c7005c..54e4628 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -154,6 +154,7 @@
LIBBINDER_NDK34 { # introduced=UpsideDownCake
global:
+ ABinderProcess_isThreadPoolStarted; # systemapi llndk
AServiceManager_getUpdatableApexName; # systemapi
AServiceManager_registerForServiceNotifications; # systemapi llndk
AServiceManager_NotificationRegistration_delete; # systemapi llndk
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index ac582a4..bc6610e 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -31,6 +31,9 @@
bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads) {
return ProcessState::self()->setThreadPoolMaxThreadCount(numThreads) == 0;
}
+bool ABinderProcess_isThreadPoolStarted() {
+ return ProcessState::self()->isThreadPoolStarted();
+}
void ABinderProcess_joinThreadPool() {
IPCThreadState::self()->joinThreadPool();
}
diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs
index 42f5567..d5f1219 100644
--- a/libs/binder/rust/rpcbinder/src/server.rs
+++ b/libs/binder/rust/rpcbinder/src/server.rs
@@ -41,14 +41,19 @@
impl RpcServer {
/// Creates a binder RPC server, serving the supplied binder service implementation on the given
- /// vsock port.
- pub fn new_vsock(mut service: SpIBinder, port: u32) -> Result<RpcServer, Error> {
+ /// vsock port. Only connections from the given CID are accepted.
+ ///
+ // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client.
+ // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface.
+ pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> {
let service = service.as_native_mut();
// SAFETY: Service ownership is transferring to the server and won't be valid afterward.
// Plus the binder objects are threadsafe.
unsafe {
- Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(service, port))
+ Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(
+ service, cid, port,
+ ))
}
}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
index 28e0200..df8a2af 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/Android.bp
@@ -21,5 +21,7 @@
"waghpawan@google.com",
"smoreland@google.com",
],
+ // hotlist "AIDL fuzzers bugs" on buganizer
+ hotlists: ["4637097"],
},
}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
index 43e407c..5cb406a 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
+++ b/libs/binder/rust/tests/parcel_fuzzer/random_parcel/fuzz_service_test/Android.bp
@@ -29,5 +29,7 @@
"waghpawan@google.com",
"smoreland@google.com",
],
+ // hotlist "AIDL fuzzers bugs" on buganizer
+ hotlists: ["4637097"],
},
}
diff --git a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
index d2bfde1..a2d48b6 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
+++ b/libs/binder/rust/tests/parcel_fuzzer/read_utils.rs
@@ -16,9 +16,9 @@
use binder::binder_impl::BorrowedParcel;
use binder::{ParcelFileDescriptor, Parcelable, SpIBinder};
-use binderReadParcelIface::aidl::EmptyParcelable::EmptyParcelable;
-use binderReadParcelIface::aidl::GenericDataParcelable::GenericDataParcelable;
-use binderReadParcelIface::aidl::SingleDataParcelable::SingleDataParcelable;
+use binderReadParcelIface::aidl::parcelables::EmptyParcelable::EmptyParcelable;
+use binderReadParcelIface::aidl::parcelables::GenericDataParcelable::GenericDataParcelable;
+use binderReadParcelIface::aidl::parcelables::SingleDataParcelable::SingleDataParcelable;
macro_rules! read_parcel_interface {
($data_type:ty) => {
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 25b524f..f7498c4 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -120,6 +120,7 @@
BINDER_LIB_TEST_CAN_GET_SID,
BINDER_LIB_TEST_GET_MAX_THREAD_COUNT,
BINDER_LIB_TEST_SET_MAX_THREAD_COUNT,
+ BINDER_LIB_TEST_IS_THREADPOOL_STARTED,
BINDER_LIB_TEST_LOCK_UNLOCK,
BINDER_LIB_TEST_PROCESS_LOCK,
BINDER_LIB_TEST_UNLOCK_AFTER_MS,
@@ -1383,6 +1384,14 @@
EXPECT_EQ(replyi, kKernelThreads + 1);
}
+TEST_F(BinderLibTest, ThreadPoolStarted) {
+ Parcel data, reply;
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != nullptr);
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_IS_THREADPOOL_STARTED, data, &reply), NO_ERROR);
+ EXPECT_TRUE(reply.readBool());
+}
+
size_t epochMillis() {
using std::chrono::duration_cast;
using std::chrono::milliseconds;
@@ -1849,6 +1858,10 @@
reply->writeInt32(ProcessState::self()->getThreadPoolMaxTotalThreadCount());
return NO_ERROR;
}
+ case BINDER_LIB_TEST_IS_THREADPOOL_STARTED: {
+ reply->writeBool(ProcessState::self()->isThreadPoolStarted());
+ return NO_ERROR;
+ }
case BINDER_LIB_TEST_PROCESS_LOCK: {
m_blockMutex.lock();
return NO_ERROR;
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 02aa45f..739c217 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -1342,7 +1342,7 @@
} break;
case SocketType::VSOCK: {
auto port = allocateVsockPort();
- auto status = rpcServer->setupVsockServer(port);
+ auto status = rpcServer->setupVsockServer(VMADDR_CID_LOCAL, port);
if (status != OK) {
return AssertionFailure() << "setupVsockServer: " << statusToString(status);
}
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index 995e761..cc9726b 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -58,7 +58,7 @@
CHECK_EQ(OK, server->setupRawSocketServer(std::move(socketFd)));
break;
case SocketType::VSOCK:
- CHECK_EQ(OK, server->setupVsockServer(serverConfig.vsockPort));
+ CHECK_EQ(OK, server->setupVsockServer(VMADDR_CID_LOCAL, serverConfig.vsockPort));
break;
case SocketType::INET: {
CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort));
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index 61a2412..35866ad 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -12,13 +12,14 @@
host_supported: true,
unstable: true,
srcs: [
- "EmptyParcelable.aidl",
- "SingleDataParcelable.aidl",
- "GenericDataParcelable.aidl",
+ "parcelables/EmptyParcelable.aidl",
+ "parcelables/SingleDataParcelable.aidl",
+ "parcelables/GenericDataParcelable.aidl",
],
backend: {
java: {
- enabled: false,
+ enabled: true,
+ platform_apis: true,
},
rust: {
enabled: true,
diff --git a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl b/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
deleted file mode 100644
index 96d6223..0000000
--- a/libs/binder/tests/parcel_fuzzer/EmptyParcelable.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-parcelable EmptyParcelable{
-}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 9dac2c9..768fbe1 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -16,9 +16,9 @@
#define FUZZ_LOG_TAG "binder"
#include "binder.h"
-#include "EmptyParcelable.h"
-#include "GenericDataParcelable.h"
-#include "SingleDataParcelable.h"
+#include "parcelables/EmptyParcelable.h"
+#include "parcelables/GenericDataParcelable.h"
+#include "parcelables/SingleDataParcelable.h"
#include "util.h"
#include <android-base/hex.h>
@@ -359,19 +359,19 @@
},
[] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call readFromParcel() with status for EmptyParcelable";
- EmptyParcelable emptyParcelable{};
+ parcelables::EmptyParcelable emptyParcelable{};
status_t status = emptyParcelable.readFromParcel(&p);
FUZZ_LOG() << " status: " << status;
},
[] (const ::android::Parcel& p , FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call readFromParcel() with status for SingleDataParcelable";
- SingleDataParcelable singleDataParcelable;
+ parcelables::SingleDataParcelable singleDataParcelable;
status_t status = singleDataParcelable.readFromParcel(&p);
FUZZ_LOG() <<" status: " << status;
},
[] (const ::android::Parcel& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to call readFromParcel() with status for GenericDataParcelable";
- GenericDataParcelable genericDataParcelable;
+ parcelables::GenericDataParcelable genericDataParcelable;
status_t status = genericDataParcelable.readFromParcel(&p);
FUZZ_LOG() <<" status: " << status;
},
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index af773a0..53e7de4 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -16,9 +16,9 @@
#define FUZZ_LOG_TAG "binder_ndk"
#include "binder_ndk.h"
-#include "aidl/EmptyParcelable.h"
-#include "aidl/GenericDataParcelable.h"
-#include "aidl/SingleDataParcelable.h"
+#include "aidl/parcelables/EmptyParcelable.h"
+#include "aidl/parcelables/GenericDataParcelable.h"
+#include "aidl/parcelables/SingleDataParcelable.h"
#include <android/binder_parcel_utils.h>
#include <android/binder_parcelable_utils.h>
@@ -183,19 +183,19 @@
[](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read parcel using readFromParcel for EmptyParcelable";
- aidl::EmptyParcelable emptyParcelable;
+ aidl::parcelables::EmptyParcelable emptyParcelable;
binder_status_t status = emptyParcelable.readFromParcel(p.aParcel());
FUZZ_LOG() << "status: " << status;
},
[](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read parcel using readFromParcel for SingleDataParcelable";
- aidl::SingleDataParcelable singleDataParcelable;
+ aidl::parcelables::SingleDataParcelable singleDataParcelable;
binder_status_t status = singleDataParcelable.readFromParcel(p.aParcel());
FUZZ_LOG() << "status: " << status;
},
[](const NdkParcelAdapter& p, FuzzedDataProvider& /*provider*/) {
FUZZ_LOG() << "about to read parcel using readFromParcel for GenericDataParcelable";
- aidl::GenericDataParcelable genericDataParcelable;
+ aidl::parcelables::GenericDataParcelable genericDataParcelable;
binder_status_t status = genericDataParcelable.readFromParcel(p.aParcel());
FUZZ_LOG() << "status: " << status;
},
diff --git a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
index 86461c8..8bef33f 100644
--- a/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
+++ b/libs/binder/tests/parcel_fuzzer/libbinder_driver.cpp
@@ -37,7 +37,9 @@
}
while (provider.remaining_bytes() > 0) {
- uint32_t code = provider.ConsumeIntegral<uint32_t>();
+ // Most of the AIDL services will have small set of transaction codes.
+ uint32_t code = provider.ConsumeBool() ? provider.ConsumeIntegral<uint32_t>()
+ : provider.ConsumeIntegralInRange<uint32_t>(0, 100);
uint32_t flags = provider.ConsumeIntegral<uint32_t>();
Parcel data;
// for increased fuzz coverage
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
similarity index 92%
copy from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
copy to libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
index d62891b..1216250 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/EmptyParcelable.aidl
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-parcelable SingleDataParcelable{
- int data;
+package parcelables;
+parcelable EmptyParcelable {
}
\ No newline at end of file
diff --git a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
similarity index 97%
rename from libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
rename to libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
index fc2542b..f1079e9 100644
--- a/libs/binder/tests/parcel_fuzzer/GenericDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/GenericDataParcelable.aidl
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package parcelables;
parcelable GenericDataParcelable {
int data;
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
similarity index 96%
rename from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
rename to libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
index d62891b..0187168 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/binder/tests/parcel_fuzzer/parcelables/SingleDataParcelable.aidl
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package parcelables;
parcelable SingleDataParcelable{
int data;
diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
index edc695f..f0beed2 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -73,6 +73,11 @@
1));
CHECK(OK == p->writeFileDescriptor(fd.get(), false /*takeOwnership*/));
} else {
+ // b/260119717 - Adding more FDs can eventually lead to FD limit exhaustion
+ if (options->extraFds.size() > 1000) {
+ return;
+ }
+
std::vector<base::unique_fd> fds = getRandomFds(&provider);
CHECK(OK ==
p->writeFileDescriptor(fds.begin()->release(),
diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
index 58bfe71..d249b2e 100644
--- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp
+++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp
@@ -239,6 +239,12 @@
}
if (!(uevt.event & IPC_HANDLE_POLL_MSG)) {
/* No message, terminate here and leave mHaveMessage false */
+ if (uevt.event & IPC_HANDLE_POLL_HUP) {
+ // Peer closed the connection. We need to preserve the order
+ // between MSG and HUP from FdTrigger.cpp, which means that
+ // getting MSG&HUP should return OK instead of DEAD_OBJECT.
+ return DEAD_OBJECT;
+ }
return OK;
}
diff --git a/libs/binder/trusty/include/log/log.h b/libs/binder/trusty/include/log/log.h
index d88d18a..de84617 100644
--- a/libs/binder/trusty/include/log/log.h
+++ b/libs/binder/trusty/include/log/log.h
@@ -121,6 +121,8 @@
TLOGE("android_errorWriteLog: tag:%x subTag:%s\n", tag, subTag); \
} while (0)
-extern "C" inline void __assert(const char* file, int line, const char* str) {
- LOG_ALWAYS_FATAL("%s:%d: assertion \"%s\" failed", file, line, str);
-}
+// Override the definition of __assert from binder_status.h
+#ifndef __BIONIC__
+#undef __assert
+#define __assert(file, line, str) LOG_ALWAYS_FATAL("%s:%d: %s", file, line, str)
+#endif // __BIONIC__
diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp
index 858739c..7b74214 100644
--- a/libs/graphicsenv/GpuStatsInfo.cpp
+++ b/libs/graphicsenv/GpuStatsInfo.cpp
@@ -89,6 +89,14 @@
if ((status = parcel->writeBool(falsePrerotation)) != OK) return status;
if ((status = parcel->writeBool(gles1InUse)) != OK) return status;
if ((status = parcel->writeBool(angleInUse)) != OK) return status;
+ if ((status = parcel->writeBool(createdGlesContext)) != OK) return status;
+ if ((status = parcel->writeBool(createdVulkanDevice)) != OK) return status;
+ if ((status = parcel->writeBool(createdVulkanSwapchain)) != OK) return status;
+ if ((status = parcel->writeUint32(vulkanApiVersion)) != OK) return status;
+ if ((status = parcel->writeUint64(vulkanDeviceFeaturesEnabled)) != OK) return status;
+ if ((status = parcel->writeInt32Vector(vulkanInstanceExtensions)) != OK) return status;
+ if ((status = parcel->writeInt32Vector(vulkanDeviceExtensions)) != OK) return status;
+
return OK;
}
@@ -103,6 +111,14 @@
if ((status = parcel->readBool(&falsePrerotation)) != OK) return status;
if ((status = parcel->readBool(&gles1InUse)) != OK) return status;
if ((status = parcel->readBool(&angleInUse)) != OK) return status;
+ if ((status = parcel->readBool(&createdGlesContext)) != OK) return status;
+ if ((status = parcel->readBool(&createdVulkanDevice)) != OK) return status;
+ if ((status = parcel->readBool(&createdVulkanSwapchain)) != OK) return status;
+ if ((status = parcel->readUint32(&vulkanApiVersion)) != OK) return status;
+ if ((status = parcel->readUint64(&vulkanDeviceFeaturesEnabled)) != OK) return status;
+ if ((status = parcel->readInt32Vector(&vulkanInstanceExtensions)) != OK) return status;
+ if ((status = parcel->readInt32Vector(&vulkanDeviceExtensions)) != OK) return status;
+
return OK;
}
@@ -114,6 +130,12 @@
StringAppendF(&result, "falsePrerotation = %d\n", falsePrerotation);
StringAppendF(&result, "gles1InUse = %d\n", gles1InUse);
StringAppendF(&result, "angleInUse = %d\n", angleInUse);
+ StringAppendF(&result, "createdGlesContext = %d\n", createdGlesContext);
+ StringAppendF(&result, "createdVulkanDevice = %d\n", createdVulkanDevice);
+ StringAppendF(&result, "createdVulkanSwapchain = %d\n", createdVulkanSwapchain);
+ StringAppendF(&result, "vulkanApiVersion = 0x%" PRIx32 "\n", vulkanApiVersion);
+ StringAppendF(&result, "vulkanDeviceFeaturesEnabled = 0x%" PRIx64 "\n",
+ vulkanDeviceFeaturesEnabled);
result.append("glDriverLoadingTime:");
for (int32_t loadingTime : glDriverLoadingTime) {
StringAppendF(&result, " %d", loadingTime);
@@ -129,6 +151,16 @@
StringAppendF(&result, " %d", loadingTime);
}
result.append("\n");
+ result.append("vulkanInstanceExtensions:");
+ for (int32_t extension : vulkanInstanceExtensions) {
+ StringAppendF(&result, " 0x%x", extension);
+ }
+ result.append("\n");
+ result.append("vulkanDeviceExtensions:");
+ for (int32_t extension : vulkanDeviceExtensions) {
+ StringAppendF(&result, " 0x%x", extension);
+ }
+ result.append("\n");
return result;
}
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 5f5f85a..46dd62d 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -259,6 +259,57 @@
sendGpuStatsLocked(api, isDriverLoaded, driverLoadingTime);
}
+// Hash function to calculate hash for null-terminated Vulkan extension names
+// We store hash values of the extensions, rather than the actual names or
+// indices to be able to support new extensions easily, avoid creating
+// a table of 'known' extensions inside Android and reduce the runtime overhead.
+static uint64_t calculateExtensionHash(const char* word) {
+ if (!word) {
+ return 0;
+ }
+ const size_t wordLen = strlen(word);
+ const uint32_t seed = 167;
+ uint64_t hash = 0;
+ for (size_t i = 0; i < wordLen; i++) {
+ hash = (hash * seed) + word[i];
+ }
+ return hash;
+}
+
+void GraphicsEnv::setVulkanInstanceExtensions(uint32_t enabledExtensionCount,
+ const char* const* ppEnabledExtensionNames) {
+ ATRACE_CALL();
+ if (enabledExtensionCount == 0 || ppEnabledExtensionNames == nullptr) {
+ return;
+ }
+
+ const uint32_t maxNumStats = android::GpuStatsAppInfo::MAX_NUM_EXTENSIONS;
+ uint64_t extensionHashes[maxNumStats];
+ const uint32_t numStats = std::min(enabledExtensionCount, maxNumStats);
+ for(uint32_t i = 0; i < numStats; i++) {
+ extensionHashes[i] = calculateExtensionHash(ppEnabledExtensionNames[i]);
+ }
+ setTargetStatsArray(android::GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION,
+ extensionHashes, numStats);
+}
+
+void GraphicsEnv::setVulkanDeviceExtensions(uint32_t enabledExtensionCount,
+ const char* const* ppEnabledExtensionNames) {
+ ATRACE_CALL();
+ if (enabledExtensionCount == 0 || ppEnabledExtensionNames == nullptr) {
+ return;
+ }
+
+ const uint32_t maxNumStats = android::GpuStatsAppInfo::MAX_NUM_EXTENSIONS;
+ uint64_t extensionHashes[maxNumStats];
+ const uint32_t numStats = std::min(enabledExtensionCount, maxNumStats);
+ for(uint32_t i = 0; i < numStats; i++) {
+ extensionHashes[i] = calculateExtensionHash(ppEnabledExtensionNames[i]);
+ }
+ setTargetStatsArray(android::GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION,
+ extensionHashes, numStats);
+}
+
static sp<IGpuService> getGpuService() {
static const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu"));
if (!binder) {
@@ -276,6 +327,11 @@
}
void GraphicsEnv::setTargetStats(const GpuStatsInfo::Stats stats, const uint64_t value) {
+ return setTargetStatsArray(stats, &value, 1);
+}
+
+void GraphicsEnv::setTargetStatsArray(const GpuStatsInfo::Stats stats, const uint64_t* values,
+ const uint32_t valueCount) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mStatsLock);
@@ -283,8 +339,8 @@
const sp<IGpuService> gpuService = getGpuService();
if (gpuService) {
- gpuService->setTargetStats(mGpuStats.appPackageName, mGpuStats.driverVersionCode, stats,
- value);
+ gpuService->setTargetStatsArray(mGpuStats.appPackageName, mGpuStats.driverVersionCode,
+ stats, values, valueCount);
}
}
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index fa25c55..ceb52f7 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -61,6 +61,14 @@
remote()->transact(BnGpuService::SET_TARGET_STATS, data, &reply, IBinder::FLAG_ONEWAY);
}
+ void setTargetStatsArray(const std::string& appPackageName, const uint64_t driverVersionCode,
+ const GpuStatsInfo::Stats stats, const uint64_t* values,
+ const uint32_t valueCount) override {
+ for (uint32_t i = 0; i < valueCount; i++) {
+ setTargetStats(appPackageName, driverVersionCode, stats, values[i]);
+ }
+ }
+
void setUpdatableDriverPath(const std::string& driverPath) override {
Parcel data, reply;
data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index 5b513d2..47607a0 100644
--- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -58,6 +58,9 @@
*/
class GpuStatsAppInfo : public Parcelable {
public:
+ // This limits the worst case number of extensions to be tracked.
+ static const uint32_t MAX_NUM_EXTENSIONS = 100;
+
GpuStatsAppInfo() = default;
GpuStatsAppInfo(const GpuStatsAppInfo&) = default;
virtual ~GpuStatsAppInfo() = default;
@@ -74,6 +77,13 @@
bool falsePrerotation = false;
bool gles1InUse = false;
bool angleInUse = false;
+ bool createdGlesContext = false;
+ bool createdVulkanDevice = false;
+ bool createdVulkanSwapchain = false;
+ uint32_t vulkanApiVersion = 0;
+ uint64_t vulkanDeviceFeaturesEnabled = 0;
+ std::vector<int32_t> vulkanInstanceExtensions = {};
+ std::vector<int32_t> vulkanDeviceExtensions = {};
std::chrono::time_point<std::chrono::system_clock> lastAccessTime;
};
@@ -101,6 +111,13 @@
CPU_VULKAN_IN_USE = 0,
FALSE_PREROTATION = 1,
GLES_1_IN_USE = 2,
+ CREATED_GLES_CONTEXT = 3,
+ CREATED_VULKAN_API_VERSION = 4,
+ CREATED_VULKAN_DEVICE = 5,
+ CREATED_VULKAN_SWAPCHAIN = 6,
+ VULKAN_DEVICE_FEATURES_ENABLED = 7,
+ VULKAN_INSTANCE_EXTENSION = 8,
+ VULKAN_DEVICE_EXTENSION = 9,
};
GpuStatsInfo() = default;
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 73d3196..b58a6d9 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -71,10 +71,19 @@
const std::string& appPackageName, const int32_t vulkanVersion);
// Set stats for target GpuStatsInfo::Stats type.
void setTargetStats(const GpuStatsInfo::Stats stats, const uint64_t value = 0);
+ // Set array of stats for target GpuStatsInfo::Stats type.
+ void setTargetStatsArray(const GpuStatsInfo::Stats stats, const uint64_t* values,
+ const uint32_t valueCount);
// Set which driver is intended to load.
void setDriverToLoad(GpuStatsInfo::Driver driver);
// Set which driver is actually loaded.
void setDriverLoaded(GpuStatsInfo::Api api, bool isDriverLoaded, int64_t driverLoadingTime);
+ // Set which instance extensions are enabled for the app.
+ void setVulkanInstanceExtensions(uint32_t enabledExtensionCount,
+ const char* const* ppEnabledExtensionNames);
+ // Set which device extensions are enabled for the app.
+ void setVulkanDeviceExtensions(uint32_t enabledExtensionCount,
+ const char* const* ppEnabledExtensionNames);
/*
* Api for Vk/GL layer injection. Presently, drivers enable certain
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index 2d59fa0..b708b0f 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -42,6 +42,10 @@
// set target stats.
virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t value = 0) = 0;
+ virtual void setTargetStatsArray(const std::string& appPackageName,
+ const uint64_t driverVersionCode,
+ const GpuStatsInfo::Stats stats, const uint64_t* values,
+ const uint32_t valueCount) = 0;
// setter and getter for updatable driver path.
virtual void setUpdatableDriverPath(const std::string& driverPath) = 0;
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index a77ca04..a0e75ff 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -42,6 +42,7 @@
namespace android {
+using gui::CallbackId;
using gui::DisplayCaptureArgs;
using gui::IDisplayEventConnection;
using gui::IRegionSamplingListener;
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 2b25b61..23d7d50 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -21,22 +21,11 @@
#include <optional>
#include <gui/ISurfaceComposer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
+#include <gui/ListenerStats.h>
#include <private/gui/ParcelUtils.h>
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
- ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
- ON_RELEASE_BUFFER,
- ON_TRANSACTION_QUEUE_STALLED,
- LAST = ON_TRANSACTION_QUEUE_STALLED,
-};
-
-} // Anonymous namespace
+namespace android::gui {
status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const {
status_t err = output->writeUint64(frameNumber);
@@ -274,60 +263,6 @@
return listenerStats;
}
-class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> {
-public:
- explicit BpTransactionCompletedListener(const sp<IBinder>& impl)
- : SafeBpInterface<ITransactionCompletedListener>(impl, "BpTransactionCompletedListener") {
- }
-
- ~BpTransactionCompletedListener() override;
-
- void onTransactionCompleted(ListenerStats stats) override {
- callRemoteAsync<decltype(&ITransactionCompletedListener::
- onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED,
- stats);
- }
-
- void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) override {
- callRemoteAsync<decltype(&ITransactionCompletedListener::
- onReleaseBuffer)>(Tag::ON_RELEASE_BUFFER, callbackId,
- releaseFence,
- currentMaxAcquiredBufferCount);
- }
-
- void onTransactionQueueStalled(const String8& reason) override {
- callRemoteAsync<
- decltype(&ITransactionCompletedListener::
- onTransactionQueueStalled)>(Tag::ON_TRANSACTION_QUEUE_STALLED,
- reason);
- }
-};
-
-// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpTransactionCompletedListener::~BpTransactionCompletedListener() = default;
-
-IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener");
-
-status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags) {
- if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
- return BBinder::onTransact(code, data, reply, flags);
- }
- auto tag = static_cast<Tag>(code);
- switch (tag) {
- case Tag::ON_TRANSACTION_COMPLETED:
- return callLocalAsync(data, reply,
- &ITransactionCompletedListener::onTransactionCompleted);
- case Tag::ON_RELEASE_BUFFER:
- return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer);
- case Tag::ON_TRANSACTION_QUEUE_STALLED:
- return callLocalAsync(data, reply,
- &ITransactionCompletedListener::onTransactionQueueStalled);
- }
-}
-
ListenerCallbacks ListenerCallbacks::filter(CallbackId::Type type) const {
std::vector<CallbackId> filteredCallbackIds;
for (const auto& callbackId : callbackIds) {
@@ -366,4 +301,4 @@
const ReleaseCallbackId ReleaseCallbackId::INVALID_ID = ReleaseCallbackId(0, 0);
-}; // namespace android
+}; // namespace android::gui
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 95962af..0d1a69b 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -51,6 +51,7 @@
namespace android {
+using gui::CallbackId;
using gui::FocusRequest;
using gui::WindowInfoHandle;
@@ -388,6 +389,27 @@
}
}
+void DisplayState::sanitize(int32_t permissions) {
+ if (what & DisplayState::eLayerStackChanged) {
+ if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~DisplayState::eLayerStackChanged;
+ ALOGE("Stripped attempt to set eLayerStackChanged in sanitize");
+ }
+ }
+ if (what & DisplayState::eDisplayProjectionChanged) {
+ if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~DisplayState::eDisplayProjectionChanged;
+ ALOGE("Stripped attempt to set eDisplayProjectionChanged in sanitize");
+ }
+ }
+ if (what & DisplayState::eSurfaceChanged) {
+ if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER)) {
+ what &= ~DisplayState::eSurfaceChanged;
+ ALOGE("Stripped attempt to set eSurfaceChanged in sanitize");
+ }
+ }
+}
+
void layer_state_t::sanitize(int32_t permissions) {
// TODO: b/109894387
//
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 1e43700..a2ed8aa 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -314,7 +314,8 @@
}
}
-void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
+binder::Status TransactionCompletedListener::onTransactionCompleted(
+ const ListenerStats& listenerStats) {
std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
std::multimap<int32_t, sp<JankDataListener>> jankListenersMap;
{
@@ -454,9 +455,10 @@
}
}
}
+ return binder::Status::ok();
}
-void TransactionCompletedListener::onTransactionQueueStalled(const String8& reason) {
+binder::Status TransactionCompletedListener::onTransactionQueueStalled(const std::string& reason) {
std::unordered_map<void*, std::function<void(const std::string&)>> callbackCopy;
{
std::scoped_lock<std::mutex> lock(mMutex);
@@ -465,6 +467,7 @@
for (auto const& it : callbackCopy) {
it.second(reason.c_str());
}
+ return binder::Status::ok();
}
void TransactionCompletedListener::addQueueStallListener(
@@ -478,9 +481,12 @@
mQueueStallListeners.erase(id);
}
-void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId,
- sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) {
+binder::Status TransactionCompletedListener::onReleaseBuffer(
+ const ReleaseCallbackId& callbackId,
+ const std::optional<os::ParcelFileDescriptor>& releaseFenceFd,
+ int32_t currentMaxAcquiredBufferCount) {
+ sp<Fence> releaseFence(releaseFenceFd ? new Fence(::dup(releaseFenceFd->get()))
+ : Fence::NO_FENCE);
ReleaseBufferCallback callback;
{
std::scoped_lock<std::mutex> lock(mMutex);
@@ -489,13 +495,14 @@
if (!callback) {
ALOGE("Could not call release buffer callback, buffer not found %s",
callbackId.to_string().c_str());
- return;
+ return binder::Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT);
}
std::optional<uint32_t> optionalMaxAcquiredBufferCount =
- currentMaxAcquiredBufferCount == UINT_MAX
+ static_cast<uint32_t>(currentMaxAcquiredBufferCount) == UINT_MAX
? std::nullopt
: std::make_optional<uint32_t>(currentMaxAcquiredBufferCount);
callback(callbackId, releaseFence, optionalMaxAcquiredBufferCount);
+ return binder::Status::ok();
}
ReleaseBufferCallback TransactionCompletedListener::popReleaseBufferCallbackLocked(
@@ -825,7 +832,11 @@
->mReleaseCallbackThread
.addReleaseCallback(state.bufferData->generateReleaseCallbackId(), fence);
} else {
- listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fence, UINT_MAX);
+ std::optional<os::ParcelFileDescriptor> fenceFd;
+ if (fence != Fence::NO_FENCE) {
+ fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(fence->get())));
+ }
+ listener->onReleaseBuffer(state.bufferData->generateReleaseCallbackId(), fenceFd, UINT_MAX);
}
}
@@ -971,14 +982,16 @@
class SyncCallback {
public:
- static void function(void* callbackContext, nsecs_t /* latchTime */,
- const sp<Fence>& /* presentFence */,
- const std::vector<SurfaceControlStats>& /* stats */) {
- if (!callbackContext) {
- ALOGE("failed to get callback context for SyncCallback");
- }
- SyncCallback* helper = static_cast<SyncCallback*>(callbackContext);
- LOG_ALWAYS_FATAL_IF(sem_post(&helper->mSemaphore), "sem_post failed");
+ static auto getCallback(std::shared_ptr<SyncCallback>& callbackContext) {
+ return [callbackContext](void* /* unused context */, nsecs_t /* latchTime */,
+ const sp<Fence>& /* presentFence */,
+ const std::vector<SurfaceControlStats>& /* stats */) {
+ if (!callbackContext) {
+ ALOGE("failed to get callback context for SyncCallback");
+ return;
+ }
+ LOG_ALWAYS_FATAL_IF(sem_post(&callbackContext->mSemaphore), "sem_post failed");
+ };
}
~SyncCallback() {
if (mInitialized) {
@@ -1013,10 +1026,11 @@
return mStatus;
}
- SyncCallback syncCallback;
+ std::shared_ptr<SyncCallback> syncCallback = std::make_shared<SyncCallback>();
if (synchronous) {
- syncCallback.init();
- addTransactionCommittedCallback(syncCallback.function, syncCallback.getContext());
+ syncCallback->init();
+ addTransactionCommittedCallback(SyncCallback::getCallback(syncCallback),
+ /*callbackContext=*/nullptr);
}
bool hasListenerCallbacks = !mListenerCallbacks.empty();
@@ -1092,7 +1106,7 @@
clear();
if (synchronous) {
- syncCallback.wait();
+ syncCallback->wait();
}
mStatus = NO_ERROR;
@@ -2144,6 +2158,12 @@
mStatus = NO_INIT;
}
+status_t SurfaceComposerClient::bootFinished() {
+ sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
+ binder::Status status = sf->bootFinished();
+ return statusTFromBinderStatus(status);
+}
+
sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, int32_t flags,
const sp<IBinder>& parentHandle,
@@ -2260,12 +2280,12 @@
return statusTFromBinderStatus(status);
}
-status_t SurfaceComposerClient::getStaticDisplayInfo(const sp<IBinder>& display,
+status_t SurfaceComposerClient::getStaticDisplayInfo(int64_t displayId,
ui::StaticDisplayInfo* outInfo) {
using Tag = android::gui::DeviceProductInfo::ManufactureOrModelDate::Tag;
gui::StaticDisplayInfo ginfo;
binder::Status status =
- ComposerServiceAIDL::getComposerService()->getStaticDisplayInfo(display, &ginfo);
+ ComposerServiceAIDL::getComposerService()->getStaticDisplayInfo(displayId, &ginfo);
if (status.isOk()) {
// convert gui::StaticDisplayInfo to ui::StaticDisplayInfo
outInfo->connectionType = static_cast<ui::DisplayConnectionType>(ginfo.connectionType);
@@ -2309,52 +2329,74 @@
return statusTFromBinderStatus(status);
}
-status_t SurfaceComposerClient::getDynamicDisplayInfo(const sp<IBinder>& display,
- ui::DynamicDisplayInfo* outInfo) {
+void SurfaceComposerClient::getDynamicDisplayInfoInternal(gui::DynamicDisplayInfo& ginfo,
+ ui::DynamicDisplayInfo*& outInfo) {
+ // convert gui::DynamicDisplayInfo to ui::DynamicDisplayInfo
+ outInfo->supportedDisplayModes.clear();
+ outInfo->supportedDisplayModes.reserve(ginfo.supportedDisplayModes.size());
+ for (const auto& mode : ginfo.supportedDisplayModes) {
+ ui::DisplayMode outMode;
+ outMode.id = mode.id;
+ outMode.resolution.width = mode.resolution.width;
+ outMode.resolution.height = mode.resolution.height;
+ outMode.xDpi = mode.xDpi;
+ outMode.yDpi = mode.yDpi;
+ outMode.refreshRate = mode.refreshRate;
+ outMode.appVsyncOffset = mode.appVsyncOffset;
+ outMode.sfVsyncOffset = mode.sfVsyncOffset;
+ outMode.presentationDeadline = mode.presentationDeadline;
+ outMode.group = mode.group;
+ std::transform(mode.supportedHdrTypes.begin(), mode.supportedHdrTypes.end(),
+ std::back_inserter(outMode.supportedHdrTypes),
+ [](const int32_t& value) { return static_cast<ui::Hdr>(value); });
+ outInfo->supportedDisplayModes.push_back(outMode);
+ }
+
+ outInfo->activeDisplayModeId = ginfo.activeDisplayModeId;
+ outInfo->renderFrameRate = ginfo.renderFrameRate;
+
+ outInfo->supportedColorModes.clear();
+ outInfo->supportedColorModes.reserve(ginfo.supportedColorModes.size());
+ for (const auto& cmode : ginfo.supportedColorModes) {
+ outInfo->supportedColorModes.push_back(static_cast<ui::ColorMode>(cmode));
+ }
+
+ outInfo->activeColorMode = static_cast<ui::ColorMode>(ginfo.activeColorMode);
+
+ std::vector<ui::Hdr> types;
+ types.reserve(ginfo.hdrCapabilities.supportedHdrTypes.size());
+ for (const auto& hdr : ginfo.hdrCapabilities.supportedHdrTypes) {
+ types.push_back(static_cast<ui::Hdr>(hdr));
+ }
+ outInfo->hdrCapabilities = HdrCapabilities(types, ginfo.hdrCapabilities.maxLuminance,
+ ginfo.hdrCapabilities.maxAverageLuminance,
+ ginfo.hdrCapabilities.minLuminance);
+
+ outInfo->autoLowLatencyModeSupported = ginfo.autoLowLatencyModeSupported;
+ outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported;
+ outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode;
+}
+
+status_t SurfaceComposerClient::getDynamicDisplayInfoFromId(int64_t displayId,
+ ui::DynamicDisplayInfo* outInfo) {
gui::DynamicDisplayInfo ginfo;
binder::Status status =
- ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfo(display, &ginfo);
+ ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfoFromId(displayId,
+ &ginfo);
if (status.isOk()) {
- // convert gui::DynamicDisplayInfo to ui::DynamicDisplayInfo
- outInfo->supportedDisplayModes.clear();
- outInfo->supportedDisplayModes.reserve(ginfo.supportedDisplayModes.size());
- for (const auto& mode : ginfo.supportedDisplayModes) {
- ui::DisplayMode outMode;
- outMode.id = mode.id;
- outMode.resolution.width = mode.resolution.width;
- outMode.resolution.height = mode.resolution.height;
- outMode.xDpi = mode.xDpi;
- outMode.yDpi = mode.yDpi;
- outMode.refreshRate = mode.refreshRate;
- outMode.appVsyncOffset = mode.appVsyncOffset;
- outMode.sfVsyncOffset = mode.sfVsyncOffset;
- outMode.presentationDeadline = mode.presentationDeadline;
- outMode.group = mode.group;
- outInfo->supportedDisplayModes.push_back(outMode);
- }
+ getDynamicDisplayInfoInternal(ginfo, outInfo);
+ }
+ return statusTFromBinderStatus(status);
+}
- outInfo->activeDisplayModeId = ginfo.activeDisplayModeId;
-
- outInfo->supportedColorModes.clear();
- outInfo->supportedColorModes.reserve(ginfo.supportedColorModes.size());
- for (const auto& cmode : ginfo.supportedColorModes) {
- outInfo->supportedColorModes.push_back(static_cast<ui::ColorMode>(cmode));
- }
-
- outInfo->activeColorMode = static_cast<ui::ColorMode>(ginfo.activeColorMode);
-
- std::vector<ui::Hdr> types;
- types.reserve(ginfo.hdrCapabilities.supportedHdrTypes.size());
- for (const auto& hdr : ginfo.hdrCapabilities.supportedHdrTypes) {
- types.push_back(static_cast<ui::Hdr>(hdr));
- }
- outInfo->hdrCapabilities = HdrCapabilities(types, ginfo.hdrCapabilities.maxLuminance,
- ginfo.hdrCapabilities.maxAverageLuminance,
- ginfo.hdrCapabilities.minLuminance);
-
- outInfo->autoLowLatencyModeSupported = ginfo.autoLowLatencyModeSupported;
- outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported;
- outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode;
+status_t SurfaceComposerClient::getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+ ui::DynamicDisplayInfo* outInfo) {
+ gui::DynamicDisplayInfo ginfo;
+ binder::Status status =
+ ComposerServiceAIDL::getComposerService()->getDynamicDisplayInfoFromToken(display,
+ &ginfo);
+ if (status.isOk()) {
+ getDynamicDisplayInfoInternal(ginfo, outInfo);
}
return statusTFromBinderStatus(status);
}
@@ -2362,7 +2404,8 @@
status_t SurfaceComposerClient::getActiveDisplayMode(const sp<IBinder>& display,
ui::DisplayMode* mode) {
ui::DynamicDisplayInfo info;
- status_t result = getDynamicDisplayInfo(display, &info);
+
+ status_t result = getDynamicDisplayInfoFromToken(display, &info);
if (result != NO_ERROR) {
return result;
}
@@ -2538,7 +2581,7 @@
gui::PullAtomData pad;
binder::Status status = ComposerServiceAIDL::getComposerService()->onPullAtom(atomId, &pad);
if (status.isOk()) {
- outData->assign((const char*)pad.data.data(), pad.data.size());
+ outData->assign(pad.data.begin(), pad.data.end());
*success = pad.success;
}
return statusTFromBinderStatus(status);
@@ -2823,7 +2866,11 @@
while (!callbackInfos.empty()) {
auto [callbackId, releaseFence] = callbackInfos.front();
- listener->onReleaseBuffer(callbackId, std::move(releaseFence), UINT_MAX);
+ std::optional<os::ParcelFileDescriptor> fenceFd;
+ if (releaseFence != Fence::NO_FENCE) {
+ fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get())));
+ }
+ listener->onReleaseBuffer(callbackId, fenceFd, UINT_MAX);
callbackInfos.pop();
}
diff --git a/libs/gui/aidl/android/gui/DisplayMode.aidl b/libs/gui/aidl/android/gui/DisplayMode.aidl
index 3cd77f8..ce30426 100644
--- a/libs/gui/aidl/android/gui/DisplayMode.aidl
+++ b/libs/gui/aidl/android/gui/DisplayMode.aidl
@@ -27,6 +27,7 @@
Size resolution;
float xDpi = 0.0f;
float yDpi = 0.0f;
+ int[] supportedHdrTypes;
float refreshRate = 0.0f;
long appVsyncOffset = 0;
diff --git a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
index 57e6081..3114929 100644
--- a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
+++ b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
@@ -27,6 +27,7 @@
List<DisplayMode> supportedDisplayModes;
int activeDisplayModeId;
+ float renderFrameRate;
int[] supportedColorModes;
int activeColorMode;
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 40410fb..a0b613c 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -62,8 +62,6 @@
* Signal that we're done booting.
* Requires ACCESS_SURFACE_FLINGER permission
*/
- // Note this must be the 1st method, so IBinder::FIRST_CALL_TRANSACTION
- // is assigned, as it is called from Java by ActivityManagerService.
void bootFinished();
/**
@@ -126,12 +124,14 @@
/**
* Gets immutable information about given physical display.
*/
- StaticDisplayInfo getStaticDisplayInfo(IBinder display);
+ StaticDisplayInfo getStaticDisplayInfo(long displayId);
/**
* Gets dynamic information about given physical display.
*/
- DynamicDisplayInfo getDynamicDisplayInfo(IBinder display);
+ DynamicDisplayInfo getDynamicDisplayInfoFromId(long displayId);
+
+ DynamicDisplayInfo getDynamicDisplayInfoFromToken(IBinder display);
DisplayPrimaries getDisplayNativePrimaries(IBinder display);
diff --git a/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl b/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl
new file mode 100644
index 0000000..dde4d38
--- /dev/null
+++ b/libs/gui/aidl/android/gui/ITransactionCompletedListener.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+import android.gui.ListenerStats;
+import android.gui.ReleaseCallbackId;
+
+/** @hide */
+oneway interface ITransactionCompletedListener {
+ void onTransactionCompleted(in ListenerStats stats);
+
+ void onReleaseBuffer(in ReleaseCallbackId callbackId,
+ in @nullable ParcelFileDescriptor releaseFenceFd,
+ int currentMaxAcquiredBufferCount);
+
+ void onTransactionQueueStalled(@utf8InCpp String name);
+}
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/gui/aidl/android/gui/ListenerStats.aidl
similarity index 80%
copy from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
copy to libs/gui/aidl/android/gui/ListenerStats.aidl
index d62891b..63248b2 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/gui/aidl/android/gui/ListenerStats.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-parcelable SingleDataParcelable{
- int data;
-}
\ No newline at end of file
+package android.gui;
+
+parcelable ListenerStats cpp_header "gui/ListenerStats.h";
diff --git a/libs/gui/aidl/android/gui/PullAtomData.aidl b/libs/gui/aidl/android/gui/PullAtomData.aidl
index 14d33c6..c307cef 100644
--- a/libs/gui/aidl/android/gui/PullAtomData.aidl
+++ b/libs/gui/aidl/android/gui/PullAtomData.aidl
@@ -18,6 +18,6 @@
/** @hide */
parcelable PullAtomData {
- @utf8InCpp String data;
+ byte[] data;
boolean success;
}
diff --git a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl b/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
similarity index 79%
copy from libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
copy to libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
index d62891b..c86de34 100644
--- a/libs/binder/tests/parcel_fuzzer/SingleDataParcelable.aidl
+++ b/libs/gui/aidl/android/gui/ReleaseCallbackId.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-parcelable SingleDataParcelable{
- int data;
-}
\ No newline at end of file
+package android.gui;
+
+parcelable ReleaseCallbackId cpp_header "gui/ReleaseCallbackId.h";
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index 9d1ee8f..8810e4e 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -79,9 +79,11 @@
(override));
MOCK_METHOD(binder::Status, getDisplayState, (const sp<IBinder>&, gui::DisplayState*),
(override));
- MOCK_METHOD(binder::Status, getStaticDisplayInfo, (const sp<IBinder>&, gui::StaticDisplayInfo*),
+ MOCK_METHOD(binder::Status, getStaticDisplayInfo, (int64_t, gui::StaticDisplayInfo*),
(override));
- MOCK_METHOD(binder::Status, getDynamicDisplayInfo,
+ MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromId, (int64_t, gui::DynamicDisplayInfo*),
+ (override));
+ MOCK_METHOD(binder::Status, getDynamicDisplayInfoFromToken,
(const sp<IBinder>&, gui::DynamicDisplayInfo*), (override));
MOCK_METHOD(binder::Status, getDisplayNativePrimaries,
(const sp<IBinder>&, gui::DisplayPrimaries*), (override));
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index d517e99..06a246e 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -23,11 +23,11 @@
#include <android/gui/IHdrLayerInfoListener.h>
#include <android/gui/IRegionSamplingListener.h>
#include <android/gui/IScreenCaptureListener.h>
+#include <android/gui/ITransactionCompletedListener.h>
#include <android/gui/ITunnelModeEnabledListener.h>
#include <android/gui/IWindowInfosListener.h>
#include <binder/IBinder.h>
#include <binder/IInterface.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/SpHash.h>
#include <math/vec4.h>
#include <stdint.h>
@@ -66,6 +66,7 @@
using gui::IDisplayEventConnection;
using gui::IRegionSamplingListener;
using gui::IScreenCaptureListener;
+using gui::ListenerCallbacks;
using gui::SpHash;
namespace gui {
@@ -102,7 +103,7 @@
// (sf vsync offset - debug.sf.early_phase_offset_ns). SurfaceFlinger will continue to be
// in the early configuration until it receives eEarlyWakeupEnd. These flags are
// expected to be used by WindowManager only and are guarded by
- // android.permission.ACCESS_SURFACE_FLINGER
+ // android.permission.WAKEUP_SURFACE_FLINGER
eEarlyWakeupStart = 0x08,
eEarlyWakeupEnd = 0x10,
eOneWay = 0x20
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 6ec6bd7..c5fdf82 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -21,10 +21,10 @@
#include <stdint.h>
#include <sys/types.h>
+#include <android/gui/ITransactionCompletedListener.h>
#include <android/gui/IWindowInfosReportedListener.h>
#include <android/native_window.h>
#include <gui/IGraphicBufferProducer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <math/mat4.h>
#include <android/gui/DropInputMode.h>
@@ -35,6 +35,7 @@
#include <gui/ISurfaceComposer.h>
#include <gui/LayerCaptureArgs.h>
#include <gui/LayerMetadata.h>
+#include <gui/ReleaseCallbackId.h>
#include <gui/SpHash.h>
#include <gui/SurfaceControl.h>
#include <gui/WindowInfo.h>
@@ -56,6 +57,9 @@
using gui::ISurfaceComposerClient;
using gui::LayerMetadata;
+using gui::ITransactionCompletedListener;
+using gui::ReleaseCallbackId;
+
struct client_cache_t {
wp<IBinder> token = nullptr;
uint64_t id;
@@ -359,6 +363,7 @@
DisplayState();
void merge(const DisplayState& other);
+ void sanitize(int32_t permissions);
uint32_t what = 0;
uint32_t flags = 0;
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ListenerStats.h
similarity index 81%
rename from libs/gui/include/gui/ITransactionCompletedListener.h
rename to libs/gui/include/gui/ListenerStats.h
index 453e8f3..3a12802 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ListenerStats.h
@@ -24,6 +24,8 @@
#include <binder/SafeInterface.h>
#include <gui/FrameTimestamps.h>
+#include <gui/ReleaseCallbackId.h>
+
#include <ui/Fence.h>
#include <utils/Timers.h>
@@ -32,10 +34,7 @@
#include <unordered_set>
#include <variant>
-namespace android {
-
-class ITransactionCompletedListener;
-class ListenerCallbacks;
+namespace android::gui {
class CallbackId : public Parcelable {
public:
@@ -54,30 +53,6 @@
std::size_t operator()(const CallbackId& key) const { return std::hash<int64_t>()(key.id); }
};
-class ReleaseCallbackId : public Parcelable {
-public:
- static const ReleaseCallbackId INVALID_ID;
-
- uint64_t bufferId;
- uint64_t framenumber;
- ReleaseCallbackId() {}
- ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber)
- : bufferId(bufferId), framenumber(framenumber) {}
- status_t writeToParcel(Parcel* output) const override;
- status_t readFromParcel(const Parcel* input) override;
-
- bool operator==(const ReleaseCallbackId& rhs) const {
- return bufferId == rhs.bufferId && framenumber == rhs.framenumber;
- }
- bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); }
- std::string to_string() const {
- if (*this == INVALID_ID) return "INVALID_ID";
-
- return "bufferId:" + std::to_string(bufferId) +
- " framenumber:" + std::to_string(framenumber);
- }
-};
-
struct ReleaseBufferCallbackIdHash {
std::size_t operator()(const ReleaseCallbackId& key) const {
return std::hash<uint64_t>()(key.bufferId);
@@ -186,27 +161,6 @@
std::vector<TransactionStats> transactionStats;
};
-class ITransactionCompletedListener : public IInterface {
-public:
- DECLARE_META_INTERFACE(TransactionCompletedListener)
-
- virtual void onTransactionCompleted(ListenerStats stats) = 0;
-
- virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) = 0;
-
- virtual void onTransactionQueueStalled(const String8& name) = 0;
-};
-
-class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
-public:
- BnTransactionCompletedListener()
- : SafeBnInterface<ITransactionCompletedListener>("BnTransactionCompletedListener") {}
-
- status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) override;
-};
-
class ListenerCallbacks {
public:
ListenerCallbacks(const sp<IBinder>& listener,
@@ -268,4 +222,4 @@
}
};
-} // namespace android
+} // namespace android::gui
diff --git a/libs/gui/include/gui/ReleaseCallbackId.h b/libs/gui/include/gui/ReleaseCallbackId.h
new file mode 100644
index 0000000..142ee5a
--- /dev/null
+++ b/libs/gui/include/gui/ReleaseCallbackId.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+
+#include <cstdint>
+
+namespace android::gui {
+
+class ReleaseCallbackId : public Parcelable {
+public:
+ static const ReleaseCallbackId INVALID_ID;
+
+ uint64_t bufferId;
+ uint64_t framenumber;
+ ReleaseCallbackId() {}
+ ReleaseCallbackId(uint64_t bufferId, uint64_t framenumber)
+ : bufferId(bufferId), framenumber(framenumber) {}
+ status_t writeToParcel(Parcel* output) const override;
+ status_t readFromParcel(const Parcel* input) override;
+
+ bool operator==(const ReleaseCallbackId& rhs) const {
+ return bufferId == rhs.bufferId && framenumber == rhs.framenumber;
+ }
+ bool operator!=(const ReleaseCallbackId& rhs) const { return !operator==(rhs); }
+ std::string to_string() const {
+ if (*this == INVALID_ID) return "INVALID_ID";
+
+ return "bufferId:" + std::to_string(bufferId) +
+ " framenumber:" + std::to_string(framenumber);
+ }
+};
+
+} // namespace android::gui
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2038f14..cc459c5 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -42,10 +42,13 @@
#include <android/gui/ISurfaceComposerClient.h>
+#include <android/gui/BnTransactionCompletedListener.h>
+
#include <gui/CpuConsumer.h>
#include <gui/ISurfaceComposer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
+#include <gui/ListenerStats.h>
+#include <gui/ReleaseCallbackId.h>
#include <gui/SurfaceControl.h>
#include <gui/WindowInfosListenerReporter.h>
#include <math/vec3.h>
@@ -59,11 +62,21 @@
class ITunnelModeEnabledListener;
class Region;
+using gui::BnTransactionCompletedListener;
+using gui::CallbackId;
+using gui::CallbackIdHash;
using gui::DisplayCaptureArgs;
+using gui::FrameEventHistoryStats;
using gui::IRegionSamplingListener;
using gui::ISurfaceComposerClient;
+using gui::ITransactionCompletedListener;
+using gui::JankData;
using gui::LayerCaptureArgs;
using gui::LayerMetadata;
+using gui::ListenerStats;
+using gui::ReleaseBufferCallbackIdHash;
+using gui::ReleaseCallbackId;
+using gui::SurfaceStats;
struct SurfaceControlStats {
SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime,
@@ -145,14 +158,17 @@
status_t linkToComposerDeath(const sp<IBinder::DeathRecipient>& recipient,
void* cookie = nullptr, uint32_t flags = 0);
+ // Notify the SurfaceComposerClient that the boot procedure has completed
+ static status_t bootFinished();
+
// Get transactional state of given display.
static status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*);
// Get immutable information about given physical display.
- static status_t getStaticDisplayInfo(const sp<IBinder>& display, ui::StaticDisplayInfo*);
+ static status_t getStaticDisplayInfo(int64_t, ui::StaticDisplayInfo*);
- // Get dynamic information about given physical display.
- static status_t getDynamicDisplayInfo(const sp<IBinder>& display, ui::DynamicDisplayInfo*);
+ // Get dynamic information about given physical display from display id
+ static status_t getDynamicDisplayInfoFromId(int64_t, ui::DynamicDisplayInfo*);
// Shorthand for the active display mode from getDynamicDisplayInfo().
// TODO(b/180391891): Update clients to use getDynamicDisplayInfo and remove this function.
@@ -714,6 +730,12 @@
ReleaseCallbackThread mReleaseCallbackThread;
private:
+ // Get dynamic information about given physical display from token
+ static status_t getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+ ui::DynamicDisplayInfo*);
+
+ static void getDynamicDisplayInfoInternal(gui::DynamicDisplayInfo& ginfo,
+ ui::DynamicDisplayInfo*& outInfo);
virtual void onFirstRef();
mutable Mutex mLock;
@@ -819,17 +841,17 @@
void setReleaseBufferCallback(const ReleaseCallbackId&, ReleaseBufferCallback);
// BnTransactionCompletedListener overrides
- void onTransactionCompleted(ListenerStats stats) override;
- void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence,
- uint32_t currentMaxAcquiredBufferCount) override;
+ binder::Status onTransactionCompleted(const ListenerStats& stats) override;
+ binder::Status onReleaseBuffer(const ReleaseCallbackId& callbackId,
+ const std::optional<os::ParcelFileDescriptor>& releaseFenceFd,
+ int32_t currentMaxAcquiredBufferCount) override;
+ binder::Status onTransactionQueueStalled(const std::string& reason) override;
void removeReleaseBufferCallback(const ReleaseCallbackId& callbackId);
// For Testing Only
static void setInstance(const sp<TransactionCompletedListener>&);
- void onTransactionQueueStalled(const String8& reason) override;
-
private:
ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&);
static sp<TransactionCompletedListener> sInstance;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 6d3b425..55242df 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -782,13 +782,18 @@
return binder::Status::ok();
}
- binder::Status getStaticDisplayInfo(const sp<IBinder>& /*display*/,
+ binder::Status getStaticDisplayInfo(int64_t /*displayId*/,
gui::StaticDisplayInfo* /*outInfo*/) override {
return binder::Status::ok();
}
- binder::Status getDynamicDisplayInfo(const sp<IBinder>& /*display*/,
- gui::DynamicDisplayInfo* /*outInfo*/) override {
+ binder::Status getDynamicDisplayInfoFromId(int64_t /*displayId*/,
+ gui::DynamicDisplayInfo* /*outInfo*/) override {
+ return binder::Status::ok();
+ }
+
+ binder::Status getDynamicDisplayInfoFromToken(const sp<IBinder>& /*display*/,
+ gui::DynamicDisplayInfo* /*outInfo*/) override {
return binder::Status::ok();
}
diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp
index b78fae3..dd7cbb5 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -339,7 +339,8 @@
DEFINE_KEYCODE(STYLUS_BUTTON_PRIMARY), \
DEFINE_KEYCODE(STYLUS_BUTTON_SECONDARY), \
DEFINE_KEYCODE(STYLUS_BUTTON_TERTIARY), \
- DEFINE_KEYCODE(STYLUS_BUTTON_TAIL)
+ DEFINE_KEYCODE(STYLUS_BUTTON_TAIL), \
+ DEFINE_KEYCODE(RECENT_APPS)
// NOTE: If you add a new axis here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list.
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 422e6e0..fa5c41f 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -43,7 +43,6 @@
// Enables debug output for mapping.
#define DEBUG_MAPPING 0
-
namespace android {
static const char* WHITESPACE = " \t\r";
@@ -93,6 +92,7 @@
: mType(other.mType),
mLoadFileName(other.mLoadFileName),
mLayoutOverlayApplied(other.mLayoutOverlayApplied),
+ mKeyRemapping(other.mKeyRemapping),
mKeysByScanCode(other.mKeysByScanCode),
mKeysByUsageCode(other.mKeysByUsageCode) {
for (size_t i = 0; i < other.mKeys.size(); i++) {
@@ -114,7 +114,7 @@
if (mLayoutOverlayApplied != other.mLayoutOverlayApplied) {
return false;
}
- if (mKeys.size() != other.mKeys.size() ||
+ if (mKeys.size() != other.mKeys.size() || mKeyRemapping.size() != other.mKeyRemapping.size() ||
mKeysByScanCode.size() != other.mKeysByScanCode.size() ||
mKeysByUsageCode.size() != other.mKeysByUsageCode.size()) {
return false;
@@ -131,22 +131,9 @@
}
}
- for (size_t i = 0; i < mKeysByScanCode.size(); i++) {
- if (mKeysByScanCode.keyAt(i) != other.mKeysByScanCode.keyAt(i)) {
- return false;
- }
- if (mKeysByScanCode.valueAt(i) != other.mKeysByScanCode.valueAt(i)) {
- return false;
- }
- }
-
- for (size_t i = 0; i < mKeysByUsageCode.size(); i++) {
- if (mKeysByUsageCode.keyAt(i) != other.mKeysByUsageCode.keyAt(i)) {
- return false;
- }
- if (mKeysByUsageCode.valueAt(i) != other.mKeysByUsageCode.valueAt(i)) {
- return false;
- }
+ if (mKeyRemapping != other.mKeyRemapping || mKeysByScanCode != other.mKeysByScanCode ||
+ mKeysByUsageCode != other.mKeysByUsageCode) {
+ return false;
}
return true;
@@ -258,14 +245,12 @@
}
}
- for (size_t i = 0; i < overlay.mKeysByScanCode.size(); i++) {
- mKeysByScanCode.replaceValueFor(overlay.mKeysByScanCode.keyAt(i),
- overlay.mKeysByScanCode.valueAt(i));
+ for (auto const& it : overlay.mKeysByScanCode) {
+ mKeysByScanCode.insert_or_assign(it.first, it.second);
}
- for (size_t i = 0; i < overlay.mKeysByUsageCode.size(); i++) {
- mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i),
- overlay.mKeysByUsageCode.valueAt(i));
+ for (auto const& it : overlay.mKeysByUsageCode) {
+ mKeysByUsageCode.insert_or_assign(it.first, it.second);
}
mLayoutOverlayApplied = true;
}
@@ -400,11 +385,26 @@
return true;
}
+void KeyCharacterMap::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) {
+ if (fromKeyCode == toKeyCode) {
+ mKeyRemapping.erase(fromKeyCode);
+#if DEBUG_MAPPING
+ ALOGD("addKeyRemapping: Cleared remapping forKeyCode=%d ~ Result Successful.", fromKeyCode);
+#endif
+ return;
+ }
+ mKeyRemapping.insert_or_assign(fromKeyCode, toKeyCode);
+#if DEBUG_MAPPING
+ ALOGD("addKeyRemapping: fromKeyCode=%d, toKeyCode=%d ~ Result Successful.", fromKeyCode,
+ toKeyCode);
+#endif
+}
+
status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const {
if (usageCode) {
- ssize_t index = mKeysByUsageCode.indexOfKey(usageCode);
- if (index >= 0) {
- *outKeyCode = mKeysByUsageCode.valueAt(index);
+ const auto it = mKeysByUsageCode.find(usageCode);
+ if (it != mKeysByUsageCode.end()) {
+ *outKeyCode = it->second;
#if DEBUG_MAPPING
ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
scanCode, usageCode, *outKeyCode);
@@ -413,9 +413,9 @@
}
}
if (scanCode) {
- ssize_t index = mKeysByScanCode.indexOfKey(scanCode);
- if (index >= 0) {
- *outKeyCode = mKeysByScanCode.valueAt(index);
+ const auto it = mKeysByScanCode.find(scanCode);
+ if (it != mKeysByScanCode.end()) {
+ *outKeyCode = it->second;
#if DEBUG_MAPPING
ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.",
scanCode, usageCode, *outKeyCode);
@@ -431,45 +431,59 @@
return NAME_NOT_FOUND;
}
-void KeyCharacterMap::tryRemapKey(int32_t keyCode, int32_t metaState,
- int32_t *outKeyCode, int32_t *outMetaState) const {
- *outKeyCode = keyCode;
- *outMetaState = metaState;
+int32_t KeyCharacterMap::applyKeyRemapping(int32_t fromKeyCode) const {
+ int32_t toKeyCode = fromKeyCode;
- const Behavior* behavior = getKeyBehavior(keyCode, metaState);
+ const auto it = mKeyRemapping.find(fromKeyCode);
+ if (it != mKeyRemapping.end()) {
+ toKeyCode = it->second;
+ }
+#if DEBUG_MAPPING
+ ALOGD("applyKeyRemapping: keyCode=%d ~ replacement keyCode=%d.", fromKeyCode, toKeyCode);
+#endif
+ return toKeyCode;
+}
+
+std::pair<int32_t, int32_t> KeyCharacterMap::applyKeyBehavior(int32_t fromKeyCode,
+ int32_t fromMetaState) const {
+ int32_t toKeyCode = fromKeyCode;
+ int32_t toMetaState = fromMetaState;
+
+ const Behavior* behavior = getKeyBehavior(fromKeyCode, fromMetaState);
if (behavior != nullptr) {
if (behavior->replacementKeyCode) {
- *outKeyCode = behavior->replacementKeyCode;
- int32_t newMetaState = metaState & ~behavior->metaState;
+ toKeyCode = behavior->replacementKeyCode;
+ toMetaState = fromMetaState & ~behavior->metaState;
// Reset dependent meta states.
if (behavior->metaState & AMETA_ALT_ON) {
- newMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON);
+ toMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON);
}
if (behavior->metaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) {
- newMetaState &= ~AMETA_ALT_ON;
+ toMetaState &= ~AMETA_ALT_ON;
}
if (behavior->metaState & AMETA_CTRL_ON) {
- newMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON);
+ toMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON);
}
if (behavior->metaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) {
- newMetaState &= ~AMETA_CTRL_ON;
+ toMetaState &= ~AMETA_CTRL_ON;
}
if (behavior->metaState & AMETA_SHIFT_ON) {
- newMetaState &= ~(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON);
+ toMetaState &= ~(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON);
}
if (behavior->metaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) {
- newMetaState &= ~AMETA_SHIFT_ON;
+ toMetaState &= ~AMETA_SHIFT_ON;
}
// ... and put universal bits back if needed
- *outMetaState = normalizeMetaState(newMetaState);
+ toMetaState = normalizeMetaState(toMetaState);
}
}
#if DEBUG_MAPPING
- ALOGD("tryRemapKey: keyCode=%d, metaState=0x%08x ~ "
- "replacement keyCode=%d, replacement metaState=0x%08x.",
- keyCode, metaState, *outKeyCode, *outMetaState);
+ ALOGD("applyKeyBehavior: keyCode=%d, metaState=0x%08x ~ "
+ "replacement keyCode=%d, replacement metaState=0x%08x.",
+ fromKeyCode, fromMetaState, toKeyCode, toMetaState);
#endif
+ return std::make_pair(toKeyCode, toMetaState);
}
bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const {
@@ -720,6 +734,18 @@
return nullptr;
}
}
+ size_t numKeyRemapping = parcel->readInt32();
+ if (parcel->errorCheck()) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < numKeyRemapping; i++) {
+ int32_t key = parcel->readInt32();
+ int32_t value = parcel->readInt32();
+ map->mKeyRemapping.insert_or_assign(key, value);
+ if (parcel->errorCheck()) {
+ return nullptr;
+ }
+ }
size_t numKeysByScanCode = parcel->readInt32();
if (parcel->errorCheck()) {
return nullptr;
@@ -727,7 +753,7 @@
for (size_t i = 0; i < numKeysByScanCode; i++) {
int32_t key = parcel->readInt32();
int32_t value = parcel->readInt32();
- map->mKeysByScanCode.add(key, value);
+ map->mKeysByScanCode.insert_or_assign(key, value);
if (parcel->errorCheck()) {
return nullptr;
}
@@ -739,7 +765,7 @@
for (size_t i = 0; i < numKeysByUsageCode; i++) {
int32_t key = parcel->readInt32();
int32_t value = parcel->readInt32();
- map->mKeysByUsageCode.add(key, value);
+ map->mKeysByUsageCode.insert_or_assign(key, value);
if (parcel->errorCheck()) {
return nullptr;
}
@@ -773,17 +799,23 @@
}
parcel->writeInt32(0);
}
+ size_t numKeyRemapping = mKeyRemapping.size();
+ parcel->writeInt32(numKeyRemapping);
+ for (auto const& [fromAndroidKeyCode, toAndroidKeyCode] : mKeyRemapping) {
+ parcel->writeInt32(fromAndroidKeyCode);
+ parcel->writeInt32(toAndroidKeyCode);
+ }
size_t numKeysByScanCode = mKeysByScanCode.size();
parcel->writeInt32(numKeysByScanCode);
- for (size_t i = 0; i < numKeysByScanCode; i++) {
- parcel->writeInt32(mKeysByScanCode.keyAt(i));
- parcel->writeInt32(mKeysByScanCode.valueAt(i));
+ for (auto const& [fromScanCode, toAndroidKeyCode] : mKeysByScanCode) {
+ parcel->writeInt32(fromScanCode);
+ parcel->writeInt32(toAndroidKeyCode);
}
size_t numKeysByUsageCode = mKeysByUsageCode.size();
parcel->writeInt32(numKeysByUsageCode);
- for (size_t i = 0; i < numKeysByUsageCode; i++) {
- parcel->writeInt32(mKeysByUsageCode.keyAt(i));
- parcel->writeInt32(mKeysByUsageCode.valueAt(i));
+ for (auto const& [fromUsageCode, toAndroidKeyCode] : mKeysByUsageCode) {
+ parcel->writeInt32(fromUsageCode);
+ parcel->writeInt32(toAndroidKeyCode);
}
}
#endif // __linux__
@@ -950,9 +982,9 @@
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
}
- KeyedVector<int32_t, int32_t>& map =
- mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
- if (map.indexOfKey(code) >= 0) {
+ std::map<int32_t, int32_t>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode;
+ const auto it = map.find(code);
+ if (it != map.end()) {
ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(),
mapUsage ? "usage" : "scan code", codeToken.string());
return BAD_VALUE;
@@ -971,7 +1003,7 @@
ALOGD("Parsed map key %s: code=%d, keyCode=%d.",
mapUsage ? "usage" : "scan code", code, keyCode);
#endif
- map.add(code, keyCode);
+ map.insert_or_assign(code, keyCode);
return NO_ERROR;
}
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
index 9f53a57..6995762 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrerrorcode.h
@@ -44,6 +44,7 @@
ERROR_JPEGR_DECODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 2,
ERROR_JPEGR_CALCULATION_ERROR = JPEGR_RUNTIME_ERROR_BASE - 3,
ERROR_JPEGR_METADATA_ERROR = JPEGR_RUNTIME_ERROR_BASE - 4,
+ ERROR_JPEGR_TONEMAP_ERROR = JPEGR_RUNTIME_ERROR_BASE - 5,
};
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
index 5597303..50ccdff 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h
@@ -30,8 +30,9 @@
// Transfer functions as defined for XMP metadata
typedef enum {
- JPEGR_TF_HLG = 0,
- JPEGR_TF_PQ = 1,
+ JPEGR_TF_LINEAR = 0,
+ JPEGR_TF_HLG = 1,
+ JPEGR_TF_PQ = 2,
} jpegr_transfer_function;
struct jpegr_info_struct {
@@ -129,6 +130,28 @@
class RecoveryMap {
public:
/*
+ * Encode API-0
+ * Compress JPEGR image from 10-bit HDR YUV.
+ *
+ * Tonemap the HDR input to a SDR image, generate recovery map from the HDR and SDR images,
+ * compress SDR YUV to 8-bit JPEG and append the recovery map to the end of the compressed
+ * JPEG.
+ * @param uncompressed_p010_image uncompressed HDR image in P010 color format
+ * @param hdr_tf transfer function of the HDR image
+ * @param dest destination of the compressed JPEGR image
+ * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
+ * the highest quality
+ * @param exif pointer to the exif metadata.
+ * @return NO_ERROR if encoding succeeds, error code if error occurs.
+ */
+ status_t encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest,
+ int quality,
+ jr_exif_ptr exif);
+
+ /*
+ * Encode API-1
* Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
*
* Generate recovery map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append
@@ -151,6 +174,7 @@
jr_exif_ptr exif);
/*
+ * Encode API-2
* Compress JPEGR image from 10-bit HDR YUV, 8-bit SDR YUV and compressed 8-bit JPEG.
*
* This method requires HAL Hardware JPEG encoder.
@@ -159,6 +183,8 @@
* compressed JPEG. HDR and SDR inputs must be the same resolution and color space.
* @param uncompressed_p010_image uncompressed HDR image in P010 color format
* @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
+ * Note: the SDR image must be the decoded version of the JPEG
+ * input
* @param compressed_jpeg_image compressed 8-bit JPEG image
* @param hdr_tf transfer function of the HDR image
* @param dest destination of the compressed JPEGR image
@@ -171,6 +197,7 @@
jr_compressed_ptr dest);
/*
+ * Encode API-3
* Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
*
* This method requires HAL Hardware JPEG encoder.
@@ -190,6 +217,7 @@
jr_compressed_ptr dest);
/*
+ * Decode API
* Decompress JPEGR image.
*
* The output JPEGR image is in RGBA_1010102 data format if decoding to HDR.
@@ -223,16 +251,6 @@
jr_info_ptr jpegr_info);
private:
/*
- * This method is called in the decoding pipeline. It will decode the recovery map.
- *
- * @param compressed_recovery_map compressed recovery map
- * @param dest decoded recover map
- * @return NO_ERROR if decoding succeeds, error code if error occurs.
- */
- status_t decompressRecoveryMap(jr_compressed_ptr compressed_recovery_map,
- jr_uncompressed_ptr dest);
-
- /*
* This method is called in the encoding pipeline. It will encode the recovery map.
*
* @param uncompressed_recovery_map uncompressed recovery map
@@ -317,45 +335,14 @@
jr_compressed_ptr dest);
/*
- * This method generates XMP metadata.
+ * This method will tone map a HDR image to an SDR image.
*
- * below is an example of the XMP metadata that this function generates where
- * secondary_image_length = 1000
- * range_scaling_factor = 1.25
- *
- * <x:xmpmeta
- * xmlns:x="adobe:ns:meta/"
- * x:xmptk="Adobe XMP Core 5.1.2">
- * <rdf:RDF
- * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- * <rdf:Description
- * xmlns:GContainer="http://ns.google.com/photos/1.0/container/">
- * <GContainer:Version>1</GContainer:Version>
- * <GContainer:RangeScalingFactor>1.25</GContainer:RangeScalingFactor>
- * <GContainer:Directory>
- * <rdf:Seq>
- * <rdf:li>
- * <GContainer:Item
- * Item:Semantic="Primary"
- * Item:Mime="image/jpeg"/>
- * </rdf:li>
- * <rdf:li>
- * <GContainer:Item
- * Item:Semantic="RecoveryMap"
- * Item:Mime="image/jpeg"
- * Item:Length="1000"/>
- * </rdf:li>
- * </rdf:Seq>
- * </GContainer:Directory>
- * </rdf:Description>
- * </rdf:RDF>
- * </x:xmpmeta>
- *
- * @param secondary_image_length length of secondary image
- * @param metadata JPEG/R metadata to encode as XMP
- * @return XMP metadata in type of string
+ * @param uncompressed_p010_image (input) uncompressed P010 image
+ * @param dest (output) tone mapping result as a YUV_420 image
+ * @return NO_ERROR if calculation succeeds, error code if error occurs.
*/
- std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
+ status_t toneMap(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr dest);
};
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
index e35f2d7..e61d0c4 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h
@@ -17,10 +17,11 @@
#ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
#define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
+#include <sstream>
#include <stdint.h>
+#include <string>
#include <cstdio>
-
namespace android::recoverymap {
struct jpegr_metadata;
@@ -35,6 +36,53 @@
*/
bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata);
+/*
+ * This method generates XMP metadata.
+ *
+ * below is an example of the XMP metadata that this function generates where
+ * secondary_image_length = 1000
+ * range_scaling_factor = 1.25
+ *
+ * <x:xmpmeta
+ * xmlns:x="adobe:ns:meta/"
+ * x:xmptk="Adobe XMP Core 5.1.2">
+ * <rdf:RDF
+ * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ * <rdf:Description
+ * xmlns:GContainer="http://ns.google.com/photos/1.0/container/"
+ * xmlns:RecoveryMap="http://ns.google.com/photos/1.0/recoverymap/">
+ * <GContainer:Version>1</GContainer:Version>
+ * <GContainer:Directory>
+ * <rdf:Seq>
+ * <rdf:li>
+ * <GContainer:Item
+ * Item:Semantic="Primary"
+ * Item:Mime="image/jpeg"
+ * RecoveryMap:Version=”1”
+ * RecoveryMap:RangeScalingFactor=”1.25”
+ * RecoveryMap:TransferFunction=”2”/>
+ * <RecoveryMap:HDR10Metadata
+ * // some attributes
+ * // some elements
+ * </RecoveryMap:HDR10Metadata>
+ * </rdf:li>
+ * <rdf:li>
+ * <GContainer:Item
+ * Item:Semantic="RecoveryMap"
+ * Item:Mime="image/jpeg"
+ * Item:Length="1000"/>
+ * </rdf:li>
+ * </rdf:Seq>
+ * </GContainer:Directory>
+ * </rdf:Description>
+ * </rdf:RDF>
+ * </x:xmpmeta>
+ *
+ * @param secondary_image_length length of secondary image
+ * @param metadata JPEG/R metadata to encode as XMP
+ * @return XMP metadata in type of string
+ */
+std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
}
#endif //ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index f7f3622..fafc319 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -21,7 +21,6 @@
#include <jpegrecoverymap/recoverymaputils.h>
#include <image_io/jpeg/jpeg_marker.h>
-#include <image_io/xml/xml_writer.h>
#include <image_io/jpeg/jpeg_info.h>
#include <image_io/jpeg/jpeg_scanner.h>
#include <image_io/jpeg/jpeg_info_builder.h>
@@ -65,19 +64,6 @@
};
/*
- * Helper function used for generating XMP metadata.
- *
- * @param prefix The prefix part of the name.
- * @param suffix The suffix part of the name.
- * @return A name of the form "prefix:suffix".
- */
-string Name(const string &prefix, const string &suffix) {
- std::stringstream ss;
- ss << prefix << ":" << suffix;
- return ss.str();
-}
-
-/*
* Helper function used for writing data to destination.
*
* @param destination destination of the data to be written.
@@ -96,6 +82,59 @@
return NO_ERROR;
}
+/* Encode API-0 */
+status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
+ jpegr_transfer_function hdr_tf,
+ jr_compressed_ptr dest,
+ int quality,
+ jr_exif_ptr /* exif */) {
+ if (uncompressed_p010_image == nullptr || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
+
+ if (quality < 0 || quality > 100) {
+ return ERROR_JPEGR_INVALID_INPUT_TYPE;
+ }
+
+ jpegr_metadata metadata;
+ metadata.version = kJpegrVersion;
+ metadata.transferFunction = hdr_tf;
+ if (hdr_tf == JPEGR_TF_PQ) {
+ metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
+ }
+
+ jpegr_uncompressed_struct uncompressed_yuv_420_image;
+ JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
+
+ jpegr_uncompressed_struct map;
+ JPEGR_CHECK(generateRecoveryMap(
+ &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
+ std::unique_ptr<uint8_t[]> map_data;
+ map_data.reset(reinterpret_cast<uint8_t*>(map.data));
+
+ jpegr_compressed_struct compressed_map;
+ compressed_map.maxLength = map.width * map.height;
+ unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
+ compressed_map.data = compressed_map_data.get();
+ JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
+
+ JpegEncoder jpeg_encoder;
+ // TODO: determine ICC data based on color gamut information
+ if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data,
+ uncompressed_yuv_420_image.width,
+ uncompressed_yuv_420_image.height, quality, nullptr, 0)) {
+ return ERROR_JPEGR_ENCODE_ERROR;
+ }
+ jpegr_compressed_struct jpeg;
+ jpeg.data = jpeg_encoder.getCompressedImagePtr();
+ jpeg.length = jpeg_encoder.getCompressedImageSize();
+
+ JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &metadata, dest));
+
+ return NO_ERROR;
+}
+
+/* Encode API-1 */
status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_uncompressed_ptr uncompressed_yuv_420_image,
jpegr_transfer_function hdr_tf,
@@ -152,6 +191,7 @@
return NO_ERROR;
}
+/* Encode API-2 */
status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_uncompressed_ptr uncompressed_yuv_420_image,
jr_compressed_ptr compressed_jpeg_image,
@@ -193,6 +233,7 @@
return NO_ERROR;
}
+/* Encode API-3 */
status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
jr_compressed_ptr compressed_jpeg_image,
jpegr_transfer_function hdr_tf,
@@ -262,7 +303,7 @@
return NO_ERROR;
}
-
+/* Decode API */
status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
jr_uncompressed_ptr dest,
jr_exif_ptr exif,
@@ -278,14 +319,24 @@
jpegr_metadata metadata;
JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map));
- jpegr_uncompressed_struct map;
- JPEGR_CHECK(decompressRecoveryMap(&compressed_map, &map));
JpegDecoder jpeg_decoder;
if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
return ERROR_JPEGR_DECODE_ERROR;
}
+ JpegDecoder recovery_map_decoder;
+ if (!recovery_map_decoder.decompressImage(compressed_map.data,
+ compressed_map.length)) {
+ return ERROR_JPEGR_DECODE_ERROR;
+ }
+
+ jpegr_uncompressed_struct map;
+ map.data = recovery_map_decoder.getDecompressedImagePtr();
+ map.width = recovery_map_decoder.getDecompressedImageWidth();
+ map.height = recovery_map_decoder.getDecompressedImageHeight();
+
+
jpegr_uncompressed_struct uncompressed_yuv_420_image;
uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
@@ -308,25 +359,6 @@
return NO_ERROR;
}
-status_t RecoveryMap::decompressRecoveryMap(jr_compressed_ptr compressed_recovery_map,
- jr_uncompressed_ptr dest) {
- if (compressed_recovery_map == nullptr || dest == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
-
- JpegDecoder jpeg_decoder;
- if (!jpeg_decoder.decompressImage(compressed_recovery_map->data,
- compressed_recovery_map->length)) {
- return ERROR_JPEGR_DECODE_ERROR;
- }
-
- dest->data = jpeg_decoder.getDecompressedImagePtr();
- dest->width = jpeg_decoder.getDecompressedImageWidth();
- dest->height = jpeg_decoder.getDecompressedImageHeight();
-
- return NO_ERROR;
-}
-
status_t RecoveryMap::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
jr_compressed_ptr dest) {
if (uncompressed_recovery_map == nullptr || dest == nullptr) {
@@ -392,6 +424,9 @@
ColorTransformFn hdrInvOetf = nullptr;
float hdr_white_nits = 0.0f;
switch (metadata->transferFunction) {
+ case JPEGR_TF_LINEAR:
+ hdrInvOetf = identityConversion;
+ break;
case JPEGR_TF_HLG:
hdrInvOetf = hlgInvOetf;
hdr_white_nits = kHlgMaxNits;
@@ -489,6 +524,9 @@
ColorTransformFn hdrOetf = nullptr;
switch (metadata->transferFunction) {
+ case JPEGR_TF_LINEAR:
+ hdrOetf = identityConversion;
+ break;
case JPEGR_TF_HLG:
hdrOetf = hlgOetf;
break;
@@ -507,7 +545,7 @@
float recovery = sampleMap(uncompressed_recovery_map, kMapDimensionScaleFactor, x, y);
Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata->rangeScalingFactor);
- Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
+ Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->rangeScalingFactor);
uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
size_t pixel_idx = x + y * width;
@@ -625,55 +663,20 @@
return NO_ERROR;
}
-string RecoveryMap::generateXmp(int secondary_image_length, jpegr_metadata& metadata) {
- const string kContainerPrefix = "GContainer";
- const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
- const string kItemPrefix = "Item";
- const string kRecoveryMap = "RecoveryMap";
- const string kDirectory = "Directory";
- const string kImageJpeg = "image/jpeg";
- const string kItem = "Item";
- const string kLength = "Length";
- const string kMime = "Mime";
- const string kPrimary = "Primary";
- const string kSemantic = "Semantic";
- const string kVersion = "Version";
+status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image,
+ jr_uncompressed_ptr dest) {
+ if (uncompressed_p010_image == nullptr || dest == nullptr) {
+ return ERROR_JPEGR_INVALID_NULL_PTR;
+ }
- const string kConDir = Name(kContainerPrefix, kDirectory);
- const string kContainerItem = Name(kContainerPrefix, kItem);
- const string kItemLength = Name(kItemPrefix, kLength);
- const string kItemMime = Name(kItemPrefix, kMime);
- const string kItemSemantic = Name(kItemPrefix, kSemantic);
+ dest->width = uncompressed_p010_image->width;
+ dest->height = uncompressed_p010_image->height;
+ unique_ptr<uint8_t[]> dest_data = make_unique<uint8_t[]>(dest->width * dest->height * 3 / 2);
+ dest->data = dest_data.get();
- const vector<string> kConDirSeq({kConDir, string("rdf:Seq")});
- const vector<string> kLiItem({string("rdf:li"), kContainerItem});
+ // TODO: Tone map algorighm here.
- std::stringstream ss;
- photos_editing_formats::image_io::XmlWriter writer(ss);
- writer.StartWritingElement("x:xmpmeta");
- writer.WriteXmlns("x", "adobe:ns:meta/");
- writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
- writer.StartWritingElement("rdf:RDF");
- writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
- writer.StartWritingElement("rdf:Description");
- writer.WriteXmlns(kContainerPrefix, kContainerUri);
- writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), metadata.version);
- writer.WriteElementAndContent(Name(kContainerPrefix, "rangeScalingFactor"),
- metadata.rangeScalingFactor);
- // TODO: determine structure for hdr10 metadata
- // TODO: write rest of metadata
- writer.StartWritingElements(kConDirSeq);
- size_t item_depth = writer.StartWritingElements(kLiItem);
- writer.WriteAttributeNameAndValue(kItemSemantic, kPrimary);
- writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg);
- writer.FinishWritingElementsToDepth(item_depth);
- writer.StartWritingElements(kLiItem);
- writer.WriteAttributeNameAndValue(kItemSemantic, kRecoveryMap);
- writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg);
- writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
- writer.FinishWriting();
-
- return ss.str();
+ return NO_ERROR;
}
} // namespace android::recoverymap
diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp
index e838f43..9ed2949 100644
--- a/libs/jpegrecoverymap/recoverymapmath.cpp
+++ b/libs/jpegrecoverymap/recoverymapmath.cpp
@@ -336,9 +336,9 @@
>> 6;
// Conversions include taking narrow-range into account.
- return {{{ static_cast<float>(y_uint) / 940.0f,
- (static_cast<float>(u_uint) - 64.0f) / 940.0f - 0.5f,
- (static_cast<float>(v_uint) - 64.0f) / 940.0f - 0.5f }}};
+ return {{{ (static_cast<float>(y_uint) - 64.0f) / 876.0f,
+ (static_cast<float>(u_uint) - 64.0f) / 896.0f - 0.5f,
+ (static_cast<float>(v_uint) - 64.0f) / 896.0f - 0.5f }}};
}
typedef Color (*getPixelFn)(jr_uncompressed_ptr, size_t, size_t);
diff --git a/libs/jpegrecoverymap/recoverymaputils.cpp b/libs/jpegrecoverymap/recoverymaputils.cpp
index fe46cba..63b25f7 100644
--- a/libs/jpegrecoverymap/recoverymaputils.cpp
+++ b/libs/jpegrecoverymap/recoverymaputils.cpp
@@ -17,26 +17,36 @@
#include <jpegrecoverymap/recoverymaputils.h>
#include <jpegrecoverymap/recoverymap.h>
#include <image_io/xml/xml_reader.h>
+#include <image_io/xml/xml_writer.h>
#include <image_io/base/message_handler.h>
#include <image_io/xml/xml_element_rules.h>
#include <image_io/xml/xml_handler.h>
#include <image_io/xml/xml_rule.h>
-#include <string>
-#include <sstream>
-
using namespace photos_editing_formats::image_io;
using namespace std;
namespace android::recoverymap {
+/*
+ * Helper function used for generating XMP metadata.
+ *
+ * @param prefix The prefix part of the name.
+ * @param suffix The suffix part of the name.
+ * @return A name of the form "prefix:suffix".
+ */
+string Name(const string &prefix, const string &suffix) {
+ std::stringstream ss;
+ ss << prefix << ":" << suffix;
+ return ss.str();
+}
// Extremely simple XML Handler - just searches for interesting elements
class XMPXmlHandler : public XmlHandler {
public:
XMPXmlHandler() : XmlHandler() {
- rangeScalingFactorState = NotStrarted;
+ gContainerItemState = NotStrarted;
}
enum ParseState {
@@ -48,11 +58,11 @@
virtual DataMatchResult StartElement(const XmlTokenContext& context) {
string val;
if (context.BuildTokenValue(&val)) {
- if (!val.compare(rangeScalingFactorName)) {
- rangeScalingFactorState = Started;
+ if (!val.compare(gContainerItemName)) {
+ gContainerItemState = Started;
} else {
- if (rangeScalingFactorState != Done) {
- rangeScalingFactorState = NotStrarted;
+ if (gContainerItemState != Done) {
+ gContainerItemState = NotStrarted;
}
}
}
@@ -60,24 +70,45 @@
}
virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
- if (rangeScalingFactorState == Started) {
- rangeScalingFactorState = Done;
+ if (gContainerItemState == Started) {
+ gContainerItemState = Done;
+ lastAttributeName = "";
}
return context.GetResult();
}
- virtual DataMatchResult ElementContent(const XmlTokenContext& context) {
+ virtual DataMatchResult AttributeName(const XmlTokenContext& context) {
string val;
- if (rangeScalingFactorState == Started) {
+ if (gContainerItemState == Started) {
if (context.BuildTokenValue(&val)) {
- rangeScalingFactorStr.assign(val);
+ if (!val.compare(rangeScalingFactorAttrName)) {
+ lastAttributeName = rangeScalingFactorAttrName;
+ } else if (!val.compare(transferFunctionAttrName)) {
+ lastAttributeName = transferFunctionAttrName;
+ } else {
+ lastAttributeName = "";
+ }
+ }
+ }
+ return context.GetResult();
+ }
+
+ virtual DataMatchResult AttributeValue(const XmlTokenContext& context) {
+ string val;
+ if (gContainerItemState == Started) {
+ if (context.BuildTokenValue(&val, true)) {
+ if (!lastAttributeName.compare(rangeScalingFactorAttrName)) {
+ rangeScalingFactorStr = val;
+ } else if (!lastAttributeName.compare(transferFunctionAttrName)) {
+ transferFunctionStr = val;
+ }
}
}
return context.GetResult();
}
bool getRangeScalingFactor(float* scaling_factor) {
- if (rangeScalingFactorState == Done) {
+ if (gContainerItemState == Done) {
stringstream ss(rangeScalingFactorStr);
float val;
if (ss >> val) {
@@ -92,19 +123,67 @@
}
bool getTransferFunction(jpegr_transfer_function* transfer_function) {
- *transfer_function = JPEGR_TF_HLG;
+ if (gContainerItemState == Done) {
+ stringstream ss(transferFunctionStr);
+ int val;
+ if (ss >> val) {
+ *transfer_function = static_cast<jpegr_transfer_function>(val);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
return true;
}
private:
- static const string rangeScalingFactorName;
+ static const string gContainerItemName;
+ static const string rangeScalingFactorAttrName;
+ static const string transferFunctionAttrName;
string rangeScalingFactorStr;
- ParseState rangeScalingFactorState;
+ string transferFunctionStr;
+ string lastAttributeName;
+ ParseState gContainerItemState;
};
-const string XMPXmlHandler::rangeScalingFactorName = "GContainer:rangeScalingFactor";
+const string XMPXmlHandler::gContainerItemName = "GContainer:Item";
+const string XMPXmlHandler::rangeScalingFactorAttrName = "RecoveryMap:RangeScalingFactor";
+const string XMPXmlHandler::transferFunctionAttrName = "RecoveryMap:TransferFunction";
+const string kContainerPrefix = "GContainer";
+const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
+const string kRecoveryMapUri = "http://ns.google.com/photos/1.0/recoverymap/";
+const string kItemPrefix = "Item";
+const string kRecoveryMap = "RecoveryMap";
+const string kDirectory = "Directory";
+const string kImageJpeg = "image/jpeg";
+const string kItem = "Item";
+const string kLength = "Length";
+const string kMime = "Mime";
+const string kPrimary = "Primary";
+const string kSemantic = "Semantic";
+const string kVersion = "Version";
+const string kHdr10Metadata = "HDR10Metadata";
+const string kSt2086Metadata = "ST2086Metadata";
+const string kSt2086Coordinate = "ST2086Coordinate";
+const string kSt2086CoordinateX = "ST2086CoordinateX";
+const string kSt2086CoordinateY = "ST2086CoordinateY";
+const string kSt2086Primary = "ST2086Primary";
+const int kSt2086PrimaryRed = 0;
+const int kSt2086PrimaryGreen = 1;
+const int kSt2086PrimaryBlue = 2;
+const int kSt2086PrimaryWhite = 3;
+const int kGContainerVersion = 1;
+
+const string kConDir = Name(kContainerPrefix, kDirectory);
+const string kContainerItem = Name(kContainerPrefix, kItem);
+const string kItemLength = Name(kItemPrefix, kLength);
+const string kItemMime = Name(kItemPrefix, kMime);
+const string kItemSemantic = Name(kItemPrefix, kSemantic);
+
bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) {
string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
@@ -150,4 +229,96 @@
return true;
}
+string generateXmp(int secondary_image_length, jpegr_metadata& metadata) {
+ const vector<string> kConDirSeq({kConDir, string("rdf:Seq")});
+ const vector<string> kLiItem({string("rdf:li"), kContainerItem});
+
+ std::stringstream ss;
+ photos_editing_formats::image_io::XmlWriter writer(ss);
+ writer.StartWritingElement("x:xmpmeta");
+ writer.WriteXmlns("x", "adobe:ns:meta/");
+ writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
+ writer.StartWritingElement("rdf:RDF");
+ writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
+ writer.StartWritingElement("rdf:Description");
+ writer.WriteXmlns(kContainerPrefix, kContainerUri);
+ writer.WriteXmlns(kRecoveryMap, kRecoveryMapUri);
+ writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), kGContainerVersion);
+ writer.StartWritingElements(kConDirSeq);
+ size_t item_depth = writer.StartWritingElements(kLiItem);
+ writer.WriteAttributeNameAndValue(kItemSemantic, kPrimary);
+ writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg);
+ writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kVersion), metadata.version);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "RangeScalingFactor"), metadata.rangeScalingFactor);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "TransferFunction"), metadata.transferFunction);
+ if (metadata.transferFunction == JPEGR_TF_PQ) {
+ writer.StartWritingElement(Name(kRecoveryMap, kHdr10Metadata));
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "HDR10MaxFALL"), metadata.hdr10Metadata.maxFALL);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "HDR10MaxCLL"), metadata.hdr10Metadata.maxCLL);
+ writer.StartWritingElement(Name(kRecoveryMap, kSt2086Metadata));
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "ST2086MaxLuminance"),
+ metadata.hdr10Metadata.st2086Metadata.maxLuminance);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, "ST2086MinLuminance"),
+ metadata.hdr10Metadata.st2086Metadata.minLuminance);
+
+ // red
+ writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate));
+ writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryRed);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateX),
+ metadata.hdr10Metadata.st2086Metadata.redPrimary.x);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateY),
+ metadata.hdr10Metadata.st2086Metadata.redPrimary.y);
+ writer.FinishWritingElement();
+
+ // green
+ writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate));
+ writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryGreen);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateX),
+ metadata.hdr10Metadata.st2086Metadata.greenPrimary.x);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateY),
+ metadata.hdr10Metadata.st2086Metadata.greenPrimary.y);
+ writer.FinishWritingElement();
+
+ // blue
+ writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate));
+ writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryBlue);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateX),
+ metadata.hdr10Metadata.st2086Metadata.bluePrimary.x);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateY),
+ metadata.hdr10Metadata.st2086Metadata.bluePrimary.y);
+ writer.FinishWritingElement();
+
+ // white
+ writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate));
+ writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryWhite);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateX),
+ metadata.hdr10Metadata.st2086Metadata.whitePoint.x);
+ writer.WriteAttributeNameAndValue(
+ Name(kRecoveryMap, kSt2086CoordinateY),
+ metadata.hdr10Metadata.st2086Metadata.whitePoint.y);
+ writer.FinishWritingElement();
+ }
+ writer.FinishWritingElementsToDepth(item_depth);
+ writer.StartWritingElements(kLiItem);
+ writer.WriteAttributeNameAndValue(kItemSemantic, kRecoveryMap);
+ writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg);
+ writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
+ writer.FinishWriting();
+
+ return ss.str();
+}
+
} // namespace android::recoverymap
\ No newline at end of file
diff --git a/libs/jpegrecoverymap/tests/data/raw_yuv420_image.yuv420 b/libs/jpegrecoverymap/tests/data/raw_yuv420_image.yuv420
new file mode 100644
index 0000000..c043da6
--- /dev/null
+++ b/libs/jpegrecoverymap/tests/data/raw_yuv420_image.yuv420
Binary files differ
diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
index ade33a0..c3c6fd4 100644
--- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp
@@ -15,15 +15,18 @@
*/
#include <jpegrecoverymap/recoverymap.h>
+#include <jpegrecoverymap/recoverymaputils.h>
#include <fcntl.h>
#include <fstream>
#include <gtest/gtest.h>
#include <utils/Log.h>
#define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010"
-#define RAW_P010_IMAGE_WIDTH 1280
-#define RAW_P010_IMAGE_HEIGHT 720
+#define RAW_YUV420_IMAGE "/sdcard/Documents/raw_yuv420_image.yuv420"
#define JPEG_IMAGE "/sdcard/Documents/jpeg_image.jpg"
+#define TEST_IMAGE_WIDTH 1280
+#define TEST_IMAGE_HEIGHT 720
+#define DEFAULT_JPEG_QUALITY 90
#define SAVE_ENCODING_RESULT true
#define SAVE_DECODING_RESULT true
@@ -39,6 +42,7 @@
virtual void TearDown();
struct jpegr_uncompressed_struct mRawP010Image;
+ struct jpegr_uncompressed_struct mRawYuv420Image;
struct jpegr_compressed_struct mJpegImage;
};
@@ -48,6 +52,7 @@
void RecoveryMapTest::SetUp() {}
void RecoveryMapTest::TearDown() {
free(mRawP010Image.data);
+ free(mRawYuv420Image.data);
free(mJpegImage.data);
}
@@ -85,6 +90,7 @@
TEST_F(RecoveryMapTest, build) {
// Force all of the recovery map lib to be linked by calling all public functions.
RecoveryMap recovery_map;
+ recovery_map.encodeJPEGR(nullptr, static_cast<jpegr_transfer_function>(0), nullptr, 0, nullptr);
recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
nullptr, 0, nullptr);
recovery_map.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
@@ -93,6 +99,201 @@
recovery_map.decodeJPEGR(nullptr, nullptr, nullptr, false);
}
+TEST_F(RecoveryMapTest, writeXmpThenRead) {
+ jpegr_metadata metadata_expected;
+ metadata_expected.transferFunction = JPEGR_TF_HLG;
+ metadata_expected.rangeScalingFactor = 1.25;
+ int length_expected = 1000;
+ std::string xmp = generateXmp(1000, metadata_expected);
+
+ jpegr_metadata metadata_read;
+ EXPECT_TRUE(getMetadataFromXMP(reinterpret_cast<uint8_t*>(xmp[0]), xmp.size(), &metadata_read));
+ ASSERT_EQ(metadata_expected.transferFunction, metadata_read.transferFunction);
+ ASSERT_EQ(metadata_expected.rangeScalingFactor, metadata_read.rangeScalingFactor);
+
+}
+
+/* Test Encode API-0 and decode */
+// TODO: enable when tonemapper is ready.
+//TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) {
+// int ret;
+//
+// // Load input files.
+// if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+// FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+// }
+// mRawP010Image.width = TEST_IMAGE_WIDTH;
+// mRawP010Image.height = TEST_IMAGE_HEIGHT;
+// mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+//
+// RecoveryMap recoveryMap;
+//
+// jpegr_compressed_struct jpegR;
+// jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
+// jpegR.data = malloc(jpegR.maxLength);
+// ret = recoveryMap.encodeJPEGR(
+// &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, 90, nullptr);
+// if (ret != OK) {
+// FAIL() << "Error code is " << ret;
+// }
+// if (SAVE_ENCODING_RESULT) {
+// // Output image data to file
+// std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+// std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+// if (!imageFile.is_open()) {
+// ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+// }
+// imageFile.write((const char*)jpegR.data, jpegR.length);
+// }
+//
+// jpegr_uncompressed_struct decodedJpegR;
+// int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+// decodedJpegR.data = malloc(decodedJpegRSize);
+// ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+// if (ret != OK) {
+// FAIL() << "Error code is " << ret;
+// }
+// if (SAVE_DECODING_RESULT) {
+// // Output image data to file
+// std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+// std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+// if (!imageFile.is_open()) {
+// ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+// }
+// imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+// }
+//
+// free(jpegR.data);
+// free(decodedJpegR.data);
+//}
+
+/* Test Encode API-1 and decode */
+TEST_F(RecoveryMapTest, encodeFromRawHdrAndSdrThenDecode) {
+ int ret;
+
+ // Load input files.
+ if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawP010Image.width = TEST_IMAGE_WIDTH;
+ mRawP010Image.height = TEST_IMAGE_HEIGHT;
+ mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+
+ if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawYuv420Image.width = TEST_IMAGE_WIDTH;
+ mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
+ mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+
+ RecoveryMap recoveryMap;
+
+ jpegr_compressed_struct jpegR;
+ jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
+ jpegR.data = malloc(jpegR.maxLength);
+ ret = recoveryMap.encodeJPEGR(
+ &mRawP010Image, &mRawYuv420Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR,
+ DEFAULT_JPEG_QUALITY, nullptr);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_ENCODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)jpegR.data, jpegR.length);
+ }
+
+ jpegr_uncompressed_struct decodedJpegR;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ decodedJpegR.data = malloc(decodedJpegRSize);
+ ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_DECODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+ }
+
+ free(jpegR.data);
+ free(decodedJpegR.data);
+}
+
+/* Test Encode API-2 and decode */
+TEST_F(RecoveryMapTest, encodeFromRawHdrAndSdrAndJpegThenDecode) {
+ int ret;
+
+ // Load input files.
+ if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawP010Image.width = TEST_IMAGE_WIDTH;
+ mRawP010Image.height = TEST_IMAGE_HEIGHT;
+ mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
+
+ if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
+ FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
+ }
+ mRawYuv420Image.width = TEST_IMAGE_WIDTH;
+ mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
+ mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+
+ if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
+ FAIL() << "Load file " << JPEG_IMAGE << " failed";
+ }
+ mJpegImage.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
+
+ RecoveryMap recoveryMap;
+
+ jpegr_compressed_struct jpegR;
+ jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
+ jpegR.data = malloc(jpegR.maxLength);
+ ret = recoveryMap.encodeJPEGR(
+ &mRawP010Image, &mRawYuv420Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_ENCODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)jpegR.data, jpegR.length);
+ }
+
+ jpegr_uncompressed_struct decodedJpegR;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
+ decodedJpegR.data = malloc(decodedJpegRSize);
+ ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
+ if (ret != OK) {
+ FAIL() << "Error code is " << ret;
+ }
+ if (SAVE_DECODING_RESULT) {
+ // Output image data to file
+ std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
+ std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
+ if (!imageFile.is_open()) {
+ ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
+ }
+ imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
+ }
+
+ free(jpegR.data);
+ free(decodedJpegR.data);
+}
+
+/* Test Encode API-3 and decode */
TEST_F(RecoveryMapTest, encodeFromJpegThenDecode) {
int ret;
@@ -100,8 +301,8 @@
if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
}
- mRawP010Image.width = RAW_P010_IMAGE_WIDTH;
- mRawP010Image.height = RAW_P010_IMAGE_HEIGHT;
+ mRawP010Image.width = TEST_IMAGE_WIDTH;
+ mRawP010Image.height = TEST_IMAGE_HEIGHT;
mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
@@ -112,7 +313,7 @@
RecoveryMap recoveryMap;
jpegr_compressed_struct jpegR;
- jpegR.maxLength = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * sizeof(uint8_t);
+ jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
jpegR.data = malloc(jpegR.maxLength);
ret = recoveryMap.encodeJPEGR(
&mRawP010Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
@@ -130,7 +331,7 @@
}
jpegr_uncompressed_struct decodedJpegR;
- int decodedJpegRSize = RAW_P010_IMAGE_WIDTH * RAW_P010_IMAGE_HEIGHT * 4;
+ int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
decodedJpegR.data = malloc(decodedJpegRSize);
ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
if (ret != OK) {
diff --git a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
index 169201c..f8dd490 100644
--- a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
@@ -36,9 +36,9 @@
}
Color P010(uint16_t y, uint16_t u, uint16_t v) {
- return {{{ static_cast<float>(y) / 940.0f,
- (static_cast<float>(u) - 64.0f) / 940.0f - 0.5f,
- (static_cast<float>(v) - 64.0f) / 940.0f - 0.5f }}};
+ return {{{ (static_cast<float>(y) - 64.0f) / 876.0f,
+ (static_cast<float>(u) - 64.0f) / 896.0f - 0.5f,
+ (static_cast<float>(v) - 64.0f) / 896.0f - 0.5f }}};
}
float Map(uint8_t e) {
@@ -821,7 +821,6 @@
bt2100Luminance(RgbBlue()) * kPqMaxNits, LuminanceEpsilon());
}
-//Color Recover(Color yuv_gamma, float recovery, float range_scaling_factor) {
TEST_F(RecoveryMapMathTest, ApplyMap) {
EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, 8.0f),
RgbWhite() * 8.0f);
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 60328e4..bf0805b 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -117,15 +117,6 @@
#define CHECK_NOT_NULL(name) \
LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
-namespace {
-
-sp<IBinder> getToken(ADisplay* display) {
- DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
- return SurfaceComposerClient::getPhysicalDisplayToken(impl->id);
-}
-
-} // namespace
-
namespace android {
int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) {
@@ -139,10 +130,9 @@
ui::DisplayConnectionType displayConnectionTypes[size];
int numModes = 0;
for (int i = 0; i < size; ++i) {
- const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
-
ui::StaticDisplayInfo staticInfo;
- if (const status_t status = SurfaceComposerClient::getStaticDisplayInfo(token, &staticInfo);
+ if (const status_t status =
+ SurfaceComposerClient::getStaticDisplayInfo(ids[i].value, &staticInfo);
status != OK) {
return status;
}
@@ -150,7 +140,7 @@
ui::DynamicDisplayInfo dynamicInfo;
if (const status_t status =
- SurfaceComposerClient::getDynamicDisplayInfo(token, &dynamicInfo);
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(ids[i].value, &dynamicInfo);
status != OK) {
return status;
}
@@ -260,14 +250,15 @@
int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) {
CHECK_NOT_NULL(display);
- sp<IBinder> token = getToken(display);
ui::DynamicDisplayInfo info;
- if (const auto status = SurfaceComposerClient::getDynamicDisplayInfo(token, &info);
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+
+ if (const auto status =
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(impl->id.value, &info);
status != OK) {
return status;
}
- DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
for (size_t i = 0; i < impl->numConfigs; i++) {
auto* config = impl->configs + i;
if (config->id == info.activeDisplayModeId) {
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index c345385..b7b2926 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -232,15 +232,6 @@
return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy);
}
-int32_t ANativeWindow_clearFrameRate(ANativeWindow* window) {
- if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
- return -EINVAL;
- }
- return native_window_set_frame_rate(window, 0,
- ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
- ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
-}
-
/**************************************************************************************************
* vndk-stable
**************************************************************************************************/
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index a27e3dd..be6623e 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -372,8 +372,12 @@
*
* \return 0 for success, -EINVAL if the window value is invalid.
*/
-int32_t ANativeWindow_clearFrameRate(ANativeWindow* window)
- __INTRODUCED_IN(__ANDROID_API_U__);
+inline int32_t ANativeWindow_clearFrameRate(ANativeWindow* window)
+ __INTRODUCED_IN(__ANDROID_API_U__) {
+ return ANativeWindow_setFrameRateWithChangeStrategy(window, 0,
+ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT,
+ ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
+}
#ifdef __cplusplus
}
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index e1ee490..c2fd6ef 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -51,7 +51,6 @@
ANativeWindow_setDequeueTimeout; # systemapi # introduced=30
ANativeWindow_setFrameRate; # introduced=30
ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31
- ANativeWindow_clearFrameRate; # introduced=UpsideDownCake
ANativeWindow_setSharedBufferMode; # llndk
ANativeWindow_setSwapInterval; # llndk
ANativeWindow_setUsage; # llndk
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 04e24ed..8d19c45 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -111,9 +111,23 @@
],
}
+// Used to consolidate and simplify pulling Skia & Skia deps into targets that depend on
+// librenderengine. This allows shared deps to be deduplicated (e.g. Perfetto), which doesn't seem
+// possible if libskia_renderengine is just pulled into librenderengine via whole_static_libs.
+cc_defaults {
+ name: "librenderengine_deps",
+ defaults: ["skia_renderengine_deps"],
+ static_libs: ["libskia_renderengine"],
+}
+
+// Note: if compilation fails when adding librenderengine as a dependency, try adding
+// librenderengine_deps to the defaults field of your dependent target.
cc_library_static {
name: "librenderengine",
- defaults: ["librenderengine_defaults"],
+ defaults: [
+ "librenderengine_defaults",
+ "librenderengine_deps",
+ ],
double_loadable: true,
cflags: [
"-fvisibility=hidden",
@@ -132,7 +146,6 @@
include_dirs: [
"external/skia/src/gpu",
],
- whole_static_libs: ["libskia_renderengine"],
lto: {
thin: true,
},
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
index afbe6cf..55c34cd 100644
--- a/libs/renderengine/benchmark/Android.bp
+++ b/libs/renderengine/benchmark/Android.bp
@@ -25,7 +25,7 @@
name: "librenderengine_bench",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
- "skia_deps",
+ "librenderengine_deps",
"surfaceflinger_defaults",
],
srcs: [
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index 6f328d7..50e166d 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -25,7 +25,7 @@
name: "librenderengine_test",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
- "skia_deps",
+ "librenderengine_deps",
"surfaceflinger_defaults",
],
test_suites: ["device-tests"],
diff --git a/libs/ui/include/ui/DisplayMode.h b/libs/ui/include/ui/DisplayMode.h
index a2791a6..65a8769 100644
--- a/libs/ui/include/ui/DisplayMode.h
+++ b/libs/ui/include/ui/DisplayMode.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <type_traits>
+#include <ui/GraphicTypes.h>
#include <ui/Size.h>
#include <utils/Flattenable.h>
#include <utils/Timers.h>
@@ -34,6 +35,7 @@
ui::Size resolution;
float xDpi = 0;
float yDpi = 0;
+ std::vector<ui::Hdr> supportedHdrTypes;
float refreshRate = 0;
nsecs_t appVsyncOffset = 0;
diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h
index 8c9fe4c..0b77754 100644
--- a/libs/ui/include/ui/DynamicDisplayInfo.h
+++ b/libs/ui/include/ui/DynamicDisplayInfo.h
@@ -35,6 +35,7 @@
// we can't use size_t because it may have different width
// in the client process.
ui::DisplayModeId activeDisplayModeId;
+ float renderFrameRate;
std::vector<ui::ColorMode> supportedColorModes;
ui::ColorMode activeColorMode;
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index 8661c36..1775d39 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -20,6 +20,7 @@
#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
#include <aidl/android/hardware/graphics/common/Compression.h>
#include <aidl/android/hardware/graphics/common/Cta861_3.h>
+#include <aidl/android/hardware/graphics/common/Hdr.h>
#include <aidl/android/hardware/graphics/common/Interlaced.h>
#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
#include <aidl/android/hardware/graphics/common/Smpte2086.h>
@@ -42,7 +43,6 @@
using android::hardware::graphics::common::V1_1::RenderIntent;
using android::hardware::graphics::common::V1_2::ColorMode;
using android::hardware::graphics::common::V1_2::Dataspace;
-using android::hardware::graphics::common::V1_2::Hdr;
using android::hardware::graphics::common::V1_2::PixelFormat;
/**
@@ -50,6 +50,7 @@
*/
using aidl::android::hardware::graphics::common::BlendMode;
using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::Hdr;
using aidl::android::hardware::graphics::common::PlaneLayout;
using aidl::android::hardware::graphics::common::Smpte2086;
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 7619a50..0527c8a 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -937,6 +937,8 @@
android::GraphicsEnv::getInstance().setTargetStats(
android::GpuStatsInfo::Stats::GLES_1_IN_USE);
}
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::CREATED_GLES_CONTEXT);
egl_context_t* c = new egl_context_t(dpy, context, config, cnx, version);
return c;
}
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 7b9782f..aaa8c18 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -82,6 +82,12 @@
mGpuStats->insertTargetStats(appPackageName, driverVersionCode, stats, value);
}
+void GpuService::setTargetStatsArray(const std::string& appPackageName,
+ const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
+ const uint64_t* values, const uint32_t valueCount) {
+ mGpuStats->insertTargetStatsArray(appPackageName, driverVersionCode, stats, values, valueCount);
+}
+
void GpuService::setUpdatableDriverPath(const std::string& driverPath) {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index d7313d1..e7e0cba 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -56,6 +56,9 @@
int64_t driverLoadingTime) override;
void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t value) override;
+ void setTargetStatsArray(const std::string& appPackageName,
+ const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
+ const uint64_t* values, const uint32_t valueCount) override;
void setUpdatableDriverPath(const std::string& driverPath) override;
std::string getUpdatableDriverPath() override;
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index d033453..f06a045 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -175,29 +175,83 @@
void GpuStats::insertTargetStats(const std::string& appPackageName,
const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
- const uint64_t /*value*/) {
+ const uint64_t value) {
+ return insertTargetStatsArray(appPackageName, driverVersionCode, stats, &value, 1);
+}
+
+void GpuStats::insertTargetStatsArray(const std::string& appPackageName,
+ const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
+ const uint64_t* values, const uint32_t valueCount) {
ATRACE_CALL();
const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
std::lock_guard<std::mutex> lock(mLock);
registerStatsdCallbacksIfNeeded();
- if (!mAppStats.count(appStatsKey)) {
+
+ const auto foundApp = mAppStats.find(appStatsKey);
+ if (foundApp == mAppStats.end()) {
return;
}
- switch (stats) {
- case GpuStatsInfo::Stats::CPU_VULKAN_IN_USE:
- mAppStats[appStatsKey].cpuVulkanInUse = true;
- break;
- case GpuStatsInfo::Stats::FALSE_PREROTATION:
- mAppStats[appStatsKey].falsePrerotation = true;
- break;
- case GpuStatsInfo::Stats::GLES_1_IN_USE:
- mAppStats[appStatsKey].gles1InUse = true;
- break;
- default:
- break;
+ GpuStatsAppInfo& targetAppStats = foundApp->second;
+
+ if (stats == GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION
+ || stats == GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION) {
+ // Handle extension arrays separately as we need to store a unique set of them
+ // in the stats vector. Storing in std::set<> is not efficient for serialization tasks.
+ std::vector<int32_t>& targetVec =
+ (stats == GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION) ?
+ targetAppStats.vulkanInstanceExtensions :
+ targetAppStats.vulkanDeviceExtensions;
+ const bool addAll = (targetVec.size() == 0);
+ targetVec.reserve(valueCount);
+
+ // Add new extensions into the set
+ for(uint32_t i = 0;
+ (i < valueCount) && (targetVec.size() < GpuStatsAppInfo::MAX_NUM_EXTENSIONS);
+ i++) {
+ const int32_t extVal = int32_t(values[i] & 0xFFFFFFFF);
+ if (addAll
+ || std::find(targetVec.cbegin(), targetVec.cend(), extVal) == targetVec.cend()) {
+ targetVec.push_back(extVal);
+ }
+ }
+ }
+ else {
+ // Handle other type of stats info events
+ for(uint32_t i = 0; i < valueCount; i++) {
+ const uint64_t value = values[i];
+ switch (stats) {
+ case GpuStatsInfo::Stats::CPU_VULKAN_IN_USE:
+ targetAppStats.cpuVulkanInUse = true;
+ break;
+ case GpuStatsInfo::Stats::FALSE_PREROTATION:
+ targetAppStats.falsePrerotation = true;
+ break;
+ case GpuStatsInfo::Stats::GLES_1_IN_USE:
+ targetAppStats.gles1InUse = true;
+ break;
+ case GpuStatsInfo::Stats::CREATED_GLES_CONTEXT:
+ targetAppStats.createdGlesContext = true;
+ break;
+ case GpuStatsInfo::Stats::CREATED_VULKAN_DEVICE:
+ targetAppStats.createdVulkanDevice = true;
+ break;
+ case GpuStatsInfo::Stats::CREATED_VULKAN_API_VERSION:
+ targetAppStats.vulkanApiVersion = uint32_t(value & 0xffffffff);
+ break;
+ case GpuStatsInfo::Stats::CREATED_VULKAN_SWAPCHAIN:
+ targetAppStats.createdVulkanSwapchain = true;
+ break;
+ case GpuStatsInfo::Stats::VULKAN_DEVICE_FEATURES_ENABLED:
+ // Merge all requested feature bits together for this app
+ targetAppStats.vulkanDeviceFeaturesEnabled |= value;
+ break;
+ default:
+ break;
+ }
+ }
}
}
@@ -347,7 +401,14 @@
ele.second.cpuVulkanInUse,
ele.second.falsePrerotation,
ele.second.gles1InUse,
- ele.second.angleInUse);
+ ele.second.angleInUse,
+ ele.second.createdGlesContext,
+ ele.second.createdVulkanDevice,
+ ele.second.createdVulkanSwapchain,
+ ele.second.vulkanApiVersion,
+ ele.second.vulkanDeviceFeaturesEnabled,
+ ele.second.vulkanInstanceExtensions,
+ ele.second.vulkanDeviceExtensions);
}
}
diff --git a/services/gpuservice/gpustats/include/gpustats/GpuStats.h b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
index 2aba651..22c64db 100644
--- a/services/gpuservice/gpustats/include/gpustats/GpuStats.h
+++ b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
@@ -41,11 +41,14 @@
// Insert target stats into app stats or potentially global stats as well.
void insertTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t value);
+ void insertTargetStatsArray(const std::string& appPackageName,
+ const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
+ const uint64_t* values, const uint32_t valueCount);
// dumpsys interface
void dump(const Vector<String16>& args, std::string* result);
// This limits the worst case number of loading times tracked.
- static const size_t MAX_NUM_LOADING_TIMES = 50;
+ static const size_t MAX_NUM_LOADING_TIMES = 16;
// Below limits the memory usage of GpuStats to be less than 10KB. This is
// the preferred number for statsd while maintaining nice data quality.
static const size_t MAX_NUM_APP_RECORDS = 100;
diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
index 7ea2288..4ce533f 100644
--- a/services/gpuservice/tests/unittests/GpuStatsTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
@@ -52,6 +52,13 @@
#define DRIVER_LOADING_TIME_2 789
#define DRIVER_LOADING_TIME_3 891
+constexpr uint64_t VULKAN_FEATURES_MASK = 0x600D;
+constexpr uint32_t VULKAN_API_VERSION = 0x400000;
+constexpr int32_t VULKAN_INSTANCE_EXTENSION_1 = 0x1234;
+constexpr int32_t VULKAN_INSTANCE_EXTENSION_2 = 0x8765;
+constexpr int32_t VULKAN_DEVICE_EXTENSION_1 = 0x9012;
+constexpr int32_t VULKAN_DEVICE_EXTENSION_2 = 0x3456;
+
enum InputCommand : int32_t {
DUMP_ALL = 0,
DUMP_GLOBAL = 1,
@@ -218,6 +225,24 @@
GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_GLES_CONTEXT, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_API_VERSION,
+ VULKAN_API_VERSION);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_DEVICE, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_SWAPCHAIN, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_FEATURES_ENABLED,
+ VULKAN_FEATURES_MASK);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION,
+ VULKAN_INSTANCE_EXTENSION_1);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION,
+ VULKAN_DEVICE_EXTENSION_1);
EXPECT_TRUE(inputCommand(InputCommand::DUMP_APP).empty());
}
@@ -233,10 +258,51 @@
GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_GLES_CONTEXT, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_API_VERSION,
+ VULKAN_API_VERSION);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_DEVICE, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_SWAPCHAIN, 0);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_FEATURES_ENABLED,
+ VULKAN_FEATURES_MASK);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION,
+ VULKAN_INSTANCE_EXTENSION_1);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION,
+ VULKAN_INSTANCE_EXTENSION_2);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION,
+ VULKAN_DEVICE_EXTENSION_1);
+ mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION,
+ VULKAN_DEVICE_EXTENSION_2);
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("cpuVulkanInUse = 1"));
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("falsePrerotation = 1"));
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("createdGlesContext = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("createdVulkanDevice = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("createdVulkanSwapchain = 1"));
+ std::stringstream expectedResult;
+ expectedResult << "vulkanApiVersion = 0x" << std::hex << VULKAN_API_VERSION;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
+ expectedResult.str("");
+ expectedResult << "vulkanDeviceFeaturesEnabled = 0x" << std::hex << VULKAN_FEATURES_MASK;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
+ expectedResult.str("");
+ expectedResult << "vulkanInstanceExtensions: 0x" << std::hex << VULKAN_INSTANCE_EXTENSION_1
+ << " 0x" << std::hex << VULKAN_INSTANCE_EXTENSION_2;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
+ expectedResult.str("");
+ expectedResult << "vulkanDeviceExtensions: 0x" << std::hex << VULKAN_DEVICE_EXTENSION_1
+ << " 0x" << std::hex << VULKAN_DEVICE_EXTENSION_2;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
}
// Verify we always have the most recently used apps in mAppStats, even when we fill it.
@@ -260,11 +326,52 @@
GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_GLES_CONTEXT, 0);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_API_VERSION,
+ VULKAN_API_VERSION);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_DEVICE, 0);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CREATED_VULKAN_SWAPCHAIN, 0);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_FEATURES_ENABLED,
+ VULKAN_FEATURES_MASK);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION,
+ VULKAN_INSTANCE_EXTENSION_1);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION,
+ VULKAN_INSTANCE_EXTENSION_2);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION,
+ VULKAN_DEVICE_EXTENSION_1);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION,
+ VULKAN_DEVICE_EXTENSION_2);
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(fullPkgName.c_str()));
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("cpuVulkanInUse = 1"));
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("falsePrerotation = 1"));
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("createdGlesContext = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("createdVulkanDevice = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("createdVulkanSwapchain = 1"));
+ std::stringstream expectedResult;
+ expectedResult << "vulkanApiVersion = 0x" << std::hex << VULKAN_API_VERSION;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
+ expectedResult.str("");
+ expectedResult << "vulkanDeviceFeaturesEnabled = 0x" << std::hex << VULKAN_FEATURES_MASK;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
+ expectedResult.str("");
+ expectedResult << "vulkanInstanceExtensions: 0x" << std::hex << VULKAN_INSTANCE_EXTENSION_1
+ << " 0x" << std::hex << VULKAN_INSTANCE_EXTENSION_2;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
+ expectedResult.str("");
+ expectedResult << "vulkanDeviceExtensions: 0x" << std::hex << VULKAN_DEVICE_EXTENSION_1
+ << " 0x" << std::hex << VULKAN_DEVICE_EXTENSION_2;
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
}
// mAppStats purges GpuStats::APP_RECORD_HEADROOM apps removed everytime it's filled up.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 466c51e..4aac377 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -148,21 +148,23 @@
}
bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) {
- switch (action & AMOTION_EVENT_ACTION_MASK) {
+ switch (MotionEvent::getActionMasked(action)) {
case AMOTION_EVENT_ACTION_DOWN:
case AMOTION_EVENT_ACTION_UP:
- case AMOTION_EVENT_ACTION_CANCEL:
+ return pointerCount == 1;
case AMOTION_EVENT_ACTION_MOVE:
- case AMOTION_EVENT_ACTION_OUTSIDE:
case AMOTION_EVENT_ACTION_HOVER_ENTER:
case AMOTION_EVENT_ACTION_HOVER_MOVE:
case AMOTION_EVENT_ACTION_HOVER_EXIT:
+ return pointerCount >= 1;
+ case AMOTION_EVENT_ACTION_CANCEL:
+ case AMOTION_EVENT_ACTION_OUTSIDE:
case AMOTION_EVENT_ACTION_SCROLL:
return true;
case AMOTION_EVENT_ACTION_POINTER_DOWN:
case AMOTION_EVENT_ACTION_POINTER_UP: {
- int32_t index = getMotionEventActionPointerIndex(action);
- return index >= 0 && index < pointerCount;
+ const int32_t index = MotionEvent::getActionIndex(action);
+ return index >= 0 && index < pointerCount && pointerCount > 1;
}
case AMOTION_EVENT_ACTION_BUTTON_PRESS:
case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
@@ -2096,7 +2098,7 @@
bool isSplit = shouldSplitTouch(tempTouchState, entry);
const bool switchedDevice = (oldState != nullptr) &&
- (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source);
+ (oldState->deviceId != entry.deviceId || oldState->source != entry.source);
const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
@@ -2104,6 +2106,7 @@
const bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
+
if (newGesture) {
bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
if (switchedDevice && tempTouchState.isDown() && !down && !isHoverAction) {
@@ -2995,6 +2998,8 @@
connection->getInputChannelName().c_str(), eventEntry->id);
ATRACE_NAME(message.c_str());
}
+ LOG_ALWAYS_FATAL_IF(!inputTarget.flags.any(InputTarget::DISPATCH_MASK),
+ "No dispatch flags are set for %s", eventEntry->getDescription().c_str());
const bool wasEmpty = connection->outboundQueue.empty();
@@ -4053,10 +4058,9 @@
args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
}
}
- if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
- args->pointerProperties)) {
- return;
- }
+ LOG_ALWAYS_FATAL_IF(!validateMotionEvent(args->action, args->actionButton, args->pointerCount,
+ args->pointerProperties),
+ "Invalid event: %s", args->dump().c_str());
uint32_t policyFlags = args->policyFlags;
policyFlags |= POLICY_FLAG_TRUSTED;
@@ -4981,10 +4985,6 @@
}
}
}
-
- if (DEBUG_FOCUS) {
- logDispatchStateLocked();
- }
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -5015,10 +5015,6 @@
} else {
changed = false;
}
-
- if (DEBUG_FOCUS) {
- logDispatchStateLocked();
- }
} // release lock
if (changed) {
@@ -5193,10 +5189,6 @@
synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection);
}
-
- if (DEBUG_FOCUS) {
- logDispatchStateLocked();
- }
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 114e0bf..c21af9e 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -34,8 +34,7 @@
void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
ftl::Flags<InputTarget::Flags> targetFlags, BitSet32 pointerIds,
std::optional<nsecs_t> eventTime) {
- for (size_t i = 0; i < windows.size(); i++) {
- TouchedWindow& touchedWindow = windows[i];
+ for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
touchedWindow.targetFlags |= targetFlags;
if (targetFlags.test(InputTarget::Flags::DISPATCH_AS_SLIPPERY_EXIT)) {
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 6d6cefb..3e4db43 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -88,6 +88,9 @@
virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask,
int32_t sw) = 0;
+ virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+ int32_t toKeyCode) const = 0;
+
virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
/* Toggle Caps Lock */
@@ -188,6 +191,9 @@
// The set of disabled input devices (disabledDevices) has changed.
CHANGE_ENABLED_STATE = 1 << 9,
+ // The device type has been updated.
+ CHANGE_DEVICE_TYPE = 1 << 10,
+
// All devices must be reopened.
CHANGE_MUST_REOPEN = 1 << 31,
};
@@ -209,6 +215,10 @@
// Used to determine which DisplayViewport should be tied to which InputDevice.
std::unordered_map<std::string, std::string> uniqueIdAssociations;
+ // The associations between input device ports device types.
+ // This is used to determine which device type and source should be tied to which InputDevice.
+ std::unordered_map<std::string, std::string> deviceTypeAssociations;
+
// The suggested display ID to show the cursor.
int32_t defaultPointerDisplayId;
@@ -323,7 +333,6 @@
std::optional<DisplayViewport> getDisplayViewportById(int32_t displayId) const;
void setDisplayViewports(const std::vector<DisplayViewport>& viewports);
-
void dump(std::string& dump) const;
void dumpViewport(std::string& dump, const DisplayViewport& viewport) const;
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 46e86de..f3b680b 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -29,6 +29,7 @@
"include",
"mapper",
"mapper/accumulator",
+ "mapper/gestures",
],
}
@@ -50,7 +51,9 @@
"mapper/SensorInputMapper.cpp",
"mapper/SingleTouchInputMapper.cpp",
"mapper/SwitchInputMapper.cpp",
+ "mapper/TouchCursorInputMapperCommon.cpp",
"mapper/TouchInputMapper.cpp",
+ "mapper/TouchpadInputMapper.cpp",
"mapper/VibratorInputMapper.cpp",
"mapper/accumulator/CursorButtonAccumulator.cpp",
"mapper/accumulator/CursorScrollAccumulator.cpp",
@@ -58,7 +61,9 @@
"mapper/accumulator/MultiTouchMotionAccumulator.cpp",
"mapper/accumulator/SingleTouchMotionAccumulator.cpp",
"mapper/accumulator/TouchButtonAccumulator.cpp",
+ "mapper/gestures/GestureConverter.cpp",
"mapper/gestures/GesturesLogging.cpp",
+ "mapper/gestures/HardwareStateConverter.cpp",
],
}
@@ -72,6 +77,7 @@
"libcutils",
"libjsoncpp",
"liblog",
+ "libPlatformProperties",
"libstatslog",
"libutils",
],
@@ -88,7 +94,6 @@
target: {
android: {
shared_libs: [
- "libPlatformProperties",
"libinput",
],
},
@@ -115,6 +120,9 @@
"libchrome-gestures_headers",
"libinputreader_headers",
],
+ whole_static_libs: [
+ "libchrome-gestures",
+ ],
}
cc_library_shared {
@@ -131,6 +139,7 @@
// This should consist only of dependencies from inputflinger. Other dependencies should be
// in cc_defaults so that they are included in the tests.
"libinputflinger_base",
+ "libjsoncpp",
],
export_header_lib_headers: [
"libinputreader_headers",
@@ -145,5 +154,6 @@
},
static_libs: [
"libc++fs",
+ "libchrome-gestures",
],
}
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 0aaef53..e26bc8c 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -952,15 +952,19 @@
device->getKeyCharacterMap()->mapKey(scanCodes[0], 0 /*usageCode*/, &outKeyCode);
switch (mapKeyRes) {
case OK:
- return outKeyCode;
+ break;
case NAME_NOT_FOUND:
// key character map doesn't re-map this scanCode, hence the keyCode remains the same
- return locationKeyCode;
+ outKeyCode = locationKeyCode;
+ break;
default:
ALOGW("Failed to get key code for key location: Key character map returned error %s",
statusToString(mapKeyRes).c_str());
- return AKEYCODE_UNKNOWN;
+ outKeyCode = AKEYCODE_UNKNOWN;
+ break;
}
+ // Remap if there is a Key remapping added to the KCM and return the remapped key
+ return device->getKeyCharacterMap()->applyKeyRemapping(outKeyCode);
}
int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const {
@@ -1023,6 +1027,18 @@
return false;
}
+void EventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ std::scoped_lock _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr) {
+ return;
+ }
+ const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap();
+ if (kcm) {
+ kcm->addKeyRemapping(fromKeyCode, toKeyCode);
+ }
+}
+
status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
std::scoped_lock _l(mLock);
@@ -1048,7 +1064,13 @@
if (status == NO_ERROR) {
if (kcm) {
- kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState);
+ // Remap keys based on user-defined key remappings and key behavior defined in the
+ // corresponding kcm file
+ *outKeycode = kcm->applyKeyRemapping(*outKeycode);
+
+ // Remap keys based on Key behavior defined in KCM file
+ std::tie(*outKeycode, *outMetaState) =
+ kcm->applyKeyBehavior(*outKeycode, metaState);
} else {
*outMetaState = metaState;
}
@@ -2215,6 +2237,10 @@
// a touch screen.
if (device->keyBitmask.test(BTN_TOUCH) || !haveGamepadButtons) {
device->classes |= (InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT);
+ if (device->propBitmask.test(INPUT_PROP_POINTER) &&
+ !device->keyBitmask.any(BTN_TOOL_PEN, BTN_TOOL_FINGER) && !haveStylusButtons) {
+ device->classes |= InputDeviceClass::TOUCHPAD;
+ }
}
// Is this an old style single-touch driver?
} else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index e6ab872..6dfe5f5 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -20,6 +20,7 @@
#include <algorithm>
+#include <android/sysprop/InputProperties.sysprop.h>
#include <ftl/flags.h>
#include "CursorInputMapper.h"
@@ -33,6 +34,7 @@
#include "SensorInputMapper.h"
#include "SingleTouchInputMapper.h"
#include "SwitchInputMapper.h"
+#include "TouchpadInputMapper.h"
#include "VibratorInputMapper.h"
using android::hardware::input::InputDeviceCountryCode;
@@ -54,6 +56,16 @@
InputDevice::~InputDevice() {}
+template <typename K, typename V>
+std::optional<V> getValueByKey(const std::unordered_map<K, V>& map, K key) {
+ auto it = map.find(key);
+ std::optional<V> value = std::nullopt;
+ if (it != map.end()) {
+ value = it->second;
+ }
+ return value;
+}
+
bool InputDevice::isEnabled() {
if (!hasEventHubDevices()) {
return false;
@@ -208,7 +220,12 @@
}
// Touchscreens and touchpad devices.
- if (classes.test(InputDeviceClass::TOUCH_MT)) {
+ static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY =
+ sysprop::InputProperties::enable_touchpad_gestures_library().value_or(false);
+ if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
+ classes.test(InputDeviceClass::TOUCH_MT)) {
+ mappers.push_back(std::make_unique<TouchpadInputMapper>(*contextPtr));
+ } else if (classes.test(InputDeviceClass::TOUCH_MT)) {
mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
} else if (classes.test(InputDeviceClass::TOUCH)) {
mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr));
@@ -284,6 +301,9 @@
context.getConfiguration(&configuration);
mConfiguration.addAll(&configuration);
});
+
+ mAssociatedDeviceType =
+ getValueByKey(config->deviceTypeAssociations, mIdentifier.location);
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
@@ -618,6 +638,12 @@
});
}
+void InputDevice::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) {
+ for_each_subdevice([fromKeyCode, toKeyCode](auto& context) {
+ context.addKeyRemapping(fromKeyCode, toKeyCode);
+ });
+}
+
void InputDevice::bumpGeneration() {
mGeneration = mContext->bumpGeneration();
}
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index f04a646..57f679c 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -650,6 +650,15 @@
return result;
}
+void InputReader::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device != nullptr) {
+ device->addKeyRemapping(fromKeyCode, toKeyCode);
+ }
+}
+
int32_t InputReader::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
std::scoped_lock _l(mLock);
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 8e5f15f..8a844b2 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -94,7 +94,7 @@
/* The input device is a cursor device such as a trackball or mouse. */
CURSOR = 0x00000008,
- /* The input device is a multi-touch touchscreen. */
+ /* The input device is a multi-touch touchscreen or touchpad. */
TOUCH_MT = 0x00000010,
/* The input device is a directional pad (implies keyboard, has DPAD keys). */
@@ -130,6 +130,9 @@
/* The input device has sysfs controllable lights */
LIGHT = 0x00008000,
+ /* The input device is a touchpad, requiring an on-screen cursor. */
+ TOUCHPAD = 0x00010000,
+
/* The input device is virtual (not a real device, not part of UI configuration). */
VIRTUAL = 0x40000000,
@@ -259,6 +262,9 @@
virtual bool hasMscEvent(int32_t deviceId, int mscEvent) const = 0;
+ virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+ int32_t toKeyCode) const = 0;
+
virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
uint32_t* outFlags) const = 0;
@@ -457,6 +463,9 @@
bool hasMscEvent(int32_t deviceId, int mscEvent) const override final;
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
+ int32_t toKeyCode) const override final;
+
status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState,
uint32_t* outFlags) const override final;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 439123b..af59fe2 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -65,6 +65,9 @@
inline std::optional<std::string> getAssociatedDisplayUniqueId() const {
return mAssociatedDisplayUniqueId;
}
+ inline std::optional<std::string> getDeviceTypeAssociation() const {
+ return mAssociatedDeviceType;
+ }
inline std::optional<DisplayViewport> getAssociatedViewport() const {
return mAssociatedViewport;
}
@@ -114,6 +117,8 @@
int32_t getMetaState();
void updateMetaState(int32_t keyCode);
+ void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
+
void bumpGeneration();
[[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when);
@@ -178,6 +183,7 @@
bool mIsExternal;
std::optional<uint8_t> mAssociatedDisplayPort;
std::optional<std::string> mAssociatedDisplayUniqueId;
+ std::optional<std::string> mAssociatedDeviceType;
std::optional<DisplayViewport> mAssociatedViewport;
bool mHasMic;
bool mDropUntilNextSync;
@@ -278,6 +284,10 @@
inline bool hasMscEvent(int mscEvent) const { return mEventHub->hasMscEvent(mId, mscEvent); }
+ inline void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) const {
+ mEventHub->addKeyRemapping(mId, fromKeyCode, toKeyCode);
+ }
+
inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
return mEventHub->mapKey(mId, scanCode, usageCode, metaState, outKeycode, outMetaState,
@@ -402,6 +412,9 @@
inline std::optional<std::string> getAssociatedDisplayUniqueId() const {
return mDevice.getAssociatedDisplayUniqueId();
}
+ inline std::optional<std::string> getDeviceTypeAssociation() const {
+ return mDevice.getDeviceTypeAssociation();
+ }
inline std::optional<DisplayViewport> getAssociatedViewport() const {
return mDevice.getAssociatedViewport();
}
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 4f2503a..e9c989a 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -68,6 +68,8 @@
int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override;
int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override;
+
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override;
void toggleCapsLockState(int32_t deviceId) override;
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index b193dff..633efc6 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -16,10 +16,8 @@
#include "../Macros.h"
-#include "MultiTouchInputMapper.h"
-#if defined(__ANDROID__)
#include <android/sysprop/InputProperties.sysprop.h>
-#endif
+#include "MultiTouchInputMapper.h"
namespace android {
@@ -218,12 +216,7 @@
bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
static const bool SIMULATE_STYLUS_WITH_TOUCH =
-#if defined(__ANDROID__)
sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
-#else
- // Disable this developer feature where sysproperties are not available
- false;
-#endif
return SIMULATE_STYLUS_WITH_TOUCH &&
mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
}
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp
new file mode 100644
index 0000000..c12e95d
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <input/DisplayViewport.h>
+#include <stdint.h>
+#include <ui/Rotation.h>
+
+#include "EventHub.h"
+#include "InputListener.h"
+#include "InputReaderContext.h"
+
+namespace android {
+
+namespace {
+
+[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKey(
+ InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t lastButtonState, int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
+ std::list<NotifyArgs> out;
+ if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
+ (currentButtonState & buttonState)) ||
+ (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
+ !(currentButtonState & buttonState))) {
+ out.push_back(NotifyKeyArgs(context->getNextId(), when, readTime, deviceId, source,
+ displayId, policyFlags, action, 0, keyCode, 0,
+ context->getGlobalMetaState(), when));
+ }
+ return out;
+}
+
+} // namespace
+
+ui::Rotation getInverseRotation(ui::Rotation orientation) {
+ switch (orientation) {
+ case ui::ROTATION_90:
+ return ui::ROTATION_270;
+ case ui::ROTATION_270:
+ return ui::ROTATION_90;
+ default:
+ return orientation;
+ }
+}
+
+void rotateDelta(ui::Rotation orientation, float* deltaX, float* deltaY) {
+ float temp;
+ switch (orientation) {
+ case ui::ROTATION_90:
+ temp = *deltaX;
+ *deltaX = *deltaY;
+ *deltaY = -temp;
+ break;
+
+ case ui::ROTATION_180:
+ *deltaX = -*deltaX;
+ *deltaY = -*deltaY;
+ break;
+
+ case ui::ROTATION_270:
+ temp = *deltaX;
+ *deltaX = -*deltaY;
+ *deltaY = temp;
+ break;
+
+ default:
+ break;
+ }
+}
+
+bool isPointerDown(int32_t buttonState) {
+ return buttonState &
+ (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY |
+ AMOTION_EVENT_BUTTON_TERTIARY);
+}
+
+[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKeys(
+ InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
+ int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t lastButtonState, int32_t currentButtonState) {
+ std::list<NotifyArgs> out;
+ out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
+ policyFlags, lastButtonState, currentButtonState,
+ AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
+ out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
+ policyFlags, lastButtonState, currentButtonState,
+ AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
+ return out;
+}
+
+std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening(
+ const InputDeviceIdentifier& identifier, nsecs_t currentEventTime, nsecs_t readTime,
+ nsecs_t lastEventTime) {
+ if (identifier.bus != BUS_BLUETOOTH) {
+ return {currentEventTime, readTime};
+ }
+
+ // Assume the fastest rate at which a Bluetooth touch device can report input events is one
+ // every 4 milliseconds, or 250 Hz. Timestamps for successive events from a Bluetooth device
+ // will be separated by at least this amount.
+ constexpr static nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
+ // We define a maximum smoothing time delta so that we don't generate events too far into the
+ // future.
+ constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32);
+ const nsecs_t smoothenedEventTime =
+ std::min(std::max(currentEventTime, lastEventTime + MIN_BLUETOOTH_TIMESTAMP_DELTA),
+ currentEventTime + MAX_BLUETOOTH_SMOOTHING_DELTA);
+ // If we are modifying the event time, treat this event as a synthetically generated event for
+ // latency tracking purposes and use the event time as the read time (zero read latency).
+ const nsecs_t smoothenedReadTime =
+ smoothenedEventTime != currentEventTime ? currentEventTime : readTime;
+ return {smoothenedEventTime, smoothenedReadTime};
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index 1c3ca97..3023e68 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -26,81 +26,18 @@
namespace android {
-// --- Static Definitions ---
+ui::Rotation getInverseRotation(ui::Rotation orientation);
-static ui::Rotation getInverseRotation(ui::Rotation orientation) {
- switch (orientation) {
- case ui::ROTATION_90:
- return ui::ROTATION_270;
- case ui::ROTATION_270:
- return ui::ROTATION_90;
- default:
- return orientation;
- }
-}
-
-static void rotateDelta(ui::Rotation orientation, float* deltaX, float* deltaY) {
- float temp;
- switch (orientation) {
- case ui::ROTATION_90:
- temp = *deltaX;
- *deltaX = *deltaY;
- *deltaY = -temp;
- break;
-
- case ui::ROTATION_180:
- *deltaX = -*deltaX;
- *deltaY = -*deltaY;
- break;
-
- case ui::ROTATION_270:
- temp = *deltaX;
- *deltaX = -*deltaY;
- *deltaY = temp;
- break;
-
- default:
- break;
- }
-}
+void rotateDelta(ui::Rotation orientation, float* deltaX, float* deltaY);
// Returns true if the pointer should be reported as being down given the specified
// button states. This determines whether the event is reported as a touch event.
-static bool isPointerDown(int32_t buttonState) {
- return buttonState &
- (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY |
- AMOTION_EVENT_BUTTON_TERTIARY);
-}
+bool isPointerDown(int32_t buttonState);
-[[nodiscard]] static std::list<NotifyArgs> synthesizeButtonKey(
+[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKeys(
InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
- int32_t lastButtonState, int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
- std::list<NotifyArgs> out;
- if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
- (currentButtonState & buttonState)) ||
- (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
- !(currentButtonState & buttonState))) {
- out.push_back(NotifyKeyArgs(context->getNextId(), when, readTime, deviceId, source,
- displayId, policyFlags, action, 0, keyCode, 0,
- context->getGlobalMetaState(), when));
- }
- return out;
-}
-
-[[nodiscard]] static std::list<NotifyArgs> synthesizeButtonKeys(
- InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
- int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
- int32_t lastButtonState, int32_t currentButtonState) {
- std::list<NotifyArgs> out;
- out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
- policyFlags, lastButtonState, currentButtonState,
- AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK);
- out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
- policyFlags, lastButtonState, currentButtonState,
- AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD);
- return out;
-}
+ int32_t lastButtonState, int32_t currentButtonState);
// For devices connected over Bluetooth, although they may produce events at a consistent rate,
// the events might end up reaching Android in a "batched" manner through the Bluetooth
@@ -111,28 +48,8 @@
// coordinates result in extremely large instantaneous velocities, which can negatively impact
// user experience. To avoid this, we augment the timestamps so that subsequent event timestamps
// differ by at least a minimum delta value.
-static std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening(
+std::tuple<nsecs_t /*eventTime*/, nsecs_t /*readTime*/> applyBluetoothTimestampSmoothening(
const InputDeviceIdentifier& identifier, nsecs_t currentEventTime, nsecs_t readTime,
- nsecs_t lastEventTime) {
- if (identifier.bus != BUS_BLUETOOTH) {
- return {currentEventTime, readTime};
- }
-
- // Assume the fastest rate at which a Bluetooth touch device can report input events is one
- // every 4 milliseconds, or 250 Hz. Timestamps for successive events from a Bluetooth device
- // will be separated by at least this amount.
- constexpr static nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
- // We define a maximum smoothing time delta so that we don't generate events too far into the
- // future.
- constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32);
- const nsecs_t smoothenedEventTime =
- std::min(std::max(currentEventTime, lastEventTime + MIN_BLUETOOTH_TIMESTAMP_DELTA),
- currentEventTime + MAX_BLUETOOTH_SMOOTHING_DELTA);
- // If we are modifying the event time, treat this event as a synthetically generated event for
- // latency tracking purposes and use the event time as the read time (zero read latency).
- const nsecs_t smoothenedReadTime =
- smoothenedEventTime != currentEventTime ? currentEventTime : readTime;
- return {smoothenedEventTime, smoothenedReadTime};
-}
+ nsecs_t lastEventTime);
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index cefc44e..160f9eb 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -392,33 +392,10 @@
}
}
- if (getDeviceContext().hasInputProperty(INPUT_PROP_DIRECT)) {
- // The device is a touch screen.
- mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
- } else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) {
- // The device is a pointing device like a track pad.
- mParameters.deviceType = Parameters::DeviceType::POINTER;
- } else {
- // The device is a touch pad of unknown purpose.
- mParameters.deviceType = Parameters::DeviceType::POINTER;
- }
+ configureDeviceType();
mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD);
- std::string deviceTypeString;
- if (getDeviceContext().getConfiguration().tryGetProperty("touch.deviceType",
- deviceTypeString)) {
- if (deviceTypeString == "touchScreen") {
- mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
- } else if (deviceTypeString == "touchNavigation") {
- mParameters.deviceType = Parameters::DeviceType::TOUCH_NAVIGATION;
- } else if (deviceTypeString == "pointer") {
- mParameters.deviceType = Parameters::DeviceType::POINTER;
- } else if (deviceTypeString != "default") {
- ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.c_str());
- }
- }
-
mParameters.orientationAware = mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
getDeviceContext().getConfiguration().tryGetProperty("touch.orientationAware",
mParameters.orientationAware);
@@ -444,7 +421,9 @@
mParameters.associatedDisplayIsExternal = false;
if (mParameters.orientationAware ||
mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN ||
- mParameters.deviceType == Parameters::DeviceType::POINTER) {
+ mParameters.deviceType == Parameters::DeviceType::POINTER ||
+ (mParameters.deviceType == Parameters::DeviceType::TOUCH_NAVIGATION &&
+ getDeviceContext().getAssociatedViewport())) {
mParameters.hasAssociatedDisplay = true;
if (mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN) {
mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal();
@@ -473,6 +452,34 @@
mParameters.enableForInactiveViewport);
}
+void TouchInputMapper::configureDeviceType() {
+ if (getDeviceContext().hasInputProperty(INPUT_PROP_DIRECT)) {
+ // The device is a touch screen.
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
+ } else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) {
+ // The device is a pointing device like a track pad.
+ mParameters.deviceType = Parameters::DeviceType::POINTER;
+ } else {
+ // The device is a touch pad of unknown purpose.
+ mParameters.deviceType = Parameters::DeviceType::POINTER;
+ }
+
+ // Type association takes precedence over the device type found in the idc file.
+ std::string deviceTypeString = getDeviceContext().getDeviceTypeAssociation().value_or("");
+ if (deviceTypeString.empty()) {
+ getDeviceContext().getConfiguration().tryGetProperty("touch.deviceType", deviceTypeString);
+ }
+ if (deviceTypeString == "touchScreen") {
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_SCREEN;
+ } else if (deviceTypeString == "touchNavigation") {
+ mParameters.deviceType = Parameters::DeviceType::TOUCH_NAVIGATION;
+ } else if (deviceTypeString == "pointer") {
+ mParameters.deviceType = Parameters::DeviceType::POINTER;
+ } else if (deviceTypeString != "default" && deviceTypeString != "") {
+ ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.c_str());
+ }
+}
+
void TouchInputMapper::dumpParameters(std::string& dump) {
dump += INDENT3 "Parameters:\n";
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 34ba625..50a7ea3 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -814,6 +814,8 @@
static void assignPointerIds(const RawState& last, RawState& current);
void rotateAndScale(float& x, float& y) const;
+
+ void configureDeviceType();
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
new file mode 100644
index 0000000..c563dba
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../Macros.h"
+
+#include <android/input.h>
+#include <linux/input-event-codes.h>
+#include <log/log_main.h>
+#include "TouchCursorInputMapperCommon.h"
+#include "TouchpadInputMapper.h"
+
+namespace android {
+
+namespace {
+
+short getMaxTouchCount(const InputDeviceContext& context) {
+ if (context.hasScanCode(BTN_TOOL_QUINTTAP)) return 5;
+ if (context.hasScanCode(BTN_TOOL_QUADTAP)) return 4;
+ if (context.hasScanCode(BTN_TOOL_TRIPLETAP)) return 3;
+ if (context.hasScanCode(BTN_TOOL_DOUBLETAP)) return 2;
+ if (context.hasScanCode(BTN_TOOL_FINGER)) return 1;
+ return 0;
+}
+
+HardwareProperties createHardwareProperties(const InputDeviceContext& context) {
+ HardwareProperties props;
+ RawAbsoluteAxisInfo absMtPositionX;
+ context.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &absMtPositionX);
+ props.left = absMtPositionX.minValue;
+ props.right = absMtPositionX.maxValue;
+ props.res_x = absMtPositionX.resolution;
+
+ RawAbsoluteAxisInfo absMtPositionY;
+ context.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &absMtPositionY);
+ props.top = absMtPositionY.minValue;
+ props.bottom = absMtPositionY.maxValue;
+ props.res_y = absMtPositionY.resolution;
+
+ RawAbsoluteAxisInfo absMtOrientation;
+ context.getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &absMtOrientation);
+ props.orientation_minimum = absMtOrientation.minValue;
+ props.orientation_maximum = absMtOrientation.maxValue;
+
+ RawAbsoluteAxisInfo absMtSlot;
+ context.getAbsoluteAxisInfo(ABS_MT_SLOT, &absMtSlot);
+ props.max_finger_cnt = absMtSlot.maxValue - absMtSlot.minValue + 1;
+ props.max_touch_cnt = getMaxTouchCount(context);
+
+ // T5R2 ("Track 5, Report 2") is a feature of some old Synaptics touchpads that could track 5
+ // fingers but only report the coordinates of 2 of them. We don't know of any external touchpads
+ // that did this, so assume false.
+ props.supports_t5r2 = false;
+
+ props.support_semi_mt = context.hasInputProperty(INPUT_PROP_SEMI_MT);
+ props.is_button_pad = context.hasInputProperty(INPUT_PROP_BUTTONPAD);
+
+ // Mouse-only properties, which will always be false.
+ props.has_wheel = false;
+ props.wheel_is_hi_res = false;
+
+ // Linux Kernel haptic touchpad support isn't merged yet, so for now assume that no touchpads
+ // are haptic.
+ props.is_haptic_pad = false;
+ return props;
+}
+
+void gestureInterpreterCallback(void* clientData, const Gesture* gesture) {
+ TouchpadInputMapper* mapper = static_cast<TouchpadInputMapper*>(clientData);
+ mapper->consumeGesture(gesture);
+}
+
+} // namespace
+
+TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext)
+ : InputMapper(deviceContext),
+ mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
+ mPointerController(getContext()->getPointerController(getDeviceId())),
+ mStateConverter(deviceContext),
+ mGestureConverter(*getContext(), getDeviceId()) {
+ mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
+ mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
+ // Even though we don't explicitly delete copy/move semantics, it's safe to
+ // give away a pointer to TouchpadInputMapper here because
+ // 1) mGestureInterpreter's lifecycle is determined by TouchpadInputMapper, and
+ // 2) TouchpadInputMapper is stored as a unique_ptr and not moved.
+ mGestureInterpreter->SetCallback(gestureInterpreterCallback, this);
+ // TODO(b/251196347): set a property provider, so we can change gesture properties.
+ // TODO(b/251196347): set a timer provider, so the library can use timers.
+}
+
+TouchpadInputMapper::~TouchpadInputMapper() {
+ if (mPointerController != nullptr) {
+ mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
+ }
+}
+
+uint32_t TouchpadInputMapper::getSources() const {
+ return AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD;
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::reset(nsecs_t when) {
+ mStateConverter.reset();
+ mGestureConverter.reset();
+ return InputMapper::reset(when);
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) {
+ std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent);
+ if (state) {
+ return sendHardwareState(rawEvent->when, rawEvent->readTime, *state);
+ } else {
+ return {};
+ }
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs_t readTime,
+ SelfContainedHardwareState schs) {
+ mProcessing = true;
+ mGestureInterpreter->PushHardwareState(&schs.state);
+ mProcessing = false;
+
+ return processGestures(when, readTime);
+}
+
+void TouchpadInputMapper::consumeGesture(const Gesture* gesture) {
+ ALOGD("Gesture ready: %s", gesture->String().c_str());
+ if (!mProcessing) {
+ ALOGE("Received gesture outside of the normal processing flow; ignoring it.");
+ return;
+ }
+ mGesturesToProcess.push_back(*gesture);
+}
+
+std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t readTime) {
+ std::list<NotifyArgs> out = {};
+ for (Gesture& gesture : mGesturesToProcess) {
+ out += mGestureConverter.handleGesture(when, readTime, gesture);
+ }
+ mGesturesToProcess.clear();
+ return out;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
new file mode 100644
index 0000000..b3bc831
--- /dev/null
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+#include <vector>
+
+#include <PointerControllerInterface.h>
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "InputMapper.h"
+#include "NotifyArgs.h"
+#include "gestures/GestureConverter.h"
+#include "gestures/HardwareStateConverter.h"
+
+#include "include/gestures.h"
+
+namespace android {
+
+class TouchpadInputMapper : public InputMapper {
+public:
+ explicit TouchpadInputMapper(InputDeviceContext& deviceContext);
+ ~TouchpadInputMapper();
+
+ uint32_t getSources() const override;
+ [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+
+ void consumeGesture(const Gesture* gesture);
+
+private:
+ [[nodiscard]] std::list<NotifyArgs> sendHardwareState(nsecs_t when, nsecs_t readTime,
+ SelfContainedHardwareState schs);
+ [[nodiscard]] std::list<NotifyArgs> processGestures(nsecs_t when, nsecs_t readTime);
+
+ std::unique_ptr<gestures::GestureInterpreter, void (*)(gestures::GestureInterpreter*)>
+ mGestureInterpreter;
+ std::shared_ptr<PointerControllerInterface> mPointerController;
+
+ HardwareStateConverter mStateConverter;
+ GestureConverter mGestureConverter;
+
+ bool mProcessing = false;
+ std::vector<Gesture> mGesturesToProcess;
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
index ed4c789..1380604 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -32,6 +32,14 @@
void process(const RawEvent* rawEvent);
uint32_t getButtonState() const;
+ inline bool isLeftPressed() const { return mBtnLeft; }
+ inline bool isRightPressed() const { return mBtnRight; }
+ inline bool isMiddlePressed() const { return mBtnMiddle; }
+ inline bool isBackPressed() const { return mBtnBack; }
+ inline bool isSidePressed() const { return mBtnSide; }
+ inline bool isForwardPressed() const { return mBtnForward; }
+ inline bool isExtraPressed() const { return mBtnExtra; }
+ inline bool isTaskPressed() const { return mBtnTask; }
private:
bool mBtnLeft;
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
index bc23a8e..6601702 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -48,6 +48,7 @@
mBtnToolDoubleTap = mDeviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP);
mBtnToolTripleTap = mDeviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP);
mBtnToolQuadTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUADTAP);
+ mBtnToolQuintTap = mDeviceContext.isKeyPressed(BTN_TOOL_QUINTTAP);
mHidUsageAccumulator.reset();
}
@@ -100,6 +101,9 @@
case BTN_TOOL_QUADTAP:
mBtnToolQuadTap = rawEvent->value;
break;
+ case BTN_TOOL_QUINTTAP:
+ mBtnToolQuintTap = rawEvent->value;
+ break;
default:
processMappedKey(rawEvent->code, rawEvent->value);
}
@@ -147,7 +151,8 @@
if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) {
return AMOTION_EVENT_TOOL_TYPE_STYLUS;
}
- if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) {
+ if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap ||
+ mBtnToolQuintTap) {
return AMOTION_EVENT_TOOL_TYPE_FINGER;
}
return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
@@ -156,7 +161,7 @@
bool TouchButtonAccumulator::isToolActive() const {
return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber || mBtnToolBrush ||
mBtnToolPencil || mBtnToolAirbrush || mBtnToolMouse || mBtnToolLens ||
- mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap;
+ mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap || mBtnToolQuintTap;
}
bool TouchButtonAccumulator::isHovering() const {
@@ -171,4 +176,15 @@
return mHaveBtnTouch;
}
+int TouchButtonAccumulator::getTouchCount() const {
+ if (mBtnTouch) {
+ if (mBtnToolQuintTap) return 5;
+ if (mBtnToolQuadTap) return 4;
+ if (mBtnToolTripleTap) return 3;
+ if (mBtnToolDoubleTap) return 2;
+ if (mBtnToolFinger) return 1;
+ }
+ return 0;
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index c2de23c..2e70e2e 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -41,6 +41,7 @@
bool isHovering() const;
bool hasStylus() const;
bool hasButtonTouch() const;
+ int getTouchCount() const;
private:
bool mHaveBtnTouch{};
@@ -60,6 +61,7 @@
bool mBtnToolDoubleTap{};
bool mBtnToolTripleTap{};
bool mBtnToolQuadTap{};
+ bool mBtnToolQuintTap{};
HidUsageAccumulator mHidUsageAccumulator{};
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
new file mode 100644
index 0000000..23216d3
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gestures/GestureConverter.h"
+
+#include <android/input.h>
+
+#include "TouchCursorInputMapperCommon.h"
+#include "input/Input.h"
+
+namespace android {
+
+namespace {
+
+uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) {
+ switch (gesturesButton) {
+ case GESTURES_BUTTON_LEFT:
+ return AMOTION_EVENT_BUTTON_PRIMARY;
+ case GESTURES_BUTTON_MIDDLE:
+ return AMOTION_EVENT_BUTTON_TERTIARY;
+ case GESTURES_BUTTON_RIGHT:
+ return AMOTION_EVENT_BUTTON_SECONDARY;
+ case GESTURES_BUTTON_BACK:
+ return AMOTION_EVENT_BUTTON_BACK;
+ case GESTURES_BUTTON_FORWARD:
+ return AMOTION_EVENT_BUTTON_FORWARD;
+ default:
+ return 0;
+ }
+}
+
+} // namespace
+
+GestureConverter::GestureConverter(InputReaderContext& readerContext, int32_t deviceId)
+ : mDeviceId(deviceId),
+ mReaderContext(readerContext),
+ mPointerController(readerContext.getPointerController(deviceId)) {}
+
+void GestureConverter::reset() {
+ mButtonState = 0;
+}
+
+std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture) {
+ switch (gesture.type) {
+ case kGestureTypeMove:
+ return {handleMove(when, readTime, gesture)};
+ case kGestureTypeButtonsChange:
+ return handleButtonsChange(when, readTime, gesture);
+ default:
+ // TODO(b/251196347): handle more gesture types.
+ return {};
+ }
+}
+
+NotifyArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture) {
+ PointerProperties props;
+ props.clear();
+ props.id = 0;
+ props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
+ mPointerController->move(gesture.details.move.dx, gesture.details.move.dy);
+ mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+ float xCursorPosition, yCursorPosition;
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+
+ PointerCoords coords;
+ coords.clear();
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, gesture.details.move.dx);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, gesture.details.move.dy);
+ const bool down = isPointerDown(mButtonState);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
+
+ const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE;
+ return makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState,
+ /* pointerCount= */ 1, &props, &coords, xCursorPosition, yCursorPosition);
+}
+
+std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture) {
+ std::list<NotifyArgs> out = {};
+
+ mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
+ mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+
+ PointerProperties props;
+ props.clear();
+ props.id = 0;
+ props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ float xCursorPosition, yCursorPosition;
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+
+ PointerCoords coords;
+ coords.clear();
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
+ const uint32_t buttonsPressed = gesture.details.buttons.down;
+ bool pointerDown = isPointerDown(mButtonState) ||
+ buttonsPressed &
+ (GESTURES_BUTTON_LEFT | GESTURES_BUTTON_MIDDLE | GESTURES_BUTTON_RIGHT);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerDown ? 1.0f : 0.0f);
+
+ uint32_t newButtonState = mButtonState;
+ std::list<NotifyArgs> pressEvents = {};
+ for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
+ if (buttonsPressed & button) {
+ uint32_t actionButton = gesturesButtonToMotionEventButton(button);
+ newButtonState |= actionButton;
+ pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+ actionButton, newButtonState,
+ /* pointerCount= */ 1, &props, &coords,
+ xCursorPosition, yCursorPosition));
+ }
+ }
+ if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) {
+ mDownTime = when;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
+ /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1,
+ &props, &coords, xCursorPosition, yCursorPosition));
+ }
+ out.splice(out.end(), pressEvents);
+
+ // The same button may be in both down and up in the same gesture, in which case we should treat
+ // it as having gone down and then up. So, we treat a single button change gesture as two state
+ // changes: a set of buttons going down, followed by a set of buttons going up.
+ mButtonState = newButtonState;
+
+ const uint32_t buttonsReleased = gesture.details.buttons.up;
+ for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
+ if (buttonsReleased & button) {
+ uint32_t actionButton = gesturesButtonToMotionEventButton(button);
+ newButtonState &= ~actionButton;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ actionButton, newButtonState, /* pointerCount= */ 1,
+ &props, &coords, xCursorPosition, yCursorPosition));
+ }
+ }
+ if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) {
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
+ newButtonState, /* pointerCount= */ 1, &props, &coords,
+ xCursorPosition, yCursorPosition));
+ }
+ mButtonState = newButtonState;
+ return out;
+}
+
+NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
+ int32_t actionButton, int32_t buttonState,
+ uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords,
+ float xCursorPosition, float yCursorPosition) {
+ // TODO(b/260226362): consider what the appropriate source for these events is.
+ const uint32_t source = AINPUT_SOURCE_MOUSE;
+
+ return NotifyMotionArgs(mReaderContext.getNextId(), when, readTime, mDeviceId, source,
+ mPointerController->getDisplayId(), /* policyFlags= */ 0, action,
+ /* actionButton= */ actionButton, /* flags= */ 0,
+ mReaderContext.getGlobalMetaState(), buttonState,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
+ pointerProperties, pointerCoords,
+ /* xPrecision= */ 1.0f, /* yPrecision= */ 1.0f, xCursorPosition,
+ yCursorPosition, /* downTime= */ mDownTime, /* videoFrames= */ {});
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
new file mode 100644
index 0000000..dc11f24
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+
+#include <PointerControllerInterface.h>
+#include <utils/Timers.h>
+
+#include "InputReaderContext.h"
+#include "NotifyArgs.h"
+
+#include "include/gestures.h"
+
+namespace android {
+
+// Converts Gesture structs from the gestures library into NotifyArgs and the appropriate
+// PointerController calls.
+class GestureConverter {
+public:
+ GestureConverter(InputReaderContext& readerContext, int32_t deviceId);
+
+ void reset();
+
+ [[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture);
+
+private:
+ NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
+ [[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime,
+ const Gesture& gesture);
+
+ NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
+ int32_t actionButton, int32_t buttonState,
+ uint32_t pointerCount,
+ const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, float xCursorPosition,
+ float yCursorPosition);
+
+ const int32_t mDeviceId;
+ InputReaderContext& mReaderContext;
+ std::shared_ptr<PointerControllerInterface> mPointerController;
+
+ // The current button state according to the gestures library, but converted into MotionEvent
+ // button values (AMOTION_EVENT_BUTTON_...).
+ uint32_t mButtonState = 0;
+ nsecs_t mDownTime = 0;
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
new file mode 100644
index 0000000..49c13ca
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gestures/HardwareStateConverter.h"
+
+#include <chrono>
+#include <vector>
+
+#include <linux/input-event-codes.h>
+
+namespace android {
+
+HardwareStateConverter::HardwareStateConverter(InputDeviceContext& deviceContext)
+ : mDeviceContext(deviceContext), mTouchButtonAccumulator(deviceContext) {
+ RawAbsoluteAxisInfo slotAxisInfo;
+ deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
+ if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
+ ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work "
+ "properly.",
+ deviceContext.getName().c_str());
+ }
+ mMotionAccumulator.configure(deviceContext, slotAxisInfo.maxValue + 1, true);
+ mTouchButtonAccumulator.configure();
+}
+
+std::optional<SelfContainedHardwareState> HardwareStateConverter::processRawEvent(
+ const RawEvent* rawEvent) {
+ std::optional<SelfContainedHardwareState> out;
+ if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+ out = produceHardwareState(rawEvent->when);
+ mMotionAccumulator.finishSync();
+ mMscTimestamp = 0;
+ }
+ if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) {
+ mMscTimestamp = rawEvent->value;
+ }
+ mCursorButtonAccumulator.process(rawEvent);
+ mMotionAccumulator.process(rawEvent);
+ mTouchButtonAccumulator.process(rawEvent);
+ return out;
+}
+
+SelfContainedHardwareState HardwareStateConverter::produceHardwareState(nsecs_t when) {
+ SelfContainedHardwareState schs;
+ // The gestures library uses doubles to represent timestamps in seconds.
+ schs.state.timestamp = std::chrono::duration<stime_t>(std::chrono::nanoseconds(when)).count();
+ schs.state.msc_timestamp =
+ std::chrono::duration<stime_t>(std::chrono::microseconds(mMscTimestamp)).count();
+
+ schs.state.buttons_down = 0;
+ if (mCursorButtonAccumulator.isLeftPressed()) {
+ schs.state.buttons_down |= GESTURES_BUTTON_LEFT;
+ }
+ if (mCursorButtonAccumulator.isMiddlePressed()) {
+ schs.state.buttons_down |= GESTURES_BUTTON_MIDDLE;
+ }
+ if (mCursorButtonAccumulator.isRightPressed()) {
+ schs.state.buttons_down |= GESTURES_BUTTON_RIGHT;
+ }
+ if (mCursorButtonAccumulator.isBackPressed() || mCursorButtonAccumulator.isSidePressed()) {
+ schs.state.buttons_down |= GESTURES_BUTTON_BACK;
+ }
+ if (mCursorButtonAccumulator.isForwardPressed() || mCursorButtonAccumulator.isExtraPressed()) {
+ schs.state.buttons_down |= GESTURES_BUTTON_FORWARD;
+ }
+
+ schs.fingers.clear();
+ for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) {
+ MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i);
+ if (slot.isInUse()) {
+ FingerState& fingerState = schs.fingers.emplace_back();
+ fingerState = {};
+ fingerState.touch_major = slot.getTouchMajor();
+ fingerState.touch_minor = slot.getTouchMinor();
+ fingerState.width_major = slot.getToolMajor();
+ fingerState.width_minor = slot.getToolMinor();
+ fingerState.pressure = slot.getPressure();
+ fingerState.orientation = slot.getOrientation();
+ fingerState.position_x = slot.getX();
+ fingerState.position_y = slot.getY();
+ fingerState.tracking_id = slot.getTrackingId();
+ }
+ }
+ schs.state.fingers = schs.fingers.data();
+ schs.state.finger_cnt = schs.fingers.size();
+ schs.state.touch_cnt = mTouchButtonAccumulator.getTouchCount();
+ return schs;
+}
+
+void HardwareStateConverter::reset() {
+ mCursorButtonAccumulator.reset(mDeviceContext);
+ mTouchButtonAccumulator.reset();
+ mMscTimestamp = 0;
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
new file mode 100644
index 0000000..fd63c05
--- /dev/null
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include <utils/Timers.h>
+
+#include "EventHub.h"
+#include "InputDevice.h"
+#include "accumulator/CursorButtonAccumulator.h"
+#include "accumulator/MultiTouchMotionAccumulator.h"
+#include "accumulator/TouchButtonAccumulator.h"
+
+#include "include/gestures.h"
+
+namespace android {
+
+// A HardwareState struct, but bundled with a vector to contain its FingerStates, so you don't have
+// to worry about where that memory is allocated.
+struct SelfContainedHardwareState {
+ HardwareState state;
+ std::vector<FingerState> fingers;
+};
+
+// Converts RawEvents into the HardwareState structs used by the gestures library.
+class HardwareStateConverter {
+public:
+ HardwareStateConverter(InputDeviceContext& deviceContext);
+
+ std::optional<SelfContainedHardwareState> processRawEvent(const RawEvent* event);
+ void reset();
+
+private:
+ SelfContainedHardwareState produceHardwareState(nsecs_t when);
+
+ InputDeviceContext& mDeviceContext;
+ CursorButtonAccumulator mCursorButtonAccumulator;
+ MultiTouchMotionAccumulator mMotionAccumulator;
+ TouchButtonAccumulator mTouchButtonAccumulator;
+ int32_t mMscTimestamp = 0;
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 2e5bec9..58a5c31 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -44,10 +44,14 @@
"FakeInputReaderPolicy.cpp",
"FakePointerController.cpp",
"FocusResolver_test.cpp",
+ "GestureConverter_test.cpp",
+ "HardwareStateConverter_test.cpp",
+ "InputMapperTest.cpp",
"InputProcessor_test.cpp",
"InputProcessorConverter_test.cpp",
"InputDispatcher_test.cpp",
"InputReader_test.cpp",
+ "InstrumentedInputReader.cpp",
"LatencyTracker_test.cpp",
"NotifyArgs_test.cpp",
"PreferStylusOverTouch_test.cpp",
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index f6cf1cc..289a780 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -154,6 +154,11 @@
getDevice(deviceId)->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode);
}
+void FakeEventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ Device* device = getDevice(deviceId);
+ device->keyRemapping.insert_or_assign(fromKeyCode, toKeyCode);
+}
+
void FakeEventHub::addLed(int32_t deviceId, int32_t led, bool initialState) {
getDevice(deviceId)->leds.add(led, initialState);
}
@@ -299,7 +304,8 @@
const KeyInfo* key = getKey(device, scanCode, usageCode);
if (key) {
if (outKeycode) {
- *outKeycode = key->keyCode;
+ auto it = device->keyRemapping.find(key->keyCode);
+ *outKeycode = it != device->keyRemapping.end() ? it->second : key->keyCode;
}
if (outFlags) {
*outFlags = key->flags;
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
index 21cb2f1..fb3c859 100644
--- a/services/inputflinger/tests/FakeEventHub.h
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -59,6 +59,7 @@
KeyedVector<int32_t, int32_t> absoluteAxisValue;
KeyedVector<int32_t, KeyInfo> keysByScanCode;
KeyedVector<int32_t, KeyInfo> keysByUsageCode;
+ std::unordered_map<int32_t, int32_t> keyRemapping;
KeyedVector<int32_t, bool> leds;
// fake mapping which would normally come from keyCharacterMap
std::unordered_map<int32_t, int32_t> keyCodeMapping;
@@ -132,6 +133,7 @@
void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode,
uint32_t flags);
void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode);
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const;
void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition);
void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType,
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 3af4298..dc7e581 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -126,6 +126,11 @@
mConfig.portAssociations.insert({inputPort, displayPort});
}
+void FakeInputReaderPolicy::addDeviceTypeAssociation(const std::string& inputPort,
+ const std::string& type) {
+ mConfig.deviceTypeAssociations.insert({inputPort, type});
+}
+
void FakeInputReaderPolicy::addInputUniqueIdAssociation(const std::string& inputUniqueId,
const std::string& displayUniqueId) {
mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId});
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index c16cda4..faa9c01 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -55,6 +55,7 @@
bool updateViewport(const DisplayViewport& viewport);
void addExcludedDeviceName(const std::string& deviceName);
void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort);
+ void addDeviceTypeAssociation(const std::string& inputPort, const std::string& type);
void addInputUniqueIdAssociation(const std::string& inputUniqueId,
const std::string& displayUniqueId);
void addDisabledDevice(int32_t deviceId);
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index 635366b..ab7879f 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -16,6 +16,8 @@
#include "FakePointerController.h"
+#include <gtest/gtest.h>
+
namespace android {
void FakePointerController::setBounds(float minX, float minY, float maxX, float maxY) {
@@ -56,6 +58,13 @@
mDisplayId = viewport.displayId;
}
+void FakePointerController::assertPosition(float x, float y) {
+ float actualX, actualY;
+ getPosition(&actualX, &actualY);
+ ASSERT_NEAR(x, actualX, 1);
+ ASSERT_NEAR(y, actualY, 1);
+}
+
bool FakePointerController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
float* outMaxY) const {
*outMinX = mMinX;
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index f00870f..d10cbcd 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -38,6 +38,8 @@
int32_t getDisplayId() const override;
void setDisplayViewport(const DisplayViewport& viewport) override;
+ void assertPosition(float x, float y);
+
private:
bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override;
void move(float deltaX, float deltaY) override;
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
new file mode 100644
index 0000000..91efd1a
--- /dev/null
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+
+#include <EventHub.h>
+#include <gestures/GestureConverter.h>
+#include <gtest/gtest.h>
+
+#include "FakeEventHub.h"
+#include "FakeInputReaderPolicy.h"
+#include "FakePointerController.h"
+#include "InstrumentedInputReader.h"
+#include "NotifyArgs.h"
+#include "TestConstants.h"
+#include "TestInputListener.h"
+#include "TestInputListenerMatchers.h"
+#include "include/gestures.h"
+
+namespace android {
+
+using testing::AllOf;
+
+class GestureConverterTest : public testing::Test {
+protected:
+ static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+ static constexpr stime_t ARBITRARY_GESTURE_TIME = 1.2;
+ static constexpr float POINTER_X = 100;
+ static constexpr float POINTER_Y = 200;
+
+ void SetUp() {
+ mFakeEventHub = std::make_unique<FakeEventHub>();
+ mFakePolicy = sp<FakeInputReaderPolicy>::make();
+ mFakeListener = std::make_unique<TestInputListener>();
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+ *mFakeListener);
+
+ mFakePointerController = std::make_shared<FakePointerController>();
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(POINTER_X, POINTER_Y);
+ mFakePolicy->setPointerController(mFakePointerController);
+ }
+
+ std::shared_ptr<FakeEventHub> mFakeEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ std::unique_ptr<TestInputListener> mFakeListener;
+ std::unique_ptr<InstrumentedInputReader> mReader;
+ std::shared_ptr<FakePointerController> mFakePointerController;
+};
+
+TEST_F(GestureConverterTest, Move) {
+ GestureConverter converter(*mReader->getContext(), DEVICE_ID);
+
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER), WithButtonState(0),
+ WithPressure(0.0f)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(95, 210));
+}
+
+TEST_F(GestureConverterTest, ButtonsChange) {
+ GestureConverter converter(*mReader->getContext(), DEVICE_ID);
+
+ // Press left and right buttons at once
+ Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
+ /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture);
+ ASSERT_EQ(3u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
+ AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
+ AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+
+ // Then release the left button
+ Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT,
+ /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, leftUpGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+
+ // Finally release the right button
+ Gesture rightUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_RIGHT,
+ /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, rightUpGesture);
+ ASSERT_EQ(2u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+}
+
+TEST_F(GestureConverterTest, DragWithButton) {
+ GestureConverter converter(*mReader->getContext(), DEVICE_ID);
+
+ // Press the button
+ Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE,
+ /* is_tap= */ false);
+ std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture);
+ ASSERT_EQ(2u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+
+ // Move
+ Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, moveGesture);
+ ASSERT_EQ(1u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f)));
+
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(95, 210));
+
+ // Release the button
+ Gesture upGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT,
+ /* is_tap= */ false);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, upGesture);
+ ASSERT_EQ(2u, args.size());
+
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY), WithButtonState(0),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
+ WithCoords(POINTER_X - 5, POINTER_Y + 10),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp
new file mode 100644
index 0000000..7921881
--- /dev/null
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <EventHub.h>
+#include <gestures/HardwareStateConverter.h>
+#include <gtest/gtest.h>
+#include <linux/input-event-codes.h>
+
+#include "FakeEventHub.h"
+#include "FakeInputReaderPolicy.h"
+#include "InstrumentedInputReader.h"
+#include "TestConstants.h"
+#include "TestInputListener.h"
+
+namespace android {
+
+class HardwareStateConverterTest : public testing::Test {
+protected:
+ static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+ static constexpr int32_t EVENTHUB_ID = 1;
+
+ void SetUp() {
+ mFakeEventHub = std::make_unique<FakeEventHub>();
+ mFakePolicy = sp<FakeInputReaderPolicy>::make();
+ mFakeListener = std::make_unique<TestInputListener>();
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+ *mFakeListener);
+ mDevice = newDevice();
+
+ mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, 0, 7, 0, 0, 0);
+ }
+
+ std::shared_ptr<InputDevice> newDevice() {
+ InputDeviceIdentifier identifier;
+ identifier.name = "device";
+ identifier.location = "USB1";
+ identifier.bus = 0;
+ std::shared_ptr<InputDevice> device =
+ std::make_shared<InputDevice>(mReader->getContext(), DEVICE_ID, /* generation= */ 2,
+ identifier);
+ mReader->pushNextDevice(device);
+ mFakeEventHub->addDevice(EVENTHUB_ID, identifier.name, InputDeviceClass::TOUCHPAD,
+ identifier.bus);
+ mReader->loopOnce();
+ return device;
+ }
+
+ void processAxis(HardwareStateConverter& conv, nsecs_t when, int32_t type, int32_t code,
+ int32_t value) {
+ RawEvent event;
+ event.when = when;
+ event.readTime = READ_TIME;
+ event.deviceId = EVENTHUB_ID;
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ std::optional<SelfContainedHardwareState> schs = conv.processRawEvent(&event);
+ EXPECT_FALSE(schs.has_value());
+ }
+
+ std::optional<SelfContainedHardwareState> processSync(HardwareStateConverter& conv,
+ nsecs_t when) {
+ RawEvent event;
+ event.when = when;
+ event.readTime = READ_TIME;
+ event.deviceId = EVENTHUB_ID;
+ event.type = EV_SYN;
+ event.code = SYN_REPORT;
+ event.value = 0;
+ return conv.processRawEvent(&event);
+ }
+
+ std::shared_ptr<FakeEventHub> mFakeEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ std::unique_ptr<TestInputListener> mFakeListener;
+ std::unique_ptr<InstrumentedInputReader> mReader;
+ std::shared_ptr<InputDevice> mDevice;
+};
+
+TEST_F(HardwareStateConverterTest, OneFinger) {
+ const nsecs_t time = 1500000000;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ HardwareStateConverter conv(deviceContext);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MAJOR, 5);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MINOR, 4);
+ processAxis(conv, time, EV_ABS, ABS_MT_PRESSURE, 42);
+ processAxis(conv, time, EV_ABS, ABS_MT_ORIENTATION, 2);
+
+ processAxis(conv, time, EV_ABS, ABS_X, 50);
+ processAxis(conv, time, EV_ABS, ABS_Y, 100);
+ processAxis(conv, time, EV_ABS, ABS_PRESSURE, 42);
+
+ processAxis(conv, time, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, time, EV_KEY, BTN_TOOL_FINGER, 1);
+ std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+
+ ASSERT_TRUE(schs.has_value());
+ const HardwareState& state = schs->state;
+ EXPECT_NEAR(1.5, state.timestamp, EPSILON);
+ EXPECT_EQ(0, state.buttons_down);
+ EXPECT_EQ(1, state.touch_cnt);
+
+ ASSERT_EQ(1, state.finger_cnt);
+ const FingerState& finger = state.fingers[0];
+ EXPECT_EQ(123, finger.tracking_id);
+ EXPECT_NEAR(50, finger.position_x, EPSILON);
+ EXPECT_NEAR(100, finger.position_y, EPSILON);
+ EXPECT_NEAR(5, finger.touch_major, EPSILON);
+ EXPECT_NEAR(4, finger.touch_minor, EPSILON);
+ EXPECT_NEAR(42, finger.pressure, EPSILON);
+ EXPECT_NEAR(2, finger.orientation, EPSILON);
+ EXPECT_EQ(0u, finger.flags);
+
+ EXPECT_EQ(0, state.rel_x);
+ EXPECT_EQ(0, state.rel_y);
+ EXPECT_EQ(0, state.rel_wheel);
+ EXPECT_EQ(0, state.rel_wheel_hi_res);
+ EXPECT_EQ(0, state.rel_hwheel);
+ EXPECT_NEAR(0.0, state.msc_timestamp, EPSILON);
+}
+
+TEST_F(HardwareStateConverterTest, TwoFingers) {
+ const nsecs_t time = ARBITRARY_TIME;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ HardwareStateConverter conv(deviceContext);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0);
+ processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MAJOR, 5);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MINOR, 4);
+ processAxis(conv, time, EV_ABS, ABS_MT_PRESSURE, 42);
+ processAxis(conv, time, EV_ABS, ABS_MT_ORIENTATION, 2);
+
+ processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 1);
+ processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 456);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, -20);
+ processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 40);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MAJOR, 8);
+ processAxis(conv, time, EV_ABS, ABS_MT_TOUCH_MINOR, 7);
+ processAxis(conv, time, EV_ABS, ABS_MT_PRESSURE, 21);
+ processAxis(conv, time, EV_ABS, ABS_MT_ORIENTATION, 1);
+
+ processAxis(conv, time, EV_ABS, ABS_X, 50);
+ processAxis(conv, time, EV_ABS, ABS_Y, 100);
+ processAxis(conv, time, EV_ABS, ABS_PRESSURE, 42);
+
+ processAxis(conv, time, EV_KEY, BTN_TOUCH, 1);
+ processAxis(conv, time, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+ std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+
+ ASSERT_TRUE(schs.has_value());
+ ASSERT_EQ(2, schs->state.finger_cnt);
+ const FingerState& finger1 = schs->state.fingers[0];
+ EXPECT_EQ(123, finger1.tracking_id);
+ EXPECT_NEAR(50, finger1.position_x, EPSILON);
+ EXPECT_NEAR(100, finger1.position_y, EPSILON);
+ EXPECT_NEAR(5, finger1.touch_major, EPSILON);
+ EXPECT_NEAR(4, finger1.touch_minor, EPSILON);
+ EXPECT_NEAR(42, finger1.pressure, EPSILON);
+ EXPECT_NEAR(2, finger1.orientation, EPSILON);
+ EXPECT_EQ(0u, finger1.flags);
+
+ const FingerState& finger2 = schs->state.fingers[1];
+ EXPECT_EQ(456, finger2.tracking_id);
+ EXPECT_NEAR(-20, finger2.position_x, EPSILON);
+ EXPECT_NEAR(40, finger2.position_y, EPSILON);
+ EXPECT_NEAR(8, finger2.touch_major, EPSILON);
+ EXPECT_NEAR(7, finger2.touch_minor, EPSILON);
+ EXPECT_NEAR(21, finger2.pressure, EPSILON);
+ EXPECT_NEAR(1, finger2.orientation, EPSILON);
+ EXPECT_EQ(0u, finger2.flags);
+}
+
+TEST_F(HardwareStateConverterTest, ButtonPressed) {
+ const nsecs_t time = ARBITRARY_TIME;
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ HardwareStateConverter conv(deviceContext);
+
+ processAxis(conv, time, EV_KEY, BTN_LEFT, 1);
+ std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+
+ ASSERT_TRUE(schs.has_value());
+ EXPECT_EQ(GESTURES_BUTTON_LEFT, schs->state.buttons_down);
+}
+
+TEST_F(HardwareStateConverterTest, MscTimestamp) {
+ const nsecs_t time = ARBITRARY_TIME;
+ mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP);
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ HardwareStateConverter conv(deviceContext);
+
+ processAxis(conv, time, EV_MSC, MSC_TIMESTAMP, 1200000);
+ std::optional<SelfContainedHardwareState> schs = processSync(conv, time);
+
+ ASSERT_TRUE(schs.has_value());
+ EXPECT_NEAR(1.2, schs->state.msc_timestamp, EPSILON);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index f3239ca..d2ff097 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -29,6 +29,7 @@
#include <sys/epoll.h>
#include <cinttypes>
+#include <compare>
#include <thread>
#include <unordered_set>
#include <vector>
@@ -56,6 +57,7 @@
static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
static constexpr int32_t SECOND_DISPLAY_ID = 1;
+static constexpr int32_t ACTION_OUTSIDE = AMOTION_EVENT_ACTION_OUTSIDE;
static constexpr int32_t POINTER_1_DOWN =
AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
static constexpr int32_t POINTER_2_DOWN =
@@ -84,6 +86,7 @@
struct PointF {
float x;
float y;
+ auto operator<=>(const PointF&) const = default;
};
/**
@@ -110,6 +113,13 @@
*result_listener << "expected action " << MotionEvent::actionToString(action)
<< ", but got " << MotionEvent::actionToString(arg.getAction());
}
+ if (action == AMOTION_EVENT_ACTION_DOWN) {
+ if (!matches) {
+ *result_listener << "; ";
+ }
+ *result_listener << "downTime should match eventTime for ACTION_DOWN events";
+ matches &= arg.getDownTime() == arg.getEventTime();
+ }
if (action == AMOTION_EVENT_ACTION_CANCEL) {
if (!matches) {
*result_listener << "; ";
@@ -120,12 +130,34 @@
return matches;
}
+MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") {
+ return arg.getDownTime() == downTime;
+}
+
MATCHER_P(WithSource, source, "InputEvent with specified source") {
*result_listener << "expected source " << inputEventSourceToString(source) << ", but got "
<< inputEventSourceToString(arg.getSource());
return arg.getSource() == source;
}
+MATCHER_P2(WithCoords, x, y, "MotionEvent with specified coordinates") {
+ if (arg.getPointerCount() != 1) {
+ *result_listener << "Expected 1 pointer, got " << arg.getPointerCount();
+ return false;
+ }
+ return arg.getX(0 /*pointerIndex*/) == x && arg.getY(0 /*pointerIndex*/) == y;
+}
+
+MATCHER_P(WithPointers, pointers, "MotionEvent with specified pointers") {
+ // Build a map for the received pointers, by pointer id
+ std::map<int32_t /*pointerId*/, PointF> actualPointers;
+ for (size_t pointerIndex = 0; pointerIndex < arg.getPointerCount(); pointerIndex++) {
+ const int32_t pointerId = arg.getPointerId(pointerIndex);
+ actualPointers[pointerId] = {arg.getX(pointerIndex), arg.getY(pointerIndex)};
+ }
+ return pointers == actualPointers;
+}
+
// --- FakeInputDispatcherPolicy ---
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
@@ -1210,6 +1242,7 @@
void consumeMotionOutsideWithZeroedCoords(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
int32_t expectedFlags = 0) {
InputEvent* event = consume();
+ ASSERT_NE(nullptr, event);
ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType());
const MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent.getActionMasked());
@@ -1231,7 +1264,7 @@
void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
MotionEvent* motionEvent = consumeMotion();
- ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event";
+ ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event, but expected " << matcher;
ASSERT_THAT(*motionEvent, matcher);
}
@@ -2056,6 +2089,7 @@
mDispatcher->waitForIdle();
InputEvent* inputEvent1 = window1->consume();
+ ASSERT_NE(inputEvent1, nullptr);
window2->assertNoEvents();
MotionEvent& motionEvent1 = static_cast<MotionEvent&>(*inputEvent1);
nsecs_t downTimeForWindow1 = motionEvent1.getDownTime();
@@ -2065,6 +2099,7 @@
mDispatcher->notifyMotion(&(args = generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}})));
mDispatcher->waitForIdle();
InputEvent* inputEvent2 = window2->consume();
+ ASSERT_NE(inputEvent2, nullptr);
MotionEvent& motionEvent2 = static_cast<MotionEvent&>(*inputEvent2);
nsecs_t downTimeForWindow2 = motionEvent2.getDownTime();
ASSERT_NE(downTimeForWindow1, downTimeForWindow2);
@@ -2074,17 +2109,13 @@
mDispatcher->notifyMotion(
&(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent3 = window2->consume();
- MotionEvent& motionEvent3 = static_cast<MotionEvent&>(*inputEvent3);
- ASSERT_EQ(motionEvent3.getDownTime(), downTimeForWindow2);
+ window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
// Now add new touch down on the second window
mDispatcher->notifyMotion(
&(args = generateTouchArgs(POINTER_2_DOWN, {{50, 50}, {151, 51}, {150, 50}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent4 = window2->consume();
- MotionEvent& motionEvent4 = static_cast<MotionEvent&>(*inputEvent4);
- ASSERT_EQ(motionEvent4.getDownTime(), downTimeForWindow2);
+ window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
// TODO(b/232530217): do not send the unnecessary MOVE event and delete the next line
window1->consumeMotionMove();
@@ -2094,16 +2125,12 @@
mDispatcher->notifyMotion(
&(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent5 = window1->consume();
- MotionEvent& motionEvent5 = static_cast<MotionEvent&>(*inputEvent5);
- ASSERT_EQ(motionEvent5.getDownTime(), downTimeForWindow1);
+ window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
mDispatcher->notifyMotion(&(
args = generateTouchArgs(POINTER_3_DOWN, {{51, 51}, {151, 51}, {150, 50}, {50, 50}})));
mDispatcher->waitForIdle();
- InputEvent* inputEvent6 = window1->consume();
- MotionEvent& motionEvent6 = static_cast<MotionEvent&>(*inputEvent6);
- ASSERT_EQ(motionEvent6.getDownTime(), downTimeForWindow1);
+ window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
}
TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
@@ -2128,10 +2155,8 @@
.x(900)
.y(400))
.build()));
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
// Move cursor into left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2142,12 +2167,9 @@
.x(300)
.y(400))
.build()));
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
// Inject a series of mouse events for a mouse click
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2170,8 +2192,7 @@
.x(300)
.y(400))
.build()));
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2183,8 +2204,7 @@
.x(300)
.y(400))
.build()));
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2205,12 +2225,47 @@
.x(900)
.y(400))
.build()));
- windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+
+ // No more events
+ windowLeft->assertNoEvents();
+ windowRight->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, HoverWithSpyWindows) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 600, 800));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 600, 800));
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}});
+
+ // Send mouse cursor to the window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher,
+ MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE)
+ .x(100)
+ .y(100))
+ .build()));
+
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithSource(AINPUT_SOURCE_MOUSE)));
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithSource(AINPUT_SOURCE_MOUSE)));
+
+ window->assertNoEvents();
+ spyWindow->assertNoEvents();
}
// This test is different from the test above that HOVER_ENTER and HOVER_EXIT events are injected
@@ -2233,8 +2288,7 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
// Inject a series of mouse events for a mouse click
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2257,8 +2311,7 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2270,8 +2323,7 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(mDispatcher,
@@ -2291,8 +2343,7 @@
.x(300)
.y(400))
.build()));
- window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
}
/**
@@ -2362,14 +2413,13 @@
.x(300)
.y(600))
.build()));
- windowDefaultDisplay->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
- windowDefaultDisplay->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
// Remove all windows in secondary display and check that no event happens on window in
// primary display.
- mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}});
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {windowDefaultDisplay}}, {SECOND_DISPLAY_ID, {}}});
windowDefaultDisplay->assertNoEvents();
// Move cursor position in window in default display and check that only hover move
@@ -2385,8 +2435,9 @@
.x(400)
.y(700))
.build()));
- windowDefaultDisplay->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE,
- ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */);
+ windowDefaultDisplay->consumeMotionEvent(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE)));
windowDefaultDisplay->assertNoEvents();
}
@@ -2506,6 +2557,39 @@
}
/**
+ * Two windows. First is a regular window. Second does not overlap with the first, and has
+ * WATCH_OUTSIDE_TOUCH.
+ * Both windows are owned by the same UID.
+ * Tap first window. Make sure that the second window receives ACTION_OUTSIDE with correct, non-zero
+ * coordinates. The coordinates are not zeroed out because both windows are owned by the same UID.
+ */
+TEST_F(InputDispatcherTest, ActionOutsideForOwnedWindowHasValidCoordinates) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "First Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect{0, 0, 100, 100});
+
+ sp<FakeWindowHandle> outsideWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
+ ADISPLAY_ID_DEFAULT);
+ outsideWindow->setFrame(Rect{100, 100, 200, 200});
+ outsideWindow->setWatchOutsideTouch(true);
+ // outsideWindow must be above 'window' to receive ACTION_OUTSIDE events when 'window' is tapped
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {outsideWindow, window}}});
+
+ // Tap on first window.
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {PointF{50, 50}});
+ mDispatcher->notifyMotion(&motionArgs);
+ window->consumeMotionDown();
+ // The coordinates of the tap in 'outsideWindow' are relative to its top left corner.
+ // Therefore, we should offset them by (100, 100) relative to the screen's top left corner.
+ outsideWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_OUTSIDE), WithCoords(-50, -50)));
+}
+
+/**
* This test documents the behavior of WATCH_OUTSIDE_TOUCH. The window will get ACTION_OUTSIDE when
* a another pointer causes ACTION_DOWN to be sent to another window for the first time. Only one
* ACTION_OUTSIDE event is sent per gesture.
@@ -2540,7 +2624,9 @@
motionArgs = generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
{PointF{-10, -10}, PointF{105, 105}});
mDispatcher->notifyMotion(&motionArgs);
- window->consumeMotionOutside();
+ const std::map<int32_t, PointF> expectedPointers{{0, PointF{-10, -10}}, {1, PointF{105, 105}}};
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_OUTSIDE), WithPointers(expectedPointers)));
secondWindow->consumeMotionDown();
thirdWindow->assertNoEvents();
@@ -4669,6 +4755,7 @@
const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
assertMotionAction(expectedAction, motionEvent.getAction());
+ ASSERT_EQ(points.size(), motionEvent.getPointerCount());
for (size_t i = 0; i < points.size(); i++) {
float expectedX = points[i].x;
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
new file mode 100644
index 0000000..a02ef05
--- /dev/null
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InputMapperTest.h"
+
+#include <InputReaderBase.h>
+#include <gtest/gtest.h>
+#include <ui/Rotation.h>
+
+namespace android {
+
+const char* InputMapperTest::DEVICE_NAME = "device";
+const char* InputMapperTest::DEVICE_LOCATION = "USB1";
+const ftl::Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
+ ftl::Flags<InputDeviceClass>(0); // not needed for current tests
+
+void InputMapperTest::SetUp(ftl::Flags<InputDeviceClass> classes, int bus) {
+ mFakeEventHub = std::make_unique<FakeEventHub>();
+ mFakePolicy = sp<FakeInputReaderPolicy>::make();
+ mFakeListener = std::make_unique<TestInputListener>();
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, *mFakeListener);
+ mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes, bus);
+ // Consume the device reset notification generated when adding a new device.
+ mFakeListener->assertNotifyDeviceResetWasCalled();
+}
+
+void InputMapperTest::SetUp() {
+ SetUp(DEVICE_CLASSES);
+}
+
+void InputMapperTest::TearDown() {
+ mFakeListener.reset();
+ mFakePolicy.clear();
+}
+
+void InputMapperTest::addConfigurationProperty(const char* key, const char* value) {
+ mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
+}
+
+std::list<NotifyArgs> InputMapperTest::configureDevice(uint32_t changes) {
+ if (!changes ||
+ (changes &
+ (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
+ InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) {
+ mReader->requestRefreshConfiguration(changes);
+ mReader->loopOnce();
+ }
+ std::list<NotifyArgs> out =
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+ // Loop the reader to flush the input listener queue.
+ for (const NotifyArgs& args : out) {
+ mFakeListener->notify(args);
+ }
+ mReader->loopOnce();
+ return out;
+}
+
+std::shared_ptr<InputDevice> InputMapperTest::newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location,
+ int32_t eventHubId,
+ ftl::Flags<InputDeviceClass> classes,
+ int bus) {
+ InputDeviceIdentifier identifier;
+ identifier.name = name;
+ identifier.location = location;
+ identifier.bus = bus;
+ std::shared_ptr<InputDevice> device =
+ std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
+ identifier);
+ mReader->pushNextDevice(device);
+ mFakeEventHub->addDevice(eventHubId, name, classes, bus);
+ mReader->loopOnce();
+ return device;
+}
+
+void InputMapperTest::setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+ ui::Rotation orientation,
+ const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort,
+ ViewportType viewportType) {
+ mFakePolicy->addDisplayViewport(displayId, width, height, orientation, /* isActive= */ true,
+ uniqueId, physicalPort, viewportType);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+}
+
+void InputMapperTest::clearViewports() {
+ mFakePolicy->clearViewports();
+}
+
+std::list<NotifyArgs> InputMapperTest::process(InputMapper& mapper, nsecs_t when, nsecs_t readTime,
+ int32_t type, int32_t code, int32_t value) {
+ RawEvent event;
+ event.when = when;
+ event.readTime = readTime;
+ event.deviceId = mapper.getDeviceContext().getEventHubId();
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ std::list<NotifyArgs> processArgList = mapper.process(&event);
+ for (const NotifyArgs& args : processArgList) {
+ mFakeListener->notify(args);
+ }
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
+ return processArgList;
+}
+
+void InputMapperTest::resetMapper(InputMapper& mapper, nsecs_t when) {
+ const auto resetArgs = mapper.reset(when);
+ for (const auto args : resetArgs) {
+ mFakeListener->notify(args);
+ }
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
+}
+
+std::list<NotifyArgs> InputMapperTest::handleTimeout(InputMapper& mapper, nsecs_t when) {
+ std::list<NotifyArgs> generatedArgs = mapper.timeoutExpired(when);
+ for (const NotifyArgs& args : generatedArgs) {
+ mFakeListener->notify(args);
+ }
+ // Loop the reader to flush the input listener queue.
+ mReader->loopOnce();
+ return generatedArgs;
+}
+
+void InputMapperTest::assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source,
+ float min, float max, float flat, float fuzz) {
+ const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
+ ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source;
+ ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source;
+ ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source;
+ ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source;
+ ASSERT_NEAR(max, range->max, EPSILON) << "Axis: " << axis << " Source: " << source;
+ ASSERT_NEAR(flat, range->flat, EPSILON) << "Axis: " << axis << " Source: " << source;
+ ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source;
+}
+
+void InputMapperTest::assertPointerCoords(const PointerCoords& coords, float x, float y,
+ float pressure, float size, float touchMajor,
+ float touchMinor, float toolMajor, float toolMinor,
+ float orientation, float distance,
+ float scaledAxisEpsilon) {
+ ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), scaledAxisEpsilon);
+ ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), scaledAxisEpsilon);
+ ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON);
+ ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON);
+ ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), scaledAxisEpsilon);
+ ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), scaledAxisEpsilon);
+ ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), scaledAxisEpsilon);
+ ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), scaledAxisEpsilon);
+ ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON);
+ ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
new file mode 100644
index 0000000..63ca44c
--- /dev/null
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <list>
+#include <memory>
+
+#include <InputDevice.h>
+#include <InputMapper.h>
+#include <NotifyArgs.h>
+#include <ftl/flags.h>
+#include <utils/StrongPointer.h>
+
+#include "FakeEventHub.h"
+#include "FakeInputReaderPolicy.h"
+#include "InstrumentedInputReader.h"
+#include "TestConstants.h"
+#include "TestInputListener.h"
+
+namespace android {
+
+class InputMapperTest : public testing::Test {
+protected:
+ static const char* DEVICE_NAME;
+ static const char* DEVICE_LOCATION;
+ static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
+ static constexpr int32_t DEVICE_GENERATION = 2;
+ static constexpr int32_t DEVICE_CONTROLLER_NUMBER = 0;
+ static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES;
+ static constexpr int32_t EVENTHUB_ID = 1;
+
+ std::shared_ptr<FakeEventHub> mFakeEventHub;
+ sp<FakeInputReaderPolicy> mFakePolicy;
+ std::unique_ptr<TestInputListener> mFakeListener;
+ std::unique_ptr<InstrumentedInputReader> mReader;
+ std::shared_ptr<InputDevice> mDevice;
+
+ virtual void SetUp(ftl::Flags<InputDeviceClass> classes, int bus = 0);
+ void SetUp() override;
+ void TearDown() override;
+
+ void addConfigurationProperty(const char* key, const char* value);
+ std::list<NotifyArgs> configureDevice(uint32_t changes);
+ std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location, int32_t eventHubId,
+ ftl::Flags<InputDeviceClass> classes, int bus = 0);
+ template <class T, typename... Args>
+ T& addMapperAndConfigure(Args... args) {
+ T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
+ configureDevice(0);
+ std::list<NotifyArgs> resetArgList = mDevice->reset(ARBITRARY_TIME);
+ resetArgList += mapper.reset(ARBITRARY_TIME);
+ // Loop the reader to flush the input listener queue.
+ for (const NotifyArgs& loopArgs : resetArgList) {
+ mFakeListener->notify(loopArgs);
+ }
+ mReader->loopOnce();
+ return mapper;
+ }
+
+ void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+ ui::Rotation orientation, const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort,
+ ViewportType viewportType);
+ void clearViewports();
+ std::list<NotifyArgs> process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type,
+ int32_t code, int32_t value);
+ void resetMapper(InputMapper& mapper, nsecs_t when);
+
+ std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when);
+
+ static void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source,
+ float min, float max, float flat, float fuzz);
+ static void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure,
+ float size, float touchMajor, float touchMinor, float toolMajor,
+ float toolMinor, float orientation, float distance,
+ float scaledAxisEpsilon = 1.f);
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 0153967..db6bf45 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -45,6 +45,8 @@
#include "FakeEventHub.h"
#include "FakeInputReaderPolicy.h"
#include "FakePointerController.h"
+#include "InputMapperTest.h"
+#include "InstrumentedInputReader.h"
#include "TestConstants.h"
#include "android/hardware/input/InputDeviceCountryCode.h"
#include "input/DisplayViewport.h"
@@ -91,9 +93,6 @@
static constexpr int32_t ACTION_POINTER_1_UP =
AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-// Error tolerance for floating point assertions.
-static const float EPSILON = 0.001f;
-
// Minimum timestamp separation between subsequent input events from a Bluetooth device.
static constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
// Maximum smoothing time delta so that we don't generate events too far into the future.
@@ -345,117 +344,6 @@
}
};
-
-// --- InstrumentedInputReader ---
-
-class InstrumentedInputReader : public InputReader {
- std::queue<std::shared_ptr<InputDevice>> mNextDevices;
-
-public:
- InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
- const sp<InputReaderPolicyInterface>& policy,
- InputListenerInterface& listener)
- : InputReader(eventHub, policy, listener), mFakeContext(this) {}
-
- virtual ~InstrumentedInputReader() {}
-
- void pushNextDevice(std::shared_ptr<InputDevice> device) { mNextDevices.push(device); }
-
- std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
- const std::string& location = "") {
- InputDeviceIdentifier identifier;
- identifier.name = name;
- identifier.location = location;
- int32_t generation = deviceId + 1;
- return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier);
- }
-
- // Make the protected loopOnce method accessible to tests.
- using InputReader::loopOnce;
-
-protected:
- virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t eventHubId,
- const InputDeviceIdentifier& identifier)
- REQUIRES(mLock) {
- if (!mNextDevices.empty()) {
- std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
- mNextDevices.pop();
- return device;
- }
- return InputReader::createDeviceLocked(eventHubId, identifier);
- }
-
- // --- FakeInputReaderContext ---
- class FakeInputReaderContext : public ContextImpl {
- int32_t mGlobalMetaState;
- bool mUpdateGlobalMetaStateWasCalled;
- int32_t mGeneration;
- std::optional<nsecs_t> mRequestedTimeout;
- std::vector<InputDeviceInfo> mExternalStylusDevices;
-
- public:
- FakeInputReaderContext(InputReader* reader)
- : ContextImpl(reader),
- mGlobalMetaState(0),
- mUpdateGlobalMetaStateWasCalled(false),
- mGeneration(1) {}
-
- virtual ~FakeInputReaderContext() {}
-
- void assertUpdateGlobalMetaStateWasCalled() {
- ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
- << "Expected updateGlobalMetaState() to have been called.";
- mUpdateGlobalMetaStateWasCalled = false;
- }
-
- void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; }
-
- uint32_t getGeneration() { return mGeneration; }
-
- void updateGlobalMetaState() override {
- mUpdateGlobalMetaStateWasCalled = true;
- ContextImpl::updateGlobalMetaState();
- }
-
- int32_t getGlobalMetaState() override {
- return mGlobalMetaState | ContextImpl::getGlobalMetaState();
- }
-
- int32_t bumpGeneration() override {
- mGeneration = ContextImpl::bumpGeneration();
- return mGeneration;
- }
-
- void requestTimeoutAtTime(nsecs_t when) override { mRequestedTimeout = when; }
-
- void assertTimeoutWasRequested(nsecs_t when) {
- ASSERT_TRUE(mRequestedTimeout) << "Expected timeout at time " << when
- << " but there was no timeout requested.";
- ASSERT_EQ(when, *mRequestedTimeout);
- mRequestedTimeout.reset();
- }
-
- void assertTimeoutWasNotRequested() {
- ASSERT_FALSE(mRequestedTimeout) << "Expected no timeout to have been requested,"
- " but one was requested at time "
- << *mRequestedTimeout;
- }
-
- void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {
- outDevices = mExternalStylusDevices;
- }
-
- void setExternalStylusDevices(std::vector<InputDeviceInfo>&& devices) {
- mExternalStylusDevices = devices;
- }
- } mFakeContext;
-
- friend class InputReaderTest;
-
-public:
- FakeInputReaderContext* getContext() { return &mFakeContext; }
-};
-
// --- InputReaderPolicyTest ---
class InputReaderPolicyTest : public testing::Test {
protected:
@@ -2617,195 +2505,6 @@
ASSERT_EQ(DEVICE_BLUETOOTH_ADDRESS, *address);
}
-// --- InputMapperTest ---
-
-class InputMapperTest : public testing::Test {
-protected:
- static const char* DEVICE_NAME;
- static const char* DEVICE_LOCATION;
- static const int32_t DEVICE_ID;
- static const int32_t DEVICE_GENERATION;
- static const int32_t DEVICE_CONTROLLER_NUMBER;
- static const ftl::Flags<InputDeviceClass> DEVICE_CLASSES;
- static const int32_t EVENTHUB_ID;
-
- std::shared_ptr<FakeEventHub> mFakeEventHub;
- sp<FakeInputReaderPolicy> mFakePolicy;
- std::unique_ptr<TestInputListener> mFakeListener;
- std::unique_ptr<InstrumentedInputReader> mReader;
- std::shared_ptr<InputDevice> mDevice;
-
- virtual void SetUp(ftl::Flags<InputDeviceClass> classes, int bus = 0) {
- mFakeEventHub = std::make_unique<FakeEventHub>();
- mFakePolicy = sp<FakeInputReaderPolicy>::make();
- mFakeListener = std::make_unique<TestInputListener>();
- mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
- *mFakeListener);
- mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes, bus);
- // Consume the device reset notification generated when adding a new device.
- mFakeListener->assertNotifyDeviceResetWasCalled();
- }
-
- void SetUp() override {
- SetUp(DEVICE_CLASSES);
- }
-
- void TearDown() override {
- mFakeListener.reset();
- mFakePolicy.clear();
- }
-
- void addConfigurationProperty(const char* key, const char* value) {
- mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, key, value);
- }
-
- std::list<NotifyArgs> configureDevice(uint32_t changes) {
- if (!changes ||
- (changes &
- (InputReaderConfiguration::CHANGE_DISPLAY_INFO |
- InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) {
- mReader->requestRefreshConfiguration(changes);
- mReader->loopOnce();
- }
- std::list<NotifyArgs> out =
- mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
- // Loop the reader to flush the input listener queue.
- for (const NotifyArgs& args : out) {
- mFakeListener->notify(args);
- }
- mReader->loopOnce();
- return out;
- }
-
- std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
- const std::string& location, int32_t eventHubId,
- ftl::Flags<InputDeviceClass> classes, int bus = 0) {
- InputDeviceIdentifier identifier;
- identifier.name = name;
- identifier.location = location;
- identifier.bus = bus;
- std::shared_ptr<InputDevice> device =
- std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
- identifier);
- mReader->pushNextDevice(device);
- mFakeEventHub->addDevice(eventHubId, name, classes, bus);
- mReader->loopOnce();
- return device;
- }
-
- template <class T, typename... Args>
- T& addMapperAndConfigure(Args... args) {
- T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
- configureDevice(0);
- std::list<NotifyArgs> resetArgList = mDevice->reset(ARBITRARY_TIME);
- resetArgList += mapper.reset(ARBITRARY_TIME);
- // Loop the reader to flush the input listener queue.
- for (const NotifyArgs& loopArgs : resetArgList) {
- mFakeListener->notify(loopArgs);
- }
- mReader->loopOnce();
- return mapper;
- }
-
- void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
- ui::Rotation orientation, const std::string& uniqueId,
- std::optional<uint8_t> physicalPort,
- ViewportType viewportType) {
- mFakePolicy->addDisplayViewport(displayId, width, height, orientation, true /*isActive*/,
- uniqueId, physicalPort, viewportType);
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
- }
-
- void clearViewports() {
- mFakePolicy->clearViewports();
- }
-
- std::list<NotifyArgs> process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type,
- int32_t code, int32_t value) {
- RawEvent event;
- event.when = when;
- event.readTime = readTime;
- event.deviceId = mapper.getDeviceContext().getEventHubId();
- event.type = type;
- event.code = code;
- event.value = value;
- std::list<NotifyArgs> processArgList = mapper.process(&event);
- for (const NotifyArgs& args : processArgList) {
- mFakeListener->notify(args);
- }
- // Loop the reader to flush the input listener queue.
- mReader->loopOnce();
- return processArgList;
- }
-
- void resetMapper(InputMapper& mapper, nsecs_t when) {
- const auto resetArgs = mapper.reset(when);
- for (const auto& args : resetArgs) {
- mFakeListener->notify(args);
- }
- // Loop the reader to flush the input listener queue.
- mReader->loopOnce();
- }
-
- std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when) {
- std::list<NotifyArgs> generatedArgs = mapper.timeoutExpired(when);
- for (const NotifyArgs& args : generatedArgs) {
- mFakeListener->notify(args);
- }
- // Loop the reader to flush the input listener queue.
- mReader->loopOnce();
- return generatedArgs;
- }
-
- static void assertMotionRange(const InputDeviceInfo& info,
- int32_t axis, uint32_t source, float min, float max, float flat, float fuzz) {
- const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
- ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source;
- ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source;
- ASSERT_EQ(source, range->source) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(min, range->min, EPSILON) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(max, range->max, EPSILON) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(flat, range->flat, EPSILON) << "Axis: " << axis << " Source: " << source;
- ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source;
- }
-
- static void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure,
- float size, float touchMajor, float touchMinor, float toolMajor,
- float toolMinor, float orientation, float distance,
- float scaledAxisEpsilon = 1.f) {
- ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), scaledAxisEpsilon);
- ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), scaledAxisEpsilon);
- ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON);
- ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON);
- ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
- scaledAxisEpsilon);
- ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON);
- ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON);
- }
-
- static void assertPosition(const FakePointerController& controller, float x, float y) {
- float actualX, actualY;
- controller.getPosition(&actualX, &actualY);
- ASSERT_NEAR(x, actualX, 1);
- ASSERT_NEAR(y, actualY, 1);
- }
-};
-
-const char* InputMapperTest::DEVICE_NAME = "device";
-const char* InputMapperTest::DEVICE_LOCATION = "USB1";
-const int32_t InputMapperTest::DEVICE_ID = END_RESERVED_ID + 1000;
-const int32_t InputMapperTest::DEVICE_GENERATION = 2;
-const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
-const ftl::Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
- ftl::Flags<InputDeviceClass>(0); // not needed for current tests
-const int32_t InputMapperTest::EVENTHUB_ID = 1;
-
// --- SwitchInputMapperTest ---
class SwitchInputMapperTest : public InputMapperTest {
@@ -3213,6 +2912,27 @@
ASSERT_EQ(ARBITRARY_TIME, args.downTime);
}
+TEST_F(KeyboardInputMapperTest, Process_KeyRemapping) {
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_B, 0, AKEYCODE_B, 0);
+ mFakeEventHub->addKeyRemapping(EVENTHUB_ID, AKEYCODE_A, AKEYCODE_B);
+
+ KeyboardInputMapper& mapper =
+ addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+
+ // Key down by scan code.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1);
+ NotifyKeyArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AKEYCODE_B, args.keyCode);
+
+ // Key up by scan code.
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(AKEYCODE_B, args.keyCode);
+}
+
/**
* Ensure that the readTime is set to the time when the EV_KEY is received.
*/
@@ -4624,7 +4344,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
}
TEST_F(CursorInputMapperTest, Process_PointerCapture) {
@@ -4652,7 +4372,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
10.0f, 20.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 100.0f, 200.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(100.0f, 200.0f));
// Button press.
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
@@ -4691,7 +4411,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
30.0f, 40.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 100.0f, 200.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(100.0f, 200.0f));
// Disable pointer capture and check that the device generation got bumped
// and events are generated the usual way.
@@ -4711,7 +4431,7 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
}
/**
@@ -4828,7 +4548,7 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
WithCoords(110.0f, 220.0f))));
- ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
}
TEST_F(CursorInputMapperTest, ConfigureDisplayId_WithAssociatedViewport) {
@@ -4855,7 +4575,7 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
WithCoords(110.0f, 220.0f))));
- ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
}
TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPointerDisplay) {
@@ -6785,6 +6505,17 @@
WithCoords(toDisplayX(100), toDisplayY(200)), WithButtonState(0))));
}
+TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsSetToTouchNavigation_setsCorrectType) {
+ mFakePolicy->addDeviceTypeAssociation(DEVICE_LOCATION, "touchNavigation");
+ prepareDisplay(ui::ROTATION_0);
+ prepareButtons();
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+
+ ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mapper.getSources());
+}
+
// --- TouchDisplayProjectionTest ---
class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
diff --git a/services/inputflinger/tests/InstrumentedInputReader.cpp b/services/inputflinger/tests/InstrumentedInputReader.cpp
new file mode 100644
index 0000000..1f8cd12
--- /dev/null
+++ b/services/inputflinger/tests/InstrumentedInputReader.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InstrumentedInputReader.h"
+
+namespace android {
+
+InstrumentedInputReader::InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ InputListenerInterface& listener)
+ : InputReader(eventHub, policy, listener), mFakeContext(this) {}
+
+void InstrumentedInputReader::pushNextDevice(std::shared_ptr<InputDevice> device) {
+ mNextDevices.push(device);
+}
+
+std::shared_ptr<InputDevice> InstrumentedInputReader::newDevice(int32_t deviceId,
+ const std::string& name,
+ const std::string& location) {
+ InputDeviceIdentifier identifier;
+ identifier.name = name;
+ identifier.location = location;
+ int32_t generation = deviceId + 1;
+ return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier);
+}
+
+std::shared_ptr<InputDevice> InstrumentedInputReader::createDeviceLocked(
+ int32_t eventHubId, const InputDeviceIdentifier& identifier) REQUIRES(mLock) {
+ if (!mNextDevices.empty()) {
+ std::shared_ptr<InputDevice> device(std::move(mNextDevices.front()));
+ mNextDevices.pop();
+ return device;
+ }
+ return InputReader::createDeviceLocked(eventHubId, identifier);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h
new file mode 100644
index 0000000..7f8d556
--- /dev/null
+++ b/services/inputflinger/tests/InstrumentedInputReader.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <queue>
+#include <string>
+
+#include <InputDevice.h>
+#include <InputReader.h>
+#include <gtest/gtest.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class InstrumentedInputReader : public InputReader {
+public:
+ InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
+ const sp<InputReaderPolicyInterface>& policy,
+ InputListenerInterface& listener);
+ virtual ~InstrumentedInputReader() {}
+
+ void pushNextDevice(std::shared_ptr<InputDevice> device);
+
+ std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+ const std::string& location = "");
+
+ // Make the protected loopOnce method accessible to tests.
+ using InputReader::loopOnce;
+
+protected:
+ virtual std::shared_ptr<InputDevice> createDeviceLocked(
+ int32_t eventHubId, const InputDeviceIdentifier& identifier);
+
+ class FakeInputReaderContext : public ContextImpl {
+ public:
+ FakeInputReaderContext(InputReader* reader)
+ : ContextImpl(reader),
+ mGlobalMetaState(0),
+ mUpdateGlobalMetaStateWasCalled(false),
+ mGeneration(1) {}
+
+ virtual ~FakeInputReaderContext() {}
+
+ void assertUpdateGlobalMetaStateWasCalled() {
+ ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
+ << "Expected updateGlobalMetaState() to have been called.";
+ mUpdateGlobalMetaStateWasCalled = false;
+ }
+
+ void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; }
+
+ uint32_t getGeneration() { return mGeneration; }
+
+ void updateGlobalMetaState() override {
+ mUpdateGlobalMetaStateWasCalled = true;
+ ContextImpl::updateGlobalMetaState();
+ }
+
+ int32_t getGlobalMetaState() override {
+ return mGlobalMetaState | ContextImpl::getGlobalMetaState();
+ }
+
+ int32_t bumpGeneration() override {
+ mGeneration = ContextImpl::bumpGeneration();
+ return mGeneration;
+ }
+
+ void requestTimeoutAtTime(nsecs_t when) override { mRequestedTimeout = when; }
+
+ void assertTimeoutWasRequested(nsecs_t when) {
+ ASSERT_TRUE(mRequestedTimeout) << "Expected timeout at time " << when
+ << " but there was no timeout requested.";
+ ASSERT_EQ(when, *mRequestedTimeout);
+ mRequestedTimeout.reset();
+ }
+
+ void assertTimeoutWasNotRequested() {
+ ASSERT_FALSE(mRequestedTimeout) << "Expected no timeout to have been requested,"
+ " but one was requested at time "
+ << *mRequestedTimeout;
+ }
+
+ void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {
+ outDevices = mExternalStylusDevices;
+ }
+
+ void setExternalStylusDevices(std::vector<InputDeviceInfo>&& devices) {
+ mExternalStylusDevices = devices;
+ }
+
+ private:
+ int32_t mGlobalMetaState;
+ bool mUpdateGlobalMetaStateWasCalled;
+ int32_t mGeneration;
+ std::optional<nsecs_t> mRequestedTimeout;
+ std::vector<InputDeviceInfo> mExternalStylusDevices;
+ } mFakeContext;
+
+ friend class InputReaderTest;
+
+public:
+ FakeInputReaderContext* getContext() { return &mFakeContext; }
+
+private:
+ std::queue<std::shared_ptr<InputDevice>> mNextDevices;
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/TestConstants.h b/services/inputflinger/tests/TestConstants.h
index 8dc9d71..27881f6 100644
--- a/services/inputflinger/tests/TestConstants.h
+++ b/services/inputflinger/tests/TestConstants.h
@@ -27,4 +27,7 @@
static constexpr nsecs_t ARBITRARY_TIME = 1234;
static constexpr nsecs_t READ_TIME = 4321;
+// Error tolerance for floating point assertions.
+static const float EPSILON = 0.001f;
+
} // namespace android
diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h
index 9db3422..e5a4b14 100644
--- a/services/inputflinger/tests/TestInputListenerMatchers.h
+++ b/services/inputflinger/tests/TestInputListenerMatchers.h
@@ -74,6 +74,14 @@
return argX == x && argY == y;
}
+MATCHER_P2(WithRelativeMotion, x, y, "InputEvent with specified relative motion") {
+ const auto argX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ const auto argY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ *result_listener << "expected relative motion (" << x << ", " << y << "), but got (" << argX
+ << ", " << argY << ")";
+ return argX == x && argY == y;
+}
+
MATCHER_P(WithPressure, pressure, "InputEvent with specified pressure") {
const auto argPressure = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
*result_listener << "expected pressure " << pressure << ", but got " << argPressure;
@@ -97,6 +105,12 @@
return arg.buttonState == buttons;
}
+MATCHER_P(WithActionButton, actionButton, "InputEvent with specified action button") {
+ *result_listener << "expected action button " << actionButton << ", but got "
+ << arg.actionButton;
+ return arg.actionButton == actionButton;
+}
+
MATCHER_P(WithEventTime, eventTime, "InputEvent with specified eventTime") {
*result_listener << "expected event time " << eventTime << ", but got " << arg.eventTime;
return arg.eventTime == eventTime;
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index 2eed997..057c15d 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -153,6 +153,10 @@
return reader->getLightPlayerId(deviceId, lightId);
}
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+ reader->addKeyRemapping(deviceId, fromKeyCode, toKeyCode);
+ }
+
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode);
}
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 445ed18..cd852d6 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -211,6 +211,7 @@
int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
return mFdp->ConsumeIntegral<int32_t>();
}
+ void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override {}
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override {
return mFdp->ConsumeIntegral<int32_t>();
}
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 0eeb820..11c56a8 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -76,7 +76,7 @@
"libaidlcommonsupport",
"android.hardware.sensors@1.0-convert",
"android.hardware.sensors-V1-convert",
- "android.hardware.sensors-V1-ndk",
+ "android.hardware.sensors-V2-ndk",
],
generated_headers: ["framework-cppstream-protos"],
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index 291c770..4ac9651 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -151,7 +151,7 @@
return PERMISSION_DENIED;
}
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si == nullptr) {
return NAME_NOT_FOUND;
}
@@ -228,7 +228,7 @@
for (auto &i : existingConnections) {
int handle = i.first;
int rateLevel = i.second;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr) {
const Sensor& s = si->getSensor();
if (mService->isSensorInCappedSet(s.getType()) &&
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index f06f947..82d0295 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -160,7 +160,7 @@
bool SensorService::SensorEventConnection::addSensor(int32_t handle) {
Mutex::Autolock _l(mConnectionLock);
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si == nullptr ||
!mService->canAccessSensor(si->getSensor(), "Add to SensorEventConnection: ",
mOpPackageName) ||
@@ -202,7 +202,7 @@
Mutex::Autolock _l(mConnectionLock);
for (auto &it : mSensorInfo) {
const int handle = it.first;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr && si->getSensor().getReportingMode() == AREPORTING_MODE_ONE_SHOT) {
return true;
}
@@ -245,7 +245,7 @@
if (mDataInjectionMode) looper_flags |= ALOOPER_EVENT_INPUT;
for (auto& it : mSensorInfo) {
const int handle = it.first;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr && si->getSensor().isWakeUpSensor()) {
looper_flags |= ALOOPER_EVENT_INPUT;
}
@@ -555,7 +555,7 @@
// flush complete events to be sent.
for (auto& it : mSensorInfo) {
const int handle = it.first;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si == nullptr) {
continue;
}
@@ -689,7 +689,7 @@
if (enabled) {
nsecs_t requestedSamplingPeriodNs = samplingPeriodNs;
bool isSensorCapped = false;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr) {
const Sensor& s = si->getSensor();
if (mService->isSensorInCappedSet(s.getType())) {
@@ -729,7 +729,7 @@
nsecs_t requestedSamplingPeriodNs = samplingPeriodNs;
bool isSensorCapped = false;
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si != nullptr) {
const Sensor& s = si->getSensor();
if (mService->isSensorInCappedSet(s.getType())) {
@@ -852,7 +852,7 @@
}
sensors_event_t sensor_event;
memcpy(&sensor_event, buf, sizeof(sensors_event_t));
- sp<SensorInterface> si =
+ std::shared_ptr<SensorInterface> si =
mService->getSensorInterfaceFromHandle(sensor_event.sensor);
if (si == nullptr) {
return 1;
@@ -903,7 +903,7 @@
size_t fifoWakeUpSensors = 0;
size_t fifoNonWakeUpSensors = 0;
for (auto& it : mSensorInfo) {
- sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(it.first);
+ std::shared_ptr<SensorInterface> si = mService->getSensorInterfaceFromHandle(it.first);
if (si == nullptr) {
continue;
}
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index 6d36b47..daff4d0 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -28,13 +28,13 @@
const Sensor SensorList::mNonSensor = Sensor("unknown");
-bool SensorList::add(
- int handle, SensorInterface* si, bool isForDebug, bool isVirtual, int deviceId) {
+bool SensorList::add(int handle, std::shared_ptr<SensorInterface> si, bool isForDebug,
+ bool isVirtual, int deviceId) {
std::lock_guard<std::mutex> lk(mLock);
if (handle == si->getSensor().getHandle() &&
mUsedHandle.insert(handle).second) {
// will succeed as the mUsedHandle does not have this handle
- mHandleMap.emplace(handle, Entry(si, isForDebug, isVirtual, deviceId));
+ mHandleMap.emplace(handle, Entry(std::move(si), isForDebug, isVirtual, deviceId));
return true;
}
// handle exist already or handle mismatch
@@ -63,12 +63,12 @@
mNonSensor.getStringType());
}
-sp<SensorInterface> SensorList::getInterface(int handle) const {
- return getOne<sp<SensorInterface>>(
- handle, [] (const Entry& e) -> sp<SensorInterface> {return e.si;}, nullptr);
+std::shared_ptr<SensorInterface> SensorList::getInterface(int handle) const {
+ return getOne<std::shared_ptr<SensorInterface>>(
+ handle, [] (const Entry& e) -> std::shared_ptr<SensorInterface> {return e.si;},
+ nullptr);
}
-
bool SensorList::isNewHandle(int handle) const {
std::lock_guard<std::mutex> lk(mLock);
return mUsedHandle.find(handle) == mUsedHandle.end();
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
index 79f6701..ad5b21f 100644
--- a/services/sensorservice/SensorList.h
+++ b/services/sensorservice/SensorList.h
@@ -37,22 +37,18 @@
class SensorList : public Dumpable {
public:
struct Entry {
- sp<SensorInterface> si;
+ std::shared_ptr<SensorInterface> si;
const bool isForDebug;
const bool isVirtual;
const int deviceId;
- Entry(SensorInterface* si_, bool debug_, bool virtual_, int deviceId_) :
- si(si_), isForDebug(debug_), isVirtual(virtual_), deviceId(deviceId_) {
+ Entry(std::shared_ptr<SensorInterface> si_, bool debug_, bool virtual_, int deviceId_) :
+ si(std::move(si_)), isForDebug(debug_), isVirtual(virtual_), deviceId(deviceId_) {
}
};
- // After SensorInterface * is added into SensorList, it can be assumed that SensorList own the
- // object it pointed to and the object should not be released elsewhere.
- bool add(int handle, SensorInterface* si, bool isForDebug = false, bool isVirtual = false,
- int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID);
-
- // After a handle is removed, the object that SensorInterface * pointing to may get deleted if
- // no more sp<> of the same object exist.
+ // SensorList owns the SensorInterface pointer.
+ bool add(int handle, std::shared_ptr<SensorInterface> si, bool isForDebug = false,
+ bool isVirtual = false, int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID);
bool remove(int handle);
inline bool hasAnySensor() const { return mHandleMap.size() > 0;}
@@ -67,7 +63,7 @@
String8 getName(int handle) const;
String8 getStringType(int handle) const;
- sp<SensorInterface> getInterface(int handle) const;
+ std::shared_ptr<SensorInterface> getInterface(int handle) const;
bool isNewHandle(int handle) const;
// Iterate through Sensor in sensor list and perform operation f on each Sensor object.
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 0c9fef5..6504b79 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <aidl/android/hardware/sensors/ISensors.h>
#include <android-base/strings.h>
#include <android/content/pm/IPackageManagerNative.h>
#include <android/util/ProtoOutputStream.h>
@@ -104,12 +105,10 @@
namespace {
-// TODO(b/259227294): Move the sensor ranges to the HAL.
int32_t nextRuntimeSensorHandle() {
- static constexpr int32_t kRuntimeHandleBase = 0x5F000000;
- static constexpr int32_t kRuntimeHandleEnd = 0x5FFFFFFF;
- static int32_t nextHandle = kRuntimeHandleBase;
- if (nextHandle == kRuntimeHandleEnd) {
+ using ::aidl::android::hardware::sensors::ISensors;
+ static int32_t nextHandle = ISensors::RUNTIME_SENSORS_HANDLE_BASE;
+ if (nextHandle == ISensors::RUNTIME_SENSORS_HANDLE_END) {
return -1;
}
return nextHandle++;
@@ -183,15 +182,14 @@
sensor_t runtimeSensor = sensor;
// force the handle to be consistent
runtimeSensor.handle = handle;
- SensorInterface *si = new RuntimeSensor(runtimeSensor, std::move(runtimeSensorCallback));
+ auto si = std::make_shared<RuntimeSensor>(runtimeSensor, std::move(runtimeSensorCallback));
Mutex::Autolock _l(mLock);
- const Sensor& s = registerSensor(si, /* isDebug= */ false, /* isVirtual= */ false, deviceId);
-
- if (s.getHandle() != handle) {
+ if (!registerSensor(std::move(si), /* isDebug= */ false, /* isVirtual= */ false, deviceId)) {
// The registration was unsuccessful.
- return s.getHandle();
+ return mSensors.getNonSensor().getHandle();
}
+
return handle;
}
@@ -319,11 +317,13 @@
}
if (useThisSensor) {
if (list[i].type == SENSOR_TYPE_PROXIMITY) {
- SensorInterface* s = new ProximitySensor(list[i], *this);
- registerSensor(s);
- mProxSensorHandles.push_back(s->getSensor().getHandle());
+ auto s = std::make_shared<ProximitySensor>(list[i], *this);
+ const int handle = s->getSensor().getHandle();
+ if (registerSensor(std::move(s))) {
+ mProxSensorHandles.push_back(handle);
+ }
} else {
- registerSensor(new HardwareSensor(list[i]));
+ registerSensor(std::make_shared<HardwareSensor>(list[i]));
}
}
}
@@ -338,56 +338,63 @@
// available in the HAL
bool needRotationVector =
(virtualSensorsNeeds & (1<<SENSOR_TYPE_ROTATION_VECTOR)) != 0;
-
- registerSensor(new RotationVectorSensor(), !needRotationVector, true);
- registerSensor(new OrientationSensor(), !needRotationVector, true);
+ registerVirtualSensor(std::make_shared<RotationVectorSensor>(),
+ /* isDebug= */ !needRotationVector);
+ registerVirtualSensor(std::make_shared<OrientationSensor>(),
+ /* isDebug= */ !needRotationVector);
// virtual debugging sensors are not for user
- registerSensor( new CorrectedGyroSensor(list, count), true, true);
- registerSensor( new GyroDriftSensor(), true, true);
+ registerVirtualSensor(std::make_shared<CorrectedGyroSensor>(list, count),
+ /* isDebug= */ true);
+ registerVirtualSensor(std::make_shared<GyroDriftSensor>(), /* isDebug= */ true);
}
if (hasAccel && (hasGyro || hasGyroUncalibrated)) {
bool needGravitySensor = (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) != 0;
- registerSensor(new GravitySensor(list, count), !needGravitySensor, true);
+ registerVirtualSensor(std::make_shared<GravitySensor>(list, count),
+ /* isDebug= */ !needGravitySensor);
bool needLinearAcceleration =
(virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) != 0;
- registerSensor(new LinearAccelerationSensor(list, count),
- !needLinearAcceleration, true);
+ registerVirtualSensor(std::make_shared<LinearAccelerationSensor>(list, count),
+ /* isDebug= */ !needLinearAcceleration);
bool needGameRotationVector =
(virtualSensorsNeeds & (1<<SENSOR_TYPE_GAME_ROTATION_VECTOR)) != 0;
- registerSensor(new GameRotationVectorSensor(), !needGameRotationVector, true);
+ registerVirtualSensor(std::make_shared<GameRotationVectorSensor>(),
+ /* isDebug= */ !needGameRotationVector);
}
if (hasAccel && hasMag) {
bool needGeoMagRotationVector =
(virtualSensorsNeeds & (1<<SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR)) != 0;
- registerSensor(new GeoMagRotationVectorSensor(), !needGeoMagRotationVector, true);
+ registerVirtualSensor(std::make_shared<GeoMagRotationVectorSensor>(),
+ /* isDebug= */ !needGeoMagRotationVector);
}
if (isAutomotive()) {
if (hasAccel) {
- registerSensor(new LimitedAxesImuSensor(list, count, SENSOR_TYPE_ACCELEROMETER),
- /*isDebug=*/false, /*isVirtual=*/true);
+ registerVirtualSensor(
+ std::make_shared<LimitedAxesImuSensor>(
+ list, count, SENSOR_TYPE_ACCELEROMETER));
}
if (hasGyro) {
- registerSensor(new LimitedAxesImuSensor(list, count, SENSOR_TYPE_GYROSCOPE),
- /*isDebug=*/false, /*isVirtual=*/true);
+ registerVirtualSensor(
+ std::make_shared<LimitedAxesImuSensor>(
+ list, count, SENSOR_TYPE_GYROSCOPE));
}
if (hasAccelUncalibrated) {
- registerSensor(new LimitedAxesImuSensor(list, count,
- SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED),
- /*isDebug=*/false, /*isVirtual=*/true);
+ registerVirtualSensor(
+ std::make_shared<LimitedAxesImuSensor>(
+ list, count, SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED));
}
if (hasGyroUncalibrated) {
- registerSensor(new LimitedAxesImuSensor(list, count,
- SENSOR_TYPE_GYROSCOPE_UNCALIBRATED),
- /*isDebug=*/false, /*isVirtual=*/true);
+ registerVirtualSensor(
+ std::make_shared<LimitedAxesImuSensor>(
+ list, count, SENSOR_TYPE_GYROSCOPE_UNCALIBRATED));
}
}
@@ -488,20 +495,21 @@
&& isUidActive(uid) && !isOperationRestrictedLocked(opPackageName);
}
-const Sensor& SensorService::registerSensor(SensorInterface* s, bool isDebug, bool isVirtual,
- int deviceId) {
- int handle = s->getSensor().getHandle();
- int type = s->getSensor().getType();
- if (mSensors.add(handle, s, isDebug, isVirtual, deviceId)) {
+bool SensorService::registerSensor(std::shared_ptr<SensorInterface> s, bool isDebug, bool isVirtual,
+ int deviceId) {
+ const int handle = s->getSensor().getHandle();
+ const int type = s->getSensor().getType();
+ if (mSensors.add(handle, std::move(s), isDebug, isVirtual, deviceId)) {
mRecentEvent.emplace(handle, new SensorServiceUtil::RecentEventLogger(type));
- return s->getSensor();
+ return true;
} else {
- return mSensors.getNonSensor();
+ LOG_FATAL("Failed to register sensor with handle %d", handle);
+ return false;
}
}
-const Sensor& SensorService::registerDynamicSensorLocked(SensorInterface* s, bool isDebug) {
- return registerSensor(s, isDebug);
+bool SensorService::registerDynamicSensorLocked(std::shared_ptr<SensorInterface> s, bool isDebug) {
+ return registerSensor(std::move(s), isDebug);
}
bool SensorService::unregisterDynamicSensorLocked(int handle) {
@@ -515,8 +523,8 @@
return ret;
}
-const Sensor& SensorService::registerVirtualSensor(SensorInterface* s, bool isDebug) {
- return registerSensor(s, isDebug, true);
+bool SensorService::registerVirtualSensor(std::shared_ptr<SensorInterface> s, bool isDebug) {
+ return registerSensor(std::move(s), isDebug, true);
}
SensorService::~SensorService() {
@@ -611,8 +619,8 @@
result.append("Recent Sensor events:\n");
for (auto&& i : mRecentEvent) {
- sp<SensorInterface> s = mSensors.getInterface(i.first);
- if (!i.second->isEmpty()) {
+ std::shared_ptr<SensorInterface> s = getSensorInterfaceFromHandle(i.first);
+ if (!i.second->isEmpty() && s != nullptr) {
if (privileged || s->getSensor().getRequiredPermission().isEmpty()) {
i.second->setFormat("normal");
} else {
@@ -729,8 +737,8 @@
// Write SensorEventsProto
token = proto.start(SENSOR_EVENTS);
for (auto&& i : mRecentEvent) {
- sp<SensorInterface> s = mSensors.getInterface(i.first);
- if (!i.second->isEmpty()) {
+ std::shared_ptr<SensorInterface> s = getSensorInterfaceFromHandle(i.first);
+ if (!i.second->isEmpty() && s != nullptr) {
i.second->setFormat(privileged || s->getSensor().getRequiredPermission().isEmpty() ?
"normal" : "mask_data");
const uint64_t mToken = proto.start(service::SensorEventsProto::RECENT_EVENTS_LOGS);
@@ -1020,7 +1028,7 @@
handle = buffer[i].meta_data.sensor;
}
if (connection->hasSensor(handle)) {
- sp<SensorInterface> si = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> si = getSensorInterfaceFromHandle(handle);
// If this buffer has an event from a one_shot sensor and this connection is registered
// for this particular one_shot sensor, try cleaning up the connection.
if (si != nullptr &&
@@ -1105,7 +1113,7 @@
break;
}
sensors_event_t out;
- sp<SensorInterface> si = mSensors.getInterface(handle);
+ std::shared_ptr<SensorInterface> si = getSensorInterfaceFromHandle(handle);
if (si == nullptr) {
ALOGE("handle %d is not an valid virtual sensor", handle);
continue;
@@ -1200,12 +1208,12 @@
// force the handle to be consistent
s.handle = handle;
- SensorInterface *si = new HardwareSensor(s, uuid);
+ auto si = std::make_shared<HardwareSensor>(s, uuid);
// This will release hold on dynamic sensor meta, so it should be called
// after Sensor object is created.
device.handleDynamicSensorConnection(handle, true /*connected*/);
- registerDynamicSensorLocked(si);
+ registerDynamicSensorLocked(std::move(si));
} else {
ALOGE("Handle %d has been used, cannot use again before reboot.", handle);
}
@@ -1332,7 +1340,7 @@
}
bool SensorService::isVirtualSensor(int handle) const {
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
return sensor != nullptr && sensor->isVirtual();
}
@@ -1341,7 +1349,7 @@
if (event.type == SENSOR_TYPE_META_DATA) {
handle = event.meta_data.sensor;
}
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
return sensor != nullptr && sensor->getSensor().isWakeUpSensor();
}
@@ -1741,7 +1749,7 @@
int handle = mActiveSensors.keyAt(i);
if (c->hasSensor(handle)) {
ALOGD_IF(DEBUG_CONNECTIONS, "%zu: disabling handle=0x%08x", i, handle);
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
if (sensor != nullptr) {
sensor->activate(c, false);
} else {
@@ -1855,7 +1863,7 @@
return NAME_NOT_FOUND;
}
-sp<SensorInterface> SensorService::getSensorInterfaceFromHandle(int handle) const {
+std::shared_ptr<SensorInterface> SensorService::getSensorInterfaceFromHandle(int handle) const {
return mSensors.getInterface(handle);
}
@@ -1865,7 +1873,7 @@
if (mInitCheck != NO_ERROR)
return mInitCheck;
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
if (sensor == nullptr ||
!canAccessSensor(sensor->getSensor(), "Tried enabling", opPackageName)) {
return BAD_VALUE;
@@ -2011,7 +2019,7 @@
Mutex::Autolock _l(mLock);
status_t err = cleanupWithoutDisableLocked(connection, handle);
if (err == NO_ERROR) {
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
err = sensor != nullptr ? sensor->activate(connection.get(), false) : status_t(BAD_VALUE);
}
@@ -2057,7 +2065,7 @@
if (mInitCheck != NO_ERROR)
return mInitCheck;
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
if (sensor == nullptr ||
!canAccessSensor(sensor->getSensor(), "Tried configuring", opPackageName)) {
return BAD_VALUE;
@@ -2083,7 +2091,7 @@
Mutex::Autolock _l(mLock);
// Loop through all sensors for this connection and call flush on each of them.
for (int handle : connection->getActiveSensorHandles()) {
- sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
+ std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
if (sensor == nullptr) {
continue;
}
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index e490398..78df501 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -375,15 +375,14 @@
String8 getSensorName(int handle) const;
String8 getSensorStringType(int handle) const;
bool isVirtualSensor(int handle) const;
- sp<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
+ std::shared_ptr<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
bool isWakeUpSensor(int type) const;
void recordLastValueLocked(sensors_event_t const* buffer, size_t count);
static void sortEventBuffer(sensors_event_t* buffer, size_t count);
- const Sensor& registerSensor(SensorInterface* sensor, bool isDebug = false,
- bool isVirtual = false,
- int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID);
- const Sensor& registerVirtualSensor(SensorInterface* sensor, bool isDebug = false);
- const Sensor& registerDynamicSensorLocked(SensorInterface* sensor, bool isDebug = false);
+ bool registerSensor(std::shared_ptr<SensorInterface> sensor, bool isDebug = false,
+ bool isVirtual = false, int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID);
+ bool registerVirtualSensor(std::shared_ptr<SensorInterface> sensor, bool isDebug = false);
+ bool registerDynamicSensorLocked(std::shared_ptr<SensorInterface> sensor, bool isDebug = false);
bool unregisterDynamicSensorLocked(int handle);
status_t cleanupWithoutDisable(const sp<SensorEventConnection>& connection, int handle);
status_t cleanupWithoutDisableLocked(const sp<SensorEventConnection>& connection, int handle);
diff --git a/services/sensorservice/aidl/Android.bp b/services/sensorservice/aidl/Android.bp
index 34d1de7..542fcae 100644
--- a/services/sensorservice/aidl/Android.bp
+++ b/services/sensorservice/aidl/Android.bp
@@ -28,7 +28,7 @@
"libbinder_ndk",
"libsensor",
"android.frameworks.sensorservice-V1-ndk",
- "android.hardware.sensors-V1-ndk",
+ "android.hardware.sensors-V2-ndk",
],
export_include_dirs: [
"include/",
diff --git a/services/sensorservice/aidl/fuzzer/Android.bp b/services/sensorservice/aidl/fuzzer/Android.bp
index 0d6e476..5301fe9 100644
--- a/services/sensorservice/aidl/fuzzer/Android.bp
+++ b/services/sensorservice/aidl/fuzzer/Android.bp
@@ -18,7 +18,7 @@
"libpermission",
"android.frameworks.sensorservice-V1-ndk",
"android.hardware.sensors-V1-convert",
- "android.hardware.sensors-V1-ndk",
+ "android.hardware.sensors-V2-ndk",
"android.hardware.common-V2-ndk",
"libsensor",
"libfakeservicemanager",
diff --git a/services/sensorservice/aidl/utils.cpp b/services/sensorservice/aidl/utils.cpp
index 26bcdc5..beb38b9 100644
--- a/services/sensorservice/aidl/utils.cpp
+++ b/services/sensorservice/aidl/utils.cpp
@@ -58,7 +58,7 @@
::aidl::android::hardware::sensors::Event convertEvent(const ::ASensorEvent& src) {
::aidl::android::hardware::sensors::Event dst;
::android::hardware::sensors::implementation::
- convertFromSensorEvent(reinterpret_cast<const sensors_event_t&>(src), &dst);
+ convertFromASensorEvent(src, &dst);
return dst;
}
diff --git a/services/sensorservice/hidl/utils.cpp b/services/sensorservice/hidl/utils.cpp
index 2f9e922..5fa594d 100644
--- a/services/sensorservice/hidl/utils.cpp
+++ b/services/sensorservice/hidl/utils.cpp
@@ -76,8 +76,8 @@
::android::hardware::sensors::V1_0::Event convertEvent(const ::ASensorEvent& src) {
::android::hardware::sensors::V1_0::Event dst;
- ::android::hardware::sensors::V1_0::implementation::convertFromSensorEvent(
- reinterpret_cast<const sensors_event_t&>(src), &dst);
+ ::android::hardware::sensors::V1_0::implementation::convertFromASensorEvent(
+ src, &dst);
return dst;
}
diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp
index 9e13849..410a5af 100644
--- a/services/stats/StatsAidl.cpp
+++ b/services/stats/StatsAidl.cpp
@@ -30,14 +30,14 @@
StatsHal::StatsHal() {}
ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) {
- std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName;
if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
-1, "Not a valid vendor atom ID");
}
- if (reverseDomainName.length() > 50) {
- ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str());
+ if (vendorAtom.reverseDomainName.length() > 50) {
+ ALOGE("Vendor atom reverse domain name %s is too long.",
+ vendorAtom.reverseDomainName.c_str());
return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
-1, "Vendor atom reverse domain name is too long");
}
diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp
index ae0a984..d27d989 100644
--- a/services/stats/StatsHal.cpp
+++ b/services/stats/StatsHal.cpp
@@ -112,13 +112,13 @@
}
hardware::Return<void> StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) {
- std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName;
if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
return hardware::Void();
}
- if (reverseDomainName.length() > 50) {
- ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str());
+ if (vendorAtom.reverseDomainName.size() > 50) {
+ ALOGE("Vendor atom reverse domain name %s is too long.",
+ vendorAtom.reverseDomainName.c_str());
return hardware::Void();
}
AStatsEvent* event = AStatsEvent_obtain();
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index b1bd705..e0dcab5 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -26,8 +26,8 @@
name: "libsurfaceflinger_defaults",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
+ "librenderengine_deps",
"surfaceflinger_defaults",
- "skia_renderengine_deps",
],
cflags: [
"-DLOG_TAG=\"SurfaceFlinger\"",
@@ -157,6 +157,7 @@
"EventLog/EventLog.cpp",
"FrontEnd/LayerCreationArgs.cpp",
"FrontEnd/LayerHandle.cpp",
+ "FrontEnd/LayerHierarchy.cpp",
"FrontEnd/LayerLifecycleManager.cpp",
"FrontEnd/RequestedLayerState.cpp",
"FrontEnd/TransactionHandler.cpp",
@@ -165,7 +166,6 @@
"FrameTracer/FrameTracer.cpp",
"FrameTracker.cpp",
"HdrLayerInfoReporter.cpp",
- "HwcSlotGenerator.cpp",
"WindowInfosListenerInvoker.cpp",
"Layer.cpp",
"LayerFE.cpp",
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 2bd8f32..09e41ff 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -118,7 +118,8 @@
Usage::READABLE));
}
-void ClientCache::erase(const client_cache_t& cacheId) {
+sp<GraphicBuffer> ClientCache::erase(const client_cache_t& cacheId) {
+ sp<GraphicBuffer> buffer;
auto& [processToken, id] = cacheId;
std::vector<sp<ErasedRecipient>> pendingErase;
{
@@ -126,9 +127,11 @@
ClientCacheBuffer* buf = nullptr;
if (!getBuffer(cacheId, &buf)) {
ALOGE("failed to erase buffer, could not retrieve buffer");
- return;
+ return nullptr;
}
+ buffer = buf->buffer->getBuffer();
+
for (auto& recipient : buf->recipients) {
sp<ErasedRecipient> erasedRecipient = recipient.promote();
if (erasedRecipient) {
@@ -142,6 +145,7 @@
for (auto& recipient : pendingErase) {
recipient->bufferErased(cacheId);
}
+ return buffer;
}
std::shared_ptr<renderengine::ExternalTexture> ClientCache::get(const client_cache_t& cacheId) {
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index cdeac2b..b56b252 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -33,6 +33,17 @@
namespace android {
+// This class manages a cache of buffer handles between SurfaceFlinger clients
+// and the SurfaceFlinger process which optimizes away some of the cost of
+// sending buffer handles across processes.
+//
+// Buffers are explicitly cached and uncached by the SurfaceFlinger client. When
+// a buffer is uncached, it is not only purged from this cache, but the buffer
+// ID is also passed down to CompositionEngine to purge it from a similar cache
+// used between SurfaceFlinger and Composer HAL. The buffer ID used to purge
+// both the SurfaceFlinger side of this other cache, as well as Composer HAL's
+// side of the cache.
+//
class ClientCache : public Singleton<ClientCache> {
public:
ClientCache();
@@ -41,7 +52,8 @@
base::expected<std::shared_ptr<renderengine::ExternalTexture>, AddError> add(
const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer);
- void erase(const client_cache_t& cacheId);
+
+ sp<GraphicBuffer> erase(const client_cache_t& cacheId);
std::shared_ptr<renderengine::ExternalTexture> get(const client_cache_t& cacheId);
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 30d34a5..f3a0186 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -11,6 +11,7 @@
name: "libcompositionengine_defaults",
defaults: [
"android.hardware.graphics.composer3-ndk_shared",
+ "librenderengine_deps",
"surfaceflinger_defaults",
],
cflags: [
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index f861fc9..4777f13 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -52,6 +52,9 @@
// All the layers that have queued updates.
Layers layersWithQueuedFrames;
+ // All graphic buffers that will no longer be used and should be removed from caches.
+ std::vector<uint64_t> bufferIdsToUncache;
+
// Controls how the color mode is chosen for an output
OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced};
@@ -64,9 +67,6 @@
// Used to correctly apply an inverse-display buffer transform if applicable
ui::Transform::RotationFlags internalDisplayRotationFlags{ui::Transform::ROT_0};
- // If true, GPU clocks will be increased when rendering blurs
- bool blursAreExpensive{false};
-
// If true, the complete output geometry needs to be recomputed this frame
bool updatingOutputGeometryThisFrame{false};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 974f7c6..ad98e93 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -163,7 +163,6 @@
// The buffer and related state
sp<GraphicBuffer> buffer;
- int bufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
sp<Fence> acquireFence = Fence::NO_FENCE;
Region surfaceDamage;
uint64_t frameNumber = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 874b330..52ebd9e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -23,6 +23,7 @@
#include <type_traits>
#include <unordered_map>
#include <utility>
+#include <vector>
#include <compositionengine/LayerFE.h>
#include <renderengine/LayerSettings.h>
@@ -272,6 +273,7 @@
virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
+ virtual void uncacheBuffers(const std::vector<uint64_t>&) = 0;
virtual void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) = 0;
virtual void collectVisibleLayers(const CompositionRefreshArgs&, CoverageState&) = 0;
virtual void ensureOutputLayerIfVisible(sp<LayerFE>&, CoverageState&) = 0;
@@ -288,12 +290,11 @@
using GpuCompositionResult = compositionengine::impl::GpuCompositionResult;
// Runs prepare frame in another thread while running client composition using
// the previous frame's composition strategy.
- virtual GpuCompositionResult prepareFrameAsync(const CompositionRefreshArgs&) = 0;
+ virtual GpuCompositionResult prepareFrameAsync() = 0;
virtual void devOptRepaintFlash(const CompositionRefreshArgs&) = 0;
- virtual void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) = 0;
+ virtual void finishFrame(GpuCompositionResult&&) = 0;
virtual std::optional<base::unique_fd> composeSurfaces(
- const Region&, const compositionengine::CompositionRefreshArgs&,
- std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&) = 0;
+ const Region&, std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&) = 0;
virtual void postFramebuffer() = 0;
virtual void renderCachedSets(const CompositionRefreshArgs&) = 0;
virtual bool chooseCompositionStrategy(
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index 6d0c395..4dbf8d2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -19,6 +19,7 @@
#include <cstdint>
#include <optional>
#include <string>
+#include <vector>
#include <ui/Transform.h>
#include <utils/StrongPointer.h>
@@ -81,6 +82,10 @@
// TODO(lpique): Make this protected once it is only internally called.
virtual CompositionState& editState() = 0;
+ // Clear the cache entries for a set of buffers that SurfaceFlinger no
+ // longer cares about.
+ virtual void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) = 0;
+
// Recalculates the state of the output layer from the output-independent
// layer. If includeGeometry is false, the geometry state can be skipped.
// internalDisplayRotationFlags must be set to the rotation flags for the
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 33a10a3..6cf1d68 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -61,7 +61,7 @@
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentAndGetFrameFences() override;
void setExpensiveRenderingExpected(bool) override;
- void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) override;
+ void finishFrame(GpuCompositionResult&&) override;
// compositionengine::Display overrides
DisplayId getId() const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index fd22aa3..6e9ea6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -17,7 +17,8 @@
#pragma once
#include <cstdint>
-#include <vector>
+#include <stack>
+#include <unordered_map>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -37,35 +38,76 @@
namespace compositionengine::impl {
-// With HIDLized hwcomposer HAL, the HAL can maintain a buffer cache for each
-// HWC display and layer. When updating a display target or a layer buffer,
-// we have the option to send the buffer handle over or to request the HAL to
-// retrieve it from its cache. The latter is cheaper since it eliminates the
-// overhead to transfer the handle over the trasport layer, and the overhead
-// for the HAL to clone and retain the handle.
-//
-// To be able to find out whether a buffer is already in the HAL's cache, we
-// use HWComposerBufferCache to mirror the cache in SF.
-class HwcBufferCache {
-public:
- HwcBufferCache();
- // Given a buffer, return the HWC cache slot and
- // buffer to be sent to HWC.
- //
- // outBuffer is set to buffer when buffer is not in the HWC cache;
- // otherwise, outBuffer is set to nullptr.
- void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
- sp<GraphicBuffer>* outBuffer);
+// The buffer cache returns both a slot and the buffer that should be sent to HWC. In cases
+// where the buffer is already cached, the buffer is a nullptr and will not be sent to HWC as
+// an optimization.
+struct HwcSlotAndBuffer {
+ uint32_t slot;
+ sp<GraphicBuffer> buffer;
+};
- // Special caching slot for the layer caching feature.
- static const constexpr size_t FLATTENER_CACHING_SLOT = BufferQueue::NUM_BUFFER_SLOTS;
+//
+// Manages the slot assignments for a buffers stored in Composer HAL's cache.
+//
+// Cache slots are an optimization when communicating buffer handles to Composer
+// HAL. When updating a layer's buffer, we can either send a new buffer handle
+// along with it's slot assignment or request the HAL to reuse a buffer handle
+// that we've already sent by using the slot assignment. The latter is cheaper
+// since it eliminates the overhead to transfer the buffer handle over IPC and
+// the overhead for the HAL to clone the handle.
+//
+class HwcBufferCache {
+private:
+ static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS;
+
+public:
+ // public for testing
+ // Override buffers don't use the normal cache slots because we don't want them to evict client
+ // buffers from the cache. We add an extra slot at the end for the override buffers.
+ static const constexpr size_t kOverrideBufferSlot = kMaxLayerBufferCount;
+
+ HwcBufferCache();
+
+ //
+ // Given a buffer, return the HWC cache slot and buffer to send to HWC.
+ //
+ // If the buffer is already in the cache, the buffer is null to optimize away sending HWC the
+ // buffer handle.
+ //
+ HwcSlotAndBuffer getHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer);
+ //
+ // Given a buffer, return the HWC cache slot and buffer to send to HWC.
+ //
+ // A special slot number is used for override buffers.
+ //
+ // If the buffer is already in the cache, the buffer is null to optimize away sending HWC the
+ // buffer handle.
+ //
+ HwcSlotAndBuffer getHwcSlotAndBufferForOverride(const sp<GraphicBuffer>& buffer);
+
+ //
+ // When a client process discards a buffer, it needs to be purged from the HWC cache.
+ //
+ // Returns the slot number of the buffer, or UINT32_MAX if it wasn't found in the cache.
+ //
+ uint32_t uncache(uint64_t graphicBufferId);
private:
- // an array where the index corresponds to a slot and the value corresponds to a (counter,
- // buffer) pair. "counter" is a unique value that indicates the last time this slot was updated
- // or used and allows us to keep track of the least-recently used buffer.
- static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
- wp<GraphicBuffer> mBuffers[kMaxLayerBufferCount];
+ uint32_t cache(const sp<GraphicBuffer>& buffer);
+ uint32_t getLeastRecentlyUsedSlot();
+
+ struct Cache {
+ sp<GraphicBuffer> buffer;
+ uint32_t slot;
+ // Cache entries are evicted according to least-recently-used when more than
+ // kMaxLayerBufferCount unique buffers have been sent to a layer.
+ uint64_t lruCounter;
+ };
+
+ std::unordered_map<uint64_t, Cache> mCacheByBufferId;
+ sp<GraphicBuffer> mLastOverrideBuffer;
+ std::stack<uint32_t> mFreeSlots;
+ uint64_t mLeastRecentlyUsedCounter;
};
} // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 9ca5da9..8ec77c0 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -82,6 +82,7 @@
void prepare(const CompositionRefreshArgs&, LayerFESet&) override;
void present(const CompositionRefreshArgs&) override;
+ void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override;
void collectVisibleLayers(const CompositionRefreshArgs&,
compositionengine::Output::CoverageState&) override;
@@ -95,11 +96,10 @@
void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
void beginFrame() override;
void prepareFrame() override;
- GpuCompositionResult prepareFrameAsync(const CompositionRefreshArgs&) override;
+ GpuCompositionResult prepareFrameAsync() override;
void devOptRepaintFlash(const CompositionRefreshArgs&) override;
- void finishFrame(const CompositionRefreshArgs&, GpuCompositionResult&&) override;
+ void finishFrame(GpuCompositionResult&&) override;
std::optional<base::unique_fd> composeSurfaces(const Region&,
- const compositionengine::CompositionRefreshArgs&,
std::shared_ptr<renderengine::ExternalTexture>,
base::unique_fd&) override;
void postFramebuffer() override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 6d4abf9..f383392 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -20,6 +20,7 @@
#include <memory>
#include <optional>
#include <string>
+#include <vector>
#include <compositionengine/LayerFE.h>
#include <compositionengine/OutputLayer.h>
@@ -44,6 +45,8 @@
void setHwcLayer(std::shared_ptr<HWC2::Layer>) override;
+ void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override;
+
void updateCompositionState(bool includeGeometry, bool forceClientComposition,
ui::Transform::RotationFlags) override;
void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 7592cac..a56fc79 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -82,6 +82,7 @@
MOCK_METHOD2(prepare, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(uncacheBuffers, void(const std::vector<uint64_t>&));
MOCK_METHOD2(rebuildLayerStacks,
void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
MOCK_METHOD2(collectVisibleLayers,
@@ -99,7 +100,7 @@
MOCK_METHOD0(beginFrame, void());
MOCK_METHOD0(prepareFrame, void());
- MOCK_METHOD1(prepareFrameAsync, GpuCompositionResult(const CompositionRefreshArgs&));
+ MOCK_METHOD0(prepareFrameAsync, GpuCompositionResult());
MOCK_METHOD1(chooseCompositionStrategy,
bool(std::optional<android::HWComposer::DeviceRequestedChanges>*));
MOCK_METHOD1(chooseCompositionStrategyAsync,
@@ -109,14 +110,12 @@
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
- MOCK_METHOD2(finishFrame,
- void(const compositionengine::CompositionRefreshArgs&, GpuCompositionResult&&));
+ MOCK_METHOD1(finishFrame, void(GpuCompositionResult&&));
- MOCK_METHOD4(composeSurfaces,
- std::optional<base::unique_fd>(
- const Region&,
- const compositionengine::CompositionRefreshArgs& refreshArgs,
- std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
+ MOCK_METHOD3(composeSurfaces,
+ std::optional<base::unique_fd>(const Region&,
+ std::shared_ptr<renderengine::ExternalTexture>,
+ base::unique_fd&));
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
MOCK_METHOD0(postFramebuffer, void());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index c22f1bf..5fef63a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -35,6 +35,8 @@
MOCK_METHOD1(setHwcLayer, void(std::shared_ptr<HWC2::Layer>));
+ MOCK_METHOD1(uncacheBuffers, void(const std::vector<uint64_t>&));
+
MOCK_CONST_METHOD0(getOutput, const compositionengine::Output&());
MOCK_CONST_METHOD0(getLayerFE, compositionengine::LayerFE&());
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 24669c2..d50a768 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -428,8 +428,7 @@
mPowerAdvisor->setGpuFenceTime(mId, std::move(gpuFence));
}
-void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs,
- GpuCompositionResult&& result) {
+void Display::finishFrame(GpuCompositionResult&& result) {
// We only need to actually compose the display if:
// 1) It is being handled by hardware composer, which may need this to
// keep its virtual display state machine in sync, or
@@ -439,7 +438,7 @@
return;
}
- impl::Output::finishFrame(refreshArgs, std::move(result));
+ impl::Output::finishFrame(std::move(result));
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index f95382d..d64fd57 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -16,43 +16,80 @@
#include <compositionengine/impl/HwcBufferCache.h>
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#include <gui/BufferQueue.h>
#include <ui/GraphicBuffer.h>
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
-
namespace android::compositionengine::impl {
HwcBufferCache::HwcBufferCache() {
- std::fill(std::begin(mBuffers), std::end(mBuffers), wp<GraphicBuffer>(nullptr));
+ for (uint32_t i = 0; i < kMaxLayerBufferCount; i++) {
+ mFreeSlots.push(i);
+ }
}
-void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
- sp<GraphicBuffer>* outBuffer) {
- // default is 0
- if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0 ||
- slot >= static_cast<int32_t>(kMaxLayerBufferCount)) {
- *outSlot = 0;
- } else {
- *outSlot = static_cast<uint32_t>(slot);
+HwcSlotAndBuffer HwcBufferCache::getHwcSlotAndBuffer(const sp<GraphicBuffer>& buffer) {
+ // TODO(b/261930578): This is for unit tests which don't mock GraphicBuffers but instead send
+ // in nullptrs.
+ if (buffer == nullptr) {
+ return {0, nullptr};
}
-
- auto& currentBuffer = mBuffers[*outSlot];
- wp<GraphicBuffer> weakCopy(buffer);
- if (currentBuffer == weakCopy) {
- // already cached in HWC, skip sending the buffer
- *outBuffer = nullptr;
- } else {
- *outBuffer = buffer;
-
- // update cache
- currentBuffer = buffer;
+ if (auto i = mCacheByBufferId.find(buffer->getId()); i != mCacheByBufferId.end()) {
+ Cache& cache = i->second;
+ // mark this cache slot as more recently used so it won't get evicted anytime soon
+ cache.lruCounter = mLeastRecentlyUsedCounter++;
+ return {cache.slot, nullptr};
}
+ return {cache(buffer), buffer};
+}
+
+HwcSlotAndBuffer HwcBufferCache::getHwcSlotAndBufferForOverride(const sp<GraphicBuffer>& buffer) {
+ if (buffer == mLastOverrideBuffer) {
+ return {kOverrideBufferSlot, nullptr};
+ }
+ mLastOverrideBuffer = buffer;
+ return {kOverrideBufferSlot, buffer};
+}
+
+uint32_t HwcBufferCache::uncache(uint64_t bufferId) {
+ if (auto i = mCacheByBufferId.find(bufferId); i != mCacheByBufferId.end()) {
+ uint32_t slot = i->second.slot;
+ mCacheByBufferId.erase(i);
+ mFreeSlots.push(slot);
+ return slot;
+ }
+ if (mLastOverrideBuffer && bufferId == mLastOverrideBuffer->getId()) {
+ mLastOverrideBuffer = nullptr;
+ return kOverrideBufferSlot;
+ }
+ return UINT32_MAX;
+}
+
+uint32_t HwcBufferCache::cache(const sp<GraphicBuffer>& buffer) {
+ Cache cache;
+ cache.slot = getLeastRecentlyUsedSlot();
+ cache.lruCounter = mLeastRecentlyUsedCounter++;
+ cache.buffer = buffer;
+ mCacheByBufferId.emplace(buffer->getId(), cache);
+ return cache.slot;
+}
+
+uint32_t HwcBufferCache::getLeastRecentlyUsedSlot() {
+ if (mFreeSlots.empty()) {
+ assert(!mCacheByBufferId.empty());
+ // evict the least recently used cache entry
+ auto cacheToErase = mCacheByBufferId.begin();
+ for (auto i = cacheToErase; i != mCacheByBufferId.end(); ++i) {
+ if (i->second.lruCounter < cacheToErase->second.lruCounter) {
+ cacheToErase = i;
+ }
+ }
+ uint32_t slot = cacheToErase->second.slot;
+ mCacheByBufferId.erase(cacheToErase);
+ mFreeSlots.push(slot);
+ }
+ uint32_t slot = mFreeSlots.top();
+ mFreeSlots.pop();
+ return slot;
}
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 6631a27..a405c4d 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -106,7 +106,6 @@
dumpVal(out, "composition type", toString(compositionType), compositionType);
out.append("\n buffer: ");
- dumpVal(out, "slot", bufferSlot);
dumpVal(out, "buffer", buffer.get());
out.append("\n ");
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 3ee8017..d513731 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -428,6 +428,7 @@
ALOGV(__FUNCTION__);
rebuildLayerStacks(refreshArgs, geomSnapshots);
+ uncacheBuffers(refreshArgs.bufferIdsToUncache);
}
void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
@@ -444,17 +445,26 @@
GpuCompositionResult result;
const bool predictCompositionStrategy = canPredictCompositionStrategy(refreshArgs);
if (predictCompositionStrategy) {
- result = prepareFrameAsync(refreshArgs);
+ result = prepareFrameAsync();
} else {
prepareFrame();
}
devOptRepaintFlash(refreshArgs);
- finishFrame(refreshArgs, std::move(result));
+ finishFrame(std::move(result));
postFramebuffer();
renderCachedSets(refreshArgs);
}
+void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) {
+ if (bufferIdsToUncache.empty()) {
+ return;
+ }
+ for (auto outputLayer : getOutputLayersOrderedByZ()) {
+ outputLayer->uncacheBuffers(bufferIdsToUncache);
+ }
+}
+
void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
LayerFESet& layerFESet) {
ATRACE_CALL();
@@ -1059,7 +1069,7 @@
[&, changes]() { return chooseCompositionStrategy(changes); });
}
-GpuCompositionResult Output::prepareFrameAsync(const CompositionRefreshArgs& refreshArgs) {
+GpuCompositionResult Output::prepareFrameAsync() {
ATRACE_CALL();
ALOGV(__FUNCTION__);
auto& state = editState();
@@ -1079,7 +1089,7 @@
GpuCompositionResult compositionResult;
if (dequeueSucceeded) {
std::optional<base::unique_fd> optFd =
- composeSurfaces(Region::INVALID_REGION, refreshArgs, buffer, bufferFence);
+ composeSurfaces(Region::INVALID_REGION, buffer, bufferFence);
if (optFd) {
compositionResult.fence = std::move(*optFd);
}
@@ -1117,7 +1127,7 @@
std::shared_ptr<renderengine::ExternalTexture> buffer;
updateProtectedContentState();
dequeueRenderBuffer(&bufferFence, &buffer);
- static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs, buffer, bufferFence));
+ static_cast<void>(composeSurfaces(dirtyRegion, buffer, bufferFence));
mRenderSurface->queueBuffer(base::unique_fd());
}
}
@@ -1129,7 +1139,7 @@
prepareFrame();
}
-void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositionResult&& result) {
+void Output::finishFrame(GpuCompositionResult&& result) {
ATRACE_CALL();
ALOGV(__FUNCTION__);
const auto& outputState = getState();
@@ -1154,7 +1164,7 @@
}
// Repaint the framebuffer (if needed), getting the optional fence for when
// the composition completes.
- optReadyFence = composeSurfaces(Region::INVALID_REGION, refreshArgs, buffer, bufferFence);
+ optReadyFence = composeSurfaces(Region::INVALID_REGION, buffer, bufferFence);
}
if (!optReadyFence) {
return;
@@ -1208,8 +1218,8 @@
}
std::optional<base::unique_fd> Output::composeSurfaces(
- const Region& debugRegion, const compositionengine::CompositionRefreshArgs& refreshArgs,
- std::shared_ptr<renderengine::ExternalTexture> tex, base::unique_fd& fd) {
+ const Region& debugRegion, std::shared_ptr<renderengine::ExternalTexture> tex,
+ base::unique_fd& fd) {
ATRACE_CALL();
ALOGV(__FUNCTION__);
@@ -1265,9 +1275,7 @@
// or complex GPU shaders and it's expensive. We boost the GPU frequency so that
// GPU composition can finish in time. We must reset GPU frequency afterwards,
// because high frequency consumes extra battery.
- const bool expensiveBlurs =
- refreshArgs.blursAreExpensive && mLayerRequestingBackgroundBlur != nullptr;
- const bool expensiveRenderingExpected = expensiveBlurs ||
+ const bool expensiveRenderingExpected =
std::any_of(clientCompositionLayers.begin(), clientCompositionLayers.end(),
[outputDataspace =
clientCompositionDisplay.outputDataspace](const auto& layer) {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index a39c527..a7c24b6 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -610,6 +610,19 @@
}
}
+void OutputLayer::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) {
+ auto& state = editState();
+ // Skip doing this if there is no HWC interface
+ if (!state.hwc) {
+ return;
+ }
+
+ for (auto bufferId : bufferIdsToUncache) {
+ state.hwc->hwcBufferCache.uncache(bufferId);
+ // TODO(b/258196272): send uncache requests to Composer HAL
+ }
+}
+
void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
const LayerFECompositionState& outputIndependentState,
bool skipLayer) {
@@ -622,27 +635,24 @@
to_string(error).c_str(), static_cast<int32_t>(error));
}
- sp<GraphicBuffer> buffer = outputIndependentState.buffer;
- sp<Fence> acquireFence = outputIndependentState.acquireFence;
- int slot = outputIndependentState.bufferSlot;
+ HwcSlotAndBuffer hwcSlotAndBuffer;
+ sp<Fence> hwcFence;
+ // Override buffers use a special cache slot so that they don't evict client buffers.
if (getState().overrideInfo.buffer != nullptr && !skipLayer) {
- buffer = getState().overrideInfo.buffer->getBuffer();
- acquireFence = getState().overrideInfo.acquireFence;
- slot = HwcBufferCache::FLATTENER_CACHING_SLOT;
+ hwcSlotAndBuffer = editState().hwc->hwcBufferCache.getHwcSlotAndBufferForOverride(
+ getState().overrideInfo.buffer->getBuffer());
+ hwcFence = getState().overrideInfo.acquireFence;
+ } else {
+ hwcSlotAndBuffer =
+ editState().hwc->hwcBufferCache.getHwcSlotAndBuffer(outputIndependentState.buffer);
+ hwcFence = outputIndependentState.acquireFence;
}
- ALOGV("Writing buffer %p", buffer.get());
-
- uint32_t hwcSlot = 0;
- sp<GraphicBuffer> hwcBuffer;
- // We need access to the output-dependent state for the buffer cache there,
- // though otherwise the buffer is not output-dependent.
- editState().hwc->hwcBufferCache.getHwcBuffer(slot, buffer, &hwcSlot, &hwcBuffer);
-
- if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
+ if (auto error = hwcLayer->setBuffer(hwcSlotAndBuffer.slot, hwcSlotAndBuffer.buffer, hwcFence);
error != hal::Error::NONE) {
- ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(), buffer->handle,
- to_string(error).c_str(), static_cast<int32_t>(error));
+ ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(),
+ hwcSlotAndBuffer.buffer->handle, to_string(error).c_str(),
+ static_cast<int32_t>(error));
}
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 95459c0..0756c1b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -959,7 +959,7 @@
mDisplay->editState().layerStackSpace.setContent(Rect(0, 0, 1, 1));
mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
- mDisplay->finishFrame({}, std::move(mResultWithBuffer));
+ mDisplay->finishFrame(std::move(mResultWithBuffer));
}
TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) {
@@ -980,7 +980,7 @@
gpuDisplay->editState().lastCompositionHadVisibleLayers = true;
gpuDisplay->beginFrame();
- gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
+ gpuDisplay->finishFrame(std::move(mResultWithoutBuffer));
}
TEST_F(DisplayFinishFrameTest, skipsCompositionIfEmpty) {
@@ -1001,7 +1001,7 @@
gpuDisplay->editState().lastCompositionHadVisibleLayers = false;
gpuDisplay->beginFrame();
- gpuDisplay->finishFrame({}, std::move(mResultWithoutBuffer));
+ gpuDisplay->finishFrame(std::move(mResultWithoutBuffer));
}
TEST_F(DisplayFinishFrameTest, performsCompositionIfDirtyAndNotEmpty) {
@@ -1022,7 +1022,7 @@
gpuDisplay->editState().lastCompositionHadVisibleLayers = true;
gpuDisplay->beginFrame();
- gpuDisplay->finishFrame({}, std::move(mResultWithBuffer));
+ gpuDisplay->finishFrame(std::move(mResultWithBuffer));
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
index 7197780..cf72e8f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
@@ -22,66 +22,172 @@
namespace android::compositionengine {
namespace {
-class TestableHwcBufferCache : public impl::HwcBufferCache {
-public:
- void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
- sp<GraphicBuffer>* outBuffer) {
- HwcBufferCache::getHwcBuffer(slot, buffer, outSlot, outBuffer);
- }
-};
+using impl::HwcBufferCache;
+using impl::HwcSlotAndBuffer;
class HwcBufferCacheTest : public testing::Test {
public:
~HwcBufferCacheTest() override = default;
- void testSlot(const int inSlot, const uint32_t expectedSlot) {
- uint32_t outSlot;
- sp<GraphicBuffer> outBuffer;
-
- // The first time, the output is the same as the input
- mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(mBuffer1, outBuffer);
-
- // The second time with the same buffer, the outBuffer is nullptr.
- mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(nullptr, outBuffer.get());
-
- // With a new buffer, the outBuffer is the input.
- mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(mBuffer2, outBuffer);
-
- // Again, the second request with the same buffer sets outBuffer to nullptr.
- mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(nullptr, outBuffer.get());
-
- // Setting a slot to use nullptr lookslike works, but note that
- // the output values make it look like no new buffer is being set....
- mCache.getHwcBuffer(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer);
- EXPECT_EQ(expectedSlot, outSlot);
- EXPECT_EQ(nullptr, outBuffer.get());
- }
-
- impl::HwcBufferCache mCache;
sp<GraphicBuffer> mBuffer1 =
sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
sp<GraphicBuffer> mBuffer2 =
sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
};
-TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) {
- testSlot(0, 0);
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_returnsUniqueSlotNumberForEachBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1);
+ EXPECT_NE(slotAndBufferFor1.slot, UINT32_MAX);
+ EXPECT_EQ(slotAndBufferFor1.buffer, mBuffer1);
+
+ HwcSlotAndBuffer slotAndBufferFor2 = cache.getHwcSlotAndBuffer(mBuffer2);
+ EXPECT_NE(slotAndBufferFor2.slot, slotAndBufferFor1.slot);
+ EXPECT_NE(slotAndBufferFor2.slot, UINT32_MAX);
+ EXPECT_EQ(slotAndBufferFor2.buffer, mBuffer2);
}
-TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) {
- testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1);
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_whenCached_returnsSameSlotNumberAndNullBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer originalSlotAndBuffer = cache.getHwcSlotAndBuffer(mBuffer1);
+ EXPECT_NE(originalSlotAndBuffer.slot, UINT32_MAX);
+ EXPECT_EQ(originalSlotAndBuffer.buffer, mBuffer1);
+
+ HwcSlotAndBuffer finalSlotAndBuffer = cache.getHwcSlotAndBuffer(mBuffer1);
+ EXPECT_EQ(finalSlotAndBuffer.slot, originalSlotAndBuffer.slot);
+ EXPECT_EQ(finalSlotAndBuffer.buffer, nullptr);
}
-TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) {
- testSlot(-123, 0);
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBuffer_whenSlotsFull_evictsOldestCachedBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ sp<GraphicBuffer> graphicBuffers[100];
+ HwcSlotAndBuffer slotsAndBuffers[100];
+ int finalCachedBufferIndex = 0;
+ for (int i = 0; i < 100; ++i) {
+ graphicBuffers[i] = sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+ slotsAndBuffers[i] = cache.getHwcSlotAndBuffer(graphicBuffers[i]);
+ // we fill up the cache when the slot number for the first buffer is reused
+ if (i > 0 && slotsAndBuffers[i].slot == slotsAndBuffers[0].slot) {
+ finalCachedBufferIndex = i;
+ break;
+ }
+ }
+ ASSERT_GT(finalCachedBufferIndex, 1);
+ // the final cached buffer has the same slot value as the oldest buffer
+ EXPECT_EQ(slotsAndBuffers[finalCachedBufferIndex].slot, slotsAndBuffers[0].slot);
+ // the oldest buffer is no longer in the cache because it was evicted
+ EXPECT_EQ(cache.uncache(graphicBuffers[0]->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenCached_returnsSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1);
+ ASSERT_NE(slotAndBufferFor1.slot, UINT32_MAX);
+
+ HwcSlotAndBuffer slotAndBufferFor2 = cache.getHwcSlotAndBuffer(mBuffer2);
+ ASSERT_NE(slotAndBufferFor2.slot, UINT32_MAX);
+
+ // the 1st buffer should be found in the cache with a slot number
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), slotAndBufferFor1.slot);
+ // since the 1st buffer has been previously uncached, we should no longer receive a slot number
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), UINT32_MAX);
+ // the 2nd buffer should be still found in the cache with a slot number
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), slotAndBufferFor2.slot);
+ // since the 2nd buffer has been previously uncached, we should no longer receive a slot number
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenUncached_returnsInvalidSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer slotAndBufferFor1 = cache.getHwcSlotAndBuffer(mBuffer1);
+ ASSERT_NE(slotAndBufferFor1.slot, UINT32_MAX);
+
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBufferForOverride_whenCached_returnsSameSlotAndNullBuffer) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer originalSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+ EXPECT_NE(originalSlotAndBuffer.slot, UINT32_MAX);
+ EXPECT_EQ(originalSlotAndBuffer.buffer, mBuffer1);
+
+ HwcSlotAndBuffer finalSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+ EXPECT_EQ(finalSlotAndBuffer.slot, originalSlotAndBuffer.slot);
+ EXPECT_EQ(finalSlotAndBuffer.buffer, nullptr);
+}
+
+TEST_F(HwcBufferCacheTest, getHwcSlotAndBufferForOverride_whenSlotsFull_returnsIndependentSlot) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ sp<GraphicBuffer> graphicBuffers[100];
+ HwcSlotAndBuffer slotsAndBuffers[100];
+ int finalCachedBufferIndex = -1;
+ for (int i = 0; i < 100; ++i) {
+ graphicBuffers[i] = sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+ slotsAndBuffers[i] = cache.getHwcSlotAndBuffer(graphicBuffers[i]);
+ // we fill up the cache when the slot number for the first buffer is reused
+ if (i > 0 && slotsAndBuffers[i].slot == slotsAndBuffers[0].slot) {
+ finalCachedBufferIndex = i;
+ break;
+ }
+ }
+ // expect to have cached at least a few buffers before evicting
+ ASSERT_GT(finalCachedBufferIndex, 1);
+
+ sp<GraphicBuffer> overrideBuffer =
+ sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
+ HwcSlotAndBuffer overrideSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(overrideBuffer);
+ // expect us to have a slot number
+ EXPECT_NE(overrideSlotAndBuffer.slot, UINT32_MAX);
+ // expect this to be the first time we cached the buffer
+ EXPECT_NE(overrideSlotAndBuffer.buffer, nullptr);
+
+ // expect the slot number to not equal any other slot number, even after the slots have been
+ // exhausted, indicating that the override buffer slot is independent from the slots for
+ // non-override buffers
+ for (int i = 0; i < finalCachedBufferIndex; ++i) {
+ EXPECT_NE(overrideSlotAndBuffer.slot, slotsAndBuffers[i].slot);
+ }
+ // the override buffer is independently uncached from the oldest cached buffer
+ // expect to find the override buffer still in the override buffer slot
+ EXPECT_EQ(cache.uncache(overrideBuffer->getId()), overrideSlotAndBuffer.slot);
+ // expect that the first buffer was not evicted from the cache when the override buffer was
+ // cached
+ EXPECT_EQ(cache.uncache(graphicBuffers[1]->getId()), slotsAndBuffers[1].slot);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenOverrideCached_returnsSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer hwcSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+ ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX);
+
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), hwcSlotAndBuffer.slot);
+ EXPECT_EQ(cache.uncache(mBuffer1->getId()), UINT32_MAX);
+}
+
+TEST_F(HwcBufferCacheTest, uncache_whenOverrideUncached_returnsInvalidSlotNumber) {
+ HwcBufferCache cache;
+ sp<GraphicBuffer> outBuffer;
+
+ HwcSlotAndBuffer hwcSlotAndBuffer = cache.getHwcSlotAndBufferForOverride(mBuffer1);
+ ASSERT_NE(hwcSlotAndBuffer.slot, UINT32_MAX);
+
+ EXPECT_EQ(cache.uncache(mBuffer2->getId()), UINT32_MAX);
}
} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 0f7dce8..03cf292 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -774,7 +774,7 @@
static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72);
static constexpr int kSupportedPerFrameMetadata = 101;
static constexpr int kExpectedHwcSlot = 0;
- static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::FLATTENER_CACHING_SLOT;
+ static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::kOverrideBufferSlot;
static constexpr bool kLayerGenericMetadata1Mandatory = true;
static constexpr bool kLayerGenericMetadata2Mandatory = true;
static constexpr float kWhitePointNits = 200.f;
@@ -823,7 +823,6 @@
mLayerFEState.hdrMetadata = kHdrMetadata;
mLayerFEState.sidebandStream = NativeHandle::create(kSidebandStreamHandle, false);
mLayerFEState.buffer = kBuffer;
- mLayerFEState.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
mLayerFEState.acquireFence = kFence;
mOutputState.displayBrightnessNits = kDisplayBrightnessNits;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 2109987..aaf0f06 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -799,20 +799,17 @@
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -839,20 +836,17 @@
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -878,20 +872,17 @@
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer1);
injectOutputLayer(layer2);
@@ -917,8 +908,7 @@
InSequence seq;
EXPECT_CALL(*layer0.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
@@ -926,9 +916,7 @@
EXPECT_CALL(*layer0.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer0.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
-
+ EXPECT_CALL(*layer0.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
// After calling planComposition (which clears overrideInfo), this test sets
// layer3 to be the peekThroughLayer for layer1 and layer2. As a result, it
@@ -938,18 +926,15 @@
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
/*zIsOverridden*/ true, /*isPeekingThrough*/
true));
- EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
/*zIsOverridden*/ true, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, z++,
/*zIsOverridden*/ true, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
injectOutputLayer(layer0);
injectOutputLayer(layer1);
@@ -1050,10 +1035,10 @@
MOCK_METHOD1(
chooseCompositionStrategyAsync,
std::future<bool>(std::optional<android::HWComposer::DeviceRequestedChanges>*));
- MOCK_METHOD4(composeSurfaces,
- std::optional<base::unique_fd>(
- const Region&, const compositionengine::CompositionRefreshArgs&,
- std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
+ MOCK_METHOD3(composeSurfaces,
+ std::optional<base::unique_fd>(const Region&,
+ std::shared_ptr<renderengine::ExternalTexture>,
+ base::unique_fd&));
MOCK_METHOD0(resetCompositionStrategy, void());
};
@@ -1087,9 +1072,9 @@
EXPECT_CALL(mOutput, chooseCompositionStrategyAsync(_))
.WillOnce(DoAll(SetArgPointee<0>(mOutput.editState().previousDeviceRequestedChanges),
Return(ByMove(p.get_future()))));
- EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+ EXPECT_CALL(mOutput, composeSurfaces(_, _, _));
- impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync();
EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::SUCCESS);
EXPECT_FALSE(result.bufferAvailable());
}
@@ -1112,7 +1097,7 @@
.WillOnce(DoAll(SetArgPointee<0>(mOutput.editState().previousDeviceRequestedChanges),
Return(ByMove(p.get_future()))));
- impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync();
EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::FAIL);
EXPECT_FALSE(result.bufferAvailable());
}
@@ -1140,9 +1125,9 @@
EXPECT_CALL(mOutput, chooseCompositionStrategyAsync(_)).WillOnce([&] {
return p.get_future();
});
- EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+ EXPECT_CALL(mOutput, composeSurfaces(_, _, _));
- impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync();
EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::FAIL);
EXPECT_TRUE(result.bufferAvailable());
}
@@ -1172,9 +1157,9 @@
EXPECT_CALL(mOutput, chooseCompositionStrategyAsync(_)).WillOnce([&] {
return p.get_future();
});
- EXPECT_CALL(mOutput, composeSurfaces(_, Ref(mRefreshArgs), _, _));
+ EXPECT_CALL(mOutput, composeSurfaces(_, _, _));
- impl::GpuCompositionResult result = mOutput.prepareFrameAsync(mRefreshArgs);
+ impl::GpuCompositionResult result = mOutput.prepareFrameAsync();
EXPECT_EQ(mOutput.getState().strategyPrediction, CompositionStrategyPredictionState::FAIL);
EXPECT_TRUE(result.bufferAvailable());
}
@@ -1192,14 +1177,49 @@
compositionengine::LayerFESet&));
};
+ OutputPrepareTest() {
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
+ .WillRepeatedly(Return(&mLayer1.outputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
+ .WillRepeatedly(Return(&mLayer2.outputLayer));
+
+ mRefreshArgs.layers.push_back(mLayer1.layerFE);
+ mRefreshArgs.layers.push_back(mLayer2.layerFE);
+ }
+
+ struct Layer {
+ StrictMock<mock::OutputLayer> outputLayer;
+ sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make();
+ };
+
StrictMock<OutputPartialMock> mOutput;
CompositionRefreshArgs mRefreshArgs;
LayerFESet mGeomSnapshots;
+ Layer mLayer1;
+ Layer mLayer2;
};
-TEST_F(OutputPrepareTest, justInvokesRebuildLayerStacks) {
+TEST_F(OutputPrepareTest, callsUncacheBuffersOnEachOutputLayerAndThenRebuildsLayerStacks) {
InSequence seq;
+
+ mRefreshArgs.bufferIdsToUncache = {1, 3, 5};
+
EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots)));
+ EXPECT_CALL(mLayer1.outputLayer, uncacheBuffers(Ref(mRefreshArgs.bufferIdsToUncache)));
+ EXPECT_CALL(mLayer2.outputLayer, uncacheBuffers(Ref(mRefreshArgs.bufferIdsToUncache)));
+
+ mOutput.prepare(mRefreshArgs, mGeomSnapshots);
+}
+
+TEST_F(OutputPrepareTest, skipsUncacheBuffersIfEmptyAndThenRebuildsLayerStacks) {
+ InSequence seq;
+
+ mRefreshArgs.bufferIdsToUncache = {};
+
+ EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots)));
+ EXPECT_CALL(mLayer1.outputLayer, uncacheBuffers(_)).Times(0);
+ EXPECT_CALL(mLayer2.outputLayer, uncacheBuffers(_)).Times(0);
mOutput.prepare(mRefreshArgs, mGeomSnapshots);
}
@@ -2000,11 +2020,9 @@
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD0(beginFrame, void());
MOCK_METHOD0(prepareFrame, void());
- MOCK_METHOD1(prepareFrameAsync, GpuCompositionResult(const CompositionRefreshArgs&));
+ MOCK_METHOD0(prepareFrameAsync, GpuCompositionResult());
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
- MOCK_METHOD2(finishFrame,
- void(const compositionengine::CompositionRefreshArgs&,
- GpuCompositionResult&&));
+ MOCK_METHOD1(finishFrame, void(GpuCompositionResult&&));
MOCK_METHOD0(postFramebuffer, void());
MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
@@ -2026,7 +2044,7 @@
EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(false));
EXPECT_CALL(mOutput, prepareFrame());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
- EXPECT_CALL(mOutput, finishFrame(Ref(args), _));
+ EXPECT_CALL(mOutput, finishFrame(_));
EXPECT_CALL(mOutput, postFramebuffer());
EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
@@ -2044,9 +2062,9 @@
EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
EXPECT_CALL(mOutput, beginFrame());
EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(true));
- EXPECT_CALL(mOutput, prepareFrameAsync(Ref(args)));
+ EXPECT_CALL(mOutput, prepareFrameAsync());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
- EXPECT_CALL(mOutput, finishFrame(Ref(args), _));
+ EXPECT_CALL(mOutput, finishFrame(_));
EXPECT_CALL(mOutput, postFramebuffer());
EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
@@ -2945,10 +2963,10 @@
// Sets up the helper functions called by the function under test to use
// mock implementations.
MOCK_METHOD(Region, getDirtyRegion, (), (const));
- MOCK_METHOD4(composeSurfaces,
- std::optional<base::unique_fd>(
- const Region&, const compositionengine::CompositionRefreshArgs&,
- std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
+ MOCK_METHOD3(composeSurfaces,
+ std::optional<base::unique_fd>(const Region&,
+ std::shared_ptr<renderengine::ExternalTexture>,
+ base::unique_fd&));
MOCK_METHOD0(postFramebuffer, void());
MOCK_METHOD0(prepareFrame, void());
MOCK_METHOD0(updateProtectedContentState, void());
@@ -3012,7 +3030,7 @@
EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kNotEmptyRegion));
EXPECT_CALL(mOutput, updateProtectedContentState());
EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _));
- EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs), _, _));
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), _, _));
EXPECT_CALL(*mRenderSurface, queueBuffer(_));
EXPECT_CALL(mOutput, postFramebuffer());
EXPECT_CALL(mOutput, prepareFrame());
@@ -3028,10 +3046,10 @@
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
// mock implementations.
- MOCK_METHOD4(composeSurfaces,
- std::optional<base::unique_fd>(
- const Region&, const compositionengine::CompositionRefreshArgs&,
- std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&));
+ MOCK_METHOD3(composeSurfaces,
+ std::optional<base::unique_fd>(const Region&,
+ std::shared_ptr<renderengine::ExternalTexture>,
+ base::unique_fd&));
MOCK_METHOD0(postFramebuffer, void());
MOCK_METHOD0(updateProtectedContentState, void());
MOCK_METHOD2(dequeueRenderBuffer,
@@ -3047,24 +3065,23 @@
StrictMock<OutputPartialMock> mOutput;
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
- CompositionRefreshArgs mRefreshArgs;
};
TEST_F(OutputFinishFrameTest, ifNotEnabledDoesNothing) {
mOutput.mState.isEnabled = false;
impl::GpuCompositionResult result;
- mOutput.finishFrame(mRefreshArgs, std::move(result));
+ mOutput.finishFrame(std::move(result));
}
TEST_F(OutputFinishFrameTest, takesEarlyOutifComposeSurfacesReturnsNoFence) {
mOutput.mState.isEnabled = true;
EXPECT_CALL(mOutput, updateProtectedContentState());
EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
- EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _, _));
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _));
impl::GpuCompositionResult result;
- mOutput.finishFrame(mRefreshArgs, std::move(result));
+ mOutput.finishFrame(std::move(result));
}
TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) {
@@ -3073,12 +3090,12 @@
InSequence seq;
EXPECT_CALL(mOutput, updateProtectedContentState());
EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
- EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _, _))
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _))
.WillOnce(Return(ByMove(base::unique_fd())));
EXPECT_CALL(*mRenderSurface, queueBuffer(_));
impl::GpuCompositionResult result;
- mOutput.finishFrame(mRefreshArgs, std::move(result));
+ mOutput.finishFrame(std::move(result));
}
TEST_F(OutputFinishFrameTest, predictionSucceeded) {
@@ -3088,7 +3105,7 @@
EXPECT_CALL(*mRenderSurface, queueBuffer(_));
impl::GpuCompositionResult result;
- mOutput.finishFrame(mRefreshArgs, std::move(result));
+ mOutput.finishFrame(std::move(result));
}
TEST_F(OutputFinishFrameTest, predictionFailedAndBufferIsReused) {
@@ -3104,11 +3121,11 @@
2);
EXPECT_CALL(mOutput,
- composeSurfaces(RegionEq(Region::INVALID_REGION), _, result.buffer,
+ composeSurfaces(RegionEq(Region::INVALID_REGION), result.buffer,
Eq(ByRef(result.fence))))
.WillOnce(Return(ByMove(base::unique_fd())));
EXPECT_CALL(*mRenderSurface, queueBuffer(_));
- mOutput.finishFrame(mRefreshArgs, std::move(result));
+ mOutput.finishFrame(std::move(result));
}
/*
@@ -3299,7 +3316,8 @@
// mock implementations.
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
MOCK_METHOD3(generateClientCompositionRequests,
- std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace, std::vector<LayerFE*>&));
+ std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace,
+ std::vector<LayerFE*>&));
MOCK_METHOD2(appendRegionFlashRequests,
void(const Region&, std::vector<LayerFE::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
@@ -3345,8 +3363,8 @@
getInstance()->mOutput.dequeueRenderBuffer(&fence, &externalTexture);
if (success) {
getInstance()->mReadyFence =
- getInstance()->mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs,
- externalTexture, fence);
+ getInstance()->mOutput.composeSurfaces(kDebugRegion, externalTexture,
+ fence);
}
return nextState<FenceCheckState>();
}
@@ -4020,7 +4038,7 @@
std::shared_ptr<renderengine::ExternalTexture> tex;
mOutput.updateProtectedContentState();
mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
+ mOutput.composeSurfaces(kDebugRegion, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) {
@@ -4042,7 +4060,7 @@
std::shared_ptr<renderengine::ExternalTexture> tex;
mOutput.updateProtectedContentState();
mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
+ mOutput.composeSurfaces(kDebugRegion, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) {
@@ -4055,7 +4073,7 @@
std::shared_ptr<renderengine::ExternalTexture> tex;
mOutput.updateProtectedContentState();
mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
+ mOutput.composeSurfaces(kDebugRegion, tex, fd);
}
TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) {
@@ -4068,7 +4086,7 @@
std::shared_ptr<renderengine::ExternalTexture> tex;
mOutput.updateProtectedContentState();
mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
+ mOutput.composeSurfaces(kDebugRegion, tex, fd);
}
struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSurfacesTest {
@@ -4101,61 +4119,7 @@
std::shared_ptr<renderengine::ExternalTexture> tex;
mOutput.updateProtectedContentState();
mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs, tex, fd);
-}
-
-struct OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur
- : public OutputComposeSurfacesTest_SetsExpensiveRendering {
- OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur() {
- mLayer.layerFEState.backgroundBlurRadius = 10;
- mLayer.layerFEState.isOpaque = false;
- mOutput.editState().isEnabled = true;
-
- EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
- EXPECT_CALL(mLayer.outputLayer,
- writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
- /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
- .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, false, _))
- .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
- .WillRepeatedly(Return(&mLayer.outputLayer));
- }
-
- NonInjectedLayer mLayer;
- compositionengine::CompositionRefreshArgs mRefreshArgs;
-};
-
-TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreExpensive) {
- mRefreshArgs.blursAreExpensive = true;
- mOutput.updateCompositionState(mRefreshArgs);
- mOutput.planComposition();
- mOutput.writeCompositionState(mRefreshArgs);
-
- EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
-
- base::unique_fd fd;
- std::shared_ptr<renderengine::ExternalTexture> tex;
- mOutput.updateProtectedContentState();
- mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, mRefreshArgs, tex, fd);
-}
-
-TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotExpensive) {
- mRefreshArgs.blursAreExpensive = false;
- mOutput.updateCompositionState(mRefreshArgs);
- mOutput.planComposition();
- mOutput.writeCompositionState(mRefreshArgs);
-
- EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(0);
-
- base::unique_fd fd;
- std::shared_ptr<renderengine::ExternalTexture> tex;
- mOutput.updateProtectedContentState();
- mOutput.dequeueRenderBuffer(&fd, &tex);
- mOutput.composeSurfaces(kDebugRegion, mRefreshArgs, tex, fd);
+ mOutput.composeSurfaces(kDebugRegion, tex, fd);
}
/*
@@ -4166,7 +4130,7 @@
struct OutputPartialMock : public OutputPartialMockBase {
// compositionengine::Output overrides
std::vector<LayerFE::LayerSettings> generateClientCompositionRequestsHelper(
- bool supportsProtectedContent, ui::Dataspace dataspace) {
+ bool supportsProtectedContent, ui::Dataspace dataspace) {
std::vector<LayerFE*> ignore;
return impl::Output::generateClientCompositionRequests(supportsProtectedContent,
dataspace, ignore);
@@ -4258,8 +4222,9 @@
EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
EXPECT_EQ(0u, requests.size());
}
@@ -4268,8 +4233,9 @@
mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(4000, 0, 4010, 10));
mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 0, 0));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
EXPECT_EQ(0u, requests.size());
}
@@ -4281,8 +4247,9 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(_))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(2u, requests.size());
EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
@@ -4312,8 +4279,9 @@
prepareClientComposition(ClientCompositionTargetSettingsBlurSettingsEq(
LayerFE::ClientCompositionTargetSettings::BlurSetting::BlurRegionsOnly)))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(2u, requests.size());
EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
@@ -4341,8 +4309,9 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(_))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(1u, requests.size());
EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
}
@@ -4364,8 +4333,9 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(_))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(1u, requests.size());
EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
}
@@ -4427,8 +4397,9 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(2u, requests.size());
// The second layer is expected to be rendered as alpha=0 black with no blending
@@ -4492,7 +4463,7 @@
static_cast<void>(
mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace));
+ kDisplayDataspace));
}
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
@@ -4714,8 +4685,9 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>()));
- static_cast<void>(mOutput.generateClientCompositionRequestsHelper(true /* supportsProtectedContent */,
- kDisplayDataspace));
+ static_cast<void>(
+ mOutput.generateClientCompositionRequestsHelper(true /* supportsProtectedContent */,
+ kDisplayDataspace));
}
TEST_F(OutputUpdateAndWriteCompositionStateTest, noBackgroundBlurWhenOpaque) {
@@ -4728,14 +4700,12 @@
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
layer2.layerFEState.backgroundBlurRadius = 10;
layer2.layerFEState.isOpaque = true;
@@ -4764,20 +4734,17 @@
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
layer2.layerFEState.backgroundBlurRadius = 10;
layer2.layerFEState.isOpaque = false;
@@ -4807,20 +4774,17 @@
EXPECT_CALL(*layer1.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
EXPECT_CALL(*layer2.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
EXPECT_CALL(*layer3.outputLayer,
writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false));
- EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
- .WillRepeatedly(Return(false));
+ EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
BlurRegion region;
layer2.layerFEState.blurRegions.push_back(region);
@@ -4917,8 +4881,8 @@
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(rightLayer.mLayerSettings)));
constexpr bool supportsProtectedContent = true;
- auto requests =
- mOutput.generateClientCompositionRequestsHelper(supportsProtectedContent, kOutputDataspace);
+ auto requests = mOutput.generateClientCompositionRequestsHelper(supportsProtectedContent,
+ kOutputDataspace);
ASSERT_EQ(2u, requests.size());
EXPECT_EQ(leftLayer.mLayerSettings, requests[0]);
EXPECT_EQ(rightLayer.mLayerSettings, requests[1]);
@@ -4956,8 +4920,9 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2Settings))))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(mShadowSettings)));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(1u, requests.size());
EXPECT_EQ(mShadowSettings, requests[0]);
@@ -4993,8 +4958,9 @@
EXPECT_CALL(*mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2Settings))))
.WillOnce(Return(std::optional<LayerFE::LayerSettings>(mLayers[2].mLayerSettings)));
- auto requests = mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
- kDisplayDataspace);
+ auto requests =
+ mOutput.generateClientCompositionRequestsHelper(false /* supportsProtectedContent */,
+ kDisplayDataspace);
ASSERT_EQ(1u, requests.size());
EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 269a5ea..96ae77f 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -203,7 +203,7 @@
mRefreshRateSelector->setActiveMode(modeId, renderFps);
if (mRefreshRateOverlay) {
- mRefreshRateOverlay->changeRefreshRate(displayFps);
+ mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps);
}
}
@@ -410,26 +410,35 @@
capabilities.getDesiredMinLuminance());
}
-void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinnner) {
+void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate) {
if (!enable) {
mRefreshRateOverlay.reset();
return;
}
+ ftl::Flags<RefreshRateOverlay::Features> features;
+ if (showSpinner) {
+ features |= RefreshRateOverlay::Features::Spinner;
+ }
+
+ if (showRenderRate) {
+ features |= RefreshRateOverlay::Features::RenderRate;
+ }
+
const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
- mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, showSpinnner);
+ mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
mRefreshRateOverlay->setLayerStack(getLayerStack());
mRefreshRateOverlay->setViewport(getSize());
- mRefreshRateOverlay->changeRefreshRate(getActiveMode().modePtr->getFps());
+ mRefreshRateOverlay->changeRefreshRate(getActiveMode().modePtr->getFps(), getActiveMode().fps);
}
bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId,
bool timerExpired) {
if (mRefreshRateSelector && mRefreshRateOverlay) {
- const auto newRefreshRate =
+ const auto newMode =
mRefreshRateSelector->onKernelTimerChanged(desiredModeId, timerExpired);
- if (newRefreshRate) {
- mRefreshRateOverlay->changeRefreshRate(*newRefreshRate);
+ if (newMode) {
+ mRefreshRateOverlay->changeRefreshRate(newMode->modePtr->getFps(), newMode->fps);
return true;
}
}
@@ -443,7 +452,8 @@
}
}
-auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) -> DesiredActiveModeAction {
+auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info, bool force)
+ -> DesiredActiveModeAction {
ATRACE_CALL();
LOG_ALWAYS_FATAL_IF(!info.modeOpt, "desired mode not provided");
@@ -464,7 +474,7 @@
const auto& desiredMode = *info.modeOpt->modePtr;
// Check if we are already at the desired mode
- if (refreshRateSelector().getActiveMode().modePtr->getId() == desiredMode.getId()) {
+ if (!force && refreshRateSelector().getActiveMode().modePtr->getId() == desiredMode.getId()) {
if (refreshRateSelector().getActiveMode() == info.modeOpt) {
return DesiredActiveModeAction::None;
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 852a8a2..d757673 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -210,7 +210,8 @@
InitiateDisplayModeSwitch,
InitiateRenderRateSwitch
};
- DesiredActiveModeAction setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock);
+ DesiredActiveModeAction setDesiredActiveMode(const ActiveModeInfo&, bool force = false)
+ EXCLUDES(mActiveModeLock);
std::optional<ActiveModeInfo> getDesiredActiveMode() const EXCLUDES(mActiveModeLock);
void clearDesiredActiveModeState() EXCLUDES(mActiveModeLock);
ActiveModeInfo getUpcomingActiveMode() const REQUIRES(kMainThreadContext) {
@@ -236,7 +237,8 @@
}
// Enables an overlay to be displayed with the current refresh rate
- void enableRefreshRateOverlay(bool enable, bool showSpinner) REQUIRES(kMainThreadContext);
+ void enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate)
+ REQUIRES(kMainThreadContext);
bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
void animateRefreshRateOverlay();
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 3782c6c..806df87 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -23,6 +23,7 @@
#include <android-base/file.h>
#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
+#include <gui/TraceUtils.h>
#include <log/log.h>
#include <utils/Trace.h>
@@ -535,7 +536,7 @@
return static_cast<Error>(status.getServiceSpecificError());
}
- *outTypes = translate<Hdr>(capabilities.types);
+ *outTypes = capabilities.types;
*outMaxLuminance = capabilities.maxLuminance;
*outMaxAverageLuminance = capabilities.maxAverageLuminance;
*outMinLuminance = capabilities.minLuminance;
@@ -578,13 +579,15 @@
}
Error AidlComposer::presentDisplay(Display display, int* outPresentFence) {
- ATRACE_NAME("HwcPresentDisplay");
+ const auto displayId = translate<int64_t>(display);
+ ATRACE_FORMAT("HwcPresentDisplay %" PRId64, displayId);
+
Error error = Error::NONE;
mMutex.lock_shared();
auto writer = getWriter(display);
auto reader = getReader(display);
if (writer && reader) {
- writer->get().presentDisplay(translate<int64_t>(display));
+ writer->get().presentDisplay(displayId);
error = execute(display);
} else {
error = Error::BAD_DISPLAY;
@@ -595,7 +598,7 @@
return error;
}
- auto fence = reader->get().takePresentFence(translate<int64_t>(display));
+ auto fence = reader->get().takePresentFence(displayId);
mMutex.unlock_shared();
// take ownership
*outPresentFence = fence.get();
@@ -707,8 +710,9 @@
Error AidlComposer::validateDisplay(Display display, nsecs_t expectedPresentTime,
uint32_t* outNumTypes, uint32_t* outNumRequests) {
- ATRACE_NAME("HwcValidateDisplay");
const auto displayId = translate<int64_t>(display);
+ ATRACE_FORMAT("HwcValidateDisplay %" PRId64, displayId);
+
Error error = Error::NONE;
mMutex.lock_shared();
auto writer = getWriter(display);
@@ -734,8 +738,9 @@
Error AidlComposer::presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
uint32_t* outNumTypes, uint32_t* outNumRequests,
int* outPresentFence, uint32_t* state) {
- ATRACE_NAME("HwcPresentOrValidateDisplay");
const auto displayId = translate<int64_t>(display);
+ ATRACE_FORMAT("HwcPresentOrValidateDisplay %" PRId64, displayId);
+
Error error = Error::NONE;
mMutex.lock_shared();
auto writer = getWriter(display);
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index d84efe7..2a043fd 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -104,7 +104,7 @@
Error getDozeSupport(Display display, bool* outSupport) override;
Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override;
- Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+ Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance,
float* outMaxAverageLuminance, float* outMinLuminance) override;
Error getOverlaySupport(OverlayProperties* outProperties) override;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index a9bf282..ec23935 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -66,7 +66,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Config;
@@ -84,6 +83,7 @@
using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
using AidlTransform = ::aidl::android::hardware::graphics::common::Transform;
+using aidl::android::hardware::graphics::common::Hdr;
class Composer {
public:
@@ -140,7 +140,7 @@
virtual Error getDozeSupport(Display display, bool* outSupport) = 0;
virtual Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) = 0;
- virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+ virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
float* outMaxLuminance, float* outMaxAverageLuminance,
float* outMinLuminance) = 0;
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index eb14933..ce602a8 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -72,6 +72,10 @@
mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
mConsumer->setMaxAcquiredBufferCount(
SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
+
+ for (size_t i = 0; i < sizeof(mHwcBufferIds) / sizeof(mHwcBufferIds[0]); ++i) {
+ mHwcBufferIds[i] = UINT64_MAX;
+ }
}
void FramebufferSurface::resizeBuffers(const ui::Size& newSize) {
@@ -88,31 +92,16 @@
}
status_t FramebufferSurface::advanceFrame() {
- uint32_t slot = 0;
- sp<GraphicBuffer> buf;
- sp<Fence> acquireFence(Fence::NO_FENCE);
- Dataspace dataspace = Dataspace::UNKNOWN;
- status_t result = nextBuffer(slot, buf, acquireFence, dataspace);
- mDataSpace = dataspace;
- if (result != NO_ERROR) {
- ALOGE("error latching next FramebufferSurface buffer: %s (%d)",
- strerror(-result), result);
- }
- return result;
-}
-
-status_t FramebufferSurface::nextBuffer(uint32_t& outSlot,
- sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence,
- Dataspace& outDataspace) {
Mutex::Autolock lock(mMutex);
BufferItem item;
status_t err = acquireBufferLocked(&item, 0);
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
- mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
+ mDataspace = Dataspace::UNKNOWN;
return NO_ERROR;
} else if (err != NO_ERROR) {
ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
+ mDataspace = Dataspace::UNKNOWN;
return err;
}
@@ -133,13 +122,18 @@
mCurrentBufferSlot = item.mSlot;
mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer;
mCurrentFence = item.mFence;
+ mDataspace = static_cast<Dataspace>(item.mDataSpace);
- outFence = item.mFence;
- mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
- outDataspace = static_cast<Dataspace>(item.mDataSpace);
- status_t result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace);
+ // assume HWC has previously seen the buffer in this slot
+ sp<GraphicBuffer> hwcBuffer = sp<GraphicBuffer>(nullptr);
+ if (mCurrentBuffer->getId() != mHwcBufferIds[mCurrentBufferSlot]) {
+ mHwcBufferIds[mCurrentBufferSlot] = mCurrentBuffer->getId();
+ hwcBuffer = mCurrentBuffer; // HWC hasn't previously seen this buffer in this slot
+ }
+ status_t result = mHwc.setClientTarget(mDisplayId, mCurrentBufferSlot, mCurrentFence, hwcBuffer,
+ mDataspace);
if (result != NO_ERROR) {
- ALOGE("error posting framebuffer: %d", result);
+ ALOGE("error posting framebuffer: %s (%d)", strerror(-result), result);
return result;
}
@@ -190,7 +184,7 @@
limitedSize.width = maxSize.height * aspectRatio;
wasLimited = true;
}
- ALOGI_IF(wasLimited, "framebuffer size has been limited to [%dx%d] from [%dx%d]",
+ ALOGI_IF(wasLimited, "Framebuffer size has been limited to [%dx%d] from [%dx%d]",
limitedSize.width, limitedSize.height, size.width, size.height);
return limitedSize;
}
@@ -198,9 +192,9 @@
void FramebufferSurface::dumpAsString(String8& result) const {
Mutex::Autolock lock(mMutex);
result.append(" FramebufferSurface\n");
- result.appendFormat(" mDataSpace=%s (%d)\n",
- dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
- mDataSpace);
+ result.appendFormat(" mDataspace=%s (%d)\n",
+ dataspaceDetails(static_cast<android_dataspace>(mDataspace)).c_str(),
+ mDataspace);
ConsumerBase::dumpLocked(result, " ");
}
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index d41a856..0b863da 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -21,7 +21,7 @@
#include <sys/types.h>
#include <compositionengine/DisplaySurface.h>
-#include <compositionengine/impl/HwcBufferCache.h>
+#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
#include <ui/DisplayId.h>
#include <ui/Size.h>
@@ -69,12 +69,6 @@
virtual void dumpLocked(String8& result, const char* prefix) const;
- // nextBuffer waits for and then latches the next buffer from the
- // BufferQueue and releases the previously latched buffer to the
- // BufferQueue. The new buffer is returned in the 'buffer' argument.
- status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer,
- sp<Fence>& outFence, ui::Dataspace& outDataspace);
-
const PhysicalDisplayId mDisplayId;
// Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
@@ -91,7 +85,7 @@
// compositing. Otherwise it will display the dataspace of the buffer
// use for compositing which can change as wide-color content is
// on/off.
- ui::Dataspace mDataSpace;
+ ui::Dataspace mDataspace;
// mCurrentBuffer is the current buffer or nullptr to indicate that there is
// no current buffer.
@@ -103,7 +97,9 @@
// Hardware composer, owned by SurfaceFlinger.
HWComposer& mHwc;
- compositionengine::impl::HwcBufferCache mHwcBufferCache;
+ // Buffers that HWC has seen before, indexed by slot number.
+ // NOTE: The BufferQueue slot number is the same as the HWC slot number.
+ uint64_t mHwcBufferIds[BufferQueue::NUM_BUFFER_SLOTS];
// Previous buffer to release after getting an updated retire fence
bool mHasPendingRelease;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index e264570..d0126d0 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -320,17 +320,17 @@
float maxLuminance = -1.0f;
float maxAverageLuminance = -1.0f;
float minLuminance = -1.0f;
- std::vector<Hwc2::Hdr> types;
- auto intError = mComposer.getHdrCapabilities(mId, &types,
- &maxLuminance, &maxAverageLuminance, &minLuminance);
+ std::vector<Hwc2::Hdr> hdrTypes;
+ auto intError = mComposer.getHdrCapabilities(mId, &hdrTypes, &maxLuminance,
+ &maxAverageLuminance, &minLuminance);
auto error = static_cast<HWC2::Error>(intError);
if (error != Error::NONE) {
return error;
}
- *outCapabilities = HdrCapabilities(std::move(types),
- maxLuminance, maxAverageLuminance, minLuminance);
+ *outCapabilities =
+ HdrCapabilities(std::move(hdrTypes), maxLuminance, maxAverageLuminance, minLuminance);
return Error::NONE;
}
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 8238828..537d545 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -20,6 +20,7 @@
#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+#include <aidl/android/hardware/graphics/common/Hdr.h>
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
@@ -39,7 +40,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Error;
@@ -69,6 +69,7 @@
using PowerMode = IComposerClient::PowerMode;
using Vsync = IComposerClient::Vsync;
using VsyncPeriodChangeConstraints = IComposerClient::VsyncPeriodChangeConstraints;
+using Hdr = aidl::android::hardware::graphics::common::Hdr;
} // namespace hardware::graphics::composer::hal
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index f8522e2..b607df0 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -496,13 +496,13 @@
"OptionalFeature::KernelIdleTimer is not supported on HIDL");
}
-Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
+Error HidlComposer::getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes,
float* outMaxLuminance, float* outMaxAverageLuminance,
float* outMinLuminance) {
Error error = kDefaultError;
if (mClient_2_3) {
mClient_2_3->getHdrCapabilities_2_3(display,
- [&](const auto& tmpError, const auto& tmpTypes,
+ [&](const auto& tmpError, const auto& tmpHdrTypes,
const auto& tmpMaxLuminance,
const auto& tmpMaxAverageLuminance,
const auto& tmpMinLuminance) {
@@ -510,15 +510,15 @@
if (error != Error::NONE) {
return;
}
+ *outHdrTypes = translate<ui::Hdr>(tmpHdrTypes);
- *outTypes = tmpTypes;
*outMaxLuminance = tmpMaxLuminance;
*outMaxAverageLuminance = tmpMaxAverageLuminance;
*outMinLuminance = tmpMinLuminance;
});
} else {
mClient->getHdrCapabilities(display,
- [&](const auto& tmpError, const auto& tmpTypes,
+ [&](const auto& tmpError, const auto& tmpHdrTypes,
const auto& tmpMaxLuminance,
const auto& tmpMaxAverageLuminance,
const auto& tmpMinLuminance) {
@@ -526,11 +526,7 @@
if (error != Error::NONE) {
return;
}
-
- outTypes->clear();
- for (auto type : tmpTypes) {
- outTypes->push_back(static_cast<Hdr>(type));
- }
+ *outHdrTypes = translate<ui::Hdr>(tmpHdrTypes);
*outMaxLuminance = tmpMaxLuminance;
*outMaxAverageLuminance = tmpMaxAverageLuminance;
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 48b720c..3602bbb 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -56,7 +56,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Config;
@@ -209,7 +208,7 @@
Error getDozeSupport(Display display, bool* outSupport) override;
Error hasDisplayIdleTimerCapability(Display display, bool* outSupport) override;
- Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
+ Error getHdrCapabilities(Display display, std::vector<Hdr>* outHdrTypes, float* outMaxLuminance,
float* outMaxAverageLuminance, float* outMinLuminance) override;
Error getOverlaySupport(aidl::android::hardware::graphics::composer3::OverlayProperties*
outProperties) override;
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 3803a78..d62075e 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -103,6 +103,10 @@
sink->setAsyncMode(true);
IGraphicBufferProducer::QueueBufferOutput output;
mSource[SOURCE_SCRATCH]->connect(nullptr, NATIVE_WINDOW_API_EGL, false, &output);
+
+ for (size_t i = 0; i < sizeof(mHwcBufferIds) / sizeof(mHwcBufferIds[0]); ++i) {
+ mHwcBufferIds[i] = UINT64_MAX;
+ }
}
VirtualDisplaySurface::~VirtualDisplaySurface() {
@@ -197,9 +201,9 @@
return NO_MEMORY;
}
- sp<GraphicBuffer> fbBuffer = mFbProducerSlot >= 0 ?
- mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr);
- sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot];
+ sp<GraphicBuffer> const& fbBuffer =
+ mFbProducerSlot >= 0 ? mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr);
+ sp<GraphicBuffer> const& outBuffer = mProducerBuffers[mOutputProducerSlot];
VDS_LOGV("%s: fb=%d(%p) out=%d(%p)", __func__, mFbProducerSlot, fbBuffer.get(),
mOutputProducerSlot, outBuffer.get());
@@ -211,12 +215,14 @@
status_t result = NO_ERROR;
if (fbBuffer != nullptr) {
- uint32_t hwcSlot = 0;
- sp<GraphicBuffer> hwcBuffer;
- mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer, &hwcSlot, &hwcBuffer);
-
+ // assume that HWC has previously seen the buffer in this slot
+ sp<GraphicBuffer> hwcBuffer = sp<GraphicBuffer>(nullptr);
+ if (fbBuffer->getId() != mHwcBufferIds[mFbProducerSlot]) {
+ mHwcBufferIds[mFbProducerSlot] = fbBuffer->getId();
+ hwcBuffer = fbBuffer; // HWC hasn't previously seen this buffer in this slot
+ }
// TODO: Correctly propagate the dataspace from GL composition
- result = mHwc.setClientTarget(*halDisplayId, hwcSlot, mFbFence, hwcBuffer,
+ result = mHwc.setClientTarget(*halDisplayId, mFbProducerSlot, mFbFence, hwcBuffer,
ui::Dataspace::UNKNOWN);
}
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index e21095a..be06e2b 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -20,7 +20,7 @@
#include <string>
#include <compositionengine/DisplaySurface.h>
-#include <compositionengine/impl/HwcBufferCache.h>
+#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
#include <gui/IGraphicBufferProducer.h>
#include <ui/DisplayId.h>
@@ -164,6 +164,10 @@
sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_*
uint32_t mDefaultOutputFormat;
+ // Buffers that HWC has seen before, indexed by HWC slot number.
+ // NOTE: The BufferQueue slot number is the same as the HWC slot number.
+ uint64_t mHwcBufferIds[BufferQueue::NUM_BUFFER_SLOTS];
+
//
// Inter-frame state
//
@@ -260,8 +264,6 @@
bool mMustRecompose = false;
- compositionengine::impl::HwcBufferCache mHwcBufferCache;
-
bool mForceHwcCopy;
};
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
new file mode 100644
index 0000000..db4e8af
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#undef LOG_TAG
+#define LOG_TAG "LayerHierarchy"
+
+#include "LayerHierarchy.h"
+#include "SwapErase.h"
+
+namespace android::surfaceflinger::frontend {
+
+namespace {
+auto layerZCompare = [](const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& lhs,
+ const std::pair<LayerHierarchy*, LayerHierarchy::Variant>& rhs) {
+ auto lhsLayer = lhs.first->getLayer();
+ auto rhsLayer = rhs.first->getLayer();
+ if (lhsLayer->layerStack != rhsLayer->layerStack) {
+ return lhsLayer->layerStack.id < rhsLayer->layerStack.id;
+ }
+ if (lhsLayer->z != rhsLayer->z) {
+ return lhsLayer->z < rhsLayer->z;
+ }
+ return lhsLayer->id < rhsLayer->id;
+};
+
+void insertSorted(std::vector<std::pair<LayerHierarchy*, LayerHierarchy::Variant>>& vec,
+ std::pair<LayerHierarchy*, LayerHierarchy::Variant> value) {
+ auto it = std::upper_bound(vec.begin(), vec.end(), value, layerZCompare);
+ vec.insert(it, std::move(value));
+}
+} // namespace
+
+LayerHierarchy::LayerHierarchy(RequestedLayerState* layer) : mLayer(layer) {}
+
+LayerHierarchy::LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnly) {
+ mLayer = (childrenOnly) ? nullptr : hierarchy.mLayer;
+ mChildren = hierarchy.mChildren;
+}
+
+void LayerHierarchy::traverse(const Visitor& visitor,
+ LayerHierarchy::TraversalPath& traversalPath) const {
+ if (mLayer) {
+ bool breakTraversal = !visitor(*this, traversalPath);
+ if (breakTraversal) {
+ return;
+ }
+ }
+ if (traversalPath.hasRelZLoop()) {
+ LOG_ALWAYS_FATAL("Found relative z loop layerId:%d", traversalPath.invalidRelativeRootId);
+ }
+ for (auto& [child, childVariant] : mChildren) {
+ ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
+ childVariant);
+ child->traverse(visitor, traversalPath);
+ }
+}
+
+void LayerHierarchy::traverseInZOrder(const Visitor& visitor,
+ LayerHierarchy::TraversalPath& traversalPath) const {
+ bool traverseThisLayer = (mLayer != nullptr);
+ for (auto it = mChildren.begin(); it < mChildren.end(); it++) {
+ auto& [child, childVariant] = *it;
+ if (traverseThisLayer && child->getLayer()->z >= 0) {
+ bool breakTraversal = !visitor(*this, traversalPath);
+ if (breakTraversal) {
+ return;
+ }
+ traverseThisLayer = false;
+ }
+ if (childVariant == LayerHierarchy::Variant::Detached) {
+ continue;
+ }
+ ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
+ childVariant);
+ child->traverseInZOrder(visitor, traversalPath);
+ }
+
+ if (traverseThisLayer) {
+ visitor(*this, traversalPath);
+ }
+}
+
+void LayerHierarchy::addChild(LayerHierarchy* child, LayerHierarchy::Variant variant) {
+ insertSorted(mChildren, {child, variant});
+}
+
+void LayerHierarchy::removeChild(LayerHierarchy* child) {
+ auto it = std::find_if(mChildren.begin(), mChildren.end(),
+ [child](const std::pair<LayerHierarchy*, Variant>& x) {
+ return x.first == child;
+ });
+ if (it == mChildren.end()) {
+ LOG_ALWAYS_FATAL("Could not find child!");
+ }
+ mChildren.erase(it);
+}
+
+void LayerHierarchy::sortChildrenByZOrder() {
+ std::sort(mChildren.begin(), mChildren.end(), layerZCompare);
+}
+
+void LayerHierarchy::updateChild(LayerHierarchy* hierarchy, LayerHierarchy::Variant variant) {
+ auto it = std::find_if(mChildren.begin(), mChildren.end(),
+ [hierarchy](std::pair<LayerHierarchy*, Variant>& child) {
+ return child.first == hierarchy;
+ });
+ if (it == mChildren.end()) {
+ LOG_ALWAYS_FATAL("Could not find child!");
+ } else {
+ it->second = variant;
+ }
+}
+
+const RequestedLayerState* LayerHierarchy::getLayer() const {
+ return mLayer;
+}
+
+std::string LayerHierarchy::getDebugStringShort() const {
+ std::string debug = "LayerHierarchy{";
+ debug += ((mLayer) ? mLayer->getDebugStringShort() : "root") + " ";
+ if (mChildren.empty()) {
+ debug += "no children";
+ } else {
+ debug += std::to_string(mChildren.size()) + " children";
+ }
+ return debug + "}";
+}
+
+std::string LayerHierarchy::getDebugString(const char* prefix) const {
+ std::string debug = prefix + getDebugStringShort();
+ for (auto& [child, childVariant] : mChildren) {
+ std::string childPrefix = " " + std::string(prefix) + " " + std::to_string(childVariant);
+ debug += "\n" + child->getDebugString(childPrefix.c_str());
+ }
+ return debug;
+}
+
+bool LayerHierarchy::hasRelZLoop(uint32_t& outInvalidRelativeRoot) const {
+ outInvalidRelativeRoot = UNASSIGNED_LAYER_ID;
+ traverse([&outInvalidRelativeRoot](const LayerHierarchy&,
+ const LayerHierarchy::TraversalPath& traversalPath) -> bool {
+ if (traversalPath.hasRelZLoop()) {
+ outInvalidRelativeRoot = traversalPath.invalidRelativeRootId;
+ return false;
+ }
+ return true;
+ });
+ return outInvalidRelativeRoot != UNASSIGNED_LAYER_ID;
+}
+
+LayerHierarchyBuilder::LayerHierarchyBuilder(
+ const std::vector<std::unique_ptr<RequestedLayerState>>& layers) {
+ mHierarchies.reserve(layers.size());
+ mLayerIdToHierarchy.reserve(layers.size());
+ for (auto& layer : layers) {
+ mHierarchies.emplace_back(std::make_unique<LayerHierarchy>(layer.get()));
+ mLayerIdToHierarchy[layer->id] = mHierarchies.back().get();
+ }
+ for (const auto& layer : layers) {
+ onLayerAdded(layer.get());
+ }
+ detachHierarchyFromRelativeParent(&mOffscreenRoot);
+}
+
+void LayerHierarchyBuilder::attachToParent(LayerHierarchy* hierarchy) {
+ auto layer = hierarchy->mLayer;
+ LayerHierarchy::Variant type = layer->hasValidRelativeParent()
+ ? LayerHierarchy::Variant::Detached
+ : LayerHierarchy::Variant::Attached;
+
+ LayerHierarchy* parent;
+
+ if (layer->parentId != UNASSIGNED_LAYER_ID) {
+ parent = getHierarchyFromId(layer->parentId);
+ } else if (layer->canBeRoot) {
+ parent = &mRoot;
+ } else {
+ parent = &mOffscreenRoot;
+ }
+ parent->addChild(hierarchy, type);
+ hierarchy->mParent = parent;
+}
+
+void LayerHierarchyBuilder::detachFromParent(LayerHierarchy* hierarchy) {
+ hierarchy->mParent->removeChild(hierarchy);
+ hierarchy->mParent = nullptr;
+}
+
+void LayerHierarchyBuilder::attachToRelativeParent(LayerHierarchy* hierarchy) {
+ auto layer = hierarchy->mLayer;
+ if (!layer->hasValidRelativeParent() || hierarchy->mRelativeParent) {
+ return;
+ }
+
+ if (layer->relativeParentId != UNASSIGNED_LAYER_ID) {
+ hierarchy->mRelativeParent = getHierarchyFromId(layer->relativeParentId);
+ } else {
+ hierarchy->mRelativeParent = &mOffscreenRoot;
+ }
+ hierarchy->mRelativeParent->addChild(hierarchy, LayerHierarchy::Variant::Relative);
+ hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Detached);
+}
+
+void LayerHierarchyBuilder::detachFromRelativeParent(LayerHierarchy* hierarchy) {
+ if (hierarchy->mRelativeParent) {
+ hierarchy->mRelativeParent->removeChild(hierarchy);
+ }
+ hierarchy->mRelativeParent = nullptr;
+ hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Attached);
+}
+
+void LayerHierarchyBuilder::attachHierarchyToRelativeParent(LayerHierarchy* root) {
+ if (root->mLayer) {
+ attachToRelativeParent(root);
+ }
+ for (auto& [child, childVariant] : root->mChildren) {
+ if (childVariant == LayerHierarchy::Variant::Detached ||
+ childVariant == LayerHierarchy::Variant::Attached) {
+ attachHierarchyToRelativeParent(child);
+ }
+ }
+}
+
+void LayerHierarchyBuilder::detachHierarchyFromRelativeParent(LayerHierarchy* root) {
+ if (root->mLayer) {
+ detachFromRelativeParent(root);
+ }
+ for (auto& [child, childVariant] : root->mChildren) {
+ if (childVariant == LayerHierarchy::Variant::Detached ||
+ childVariant == LayerHierarchy::Variant::Attached) {
+ detachHierarchyFromRelativeParent(child);
+ }
+ }
+}
+
+void LayerHierarchyBuilder::onLayerAdded(RequestedLayerState* layer) {
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+ attachToParent(hierarchy);
+ attachToRelativeParent(hierarchy);
+
+ if (layer->mirrorId != UNASSIGNED_LAYER_ID) {
+ LayerHierarchy* mirror = getHierarchyFromId(layer->mirrorId);
+ hierarchy->addChild(mirror, LayerHierarchy::Variant::Mirror);
+ }
+}
+
+void LayerHierarchyBuilder::onLayerDestroyed(RequestedLayerState* layer) {
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id, /*crashOnFailure=*/false);
+ if (!hierarchy) {
+ // Layer was never part of the hierarchy if it was created and destroyed in the same
+ // transaction.
+ return;
+ }
+ // detach from parent
+ detachFromRelativeParent(hierarchy);
+ detachFromParent(hierarchy);
+
+ // detach children
+ for (auto& [child, variant] : hierarchy->mChildren) {
+ if (variant == LayerHierarchy::Variant::Attached ||
+ variant == LayerHierarchy::Variant::Detached) {
+ mOffscreenRoot.addChild(child, LayerHierarchy::Variant::Attached);
+ child->mParent = &mOffscreenRoot;
+ } else if (variant == LayerHierarchy::Variant::Relative) {
+ mOffscreenRoot.addChild(child, LayerHierarchy::Variant::Attached);
+ child->mRelativeParent = &mOffscreenRoot;
+ }
+ }
+
+ swapErase(mHierarchies, [hierarchy](std::unique_ptr<LayerHierarchy>& layerHierarchy) {
+ return layerHierarchy.get() == hierarchy;
+ });
+ mLayerIdToHierarchy.erase(layer->id);
+}
+
+void LayerHierarchyBuilder::updateMirrorLayer(RequestedLayerState* layer) {
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+ auto it = hierarchy->mChildren.begin();
+ while (it != hierarchy->mChildren.end()) {
+ if (it->second == LayerHierarchy::Variant::Mirror) {
+ hierarchy->mChildren.erase(it);
+ break;
+ }
+ it++;
+ }
+
+ if (layer->mirrorId != UNASSIGNED_LAYER_ID) {
+ hierarchy->addChild(getHierarchyFromId(layer->mirrorId), LayerHierarchy::Variant::Mirror);
+ }
+}
+
+void LayerHierarchyBuilder::update(
+ const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
+ const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers) {
+ // rebuild map
+ for (auto& layer : layers) {
+ if (layer->changes.test(RequestedLayerState::Changes::Created)) {
+ mHierarchies.emplace_back(std::make_unique<LayerHierarchy>(layer.get()));
+ mLayerIdToHierarchy[layer->id] = mHierarchies.back().get();
+ }
+ }
+
+ for (auto& layer : layers) {
+ if (layer->changes.get() == 0) {
+ continue;
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::Created)) {
+ onLayerAdded(layer.get());
+ continue;
+ }
+ LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
+ if (layer->changes.test(RequestedLayerState::Changes::Parent)) {
+ detachFromParent(hierarchy);
+ attachToParent(hierarchy);
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::RelativeParent)) {
+ detachFromRelativeParent(hierarchy);
+ attachToRelativeParent(hierarchy);
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::Z)) {
+ hierarchy->mParent->sortChildrenByZOrder();
+ if (hierarchy->mRelativeParent) {
+ hierarchy->mRelativeParent->sortChildrenByZOrder();
+ }
+ }
+ if (layer->changes.test(RequestedLayerState::Changes::Mirror)) {
+ updateMirrorLayer(layer.get());
+ }
+ }
+
+ for (auto& layer : destroyedLayers) {
+ onLayerDestroyed(layer.get());
+ }
+ // When moving from onscreen to offscreen and vice versa, we need to attach and detach
+ // from our relative parents. This walks down both trees to do so. We can optimize this
+ // further by tracking onscreen, offscreen state in LayerHierarchy.
+ detachHierarchyFromRelativeParent(&mOffscreenRoot);
+ attachHierarchyToRelativeParent(&mRoot);
+}
+
+const LayerHierarchy& LayerHierarchyBuilder::getHierarchy() const {
+ return mRoot;
+}
+
+const LayerHierarchy& LayerHierarchyBuilder::getOffscreenHierarchy() const {
+ return mOffscreenRoot;
+}
+
+std::string LayerHierarchyBuilder::getDebugString(uint32_t layerId, uint32_t depth) const {
+ if (depth > 10) return "too deep, loop?";
+ if (layerId == UNASSIGNED_LAYER_ID) return "";
+ auto it = mLayerIdToHierarchy.find(layerId);
+ if (it == mLayerIdToHierarchy.end()) return "not found";
+
+ LayerHierarchy* hierarchy = it->second;
+ if (!hierarchy->mLayer) return "none";
+
+ std::string debug =
+ "[" + std::to_string(hierarchy->mLayer->id) + "] " + hierarchy->mLayer->name;
+ if (hierarchy->mRelativeParent) {
+ debug += " Relative:" + hierarchy->mRelativeParent->getDebugStringShort();
+ }
+ if (hierarchy->mParent) {
+ debug += " Parent:" + hierarchy->mParent->getDebugStringShort();
+ }
+ return debug;
+}
+
+LayerHierarchy LayerHierarchyBuilder::getPartialHierarchy(uint32_t layerId,
+ bool childrenOnly) const {
+ auto it = mLayerIdToHierarchy.find(layerId);
+ if (it == mLayerIdToHierarchy.end()) return {nullptr};
+
+ LayerHierarchy hierarchy(*it->second, childrenOnly);
+ return hierarchy;
+}
+
+LayerHierarchy* LayerHierarchyBuilder::getHierarchyFromId(uint32_t layerId, bool crashOnFailure) {
+ auto it = mLayerIdToHierarchy.find(layerId);
+ if (it == mLayerIdToHierarchy.end()) {
+ if (crashOnFailure) {
+ LOG_ALWAYS_FATAL("Could not find hierarchy for layer id %d", layerId);
+ }
+ return nullptr;
+ };
+
+ return it->second;
+}
+
+LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::ROOT_TRAVERSAL_ID =
+ {.id = UNASSIGNED_LAYER_ID, .variant = LayerHierarchy::Attached};
+
+std::string LayerHierarchy::TraversalPath::toString() const {
+ std::string debugString = "TraversalPath{.id = " + std::to_string(id);
+
+ if (!mirrorRootIds.empty()) {
+ debugString += ", .mirrorRootIds=";
+ for (auto rootId : mirrorRootIds) {
+ debugString += std::to_string(rootId) + ",";
+ }
+ }
+
+ if (!relativeRootIds.empty()) {
+ debugString += ", .relativeRootIds=";
+ for (auto rootId : relativeRootIds) {
+ debugString += std::to_string(rootId) + ",";
+ }
+ }
+
+ if (hasRelZLoop()) {
+ debugString += ", hasRelZLoop=true invalidRelativeRootId=";
+ debugString += std::to_string(invalidRelativeRootId) + ",";
+ }
+
+ debugString += "}";
+ return debugString;
+}
+
+// Helper class to update a passed in TraversalPath when visiting a child. When the object goes out
+// of scope the TraversalPath is reset to its original state.
+LayerHierarchy::ScopedAddToTraversalPath::ScopedAddToTraversalPath(TraversalPath& traversalPath,
+ uint32_t layerId,
+ LayerHierarchy::Variant variant)
+ : mTraversalPath(traversalPath),
+ mParentId(traversalPath.id),
+ mParentVariant(traversalPath.variant) {
+ // Update the traversal id with the child layer id and variant. Parent id and variant are
+ // stored to reset the id upon destruction.
+ traversalPath.id = layerId;
+ traversalPath.variant = variant;
+ if (variant == LayerHierarchy::Variant::Mirror) {
+ traversalPath.mirrorRootIds.emplace_back(layerId);
+ }
+ if (variant == LayerHierarchy::Variant::Relative) {
+ if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(),
+ layerId) != traversalPath.relativeRootIds.end()) {
+ traversalPath.invalidRelativeRootId = layerId;
+ }
+ traversalPath.relativeRootIds.emplace_back(layerId);
+ }
+}
+LayerHierarchy::ScopedAddToTraversalPath::~ScopedAddToTraversalPath() {
+ // Reset the traversal id to its original parent state using the state that was saved in
+ // the constructor.
+ if (mTraversalPath.variant == LayerHierarchy::Variant::Mirror) {
+ mTraversalPath.mirrorRootIds.pop_back();
+ }
+ if (mTraversalPath.variant == LayerHierarchy::Variant::Relative) {
+ mTraversalPath.relativeRootIds.pop_back();
+ }
+ if (mTraversalPath.invalidRelativeRootId == mTraversalPath.id) {
+ mTraversalPath.invalidRelativeRootId = UNASSIGNED_LAYER_ID;
+ }
+ mTraversalPath.id = mParentId;
+ mTraversalPath.variant = mParentVariant;
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
new file mode 100644
index 0000000..f83a859
--- /dev/null
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "FrontEnd/LayerCreationArgs.h"
+#include "RequestedLayerState.h"
+#include "ftl/small_vector.h"
+
+namespace android::surfaceflinger::frontend {
+class LayerHierarchyBuilder;
+
+// LayerHierarchy allows us to navigate the layer hierarchy in z-order, or depth first traversal.
+// The hierarchy is created from a set of RequestedLayerStates. The hierarchy itself does not
+// contain additional states. Instead, it is a representation of RequestedLayerStates as a graph.
+//
+// Each node in the hierarchy can be visited by multiple parents (making this a graph). While
+// traversing the hierarchy, a new concept called Variant can be used to understand the
+// relationship of the layer to its parent. The following variants are possible:
+// Attached - child of the parent
+// Detached - child of the parent but currently relative parented to another layer
+// Relative - relative child of the parent
+// Mirror - mirrored from another layer
+//
+// By representing the hierarchy as a graph, we can represent mirrored layer hierarchies without
+// cloning the layer requested state. The mirrored hierarchy and its corresponding
+// RequestedLayerStates are kept in sync because the mirrored hierarchy does not clone any
+// states.
+class LayerHierarchy {
+public:
+ enum Variant {
+ Attached,
+ Detached,
+ Relative,
+ Mirror,
+ };
+ // Represents a unique path to a node.
+ struct TraversalPath {
+ uint32_t id;
+ LayerHierarchy::Variant variant;
+ // Mirrored layers can have a different geometry than their parents so we need to track
+ // the mirror roots in the traversal.
+ ftl::SmallVector<uint32_t, 5> mirrorRootIds;
+ // Relative layers can be visited twice, once by their parent and then once again by
+ // their relative parent. We keep track of the roots here to detect any loops in the
+ // hierarchy. If a relative root already exists in the list while building the
+ // TraversalPath, it means that somewhere in the hierarchy two layers are relatively
+ // parented to each other.
+ ftl::SmallVector<uint32_t, 5> relativeRootIds;
+ // First duplicate relative root id found. If this is a valid layer id that means we are
+ // in a loop.
+ uint32_t invalidRelativeRootId = UNASSIGNED_LAYER_ID;
+ bool hasRelZLoop() const { return invalidRelativeRootId != UNASSIGNED_LAYER_ID; }
+ bool isRelative() { return !relativeRootIds.empty(); }
+
+ bool operator==(const TraversalPath& other) const {
+ return id == other.id && mirrorRootIds == other.mirrorRootIds;
+ }
+ std::string toString() const;
+
+ static TraversalPath ROOT_TRAVERSAL_ID;
+ };
+
+ // Helper class to add nodes to an existing traversal id and removes the
+ // node when it goes out of scope.
+ class ScopedAddToTraversalPath {
+ public:
+ ScopedAddToTraversalPath(TraversalPath& traversalPath, uint32_t layerId,
+ LayerHierarchy::Variant variantArg);
+ ~ScopedAddToTraversalPath();
+
+ private:
+ TraversalPath& mTraversalPath;
+ uint32_t mParentId;
+ LayerHierarchy::Variant mParentVariant;
+ };
+ LayerHierarchy(RequestedLayerState* layer);
+
+ // Visitor function that provides the hierarchy node and a traversal id which uniquely
+ // identifies how was visited. The hierarchy contains a pointer to the RequestedLayerState.
+ // Return false to stop traversing down the hierarchy.
+ typedef std::function<bool(const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath& traversalPath)>
+ Visitor;
+
+ // Traverse the hierarchy and visit all child variants.
+ void traverse(const Visitor& visitor) const {
+ traverse(visitor, TraversalPath::ROOT_TRAVERSAL_ID);
+ }
+
+ // Traverse the hierarchy in z-order, skipping children that have relative parents.
+ void traverseInZOrder(const Visitor& visitor) const {
+ traverseInZOrder(visitor, TraversalPath::ROOT_TRAVERSAL_ID);
+ }
+
+ const RequestedLayerState* getLayer() const;
+ std::string getDebugString(const char* prefix = "") const;
+ std::string getDebugStringShort() const;
+ // Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot
+ // will contain the first relative root that was visited twice in a traversal.
+ bool hasRelZLoop(uint32_t& outInvalidRelativeRoot) const;
+ std::vector<std::pair<LayerHierarchy*, Variant>> mChildren;
+
+private:
+ friend LayerHierarchyBuilder;
+ LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnly);
+ void addChild(LayerHierarchy*, LayerHierarchy::Variant);
+ void removeChild(LayerHierarchy*);
+ void sortChildrenByZOrder();
+ void updateChild(LayerHierarchy*, LayerHierarchy::Variant);
+ void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+ void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+
+ const RequestedLayerState* mLayer;
+ LayerHierarchy* mParent = nullptr;
+ LayerHierarchy* mRelativeParent = nullptr;
+};
+
+// Given a list of RequestedLayerState, this class will build a root hierarchy and an
+// offscreen hierarchy. The builder also has an update method which can update an existing
+// hierarchy from a list of RequestedLayerState and associated change flags.
+class LayerHierarchyBuilder {
+public:
+ LayerHierarchyBuilder(const std::vector<std::unique_ptr<RequestedLayerState>>&);
+ void update(const std::vector<std::unique_ptr<RequestedLayerState>>& layers,
+ const std::vector<std::unique_ptr<RequestedLayerState>>& destroyedLayers);
+ LayerHierarchy getPartialHierarchy(uint32_t, bool childrenOnly) const;
+ const LayerHierarchy& getHierarchy() const;
+ const LayerHierarchy& getOffscreenHierarchy() const;
+ std::string getDebugString(uint32_t layerId, uint32_t depth = 0) const;
+
+private:
+ void onLayerAdded(RequestedLayerState* layer);
+ void attachToParent(LayerHierarchy*);
+ void detachFromParent(LayerHierarchy*);
+ void attachToRelativeParent(LayerHierarchy*);
+ void detachFromRelativeParent(LayerHierarchy*);
+ void attachHierarchyToRelativeParent(LayerHierarchy*);
+ void detachHierarchyFromRelativeParent(LayerHierarchy*);
+
+ void onLayerDestroyed(RequestedLayerState* layer);
+ void updateMirrorLayer(RequestedLayerState* layer);
+ LayerHierarchy* getHierarchyFromId(uint32_t layerId, bool crashOnFailure = true);
+ std::unordered_map<uint32_t, LayerHierarchy*> mLayerIdToHierarchy;
+ std::vector<std::unique_ptr<LayerHierarchy>> mHierarchies;
+ LayerHierarchy mRoot{nullptr};
+ LayerHierarchy mOffscreenRoot{nullptr};
+};
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 7afa144..fdf60b3 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -42,10 +42,10 @@
it->second.owner.getDebugString().c_str());
}
- linkLayer(layer.parentId, layer.id);
- linkLayer(layer.relativeParentId, layer.id);
- linkLayer(layer.mirrorId, layer.id);
- linkLayer(layer.touchCropId, layer.id);
+ layer.parentId = linkLayer(layer.parentId, layer.id);
+ layer.relativeParentId = linkLayer(layer.relativeParentId, layer.id);
+ layer.mirrorId = linkLayer(layer.mirrorId, layer.id);
+ layer.touchCropId = linkLayer(layer.touchCropId, layer.id);
mLayers.emplace_back(std::move(newLayer));
}
@@ -83,10 +83,10 @@
RequestedLayerState& layer = it->second.owner;
- unlinkLayer(layer.parentId, layer.id);
- unlinkLayer(layer.relativeParentId, layer.id);
- unlinkLayer(layer.mirrorId, layer.id);
- unlinkLayer(layer.touchCropId, layer.id);
+ layer.parentId = unlinkLayer(layer.parentId, layer.id);
+ layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id);
+ layer.mirrorId = unlinkLayer(layer.mirrorId, layer.id);
+ layer.touchCropId = unlinkLayer(layer.touchCropId, layer.id);
auto& references = it->second.references;
for (uint32_t linkedLayerId : references) {
@@ -199,15 +199,15 @@
if (oldParentId != layer->parentId) {
unlinkLayer(oldParentId, layer->id);
- linkLayer(layer->parentId, layer->id);
+ layer->parentId = linkLayer(layer->parentId, layer->id);
}
if (oldRelativeParentId != layer->relativeParentId) {
unlinkLayer(oldRelativeParentId, layer->id);
- linkLayer(layer->relativeParentId, layer->id);
+ layer->relativeParentId = linkLayer(layer->relativeParentId, layer->id);
}
if (oldTouchCropId != layer->touchCropId) {
unlinkLayer(oldTouchCropId, layer->id);
- linkLayer(layer->touchCropId, layer->id);
+ layer->touchCropId = linkLayer(layer->touchCropId, layer->id);
}
mGlobalChanges |= layer->changes &
@@ -287,26 +287,28 @@
return &it->second.references;
}
-void LayerLifecycleManager::linkLayer(uint32_t layerId, uint32_t layerToLink) {
- if (layerToLink && layerId != UNASSIGNED_LAYER_ID) {
- std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId);
- if (!linkedLayers) {
- LOG_ALWAYS_FATAL("Could not find layer id %d to link %d", layerId, layerToLink);
- return;
- }
- linkedLayers->emplace_back(layerToLink);
+uint32_t LayerLifecycleManager::linkLayer(uint32_t layerId, uint32_t layerToLink) {
+ if (layerId == UNASSIGNED_LAYER_ID) {
+ return UNASSIGNED_LAYER_ID;
}
-}
-
-void LayerLifecycleManager::unlinkLayer(uint32_t& inOutLayerId, uint32_t linkedLayer) {
- uint32_t layerId = inOutLayerId;
- inOutLayerId = UNASSIGNED_LAYER_ID;
std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId);
if (!linkedLayers) {
- return;
+ ALOGV("Could not find layer id %d to link %d. Parent is probably destroyed", layerId,
+ layerToLink);
+ return UNASSIGNED_LAYER_ID;
+ }
+ linkedLayers->emplace_back(layerToLink);
+ return layerId;
+}
+
+uint32_t LayerLifecycleManager::unlinkLayer(uint32_t layerId, uint32_t linkedLayer) {
+ std::vector<uint32_t>* linkedLayers = getLinkedLayersFromId(layerId);
+ if (!linkedLayers) {
+ return UNASSIGNED_LAYER_ID;
}
swapErase(*linkedLayers, linkedLayer);
+ return UNASSIGNED_LAYER_ID;
}
std::string LayerLifecycleManager::References::getDebugString() const {
@@ -318,4 +320,16 @@
return debugInfo;
}
+void LayerLifecycleManager::fixRelativeZLoop(uint32_t relativeRootId) {
+ auto it = mIdToLayer.find(relativeRootId);
+ if (it == mIdToLayer.end()) {
+ return;
+ }
+ RequestedLayerState& layer = it->second.owner;
+ layer.relativeParentId = unlinkLayer(layer.relativeParentId, layer.id);
+ layer.changes |=
+ RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::RelativeParent;
+ mGlobalChanges |= RequestedLayerState::Changes::Hierarchy;
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
index ad70d3f..63a7afc 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.h
@@ -42,6 +42,12 @@
void applyTransactions(const std::vector<TransactionState>&);
void onHandlesDestroyed(const std::vector<uint32_t>&);
+ // Detaches the layer from its relative parent to prevent a loop in the
+ // layer hierarchy. This overrides the RequestedLayerState and leaves
+ // the system in an invalid state. This is always a client error that
+ // needs to be fixed but overriding the state allows us to fail gracefully.
+ void fixRelativeZLoop(uint32_t relativeRootId);
+
// Destroys RequestedLayerStates that are marked to be destroyed. Invokes all
// ILifecycleListener callbacks and clears any change flags from previous state
// updates. This function should be called outside the hot path since it's not
@@ -72,8 +78,8 @@
RequestedLayerState* getLayerFromId(uint32_t);
std::vector<uint32_t>* getLinkedLayersFromId(uint32_t);
- void linkLayer(uint32_t layerId, uint32_t layerToLink);
- void unlinkLayer(uint32_t& inOutLayerId, uint32_t linkedLayer);
+ uint32_t linkLayer(uint32_t layerId, uint32_t layerToLink);
+ uint32_t unlinkLayer(uint32_t layerId, uint32_t linkedLayer);
struct References {
// Lifetime tied to mLayers
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 45058d9..054382c 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -40,7 +40,7 @@
}
std::string layerIdToString(uint32_t layerId) {
- return layerId == UNASSIGNED_LAYER_ID ? std::to_string(layerId) : "none";
+ return layerId == UNASSIGNED_LAYER_ID ? "none" : std::to_string(layerId);
}
} // namespace
@@ -147,13 +147,17 @@
static const mat4 identityMatrix = mat4();
hasColorTransform = colorTransform != identityMatrix;
}
- if (clientState.what & layer_state_t::eLayerChanged) {
+ if (clientState.what & (layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged)) {
changes |= RequestedLayerState::Changes::Z;
}
if (clientState.what & layer_state_t::eReparent) {
changes |= RequestedLayerState::Changes::Parent;
parentId = getLayerIdFromSurfaceControl(clientState.parentSurfaceControlForChild);
parentSurfaceControlForChild = nullptr;
+ // Once a layer has be reparented, it cannot be placed at the root. It sounds odd
+ // but thats the existing logic and until we make this behavior more explicit, we need
+ // to maintain this logic.
+ canBeRoot = false;
}
if (clientState.what & layer_state_t::eRelativeLayerChanged) {
changes |= RequestedLayerState::Changes::RelativeParent;
@@ -190,7 +194,6 @@
if (clientState.what & layer_state_t::eBufferChanged) {
externalTexture = resolvedComposerState.externalTexture;
- hwcBufferSlot = resolvedComposerState.hwcBufferSlot;
}
if (clientState.what & layer_state_t::ePositionChanged) {
@@ -254,7 +257,7 @@
",relativeParent=" + layerIdToString(relativeParentId) +
",isRelativeOf=" + std::to_string(isRelativeOf) +
",mirrorId=" + layerIdToString(mirrorId) +
- ",handleAlive=" + std::to_string(handleAlive);
+ ",handleAlive=" + std::to_string(handleAlive) + ",z=" + std::to_string(z);
}
std::string RequestedLayerState::getDebugStringShort() const {
@@ -355,4 +358,13 @@
return Region(win).subtract(exclude).getBounds();
}
+// Returns true if the layer has a relative parent that is not its own parent. This is an input
+// error from the client, and this check allows us to handle it gracefully. If both parentId and
+// relativeParentId is unassigned then the layer does not have a valid relative parent.
+// If the relative parentid is unassigned, the layer will be considered relative but won't be
+// reachable.
+bool RequestedLayerState::hasValidRelativeParent() const {
+ return isRelativeOf && parentId != relativeParentId;
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 0ddf5e2..7849165 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -62,13 +62,14 @@
std::string getDebugString() const;
std::string getDebugStringShort() const;
aidl::android::hardware::graphics::composer3::Composition getCompositionType() const;
+ bool hasValidRelativeParent() const;
// Layer serial number. This gives layers an explicit ordering, so we
// have a stable sort order when their layer stack and Z-order are
// the same.
const uint32_t id;
const std::string name;
- const bool canBeRoot = false;
+ bool canBeRoot = false;
const uint32_t layerCreationFlags;
const uint32_t textureName;
// The owner of the layer. If created from a non system process, it will be the calling uid.
@@ -86,7 +87,6 @@
ui::Transform requestedTransform;
std::shared_ptr<FenceTime> acquireFenceTime;
std::shared_ptr<renderengine::ExternalTexture> externalTexture;
- int hwcBufferSlot = 0;
// book keeping states
bool handleAlive = true;
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
index 8629671..c2109b3 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.cpp
@@ -177,7 +177,7 @@
}
mStalledTransactions.push_back(transactionId);
- listener->onTransactionQueueStalled(String8(reason.c_str()));
+ listener->onTransactionQueueStalled(reason);
}
void TransactionHandler::removeFromStalledTransactions(uint64_t id) {
diff --git a/services/surfaceflinger/FrontEnd/TransactionHandler.h b/services/surfaceflinger/FrontEnd/TransactionHandler.h
index a06b870..475ff1b 100644
--- a/services/surfaceflinger/FrontEnd/TransactionHandler.h
+++ b/services/surfaceflinger/FrontEnd/TransactionHandler.h
@@ -29,6 +29,7 @@
namespace android {
class TestableSurfaceFlinger;
+using gui::IListenerHash;
namespace surfaceflinger::frontend {
class TransactionHandler {
diff --git a/services/surfaceflinger/HwcSlotGenerator.cpp b/services/surfaceflinger/HwcSlotGenerator.cpp
deleted file mode 100644
index 939c35b..0000000
--- a/services/surfaceflinger/HwcSlotGenerator.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "HwcSlotGenerator"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include <gui/BufferQueue.h>
-
-#include "HwcSlotGenerator.h"
-
-namespace android {
-
-HwcSlotGenerator::HwcSlotGenerator() {
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- mFreeHwcCacheSlots.push(i);
- }
-}
-
-void HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) {
- std::lock_guard lock(mMutex);
- if (!clientCacheId.isValid()) {
- ALOGE("invalid process, failed to erase buffer");
- return;
- }
- eraseBufferLocked(clientCacheId);
-}
-
-int HwcSlotGenerator::getHwcCacheSlot(const client_cache_t& clientCacheId) {
- std::lock_guard<std::mutex> lock(mMutex);
- auto itr = mCachedBuffers.find(clientCacheId);
- if (itr == mCachedBuffers.end()) {
- return addCachedBuffer(clientCacheId);
- }
- auto& [hwcCacheSlot, counter] = itr->second;
- counter = mCounter++;
- return hwcCacheSlot;
-}
-
-int HwcSlotGenerator::addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex) {
- if (!clientCacheId.isValid()) {
- ALOGE("invalid process, returning invalid slot");
- return BufferQueue::INVALID_BUFFER_SLOT;
- }
-
- ClientCache::getInstance().registerErasedRecipient(clientCacheId,
- wp<ErasedRecipient>::fromExisting(this));
-
- int hwcCacheSlot = getFreeHwcCacheSlot();
- mCachedBuffers[clientCacheId] = {hwcCacheSlot, mCounter++};
- return hwcCacheSlot;
-}
-
-int HwcSlotGenerator::getFreeHwcCacheSlot() REQUIRES(mMutex) {
- if (mFreeHwcCacheSlots.empty()) {
- evictLeastRecentlyUsed();
- }
-
- int hwcCacheSlot = mFreeHwcCacheSlots.top();
- mFreeHwcCacheSlots.pop();
- return hwcCacheSlot;
-}
-
-void HwcSlotGenerator::evictLeastRecentlyUsed() REQUIRES(mMutex) {
- uint64_t minCounter = UINT_MAX;
- client_cache_t minClientCacheId = {};
- for (const auto& [clientCacheId, slotCounter] : mCachedBuffers) {
- const auto& [hwcCacheSlot, counter] = slotCounter;
- if (counter < minCounter) {
- minCounter = counter;
- minClientCacheId = clientCacheId;
- }
- }
- eraseBufferLocked(minClientCacheId);
-
- ClientCache::getInstance().unregisterErasedRecipient(minClientCacheId,
- wp<ErasedRecipient>::fromExisting(this));
-}
-
-void HwcSlotGenerator::eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex) {
- auto itr = mCachedBuffers.find(clientCacheId);
- if (itr == mCachedBuffers.end()) {
- return;
- }
- auto& [hwcCacheSlot, counter] = itr->second;
-
- // TODO send to hwc cache and resources
-
- mFreeHwcCacheSlots.push(hwcCacheSlot);
- mCachedBuffers.erase(clientCacheId);
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/HwcSlotGenerator.h b/services/surfaceflinger/HwcSlotGenerator.h
deleted file mode 100644
index 5a1b6d7..0000000
--- a/services/surfaceflinger/HwcSlotGenerator.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <functional>
-#include <mutex>
-#include <stack>
-#include <unordered_map>
-
-#include "ClientCache.h"
-
-namespace android {
-
-class HwcSlotGenerator : public ClientCache::ErasedRecipient {
-public:
- HwcSlotGenerator();
- void bufferErased(const client_cache_t& clientCacheId);
- int getHwcCacheSlot(const client_cache_t& clientCacheId);
-
-private:
- friend class SlotGenerationTest;
- int addCachedBuffer(const client_cache_t& clientCacheId) REQUIRES(mMutex);
- int getFreeHwcCacheSlot() REQUIRES(mMutex);
- void evictLeastRecentlyUsed() REQUIRES(mMutex);
- void eraseBufferLocked(const client_cache_t& clientCacheId) REQUIRES(mMutex);
-
- struct CachedBufferHash {
- std::size_t operator()(const client_cache_t& clientCacheId) const {
- return std::hash<uint64_t>{}(clientCacheId.id);
- }
- };
-
- std::mutex mMutex;
-
- std::unordered_map<client_cache_t, std::pair<int /*HwcCacheSlot*/, uint64_t /*counter*/>,
- CachedBufferHash>
- mCachedBuffers GUARDED_BY(mMutex);
- std::stack<int /*HwcCacheSlot*/> mFreeHwcCacheSlots GUARDED_BY(mMutex);
-
- // The cache increments this counter value when a slot is updated or used.
- // Used to track the least recently-used buffer
- uint64_t mCounter = 0;
-};
-} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 0017af0..1f159ae 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -144,7 +144,6 @@
mLayerCreationFlags(args.flags),
mBorderEnabled(false),
mTextureName(args.textureName),
- mHwcSlotGenerator(sp<HwcSlotGenerator>::make()),
mLayerFE(args.flinger->getFactory().createLayerFE(mName)) {
ALOGV("Creating Layer %s", getDebugName());
@@ -217,6 +216,9 @@
}
Layer::~Layer() {
+ LOG_ALWAYS_FATAL_IF(std::this_thread::get_id() != mFlinger->mMainThreadId,
+ "Layer destructor called off the main thread.");
+
// The original layer and the clone layer share the same texture and buffer. Therefore, only
// one of the layers, in this case the original layer, needs to handle the deletion. The
// original layer and the clone should be removed at the same time so there shouldn't be any
@@ -573,9 +575,6 @@
}
snapshot->buffer = getBuffer();
- snapshot->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
- ? 0
- : mBufferInfo.mBufferSlot;
snapshot->acquireFence = mBufferInfo.mFence;
snapshot->frameNumber = mBufferInfo.mFrameNumber;
snapshot->sidebandStreamHasFrame = false;
@@ -1472,8 +1471,9 @@
mFrameTracker.getStats(outStats);
}
-void Layer::dumpCallingUidPid(std::string& result) const {
- StringAppendF(&result, "Layer %s (%s) ownerPid:%d ownerUid:%d\n", getName().c_str(), getType(),
+void Layer::dumpOffscreenDebugInfo(std::string& result) const {
+ std::string hasBuffer = hasBufferOrSidebandStream() ? " (contains buffer)" : "";
+ StringAppendF(&result, "Layer %s%s pid:%d uid:%d\n", getName().c_str(), hasBuffer.c_str(),
mOwnerPid, mOwnerUid);
}
@@ -2604,9 +2604,12 @@
return;
}
ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber);
- listener->onReleaseBuffer({buffer->getId(), framenumber},
- releaseFence ? releaseFence : Fence::NO_FENCE,
- currentMaxAcquiredBufferCount);
+ std::optional<os::ParcelFileDescriptor> fenceFd;
+ if (releaseFence) {
+ fenceFd = os::ParcelFileDescriptor(base::unique_fd(::dup(releaseFence->get())));
+ }
+ listener->onReleaseBuffer({buffer->getId(), framenumber}, fenceFd,
+ static_cast<int32_t>(currentMaxAcquiredBufferCount));
}
void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) {
@@ -2840,7 +2843,7 @@
bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime,
- const FrameTimelineInfo& info, int hwcBufferSlot) {
+ const FrameTimelineInfo& info) {
ATRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
if (!buffer) {
return false;
@@ -2886,7 +2889,6 @@
mDrawingState.releaseBufferListener = bufferData.releaseBufferListener;
mDrawingState.buffer = std::move(buffer);
mDrawingState.clientCacheId = bufferData.cachedBuffer;
- mDrawingState.hwcBufferSlot = hwcBufferSlot;
mDrawingState.acquireFence = bufferData.flags.test(BufferData::BufferDataChange::fenceChanged)
? bufferData.acquireFence
: Fence::NO_FENCE;
@@ -3185,7 +3187,6 @@
mBufferInfo.mHdrMetadata = mDrawingState.hdrMetadata;
mBufferInfo.mApi = mDrawingState.api;
mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse;
- mBufferInfo.mBufferSlot = mDrawingState.hwcBufferSlot;
}
Rect Layer::computeBufferCrop(const State& s) {
@@ -3968,10 +3969,6 @@
}
}
-int Layer::getHwcCacheSlot(const client_cache_t& clientCacheId) {
- return mHwcSlotGenerator->getHwcCacheSlot(clientCacheId);
-}
-
LayerSnapshotGuard::LayerSnapshotGuard(Layer* layer) : mLayer(layer) {
if (mLayer) {
mLayer->mLayerFE->mSnapshot = std::move(mLayer->mSnapshot);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index f743896..08a13a3 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -51,7 +51,6 @@
#include "Client.h"
#include "DisplayHardware/HWComposer.h"
#include "FrameTracker.h"
-#include "HwcSlotGenerator.h"
#include "LayerFE.h"
#include "LayerVector.h"
#include "Scheduler/LayerInfo.h"
@@ -146,7 +145,6 @@
bool transformToDisplayInverse;
Region transparentRegionHint;
std::shared_ptr<renderengine::ExternalTexture> buffer;
- int hwcBufferSlot;
client_cache_t clientCacheId;
sp<Fence> acquireFence;
std::shared_ptr<FenceTime> acquireFenceTime;
@@ -298,8 +296,7 @@
bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */,
const BufferData& /* bufferData */, nsecs_t /* postTime */,
nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
- std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/,
- int /* hwcBufferSlot */);
+ std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/);
bool setDataspace(ui::Dataspace /*dataspace*/);
bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/);
bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/);
@@ -500,7 +497,6 @@
std::shared_ptr<renderengine::ExternalTexture> mBuffer;
uint64_t mFrameNumber;
- int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
bool mFrameLatencyNeeded{false};
};
@@ -634,7 +630,7 @@
void miniDump(std::string& result, const DisplayDevice&) const;
void dumpFrameStats(std::string& result) const;
- void dumpCallingUidPid(std::string& result) const;
+ void dumpOffscreenDebugInfo(std::string& result) const;
void clearFrameStats();
void logFrameStats();
void getFrameStats(FrameStats* outStats) const;
@@ -813,7 +809,6 @@
void updateMetadataSnapshot(const LayerMetadata& parentMetadata);
void updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMetadata,
std::unordered_set<Layer*>& visited);
- int getHwcCacheSlot(const client_cache_t& clientCacheId);
protected:
// For unit tests
@@ -1124,8 +1119,6 @@
// not specify a destination frame.
ui::Transform mRequestedTransform;
- sp<HwcSlotGenerator> mHwcSlotGenerator;
-
sp<LayerFE> mLayerFE;
std::unique_ptr<LayerSnapshot> mSnapshot = std::make_unique<LayerSnapshot>();
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 9b19afb..7aa7e17 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -42,8 +42,8 @@
constexpr int kDigitHeight = 100;
constexpr int kDigitSpace = 16;
-// Layout is digit, space, digit, space, digit, space, spinner.
-constexpr int kBufferWidth = 4 * kDigitWidth + 3 * kDigitSpace;
+constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1;
+constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace;
constexpr int kBufferHeight = kDigitHeight;
SurfaceComposerClient::Transaction createTransaction(const sp<SurfaceControl>& surface) {
@@ -121,16 +121,10 @@
drawSegment(Segment::Bottom, left, color, canvas);
}
-auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color,
+auto RefreshRateOverlay::SevenSegmentDrawer::draw(int displayFps, int renderFps, SkColor color,
ui::Transform::RotationFlags rotation,
- bool showSpinner) -> Buffers {
- if (number < 0 || number > 1000) return {};
-
- const auto hundreds = number / 100;
- const auto tens = (number / 10) % 10;
- const auto ones = number % 10;
-
- const size_t loopCount = showSpinner ? 6 : 1;
+ ftl::Flags<Features> features) -> Buffers {
+ const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
Buffers buffers;
buffers.reserve(loopCount);
@@ -169,20 +163,9 @@
canvas->setMatrix(canvasTransform);
int left = 0;
- if (hundreds != 0) {
- drawDigit(hundreds, left, color, *canvas);
- }
- left += kDigitWidth + kDigitSpace;
-
- if (tens != 0) {
- drawDigit(tens, left, color, *canvas);
- }
- left += kDigitWidth + kDigitSpace;
-
- drawDigit(ones, left, color, *canvas);
- left += kDigitWidth + kDigitSpace;
-
- if (showSpinner) {
+ drawNumber(displayFps, left, color, *canvas);
+ left += 3 * (kDigitWidth + kDigitSpace);
+ if (features.test(Features::Spinner)) {
switch (i) {
case 0:
drawSegment(Segment::Upper, left, color, *canvas);
@@ -205,6 +188,13 @@
}
}
+ left += kDigitWidth + kDigitSpace;
+
+ if (features.test(Features::RenderRate)) {
+ drawNumber(renderFps, left, color, *canvas);
+ }
+ left += 3 * (kDigitWidth + kDigitSpace);
+
void* pixels = nullptr;
buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
@@ -219,6 +209,23 @@
return buffers;
}
+void RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number, int left, SkColor color,
+ SkCanvas& canvas) {
+ if (number < 0 || number >= 1000) return;
+
+ if (number >= 100) {
+ drawDigit(number / 100, left, color, canvas);
+ }
+ left += kDigitWidth + kDigitSpace;
+
+ if (number >= 10) {
+ drawDigit((number / 10) % 10, left, color, canvas);
+ }
+ left += kDigitWidth + kDigitSpace;
+
+ drawDigit(number % 10, left, color, canvas);
+}
+
std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() {
sp<SurfaceControl> surfaceControl =
SurfaceComposerClient::getDefault()
@@ -228,10 +235,8 @@
return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl));
}
-RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner)
- : mFpsRange(fpsRange),
- mShowSpinner(showSpinner),
- mSurfaceControl(createSurfaceControlHolder()) {
+RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, ftl::Flags<Features> features)
+ : mFpsRange(fpsRange), mFeatures(features), mSurfaceControl(createSurfaceControlHolder()) {
if (!mSurfaceControl) {
ALOGE("%s: Failed to create buffer state layer", __func__);
return;
@@ -243,10 +248,15 @@
.apply();
}
-auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& {
+auto RefreshRateOverlay::getOrCreateBuffers(Fps displayFps, Fps renderFps) -> const Buffers& {
static const Buffers kNoBuffers;
if (!mSurfaceControl) return kNoBuffers;
+ // avoid caching different render rates if RenderRate is anyway not visible
+ if (!mFeatures.test(Features::RenderRate)) {
+ renderFps = 0_Hz;
+ }
+
const auto transformHint =
static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
@@ -266,17 +276,20 @@
.setTransform(mSurfaceControl->get(), transform)
.apply();
- BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint});
+ BufferCache::const_iterator it =
+ mBufferCache.find({displayFps.getIntValue(), renderFps.getIntValue(), transformHint});
if (it == mBufferCache.end()) {
const int minFps = mFpsRange.min.getIntValue();
const int maxFps = mFpsRange.max.getIntValue();
- // Clamp to the range. The current fps may be outside of this range if the display has
- // changed its set of supported refresh rates.
- const int intFps = std::clamp(fps.getIntValue(), minFps, maxFps);
+ // Clamp to the range. The current displayFps may be outside of this range if the display
+ // has changed its set of supported refresh rates.
+ const int displayIntFps = std::clamp(displayFps.getIntValue(), minFps, maxFps);
+ const int renderIntFps = renderFps.getIntValue();
// Ensure non-zero range to avoid division by zero.
- const float fpsScale = static_cast<float>(intFps - minFps) / std::max(1, maxFps - minFps);
+ const float fpsScale =
+ static_cast<float>(displayIntFps - minFps) / std::max(1, maxFps - minFps);
constexpr SkColor kMinFpsColor = SK_ColorRED;
constexpr SkColor kMaxFpsColor = SK_ColorGREEN;
@@ -292,8 +305,11 @@
const SkColor color = colorBase.toSkColor();
- auto buffers = SevenSegmentDrawer::draw(intFps, color, transformHint, mShowSpinner);
- it = mBufferCache.try_emplace({intFps, transformHint}, std::move(buffers)).first;
+ auto buffers = SevenSegmentDrawer::draw(displayIntFps, renderIntFps, color, transformHint,
+ mFeatures);
+ it = mBufferCache
+ .try_emplace({displayIntFps, renderIntFps, transformHint}, std::move(buffers))
+ .first;
}
return it->second;
@@ -303,7 +319,7 @@
constexpr int32_t kMaxWidth = 1000;
const auto width = std::min({kMaxWidth, viewport.width, viewport.height});
const auto height = 2 * width;
- Rect frame((3 * width) >> 4, height >> 5);
+ Rect frame((5 * width) >> 4, height >> 5);
frame.offsetBy(width >> 5, height >> 4);
createTransaction(mSurfaceControl->get())
@@ -317,16 +333,17 @@
createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply();
}
-void RefreshRateOverlay::changeRefreshRate(Fps fps) {
- mCurrentFps = fps;
- const auto buffer = getOrCreateBuffers(fps)[mFrame];
+void RefreshRateOverlay::changeRefreshRate(Fps displayFps, Fps renderFps) {
+ mDisplayFps = displayFps;
+ mRenderFps = renderFps;
+ const auto buffer = getOrCreateBuffers(displayFps, renderFps)[mFrame];
createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
}
void RefreshRateOverlay::animate() {
- if (!mShowSpinner || !mCurrentFps) return;
+ if (!mFeatures.test(Features::Spinner) || !mDisplayFps) return;
- const auto& buffers = getOrCreateBuffers(*mCurrentFps);
+ const auto& buffers = getOrCreateBuffers(*mDisplayFps, *mRenderFps);
mFrame = (mFrame + 1) % buffers.size();
const auto buffer = buffers[mFrame];
createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply();
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index a2966e6..d6f828f 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -19,6 +19,7 @@
#include <SkColor.h>
#include <vector>
+#include <ftl/flags.h>
#include <ftl/small_map.h>
#include <ui/LayerStack.h>
#include <ui/Size.h>
@@ -50,11 +51,16 @@
class RefreshRateOverlay {
public:
- RefreshRateOverlay(FpsRange, bool showSpinner);
+ enum class Features {
+ Spinner = 1 << 0,
+ RenderRate = 1 << 1,
+ };
+
+ RefreshRateOverlay(FpsRange, ftl::Flags<Features>);
void setLayerStack(ui::LayerStack);
void setViewport(ui::Size);
- void changeRefreshRate(Fps);
+ void changeRefreshRate(Fps, Fps);
void animate();
private:
@@ -62,32 +68,39 @@
class SevenSegmentDrawer {
public:
- static Buffers draw(int number, SkColor, ui::Transform::RotationFlags, bool showSpinner);
+ static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags,
+ ftl::Flags<Features>);
private:
enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom };
static void drawSegment(Segment, int left, SkColor, SkCanvas&);
static void drawDigit(int digit, int left, SkColor, SkCanvas&);
+ static void drawNumber(int number, int left, SkColor, SkCanvas&);
};
- const Buffers& getOrCreateBuffers(Fps);
+ const Buffers& getOrCreateBuffers(Fps, Fps);
struct Key {
- int fps;
+ int displayFps;
+ int renderFps;
ui::Transform::RotationFlags flags;
- bool operator==(Key other) const { return fps == other.fps && flags == other.flags; }
+ bool operator==(Key other) const {
+ return displayFps == other.displayFps && renderFps == other.renderFps &&
+ flags == other.flags;
+ }
};
using BufferCache = ftl::SmallMap<Key, Buffers, 9>;
BufferCache mBufferCache;
- std::optional<Fps> mCurrentFps;
+ std::optional<Fps> mDisplayFps;
+ std::optional<Fps> mRenderFps;
size_t mFrame = 0;
const FpsRange mFpsRange; // For color interpolation.
- const bool mShowSpinner;
+ const ftl::Flags<Features> mFeatures;
const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
};
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 1acf15a..008d8c4 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -124,12 +124,12 @@
return event;
}
-DisplayEventReceiver::Event makeModeChanged(DisplayModePtr mode) {
+DisplayEventReceiver::Event makeModeChanged(const scheduler::FrameRateMode& mode) {
DisplayEventReceiver::Event event;
- event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, mode->getPhysicalDisplayId(),
- systemTime()};
- event.modeChange.modeId = mode->getId().value();
- event.modeChange.vsyncPeriod = mode->getVsyncPeriod();
+ event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE,
+ mode.modePtr->getPhysicalDisplayId(), systemTime()};
+ event.modeChange.modeId = mode.modePtr->getId().value();
+ event.modeChange.vsyncPeriod = mode.fps.getPeriodNsecs();
return event;
}
@@ -405,7 +405,7 @@
mCondition.notify_all();
}
-void EventThread::onModeChanged(DisplayModePtr mode) {
+void EventThread::onModeChanged(const scheduler::FrameRateMode& mode) {
std::lock_guard<std::mutex> lock(mMutex);
mPendingEvents.push_back(makeModeChanged(mode));
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 7a5a348..43c3598 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -23,6 +23,7 @@
#include <sys/types.h>
#include <utils/Errors.h>
+#include <scheduler/FrameRateMode.h>
#include <condition_variable>
#include <cstdint>
#include <deque>
@@ -134,7 +135,7 @@
virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
// called when SF changes the active mode and apps needs to be notified about the change
- virtual void onModeChanged(DisplayModePtr) = 0;
+ virtual void onModeChanged(const scheduler::FrameRateMode&) = 0;
// called when SF updates the Frame Rate Override list
virtual void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
@@ -185,7 +186,7 @@
void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
- void onModeChanged(DisplayModePtr) override;
+ void onModeChanged(const scheduler::FrameRateMode&) override;
void onFrameRateOverridesChanged(PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) override;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index c781a6d..a05d3df 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -837,21 +837,25 @@
return frameRateOverrides;
}
-std::optional<Fps> RefreshRateSelector::onKernelTimerChanged(
+ftl::Optional<FrameRateMode> RefreshRateSelector::onKernelTimerChanged(
std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const {
std::lock_guard lock(mLock);
- const DisplayModePtr& current = desiredActiveModeId
- ? mDisplayModes.get(*desiredActiveModeId)->get()
- : getActiveModeLocked().modePtr.get();
+ const auto current = [&]() REQUIRES(mLock) -> FrameRateMode {
+ if (desiredActiveModeId) {
+ const auto& modePtr = mDisplayModes.get(*desiredActiveModeId)->get();
+ return FrameRateMode{modePtr->getFps(), ftl::as_non_null(modePtr)};
+ }
+
+ return getActiveModeLocked();
+ }();
const DisplayModePtr& min = mMinRefreshRateModeIt->second;
- if (current == min) {
+ if (current.modePtr->getId() == min->getId()) {
return {};
}
- const auto& mode = timerExpired ? min : current;
- return mode->getFps();
+ return timerExpired ? FrameRateMode{min->getFps(), ftl::as_non_null(min)} : current;
}
const DisplayModePtr& RefreshRateSelector::getMinRefreshRateByPolicyLocked() const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index 1ed16c6..4f5842a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -240,8 +240,9 @@
return {mMinRefreshRateModeIt->second->getFps(), mMaxRefreshRateModeIt->second->getFps()};
}
- std::optional<Fps> onKernelTimerChanged(std::optional<DisplayModeId> desiredActiveModeId,
- bool timerExpired) const EXCLUDES(mLock);
+ ftl::Optional<FrameRateMode> onKernelTimerChanged(
+ std::optional<DisplayModeId> desiredActiveModeId, bool timerExpired) const
+ EXCLUDES(mLock);
void setActiveMode(DisplayModeId, Fps renderFrameRate) EXCLUDES(mLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 7f8f600..856fda0 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -330,7 +330,7 @@
RETURN_IF_INVALID_HANDLE(handle);
thread = mConnections[handle].thread.get();
}
- thread->onModeChanged(mode.modePtr.get());
+ thread->onModeChanged(mode);
}
size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) {
@@ -815,18 +815,18 @@
.powerOnImminent = powerOnImminent};
}
-ftl::Optional<FrameRateMode> Scheduler::getPreferredDisplayMode() {
+FrameRateMode Scheduler::getPreferredDisplayMode() {
std::lock_guard<std::mutex> lock(mPolicyLock);
- // Make sure the stored mode is up to date.
- if (mPolicy.modeOpt) {
- const auto ranking =
- leaderSelectorPtr()
- ->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals())
- .ranking;
+ const auto frameRateMode =
+ leaderSelectorPtr()
+ ->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals())
+ .ranking.front()
+ .frameRateMode;
- mPolicy.modeOpt = ranking.front().frameRateMode;
- }
- return mPolicy.modeOpt;
+ // Make sure the stored mode is up to date.
+ mPolicy.modeOpt = frameRateMode;
+
+ return frameRateMode;
}
void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index cf2ffb8..f189426 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -209,8 +209,8 @@
void dump(ConnectionHandle, std::string&) const;
void dumpVsync(std::string&) const;
- // Get the appropriate refresh for current conditions.
- ftl::Optional<FrameRateMode> getPreferredDisplayMode();
+ // Returns the preferred refresh rate and frame rate for the leader display.
+ FrameRateMode getPreferredDisplayMode();
// Notifies the scheduler about a refresh rate timeline change.
void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 7bd08a7..b930477 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -112,6 +112,7 @@
#include <ui/DisplayIdentification.h>
#include "BackgroundExecutor.h"
#include "Client.h"
+#include "ClientCache.h"
#include "Colorizer.h"
#include "Display/DisplayMap.h"
#include "DisplayDevice.h"
@@ -198,6 +199,9 @@
namespace {
+static constexpr int FOUR_K_WIDTH = 3840;
+static constexpr int FOUR_K_HEIGHT = 2160;
+
// TODO(b/141333600): Consolidate with DisplayMode::Builder::getDefaultDensity.
constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
@@ -245,6 +249,44 @@
return displaySupportKernelIdleTimer || sysprop::support_kernel_idle_timer(false);
}
+bool isAbove4k30(const ui::DisplayMode& outMode) {
+ using fps_approx_ops::operator>;
+ Fps refreshRate = Fps::fromValue(outMode.refreshRate);
+ return outMode.resolution.getWidth() >= FOUR_K_WIDTH &&
+ outMode.resolution.getHeight() >= FOUR_K_HEIGHT && refreshRate > 30_Hz;
+}
+
+void excludeDolbyVisionIf4k30Present(const std::vector<ui::Hdr>& displayHdrTypes,
+ ui::DisplayMode& outMode) {
+ if (isAbove4k30(outMode) &&
+ std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION_4K30; })) {
+ for (ui::Hdr type : displayHdrTypes) {
+ if (type != ui::Hdr::DOLBY_VISION_4K30 && type != ui::Hdr::DOLBY_VISION) {
+ outMode.supportedHdrTypes.push_back(type);
+ }
+ }
+ } else {
+ for (ui::Hdr type : displayHdrTypes) {
+ if (type != ui::Hdr::DOLBY_VISION_4K30) {
+ outMode.supportedHdrTypes.push_back(type);
+ }
+ }
+ }
+}
+
+HdrCapabilities filterOut4k30(const HdrCapabilities& displayHdrCapabilities) {
+ std::vector<ui::Hdr> hdrTypes;
+ for (ui::Hdr type : displayHdrCapabilities.getSupportedHdrTypes()) {
+ if (type != ui::Hdr::DOLBY_VISION_4K30) {
+ hdrTypes.push_back(type);
+ }
+ }
+ return {hdrTypes, displayHdrCapabilities.getDesiredMaxLuminance(),
+ displayHdrCapabilities.getDesiredMaxAverageLuminance(),
+ displayHdrCapabilities.getDesiredMinLuminance()};
+}
+
} // namespace anonymous
// ---------------------------------------------------------------------------
@@ -257,6 +299,7 @@
const String16 sDump("android.permission.DUMP");
const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT");
const String16 sInternalSystemWindow("android.permission.INTERNAL_SYSTEM_WINDOW");
+const String16 sWakeupSurfaceFlinger("android.permission.WAKEUP_SURFACE_FLINGER");
const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
@@ -290,20 +333,12 @@
}
}
-bool callingThreadHasRotateSurfaceFlingerAccess() {
+bool callingThreadHasPermission(const String16& permission) {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
- PermissionCache::checkPermission(sRotateSurfaceFlinger, pid, uid);
-}
-
-bool callingThreadHasInternalSystemWindowAccess() {
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int uid = ipc->getCallingUid();
- return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
- PermissionCache::checkPermission(sInternalSystemWindow, pid, uid);
+ PermissionCache::checkPermission(permission, pid, uid);
}
SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
@@ -384,8 +419,6 @@
bool supportsBlurs = atoi(value);
mSupportsBlur = supportsBlurs;
ALOGI_IF(!mSupportsBlur, "Disabling blur effects, they are not supported.");
- property_get("ro.sf.blurs_are_expensive", value, "0");
- mBlursAreExpensive = atoi(value);
const size_t defaultListSize = MAX_LAYERS;
auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
@@ -422,7 +455,9 @@
android::hardware::details::setTrebleTestingOverride(true);
}
- mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
+ mRefreshRateOverlaySpinner = property_get_bool("debug.sf.show_refresh_rate_overlay_spinner", 0);
+ mRefreshRateOverlayRenderRate =
+ property_get_bool("debug.sf.show_refresh_rate_overlay_render_rate", 0);
if (!mIsUserBuild && base::GetBoolProperty("debug.sf.enable_transaction_tracing"s, true)) {
mTransactionTracing.emplace();
@@ -931,17 +966,14 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getStaticDisplayInfo(const sp<IBinder>& displayToken,
- ui::StaticDisplayInfo* info) {
- if (!displayToken || !info) {
+status_t SurfaceFlinger::getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo* info) {
+ if (!info) {
return BAD_VALUE;
}
Mutex::Autolock lock(mStateLock);
-
- const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
- .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
- .and_then(getDisplayDeviceAndSnapshot());
+ const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId));
+ const auto displayOpt = mPhysicalDisplays.get(*id).and_then(getDisplayDeviceAndSnapshot());
if (!displayOpt) {
return NAME_NOT_FOUND;
@@ -968,26 +1000,10 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getDynamicDisplayInfo(const sp<IBinder>& displayToken,
- ui::DynamicDisplayInfo* info) {
- if (!displayToken || !info) {
- return BAD_VALUE;
- }
-
- Mutex::Autolock lock(mStateLock);
-
- const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
- .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
- .and_then(getDisplayDeviceAndSnapshot());
- if (!displayOpt) {
- return NAME_NOT_FOUND;
- }
-
- const auto& [display, snapshotRef] = *displayOpt;
- const auto& snapshot = snapshotRef.get();
-
+void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info,
+ const sp<DisplayDevice>& display,
+ const display::DisplaySnapshot& snapshot) {
const auto& displayModes = snapshot.displayModes();
-
info->supportedDisplayModes.clear();
info->supportedDisplayModes.reserve(displayModes.size());
@@ -1031,7 +1047,8 @@
// We add an additional 1ms to allow for processing time and
// differences between the ideal and actual refresh rate.
outMode.presentationDeadline = period - outMode.sfVsyncOffset + 1000000;
-
+ excludeDolbyVisionIf4k30Present(display->getHdrCapabilities().getSupportedHdrTypes(),
+ outMode);
info->supportedDisplayModes.push_back(outMode);
}
@@ -1039,10 +1056,11 @@
const PhysicalDisplayId displayId = snapshot.displayId();
- info->activeDisplayModeId =
- display->refreshRateSelector().getActiveMode().modePtr->getId().value();
+ const auto mode = display->refreshRateSelector().getActiveMode();
+ info->activeDisplayModeId = mode.modePtr->getId().value();
+ info->renderFrameRate = mode.fps.getValue();
info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
- info->hdrCapabilities = display->getHdrCapabilities();
+ info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
info->autoLowLatencyModeSupported =
getHwComposer().hasDisplayCapability(displayId,
@@ -1059,7 +1077,47 @@
}
}
}
+}
+status_t SurfaceFlinger::getDynamicDisplayInfoFromId(int64_t physicalDisplayId,
+ ui::DynamicDisplayInfo* info) {
+ if (!info) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto id_ =
+ DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(physicalDisplayId));
+ const auto displayOpt = mPhysicalDisplays.get(*id_).and_then(getDisplayDeviceAndSnapshot());
+
+ if (!displayOpt) {
+ return NAME_NOT_FOUND;
+ }
+
+ const auto& [display, snapshotRef] = *displayOpt;
+ getDynamicDisplayInfoInternal(info, display, snapshotRef.get());
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+ ui::DynamicDisplayInfo* info) {
+ if (!displayToken || !info) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+
+ const auto displayOpt = ftl::find_if(mPhysicalDisplays, PhysicalDisplay::hasToken(displayToken))
+ .transform(&ftl::to_mapped_ref<PhysicalDisplays>)
+ .and_then(getDisplayDeviceAndSnapshot());
+
+ if (!displayOpt) {
+ return NAME_NOT_FOUND;
+ }
+
+ const auto& [display, snapshotRef] = *displayOpt;
+ getDynamicDisplayInfoInternal(info, display, snapshotRef.get());
return NO_ERROR;
}
@@ -1074,7 +1132,7 @@
return NO_ERROR;
}
-void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request) {
+void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request, bool force) {
ATRACE_CALL();
auto display = getDisplayDeviceLocked(request.mode.modePtr->getPhysicalDisplayId());
@@ -1083,30 +1141,32 @@
return;
}
- const Fps renderFps = request.mode.fps;
- const Fps displayFps = request.mode.modePtr->getFps();
+ const auto mode = request.mode;
+ const bool emitEvent = request.emitEvent;
- switch (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)))) {
+ switch (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)),
+ force)) {
case DisplayDevice::DesiredActiveModeAction::InitiateDisplayModeSwitch:
scheduleComposite(FrameHint::kNone);
// Start receiving vsync samples now, so that we can detect a period
// switch.
- mScheduler->resyncToHardwareVsync(true, displayFps);
+ mScheduler->resyncToHardwareVsync(true, mode.modePtr->getFps());
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// VsyncController model is locked.
modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
- updatePhaseConfiguration(renderFps);
+ updatePhaseConfiguration(mode.fps);
mScheduler->setModeChangePending(true);
break;
case DisplayDevice::DesiredActiveModeAction::InitiateRenderRateSwitch:
- mScheduler->setRenderRate(renderFps);
- updatePhaseConfiguration(renderFps);
- mRefreshRateStats->setRefreshRate(renderFps);
+ mScheduler->setRenderRate(mode.fps);
+ updatePhaseConfiguration(mode.fps);
+ mRefreshRateStats->setRefreshRate(mode.fps);
+ if (display->getPhysicalId() == mActiveDisplayId && emitEvent) {
+ mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, mode);
+ }
- // TODO(b/259740021): send event to display manager about
- // the render rate change
break;
case DisplayDevice::DesiredActiveModeAction::None:
break;
@@ -1512,7 +1572,8 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::string* pulledData, bool* success) {
+status_t SurfaceFlinger::onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData,
+ bool* success) {
*success = mTimeStats->onPullAtom(atomId, pulledData);
return NO_ERROR;
}
@@ -2211,6 +2272,8 @@
});
}
+ refreshArgs.bufferIdsToUncache = std::move(mBufferIdsToUncache);
+
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
for (auto layer : mLayersWithQueuedFrames) {
if (auto layerFE = layer->getCompositionEngineLayerFE())
@@ -2234,7 +2297,6 @@
layers.push_back(layer);
}
});
- refreshArgs.blursAreExpensive = mBlursAreExpensive;
refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags();
if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
@@ -3396,10 +3458,21 @@
for (auto& request : modeRequests) {
const auto& modePtr = request.mode.modePtr;
- const auto display = getDisplayDeviceLocked(modePtr->getPhysicalDisplayId());
+
+ const auto displayId = modePtr->getPhysicalDisplayId();
+ const auto display = getDisplayDeviceLocked(displayId);
if (!display) continue;
+ const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
+ .transform(&PhysicalDisplay::isInternal)
+ .value_or(false);
+
+ if (isInternalDisplay && displayId != mActiveDisplayId) {
+ ALOGV("%s(%s): Inactive display", __func__, to_string(displayId).c_str());
+ continue;
+ }
+
if (display->refreshRateSelector().isModeAllowed(request.mode)) {
setDesiredActiveMode(std::move(request));
} else {
@@ -3921,18 +3994,23 @@
// Avoid checking for rotation permissions if the caller already has ACCESS_SURFACE_FLINGER
// permissions.
if ((permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) ||
- callingThreadHasRotateSurfaceFlingerAccess()) {
+ callingThreadHasPermission(sRotateSurfaceFlinger)) {
permissions |= layer_state_t::Permission::ROTATE_SURFACE_FLINGER;
}
- if (callingThreadHasInternalSystemWindowAccess()) {
+ if (callingThreadHasPermission(sInternalSystemWindow)) {
permissions |= layer_state_t::Permission::INTERNAL_SYSTEM_WINDOW;
}
- if (!(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) &&
- (flags & (eEarlyWakeupStart | eEarlyWakeupEnd))) {
- ALOGE("Only WindowManager is allowed to use eEarlyWakeup[Start|End] flags");
- flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
+ if (flags & (eEarlyWakeupStart | eEarlyWakeupEnd)) {
+ const bool hasPermission =
+ (permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) ||
+ callingThreadHasPermission(sWakeupSurfaceFlinger);
+ if (!hasPermission) {
+ ALOGE("Caller needs permission android.permission.WAKEUP_SURFACE_FLINGER to use "
+ "eEarlyWakeup[Start|End] flags");
+ flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
+ }
}
const int64_t postTime = systemTime();
@@ -3955,10 +4033,6 @@
getExternalTextureFromBufferData(*resolvedState.state.bufferData,
layerName.c_str(), transactionId);
mBufferCountTracker.increment(resolvedState.state.surface->localBinder());
- if (layer) {
- resolvedState.hwcBufferSlot =
- layer->getHwcCacheSlot(resolvedState.state.bufferData->cachedBuffer);
- }
}
}
@@ -3990,7 +4064,7 @@
bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
std::vector<ResolvedComposerState>& states,
- const Vector<DisplayState>& displays, uint32_t flags,
+ Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& uncacheBuffer,
@@ -3999,7 +4073,8 @@
const std::vector<ListenerCallbacks>& listenerCallbacks,
int originPid, int originUid, uint64_t transactionId) {
uint32_t transactionFlags = 0;
- for (const DisplayState& display : displays) {
+ for (DisplayState& display : displays) {
+ display.sanitize(permissions);
transactionFlags |= setDisplayStateLocked(display);
}
@@ -4034,7 +4109,10 @@
}
if (uncacheBuffer.isValid()) {
- ClientCache::getInstance().erase(uncacheBuffer);
+ sp<GraphicBuffer> buffer = ClientCache::getInstance().erase(uncacheBuffer);
+ if (buffer != nullptr) {
+ mBufferIdsToUncache.push_back(buffer->getId());
+ }
}
// If a synchronous transaction is explicitly requested without any changes, force a transaction
@@ -4425,7 +4503,7 @@
if (what & layer_state_t::eBufferChanged) {
if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp,
- frameTimelineInfo, composerState.hwcBufferSlot)) {
+ frameTimelineInfo)) {
flags |= eTraversalNeeded;
}
} else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
@@ -4685,19 +4763,34 @@
.value_or(false);
const auto activeDisplay = getDisplayDeviceLocked(mActiveDisplayId);
- if (isInternalDisplay && activeDisplay != display && activeDisplay &&
- activeDisplay->isPoweredOn()) {
- ALOGW("Trying to change power mode on non active display while the active display is ON");
- }
+
+ ALOGW_IF(display != activeDisplay && isInternalDisplay && activeDisplay &&
+ activeDisplay->isPoweredOn(),
+ "Trying to change power mode on inactive display without powering off active display");
display->setPowerMode(mode);
const auto refreshRate = display->refreshRateSelector().getActiveMode().modePtr->getFps();
if (!currentModeOpt || *currentModeOpt == hal::PowerMode::OFF) {
// Turn on the display
- if (isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn())) {
- onActiveDisplayChangedLocked(display);
+
+ // Activate the display (which involves a modeset to the active mode):
+ // 1) When the first (a.k.a. primary) display is powered on during boot.
+ // 2) When the inner or outer display of a foldable is powered on. This condition relies
+ // on the above DisplayDevice::setPowerMode. If `display` and `activeDisplay` are the
+ // same display, then the `activeDisplay->isPoweredOn()` below is true, such that the
+ // display is not activated every time it is powered on.
+ //
+ // TODO(b/255635821): Remove the concept of active display.
+ const bool activeDisplayChanged =
+ isInternalDisplay && (!activeDisplay || !activeDisplay->isPoweredOn());
+
+ static bool sPrimaryDisplay = true;
+ if (sPrimaryDisplay || activeDisplayChanged) {
+ onActiveDisplayChangedLocked(activeDisplay, display);
+ sPrimaryDisplay = false;
}
+
// Keep uclamp in a separate syscall and set it before changing to RT due to b/190237315.
// We can merge the syscall later.
if (SurfaceFlinger::setSchedAttr(true) != NO_ERROR) {
@@ -5150,7 +5243,7 @@
std::string result;
for (Layer* offscreenLayer : mOffscreenLayers) {
offscreenLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* layer) { layer->dumpCallingUidPid(result); });
+ [&](Layer* layer) { layer->dumpOffscreenDebugInfo(result); });
}
return result;
});
@@ -6644,7 +6737,7 @@
ftl::Optional<scheduler::FrameRateMode> SurfaceFlinger::getPreferredDisplayMode(
PhysicalDisplayId displayId, DisplayModeId defaultModeId) const {
if (const auto schedulerMode = mScheduler->getPreferredDisplayMode();
- schedulerMode && schedulerMode->modePtr->getPhysicalDisplayId() == displayId) {
+ schedulerMode.modePtr->getPhysicalDisplayId() == displayId) {
return schedulerMode;
}
@@ -6679,12 +6772,24 @@
case SetPolicyResult::Unchanged:
return NO_ERROR;
case SetPolicyResult::Changed:
- return applyRefreshRateSelectorPolicy(displayId, selector);
+ break;
}
+
+ const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
+ .transform(&PhysicalDisplay::isInternal)
+ .value_or(false);
+
+ if (isInternalDisplay && displayId != mActiveDisplayId) {
+ // The policy will be be applied when the display becomes active.
+ ALOGV("%s(%s): Inactive display", __func__, to_string(displayId).c_str());
+ return NO_ERROR;
+ }
+
+ return applyRefreshRateSelectorPolicy(displayId, selector);
}
status_t SurfaceFlinger::applyRefreshRateSelectorPolicy(
- PhysicalDisplayId displayId, const scheduler::RefreshRateSelector& selector) {
+ PhysicalDisplayId displayId, const scheduler::RefreshRateSelector& selector, bool force) {
const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy();
ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
@@ -6714,7 +6819,7 @@
return INVALID_OPERATION;
}
- setDesiredActiveMode({std::move(preferredMode), .emitEvent = true});
+ setDesiredActiveMode({std::move(preferredMode), .emitEvent = true}, force);
return NO_ERROR;
}
@@ -6882,7 +6987,8 @@
for (const auto& [id, display] : mPhysicalDisplays) {
if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
if (const auto device = getDisplayDeviceLocked(id)) {
- device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner);
+ device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner,
+ mRefreshRateOverlayRenderRate);
}
}
}
@@ -6995,17 +7101,14 @@
getRenderEngine().onActiveDisplaySizeChanged(activeDisplay->getSize());
}
-void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay) {
+void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& inactiveDisplay,
+ const sp<DisplayDevice>& activeDisplay) {
ATRACE_CALL();
- if (const auto display = getDisplayDeviceLocked(mActiveDisplayId)) {
- display->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
+ if (inactiveDisplay) {
+ inactiveDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
}
- if (!activeDisplay) {
- ALOGE("%s: activeDisplay is null", __func__);
- return;
- }
mActiveDisplayId = activeDisplay->getPhysicalId();
activeDisplay->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
@@ -7020,7 +7123,8 @@
// case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In either
// case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode,
// and the kernel idle timer of the newly active display must be toggled.
- applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay->refreshRateSelector());
+ constexpr bool kForce = true;
+ applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay->refreshRateSelector(), kForce);
}
status_t SurfaceFlinger::addWindowInfosListener(
@@ -7046,8 +7150,7 @@
layerName, static_cast<uint32_t>(mMaxRenderTargetSize));
ALOGD("%s", errorMessage.c_str());
if (bufferData.releaseBufferListener) {
- bufferData.releaseBufferListener->onTransactionQueueStalled(
- String8(errorMessage.c_str()));
+ bufferData.releaseBufferListener->onTransactionQueueStalled(errorMessage);
}
return nullptr;
}
@@ -7065,7 +7168,7 @@
if (bufferData.releaseBufferListener) {
bufferData.releaseBufferListener->onTransactionQueueStalled(
- String8("Buffer processing hung due to full buffer cache"));
+ "Buffer processing hung due to full buffer cache");
}
}
@@ -7239,6 +7342,10 @@
binder::Status SurfaceComposerAIDL::getPhysicalDisplayToken(int64_t displayId,
sp<IBinder>* outDisplay) {
+ status_t status = checkAccessPermission();
+ if (status != OK) {
+ return binderStatusFromStatusT(status);
+ }
const auto id = DisplayId::fromValue<PhysicalDisplayId>(static_cast<uint64_t>(displayId));
*outDisplay = mFlinger->getPhysicalDisplayToken(*id);
return binder::Status::ok();
@@ -7289,11 +7396,12 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::getStaticDisplayInfo(const sp<IBinder>& display,
+binder::Status SurfaceComposerAIDL::getStaticDisplayInfo(int64_t displayId,
gui::StaticDisplayInfo* outInfo) {
using Tag = gui::DeviceProductInfo::ManufactureOrModelDate::Tag;
ui::StaticDisplayInfo info;
- status_t status = mFlinger->getStaticDisplayInfo(display, &info);
+
+ status_t status = mFlinger->getStaticDisplayInfo(displayId, &info);
if (status == NO_ERROR) {
// convert ui::StaticDisplayInfo to gui::StaticDisplayInfo
outInfo->connectionType = static_cast<gui::DisplayConnectionType>(info.connectionType);
@@ -7332,53 +7440,71 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::getDynamicDisplayInfo(const sp<IBinder>& display,
- gui::DynamicDisplayInfo* outInfo) {
+void SurfaceComposerAIDL::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
+ gui::DynamicDisplayInfo*& outInfo) {
+ // convert ui::DynamicDisplayInfo to gui::DynamicDisplayInfo
+ outInfo->supportedDisplayModes.clear();
+ outInfo->supportedDisplayModes.reserve(info.supportedDisplayModes.size());
+ for (const auto& mode : info.supportedDisplayModes) {
+ gui::DisplayMode outMode;
+ outMode.id = mode.id;
+ outMode.resolution.width = mode.resolution.width;
+ outMode.resolution.height = mode.resolution.height;
+ outMode.xDpi = mode.xDpi;
+ outMode.yDpi = mode.yDpi;
+ outMode.refreshRate = mode.refreshRate;
+ outMode.appVsyncOffset = mode.appVsyncOffset;
+ outMode.sfVsyncOffset = mode.sfVsyncOffset;
+ outMode.presentationDeadline = mode.presentationDeadline;
+ outMode.group = mode.group;
+ std::transform(mode.supportedHdrTypes.begin(), mode.supportedHdrTypes.end(),
+ std::back_inserter(outMode.supportedHdrTypes),
+ [](const ui::Hdr& value) { return static_cast<int32_t>(value); });
+ outInfo->supportedDisplayModes.push_back(outMode);
+ }
+
+ outInfo->activeDisplayModeId = info.activeDisplayModeId;
+ outInfo->renderFrameRate = info.renderFrameRate;
+
+ outInfo->supportedColorModes.clear();
+ outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
+ for (const auto& cmode : info.supportedColorModes) {
+ outInfo->supportedColorModes.push_back(static_cast<int32_t>(cmode));
+ }
+
+ outInfo->activeColorMode = static_cast<int32_t>(info.activeColorMode);
+
+ gui::HdrCapabilities& hdrCapabilities = outInfo->hdrCapabilities;
+ hdrCapabilities.supportedHdrTypes.clear();
+ hdrCapabilities.supportedHdrTypes.reserve(info.hdrCapabilities.getSupportedHdrTypes().size());
+ for (const auto& hdr : info.hdrCapabilities.getSupportedHdrTypes()) {
+ hdrCapabilities.supportedHdrTypes.push_back(static_cast<int32_t>(hdr));
+ }
+ hdrCapabilities.maxLuminance = info.hdrCapabilities.getDesiredMaxLuminance();
+ hdrCapabilities.maxAverageLuminance = info.hdrCapabilities.getDesiredMaxAverageLuminance();
+ hdrCapabilities.minLuminance = info.hdrCapabilities.getDesiredMinLuminance();
+
+ outInfo->autoLowLatencyModeSupported = info.autoLowLatencyModeSupported;
+ outInfo->gameContentTypeSupported = info.gameContentTypeSupported;
+ outInfo->preferredBootDisplayMode = info.preferredBootDisplayMode;
+}
+
+binder::Status SurfaceComposerAIDL::getDynamicDisplayInfoFromToken(
+ const sp<IBinder>& display, gui::DynamicDisplayInfo* outInfo) {
ui::DynamicDisplayInfo info;
- status_t status = mFlinger->getDynamicDisplayInfo(display, &info);
+ status_t status = mFlinger->getDynamicDisplayInfoFromToken(display, &info);
if (status == NO_ERROR) {
- // convert ui::DynamicDisplayInfo to gui::DynamicDisplayInfo
- outInfo->supportedDisplayModes.clear();
- outInfo->supportedDisplayModes.reserve(info.supportedDisplayModes.size());
- for (const auto& mode : info.supportedDisplayModes) {
- gui::DisplayMode outMode;
- outMode.id = mode.id;
- outMode.resolution.width = mode.resolution.width;
- outMode.resolution.height = mode.resolution.height;
- outMode.xDpi = mode.xDpi;
- outMode.yDpi = mode.yDpi;
- outMode.refreshRate = mode.refreshRate;
- outMode.appVsyncOffset = mode.appVsyncOffset;
- outMode.sfVsyncOffset = mode.sfVsyncOffset;
- outMode.presentationDeadline = mode.presentationDeadline;
- outMode.group = mode.group;
- outInfo->supportedDisplayModes.push_back(outMode);
- }
+ getDynamicDisplayInfoInternal(info, outInfo);
+ }
+ return binderStatusFromStatusT(status);
+}
- outInfo->activeDisplayModeId = info.activeDisplayModeId;
-
- outInfo->supportedColorModes.clear();
- outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
- for (const auto& cmode : info.supportedColorModes) {
- outInfo->supportedColorModes.push_back(static_cast<int32_t>(cmode));
- }
-
- outInfo->activeColorMode = static_cast<int32_t>(info.activeColorMode);
-
- gui::HdrCapabilities& hdrCapabilities = outInfo->hdrCapabilities;
- hdrCapabilities.supportedHdrTypes.clear();
- hdrCapabilities.supportedHdrTypes.reserve(
- info.hdrCapabilities.getSupportedHdrTypes().size());
- for (const auto& hdr : info.hdrCapabilities.getSupportedHdrTypes()) {
- hdrCapabilities.supportedHdrTypes.push_back(static_cast<int32_t>(hdr));
- }
- hdrCapabilities.maxLuminance = info.hdrCapabilities.getDesiredMaxLuminance();
- hdrCapabilities.maxAverageLuminance = info.hdrCapabilities.getDesiredMaxAverageLuminance();
- hdrCapabilities.minLuminance = info.hdrCapabilities.getDesiredMinLuminance();
-
- outInfo->autoLowLatencyModeSupported = info.autoLowLatencyModeSupported;
- outInfo->gameContentTypeSupported = info.gameContentTypeSupported;
- outInfo->preferredBootDisplayMode = info.preferredBootDisplayMode;
+binder::Status SurfaceComposerAIDL::getDynamicDisplayInfoFromId(int64_t displayId,
+ gui::DynamicDisplayInfo* outInfo) {
+ ui::DynamicDisplayInfo info;
+ status_t status = mFlinger->getDynamicDisplayInfoFromId(displayId, &info);
+ if (status == NO_ERROR) {
+ getDynamicDisplayInfoInternal(info, outInfo);
}
return binderStatusFromStatusT(status);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e3217a3..7bb0514 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -26,6 +26,7 @@
#include <android/gui/DisplayStatInfo.h>
#include <android/gui/DisplayState.h>
#include <android/gui/ISurfaceComposerClient.h>
+#include <android/gui/ITransactionCompletedListener.h>
#include <cutils/atomic.h>
#include <cutils/compiler.h>
#include <ftl/future.h>
@@ -34,8 +35,8 @@
#include <gui/CompositorTiming.h>
#include <gui/FrameTimestamps.h>
#include <gui/ISurfaceComposer.h>
-#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerDebugInfo.h>
+
#include <gui/LayerState.h>
#include <layerproto/LayerProtoHeader.h>
#include <math/mat4.h>
@@ -96,6 +97,7 @@
#include <unordered_map>
#include <unordered_set>
#include <utility>
+#include <vector>
#include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h>
#include "Client.h"
@@ -125,7 +127,9 @@
using gui::CaptureArgs;
using gui::DisplayCaptureArgs;
using gui::IRegionSamplingListener;
+using gui::ITransactionCompletedListener;
using gui::LayerCaptureArgs;
+
using gui::ScreenCaptureResults;
namespace frametimeline {
@@ -512,10 +516,13 @@
status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats);
status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*)
EXCLUDES(mStateLock);
- status_t getStaticDisplayInfo(const sp<IBinder>& displayToken, ui::StaticDisplayInfo*)
+ status_t getStaticDisplayInfo(int64_t displayId, ui::StaticDisplayInfo*) EXCLUDES(mStateLock);
+ status_t getDynamicDisplayInfoFromId(int64_t displayId, ui::DynamicDisplayInfo*)
EXCLUDES(mStateLock);
- status_t getDynamicDisplayInfo(const sp<IBinder>& displayToken, ui::DynamicDisplayInfo*)
- EXCLUDES(mStateLock);
+ status_t getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+ ui::DynamicDisplayInfo*) EXCLUDES(mStateLock);
+ void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*&, const sp<DisplayDevice>&,
+ const display::DisplaySnapshot&);
status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken, ui::DisplayPrimaries&);
status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode);
status_t getBootDisplayModeSupport(bool* outSupport) const;
@@ -527,7 +534,7 @@
void setPowerMode(const sp<IBinder>& displayToken, int mode);
status_t overrideHdrTypes(const sp<IBinder>& displayToken,
const std::vector<ui::Hdr>& hdrTypes);
- status_t onPullAtom(const int32_t atomId, std::string* pulledData, bool* success);
+ status_t onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData, bool* success);
status_t getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers);
status_t getColorManagement(bool* outGetColorManagement) const;
status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
@@ -639,8 +646,11 @@
bool mKernelIdleTimerEnabled = false;
// Show spinner with refresh rate overlay
bool mRefreshRateOverlaySpinner = false;
+ // Show render rate with refresh rate overlay
+ bool mRefreshRateOverlayRenderRate = false;
- void setDesiredActiveMode(display::DisplayModeRequest&&) REQUIRES(mStateLock);
+ void setDesiredActiveMode(display::DisplayModeRequest&&, bool force = false)
+ REQUIRES(mStateLock);
status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId);
// Sets the active mode and a new refresh rate in SF.
@@ -669,7 +679,8 @@
// TODO(b/241285191): Look up RefreshRateSelector on Scheduler to remove redundant parameter.
status_t applyRefreshRateSelectorPolicy(PhysicalDisplayId,
- const scheduler::RefreshRateSelector&)
+ const scheduler::RefreshRateSelector&,
+ bool force = false)
REQUIRES(mStateLock, kMainThreadContext);
void commitTransactions() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
@@ -700,7 +711,7 @@
*/
bool applyTransactionState(const FrameTimelineInfo& info,
std::vector<ResolvedComposerState>& state,
- const Vector<DisplayState>& displays, uint32_t flags,
+ Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
const client_cache_t& uncacheBuffer, const int64_t postTime,
@@ -1000,7 +1011,10 @@
VirtualDisplayId acquireVirtualDisplay(ui::Size, ui::PixelFormat) REQUIRES(mStateLock);
void releaseVirtualDisplay(VirtualDisplayId);
- void onActiveDisplayChangedLocked(const sp<DisplayDevice>& activeDisplay)
+ // TODO(b/255635821): Replace pointers with references. `inactiveDisplay` is only ever `nullptr`
+ // in tests, and `activeDisplay` must not be `nullptr` as a precondition.
+ void onActiveDisplayChangedLocked(const sp<DisplayDevice>& inactiveDisplay,
+ const sp<DisplayDevice>& activeDisplay)
REQUIRES(mStateLock, kMainThreadContext);
void onActiveDisplaySizeChanged(const sp<const DisplayDevice>&);
@@ -1089,6 +1103,10 @@
std::atomic<uint32_t> mUniqueTransactionId = 1;
SortedVector<sp<Layer>> mLayersPendingRemoval;
+ // Buffers that have been discarded by clients and need to be evicted from per-layer caches so
+ // the graphics memory can be immediately freed.
+ std::vector<uint64_t> mBufferIdsToUncache;
+
// global color transform states
Daltonizer mDaltonizer;
float mGlobalSaturationFactor = 1.0f;
@@ -1171,7 +1189,9 @@
display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
// The inner or outer display for foldables, assuming they have mutually exclusive power states.
- PhysicalDisplayId mActiveDisplayId GUARDED_BY(mStateLock);
+ // Atomic because writes from onActiveDisplayChangedLocked are not always under mStateLock, but
+ // reads from ISchedulerCallback::requestDisplayModes may happen concurrently.
+ std::atomic<PhysicalDisplayId> mActiveDisplayId GUARDED_BY(mStateLock);
struct {
DisplayIdGenerator<GpuVirtualDisplayId> gpu;
@@ -1201,8 +1221,6 @@
// If blurs should be enabled on this device.
bool mSupportsBlur = false;
- // If blurs are considered expensive and should require high GPU frequency.
- bool mBlursAreExpensive = false;
std::atomic<uint32_t> mFrameMissedCount = 0;
std::atomic<uint32_t> mHwcFrameMissedCount = 0;
std::atomic<uint32_t> mGpuFrameMissedCount = 0;
@@ -1396,10 +1414,12 @@
gui::DisplayStatInfo* outStatInfo) override;
binder::Status getDisplayState(const sp<IBinder>& display,
gui::DisplayState* outState) override;
- binder::Status getStaticDisplayInfo(const sp<IBinder>& display,
+ binder::Status getStaticDisplayInfo(int64_t displayId,
gui::StaticDisplayInfo* outInfo) override;
- binder::Status getDynamicDisplayInfo(const sp<IBinder>& display,
- gui::DynamicDisplayInfo* outInfo) override;
+ binder::Status getDynamicDisplayInfoFromId(int64_t displayId,
+ gui::DynamicDisplayInfo* outInfo) override;
+ binder::Status getDynamicDisplayInfoFromToken(const sp<IBinder>& display,
+ gui::DynamicDisplayInfo* outInfo) override;
binder::Status getDisplayNativePrimaries(const sp<IBinder>& display,
gui::DisplayPrimaries* outPrimaries) override;
binder::Status setActiveColorMode(const sp<IBinder>& display, int colorMode) override;
@@ -1484,6 +1504,8 @@
status_t checkAccessPermission(bool usePermissionCache = kUsePermissionCache);
status_t checkControlDisplayBrightnessPermission();
status_t checkReadFrameBufferPermission();
+ static void getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& info,
+ gui::DynamicDisplayInfo*& outInfo);
private:
sp<SurfaceFlinger> mFlinger;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index e860d88..630cef1 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -27,6 +27,7 @@
#include <algorithm>
#include <chrono>
+#include <cmath>
#include <unordered_map>
#include "TimeStats.h"
@@ -90,7 +91,7 @@
}
} // namespace
-bool TimeStats::populateGlobalAtom(std::string* pulledData) {
+bool TimeStats::populateGlobalAtom(std::vector<uint8_t>* pulledData) {
std::lock_guard<std::mutex> lock(mMutex);
if (mTimeStats.statsStartLegacy == 0) {
@@ -138,10 +139,11 @@
// Always clear data.
clearGlobalLocked();
- return atomList.SerializeToString(pulledData);
+ pulledData->resize(atomList.ByteSizeLong());
+ return atomList.SerializeToArray(pulledData->data(), atomList.ByteSizeLong());
}
-bool TimeStats::populateLayerAtom(std::string* pulledData) {
+bool TimeStats::populateLayerAtom(std::vector<uint8_t>* pulledData) {
std::lock_guard<std::mutex> lock(mMutex);
std::vector<TimeStatsHelper::TimeStatsLayer*> dumpStats;
@@ -179,6 +181,12 @@
*atom->mutable_present_to_present() =
histogramToProto(present2PresentHist->second.hist, mMaxPulledHistogramBuckets);
}
+ const auto& present2PresentDeltaHist = layer->deltas.find("present2presentDelta");
+ if (present2PresentDeltaHist != layer->deltas.cend()) {
+ *atom->mutable_present_to_present_delta() =
+ histogramToProto(present2PresentDeltaHist->second.hist,
+ mMaxPulledHistogramBuckets);
+ }
const auto& post2presentHist = layer->deltas.find("post2present");
if (post2presentHist != layer->deltas.cend()) {
*atom->mutable_post_to_present() =
@@ -229,7 +237,8 @@
// Always clear data.
clearLayersLocked();
- return atomList.SerializeToString(pulledData);
+ pulledData->resize(atomList.ByteSizeLong());
+ return atomList.SerializeToArray(pulledData->data(), atomList.ByteSizeLong());
}
TimeStats::TimeStats() : TimeStats(std::nullopt, std::nullopt) {}
@@ -245,7 +254,7 @@
}
}
-bool TimeStats::onPullAtom(const int atomId, std::string* pulledData) {
+bool TimeStats::onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) {
bool success = false;
if (atomId == 10062) { // SURFACEFLINGER_STATS_GLOBAL_INFO
success = populateGlobalAtom(pulledData);
@@ -450,6 +459,7 @@
LayerRecord& layerRecord = mTimeStatsTracker[layerId];
TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
+ std::optional<int32_t>& prevPresentToPresentMs = layerRecord.prevPresentToPresentMs;
std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
const int32_t refreshRateBucket =
clampToNearestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
@@ -527,6 +537,12 @@
ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerId,
timeRecords[0].frameTime.frameNumber, presentToPresentMs);
timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
+ if (prevPresentToPresentMs) {
+ const int32_t presentToPresentDeltaMs =
+ std::abs(presentToPresentMs - *prevPresentToPresentMs);
+ timeStatsLayer.deltas["present2presentDelta"].insert(presentToPresentDeltaMs);
+ }
+ prevPresentToPresentMs = presentToPresentMs;
}
prevTimeRecord = timeRecords[0];
timeRecords.pop_front();
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 61d7c22..5f58657 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -47,7 +47,7 @@
virtual ~TimeStats() = default;
// Process a pull request from statsd.
- virtual bool onPullAtom(const int atomId, std::string* pulledData) = 0;
+ virtual bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) = 0;
virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
virtual bool isEnabled() = 0;
@@ -219,6 +219,7 @@
uint32_t lateAcquireFrames = 0;
uint32_t badDesiredPresentFrames = 0;
TimeRecord prevTimeRecord;
+ std::optional<int32_t> prevPresentToPresentMs;
std::deque<TimeRecord> timeRecords;
};
@@ -244,7 +245,7 @@
TimeStats(std::optional<size_t> maxPulledLayers,
std::optional<size_t> maxPulledHistogramBuckets);
- bool onPullAtom(const int atomId, std::string* pulledData) override;
+ bool onPullAtom(const int atomId, std::vector<uint8_t>* pulledData) override;
void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
bool isEnabled() override;
std::string miniDump() override;
@@ -292,8 +293,8 @@
static const size_t MAX_NUM_TIME_RECORDS = 64;
private:
- bool populateGlobalAtom(std::string* pulledData);
- bool populateLayerAtom(std::string* pulledData);
+ bool populateGlobalAtom(std::vector<uint8_t>* pulledData);
+ bool populateLayerAtom(std::vector<uint8_t>* pulledData);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
std::optional<Fps> renderRate, SetFrameRateVote,
diff --git a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
index d4d444e..8615947 100644
--- a/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
+++ b/services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto
@@ -289,7 +289,11 @@
// Introduced in Android 12.
optional FrameTimingHistogram app_deadline_misses = 25;
- // Next ID: 27
+ // Variability histogram of present_to_present timings.
+ // Introduced in Android 14.
+ optional FrameTimingHistogram present_to_present_delta = 27;
+
+ // Next ID: 28
}
/**
diff --git a/services/surfaceflinger/Tracing/tools/Android.bp b/services/surfaceflinger/Tracing/tools/Android.bp
index e8fe734..b6435a8 100644
--- a/services/surfaceflinger/Tracing/tools/Android.bp
+++ b/services/surfaceflinger/Tracing/tools/Android.bp
@@ -25,8 +25,8 @@
name: "layertracegenerator",
defaults: [
"libsurfaceflinger_mocks_defaults",
+ "librenderengine_deps",
"surfaceflinger_defaults",
- "skia_renderengine_deps",
],
srcs: [
":libsurfaceflinger_sources",
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 61ff9bc..c09bcce 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -26,14 +26,27 @@
#include <unordered_set>
#include <android-base/thread_annotations.h>
+#include <android/gui/ITransactionCompletedListener.h>
+
#include <binder/IBinder.h>
-#include <ftl/future.h>
-#include <gui/ITransactionCompletedListener.h>
+#include <gui/ListenerStats.h>
+#include <gui/ReleaseCallbackId.h>
+#include <renderengine/RenderEngine.h>
#include <ui/Fence.h>
#include <ui/FenceResult.h>
namespace android {
+using gui::CallbackId;
+using gui::FrameEventHistoryStats;
+using gui::IListenerHash;
+using gui::ITransactionCompletedListener;
+using gui::JankData;
+using gui::ListenerCallbacks;
+using gui::ListenerStats;
+using gui::ReleaseCallbackId;
+using gui::TransactionStats;
+
class CallbackHandle : public RefBase {
public:
CallbackHandle(const sp<IBinder>& transactionListener, const std::vector<CallbackId>& ids,
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 7bde2c1..380301f 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -27,13 +27,12 @@
namespace android {
-// Extends the client side composer state by resolving buffer cache ids.
+// Extends the client side composer state by resolving buffer.
class ResolvedComposerState : public ComposerState {
public:
ResolvedComposerState() = default;
ResolvedComposerState(ComposerState&& source) { state = std::move(source.state); }
std::shared_ptr<renderengine::ExternalTexture> externalTexture;
- int hwcBufferSlot = 0;
};
struct TransactionState {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index dbfce78..81ca659 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -81,7 +81,6 @@
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-using types::V1_2::Hdr;
using types::V1_2::PixelFormat;
using V2_1::Config;
@@ -420,7 +419,7 @@
void onPullAtom(FuzzedDataProvider *fdp) {
const int32_t atomId = fdp->ConsumeIntegral<uint8_t>();
- std::string pulledData = fdp->ConsumeRandomLengthString().c_str();
+ std::vector<uint8_t> pulledData = fdp->ConsumeRemainingBytes<uint8_t>();
bool success = fdp->ConsumeBool();
mFlinger->onPullAtom(atomId, &pulledData, &success);
}
@@ -492,14 +491,14 @@
mFlinger->getDisplayState(display, &displayState);
}
- void getStaticDisplayInfo(sp<IBinder> &display) {
+ void getStaticDisplayInfo(int64_t displayId) {
ui::StaticDisplayInfo staticDisplayInfo;
- mFlinger->getStaticDisplayInfo(display, &staticDisplayInfo);
+ mFlinger->getStaticDisplayInfo(displayId, &staticDisplayInfo);
}
- void getDynamicDisplayInfo(sp<IBinder> &display) {
+ void getDynamicDisplayInfo(int64_t displayId) {
android::ui::DynamicDisplayInfo dynamicDisplayInfo;
- mFlinger->getDynamicDisplayInfo(display, &dynamicDisplayInfo);
+ mFlinger->getDynamicDisplayInfoFromId(displayId, &dynamicDisplayInfo);
}
void getDisplayNativePrimaries(sp<IBinder> &display) {
android::ui::DisplayPrimaries displayPrimaries;
@@ -523,7 +522,7 @@
return ids.front();
}
- sp<IBinder> fuzzBoot(FuzzedDataProvider *fdp) {
+ std::pair<sp<IBinder>, int64_t> fuzzBoot(FuzzedDataProvider *fdp) {
mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(fdp->ConsumeBool());
const sp<Client> client = sp<Client>::make(mFlinger);
@@ -550,13 +549,13 @@
mFlinger->bootFinished();
- return display;
+ return {display, physicalDisplayId.value};
}
void fuzzSurfaceFlinger(const uint8_t *data, size_t size) {
FuzzedDataProvider mFdp(data, size);
- sp<IBinder> display = fuzzBoot(&mFdp);
+ auto [display, displayId] = fuzzBoot(&mFdp);
sp<IGraphicBufferProducer> bufferProducer = sp<mock::GraphicBufferProducer>::make();
@@ -564,8 +563,8 @@
getDisplayStats(display);
getDisplayState(display);
- getStaticDisplayInfo(display);
- getDynamicDisplayInfo(display);
+ getStaticDisplayInfo(displayId);
+ getDynamicDisplayInfo(displayId);
getDisplayNativePrimaries(display);
mFlinger->setAutoLowLatencyMode(display, mFdp.ConsumeBool());
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index c5b3fa6..acfc1d4 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -160,7 +160,7 @@
layer->setBuffer(texture, {} /*bufferData*/, mFdp.ConsumeIntegral<nsecs_t>() /*postTime*/,
mFdp.ConsumeIntegral<nsecs_t>() /*desiredTime*/,
mFdp.ConsumeBool() /*isAutoTimestamp*/,
- {mFdp.ConsumeIntegral<nsecs_t>()} /*dequeue*/, {} /*info*/, 0 /* hwcslot */);
+ {mFdp.ConsumeIntegral<nsecs_t>()} /*dequeue*/, {} /*info*/);
LayerRenderArea layerArea(*(flinger.flinger()), layer, getFuzzedRect(),
{mFdp.ConsumeIntegral<int32_t>(),
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 1676844..4a45eb5 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -83,6 +83,15 @@
return SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
}
+ static std::optional<uint64_t> getFirstDisplayId() {
+ const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ if (ids.empty()) {
+ return std::nullopt;
+ }
+
+ return ids.front().value;
+ }
+
void setupBackgroundSurface() {
mDisplay = getFirstDisplayToken();
ASSERT_FALSE(mDisplay == nullptr);
@@ -169,29 +178,25 @@
TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) {
std::function<bool()> condition = [] { return getFirstDisplayToken() != nullptr; };
// Anyone can access display information.
- ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, true));
+ ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
}
TEST_F(CredentialsTest, AllowedGetterMethodsTest) {
// The following methods are tested with a UID that is not root, graphics,
// or system, to show that anyone can access them.
UIDFaker f(AID_BIN);
- const auto display = getFirstDisplayToken();
- ASSERT_TRUE(display != nullptr);
-
- ui::DisplayMode mode;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
-
- Vector<ui::DisplayMode> modes;
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
ui::DynamicDisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info));
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info));
}
TEST_F(CredentialsTest, GetDynamicDisplayInfoTest) {
- const auto display = getFirstDisplayToken();
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
std::function<status_t()> condition = [=]() {
ui::DynamicDisplayInfo info;
- return SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+ return SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
}
@@ -335,8 +340,10 @@
status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result);
ASSERT_EQ(NO_ERROR, error);
bool hasWideColorMode = false;
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
ui::DynamicDisplayInfo info;
- SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
const auto& colorModes = info.supportedColorModes;
for (ColorMode colorMode : colorModes) {
switch (colorMode) {
@@ -363,10 +370,10 @@
}
TEST_F(CredentialsTest, GetActiveColorModeBasicCorrectness) {
- const auto display = getFirstDisplayToken();
- ASSERT_FALSE(display == nullptr);
+ const auto id = getFirstDisplayId();
+ ASSERT_TRUE(id);
ui::DynamicDisplayInfo info;
- SurfaceComposerClient::getDynamicDisplayInfo(display, &info);
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(*id, &info);
ColorMode colorMode = info.activeColorMode;
ASSERT_NE(static_cast<ColorMode>(BAD_VALUE), colorMode);
}
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 10dae46..4be961b 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -45,6 +45,7 @@
void SetUp() override {
const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
ASSERT_FALSE(ids.empty());
+ mDisplayId = ids.front().value;
mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
status_t res = SurfaceComposerClient::getDesiredDisplayModeSpecs(mDisplayToken, &mSpecs);
ASSERT_EQ(res, NO_ERROR);
@@ -58,11 +59,14 @@
void testSetAllowGroupSwitching(bool allowGroupSwitching);
sp<IBinder> mDisplayToken;
+ uint64_t mDisplayId;
};
TEST_F(RefreshRateRangeTest, setAllConfigs) {
ui::DynamicDisplayInfo info;
- status_t res = SurfaceComposerClient::getDynamicDisplayInfo(mDisplayToken, &info);
+ status_t res =
+ SurfaceComposerClient::getDynamicDisplayInfoFromId(static_cast<int64_t>(mDisplayId),
+ &info);
const auto& modes = info.supportedDisplayModes;
ASSERT_EQ(res, NO_ERROR);
ASSERT_GT(modes.size(), 0);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 0114577..6d1b3fe 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -70,7 +70,6 @@
":libsurfaceflinger_sources",
"libsurfaceflinger_unittest_main.cpp",
"AidlPowerHalWrapperTest.cpp",
- "CachingTest.cpp",
"CompositionTest.cpp",
"DispSyncSourceTest.cpp",
"DisplayIdGeneratorTest.cpp",
@@ -93,6 +92,7 @@
"LayerHistoryTest.cpp",
"LayerInfoTest.cpp",
"LayerMetadataTest.cpp",
+ "LayerHierarchyTest.cpp",
"LayerLifecycleManagerTest.cpp",
"LayerTest.cpp",
"LayerTestUtils.cpp",
@@ -111,6 +111,7 @@
"SurfaceFlinger_SetPowerModeInternalTest.cpp",
"SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
"SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp",
+ "SurfaceFlinger_ExcludeDolbyVisionTest.cpp",
"SchedulerTest.cpp",
"SetFrameRateTest.cpp",
"RefreshRateSelectorTest.cpp",
@@ -139,6 +140,7 @@
defaults: [
"android.hardware.graphics.common-ndk_static",
"android.hardware.graphics.composer3-ndk_static",
+ "librenderengine_deps",
],
static_libs: [
"android.hardware.common-V2-ndk",
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
deleted file mode 100644
index c1cbbfb..0000000
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "CachingTest"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/BufferQueue.h>
-
-#include "HwcSlotGenerator.h"
-
-namespace android {
-
-class SlotGenerationTest : public testing::Test {
-protected:
- sp<HwcSlotGenerator> mHwcSlotGenerator = sp<HwcSlotGenerator>::make();
- sp<GraphicBuffer> mBuffer1 =
- sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
- sp<GraphicBuffer> mBuffer2 =
- sp<GraphicBuffer>::make(1u, 1u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
- sp<GraphicBuffer> mBuffer3 =
- sp<GraphicBuffer>::make(10u, 10u, HAL_PIXEL_FORMAT_RGBA_8888, 1u, 0u);
-};
-
-TEST_F(SlotGenerationTest, getHwcCacheSlot_Invalid) {
- sp<IBinder> binder = sp<BBinder>::make();
- // test getting invalid client_cache_id
- client_cache_t id;
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::INVALID_BUFFER_SLOT, slot);
-}
-
-TEST_F(SlotGenerationTest, getHwcCacheSlot_Basic) {
- sp<IBinder> binder = sp<BBinder>::make();
- client_cache_t id;
- id.token = binder;
- id.id = 0;
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
-
- client_cache_t idB;
- idB.token = binder;
- idB.id = 1;
- slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
-
- slot = mHwcSlotGenerator->getHwcCacheSlot(idB);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 2, slot);
-
- slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - 1, slot);
-}
-
-TEST_F(SlotGenerationTest, getHwcCacheSlot_Reuse) {
- sp<IBinder> binder = sp<BBinder>::make();
- std::vector<client_cache_t> ids;
- uint32_t cacheId = 0;
- // fill up cache
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- client_cache_t id;
- id.token = binder;
- id.id = cacheId;
- ids.push_back(id);
-
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
- cacheId++;
- }
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- int slot = mHwcSlotGenerator->getHwcCacheSlot(ids[static_cast<uint32_t>(i)]);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
- }
-
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- client_cache_t id;
- id.token = binder;
- id.id = cacheId;
- int slot = mHwcSlotGenerator->getHwcCacheSlot(id);
- EXPECT_EQ(BufferQueue::NUM_BUFFER_SLOTS - (i + 1), slot);
- cacheId++;
- }
-}
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 19c7d5c..d58e644 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -359,6 +359,7 @@
}
// Called by tests to inject a HWC display setup
+ template <bool kInitPowerMode = true>
static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
const auto displayId = DisplayVariant::DISPLAY_ID::get();
ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
@@ -367,18 +368,21 @@
.setHwcDisplayId(HWC_DISPLAY_ID)
.setResolution(DisplayVariant::RESOLUTION)
.setActiveConfig(HWC_ACTIVE_CONFIG_ID)
- .setPowerMode(INIT_POWER_MODE)
+ .setPowerMode(kInitPowerMode ? std::make_optional(INIT_POWER_MODE) : std::nullopt)
.inject(&test->mFlinger, test->mComposer);
}
// Called by tests to inject a HWC display setup
+ template <bool kInitPowerMode = true>
static void injectHwcDisplay(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
.WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
- .WillOnce(Return(Error::NONE));
- injectHwcDisplayWithNoDefaultCapabilities(test);
+ if constexpr (kInitPowerMode) {
+ EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
+ .WillOnce(Return(Error::NONE));
+ }
+ injectHwcDisplayWithNoDefaultCapabilities<kInitPowerMode>(test);
}
static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index a5beaba..dd87f9d 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -633,9 +633,10 @@
.setId(DisplayModeId(7))
.setVsyncPeriod(16666666)
.build();
+ const Fps fps = mode->getFps() / 2;
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, 16666666);
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, fps.getPeriodNsecs());
}
TEST_F(EventThreadTest, postConfigChangedExternal) {
@@ -644,9 +645,10 @@
.setId(DisplayModeId(5))
.setVsyncPeriod(16666666)
.build();
+ const Fps fps = mode->getFps() / 2;
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, 16666666);
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, fps.getPeriodNsecs());
}
TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
@@ -655,8 +657,9 @@
.setId(DisplayModeId(7))
.setVsyncPeriod(16666666)
.build();
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666);
+ const Fps fps = mode->getFps() / 2;
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, fps.getPeriodNsecs());
}
TEST_F(EventThreadTest, suppressConfigChanged) {
@@ -669,9 +672,10 @@
.setId(DisplayModeId(9))
.setVsyncPeriod(16666666)
.build();
+ const Fps fps = mode->getFps() / 2;
- mThread->onModeChanged(mode);
- expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666);
+ mThread->onModeChanged({fps, ftl::as_non_null(mode)});
+ expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, fps.getPeriodNsecs());
auto args = suppressConnectionEventRecorder.waitForCall();
ASSERT_FALSE(args.has_value());
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
new file mode 100644
index 0000000..8560902
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -0,0 +1,728 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "FrontEnd/LayerHandle.h"
+#include "FrontEnd/LayerHierarchy.h"
+#include "FrontEnd/LayerLifecycleManager.h"
+#include "Layer.h"
+#include "gui/SurfaceComposerClient.h"
+
+#define UPDATE_AND_VERIFY(HIERARCHY) \
+ ({ \
+ SCOPED_TRACE(""); \
+ updateAndVerify((HIERARCHY)); \
+ })
+
+namespace android::surfaceflinger::frontend {
+
+namespace {
+LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, wp<IBinder> mirror) {
+ LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id));
+ args.addToRoot = canBeRoot;
+ args.parentHandle = parent;
+ args.mirrorLayerHandle = mirror;
+ return args;
+}
+} // namespace
+
+// To run test:
+/**
+ mp :libsurfaceflinger_unittest && adb sync; adb shell \
+ /data/nativetest/libsurfaceflinger_unittest/libsurfaceflinger_unittest \
+ --gtest_filter="LayerHierarchyTest.*" --gtest_repeat=100 \
+ --gtest_shuffle \
+ --gtest_brief=1
+*/
+
+class LayerHierarchyTest : public testing::Test {
+protected:
+ LayerHierarchyTest() {
+ // tree with 3 levels of children
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ ├── 121
+ // │ │ └── 122
+ // │ │ └── 1221
+ // │ └── 13
+ // └── 2
+
+ createRootLayer(1);
+ createRootLayer(2);
+ createLayer(11, 1);
+ createLayer(12, 1);
+ createLayer(13, 1);
+ createLayer(111, 11);
+ createLayer(121, 12);
+ createLayer(122, 12);
+ createLayer(1221, 122);
+ mLifecycleManager.commitChanges();
+ }
+ std::vector<uint32_t> getTraversalPath(const LayerHierarchy& hierarchy) const {
+ std::vector<uint32_t> layerIds;
+ hierarchy.traverse([&layerIds = layerIds](const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath&) -> bool {
+ layerIds.emplace_back(hierarchy.getLayer()->id);
+ return true;
+ });
+ return layerIds;
+ }
+
+ std::vector<uint32_t> getTraversalPathInZOrder(const LayerHierarchy& hierarchy) const {
+ std::vector<uint32_t> layerIds;
+ hierarchy.traverseInZOrder(
+ [&layerIds = layerIds](const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath&) -> bool {
+ layerIds.emplace_back(hierarchy.getLayer()->id);
+ return true;
+ });
+ return layerIds;
+ }
+
+ void createRootLayer(uint32_t id) {
+ sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+ mHandles[id] = handle;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr)));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void createLayer(uint32_t id, uint32_t parentId) {
+ sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+ mHandles[id] = handle;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/mHandles[parentId],
+ /*mirror=*/nullptr)));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void reparentLayer(uint32_t id, uint32_t newParentId) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ if (newParentId == UNASSIGNED_LAYER_ID) {
+ transactions.back().states.front().state.parentSurfaceControlForChild = nullptr;
+ } else {
+ auto parentHandle = mHandles[newParentId];
+ transactions.back().states.front().state.parentSurfaceControlForChild =
+ sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle,
+ static_cast<int32_t>(newParentId), "Test");
+ }
+ transactions.back().states.front().state.what = layer_state_t::eReparent;
+ transactions.back().states.front().state.surface = mHandles[id];
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ if (relativeParentId == UNASSIGNED_LAYER_ID) {
+ transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+ } else {
+ auto parentHandle = mHandles[relativeParentId];
+ transactions.back().states.front().state.relativeLayerSurfaceControl =
+ sp<SurfaceControl>::make(SurfaceComposerClient::getDefault(), parentHandle,
+ static_cast<int32_t>(relativeParentId), "test");
+ transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged;
+ }
+ transactions.back().states.front().state.surface = mHandles[id];
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void mirrorLayer(uint32_t id, uint32_t parent, uint32_t layerToMirror) {
+ auto parentHandle = (parent == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[parent];
+ auto mirrorHandle =
+ (layerToMirror == UNASSIGNED_LAYER_ID) ? nullptr : mHandles[layerToMirror];
+
+ sp<LayerHandle> handle = sp<LayerHandle>::make(id);
+ mHandles[id] = handle;
+ std::vector<std::unique_ptr<RequestedLayerState>> layers;
+ layers.emplace_back(std::make_unique<RequestedLayerState>(
+ createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentHandle,
+ /*mirror=*/mHandles[layerToMirror])));
+ mLifecycleManager.addLayers(std::move(layers));
+ }
+
+ void updateBackgroundColor(uint32_t id, half alpha) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged;
+ transactions.back().states.front().state.bgColorAlpha = alpha;
+ transactions.back().states.front().state.surface = mHandles[id];
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
+ void destroyLayerHandle(uint32_t id) { mLifecycleManager.onHandlesDestroyed({id}); }
+
+ void updateAndVerify(LayerHierarchyBuilder& hierarchyBuilder) {
+ if (mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)) {
+ hierarchyBuilder.update(mLifecycleManager.getLayers(),
+ mLifecycleManager.getDestroyedLayers());
+ }
+ mLifecycleManager.commitChanges();
+
+ // rebuild layer hierarchy from scratch and verify that it matches the updated state.
+ LayerHierarchyBuilder newBuilder(mLifecycleManager.getLayers());
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()),
+ getTraversalPath(newBuilder.getHierarchy()));
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()),
+ getTraversalPathInZOrder(newBuilder.getHierarchy()));
+ EXPECT_FALSE(
+ mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy));
+ }
+
+ void setZ(uint32_t id, int32_t z) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eLayerChanged;
+ transactions.back().states.front().state.layerId = static_cast<int32_t>(id);
+ transactions.back().states.front().state.z = z;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+ LayerLifecycleManager mLifecycleManager;
+ std::unordered_map<uint32_t, sp<LayerHandle>> mHandles;
+};
+
+// reparenting tests
+TEST_F(LayerHierarchyTest, addLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ createRootLayer(3);
+ createLayer(112, 11);
+ createLayer(12211, 1221);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ expectedTraversalPath = {1, 11, 111, 112, 12, 121, 122, 1221, 12211, 13, 2, 3};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(2, 11);
+ reparentLayer(111, 12);
+ reparentLayer(1221, 1);
+ reparentLayer(1221, 13);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 12, 111, 121, 122, 13, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayerToNull) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ reparentLayer(1221, 13);
+ reparentLayer(1221, UNASSIGNED_LAYER_ID);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {2, 11, 111, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentLayerToNullAndDestroyHandles) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ reparentLayer(1221, UNASSIGNED_LAYER_ID);
+
+ destroyLayerHandle(2);
+ destroyLayerHandle(11);
+ destroyLayerHandle(1221);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, destroyHandleThenDestroyParentLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ destroyLayerHandle(111);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ // handle is destroyed but layer is kept alive and reachable by parent
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // destroy parent layer and the child gets destroyed
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ destroyLayerHandle(11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, layerSurvivesTemporaryReparentToNull) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ reparentLayer(11, 1);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// offscreen tests
+TEST_F(LayerHierarchyTest, layerMovesOnscreen) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentLayer(11, 1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, addLayerToOffscreenParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ createLayer(112, 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111, 112};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// rel-z tests
+TEST_F(LayerHierarchyTest, setRelativeParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentFromRelativeParentWithSetLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentRelativeLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, reparentToRelativeParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, setParentAsRelativeParent) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, relativeChildMovesOffscreenIsNotTraversable) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(11, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {2, 11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// mirror tests
+TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122,
+ 1221, 13, 14, 11, 111, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, canMirrorOffscreenLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 12, 121, 122, 1221, 13, 14, 11, 111, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, newChildLayerIsUpdatedInMirrorHierarchy) {
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ createLayer(1111, 111);
+ createLayer(112, 11);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 1111, 112, 12, 121, 122,
+ 1221, 13, 14, 11, 111, 1111, 112, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// mirror & relatives tests
+TEST_F(LayerHierarchyTest, mirrorWithRelativeOutsideMirrorHierarchy) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(111, 12);
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ ├── 121
+ // │ │ ├── 122
+ // │ │ │ └── 1221
+ // │ │ └ - 111 (relative)
+ // │ ├── 13
+ // │ └── 14
+ // │ └ * 11 (mirroring)
+ // └── 2
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 111, 121, 122,
+ 1221, 13, 14, 11, 111, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ // 111 is not reachable in the mirror
+ expectedTraversalPath = {1, 11, 12, 111, 121, 122, 1221, 13, 14, 11, 2};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, mirrorWithRelativeInsideMirrorHierarchy) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(1221, 12);
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 12);
+
+ // ROOT
+ // ├── 1
+ // │ ├── 11
+ // │ │ └── 111
+ // │ ├── 12
+ // │ │ ├── 121
+ // │ │ ├── 122
+ // │ │ │ └── 1221
+ // │ │ └ - 1221 (relative)
+ // │ ├── 13
+ // │ └── 14
+ // │ └ * 12 (mirroring)
+ // └── 2
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 1221,
+ 13, 14, 12, 121, 122, 1221, 1221, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ // relative layer 1221 is traversable in the mirrored hierarchy as well
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 14, 12, 121, 122, 1221, 2};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, childMovesOffscreenWhenRelativeParentDies) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ reparentRelativeLayer(11, 2);
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ destroyLayerHandle(2);
+
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // remove relative parent so layer becomes onscreen again
+ reparentRelativeLayer(11, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, offscreenLayerCannotBeRelativeToOnscreenLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentRelativeLayer(1221, 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ // verify relz path
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // offscreen layer cannot be reached as a relative child
+ reparentLayer(12, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 11, 111, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {12, 121, 122, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // layer when onscreen can be reached as a relative child again
+ reparentLayer(12, 1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 13, 2, 1221};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, backgroundLayersAreBehindParentLayer) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ updateBackgroundColor(1, 0.5);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 1222, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1222, 1, 11, 111, 12, 121, 122, 1221, 13, 2};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// cycle tests
+TEST_F(LayerHierarchyTest, ParentBecomesTheChild) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ createRootLayer(1);
+ createLayer(11, 1);
+ reparentLayer(1, 11);
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ std::vector<uint32_t> expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, RelativeLoops) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ createRootLayer(1);
+ createRootLayer(2);
+ createLayer(11, 1);
+ reparentRelativeLayer(11, 2);
+ reparentRelativeLayer(2, 11);
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ // fix loop
+ uint32_t invalidRelativeRoot;
+ bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot);
+ EXPECT_TRUE(hasRelZLoop);
+ mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
+ hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers());
+ EXPECT_EQ(invalidRelativeRoot, 11u);
+ EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot));
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 2, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, IndirectRelativeLoops) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ createRootLayer(1);
+ createRootLayer(2);
+ createLayer(11, 1);
+ createLayer(111, 11);
+ createLayer(21, 2);
+ createLayer(22, 2);
+ createLayer(221, 22);
+ reparentRelativeLayer(22, 111);
+ reparentRelativeLayer(11, 221);
+ mLifecycleManager.commitChanges();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+
+ // fix loop
+ uint32_t invalidRelativeRoot;
+ bool hasRelZLoop = hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot);
+ EXPECT_TRUE(hasRelZLoop);
+ mLifecycleManager.fixRelativeZLoop(invalidRelativeRoot);
+ hierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers());
+ EXPECT_FALSE(hierarchyBuilder.getHierarchy().hasRelZLoop(invalidRelativeRoot));
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 22, 221, 2, 21, 22, 221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 2, 21};
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {11, 111, 22, 221};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, ReparentRootLayerToNull) {
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ reparentLayer(1, UNASSIGNED_LAYER_ID);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+TEST_F(LayerHierarchyTest, AddRemoveLayerInSameTransaction) {
+ // remove default hierarchy
+ mLifecycleManager = LayerLifecycleManager();
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ createRootLayer(1);
+ destroyLayerHandle(1);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
+// traversal path test
+TEST_F(LayerHierarchyTest, traversalPathId) {
+ setZ(122, -1);
+ LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers());
+ auto checkTraversalPathIdVisitor =
+ [](const LayerHierarchy& hierarchy,
+ const LayerHierarchy::TraversalPath& traversalPath) -> bool {
+ EXPECT_EQ(hierarchy.getLayer()->id, traversalPath.id);
+ return true;
+ };
+ hierarchyBuilder.getHierarchy().traverse(checkTraversalPathIdVisitor);
+ hierarchyBuilder.getHierarchy().traverseInZOrder(checkTraversalPathIdVisitor);
+}
+
+} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index b81693a..ad3bd35 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#include "mock/MockDisplayModeSpecs.h"
-#include "mock/MockEventThread.h"
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
#include "DisplayTransactionTestHelpers.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockDisplayModeSpecs.h"
#include <ftl/fake_guard.h>
#include <scheduler/Fps.h>
@@ -42,8 +42,7 @@
PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
- DisplayModes modes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
- auto selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60);
+ auto selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(kModes, kModeId60);
setupScheduler(selectorPtr);
@@ -51,7 +50,7 @@
mFlinger.configureAndCommit();
mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
- .setDisplayModes(std::move(modes), kModeId60, std::move(selectorPtr))
+ .setDisplayModes(kModes, kModeId60, std::move(selectorPtr))
.inject();
// isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
@@ -78,6 +77,8 @@
static constexpr ui::Size kResolution4K{3840, 2160};
static inline const DisplayModePtr kMode90_4K =
createDisplayMode(kModeId90_4K, 90_Hz, 3, kResolution4K);
+
+ static inline const DisplayModes kModes = makeModes(kMode60, kMode90, kMode120, kMode90_4K);
};
void DisplayModeSwitchingTest::setupScheduler(
@@ -144,7 +145,8 @@
// Verify that the next commit will complete the mode change and send
// a onModeChanged event to the framework.
- EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90));
+ EXPECT_CALL(*mAppEventThread,
+ onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)}));
mFlinger.commit();
Mock::VerifyAndClearExpectations(mAppEventThread);
@@ -175,7 +177,8 @@
hal::HWConfigId(kModeId90.value()), _, _))
.WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
- EXPECT_CALL(*mAppEventThread, onModeChanged(kMode90));
+ EXPECT_CALL(*mAppEventThread,
+ onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)}));
mFlinger.commit();
@@ -281,5 +284,114 @@
ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K);
}
+TEST_F(DisplayModeSwitchingTest, multiDisplay) {
+ ftl::FakeGuard guard(kMainThreadContext);
+
+ constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID;
+ constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1;
+
+ constexpr PhysicalDisplayId kOuterDisplayId = PhysicalDisplayId::fromPort(254u);
+
+ constexpr bool kIsPrimary = false;
+ TestableSurfaceFlinger::FakeHwcDisplayInjector(kOuterDisplayId, hal::DisplayType::PHYSICAL,
+ kIsPrimary)
+ .setHwcDisplayId(kOuterDisplayHwcId)
+ .inject(&mFlinger, mComposer);
+
+ const auto outerDisplay = mFakeDisplayInjector.injectInternalDisplay(
+ [&](FakeDisplayDeviceInjector& injector) {
+ injector.setDisplayModes(mock::cloneForDisplay(kOuterDisplayId, kModes),
+ kModeId120);
+ },
+ {.displayId = kOuterDisplayId,
+ .hwcDisplayId = kOuterDisplayHwcId,
+ .isPrimary = kIsPrimary});
+
+ const auto& innerDisplay = mDisplay;
+
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+
+ EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId120);
+
+ mFlinger.onActiveDisplayChanged(innerDisplay);
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId90.value(),
+ false, 0.f, 120.f)));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId60.value(),
+ false, 0.f, 120.f)));
+
+ // Transition on the inner display.
+ ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+
+ // No transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kInnerDisplayHwcId,
+ hal::HWConfigId(kModeId90.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ // Transition on the inner display.
+ ASSERT_TRUE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90);
+
+ // No transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+
+ mFlinger.commit();
+
+ // Transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId90);
+
+ // No transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId120);
+
+ mFlinger.onActiveDisplayChanged(outerDisplay);
+
+ // No transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+
+ // Transition on the outer display.
+ ASSERT_TRUE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId60);
+
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(kOuterDisplayHwcId,
+ hal::HWConfigId(kModeId60.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ // No transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+
+ // Transition on the outer display.
+ ASSERT_TRUE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId60);
+
+ mFlinger.commit();
+
+ // No transition on the inner display.
+ EXPECT_FALSE(innerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId90);
+
+ // Transition on the outer display.
+ EXPECT_FALSE(outerDisplay->getDesiredActiveMode());
+ EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId60);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp
new file mode 100644
index 0000000..0e149d2
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ExcludeDolbyVisionTest.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class ExcludeDolbyVisionTest : public DisplayTransactionTest {
+public:
+ void injectDisplayModes(std::vector<DisplayModePtr> displayModePtrs) {
+ DisplayModes modes;
+ for (DisplayModePtr displayMode : displayModePtrs) {
+ modes.try_emplace(displayMode->getId(), displayMode);
+ }
+
+ mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
+ .setDisplayModes(std::move(modes), displayModePtrs[0]->getId())
+ .inject();
+ mDisplay->overrideHdrTypes(types);
+ }
+
+protected:
+ sp<DisplayDevice> mDisplay;
+
+ static constexpr DisplayModeId modeId1080p60{0};
+ static constexpr DisplayModeId modeId4k30{1};
+ static constexpr DisplayModeId modeId4k60{2};
+
+ static inline const DisplayModePtr mode1080p60 =
+ createDisplayMode(modeId1080p60, 60_Hz, 0, ui::Size(1920, 1080));
+ static inline const DisplayModePtr mode4k30 =
+ createDisplayMode(modeId4k30, 30_Hz, 1, ui::Size(3840, 2160));
+ static inline const DisplayModePtr mode4k30NonStandard =
+ createDisplayMode(modeId4k30, 30.1_Hz, 1, ui::Size(3840, 2160));
+ static inline const DisplayModePtr mode4k60 =
+ createDisplayMode(modeId4k60, 60_Hz, 2, ui::Size(3840, 2160));
+
+ const std::vector<ui::Hdr> types = {ui::Hdr::DOLBY_VISION, ui::Hdr::DOLBY_VISION_4K30,
+ ui::Hdr::HDR10_PLUS};
+};
+
+TEST_F(ExcludeDolbyVisionTest, excludesDolbyVisionOnModesHigherThan4k30) {
+ injectDisplayModes({mode4k60});
+ ui::DynamicDisplayInfo info;
+ mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+ std::vector<ui::DisplayMode> displayModes = info.supportedDisplayModes;
+
+ ASSERT_EQ(1, displayModes.size());
+ ASSERT_TRUE(std::any_of(displayModes[0].supportedHdrTypes.begin(),
+ displayModes[0].supportedHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+ ASSERT_TRUE(displayModes[0].supportedHdrTypes.size() == 1);
+}
+
+TEST_F(ExcludeDolbyVisionTest, includesDolbyVisionOnModesLowerThanOrEqualTo4k30) {
+ injectDisplayModes({mode1080p60, mode4k30, mode4k30NonStandard});
+ ui::DynamicDisplayInfo info;
+ mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+ std::vector<ui::DisplayMode> displayModes = info.supportedDisplayModes;
+
+ ASSERT_EQ(2, displayModes.size());
+ for (size_t i = 0; i < displayModes.size(); i++) {
+ ASSERT_TRUE(std::any_of(displayModes[i].supportedHdrTypes.begin(),
+ displayModes[i].supportedHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+ ASSERT_TRUE(std::any_of(displayModes[i].supportedHdrTypes.begin(),
+ displayModes[i].supportedHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION; }));
+ ASSERT_TRUE(displayModes[i].supportedHdrTypes.size() == 2);
+ }
+}
+
+TEST_F(ExcludeDolbyVisionTest, 4k30IsNotReportedAsAValidHdrType) {
+ injectDisplayModes({mode4k60});
+ ui::DynamicDisplayInfo info;
+ mFlinger.getDynamicDisplayInfoFromToken(mDisplay->getDisplayToken().promote(), &info);
+
+ std::vector<ui::Hdr> displayHdrTypes = info.hdrCapabilities.getSupportedHdrTypes();
+
+ ASSERT_EQ(2, displayHdrTypes.size());
+ ASSERT_TRUE(std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::HDR10_PLUS; }));
+ ASSERT_TRUE(std::any_of(displayHdrTypes.begin(), displayHdrTypes.end(),
+ [](ui::Hdr type) { return type == ui::Hdr::DOLBY_VISION; }));
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
index bc66961..622717f 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp
@@ -97,7 +97,6 @@
.setNativeWindow(mNativeWindow)
.setPowerMode(hal::PowerMode::ON)
.inject();
- mFlinger.mutableActiveDisplayId() = mDisplay->getPhysicalId();
}
void SurfaceFlingerPowerHintTest::setupScheduler() {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 25857ec..6e50059 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -259,12 +259,6 @@
auto injector = Display::makeFakeExistingDisplayInjector(test);
const auto display = injector.inject();
display->setPowerMode(mode);
- if (injector.physicalDisplay()
- .transform(&display::PhysicalDisplay::isInternal)
- .value_or(false)) {
- test->mFlinger.mutableActiveDisplayId() = display->getPhysicalId();
- }
-
return display;
}
@@ -490,5 +484,38 @@
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
}
+// TODO(b/262417075)
+TEST_F(SetPowerModeInternalTest, DISABLED_designatesLeaderDisplay) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Inject a primary display with uninitialized power mode.
+ constexpr bool kInitPowerMode = false;
+ Case::Display::injectHwcDisplay<kInitPowerMode>(this);
+ auto injector = Case::Display::makeFakeExistingDisplayInjector(this);
+ injector.setPowerMode(std::nullopt);
+ const auto display = injector.inject();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ // FakeDisplayDeviceInjector registers the display with Scheduler, so it has already been
+ // designated as the leader. Set an arbitrary leader to verify that `setPowerModeInternal`
+ // designates a leader regardless of any preceding `Scheduler::registerDisplay` call(s).
+ constexpr PhysicalDisplayId kPlaceholderId = PhysicalDisplayId::fromPort(42);
+ ASSERT_NE(display->getPhysicalId(), kPlaceholderId);
+ mFlinger.scheduler()->setLeaderDisplay(kPlaceholderId);
+
+ mFlinger.setPowerModeInternal(display, PowerMode::ON);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The primary display should be designated as the leader.
+ EXPECT_EQ(mFlinger.scheduler()->leaderDisplayId(), display->getPhysicalId());
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 54c10c5..b8a6063 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -87,6 +87,10 @@
Scheduler::unregisterDisplay(displayId);
}
+ std::optional<PhysicalDisplayId> leaderDisplayId() const NO_THREAD_SAFETY_ANALYSIS {
+ return mLeaderDisplayId;
+ }
+
void setLeaderDisplay(PhysicalDisplayId displayId) {
ftl::FakeGuard guard(kMainThreadContext);
Scheduler::setLeaderDisplay(displayId);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index e29dd67..2117084 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -30,6 +30,7 @@
#include <ftl/fake_guard.h>
#include <gui/ScreenCaptureResults.h>
+#include <ui/DynamicDisplayInfo.h>
#include "DisplayDevice.h"
#include "FakeVsyncConfiguration.h"
#include "FrameTracer/FrameTracer.h"
@@ -470,7 +471,7 @@
void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) {
Mutex::Autolock lock(mFlinger->mStateLock);
ftl::FakeGuard guard(kMainThreadContext);
- mFlinger->onActiveDisplayChangedLocked(activeDisplay);
+ mFlinger->onActiveDisplayChangedLocked(nullptr, activeDisplay);
}
auto createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle,
@@ -486,6 +487,11 @@
void updateLayerMetadataSnapshot() { mFlinger->updateLayerMetadataSnapshot(); }
+ void getDynamicDisplayInfoFromToken(const sp<IBinder>& displayToken,
+ ui::DynamicDisplayInfo* dynamicDisplayInfo) {
+ mFlinger->getDynamicDisplayInfoFromToken(displayToken, dynamicDisplayInfo);
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
@@ -621,7 +627,7 @@
return *this;
}
- auto& setPowerMode(hal::PowerMode mode) {
+ auto& setPowerMode(std::optional<hal::PowerMode> mode) {
mPowerMode = mode;
return *this;
}
@@ -643,16 +649,18 @@
// is much longer lived.
auto display = std::make_unique<HWC2Display>(*composer, *mCapabilities, mHwcDisplayId,
mHwcDisplayType);
-
display->mutableIsConnected() = true;
- display->setPowerMode(mPowerMode);
+
+ if (mPowerMode) {
+ display->setPowerMode(*mPowerMode);
+ }
+
flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
EXPECT_CALL(*composer, getDisplayConfigs(mHwcDisplayId, _))
.WillRepeatedly(
DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{mActiveConfig}),
Return(hal::Error::NONE)));
-
EXPECT_CALL(*composer,
getDisplayAttribute(mHwcDisplayId, mActiveConfig, hal::Attribute::WIDTH, _))
.WillRepeatedly(DoAll(SetArgPointee<3>(mResolution.getWidth()),
@@ -711,7 +719,7 @@
int32_t mDpiY = DEFAULT_DPI;
int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
hal::HWConfigId mActiveConfig = DEFAULT_ACTIVE_CONFIG;
- hal::PowerMode mPowerMode = DEFAULT_POWER_MODE;
+ std::optional<hal::PowerMode> mPowerMode = DEFAULT_POWER_MODE;
const std::unordered_set<aidl::android::hardware::graphics::composer3::Capability>*
mCapabilities = nullptr;
};
@@ -788,7 +796,7 @@
return *this;
}
- auto& setPowerMode(hal::PowerMode mode) {
+ auto& setPowerMode(std::optional<hal::PowerMode> mode) {
mCreationArgs.initialPowerMode = mode;
return *this;
}
@@ -853,6 +861,10 @@
LOG_ALWAYS_FATAL_IF(!physicalIdOpt);
const auto physicalId = *physicalIdOpt;
+ if (mCreationArgs.isPrimary) {
+ mFlinger.mutableActiveDisplayId() = physicalId;
+ }
+
LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
const auto activeMode = modes.get(activeModeId);
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 1dd4f25..a9ae1d3 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -44,11 +44,14 @@
namespace {
using testing::_;
+using testing::AllOf;
using testing::AnyNumber;
using testing::Contains;
+using testing::ElementsAre;
using testing::HasSubstr;
using testing::InSequence;
using testing::Not;
+using testing::Property;
using testing::SizeIs;
using testing::StrEq;
using testing::UnorderedElementsAre;
@@ -645,7 +648,7 @@
ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
ASSERT_EQ(1, globalProto.stats_size());
- const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats(0);
ASSERT_TRUE(layerProto.has_layer_name());
EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
ASSERT_TRUE(layerProto.has_total_frames());
@@ -653,7 +656,7 @@
ASSERT_EQ(6, layerProto.deltas_size());
for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
ASSERT_EQ(1, deltaProto.histograms_size());
- const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0);
+ const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms(0);
EXPECT_EQ(1, histogramProto.frame_count());
if ("post2acquire" == deltaProto.delta_name()) {
EXPECT_EQ(1, histogramProto.time_millis());
@@ -673,6 +676,46 @@
}
}
+using LayerProto = SFTimeStatsLayerProto;
+using DeltaProto = SFTimeStatsDeltaProto;
+using BucketProto = SFTimeStatsHistogramBucketProto;
+
+TEST_F(TimeStatsTest, canComputeLayerStabilityHistogram) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000); // 0ms delta
+ // Slightly unstable frames
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000); // 1ms delta
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 6000000); // 1ms delta
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ EXPECT_THAT(globalProto.stats(),
+ ElementsAre(AllOf(
+ Property(&LayerProto::layer_name, genLayerName(LAYER_ID_0)),
+ Property(&LayerProto::total_frames, 4),
+ Property(&LayerProto::deltas,
+ Contains(AllOf(Property(&DeltaProto::delta_name,
+ "present2presentDelta"),
+ Property(&DeltaProto::histograms,
+ UnorderedElementsAre(
+ AllOf(Property(&BucketProto::
+ time_millis,
+ 0),
+ Property(&BucketProto::
+ frame_count,
+ 1)),
+ AllOf(Property(&BucketProto::
+ time_millis,
+ 1),
+ Property(&BucketProto::
+ frame_count,
+ 2))))))))));
+}
+
TEST_F(TimeStatsTest, canNotInsertInvalidLayerNameTimeStats) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
@@ -1099,8 +1142,10 @@
kGameMode, JankType::None, DISPLAY_DEADLINE_DELTA,
DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA});
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
android::surfaceflinger::SurfaceflingerStatsGlobalInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1234,8 +1279,10 @@
GameMode::Standard, JankType::None, DISPLAY_DEADLINE_DELTA,
DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1322,8 +1369,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, GameMode::Battery);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 6, 5000000, {}, GameMode::Custom);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1412,8 +1461,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1437,8 +1488,10 @@
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1456,8 +1509,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
@@ -1476,8 +1531,10 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 4, 5000000);
+ std::vector<uint8_t> pulledBytes;
+ EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledBytes));
std::string pulledData;
- EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
+ pulledData.assign(pulledBytes.begin(), pulledBytes.end());
SurfaceflingerStatsLayerInfoWrapper atomList;
ASSERT_TRUE(atomList.ParseFromString(pulledData));
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index 09d002f..1173d1c 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -126,7 +126,7 @@
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
layer->setBuffer(externalTexture, bufferData, postTime, /*desiredPresentTime*/ 30, false,
- dequeueTime, FrameTimelineInfo{}, 0);
+ dequeueTime, FrameTimelineInfo{});
commitTransaction(layer.get());
nsecs_t latchTime = 25;
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 7dfbcc0..ae03db4 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -131,7 +131,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
acquireFence->signalForTest(12);
commitTransaction(layer.get());
@@ -166,7 +166,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -183,7 +183,7 @@
2ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo);
nsecs_t end = systemTime();
acquireFence2->signalForTest(12);
@@ -229,7 +229,7 @@
1ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
acquireFence->signalForTest(12);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
@@ -264,7 +264,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -307,7 +307,7 @@
FrameTimelineInfo ftInfo3;
ftInfo3.vsyncId = 3;
ftInfo3.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo3, 0);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo3);
EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -352,7 +352,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -367,7 +367,7 @@
1ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo);
acquireFence2->signalForTest(12);
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -404,7 +404,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -424,7 +424,7 @@
FrameTimelineInfo ftInfoInv;
ftInfoInv.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
ftInfoInv.inputEventId = 0;
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfoInv, 0);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfoInv);
auto dropEndTime1 = systemTime();
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -445,7 +445,7 @@
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 2;
ftInfo2.inputEventId = 0;
- layer->setBuffer(externalTexture3, bufferData, 10, 20, false, std::nullopt, ftInfo2, 0);
+ layer->setBuffer(externalTexture3, bufferData, 10, 20, false, std::nullopt, ftInfo2);
auto dropEndTime2 = systemTime();
acquireFence3->signalForTest(12);
@@ -494,7 +494,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo, 0);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 2;
ftInfo2.inputEventId = 0;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 5ee38ec..836e3a4 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -32,7 +32,6 @@
using android::hardware::graphics::common::V1_1::RenderIntent;
using android::hardware::graphics::common::V1_2::ColorMode;
using android::hardware::graphics::common::V1_2::Dataspace;
-using android::hardware::graphics::common::V1_2::Hdr;
using android::hardware::graphics::common::V1_2::PixelFormat;
using android::hardware::graphics::composer::V2_1::Config;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
index c78b6bd..3b36361 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
@@ -38,4 +38,24 @@
return createDisplayMode(modeId, refreshRate, {}, {}, displayId);
}
+inline DisplayModePtr cloneForDisplay(PhysicalDisplayId displayId, const DisplayModePtr& modePtr) {
+ return DisplayMode::Builder(modePtr->getHwcId())
+ .setId(modePtr->getId())
+ .setPhysicalDisplayId(displayId)
+ .setVsyncPeriod(modePtr->getVsyncPeriod())
+ .setGroup(modePtr->getGroup())
+ .setResolution(modePtr->getResolution())
+ .build();
+}
+
+inline DisplayModes cloneForDisplay(PhysicalDisplayId displayId, const DisplayModes& modes) {
+ DisplayModes clones;
+
+ for (const auto& [id, modePtr] : modes) {
+ clones.try_emplace(id, cloneForDisplay(displayId, modePtr));
+ }
+
+ return clones;
+}
+
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index ded6806..f8567bd 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -34,7 +34,7 @@
MOCK_METHOD0(onScreenReleased, void());
MOCK_METHOD0(onScreenAcquired, void());
MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
- MOCK_METHOD1(onModeChanged, void(DisplayModePtr));
+ MOCK_METHOD1(onModeChanged, void(const scheduler::FrameRateMode &));
MOCK_METHOD2(onFrameRateOverridesChanged,
void(PhysicalDisplayId, std::vector<FrameRateOverride>));
MOCK_CONST_METHOD1(dump, void(std::string&));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 0dee800..86fbadc 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -27,7 +27,7 @@
TimeStats();
~TimeStats() override;
- MOCK_METHOD2(onPullAtom, bool(const int, std::string*));
+ MOCK_METHOD2(onPullAtom, bool(const int, std::vector<uint8_t>*));
MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
MOCK_METHOD0(isEnabled, bool());
MOCK_METHOD0(miniDump, std::string());
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index df70bf4..a9706bc 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -682,6 +682,7 @@
"vkGetPhysicalDeviceMemoryProperties2",
"vkGetPhysicalDeviceMemoryProperties2KHR",
"vkGetPhysicalDeviceMultisamplePropertiesEXT",
+ "vkGetPhysicalDeviceOpticalFlowImageFormatsNV",
"vkGetPhysicalDevicePresentRectanglesKHR",
"vkGetPhysicalDeviceProperties",
"vkGetPhysicalDeviceProperties2",
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 4927150..9ed992b 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1027,8 +1027,11 @@
}
}
-bool GetAndroidNativeBufferSpecVersion9Support(
- VkPhysicalDevice physicalDevice) {
+VkResult GetAndroidNativeBufferSpecVersion9Support(
+ VkPhysicalDevice physicalDevice,
+ bool& support) {
+ support = false;
+
const InstanceData& data = GetData(physicalDevice);
// Call to get propertyCount
@@ -1038,6 +1041,10 @@
physicalDevice, nullptr, &propertyCount, nullptr);
ATRACE_END();
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ return result;
+ }
+
// Call to enumerate properties
std::vector<VkExtensionProperties> properties(propertyCount);
ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
@@ -1045,6 +1052,10 @@
physicalDevice, nullptr, &propertyCount, properties.data());
ATRACE_END();
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ return result;
+ }
+
for (uint32_t i = 0; i < propertyCount; i++) {
auto& prop = properties[i];
@@ -1053,11 +1064,12 @@
continue;
if (prop.specVersion >= 9) {
- return true;
+ support = true;
+ return result;
}
}
- return false;
+ return result;
}
VkResult EnumerateDeviceExtensionProperties(
@@ -1101,18 +1113,30 @@
swapchainCompFeats.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT;
swapchainCompFeats.pNext = nullptr;
+ swapchainCompFeats.imageCompressionControlSwapchain = false;
VkPhysicalDeviceImageCompressionControlFeaturesEXT imageCompFeats = {};
imageCompFeats.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
imageCompFeats.pNext = &swapchainCompFeats;
+ imageCompFeats.imageCompressionControl = false;
VkPhysicalDeviceFeatures2 feats2 = {};
feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
feats2.pNext = &imageCompFeats;
- GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+ const auto& driver = GetData(physicalDevice).driver;
+ if (driver.GetPhysicalDeviceFeatures2 ||
+ driver.GetPhysicalDeviceFeatures2KHR) {
+ GetPhysicalDeviceFeatures2(physicalDevice, &feats2);
+ }
- bool anb9 = GetAndroidNativeBufferSpecVersion9Support(physicalDevice);
+ bool anb9 = false;
+ VkResult result =
+ GetAndroidNativeBufferSpecVersion9Support(physicalDevice, anb9);
+
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ return result;
+ }
if (anb9 && imageCompFeats.imageCompressionControl) {
loader_extensions.push_back(
@@ -1142,7 +1166,7 @@
}
ATRACE_BEGIN("driver.EnumerateDeviceExtensionProperties");
- VkResult result = data.driver.EnumerateDeviceExtensionProperties(
+ result = data.driver.EnumerateDeviceExtensionProperties(
physicalDevice, pLayerName, pPropertyCount, pProperties);
ATRACE_END();
@@ -1242,6 +1266,27 @@
return VK_ERROR_INCOMPATIBLE_DRIVER;
}
+ // TODO(b/259516419) avoid getting stats from hwui
+ // const bool reportStats = (pCreateInfo->pApplicationInfo == nullptr )
+ // || (strcmp("android framework",
+ // pCreateInfo->pApplicationInfo->pEngineName) != 0);
+ const bool reportStats = true;
+ if (reportStats) {
+ // Set stats for Vulkan api version requested with application info
+ if (pCreateInfo->pApplicationInfo) {
+ const uint32_t vulkanApiVersion =
+ pCreateInfo->pApplicationInfo->apiVersion;
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::CREATED_VULKAN_API_VERSION,
+ vulkanApiVersion);
+ }
+
+ // Update stats for the extensions requested
+ android::GraphicsEnv::getInstance().setVulkanInstanceExtensions(
+ pCreateInfo->enabledExtensionCount,
+ pCreateInfo->ppEnabledExtensionNames);
+ }
+
*pInstance = instance;
return VK_SUCCESS;
@@ -1347,6 +1392,65 @@
*pDevice = dev;
+ // TODO(b/259516419) avoid getting stats from hwui
+ const bool reportStats = true;
+ if (reportStats) {
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::CREATED_VULKAN_DEVICE);
+
+ // Set stats for creating a Vulkan device and report features in use
+ const VkPhysicalDeviceFeatures* pEnabledFeatures =
+ pCreateInfo->pEnabledFeatures;
+ if (!pEnabledFeatures) {
+ // Use features from the chained VkPhysicalDeviceFeatures2
+ // structure, if given
+ const VkPhysicalDeviceFeatures2* features2 =
+ reinterpret_cast<const VkPhysicalDeviceFeatures2*>(
+ pCreateInfo->pNext);
+ while (features2 &&
+ features2->sType !=
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2) {
+ features2 = reinterpret_cast<const VkPhysicalDeviceFeatures2*>(
+ features2->pNext);
+ }
+ if (features2) {
+ pEnabledFeatures = &features2->features;
+ }
+ }
+ const VkBool32* pFeatures =
+ reinterpret_cast<const VkBool32*>(pEnabledFeatures);
+ if (pFeatures) {
+ // VkPhysicalDeviceFeatures consists of VkBool32 values, go over all
+ // of them using pointer arithmetic here and save the features in a
+ // 64-bit bitfield
+ static_assert(
+ (sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32)) <= 64,
+ "VkPhysicalDeviceFeatures has too many elements for bitfield "
+ "packing");
+ static_assert(
+ (sizeof(VkPhysicalDeviceFeatures) % sizeof(VkBool32)) == 0,
+ "VkPhysicalDeviceFeatures has invalid size for bitfield "
+ "packing");
+ const int numFeatures =
+ sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32);
+
+ uint64_t enableFeatureBits = 0;
+ for (int i = 0; i < numFeatures; i++) {
+ if (pFeatures[i] != VK_FALSE) {
+ enableFeatureBits |= (uint64_t(1) << i);
+ }
+ }
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::VULKAN_DEVICE_FEATURES_ENABLED,
+ enableFeatureBits);
+ }
+
+ // Update stats for the extensions requested
+ android::GraphicsEnv::getInstance().setVulkanDeviceExtensions(
+ pCreateInfo->enabledExtensionCount,
+ pCreateInfo->ppEnabledExtensionNames);
+ }
+
return VK_SUCCESS;
}
@@ -1532,6 +1636,11 @@
} break;
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT: {
+ VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*
+ compressionFeat = reinterpret_cast<
+ VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT*>(
+ pFeats);
+ compressionFeat->imageCompressionControlSwapchain = false;
imageCompressionControlSwapchainInChain = true;
} break;
@@ -1551,6 +1660,7 @@
imageCompFeats.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_COMPRESSION_CONTROL_FEATURES_EXT;
imageCompFeats.pNext = nullptr;
+ imageCompFeats.imageCompressionControl = false;
VkPhysicalDeviceFeatures2 feats2 = {};
feats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 475bc40..1bf7abb 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -243,6 +243,11 @@
// syncronous requests to Surface Flinger):
enum { MIN_NUM_FRAMES_AGO = 5 };
+bool IsSharedPresentMode(VkPresentModeKHR mode) {
+ return mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
+ mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR;
+}
+
struct Swapchain {
Swapchain(Surface& surface_,
uint32_t num_images_,
@@ -254,9 +259,7 @@
pre_transform(pre_transform_),
frame_timestamps_enabled(false),
acquire_next_image_timeout(-1),
- shared(present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
- present_mode ==
- VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
+ shared(IsSharedPresentMode(present_mode)) {
ANativeWindow* window = surface.window.get();
native_window_get_refresh_cycle_duration(
window,
@@ -930,27 +933,36 @@
return GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice,
pSurfaceInfo->surface,
pSurfaceFormatCount, nullptr);
- } else {
- // temp vector for forwarding; we'll marshal it into the pSurfaceFormats
- // after the call.
- std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount);
- VkResult result = GetPhysicalDeviceSurfaceFormatsKHR(
- physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount,
- surface_formats.data());
+ }
- if (result == VK_SUCCESS || result == VK_INCOMPLETE) {
- const auto& driver = GetData(physicalDevice).driver;
+ // temp vector for forwarding; we'll marshal it into the pSurfaceFormats
+ // after the call.
+ std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount);
+ VkResult result = GetPhysicalDeviceSurfaceFormatsKHR(
+ physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount,
+ surface_formats.data());
- // marshal results individually due to stride difference.
- uint32_t formats_to_marshal = *pSurfaceFormatCount;
- for (uint32_t i = 0u; i < formats_to_marshal; i++) {
- pSurfaceFormats[i].surfaceFormat = surface_formats[i];
+ if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
+ return result;
+ }
- // Query the compression properties for the surface format
- if (pSurfaceFormats[i].pNext) {
+ const auto& driver = GetData(physicalDevice).driver;
+
+ // marshal results individually due to stride difference.
+ uint32_t formats_to_marshal = *pSurfaceFormatCount;
+ for (uint32_t i = 0u; i < formats_to_marshal; i++) {
+ pSurfaceFormats[i].surfaceFormat = surface_formats[i];
+
+ // Query the compression properties for the surface format
+ VkSurfaceFormat2KHR* pSurfaceFormat = &pSurfaceFormats[i];
+ while (pSurfaceFormat->pNext) {
+ pSurfaceFormat =
+ reinterpret_cast<VkSurfaceFormat2KHR*>(pSurfaceFormat->pNext);
+ switch (pSurfaceFormat->sType) {
+ case VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT: {
VkImageCompressionPropertiesEXT* surfaceCompressionProps =
reinterpret_cast<VkImageCompressionPropertiesEXT*>(
- pSurfaceFormats[i].pNext);
+ pSurfaceFormat);
if (surfaceCompressionProps &&
driver.GetPhysicalDeviceImageFormatProperties2KHR) {
@@ -992,12 +1004,16 @@
return compressionRes;
}
}
- }
+ } break;
+
+ default:
+ // Ignore all other extension structs
+ break;
}
}
-
- return result;
}
+
+ return result;
}
VKAPI_ATTR
@@ -1614,6 +1630,10 @@
android::GpuStatsInfo::Stats::FALSE_PREROTATION);
}
+ // Set stats for creating a Vulkan swapchain
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::CREATED_VULKAN_SWAPCHAIN);
+
surface.swapchain_handle = HandleFromSwapchain(swapchain);
*swapchain_handle = surface.swapchain_handle;
return VK_SUCCESS;
@@ -1783,6 +1803,173 @@
return a != VK_SUCCESS ? a : b;
}
+// KHR_incremental_present aspect of QueuePresentKHR
+static void SetSwapchainSurfaceDamage(ANativeWindow *window, const VkPresentRegionKHR *pRegion) {
+ std::vector<android_native_rect_t> rects(pRegion->rectangleCount);
+ for (auto i = 0u; i < pRegion->rectangleCount; i++) {
+ auto const& rect = pRegion->pRectangles[i];
+ if (rect.layer > 0) {
+ ALOGV("vkQueuePresentKHR ignoring invalid layer (%u); using layer 0 instead",
+ rect.layer);
+ }
+
+ rects[i].left = rect.offset.x;
+ rects[i].bottom = rect.offset.y;
+ rects[i].right = rect.offset.x + rect.extent.width;
+ rects[i].top = rect.offset.y + rect.extent.height;
+ }
+ native_window_set_surface_damage(window, rects.data(), rects.size());
+}
+
+// GOOGLE_display_timing aspect of QueuePresentKHR
+static void SetSwapchainFrameTimestamp(Swapchain &swapchain, const VkPresentTimeGOOGLE *pTime) {
+ ANativeWindow *window = swapchain.surface.window.get();
+
+ // We don't know whether the app will actually use GOOGLE_display_timing
+ // with a particular swapchain until QueuePresent; enable it on the BQ
+ // now if needed
+ if (!swapchain.frame_timestamps_enabled) {
+ ALOGV("Calling native_window_enable_frame_timestamps(true)");
+ native_window_enable_frame_timestamps(window, true);
+ swapchain.frame_timestamps_enabled = true;
+ }
+
+ // Record the nativeFrameId so it can be later correlated to
+ // this present.
+ uint64_t nativeFrameId = 0;
+ int err = native_window_get_next_frame_id(
+ window, &nativeFrameId);
+ if (err != android::OK) {
+ ALOGE("Failed to get next native frame ID.");
+ }
+
+ // Add a new timing record with the user's presentID and
+ // the nativeFrameId.
+ swapchain.timing.emplace_back(pTime, nativeFrameId);
+ if (swapchain.timing.size() > MAX_TIMING_INFOS) {
+ swapchain.timing.erase(
+ swapchain.timing.begin(),
+ swapchain.timing.begin() + swapchain.timing.size() - MAX_TIMING_INFOS);
+ }
+ if (pTime->desiredPresentTime) {
+ ALOGV(
+ "Calling native_window_set_buffers_timestamp(%" PRId64 ")",
+ pTime->desiredPresentTime);
+ native_window_set_buffers_timestamp(
+ window,
+ static_cast<int64_t>(pTime->desiredPresentTime));
+ }
+}
+
+static VkResult PresentOneSwapchain(
+ VkQueue queue,
+ Swapchain& swapchain,
+ uint32_t imageIndex,
+ const VkPresentRegionKHR *pRegion,
+ const VkPresentTimeGOOGLE *pTime,
+ uint32_t waitSemaphoreCount,
+ const VkSemaphore *pWaitSemaphores) {
+
+ VkDevice device = GetData(queue).driver_device;
+ const auto& dispatch = GetData(queue).driver;
+
+ Swapchain::Image& img = swapchain.images[imageIndex];
+ VkResult swapchain_result = VK_SUCCESS;
+ VkResult result;
+ int err;
+
+ // XXX: long standing issue: QueueSignalReleaseImageANDROID consumes the
+ // wait semaphores, so this doesn't actually work for the multiple swapchain
+ // case.
+ int fence = -1;
+ result = dispatch.QueueSignalReleaseImageANDROID(
+ queue, waitSemaphoreCount,
+ pWaitSemaphores, img.image, &fence);
+ if (result != VK_SUCCESS) {
+ ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
+ swapchain_result = result;
+ }
+ if (img.release_fence >= 0)
+ close(img.release_fence);
+ img.release_fence = fence < 0 ? -1 : dup(fence);
+
+ if (swapchain.surface.swapchain_handle == HandleFromSwapchain(&swapchain)) {
+ ANativeWindow* window = swapchain.surface.window.get();
+ if (swapchain_result == VK_SUCCESS) {
+
+ if (pRegion) {
+ SetSwapchainSurfaceDamage(window, pRegion);
+ }
+ if (pTime) {
+ SetSwapchainFrameTimestamp(swapchain, pTime);
+ }
+
+ err = window->queueBuffer(window, img.buffer.get(), fence);
+ // queueBuffer always closes fence, even on error
+ if (err != android::OK) {
+ ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
+ swapchain_result = WorstPresentResult(
+ swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
+ } else {
+ if (img.dequeue_fence >= 0) {
+ close(img.dequeue_fence);
+ img.dequeue_fence = -1;
+ }
+ img.dequeued = false;
+ }
+
+ // If the swapchain is in shared mode, immediately dequeue the
+ // buffer so it can be presented again without an intervening
+ // call to AcquireNextImageKHR. We expect to get the same buffer
+ // back from every call to dequeueBuffer in this mode.
+ if (swapchain.shared && swapchain_result == VK_SUCCESS) {
+ ANativeWindowBuffer* buffer;
+ int fence_fd;
+ err = window->dequeueBuffer(window, &buffer, &fence_fd);
+ if (err != android::OK) {
+ ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
+ swapchain_result = WorstPresentResult(swapchain_result,
+ VK_ERROR_SURFACE_LOST_KHR);
+ } else if (img.buffer != buffer) {
+ ALOGE("got wrong image back for shared swapchain");
+ swapchain_result = WorstPresentResult(swapchain_result,
+ VK_ERROR_SURFACE_LOST_KHR);
+ } else {
+ img.dequeue_fence = fence_fd;
+ img.dequeued = true;
+ }
+ }
+ }
+ if (swapchain_result != VK_SUCCESS) {
+ OrphanSwapchain(device, &swapchain);
+ }
+ // Android will only return VK_SUBOPTIMAL_KHR for vkQueuePresentKHR,
+ // and only when the window's transform/rotation changes. Extent
+ // changes will not cause VK_SUBOPTIMAL_KHR because of the
+ // application issues that were caused when the following transform
+ // change was added.
+ int window_transform_hint;
+ err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
+ &window_transform_hint);
+ if (err != android::OK) {
+ ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
+ strerror(-err), err);
+ swapchain_result = WorstPresentResult(
+ swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
+ }
+ if (swapchain.pre_transform != window_transform_hint) {
+ swapchain_result =
+ WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR);
+ }
+ } else {
+ ReleaseSwapchainImage(device, swapchain.shared, nullptr, fence,
+ img, true);
+ swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
+ }
+
+ return swapchain_result;
+}
+
VKAPI_ATTR
VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) {
ATRACE_CALL();
@@ -1791,8 +1978,6 @@
"vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d",
present_info->sType);
- VkDevice device = GetData(queue).driver_device;
- const auto& dispatch = GetData(queue).driver;
VkResult final_result = VK_SUCCESS;
// Look at the pNext chain for supported extension structs:
@@ -1828,175 +2013,19 @@
(present_regions) ? present_regions->pRegions : nullptr;
const VkPresentTimeGOOGLE* times =
(present_times) ? present_times->pTimes : nullptr;
- const VkAllocationCallbacks* allocator = &GetData(device).allocator;
- android_native_rect_t* rects = nullptr;
- uint32_t nrects = 0;
for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
Swapchain& swapchain =
*SwapchainFromHandle(present_info->pSwapchains[sc]);
- uint32_t image_idx = present_info->pImageIndices[sc];
- Swapchain::Image& img = swapchain.images[image_idx];
- const VkPresentRegionKHR* region =
- (regions && !swapchain.mailbox_mode) ? ®ions[sc] : nullptr;
- const VkPresentTimeGOOGLE* time = (times) ? ×[sc] : nullptr;
- VkResult swapchain_result = VK_SUCCESS;
- VkResult result;
- int err;
- int fence = -1;
- result = dispatch.QueueSignalReleaseImageANDROID(
- queue, present_info->waitSemaphoreCount,
- present_info->pWaitSemaphores, img.image, &fence);
- if (result != VK_SUCCESS) {
- ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
- swapchain_result = result;
- }
- if (img.release_fence >= 0)
- close(img.release_fence);
- img.release_fence = fence < 0 ? -1 : dup(fence);
-
- if (swapchain.surface.swapchain_handle ==
- present_info->pSwapchains[sc]) {
- ANativeWindow* window = swapchain.surface.window.get();
- if (swapchain_result == VK_SUCCESS) {
- if (region) {
- // Process the incremental-present hint for this swapchain:
- uint32_t rcount = region->rectangleCount;
- if (rcount > nrects) {
- android_native_rect_t* new_rects =
- static_cast<android_native_rect_t*>(
- allocator->pfnReallocation(
- allocator->pUserData, rects,
- sizeof(android_native_rect_t) * rcount,
- alignof(android_native_rect_t),
- VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
- if (new_rects) {
- rects = new_rects;
- nrects = rcount;
- } else {
- rcount = 0; // Ignore the hint for this swapchain
- }
- }
- for (uint32_t r = 0; r < rcount; ++r) {
- if (region->pRectangles[r].layer > 0) {
- ALOGV(
- "vkQueuePresentKHR ignoring invalid layer "
- "(%u); using layer 0 instead",
- region->pRectangles[r].layer);
- }
- int x = region->pRectangles[r].offset.x;
- int y = region->pRectangles[r].offset.y;
- int width = static_cast<int>(
- region->pRectangles[r].extent.width);
- int height = static_cast<int>(
- region->pRectangles[r].extent.height);
- android_native_rect_t* cur_rect = &rects[r];
- cur_rect->left = x;
- cur_rect->top = y + height;
- cur_rect->right = x + width;
- cur_rect->bottom = y;
- }
- native_window_set_surface_damage(window, rects, rcount);
- }
- if (time) {
- if (!swapchain.frame_timestamps_enabled) {
- ALOGV(
- "Calling "
- "native_window_enable_frame_timestamps(true)");
- native_window_enable_frame_timestamps(window, true);
- swapchain.frame_timestamps_enabled = true;
- }
-
- // Record the nativeFrameId so it can be later correlated to
- // this present.
- uint64_t nativeFrameId = 0;
- err = native_window_get_next_frame_id(
- window, &nativeFrameId);
- if (err != android::OK) {
- ALOGE("Failed to get next native frame ID.");
- }
-
- // Add a new timing record with the user's presentID and
- // the nativeFrameId.
- swapchain.timing.emplace_back(time, nativeFrameId);
- while (swapchain.timing.size() > MAX_TIMING_INFOS) {
- swapchain.timing.erase(swapchain.timing.begin());
- }
- if (time->desiredPresentTime) {
- // Set the desiredPresentTime:
- ALOGV(
- "Calling "
- "native_window_set_buffers_timestamp(%" PRId64 ")",
- time->desiredPresentTime);
- native_window_set_buffers_timestamp(
- window,
- static_cast<int64_t>(time->desiredPresentTime));
- }
- }
-
- err = window->queueBuffer(window, img.buffer.get(), fence);
- // queueBuffer always closes fence, even on error
- if (err != android::OK) {
- ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
- swapchain_result = WorstPresentResult(
- swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
- } else {
- if (img.dequeue_fence >= 0) {
- close(img.dequeue_fence);
- img.dequeue_fence = -1;
- }
- img.dequeued = false;
- }
-
- // If the swapchain is in shared mode, immediately dequeue the
- // buffer so it can be presented again without an intervening
- // call to AcquireNextImageKHR. We expect to get the same buffer
- // back from every call to dequeueBuffer in this mode.
- if (swapchain.shared && swapchain_result == VK_SUCCESS) {
- ANativeWindowBuffer* buffer;
- int fence_fd;
- err = window->dequeueBuffer(window, &buffer, &fence_fd);
- if (err != android::OK) {
- ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
- swapchain_result = WorstPresentResult(swapchain_result,
- VK_ERROR_SURFACE_LOST_KHR);
- } else if (img.buffer != buffer) {
- ALOGE("got wrong image back for shared swapchain");
- swapchain_result = WorstPresentResult(swapchain_result,
- VK_ERROR_SURFACE_LOST_KHR);
- } else {
- img.dequeue_fence = fence_fd;
- img.dequeued = true;
- }
- }
- }
- if (swapchain_result != VK_SUCCESS) {
- OrphanSwapchain(device, &swapchain);
- }
- // Android will only return VK_SUBOPTIMAL_KHR for vkQueuePresentKHR,
- // and only when the window's transform/rotation changes. Extent
- // changes will not cause VK_SUBOPTIMAL_KHR because of the
- // application issues that were caused when the following transform
- // change was added.
- int window_transform_hint;
- err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
- &window_transform_hint);
- if (err != android::OK) {
- ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
- strerror(-err), err);
- swapchain_result = WorstPresentResult(
- swapchain_result, VK_ERROR_SURFACE_LOST_KHR);
- }
- if (swapchain.pre_transform != window_transform_hint) {
- swapchain_result =
- WorstPresentResult(swapchain_result, VK_SUBOPTIMAL_KHR);
- }
- } else {
- ReleaseSwapchainImage(device, swapchain.shared, nullptr, fence,
- img, true);
- swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
- }
+ VkResult swapchain_result = PresentOneSwapchain(
+ queue,
+ swapchain,
+ present_info->pImageIndices[sc],
+ (regions && !swapchain.mailbox_mode) ? ®ions[sc] : nullptr,
+ times ? ×[sc] : nullptr,
+ present_info->waitSemaphoreCount,
+ present_info->pWaitSemaphores);
if (present_info->pResults)
present_info->pResults[sc] = swapchain_result;
@@ -2004,9 +2033,6 @@
if (swapchain_result != final_result)
final_result = WorstPresentResult(final_result, swapchain_result);
}
- if (rects) {
- allocator->pfnFree(allocator->pUserData, rects);
- }
return final_result;
}