Merge "inputdispatcher: Block touch while stylus is hovering" into main
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 620c23c..f90c618 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -251,13 +251,15 @@
     srcs: [
         // Trusty-specific files
         "OS_android.cpp",
-        "trusty/logging.cpp",
         "trusty/OS.cpp",
         "trusty/RpcServerTrusty.cpp",
         "trusty/RpcTransportTipcTrusty.cpp",
         "trusty/TrustyStatus.cpp",
         "trusty/socket.cpp",
     ],
+    shared_libs: [
+        "liblog",
+    ],
 }
 
 cc_defaults {
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 8243238..301dbf6 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -19,7 +19,6 @@
 #include <atomic>
 #include <set>
 
-#include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <binder/BpBinder.h>
 #include <binder/IInterface.h>
@@ -308,7 +307,7 @@
     Extras* e = getOrCreateExtras();
     RpcMutexUniqueLock lock(e->mLock);
     if (mRecordingOn) {
-        LOG(INFO) << "Could not start Binder recording. Another is already in progress.";
+        ALOGI("Could not start Binder recording. Another is already in progress.");
         return INVALID_OPERATION;
     } else {
         status_t readStatus = data.readUniqueFileDescriptor(&(e->mRecordingFd));
@@ -316,7 +315,7 @@
             return readStatus;
         }
         mRecordingOn = true;
-        LOG(INFO) << "Started Binder recording.";
+        ALOGI("Started Binder recording.");
         return NO_ERROR;
     }
 }
@@ -340,10 +339,10 @@
     if (mRecordingOn) {
         e->mRecordingFd.reset();
         mRecordingOn = false;
-        LOG(INFO) << "Stopped Binder recording.";
+        ALOGI("Stopped Binder recording.");
         return NO_ERROR;
     } else {
-        LOG(INFO) << "Could not stop Binder recording. One is not in progress.";
+        ALOGI("Could not stop Binder recording. One is not in progress.");
         return INVALID_OPERATION;
     }
 }
@@ -377,11 +376,11 @@
             err = stopRecordingTransactions();
             break;
         case EXTENSION_TRANSACTION:
-            CHECK(reply != nullptr);
+            LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr");
             err = reply->writeStrongBinder(getExtension());
             break;
         case DEBUG_PID_TRANSACTION:
-            CHECK(reply != nullptr);
+            LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr");
             err = reply->writeInt32(getDebugPid());
             break;
         case SET_RPC_CLIENT_TRANSACTION: {
@@ -414,10 +413,10 @@
                                 reply ? *reply : emptyReply, err);
             if (transaction) {
                 if (status_t err = transaction->dumpToFile(e->mRecordingFd); err != NO_ERROR) {
-                    LOG(INFO) << "Failed to dump RecordedTransaction to file with error " << err;
+                    ALOGI("Failed to dump RecordedTransaction to file with error %d", err);
                 }
             } else {
-                LOG(INFO) << "Failed to create RecordedTransaction object.";
+                ALOGI("Failed to create RecordedTransaction object.");
             }
         }
     }
@@ -705,7 +704,7 @@
         return status;
     }
     rpcServer->setMaxThreads(binderThreadPoolMaxCount);
-    LOG(INFO) << "RpcBinder: Started Binder debug on " << getInterfaceDescriptor();
+    ALOGI("RpcBinder: Started Binder debug on %s", String8(getInterfaceDescriptor()).c_str());
     rpcServer->start();
     e->mRpcServerLinks.emplace(link);
     LOG_RPC_DETAIL("%s(fd=%d) successful", __PRETTY_FUNCTION__, socketFdForPrint);
@@ -723,20 +722,20 @@
 {
     if (!wasParceled()) {
         if (getExtension()) {
-             ALOGW("Binder %p destroyed with extension attached before being parceled.", this);
+            ALOGW("Binder %p destroyed with extension attached before being parceled.", this);
         }
         if (isRequestingSid()) {
-             ALOGW("Binder %p destroyed when requesting SID before being parceled.", this);
+            ALOGW("Binder %p destroyed when requesting SID before being parceled.", this);
         }
         if (isInheritRt()) {
-             ALOGW("Binder %p destroyed after setInheritRt before being parceled.", this);
+            ALOGW("Binder %p destroyed after setInheritRt before being parceled.", this);
         }
 #ifdef __linux__
         if (getMinSchedulerPolicy() != SCHED_NORMAL) {
-             ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this);
+            ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this);
         }
         if (getMinSchedulerPriority() != 0) {
-             ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this);
+            ALOGW("Binder %p destroyed after setMinSchedulerPolicy before being parceled.", this);
         }
 #endif // __linux__
     }
@@ -752,7 +751,7 @@
 {
     switch (code) {
         case INTERFACE_TRANSACTION:
-            CHECK(reply != nullptr);
+            LOG_ALWAYS_FATAL_IF(reply == nullptr, "reply == nullptr");
             reply->writeString16(getInterfaceDescriptor());
             return NO_ERROR;
 
diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp
index cedd3af..f7b5a55 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -15,10 +15,10 @@
  */
 
 #include <android-base/file.h>
-#include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <binder/Functional.h>
 #include <binder/RecordedTransaction.h>
+#include <inttypes.h>
 #include <sys/mman.h>
 #include <algorithm>
 
@@ -127,18 +127,17 @@
 
     t.mData.mInterfaceName = std::string(String8(interfaceName).c_str());
     if (interfaceName.size() != t.mData.mInterfaceName.size()) {
-        LOG(ERROR) << "Interface Name is not valid. Contains characters that aren't single byte "
-                      "utf-8.";
+        ALOGE("Interface Name is not valid. Contains characters that aren't single byte utf-8.");
         return std::nullopt;
     }
 
     if (t.mSent.setData(dataParcel.data(), dataParcel.dataBufferSize()) != android::NO_ERROR) {
-        LOG(ERROR) << "Failed to set sent parcel data.";
+        ALOGE("Failed to set sent parcel data.");
         return std::nullopt;
     }
 
     if (t.mReply.setData(replyParcel.data(), replyParcel.dataBufferSize()) != android::NO_ERROR) {
-        LOG(ERROR) << "Failed to set reply parcel data.";
+        ALOGE("Failed to set reply parcel data.");
         return std::nullopt;
     }
 
@@ -168,38 +167,37 @@
     const long pageSize = sysconf(_SC_PAGE_SIZE);
     struct stat fileStat;
     if (fstat(fd.get(), &fileStat) != 0) {
-        LOG(ERROR) << "Unable to get file information";
+        ALOGE("Unable to get file information");
         return std::nullopt;
     }
 
     off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
     if (fdCurrentPosition == -1) {
-        LOG(ERROR) << "Invalid offset in file descriptor.";
+        ALOGE("Invalid offset in file descriptor.");
         return std::nullopt;
     }
     do {
         if (fileStat.st_size < (fdCurrentPosition + (off_t)sizeof(ChunkDescriptor))) {
-            LOG(ERROR) << "Not enough file remains to contain expected chunk descriptor";
+            ALOGE("Not enough file remains to contain expected chunk descriptor");
             return std::nullopt;
         }
 
         if (!android::base::ReadFully(fd, &chunk, sizeof(ChunkDescriptor))) {
-            LOG(ERROR) << "Failed to read ChunkDescriptor from fd " << fd.get() << ". "
-                       << strerror(errno);
+            ALOGE("Failed to read ChunkDescriptor from fd %d. %s", fd.get(), strerror(errno));
             return std::nullopt;
         }
         transaction_checksum_t checksum = *reinterpret_cast<transaction_checksum_t*>(&chunk);
 
         fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR);
         if (fdCurrentPosition == -1) {
-            LOG(ERROR) << "Invalid offset in file descriptor.";
+            ALOGE("Invalid offset in file descriptor.");
             return std::nullopt;
         }
         off_t mmapPageAlignedStart = (fdCurrentPosition / pageSize) * pageSize;
         off_t mmapPayloadStartOffset = fdCurrentPosition - mmapPageAlignedStart;
 
         if (chunk.dataSize > kMaxChunkDataSize) {
-            LOG(ERROR) << "Chunk data exceeds maximum size.";
+            ALOGE("Chunk data exceeds maximum size.");
             return std::nullopt;
         }
 
@@ -207,12 +205,12 @@
                 chunk.dataSize + PADDING8(chunk.dataSize) + sizeof(transaction_checksum_t);
 
         if (chunkPayloadSize > (size_t)(fileStat.st_size - fdCurrentPosition)) {
-            LOG(ERROR) << "Chunk payload exceeds remaining file size.";
+            ALOGE("Chunk payload exceeds remaining file size.");
             return std::nullopt;
         }
 
         if (PADDING8(chunkPayloadSize) != 0) {
-            LOG(ERROR) << "Invalid chunk size, not aligned " << chunkPayloadSize;
+            ALOGE("Invalid chunk size, not aligned %zu", chunkPayloadSize);
             return std::nullopt;
         }
 
@@ -228,8 +226,7 @@
                 sizeof(transaction_checksum_t); // Skip chunk descriptor and required mmap
                                                 // page-alignment
         if (payloadMap == MAP_FAILED) {
-            LOG(ERROR) << "Memory mapping failed for fd " << fd.get() << ": " << errno << " "
-                       << strerror(errno);
+            ALOGE("Memory mapping failed for fd %d: %d %s", fd.get(), errno, strerror(errno));
             return std::nullopt;
         }
         for (size_t checksumIndex = 0;
@@ -237,21 +234,21 @@
             checksum ^= payloadMap[checksumIndex];
         }
         if (checksum != 0) {
-            LOG(ERROR) << "Checksum failed.";
+            ALOGE("Checksum failed.");
             return std::nullopt;
         }
 
         fdCurrentPosition = lseek(fd.get(), chunkPayloadSize, SEEK_CUR);
         if (fdCurrentPosition == -1) {
-            LOG(ERROR) << "Invalid offset in file descriptor.";
+            ALOGE("Invalid offset in file descriptor.");
             return std::nullopt;
         }
 
         switch (chunk.chunkType) {
             case HEADER_CHUNK: {
                 if (chunk.dataSize != static_cast<uint32_t>(sizeof(TransactionHeader))) {
-                    LOG(ERROR) << "Header Chunk indicated size " << chunk.dataSize << "; Expected "
-                               << sizeof(TransactionHeader) << ".";
+                    ALOGE("Header Chunk indicated size %" PRIu32 "; Expected %zu.", chunk.dataSize,
+                          sizeof(TransactionHeader));
                     return std::nullopt;
                 }
                 t.mData.mHeader = *reinterpret_cast<TransactionHeader*>(payloadMap);
@@ -265,7 +262,7 @@
             case DATA_PARCEL_CHUNK: {
                 if (t.mSent.setData(reinterpret_cast<const unsigned char*>(payloadMap),
                                     chunk.dataSize) != android::NO_ERROR) {
-                    LOG(ERROR) << "Failed to set sent parcel data.";
+                    ALOGE("Failed to set sent parcel data.");
                     return std::nullopt;
                 }
                 break;
@@ -273,7 +270,7 @@
             case REPLY_PARCEL_CHUNK: {
                 if (t.mReply.setData(reinterpret_cast<const unsigned char*>(payloadMap),
                                      chunk.dataSize) != android::NO_ERROR) {
-                    LOG(ERROR) << "Failed to set reply parcel data.";
+                    ALOGE("Failed to set reply parcel data.");
                     return std::nullopt;
                 }
                 break;
@@ -281,7 +278,7 @@
             case END_CHUNK:
                 break;
             default:
-                LOG(INFO) << "Unrecognized chunk.";
+                ALOGI("Unrecognized chunk.");
                 break;
         }
     } while (chunk.chunkType != END_CHUNK);
@@ -292,7 +289,7 @@
 android::status_t RecordedTransaction::writeChunk(borrowed_fd fd, uint32_t chunkType,
                                                   size_t byteCount, const uint8_t* data) const {
     if (byteCount > kMaxChunkDataSize) {
-        LOG(ERROR) << "Chunk data exceeds maximum size";
+        ALOGE("Chunk data exceeds maximum size");
         return BAD_VALUE;
     }
     ChunkDescriptor descriptor = {.chunkType = chunkType,
@@ -321,7 +318,7 @@
 
     // Write buffer to file
     if (!android::base::WriteFully(fd, buffer.data(), buffer.size())) {
-        LOG(ERROR) << "Failed to write chunk fd " << fd.get();
+        ALOGE("Failed to write chunk fd %d", fd.get());
         return UNKNOWN_ERROR;
     }
     return NO_ERROR;
@@ -331,26 +328,26 @@
     if (NO_ERROR !=
         writeChunk(fd, HEADER_CHUNK, sizeof(TransactionHeader),
                    reinterpret_cast<const uint8_t*>(&(mData.mHeader)))) {
-        LOG(ERROR) << "Failed to write transactionHeader to fd " << fd.get();
+        ALOGE("Failed to write transactionHeader to fd %d", fd.get());
         return UNKNOWN_ERROR;
     }
     if (NO_ERROR !=
         writeChunk(fd, INTERFACE_NAME_CHUNK, mData.mInterfaceName.size() * sizeof(uint8_t),
                    reinterpret_cast<const uint8_t*>(mData.mInterfaceName.c_str()))) {
-        LOG(INFO) << "Failed to write Interface Name Chunk to fd " << fd.get();
+        ALOGI("Failed to write Interface Name Chunk to fd %d", fd.get());
         return UNKNOWN_ERROR;
     }
 
     if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataBufferSize(), mSent.data())) {
-        LOG(ERROR) << "Failed to write sent Parcel to fd " << fd.get();
+        ALOGE("Failed to write sent Parcel to fd %d", fd.get());
         return UNKNOWN_ERROR;
     }
     if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataBufferSize(), mReply.data())) {
-        LOG(ERROR) << "Failed to write reply Parcel to fd " << fd.get();
+        ALOGE("Failed to write reply Parcel to fd %d", fd.get());
         return UNKNOWN_ERROR;
     }
     if (NO_ERROR != writeChunk(fd, END_CHUNK, 0, NULL)) {
-        LOG(ERROR) << "Failed to write end chunk to fd " << fd.get();
+        ALOGE("Failed to write end chunk to fd %d", fd.get());
         return UNKNOWN_ERROR;
     }
     return NO_ERROR;
diff --git a/libs/binder/RpcTrusty.cpp b/libs/binder/RpcTrusty.cpp
index 3b53b05..2f2fe7d 100644
--- a/libs/binder/RpcTrusty.cpp
+++ b/libs/binder/RpcTrusty.cpp
@@ -16,7 +16,6 @@
 
 #define LOG_TAG "RpcTrusty"
 
-#include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <binder/RpcSession.h>
 #include <binder/RpcTransportTipcAndroid.h>
@@ -35,13 +34,13 @@
     auto request = [=] {
         int tipcFd = tipc_connect(device, port);
         if (tipcFd < 0) {
-            LOG(ERROR) << "Failed to connect to Trusty service. Error code: " << tipcFd;
+            ALOGE("Failed to connect to Trusty service. Error code: %d", tipcFd);
             return unique_fd();
         }
         return unique_fd(tipcFd);
     };
     if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
-        LOG(ERROR) << "Failed to set up Trusty client. Error: " << statusToString(status).c_str();
+        ALOGE("Failed to set up Trusty client. Error: %s", statusToString(status).c_str());
         return nullptr;
     }
     return session;
diff --git a/libs/binder/Utils.cpp b/libs/binder/Utils.cpp
index 47fd17d..d9a96af 100644
--- a/libs/binder/Utils.cpp
+++ b/libs/binder/Utils.cpp
@@ -16,7 +16,6 @@
 
 #include "Utils.h"
 
-#include <android-base/logging.h>
 #include <string.h>
 
 namespace android {
@@ -26,7 +25,7 @@
 }
 
 std::string HexString(const void* bytes, size_t len) {
-    CHECK(bytes != nullptr || len == 0) << bytes << " " << len;
+    LOG_ALWAYS_FATAL_IF(len > 0 && bytes == nullptr, "%p %zu", bytes, len);
 
     // b/132916539: Doing this the 'C way', std::setfill triggers ubsan implicit conversion
     const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h
index c8431aa..b8aaf67 100644
--- a/libs/binder/Utils.h
+++ b/libs/binder/Utils.h
@@ -22,8 +22,16 @@
 #include <log/log.h>
 #include <utils/Errors.h>
 
-#define PLOGE_VA_ARGS(...) , ##__VA_ARGS__
-#define PLOGE(fmt, ...) ALOGE(fmt ": %s" PLOGE_VA_ARGS(__VA_ARGS__), strerror(errno))
+#define PLOGE(fmt, ...)                                                     \
+    do {                                                                    \
+        auto savedErrno = errno;                                            \
+        ALOGE(fmt ": %s" __VA_OPT__(, ) __VA_ARGS__, strerror(savedErrno)); \
+    } while (0)
+#define PLOGF(fmt, ...)                                                                \
+    do {                                                                               \
+        auto savedErrno = errno;                                                       \
+        LOG_ALWAYS_FATAL(fmt ": %s" __VA_OPT__(, ) __VA_ARGS__, strerror(savedErrno)); \
+    } while (0)
 
 /* TEMP_FAILURE_RETRY is not available on macOS and Trusty. */
 #ifndef TEMP_FAILURE_RETRY
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index f51cd9b..118409e 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -16,7 +16,6 @@
 
 #include <binder_rpc_unstable.hpp>
 
-#include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <android/binder_libbinder.h>
 #include <binder/RpcServer.h>
@@ -85,8 +84,8 @@
     }
 
     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();
+        ALOGE("Failed to set up vsock server with port %u error: %s", port,
+              statusToString(status).c_str());
         return nullptr;
     }
     if (cid != VMADDR_CID_ANY) {
@@ -95,7 +94,7 @@
             const sockaddr_vm* vaddr = reinterpret_cast<const sockaddr_vm*>(addr);
             LOG_ALWAYS_FATAL_IF(vaddr->svm_family != AF_VSOCK, "address is not a vsock");
             if (cid != vaddr->svm_cid) {
-                LOG(ERROR) << "Rejected vsock connection from CID " << vaddr->svm_cid;
+                ALOGE("Rejected vsock connection from CID %u", vaddr->svm_cid);
                 return false;
             }
             return true;
@@ -109,12 +108,12 @@
     auto server = RpcServer::make();
     auto fd = unique_fd(socketFd);
     if (!fd.ok()) {
-        LOG(ERROR) << "Invalid socket fd " << socketFd;
+        ALOGE("Invalid socket fd %d", socketFd);
         return nullptr;
     }
     if (status_t status = server->setupRawSocketServer(std::move(fd)); status != OK) {
-        LOG(ERROR) << "Failed to set up RPC server with fd " << socketFd
-                   << " error: " << statusToString(status).c_str();
+        ALOGE("Failed to set up RPC server with fd %d error: %s", socketFd,
+              statusToString(status).c_str());
         return nullptr;
     }
     server->setRootObject(AIBinder_toPlatformBinder(service));
@@ -125,13 +124,13 @@
     auto server = RpcServer::make();
     auto fd = unique_fd(bootstrapFd);
     if (!fd.ok()) {
-        LOG(ERROR) << "Invalid bootstrap fd " << bootstrapFd;
+        ALOGE("Invalid bootstrap fd %d", bootstrapFd);
         return nullptr;
     }
     if (status_t status = server->setupUnixDomainSocketBootstrapServer(std::move(fd));
         status != OK) {
-        LOG(ERROR) << "Failed to set up Unix Domain RPC server with bootstrap fd " << bootstrapFd
-                   << " error: " << statusToString(status).c_str();
+        ALOGE("Failed to set up Unix Domain RPC server with bootstrap fd %d error: %s", bootstrapFd,
+              statusToString(status).c_str());
         return nullptr;
     }
     server->setRootObject(AIBinder_toPlatformBinder(service));
@@ -141,8 +140,8 @@
 ARpcServer* ARpcServer_newInet(AIBinder* service, const char* address, unsigned int port) {
     auto server = RpcServer::make();
     if (status_t status = server->setupInetServer(address, port, nullptr); status != OK) {
-        LOG(ERROR) << "Failed to set up inet RPC server with address " << address << " and port "
-                   << port << " error: " << statusToString(status).c_str();
+        ALOGE("Failed to set up inet RPC server with address %s and port %u error: %s", address,
+              port, statusToString(status).c_str());
         return nullptr;
     }
     server->setRootObject(AIBinder_toPlatformBinder(service));
@@ -191,8 +190,8 @@
 AIBinder* ARpcSession_setupVsockClient(ARpcSession* handle, unsigned int cid, unsigned int port) {
     auto session = handleToStrongPointer<RpcSession>(handle);
     if (status_t status = session->setupVsockClient(cid, port); status != OK) {
-        LOG(ERROR) << "Failed to set up vsock client with CID " << cid << " and port " << port
-                   << " error: " << statusToString(status).c_str();
+        ALOGE("Failed to set up vsock client with CID %u and port %u error: %s", cid, port,
+              statusToString(status).c_str());
         return nullptr;
     }
     return AIBinder_fromPlatformBinder(session->getRootObject());
@@ -203,8 +202,8 @@
     pathname = ANDROID_SOCKET_DIR "/" + pathname;
     auto session = handleToStrongPointer<RpcSession>(handle);
     if (status_t status = session->setupUnixDomainClient(pathname.c_str()); status != OK) {
-        LOG(ERROR) << "Failed to set up Unix Domain RPC client with path: " << pathname
-                   << " error: " << statusToString(status).c_str();
+        ALOGE("Failed to set up Unix Domain RPC client with path: %s error: %s", pathname.c_str(),
+              statusToString(status).c_str());
         return nullptr;
     }
     return AIBinder_fromPlatformBinder(session->getRootObject());
@@ -214,13 +213,13 @@
     auto session = handleToStrongPointer<RpcSession>(handle);
     auto fd = unique_fd(dup(bootstrapFd));
     if (!fd.ok()) {
-        LOG(ERROR) << "Invalid bootstrap fd " << bootstrapFd;
+        ALOGE("Invalid bootstrap fd %d", bootstrapFd);
         return nullptr;
     }
     if (status_t status = session->setupUnixDomainSocketBootstrapClient(std::move(fd));
         status != OK) {
-        LOG(ERROR) << "Failed to set up Unix Domain RPC client with bootstrap fd: " << bootstrapFd
-                   << " error: " << statusToString(status).c_str();
+        ALOGE("Failed to set up Unix Domain RPC client with bootstrap fd: %d error: %s",
+              bootstrapFd, statusToString(status).c_str());
         return nullptr;
     }
     return AIBinder_fromPlatformBinder(session->getRootObject());
@@ -229,8 +228,8 @@
 AIBinder* ARpcSession_setupInet(ARpcSession* handle, const char* address, unsigned int port) {
     auto session = handleToStrongPointer<RpcSession>(handle);
     if (status_t status = session->setupInetClient(address, port); status != OK) {
-        LOG(ERROR) << "Failed to set up inet RPC client with address " << address << " and port "
-                   << port << " error: " << statusToString(status).c_str();
+        ALOGE("Failed to set up inet RPC client with address %s and port %u error: %s", address,
+              port, statusToString(status).c_str());
         return nullptr;
     }
     return AIBinder_fromPlatformBinder(session->getRootObject());
@@ -241,7 +240,7 @@
     auto session = handleToStrongPointer<RpcSession>(handle);
     auto request = [=] { return unique_fd{requestFd(param)}; };
     if (status_t status = session->setupPreconnectedClient(unique_fd{}, request); status != OK) {
-        LOG(ERROR) << "Failed to set up vsock client. error: " << statusToString(status).c_str();
+        ALOGE("Failed to set up vsock client. error: %s", statusToString(status).c_str());
         return nullptr;
     }
     return AIBinder_fromPlatformBinder(session->getRootObject());
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 47da296..bf7a0ba 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <android-base/logging.h>
 #include <android/binder_ibinder.h>
 #include <android/binder_ibinder_platform.h>
 #include <android/binder_stability.h>
@@ -48,8 +47,8 @@
 void clean(const void* /*id*/, void* /*obj*/, void* /*cookie*/){/* do nothing */};
 
 static void attach(const sp<IBinder>& binder) {
-    // can only attach once
-    CHECK_EQ(nullptr, binder->attachObject(kId, kValue, nullptr /*cookie*/, clean));
+    auto alreadyAttached = binder->attachObject(kId, kValue, nullptr /*cookie*/, clean);
+    LOG_ALWAYS_FATAL_IF(alreadyAttached != nullptr, "can only attach once");
 }
 static bool has(const sp<IBinder>& binder) {
     return binder != nullptr && binder->findObject(kId) == kValue;
@@ -65,9 +64,9 @@
 };
 void clean(const void* id, void* obj, void* cookie) {
     // be weary of leaks!
-    // LOG(INFO) << "Deleting an ABpBinder";
+    // ALOGI("Deleting an ABpBinder");
 
-    CHECK(id == kId) << id << " " << obj << " " << cookie;
+    LOG_ALWAYS_FATAL_IF(id != kId, "%p %p %p", id, obj, cookie);
 
     delete static_cast<Value*>(obj);
 };
@@ -121,14 +120,13 @@
     if (mClazz != nullptr && !asABpBinder()) {
         const String16& currentDescriptor = mClazz->getInterfaceDescriptor();
         if (newDescriptor == currentDescriptor) {
-            LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor
-                       << "' match during associateClass, but they are different class objects ("
-                       << clazz << " vs " << mClazz << "). Class descriptor collision?";
+            ALOGE("Class descriptors '%s' match during associateClass, but they are different class"
+                  " objects (%p vs %p). Class descriptor collision?",
+                  String8(currentDescriptor).c_str(), clazz, mClazz);
         } else {
-            LOG(ERROR) << __func__
-                       << ": Class cannot be associated on object which already has a class. "
-                          "Trying to associate to '"
-                       << newDescriptor << "' but already set to '" << currentDescriptor << "'.";
+            ALOGE("%s: Class cannot be associated on object which already has a class. "
+                  "Trying to associate to '%s' but already set to '%s'.",
+                  __func__, String8(newDescriptor).c_str(), String8(currentDescriptor).c_str());
         }
 
         // always a failure because we know mClazz != clazz
@@ -141,13 +139,12 @@
     // more flake-proof. However, the check is not dependent on the lock.
     if (descriptor != newDescriptor && !(asABpBinder() && asABpBinder()->isServiceFuzzing())) {
         if (getBinder()->isBinderAlive()) {
-            LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor
-                       << "' but descriptor is actually '" << SanitizeString(descriptor) << "'.";
+            ALOGE("%s: Expecting binder to have class '%s' but descriptor is actually '%s'.",
+                  __func__, String8(newDescriptor).c_str(), SanitizeString(descriptor).c_str());
         } else {
             // b/155793159
-            LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor
-                       << "' to dead binder with cached descriptor '" << SanitizeString(descriptor)
-                       << "'.";
+            ALOGE("%s: Cannot associate class '%s' to dead binder with cached descriptor '%s'.",
+                  __func__, String8(newDescriptor).c_str(), SanitizeString(descriptor).c_str());
         }
         return false;
     }
@@ -164,7 +161,7 @@
 
 ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData)
     : AIBinder(clazz), BBinder(), mUserData(userData) {
-    CHECK(clazz != nullptr);
+    LOG_ALWAYS_FATAL_IF(clazz == nullptr, "clazz == nullptr");
 }
 ABBinder::~ABBinder() {
     getClass()->onDestroy(mUserData);
@@ -184,7 +181,7 @@
     // technically UINT32_MAX would be okay here, but INT32_MAX is expected since this may be
     // null in Java
     if (args.size() > INT32_MAX) {
-        LOG(ERROR) << "ABBinder::dump received too many arguments: " << args.size();
+        ALOGE("ABBinder::dump received too many arguments: %zu", args.size());
         return STATUS_BAD_VALUE;
     }
 
@@ -263,7 +260,7 @@
 
 ABpBinder::ABpBinder(const ::android::sp<::android::IBinder>& binder)
     : AIBinder(nullptr /*clazz*/), mRemote(binder) {
-    CHECK(binder != nullptr);
+    LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr");
 }
 ABpBinder::~ABpBinder() {}
 
@@ -373,27 +370,27 @@
 }
 
 void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) {
-    CHECK(clazz != nullptr) << "setOnDump requires non-null clazz";
+    LOG_ALWAYS_FATAL_IF(clazz == nullptr, "setOnDump requires non-null clazz");
 
     // this is required to be called before instances are instantiated
     clazz->onDump = onDump;
 }
 
 void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) {
-    CHECK(clazz != nullptr) << "disableInterfaceTokenHeader requires non-null clazz";
+    LOG_ALWAYS_FATAL_IF(clazz == nullptr, "disableInterfaceTokenHeader requires non-null clazz");
 
     clazz->writeHeader = false;
 }
 
 void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz,
                                           AIBinder_handleShellCommand handleShellCommand) {
-    CHECK(clazz != nullptr) << "setHandleShellCommand requires non-null clazz";
+    LOG_ALWAYS_FATAL_IF(clazz == nullptr, "setHandleShellCommand requires non-null clazz");
 
     clazz->handleShellCommand = handleShellCommand;
 }
 
 const char* AIBinder_Class_getDescriptor(const AIBinder_Class* clazz) {
-    CHECK(clazz != nullptr) << "getDescriptor requires non-null clazz";
+    LOG_ALWAYS_FATAL_IF(clazz == nullptr, "getDescriptor requires non-null clazz");
 
     return clazz->getInterfaceDescriptorUtf8();
 }
@@ -405,8 +402,8 @@
 }
 
 void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
-    CHECK(who == mWho) << who.unsafe_get() << "(" << who.get_refs() << ") vs " << mWho.unsafe_get()
-                       << " (" << mWho.get_refs() << ")";
+    LOG_ALWAYS_FATAL_IF(who != mWho, "%p (%p) vs %p (%p)", who.unsafe_get(), who.get_refs(),
+                        mWho.unsafe_get(), mWho.get_refs());
 
     mOnDied(mCookie);
 
@@ -417,7 +414,7 @@
     if (recipient != nullptr && strongWho != nullptr) {
         status_t result = recipient->unlinkToDeath(strongWho, mCookie);
         if (result != ::android::DEAD_OBJECT) {
-            LOG(WARNING) << "Unlinking to dead binder resulted in: " << result;
+            ALOGW("Unlinking to dead binder resulted in: %d", result);
         }
     }
 
@@ -426,7 +423,7 @@
 
 AIBinder_DeathRecipient::AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied)
     : mOnDied(onDied), mOnUnlinked(nullptr) {
-    CHECK(onDied != nullptr);
+    LOG_ALWAYS_FATAL_IF(onDied == nullptr, "onDied == nullptr");
 }
 
 void AIBinder_DeathRecipient::pruneDeadTransferEntriesLocked() {
@@ -438,7 +435,7 @@
 }
 
 binder_status_t AIBinder_DeathRecipient::linkToDeath(const sp<IBinder>& binder, void* cookie) {
-    CHECK(binder != nullptr);
+    LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr");
 
     std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
 
@@ -459,7 +456,7 @@
 }
 
 binder_status_t AIBinder_DeathRecipient::unlinkToDeath(const sp<IBinder>& binder, void* cookie) {
-    CHECK(binder != nullptr);
+    LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr");
 
     std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
 
@@ -471,9 +468,8 @@
 
             status_t status = binder->unlinkToDeath(recipient, cookie, 0 /*flags*/);
             if (status != ::android::OK) {
-                LOG(ERROR) << __func__
-                           << ": removed reference to death recipient but unlink failed: "
-                           << statusToString(status);
+                ALOGE("%s: removed reference to death recipient but unlink failed: %s", __func__,
+                      statusToString(status).c_str());
             }
             return PruneStatusT(status);
         }
@@ -490,7 +486,7 @@
 
 AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args) {
     if (clazz == nullptr) {
-        LOG(ERROR) << __func__ << ": Must provide class to construct local binder.";
+        ALOGE("%s: Must provide class to construct local binder.", __func__);
         return nullptr;
     }
 
@@ -554,8 +550,7 @@
 binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
                                      void* cookie) {
     if (binder == nullptr || recipient == nullptr) {
-        LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient ("
-                   << recipient << ")";
+        ALOGE("%s: Must provide binder (%p) and recipient (%p)", __func__, binder, recipient);
         return STATUS_UNEXPECTED_NULL;
     }
 
@@ -566,8 +561,7 @@
 binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
                                        void* cookie) {
     if (binder == nullptr || recipient == nullptr) {
-        LOG(ERROR) << __func__ << ": Must provide binder (" << binder << ") and recipient ("
-                   << recipient << ")";
+        ALOGE("%s: Must provide binder (%p) and recipient (%p)", __func__, binder, recipient);
         return STATUS_UNEXPECTED_NULL;
     }
 
@@ -596,7 +590,7 @@
 }
 void AIBinder_decStrong(AIBinder* binder) {
     if (binder == nullptr) {
-        LOG(ERROR) << __func__ << ": on null binder";
+        ALOGE("%s: on null binder", __func__);
         return;
     }
 
@@ -604,7 +598,7 @@
 }
 int32_t AIBinder_debugGetRefCount(AIBinder* binder) {
     if (binder == nullptr) {
-        LOG(ERROR) << __func__ << ": on null binder";
+        ALOGE("%s: on null binder", __func__);
         return -1;
     }
 
@@ -642,15 +636,14 @@
 
 binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) {
     if (binder == nullptr || in == nullptr) {
-        LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder
-                   << ") and in (" << in << ").";
+        ALOGE("%s: requires non-null parameters binder (%p) and in (%p).", __func__, binder, in);
         return STATUS_UNEXPECTED_NULL;
     }
     const AIBinder_Class* clazz = binder->getClass();
     if (clazz == nullptr) {
-        LOG(ERROR) << __func__
-                   << ": Class must be defined for a remote binder transaction. See "
-                      "AIBinder_associateClass.";
+        ALOGE("%s: Class must be defined for a remote binder transaction. See "
+              "AIBinder_associateClass.",
+              __func__);
         return STATUS_INVALID_OPERATION;
     }
 
@@ -683,7 +676,7 @@
 binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, AParcel** in,
                                   AParcel** out, binder_flags_t flags) {
     if (in == nullptr) {
-        LOG(ERROR) << __func__ << ": requires non-null in parameter";
+        ALOGE("%s: requires non-null in parameter", __func__);
         return STATUS_UNEXPECTED_NULL;
     }
 
@@ -693,27 +686,26 @@
     AutoParcelDestroyer forIn(in, DestroyParcel);
 
     if (!isUserCommand(code)) {
-        LOG(ERROR) << __func__
-                   << ": Only user-defined transactions can be made from the NDK, but requested: "
-                   << code;
+        ALOGE("%s: Only user-defined transactions can be made from the NDK, but requested: %d",
+              __func__, code);
         return STATUS_UNKNOWN_TRANSACTION;
     }
 
     constexpr binder_flags_t kAllFlags = FLAG_PRIVATE_VENDOR | FLAG_ONEWAY | FLAG_CLEAR_BUF;
     if ((flags & ~kAllFlags) != 0) {
-        LOG(ERROR) << __func__ << ": Unrecognized flags sent: " << flags;
+        ALOGE("%s: Unrecognized flags sent: %d", __func__, flags);
         return STATUS_BAD_VALUE;
     }
 
     if (binder == nullptr || *in == nullptr || out == nullptr) {
-        LOG(ERROR) << __func__ << ": requires non-null parameters binder (" << binder << "), in ("
-                   << in << "), and out (" << out << ").";
+        ALOGE("%s: requires non-null parameters binder (%p), in (%p), and out (%p).", __func__,
+              binder, in, out);
         return STATUS_UNEXPECTED_NULL;
     }
 
     if ((*in)->getBinder() != binder) {
-        LOG(ERROR) << __func__ << ": parcel is associated with binder object " << binder
-                   << " but called with " << (*in)->getBinder();
+        ALOGE("%s: parcel is associated with binder object %p but called with %p", __func__, binder,
+              (*in)->getBinder());
         return STATUS_BAD_VALUE;
     }
 
@@ -733,7 +725,7 @@
 AIBinder_DeathRecipient* AIBinder_DeathRecipient_new(
         AIBinder_DeathRecipient_onBinderDied onBinderDied) {
     if (onBinderDied == nullptr) {
-        LOG(ERROR) << __func__ << ": requires non-null onBinderDied parameter.";
+        ALOGE("%s: requires non-null onBinderDied parameter.", __func__);
         return nullptr;
     }
     auto ret = new AIBinder_DeathRecipient(onBinderDied);
@@ -799,9 +791,8 @@
 
 void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid) {
     ABBinder* localBinder = binder->asABBinder();
-    if (localBinder == nullptr) {
-        LOG(FATAL) << "AIBinder_setRequestingSid must be called on a local binder";
-    }
+    LOG_ALWAYS_FATAL_IF(localBinder == nullptr,
+                        "AIBinder_setRequestingSid must be called on a local binder");
 
     localBinder->setRequestingSid(requestingSid);
 }
@@ -816,9 +807,8 @@
 
 void AIBinder_setInheritRt(AIBinder* binder, bool inheritRt) {
     ABBinder* localBinder = binder->asABBinder();
-    if (localBinder == nullptr) {
-        LOG(FATAL) << "AIBinder_setInheritRt must be called on a local binder";
-    }
+    LOG_ALWAYS_FATAL_IF(localBinder == nullptr,
+                        "AIBinder_setInheritRt must be called on a local binder");
 
     localBinder->setInheritRt(inheritRt);
 }
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 037aa2e..c15bcf9 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -14,20 +14,19 @@
  * limitations under the License.
  */
 
+#include <android-base/unique_fd.h>
 #include <android/binder_parcel.h>
 #include <android/binder_parcel_platform.h>
-#include "parcel_internal.h"
-
-#include "ibinder_internal.h"
-#include "status_internal.h"
+#include <binder/Parcel.h>
+#include <binder/ParcelFileDescriptor.h>
+#include <inttypes.h>
+#include <utils/Unicode.h>
 
 #include <limits>
 
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <binder/Parcel.h>
-#include <binder/ParcelFileDescriptor.h>
-#include <utils/Unicode.h>
+#include "ibinder_internal.h"
+#include "parcel_internal.h"
+#include "status_internal.h"
 
 using ::android::IBinder;
 using ::android::Parcel;
@@ -52,11 +51,11 @@
     if (length < -1) return STATUS_BAD_VALUE;
 
     if (!isNullArray && length < 0) {
-        LOG(ERROR) << __func__ << ": non-null array but length is " << length;
+        ALOGE("non-null array but length is %" PRIi32, length);
         return STATUS_BAD_VALUE;
     }
     if (isNullArray && length > 0) {
-        LOG(ERROR) << __func__ << ": null buffer cannot be for size " << length << " array.";
+        ALOGE("null buffer cannot be for size %" PRIi32 " array.", length);
         return STATUS_BAD_VALUE;
     }
 
@@ -325,7 +324,7 @@
 binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t length) {
     if (string == nullptr) {
         if (length != -1) {
-            LOG(WARNING) << __func__ << ": null string must be used with length == -1.";
+            ALOGW("null string must be used with length == -1.");
             return STATUS_BAD_VALUE;
         }
 
@@ -334,7 +333,7 @@
     }
 
     if (length < 0) {
-        LOG(WARNING) << __func__ << ": Negative string length: " << length;
+        ALOGW("Negative string length: %" PRIi32, length);
         return STATUS_BAD_VALUE;
     }
 
@@ -342,7 +341,7 @@
     const ssize_t len16 = utf8_to_utf16_length(str8, length);
 
     if (len16 < 0 || len16 >= std::numeric_limits<int32_t>::max()) {
-        LOG(WARNING) << __func__ << ": Invalid string length: " << len16;
+        ALOGW("Invalid string length: %zd", len16);
         return STATUS_BAD_VALUE;
     }
 
@@ -383,7 +382,7 @@
     }
 
     if (len8 <= 0 || len8 > std::numeric_limits<int32_t>::max()) {
-        LOG(WARNING) << __func__ << ": Invalid string length: " << len8;
+        ALOGW("Invalid string length: %zd", len8);
         return STATUS_BAD_VALUE;
     }
 
@@ -391,7 +390,7 @@
     bool success = allocator(stringData, len8, &str8);
 
     if (!success || str8 == nullptr) {
-        LOG(WARNING) << __func__ << ": AParcel_stringAllocator failed to allocate.";
+        ALOGW("AParcel_stringAllocator failed to allocate.");
         return STATUS_NO_MEMORY;
     }
 
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp
index 0fea57b..0072ac3 100644
--- a/libs/binder/ndk/process.cpp
+++ b/libs/binder/ndk/process.cpp
@@ -15,12 +15,10 @@
  */
 
 #include <android/binder_process.h>
+#include <binder/IPCThreadState.h>
 
 #include <mutex>
 
-#include <android-base/logging.h>
-#include <binder/IPCThreadState.h>
-
 using ::android::IPCThreadState;
 using ::android::ProcessState;
 
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index 2977786..3bfdc59 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -15,14 +15,12 @@
  */
 
 #include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
+#include <binder/LazyServiceRegistrar.h>
 
 #include "ibinder_internal.h"
 #include "status_internal.h"
 
-#include <android-base/logging.h>
-#include <binder/IServiceManager.h>
-#include <binder/LazyServiceRegistrar.h>
-
 using ::android::defaultServiceManager;
 using ::android::IBinder;
 using ::android::IServiceManager;
@@ -115,7 +113,8 @@
         std::lock_guard<std::mutex> l(m);
         if (onRegister == nullptr) return;
 
-        CHECK_EQ(String8(smInstance), instance);
+        LOG_ALWAYS_FATAL_IF(String8(smInstance) != instance, "onServiceRegistration: %s != %s",
+                            String8(smInstance).c_str(), instance);
 
         sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder);
         AIBinder_incStrong(ret.get());
@@ -135,8 +134,8 @@
 AServiceManager_registerForServiceNotifications(const char* instance,
                                                 AServiceManager_onRegister onRegister,
                                                 void* cookie) {
-    CHECK_NE(instance, nullptr);
-    CHECK_NE(onRegister, nullptr) << instance;
+    LOG_ALWAYS_FATAL_IF(instance == nullptr, "instance == nullptr");
+    LOG_ALWAYS_FATAL_IF(onRegister == nullptr, "onRegister == nullptr for %s", instance);
     // cookie can be nullptr
 
     auto cb = sp<AServiceManager_NotificationRegistration>::make();
@@ -146,8 +145,8 @@
 
     sp<IServiceManager> sm = defaultServiceManager();
     if (status_t res = sm->registerForNotifications(String16(instance), cb); res != STATUS_OK) {
-        LOG(ERROR) << "Failed to register for service notifications for " << instance << ": "
-                   << statusToString(res);
+        ALOGE("Failed to register for service notifications for %s: %s", instance,
+              statusToString(res).c_str());
         return nullptr;
     }
 
@@ -157,7 +156,7 @@
 
 void AServiceManager_NotificationRegistration_delete(
         AServiceManager_NotificationRegistration* notification) {
-    CHECK_NE(notification, nullptr);
+    LOG_ALWAYS_FATAL_IF(notification == nullptr, "notification == nullptr");
     notification->clear();
     notification->decStrong(nullptr);
 }
@@ -172,9 +171,9 @@
 }
 void AServiceManager_forEachDeclaredInstance(const char* interface, void* context,
                                              void (*callback)(const char*, void*)) {
-    CHECK(interface != nullptr);
+    LOG_ALWAYS_FATAL_IF(interface == nullptr, "interface == nullptr");
     // context may be nullptr
-    CHECK(callback != nullptr);
+    LOG_ALWAYS_FATAL_IF(callback == nullptr, "callback == nullptr");
 
     sp<IServiceManager> sm = defaultServiceManager();
     for (const String16& instance : sm->getDeclaredInstances(String16(interface))) {
@@ -191,9 +190,9 @@
 }
 void AServiceManager_getUpdatableApexName(const char* instance, void* context,
                                           void (*callback)(const char*, void*)) {
-    CHECK_NE(instance, nullptr);
+    LOG_ALWAYS_FATAL_IF(instance == nullptr, "instance == nullptr");
     // context may be nullptr
-    CHECK_NE(callback, nullptr);
+    LOG_ALWAYS_FATAL_IF(callback == nullptr, "callback == nullptr");
 
     sp<IServiceManager> sm = defaultServiceManager();
     std::optional<String16> updatableViaApex = sm->updatableViaApex(String16(instance));
diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp
index 8ed91a5..3aac3c0 100644
--- a/libs/binder/ndk/status.cpp
+++ b/libs/binder/ndk/status.cpp
@@ -17,8 +17,6 @@
 #include <android/binder_status.h>
 #include "status_internal.h"
 
-#include <android-base/logging.h>
-
 using ::android::status_t;
 using ::android::statusToString;
 using ::android::binder::Status;
@@ -127,8 +125,8 @@
             return STATUS_UNKNOWN_ERROR;
 
         default:
-            LOG(WARNING) << __func__ << ": Unknown status_t (" << statusToString(status)
-                         << ") pruned into STATUS_UNKNOWN_ERROR";
+            ALOGW("%s: Unknown status_t (%s) pruned into STATUS_UNKNOWN_ERROR", __func__,
+                  statusToString(status).c_str());
             return STATUS_UNKNOWN_ERROR;
     }
 }
@@ -159,8 +157,8 @@
             return EX_TRANSACTION_FAILED;
 
         default:
-            LOG(WARNING) << __func__ << ": Unknown binder exception (" << exception
-                         << ") pruned into EX_TRANSACTION_FAILED";
+            ALOGW("%s: Unknown binder exception (%d) pruned into EX_TRANSACTION_FAILED", __func__,
+                  exception);
             return EX_TRANSACTION_FAILED;
     }
 }
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
index 5c688fa..6afe5ab 100644
--- a/libs/binder/rust/src/parcel/file_descriptor.rs
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -22,29 +22,28 @@
 use crate::error::{status_result, Result, StatusCode};
 use crate::sys;
 
-use std::fs::File;
-use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
 
 /// Rust version of the Java class android.os.ParcelFileDescriptor
 #[derive(Debug)]
-pub struct ParcelFileDescriptor(File);
+pub struct ParcelFileDescriptor(OwnedFd);
 
 impl ParcelFileDescriptor {
     /// Create a new `ParcelFileDescriptor`
-    pub fn new(file: File) -> Self {
-        Self(file)
+    pub fn new<F: Into<OwnedFd>>(fd: F) -> Self {
+        Self(fd.into())
     }
 }
 
-impl AsRef<File> for ParcelFileDescriptor {
-    fn as_ref(&self) -> &File {
+impl AsRef<OwnedFd> for ParcelFileDescriptor {
+    fn as_ref(&self) -> &OwnedFd {
         &self.0
     }
 }
 
-impl From<ParcelFileDescriptor> for File {
-    fn from(file: ParcelFileDescriptor) -> File {
-        file.0
+impl From<ParcelFileDescriptor> for OwnedFd {
+    fn from(fd: ParcelFileDescriptor) -> OwnedFd {
+        fd.0
     }
 }
 
@@ -120,7 +119,7 @@
             // Safety: At this point, we know that the file descriptor was
             // not -1, so must be a valid, owned file descriptor which we
             // can safely turn into a `File`.
-            let file = unsafe { File::from_raw_fd(fd) };
+            let file = unsafe { OwnedFd::from_raw_fd(fd) };
             Ok(Some(ParcelFileDescriptor::new(file)))
         }
     }
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index bbb310a..b86eb94 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -87,8 +87,8 @@
                                      android::base::borrowed_fd /* readEnd */)>& f) {
         android::base::unique_fd childWriteEnd;
         android::base::unique_fd childReadEnd;
-        CHECK(android::base::Pipe(&mReadEnd, &childWriteEnd, 0)) << strerror(errno);
-        CHECK(android::base::Pipe(&childReadEnd, &mWriteEnd, 0)) << strerror(errno);
+        if (!android::base::Pipe(&mReadEnd, &childWriteEnd, 0)) PLOGF("child write pipe failed");
+        if (!android::base::Pipe(&childReadEnd, &mWriteEnd, 0)) PLOGF("child read pipe failed");
         if (0 == (mPid = fork())) {
             // racey: assume parent doesn't crash before this is set
             prctl(PR_SET_PDEATHSIG, SIGHUP);
@@ -146,8 +146,10 @@
     auto socket_addr = UnixSocketAddress(addr.c_str());
     base::unique_fd fd(
             TEMP_FAILURE_RETRY(socket(socket_addr.addr()->sa_family, SOCK_STREAM, AF_UNIX)));
-    CHECK(fd.ok());
-    CHECK_EQ(0, TEMP_FAILURE_RETRY(bind(fd.get(), socket_addr.addr(), socket_addr.addrSize())));
+    if (!fd.ok()) PLOGF("initUnixSocket failed to create socket");
+    if (0 != TEMP_FAILURE_RETRY(bind(fd.get(), socket_addr.addr(), socket_addr.addrSize()))) {
+        PLOGF("initUnixSocket failed to bind");
+    }
     return fd;
 }
 
@@ -205,14 +207,12 @@
 static base::unique_fd connectTo(const RpcSocketAddress& addr) {
     base::unique_fd serverFd(
             TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
-    int savedErrno = errno;
-    CHECK(serverFd.ok()) << "Could not create socket " << addr.toString() << ": "
-                         << strerror(savedErrno);
+    if (!serverFd.ok()) {
+        PLOGF("Could not create socket %s", addr.toString().c_str());
+    }
 
     if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
-        int savedErrno = errno;
-        LOG(FATAL) << "Could not connect to socket " << addr.toString() << ": "
-                   << strerror(savedErrno);
+        PLOGF("Could not connect to socket %s", addr.toString().c_str());
     }
     return serverFd;
 }
@@ -221,8 +221,7 @@
 static base::unique_fd connectToUnixBootstrap(const RpcTransportFd& transportFd) {
     base::unique_fd sockClient, sockServer;
     if (!base::Socketpair(SOCK_STREAM, &sockClient, &sockServer)) {
-        int savedErrno = errno;
-        LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno);
+        PLOGF("Failed socketpair()");
     }
 
     int zero = 0;
@@ -231,8 +230,7 @@
     fds.emplace_back(std::move(sockServer));
 
     if (binder::os::sendMessageOnSocket(transportFd, &iov, 1, &fds) < 0) {
-        int savedErrno = errno;
-        LOG(FATAL) << "Failed sendMessageOnSocket: " << strerror(savedErrno);
+        PLOGF("Failed sendMessageOnSocket");
     }
     return std::move(sockClient);
 }
@@ -246,10 +244,12 @@
 // threads.
 std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc(
         const BinderRpcOptions& options) {
-    CHECK_GE(options.numSessions, 1) << "Must have at least one session to a server";
+    LOG_ALWAYS_FATAL_IF(options.numSessions < 1, "Must have at least one session to a server");
 
     if (options.numIncomingConnectionsBySession.size() != 0) {
-        CHECK_EQ(options.numIncomingConnectionsBySession.size(), options.numSessions);
+        LOG_ALWAYS_FATAL_IF(options.numIncomingConnectionsBySession.size() != options.numSessions,
+                            "%s: %zu != %zu", __func__,
+                            options.numIncomingConnectionsBySession.size(), options.numSessions);
     }
 
     SocketType socketType = GetParam().type;
@@ -274,8 +274,7 @@
         // Do not set O_CLOEXEC, bootstrapServerFd needs to survive fork/exec.
         // This is because we cannot pass ParcelFileDescriptor over a pipe.
         if (!base::Socketpair(SOCK_STREAM, &bootstrapClientFd, &socketFd)) {
-            int savedErrno = errno;
-            LOG(FATAL) << "Failed socketpair(): " << strerror(savedErrno);
+            PLOGF("Failed socketpair()");
         }
     }
 
@@ -334,16 +333,16 @@
         }
         writeToFd(ret->host.writeEnd(), clientInfo);
 
-        CHECK_LE(serverInfo.port, std::numeric_limits<unsigned int>::max());
+        LOG_ALWAYS_FATAL_IF(serverInfo.port > std::numeric_limits<unsigned int>::max());
         if (socketType == SocketType::INET) {
-            CHECK_NE(0, serverInfo.port);
+            LOG_ALWAYS_FATAL_IF(0 == serverInfo.port);
         }
 
         if (rpcSecurity == RpcSecurity::TLS) {
             const auto& serverCert = serverInfo.cert.data;
-            CHECK_EQ(OK,
-                     certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
-                                                             serverCert));
+            LOG_ALWAYS_FATAL_IF(
+                    OK !=
+                    certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM, serverCert));
         }
     }
 
@@ -356,7 +355,7 @@
                 ? options.numIncomingConnectionsBySession.at(i)
                 : 0;
 
-        CHECK(session->setProtocolVersion(clientVersion));
+        LOG_ALWAYS_FATAL_IF(!session->setProtocolVersion(clientVersion));
         session->setMaxIncomingThreads(numIncoming);
         session->setMaxOutgoingConnections(options.numOutgoingConnections);
         session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
@@ -408,7 +407,7 @@
             ret->sessions.clear();
             break;
         }
-        CHECK_EQ(status, OK) << "Could not connect: " << statusToString(status);
+        LOG_ALWAYS_FATAL_IF(status != OK, "Could not connect: %s", statusToString(status).c_str());
         ret->sessions.push_back({session, session->getRootObject()});
     }
     return ret;
@@ -589,12 +588,12 @@
     android::os::ParcelFileDescriptor fdA;
     EXPECT_OK(proc.rootIface->blockingRecvFd(&fdA));
     std::string result;
-    CHECK(android::base::ReadFdToString(fdA.get(), &result));
+    ASSERT_TRUE(android::base::ReadFdToString(fdA.get(), &result));
     EXPECT_EQ(result, "a");
 
     android::os::ParcelFileDescriptor fdB;
     EXPECT_OK(proc.rootIface->blockingRecvFd(&fdB));
-    CHECK(android::base::ReadFdToString(fdB.get(), &result));
+    ASSERT_TRUE(android::base::ReadFdToString(fdB.get(), &result));
     EXPECT_EQ(result, "b");
 
     saturateThreadPool(kNumServerThreads, proc.rootIface);
@@ -951,8 +950,8 @@
     ASSERT_TRUE(status.isOk()) << status;
 
     std::string result;
-    CHECK(android::base::ReadFdToString(out.get(), &result));
-    EXPECT_EQ(result, "hello");
+    ASSERT_TRUE(android::base::ReadFdToString(out.get(), &result));
+    ASSERT_EQ(result, "hello");
 }
 
 TEST_P(BinderRpc, SendFiles) {
@@ -981,7 +980,7 @@
     ASSERT_TRUE(status.isOk()) << status;
 
     std::string result;
-    CHECK(android::base::ReadFdToString(out.get(), &result));
+    EXPECT_TRUE(android::base::ReadFdToString(out.get(), &result));
     EXPECT_EQ(result, "123abcd");
 }
 
@@ -1006,7 +1005,7 @@
     ASSERT_TRUE(status.isOk()) << status;
 
     std::string result;
-    CHECK(android::base::ReadFdToString(out.get(), &result));
+    EXPECT_TRUE(android::base::ReadFdToString(out.get(), &result));
     EXPECT_EQ(result, std::string(253, 'a'));
 }
 
@@ -1359,7 +1358,11 @@
 };
 
 TEST(BinderRpc, Java) {
-#if !defined(__ANDROID__)
+    bool expectDebuggable = false;
+#if defined(__ANDROID__)
+    expectDebuggable = android::base::GetBoolProperty("ro.debuggable", false) &&
+            android::base::GetProperty("ro.build.type", "") != "user";
+#else
     GTEST_SKIP() << "This test is only run on Android. Though it can technically run on host on"
                     "createRpcDelegateServiceManager() with a device attached, such test belongs "
                     "to binderHostDeviceTest. Hence, just disable this test on host.";
@@ -1387,8 +1390,7 @@
     auto keepAlive = sp<BBinder>::make();
     auto setRpcClientDebugStatus = binder->setRpcClientDebug(std::move(socket), keepAlive);
 
-    if (!android::base::GetBoolProperty("ro.debuggable", false) ||
-        android::base::GetProperty("ro.build.type", "") == "user") {
+    if (!expectDebuggable) {
         ASSERT_EQ(INVALID_OPERATION, setRpcClientDebugStatus)
                 << "setRpcClientDebug should return INVALID_OPERATION on non-debuggable or user "
                    "builds, but get "
@@ -1593,12 +1595,10 @@
             iovec iov{&buf, sizeof(buf)};
 
             if (binder::os::receiveMessageFromSocket(mFd, &iov, 1, &fds) < 0) {
-                int savedErrno = errno;
-                LOG(FATAL) << "Failed receiveMessage: " << strerror(savedErrno);
+                PLOGF("Failed receiveMessage");
             }
-            if (fds.size() != 1) {
-                LOG(FATAL) << "Expected one FD from receiveMessage(), got " << fds.size();
-            }
+            LOG_ALWAYS_FATAL_IF(fds.size() != 1, "Expected one FD from receiveMessage(), got %zu",
+                                fds.size());
             return std::move(std::get<base::unique_fd>(fds[0]));
         }
 
@@ -2083,7 +2083,7 @@
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
-    android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
+    __android_log_set_logger(__android_log_stderr_logger);
 
     return RUN_ALL_TESTS();
 }
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index c3070dd..b2b63e4 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -36,10 +36,12 @@
 #include <string>
 #include <vector>
 
+#ifdef __ANDROID__
+#include <android-base/properties.h>
+#endif
+
 #ifndef __TRUSTY__
 #include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
 #include <android/binder_auto_utils.h>
 #include <android/binder_libbinder.h>
 #include <binder/ProcessState.h>
@@ -156,21 +158,21 @@
 #ifndef __TRUSTY__
 static inline void writeString(android::base::borrowed_fd fd, std::string_view str) {
     uint64_t length = str.length();
-    CHECK(android::base::WriteFully(fd, &length, sizeof(length)));
-    CHECK(android::base::WriteFully(fd, str.data(), str.length()));
+    LOG_ALWAYS_FATAL_IF(!android::base::WriteFully(fd, &length, sizeof(length)));
+    LOG_ALWAYS_FATAL_IF(!android::base::WriteFully(fd, str.data(), str.length()));
 }
 
 static inline std::string readString(android::base::borrowed_fd fd) {
     uint64_t length;
-    CHECK(android::base::ReadFully(fd, &length, sizeof(length)));
+    LOG_ALWAYS_FATAL_IF(!android::base::ReadFully(fd, &length, sizeof(length)));
     std::string ret(length, '\0');
-    CHECK(android::base::ReadFully(fd, ret.data(), length));
+    LOG_ALWAYS_FATAL_IF(!android::base::ReadFully(fd, ret.data(), length));
     return ret;
 }
 
 static inline void writeToFd(android::base::borrowed_fd fd, const Parcelable& parcelable) {
     Parcel parcel;
-    CHECK_EQ(OK, parcelable.writeToParcel(&parcel));
+    LOG_ALWAYS_FATAL_IF(OK != parcelable.writeToParcel(&parcel));
     writeString(fd, std::string(reinterpret_cast<const char*>(parcel.data()), parcel.dataSize()));
 }
 
@@ -178,9 +180,10 @@
 static inline T readFromFd(android::base::borrowed_fd fd) {
     std::string data = readString(fd);
     Parcel parcel;
-    CHECK_EQ(OK, parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
+    LOG_ALWAYS_FATAL_IF(OK !=
+                        parcel.setData(reinterpret_cast<const uint8_t*>(data.data()), data.size()));
     T object;
-    CHECK_EQ(OK, object.readFromParcel(&parcel));
+    LOG_ALWAYS_FATAL_IF(OK != object.readFromParcel(&parcel));
     return object;
 }
 
@@ -207,7 +210,7 @@
 // Create an FD that returns `contents` when read.
 static inline base::unique_fd mockFileDescriptor(std::string contents) {
     android::base::unique_fd readFd, writeFd;
-    CHECK(android::base::Pipe(&readFd, &writeFd)) << strerror(errno);
+    LOG_ALWAYS_FATAL_IF(!android::base::Pipe(&readFd, &writeFd), "%s", strerror(errno));
     RpcMaybeThread([writeFd = std::move(writeFd), contents = std::move(contents)]() {
         signal(SIGPIPE, SIG_IGN); // ignore possible SIGPIPE from the write
         if (!WriteStringToFd(contents, writeFd)) {
diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp
index 7435f30..5025bd6 100644
--- a/libs/binder/tests/binderRpcTestService.cpp
+++ b/libs/binder/tests/binderRpcTestService.cpp
@@ -65,7 +65,7 @@
         std::string acc;
         for (const auto& file : files) {
             std::string result;
-            CHECK(android::base::ReadFdToString(file.get(), &result));
+            LOG_ALWAYS_FATAL_IF(!android::base::ReadFdToString(file.get(), &result));
             acc.append(result);
         }
         out->reset(mockFileDescriptor(acc));
@@ -98,7 +98,7 @@
 };
 
 int main(int argc, char* argv[]) {
-    android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
+    __android_log_set_logger(__android_log_stderr_logger);
 
     LOG_ALWAYS_FATAL_IF(argc != 3, "Invalid number of arguments: %d", argc);
     base::unique_fd writeEnd(atoi(argv[1]));
@@ -118,7 +118,7 @@
     auto certVerifier = std::make_shared<RpcCertificateVerifierSimple>();
     sp<RpcServer> server = RpcServer::make(newTlsFactory(rpcSecurity, certVerifier));
 
-    CHECK(server->setProtocolVersion(serverConfig.serverVersion));
+    LOG_ALWAYS_FATAL_IF(!server->setProtocolVersion(serverConfig.serverVersion));
     server->setMaxThreads(serverConfig.numThreads);
     server->setSupportedFileDescriptorTransportModes(serverSupportedFileDescriptorTransportModes);
 
@@ -129,22 +129,25 @@
         case SocketType::PRECONNECTED:
             [[fallthrough]];
         case SocketType::UNIX:
-            CHECK_EQ(OK, server->setupUnixDomainServer(serverConfig.addr.c_str()))
-                    << serverConfig.addr;
+            LOG_ALWAYS_FATAL_IF(OK != server->setupUnixDomainServer(serverConfig.addr.c_str()),
+                                "%s", serverConfig.addr.c_str());
             break;
         case SocketType::UNIX_BOOTSTRAP:
-            CHECK_EQ(OK, server->setupUnixDomainSocketBootstrapServer(std::move(socketFd)));
+            LOG_ALWAYS_FATAL_IF(OK !=
+                                server->setupUnixDomainSocketBootstrapServer(std::move(socketFd)));
             break;
         case SocketType::UNIX_RAW:
-            CHECK_EQ(OK, server->setupRawSocketServer(std::move(socketFd)));
+            LOG_ALWAYS_FATAL_IF(OK != server->setupRawSocketServer(std::move(socketFd)));
             break;
         case SocketType::VSOCK:
-            CHECK_EQ(OK, server->setupVsockServer(VMADDR_CID_LOCAL, serverConfig.vsockPort))
-                    << "Need `sudo modprobe vsock_loopback`?";
+            LOG_ALWAYS_FATAL_IF(OK !=
+                                        server->setupVsockServer(VMADDR_CID_LOCAL,
+                                                                 serverConfig.vsockPort),
+                                "Need `sudo modprobe vsock_loopback`?");
             break;
         case SocketType::INET: {
-            CHECK_EQ(OK, server->setupInetServer(kLocalInetAddress, 0, &outPort));
-            CHECK_NE(0, outPort);
+            LOG_ALWAYS_FATAL_IF(OK != server->setupInetServer(kLocalInetAddress, 0, &outPort));
+            LOG_ALWAYS_FATAL_IF(0 == outPort);
             break;
         }
         default:
@@ -159,21 +162,21 @@
 
     if (rpcSecurity == RpcSecurity::TLS) {
         for (const auto& clientCert : clientInfo.certs) {
-            CHECK_EQ(OK,
-                     certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
-                                                             clientCert.data));
+            LOG_ALWAYS_FATAL_IF(OK !=
+                                certVerifier->addTrustedPeerCertificate(RpcCertificateFormat::PEM,
+                                                                        clientCert.data));
         }
     }
 
     server->setPerSessionRootObject([&](wp<RpcSession> session, const void* addrPtr, size_t len) {
         {
             sp<RpcSession> spSession = session.promote();
-            CHECK_NE(nullptr, spSession.get());
+            LOG_ALWAYS_FATAL_IF(nullptr == spSession.get());
         }
 
         // UNIX sockets with abstract addresses return
         // sizeof(sa_family_t)==2 in addrlen
-        CHECK_GE(len, sizeof(sa_family_t));
+        LOG_ALWAYS_FATAL_IF(len < sizeof(sa_family_t));
         const sockaddr* addr = reinterpret_cast<const sockaddr*>(addrPtr);
         sp<MyBinderRpcTestAndroid> service = sp<MyBinderRpcTestAndroid>::make();
         switch (addr->sa_family) {
@@ -181,15 +184,15 @@
                 // nothing to save
                 break;
             case AF_VSOCK:
-                CHECK_EQ(len, sizeof(sockaddr_vm));
+                LOG_ALWAYS_FATAL_IF(len != sizeof(sockaddr_vm));
                 service->port = reinterpret_cast<const sockaddr_vm*>(addr)->svm_port;
                 break;
             case AF_INET:
-                CHECK_EQ(len, sizeof(sockaddr_in));
+                LOG_ALWAYS_FATAL_IF(len != sizeof(sockaddr_in));
                 service->port = ntohs(reinterpret_cast<const sockaddr_in*>(addr)->sin_port);
                 break;
             case AF_INET6:
-                CHECK_EQ(len, sizeof(sockaddr_in));
+                LOG_ALWAYS_FATAL_IF(len != sizeof(sockaddr_in));
                 service->port = ntohs(reinterpret_cast<const sockaddr_in6*>(addr)->sin6_port);
                 break;
             default:
diff --git a/libs/binder/trusty/kernel/rules.mk b/libs/binder/trusty/kernel/rules.mk
index 1f05ef7..d2b37aa 100644
--- a/libs/binder/trusty/kernel/rules.mk
+++ b/libs/binder/trusty/kernel/rules.mk
@@ -24,7 +24,6 @@
 FMTLIB_DIR := external/fmtlib
 
 MODULE_SRCS := \
-	$(LOCAL_DIR)/../logging.cpp \
 	$(LOCAL_DIR)/../TrustyStatus.cpp \
 	$(LIBBINDER_DIR)/Binder.cpp \
 	$(LIBBINDER_DIR)/BpBinder.cpp \
diff --git a/libs/binder/trusty/logging.cpp b/libs/binder/trusty/logging.cpp
deleted file mode 100644
index 88a1075..0000000
--- a/libs/binder/trusty/logging.cpp
+++ /dev/null
@@ -1,165 +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.
- */
-
-#define TLOG_TAG "libbinder"
-
-#include "android-base/logging.h"
-
-#include <trusty_log.h>
-#include <iostream>
-#include <string>
-
-#include <android-base/strings.h>
-
-namespace android {
-namespace base {
-
-static const char* GetFileBasename(const char* file) {
-    const char* last_slash = strrchr(file, '/');
-    if (last_slash != nullptr) {
-        return last_slash + 1;
-    }
-    return file;
-}
-
-// This splits the message up line by line, by calling log_function with a pointer to the start of
-// each line and the size up to the newline character.  It sends size = -1 for the final line.
-template <typename F, typename... Args>
-static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
-    const char* newline;
-    while ((newline = strchr(msg, '\n')) != nullptr) {
-        log_function(msg, newline - msg, args...);
-        msg = newline + 1;
-    }
-
-    log_function(msg, -1, args...);
-}
-
-void DefaultAborter(const char* abort_message) {
-    TLOGC("aborting: %s\n", abort_message);
-    abort();
-}
-
-static void TrustyLogLine(const char* msg, int /*length*/, android::base::LogSeverity severity,
-                          const char* tag) {
-    switch (severity) {
-        case VERBOSE:
-        case DEBUG:
-            TLOGD("%s: %s\n", tag, msg);
-            break;
-        case INFO:
-            TLOGI("%s: %s\n", tag, msg);
-            break;
-        case WARNING:
-            TLOGW("%s: %s\n", tag, msg);
-            break;
-        case ERROR:
-            TLOGE("%s: %s\n", tag, msg);
-            break;
-        case FATAL_WITHOUT_ABORT:
-        case FATAL:
-            TLOGC("%s: %s\n", tag, msg);
-            break;
-    }
-}
-
-void TrustyLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag,
-                  const char*, unsigned int, const char* full_message) {
-    SplitByLines(full_message, TrustyLogLine, severity, tag);
-}
-
-// This indirection greatly reduces the stack impact of having lots of
-// checks/logging in a function.
-class LogMessageData {
-public:
-    LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag,
-                   int error)
-          : file_(GetFileBasename(file)),
-            line_number_(line),
-            severity_(severity),
-            tag_(tag),
-            error_(error) {}
-
-    const char* GetFile() const { return file_; }
-
-    unsigned int GetLineNumber() const { return line_number_; }
-
-    LogSeverity GetSeverity() const { return severity_; }
-
-    const char* GetTag() const { return tag_; }
-
-    int GetError() const { return error_; }
-
-    std::ostream& GetBuffer() { return buffer_; }
-
-    std::string ToString() const { return buffer_.str(); }
-
-private:
-    std::ostringstream buffer_;
-    const char* const file_;
-    const unsigned int line_number_;
-    const LogSeverity severity_;
-    const char* const tag_;
-    const int error_;
-
-    DISALLOW_COPY_AND_ASSIGN(LogMessageData);
-};
-
-LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity,
-                       const char* tag, int error)
-      : LogMessage(file, line, severity, tag, error) {}
-
-LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag,
-                       int error)
-      : data_(new LogMessageData(file, line, severity, tag, error)) {}
-
-LogMessage::~LogMessage() {
-    // Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
-    if (!WOULD_LOG(data_->GetSeverity())) {
-        return;
-    }
-
-    // Finish constructing the message.
-    if (data_->GetError() != -1) {
-        data_->GetBuffer() << ": " << strerror(data_->GetError());
-    }
-    std::string msg(data_->ToString());
-
-    LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
-            msg.c_str());
-
-    // Abort if necessary.
-    if (data_->GetSeverity() == FATAL) {
-        DefaultAborter(msg.c_str());
-    }
-}
-
-std::ostream& LogMessage::stream() {
-    return data_->GetBuffer();
-}
-
-void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
-                         const char* message) {
-    TrustyLogger(DEFAULT, severity, tag ?: "<unknown>", file, line, message);
-}
-
-bool ShouldLog(LogSeverity /*severity*/, const char* /*tag*/) {
-    // This is controlled by Trusty's log level.
-    return true;
-}
-
-} // namespace base
-} // namespace android
diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk
index 2e56cbd..9cad556 100644
--- a/libs/binder/trusty/rules.mk
+++ b/libs/binder/trusty/rules.mk
@@ -24,7 +24,6 @@
 FMTLIB_DIR := external/fmtlib
 
 MODULE_SRCS := \
-	$(LOCAL_DIR)/logging.cpp \
 	$(LOCAL_DIR)/OS.cpp \
 	$(LOCAL_DIR)/RpcServerTrusty.cpp \
 	$(LOCAL_DIR)/RpcTransportTipcTrusty.cpp \
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 86ced2c..0e26544 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -208,21 +208,6 @@
         ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
     }
 
-    static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
-                                   ScreenCaptureResults& captureResults) {
-        const auto sf = ComposerServiceAIDL::getComposerService();
-        SurfaceComposerClient::Transaction().apply(true);
-
-        const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
-        binder::Status status = sf->captureDisplay(captureArgs, captureListener);
-        status_t err = gui::aidl_utils::statusTFromBinderStatus(status);
-        if (err != NO_ERROR) {
-            return err;
-        }
-        captureResults = captureListener->waitForResults();
-        return fenceStatus(captureResults.fenceResult);
-    }
-
     sp<Surface> mSurface;
     sp<SurfaceComposerClient> mComposerClient;
     sp<SurfaceControl> mSurfaceControl;
@@ -260,56 +245,6 @@
     EXPECT_EQ(1, result);
 }
 
-// This test probably doesn't belong here.
-TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersDontSucceed) {
-    sp<ANativeWindow> anw(mSurface);
-
-    // Verify the screenshot works with no protected buffers.
-    const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
-    ASSERT_FALSE(ids.empty());
-    // display 0 is picked for now, can extend to support all displays if needed
-    const sp<IBinder> display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
-    ASSERT_FALSE(display == nullptr);
-
-    DisplayCaptureArgs captureArgs;
-    captureArgs.displayToken = display;
-    captureArgs.width = 64;
-    captureArgs.height = 64;
-
-    ScreenCaptureResults captureResults;
-    ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
-
-    ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(),
-            NATIVE_WINDOW_API_CPU));
-    // Set the PROTECTED usage bit and verify that the screenshot fails.  Note
-    // that we need to dequeue a buffer in order for it to actually get
-    // allocated in SurfaceFlinger.
-    ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(),
-            GRALLOC_USAGE_PROTECTED));
-    ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3));
-    ANativeWindowBuffer* buf = nullptr;
-
-    status_t err = native_window_dequeue_buffer_and_wait(anw.get(), &buf);
-    if (err) {
-        // we could fail if GRALLOC_USAGE_PROTECTED is not supported.
-        // that's okay as long as this is the reason for the failure.
-        // try again without the GRALLOC_USAGE_PROTECTED bit.
-        ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), 0));
-        ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(),
-                &buf));
-        return;
-    }
-    ASSERT_EQ(NO_ERROR, anw->cancelBuffer(anw.get(), buf, -1));
-
-    for (int i = 0; i < 4; i++) {
-        // Loop to make sure SurfaceFlinger has retired a protected buffer.
-        ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(),
-                &buf));
-        ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
-    }
-    ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
-}
-
 TEST_F(SurfaceTest, ConcreteTypeIsSurface) {
     sp<ANativeWindow> anw(mSurface);
     int result = -123;
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index c37db16..dd8dc8d 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -50,7 +50,7 @@
     // overrides for flags, without having to link against a separate version of libinput or of this
     // library. Bundling this library directly into libinput prevents us from having to add this
     // library as a shared lib dependency everywhere where libinput is used.
-    test: true,
+    mode: "test",
     shared: {
         enabled: false,
     },
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp
index 538c1d2..4246c40 100644
--- a/libs/ui/FenceTime.cpp
+++ b/libs/ui/FenceTime.cpp
@@ -363,9 +363,9 @@
 }
 
 void FenceToFenceTimeMap::garbageCollectLocked() {
-    for (auto& it : mMap) {
+    for (auto it = mMap.begin(); it != mMap.end();) {
         // Erase all expired weak pointers from the vector.
-        auto& vect = it.second;
+        auto& vect = it->second;
         vect.erase(
                 std::remove_if(vect.begin(), vect.end(),
                         [](const std::weak_ptr<FenceTime>& ft) {
@@ -375,7 +375,9 @@
 
         // Also erase the map entry if the vector is now empty.
         if (vect.empty()) {
-            mMap.erase(it.first);
+            it = mMap.erase(it);
+        } else {
+            it++;
         }
     }
 }
diff --git a/opengl/Android.bp b/opengl/Android.bp
index b15694b..4454f36 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -72,6 +72,10 @@
     llndk: {
         llndk_headers: true,
     },
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.virt",
+    ],
 }
 
 subdirs = [
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 1092bdb..e529bdd 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -31,16 +31,30 @@
             args.pointerProperties[0].toolType == ToolType::MOUSE;
 }
 
+bool isHoverAction(int32_t action) {
+    return action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+            action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT;
+}
+
+bool isStylusHoverEvent(const NotifyMotionArgs& args) {
+    return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action);
+}
 } // namespace
 
 // --- PointerChoreographer ---
 
 PointerChoreographer::PointerChoreographer(InputListenerInterface& listener,
                                            PointerChoreographerPolicyInterface& policy)
-      : mNextListener(listener),
+      : mTouchControllerConstructor([this]() REQUIRES(mLock) {
+            return mPolicy.createPointerController(
+                    PointerControllerInterface::ControllerType::TOUCH);
+        }),
+        mNextListener(listener),
         mPolicy(policy),
         mDefaultMouseDisplayId(ADISPLAY_ID_DEFAULT),
-        mNotifiedPointerDisplayId(ADISPLAY_ID_NONE) {}
+        mNotifiedPointerDisplayId(ADISPLAY_ID_NONE),
+        mShowTouchesEnabled(false),
+        mStylusPointerIconEnabled(false) {}
 
 void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
     std::scoped_lock _l(mLock);
@@ -69,8 +83,10 @@
 
     if (isFromMouse(args)) {
         return processMouseEventLocked(args);
+    } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) {
+        processStylusHoverEventLocked(args);
     } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
-        return processTouchscreenEventLocked(args);
+        processTouchscreenAndStylusEventLocked(args);
     }
     return args;
 }
@@ -114,12 +130,70 @@
  * mouse device keeps moving and unfades the cursor.
  * For touch events, we do not need to populate the cursor position.
  */
-NotifyMotionArgs PointerChoreographer::processTouchscreenEventLocked(const NotifyMotionArgs& args) {
+void PointerChoreographer::processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) {
+    if (args.displayId == ADISPLAY_ID_NONE) {
+        return;
+    }
+
     if (const auto it = mMousePointersByDisplay.find(args.displayId);
         it != mMousePointersByDisplay.end() && args.action == AMOTION_EVENT_ACTION_DOWN) {
         it->second->fade(PointerControllerInterface::Transition::GRADUAL);
     }
-    return args;
+
+    if (!mShowTouchesEnabled) {
+        return;
+    }
+
+    // Get the touch pointer controller for the device, or create one if it doesn't exist.
+    auto [it, _] = mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor);
+
+    PointerControllerInterface& pc = *it->second;
+
+    const PointerCoords* coords = args.pointerCoords.data();
+    const int32_t maskedAction = MotionEvent::getActionMasked(args.action);
+    const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
+    std::array<uint32_t, MAX_POINTER_ID + 1> idToIndex;
+    BitSet32 idBits;
+    if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL) {
+        for (size_t i = 0; i < args.getPointerCount(); i++) {
+            if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && actionIndex == i) {
+                continue;
+            }
+            uint32_t id = args.pointerProperties[i].id;
+            idToIndex[id] = i;
+            idBits.markBit(id);
+        }
+    }
+    // The PointerController already handles setting spots per-display, so
+    // we do not need to manually manage display changes for touch spots for now.
+    pc.setSpots(coords, idToIndex.cbegin(), idBits, args.displayId);
+}
+
+void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& args) {
+    if (args.displayId == ADISPLAY_ID_NONE) {
+        return;
+    }
+
+    if (args.getPointerCount() != 1) {
+        LOG(WARNING) << "Only stylus hover events with a single pointer are currently supported: "
+                     << args.dump();
+    }
+
+    // Get the stylus pointer controller for the device, or create one if it doesn't exist.
+    auto [it, _] =
+            mStylusPointersByDevice.try_emplace(args.deviceId,
+                                                getStylusControllerConstructor(args.displayId));
+
+    PointerControllerInterface& pc = *it->second;
+
+    const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
+    const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
+    pc.setPosition(x, y);
+    if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
+        pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
+    } else {
+        pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
+    }
 }
 
 void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) {
@@ -135,9 +209,17 @@
 }
 
 void PointerChoreographer::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
+    processDeviceReset(args);
+
     mNextListener.notify(args);
 }
 
+void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) {
+    std::scoped_lock _l(mLock);
+    mTouchPointersByDevice.erase(args.deviceId);
+    mStylusPointersByDevice.erase(args.deviceId);
+}
+
 void PointerChoreographer::notifyPointerCaptureChanged(
         const NotifyPointerCaptureChangedArgs& args) {
     if (args.request.enable) {
@@ -153,12 +235,25 @@
     std::scoped_lock _l(mLock);
 
     dump += "PointerChoreographer:\n";
+    dump += StringPrintf("show touches: %s\n", mShowTouchesEnabled ? "true" : "false");
+    dump += StringPrintf("stylus pointer icon enabled: %s\n",
+                         mStylusPointerIconEnabled ? "true" : "false");
 
     dump += INDENT "MousePointerControllers:\n";
     for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
         std::string pointerControllerDump = addLinePrefix(mousePointerController->dump(), INDENT);
         dump += INDENT + std::to_string(displayId) + " : " + pointerControllerDump;
     }
+    dump += INDENT "TouchPointerControllers:\n";
+    for (const auto& [deviceId, touchPointerController] : mTouchPointersByDevice) {
+        std::string pointerControllerDump = addLinePrefix(touchPointerController->dump(), INDENT);
+        dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
+    }
+    dump += INDENT "StylusPointerControllers:\n";
+    for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
+        std::string pointerControllerDump = addLinePrefix(stylusPointerController->dump(), INDENT);
+        dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
+    }
     dump += "\n";
 }
 
@@ -175,8 +270,16 @@
     return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId;
 }
 
+InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) {
+    auto it = std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
+                           [deviceId](const auto& info) { return info.getId() == deviceId; });
+    return it != mInputDeviceInfos.end() ? &(*it) : nullptr;
+}
+
 void PointerChoreographer::updatePointerControllersLocked() {
     std::set<int32_t /*displayId*/> mouseDisplaysToKeep;
+    std::set<DeviceId> touchDevicesToKeep;
+    std::set<DeviceId> stylusDevicesToKeep;
 
     // Mark the displayIds or deviceIds of PointerControllers currently needed.
     for (const auto& info : mInputDeviceInfos) {
@@ -187,17 +290,25 @@
                     getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
             mouseDisplaysToKeep.insert(resolvedDisplayId);
         }
+        if (isFromSource(sources, AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled &&
+            info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
+            touchDevicesToKeep.insert(info.getId());
+        }
+        if (isFromSource(sources, AINPUT_SOURCE_STYLUS) && mStylusPointerIconEnabled &&
+            info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
+            stylusDevicesToKeep.insert(info.getId());
+        }
     }
 
     // Remove PointerControllers no longer needed.
-    // This has the side-effect of fading pointers or clearing spots before removal.
     std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) {
-        auto& [displayId, controller] = pair;
-        if (mouseDisplaysToKeep.find(displayId) == mouseDisplaysToKeep.end()) {
-            controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
-            return true;
-        }
-        return false;
+        return mouseDisplaysToKeep.find(pair.first) == mouseDisplaysToKeep.end();
+    });
+    std::erase_if(mTouchPointersByDevice, [&touchDevicesToKeep](const auto& pair) {
+        return touchDevicesToKeep.find(pair.first) == touchDevicesToKeep.end();
+    });
+    std::erase_if(mStylusPointersByDevice, [&stylusDevicesToKeep](const auto& pair) {
+        return stylusDevicesToKeep.find(pair.first) == stylusDevicesToKeep.end();
     });
 
     // Notify the policy if there's a change on the pointer display ID.
@@ -234,10 +345,17 @@
 void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
     std::scoped_lock _l(mLock);
     for (const auto& viewport : viewports) {
-        if (const auto it = mMousePointersByDisplay.find(viewport.displayId);
+        const int32_t displayId = viewport.displayId;
+        if (const auto it = mMousePointersByDisplay.find(displayId);
             it != mMousePointersByDisplay.end()) {
             it->second->setDisplayViewport(viewport);
         }
+        for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
+            const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
+            if (info && info->getAssociatedDisplayId() == displayId) {
+                stylusPointerController->setDisplayViewport(viewport);
+            }
+        }
     }
     mViewports = viewports;
     notifyPointerDisplayIdChangedLocked();
@@ -263,6 +381,24 @@
     return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
 }
 
+void PointerChoreographer::setShowTouchesEnabled(bool enabled) {
+    std::scoped_lock _l(mLock);
+    if (mShowTouchesEnabled == enabled) {
+        return;
+    }
+    mShowTouchesEnabled = enabled;
+    updatePointerControllersLocked();
+}
+
+void PointerChoreographer::setStylusPointerIconEnabled(bool enabled) {
+    std::scoped_lock _l(mLock);
+    if (mStylusPointerIconEnabled == enabled) {
+        return;
+    }
+    mStylusPointerIconEnabled = enabled;
+    updatePointerControllersLocked();
+}
+
 PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
         int32_t displayId) {
     std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
@@ -277,4 +413,18 @@
     return ConstructorDelegate(std::move(ctor));
 }
 
+PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusControllerConstructor(
+        int32_t displayId) {
+    std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
+            [this, displayId]() REQUIRES(mLock) {
+                auto pc = mPolicy.createPointerController(
+                        PointerControllerInterface::ControllerType::STYLUS);
+                if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
+                    pc->setDisplayViewport(*viewport);
+                }
+                return pc;
+            };
+    return ConstructorDelegate(std::move(ctor));
+}
+
 } // namespace android
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index c1b900f..26d2fef 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -56,6 +56,8 @@
     virtual std::optional<DisplayViewport> getViewportForPointerDevice(
             int32_t associatedDisplayId = ADISPLAY_ID_NONE) = 0;
     virtual FloatPoint getMouseCursorPosition(int32_t displayId) = 0;
+    virtual void setShowTouchesEnabled(bool enabled) = 0;
+    virtual void setStylusPointerIconEnabled(bool enabled) = 0;
     /**
      * This method may be called on any thread (usually by the input manager on a binder thread).
      */
@@ -73,6 +75,8 @@
     std::optional<DisplayViewport> getViewportForPointerDevice(
             int32_t associatedDisplayId) override;
     FloatPoint getMouseCursorPosition(int32_t displayId) override;
+    void setShowTouchesEnabled(bool enabled) override;
+    void setStylusPointerIconEnabled(bool enabled) override;
 
     void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
     void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
@@ -91,14 +95,19 @@
     void notifyPointerDisplayIdChangedLocked() REQUIRES(mLock);
     const DisplayViewport* findViewportByIdLocked(int32_t displayId) const REQUIRES(mLock);
     int32_t getTargetMouseDisplayLocked(int32_t associatedDisplayId) const REQUIRES(mLock);
+    InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock);
 
     NotifyMotionArgs processMotion(const NotifyMotionArgs& args);
     NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
-    NotifyMotionArgs processTouchscreenEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+    void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+    void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
+    void processDeviceReset(const NotifyDeviceResetArgs& args);
 
     using ControllerConstructor =
             ConstructorDelegate<std::function<std::shared_ptr<PointerControllerInterface>()>>;
+    ControllerConstructor mTouchControllerConstructor GUARDED_BY(mLock);
     ControllerConstructor getMouseControllerConstructor(int32_t displayId) REQUIRES(mLock);
+    ControllerConstructor getStylusControllerConstructor(int32_t displayId) REQUIRES(mLock);
 
     std::mutex mLock;
 
@@ -107,11 +116,17 @@
 
     std::map<int32_t, std::shared_ptr<PointerControllerInterface>> mMousePointersByDisplay
             GUARDED_BY(mLock);
+    std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mTouchPointersByDevice
+            GUARDED_BY(mLock);
+    std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mStylusPointersByDevice
+            GUARDED_BY(mLock);
 
     int32_t mDefaultMouseDisplayId GUARDED_BY(mLock);
     int32_t mNotifiedPointerDisplayId GUARDED_BY(mLock);
     std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(mLock);
     std::vector<DisplayViewport> mViewports GUARDED_BY(mLock);
+    bool mShowTouchesEnabled GUARDED_BY(mLock);
+    bool mStylusPointerIconEnabled GUARDED_BY(mLock);
 };
 
 } // namespace android
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index 8837b25..ef74a55 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -48,8 +48,8 @@
  */
 class PointerControllerInterface {
 protected:
-    PointerControllerInterface() { }
-    virtual ~PointerControllerInterface() { }
+    PointerControllerInterface() {}
+    virtual ~PointerControllerInterface() {}
 
 public:
     /**
@@ -63,6 +63,10 @@
         LEGACY,
         // Represents a single mouse pointer.
         MOUSE,
+        // Represents multiple touch spots.
+        TOUCH,
+        // Represents a single stylus pointer.
+        STYLUS,
     };
 
     /* Dumps the state of the pointer controller. */
@@ -121,7 +125,7 @@
      * pressed (not hovering).
      */
     virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
-            BitSet32 spotIdBits, int32_t displayId) = 0;
+                          BitSet32 spotIdBits, int32_t displayId) = 0;
 
     /* Removes all spots. */
     virtual void clearSpots() = 0;
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index ca517f3..5475594 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -47,6 +47,8 @@
 
 void FakePointerController::setDisplayViewport(const DisplayViewport& viewport) {
     mDisplayId = viewport.displayId;
+    setBounds(viewport.logicalLeft, viewport.logicalTop, viewport.logicalRight - 1,
+              viewport.logicalBottom - 1);
 }
 
 void FakePointerController::assertPosition(float x, float y) {
@@ -55,6 +57,12 @@
     ASSERT_NEAR(y, actualY, 1);
 }
 
+void FakePointerController::assertSpotCount(int32_t displayId, int32_t count) {
+    auto it = mSpotsByDisplay.find(displayId);
+    ASSERT_TRUE(it != mSpotsByDisplay.end()) << "Spots not found for display " << displayId;
+    ASSERT_EQ(static_cast<size_t>(count), it->second.size());
+}
+
 bool FakePointerController::isPointerShown() {
     return mIsPointerShown;
 }
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index c75f6ed..d7e40b3 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -37,6 +37,7 @@
     void setDisplayViewport(const DisplayViewport& viewport) override;
 
     void assertPosition(float x, float y);
+    void assertSpotCount(int32_t displayId, int32_t count);
     bool isPointerShown();
 
 private:
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index da2e205..68f5857 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -27,6 +27,7 @@
 namespace android {
 
 using ControllerType = PointerControllerInterface::ControllerType;
+using testing::AllOf;
 
 namespace {
 
@@ -37,12 +38,18 @@
 Visitor(V...) -> Visitor<V...>;
 
 constexpr int32_t DEVICE_ID = 3;
+constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
 constexpr int32_t DISPLAY_ID = 5;
 constexpr int32_t ANOTHER_DISPLAY_ID = 10;
+constexpr int32_t DISPLAY_WIDTH = 480;
+constexpr int32_t DISPLAY_HEIGHT = 800;
 
 const auto MOUSE_POINTER = PointerBuilder(/*id=*/0, ToolType::MOUSE)
                                    .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 10)
                                    .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 20);
+const auto FIRST_TOUCH_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200);
+const auto SECOND_TOUCH_POINTER = PointerBuilder(/*id=*/1, ToolType::FINGER).x(200).y(300);
+const auto STYLUS_POINTER = PointerBuilder(/*id=*/0, ToolType::STYLUS).x(100).y(200);
 
 static InputDeviceInfo generateTestDeviceInfo(int32_t deviceId, uint32_t source,
                                               int32_t associatedDisplayId) {
@@ -60,6 +67,8 @@
     for (auto displayId : displayIds) {
         DisplayViewport viewport;
         viewport.displayId = displayId;
+        viewport.logicalRight = DISPLAY_WIDTH;
+        viewport.logicalBottom = DISPLAY_HEIGHT;
         viewports.push_back(viewport);
     }
     return viewports;
@@ -115,6 +124,7 @@
         EXPECT_FALSE(mLastCreatedController.has_value())
                 << "More than one PointerController created at a time";
         std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>();
+        EXPECT_FALSE(pc->isPointerShown());
         mLastCreatedController = {type, pc};
         return pc;
     }
@@ -385,4 +395,659 @@
     assertPointerDisplayIdNotified(ANOTHER_DISPLAY_ID);
 }
 
+TEST_F(PointerChoreographerTest, MouseMovesPointerAndReturnsNewArgs) {
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+    // Set bounds and initial position of the PointerController.
+    pc->setPosition(100, 200);
+
+    // Make NotifyMotionArgs and notify Choreographer.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+
+    // Check that the PointerController updated the position and the pointer is shown.
+    pc->assertPosition(110, 220);
+    ASSERT_TRUE(pc->isPointerShown());
+
+    // Check that x-y cooridnates, displayId and cursor position are correctly updated.
+    mTestListener.assertNotifyMotionWasCalled(
+            AllOf(WithCoords(110, 220), WithDisplayId(DISPLAY_ID), WithCursorPosition(110, 220)));
+}
+
+TEST_F(PointerChoreographerTest,
+       AssociatedMouseMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) {
+    // Add two displays and set one to default.
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+
+    // Add two devices, one unassociated and the other associated with non-default mouse display.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
+              generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+    auto unassociatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+    ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId());
+
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(SECOND_DEVICE_ID)
+                    .displayId(ANOTHER_DISPLAY_ID)
+                    .build());
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+    auto associatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
+    ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId());
+
+    // Set bounds and initial position for PointerControllers.
+    unassociatedMousePc->setPosition(100, 200);
+    associatedMousePc->setPosition(300, 400);
+
+    // Make NotifyMotionArgs from the associated mouse and notify Choreographer.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(SECOND_DEVICE_ID)
+                    .displayId(ANOTHER_DISPLAY_ID)
+                    .build());
+
+    // Check the status of the PointerControllers.
+    unassociatedMousePc->assertPosition(100, 200);
+    ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId());
+    associatedMousePc->assertPosition(310, 420);
+    ASSERT_EQ(ANOTHER_DISPLAY_ID, associatedMousePc->getDisplayId());
+    ASSERT_TRUE(associatedMousePc->isPointerShown());
+
+    // Check that x-y cooridnates, displayId and cursor position are correctly updated.
+    mTestListener.assertNotifyMotionWasCalled(
+            AllOf(WithCoords(310, 420), WithDeviceId(SECOND_DEVICE_ID),
+                  WithDisplayId(ANOTHER_DISPLAY_ID), WithCursorPosition(310, 420)));
+}
+
+TEST_F(PointerChoreographerTest, DoesNotMovePointerForMouseRelativeSource) {
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+    // Set bounds and initial position of the PointerController.
+    pc->setPosition(100, 200);
+
+    // Assume that pointer capture is enabled.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/1,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE_RELATIVE, ADISPLAY_ID_NONE)}});
+    mChoreographer.notifyPointerCaptureChanged(
+            NotifyPointerCaptureChangedArgs(/*id=*/2, systemTime(SYSTEM_TIME_MONOTONIC),
+                                            PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+
+    // Notify motion as if pointer capture is enabled.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE_RELATIVE)
+                    .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE)
+                                     .x(10)
+                                     .y(20)
+                                     .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 10)
+                                     .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 20))
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+
+    // Check that there's no update on the PointerController.
+    pc->assertPosition(100, 200);
+    ASSERT_FALSE(pc->isPointerShown());
+
+    // Check x-y cooridnates, displayId and cursor position are not changed.
+    mTestListener.assertNotifyMotionWasCalled(
+            AllOf(WithCoords(10, 20), WithRelativeMotion(10, 20), WithDisplayId(ADISPLAY_ID_NONE),
+                  WithCursorPosition(AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                     AMOTION_EVENT_INVALID_CURSOR_POSITION)));
+}
+
+TEST_F(PointerChoreographerTest, WhenPointerCaptureEnabledHidesPointer) {
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+    mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+    mTestListener.assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE));
+    auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+    ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+    // Set bounds and initial position of the PointerController.
+    pc->setPosition(100, 200);
+
+    // Make NotifyMotionArgs and notify Choreographer.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+                    .pointer(MOUSE_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(ADISPLAY_ID_NONE)
+                    .build());
+
+    // Check that the PointerController updated the position and the pointer is shown.
+    pc->assertPosition(110, 220);
+    ASSERT_TRUE(pc->isPointerShown());
+
+    // Enable pointer capture and check if the PointerController hid the pointer.
+    mChoreographer.notifyPointerCaptureChanged(
+            NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC),
+                                            PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+    ASSERT_FALSE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerTest, WhenShowTouchesEnabledAndDisabledDoesNotCreatePointerController) {
+    // Disable show touches and add a touch device.
+    mChoreographer.setShowTouchesEnabled(false);
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+    assertPointerControllerNotCreated();
+
+    // Enable show touches. PointerController still should not be created.
+    mChoreographer.setShowTouchesEnabled(true);
+    assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, WhenTouchEventOccursCreatesPointerController) {
+    // Add a touch device and enable show touches.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+    mChoreographer.setShowTouchesEnabled(true);
+
+    // Emit touch event. Now PointerController should be created.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    assertPointerControllerCreated(ControllerType::TOUCH);
+}
+
+TEST_F(PointerChoreographerTest,
+       WhenShowTouchesDisabledAndTouchEventOccursDoesNotCreatePointerController) {
+    // Add a touch device and disable show touches.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+    mChoreographer.setShowTouchesEnabled(false);
+    assertPointerControllerNotCreated();
+
+    // Emit touch event. Still, PointerController should not be created.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, WhenTouchDeviceIsRemovedRemovesPointerController) {
+    // Make sure the PointerController is created.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+    mChoreographer.setShowTouchesEnabled(true);
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+
+    // Remove the device.
+    mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
+    assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, WhenShowTouchesDisabledRemovesPointerController) {
+    // Make sure the PointerController is created.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+    mChoreographer.setShowTouchesEnabled(true);
+    assertPointerControllerNotCreated();
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+
+    // Disable show touches.
+    mChoreographer.setShowTouchesEnabled(false);
+    assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, TouchSetsSpots) {
+    mChoreographer.setShowTouchesEnabled(true);
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+
+    // Emit first pointer down.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+    pc->assertSpotCount(DISPLAY_ID, 1);
+
+    // Emit second pointer down.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+                                      (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                              AINPUT_SOURCE_TOUCHSCREEN)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .pointer(SECOND_TOUCH_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    pc->assertSpotCount(DISPLAY_ID, 2);
+
+    // Emit second pointer up.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_UP |
+                                      (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                              AINPUT_SOURCE_TOUCHSCREEN)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .pointer(SECOND_TOUCH_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    pc->assertSpotCount(DISPLAY_ID, 1);
+
+    // Emit first pointer up.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    pc->assertSpotCount(DISPLAY_ID, 0);
+}
+
+TEST_F(PointerChoreographerTest, TouchSetsSpotsForStylusEvent) {
+    mChoreographer.setShowTouchesEnabled(true);
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
+                                     DISPLAY_ID)}});
+
+    // Emit down event with stylus properties.
+    mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN,
+                                                  AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS)
+                                        .pointer(STYLUS_POINTER)
+                                        .deviceId(DEVICE_ID)
+                                        .displayId(DISPLAY_ID)
+                                        .build());
+    auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+    pc->assertSpotCount(DISPLAY_ID, 1);
+}
+
+TEST_F(PointerChoreographerTest, TouchSetsSpotsForTwoDisplays) {
+    mChoreographer.setShowTouchesEnabled(true);
+    // Add two touch devices associated to different displays.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID),
+              generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+                                     ANOTHER_DISPLAY_ID)}});
+
+    // Emit touch event with first device.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    auto firstDisplayPc = assertPointerControllerCreated(ControllerType::TOUCH);
+    firstDisplayPc->assertSpotCount(DISPLAY_ID, 1);
+
+    // Emit touch events with second device.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .deviceId(SECOND_DEVICE_ID)
+                    .displayId(ANOTHER_DISPLAY_ID)
+                    .build());
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .pointer(SECOND_TOUCH_POINTER)
+                    .deviceId(SECOND_DEVICE_ID)
+                    .displayId(ANOTHER_DISPLAY_ID)
+                    .build());
+
+    // There should be another PointerController created.
+    auto secondDisplayPc = assertPointerControllerCreated(ControllerType::TOUCH);
+
+    // Check if the spots are set for the second device.
+    secondDisplayPc->assertSpotCount(ANOTHER_DISPLAY_ID, 2);
+
+    // Check if there's no change on the spot of the first device.
+    firstDisplayPc->assertSpotCount(DISPLAY_ID, 1);
+}
+
+TEST_F(PointerChoreographerTest, WhenTouchDeviceIsResetClearsSpots) {
+    // Make sure the PointerController is created and there is a spot.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+    mChoreographer.setShowTouchesEnabled(true);
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .pointer(FIRST_TOUCH_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+    pc->assertSpotCount(DISPLAY_ID, 1);
+
+    // Reset the device and ensure the touch pointer controller was removed.
+    mChoreographer.notifyDeviceReset(NotifyDeviceResetArgs(/*id=*/1, /*eventTime=*/0, DEVICE_ID));
+    assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest,
+       WhenStylusPointerIconEnabledAndDisabledDoesNotCreatePointerController) {
+    // Disable stylus pointer icon and add a stylus device.
+    mChoreographer.setStylusPointerIconEnabled(false);
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+    assertPointerControllerNotCreated();
+
+    // Enable stylus pointer icon. PointerController still should not be created.
+    mChoreographer.setStylusPointerIconEnabled(true);
+    assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, WhenStylusHoverEventOccursCreatesPointerController) {
+    // Add a stylus device and enable stylus pointer icon.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+    mChoreographer.setStylusPointerIconEnabled(true);
+    assertPointerControllerNotCreated();
+
+    // Emit hover event. Now PointerController should be created.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                    .pointer(STYLUS_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    assertPointerControllerCreated(ControllerType::STYLUS);
+}
+
+TEST_F(PointerChoreographerTest,
+       WhenStylusPointerIconDisabledAndHoverEventOccursDoesNotCreatePointerController) {
+    // Add a stylus device and disable stylus pointer icon.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+    mChoreographer.setStylusPointerIconEnabled(false);
+    assertPointerControllerNotCreated();
+
+    // Emit hover event. Still, PointerController should not be created.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                    .pointer(STYLUS_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, WhenStylusDeviceIsRemovedRemovesPointerController) {
+    // Make sure the PointerController is created.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+    mChoreographer.setStylusPointerIconEnabled(true);
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                    .pointer(STYLUS_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+    // Remove the device.
+    mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
+    assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, WhenStylusPointerIconDisabledRemovesPointerController) {
+    // Make sure the PointerController is created.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+    mChoreographer.setStylusPointerIconEnabled(true);
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                    .pointer(STYLUS_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+    // Disable stylus pointer icon.
+    mChoreographer.setStylusPointerIconEnabled(false);
+    assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, SetsViewportForStylusPointerController) {
+    // Set viewport.
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+    // Make sure the PointerController is created.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+    mChoreographer.setStylusPointerIconEnabled(true);
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                    .pointer(STYLUS_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+    // Check that displayId is set.
+    ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterSetsViewportForStylusPointerController) {
+    // Make sure the PointerController is created.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+    mChoreographer.setStylusPointerIconEnabled(true);
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                    .pointer(STYLUS_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+    // Check that displayId is unset.
+    ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
+
+    // Set viewport.
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+    // Check that displayId is set.
+    ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest,
+       WhenViewportDoesNotMatchDoesNotSetViewportForStylusPointerController) {
+    // Make sure the PointerController is created.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+    mChoreographer.setStylusPointerIconEnabled(true);
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                    .pointer(STYLUS_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+    // Check that displayId is unset.
+    ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
+
+    // Set viewport which does not match the associated display of the stylus.
+    mChoreographer.setDisplayViewports(createViewports({ANOTHER_DISPLAY_ID}));
+
+    // Check that displayId is still unset.
+    ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId());
+}
+
+TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointer) {
+    mChoreographer.setStylusPointerIconEnabled(true);
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+    // Emit hover enter event. This is for creating PointerController.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                    .pointer(STYLUS_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+    // Emit hover move event. After bounds are set, PointerController will update the position.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+                    .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(250))
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    pc->assertPosition(150, 250);
+    ASSERT_TRUE(pc->isPointerShown());
+
+    // Emit hover exit event.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS)
+                    .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(250))
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    // Check that the pointer is gone.
+    ASSERT_FALSE(pc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerTest, StylusHoverManipulatesPointerForTwoDisplays) {
+    mChoreographer.setStylusPointerIconEnabled(true);
+    // Add two stylus devices associated to different displays.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0,
+             {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID),
+              generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_STYLUS, ANOTHER_DISPLAY_ID)}});
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+
+    // Emit hover event with first device. This is for creating PointerController.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                    .pointer(STYLUS_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    auto firstDisplayPc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+    // Emit hover event with second device. This is for creating PointerController.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                    .pointer(STYLUS_POINTER)
+                    .deviceId(SECOND_DEVICE_ID)
+                    .displayId(ANOTHER_DISPLAY_ID)
+                    .build());
+
+    // There should be another PointerController created.
+    auto secondDisplayPc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+    // Emit hover event with first device.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+                    .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(150).y(250))
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+
+    // Check the pointer of the first device.
+    firstDisplayPc->assertPosition(150, 250);
+    ASSERT_TRUE(firstDisplayPc->isPointerShown());
+
+    // Emit hover event with second device.
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+                    .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS).x(250).y(350))
+                    .deviceId(SECOND_DEVICE_ID)
+                    .displayId(ANOTHER_DISPLAY_ID)
+                    .build());
+
+    // Check the pointer of the second device.
+    secondDisplayPc->assertPosition(250, 350);
+    ASSERT_TRUE(secondDisplayPc->isPointerShown());
+
+    // Check that there's no change on the pointer of the first device.
+    firstDisplayPc->assertPosition(150, 250);
+    ASSERT_TRUE(firstDisplayPc->isPointerShown());
+}
+
+TEST_F(PointerChoreographerTest, WhenStylusDeviceIsResetRemovesPointer) {
+    // Make sure the PointerController is created and there is a pointer.
+    mChoreographer.notifyInputDevicesChanged(
+            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_STYLUS, DISPLAY_ID)}});
+    mChoreographer.setStylusPointerIconEnabled(true);
+    mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+    mChoreographer.notifyMotion(
+            MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+                    .pointer(STYLUS_POINTER)
+                    .deviceId(DEVICE_ID)
+                    .displayId(DISPLAY_ID)
+                    .build());
+    auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+    ASSERT_TRUE(pc->isPointerShown());
+
+    // Reset the device and see the pointer controller was removed.
+    mChoreographer.notifyDeviceReset(NotifyDeviceResetArgs(/*id=*/1, /*eventTime=*/0, DEVICE_ID));
+    assertPointerControllerRemoved(pc);
+}
+
 } // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 00590e6..d9318af 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -223,9 +223,6 @@
 }
 
 TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) {
-    mTestLayers[0]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold / 2;
-    mTestLayers[1]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold;
-
     auto& layerState1 = mTestLayers[0]->layerState;
     auto& layerState2 = mTestLayers[1]->layerState;
 
@@ -235,6 +232,10 @@
     };
 
     initializeFlattener(layers);
+
+    mTestLayers[0]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold / 2;
+    mTestLayers[1]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold;
+
     expectAllLayersFlattened(layers);
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 2a6443a..317f2b0 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -19,6 +19,7 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "AidlComposerHal.h"
+#include "FlagManager.h"
 
 #include <SurfaceFlingerProperties.h>
 #include <android-base/file.h>
@@ -281,7 +282,7 @@
 }
 
 bool AidlComposer::getDisplayConfigurationsSupported() const {
-    return mComposerInterfaceVersion >= 3;
+    return mComposerInterfaceVersion >= 3 && FlagManager::getInstance().vrr_config();
 }
 
 std::vector<Capability> AidlComposer::getCapabilities() {
diff --git a/services/surfaceflinger/FlagManager.cpp b/services/surfaceflinger/FlagManager.cpp
index 6d0dbc7..13b8a6c 100644
--- a/services/surfaceflinger/FlagManager.cpp
+++ b/services/surfaceflinger/FlagManager.cpp
@@ -110,6 +110,7 @@
     /// Trunk stable server flags ///
     DUMP_SERVER_FLAG(late_boot_misc2);
     DUMP_SERVER_FLAG(dont_skip_on_early);
+    DUMP_SERVER_FLAG(refresh_rate_overlay_on_external_display);
 
     /// Trunk stable readonly flags ///
     DUMP_READ_ONLY_FLAG(connected_display);
@@ -190,6 +191,7 @@
 
 /// Trunk stable server flags ///
 FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "")
+FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
 
 /// Exceptions ///
 bool FlagManager::dont_skip_on_early() const {
diff --git a/services/surfaceflinger/FlagManager.h b/services/surfaceflinger/FlagManager.h
index cefce9b..e3e4f80 100644
--- a/services/surfaceflinger/FlagManager.h
+++ b/services/surfaceflinger/FlagManager.h
@@ -49,6 +49,7 @@
     /// Trunk stable server flags ///
     bool late_boot_misc2() const;
     bool dont_skip_on_early() const;
+    bool refresh_rate_overlay_on_external_display() const;
 
     /// Trunk stable readonly flags ///
     bool connected_display() const;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 8fc9cba..450ba1d 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -167,6 +167,27 @@
     info->setDefaultLayerVote(getVoteType(frameRateCompatibility, contentDetectionEnabled));
 }
 
+void LayerHistory::setLayerProperties(int32_t id, const LayerProps& properties) {
+    std::lock_guard lock(mLock);
+
+    auto [found, layerPair] = findLayer(id);
+    if (found == LayerStatus::NotFound) {
+        // Offscreen layer
+        ALOGV("%s: %d not registered", __func__, id);
+        return;
+    }
+
+    const auto& info = layerPair->second;
+    info->setProperties(properties);
+
+    // Activate layer if inactive and visible.
+    if (found == LayerStatus::LayerInInactiveMap && info->isVisible()) {
+        mActiveLayerInfos.insert(
+                {id, std::make_pair(layerPair->first, std::move(layerPair->second))});
+        mInactiveLayerInfos.erase(id);
+    }
+}
+
 auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) -> Summary {
     ATRACE_CALL();
     Summary summary;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index bac1ec6..5a9445b 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -73,7 +73,7 @@
     // does not set a preference for refresh rate.
     void setDefaultFrameRateCompatibility(int32_t id, FrameRateCompatibility frameRateCompatibility,
                                           bool contentDetectionEnabled);
-
+    void setLayerProperties(int32_t id, const LayerProps&);
     using Summary = std::vector<RefreshRateSelector::LayerRequirement>;
 
     // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 8d18769..bf3a7bc 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -75,6 +75,10 @@
     }
 }
 
+void LayerInfo::setProperties(const android::scheduler::LayerProps& properties) {
+    *mLayerProps = properties;
+}
+
 bool LayerInfo::isFrameTimeValid(const FrameTimeData& frameTime) const {
     return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
                                           mFrameTimeValidSince.time_since_epoch())
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 03ab0df..d24fc33 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -182,6 +182,8 @@
     // layer can go back to whatever vote it had before the app voted for it.
     void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
 
+    void setProperties(const LayerProps&);
+
     // Resets the layer vote to its default.
     void resetLayerVote() {
         mLayerVote = {mDefaultVote, Fps(), Seamlessness::Default, FrameRateCategory::Default};
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 56a4ae2..f41243c 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -654,6 +654,10 @@
                                                    mFeatures.test(Feature::kContentDetection));
 }
 
+void Scheduler::setLayerProperties(int32_t id, const android::scheduler::LayerProps& properties) {
+    mLayerHistory.setLayerProperties(id, properties);
+}
+
 void Scheduler::chooseRefreshRateForContent(
         const surfaceflinger::frontend::LayerHierarchy* hierarchy,
         bool updateAttachedChoreographer) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index a02180a..c78051a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -234,6 +234,7 @@
                             nsecs_t now, LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
     void setModeChangePending(bool pending);
     void setDefaultFrameRateCompatibility(int32_t id, scheduler::FrameRateCompatibility);
+    void setLayerProperties(int32_t id, const LayerProps&);
     void deregisterLayer(Layer*);
     void onLayerDestroyed(Layer*) EXCLUDES(mChoreographerLock);
 
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index 19e951a..2806450 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -83,7 +83,7 @@
 };
 
 // The frame rate category of a Layer.
-enum class FrameRateCategory {
+enum class FrameRateCategory : int32_t {
     Default,
     NoPreference,
     Low,
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 2d5ccbf..9c8555e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2219,7 +2219,8 @@
                  snapshot->changes.any(Changes::Geometry));
 
         const bool hasChanges =
-                snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation) ||
+                snapshot->changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation |
+                                      Changes::Geometry | Changes::Visibility) ||
                 (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) !=
                         0;
 
@@ -2250,6 +2251,10 @@
                 .isFrontBuffered = snapshot->isFrontBuffered(),
         };
 
+        if (snapshot->changes.any(Changes::Geometry | Changes::Visibility)) {
+            mScheduler->setLayerProperties(snapshot->sequence, layerProps);
+        }
+
         if (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
             mScheduler->setDefaultFrameRateCompatibility(snapshot->sequence,
                                                          snapshot->defaultFrameRateCompatibility);
@@ -8417,7 +8422,8 @@
 void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
     bool setByHwc = getHwComposer().hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG);
     for (const auto& [id, display] : mPhysicalDisplays) {
-        if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) {
+        if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal ||
+            FlagManager::getInstance().refresh_rate_overlay_on_external_display()) {
             if (const auto device = getDisplayDeviceLocked(id)) {
                 const auto enableOverlay = [&](const bool setByHwc) FTL_FAKE_GUARD(
                                                    kMainThreadContext) {
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index a81f9b8..bb3c94a 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -70,3 +70,9 @@
   is_fixed_read_only: true
 }
 
+flag {
+  name: "refresh_rate_overlay_on_external_display"
+  namespace: "core_graphics"
+  description: "enable refresh rate indicator on the external display"
+  bug: "301647974"
+}
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index 9269e7c..c9af432 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -47,7 +47,6 @@
         ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
 
         sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
-        mCaptureArgs.displayToken = mDisplay;
     }
 
     virtual void TearDown() {
@@ -279,8 +278,6 @@
     const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256;
 
     sp<SurfaceControl> mBlackBgSurface;
-
-    DisplayCaptureArgs mCaptureArgs;
     ScreenCaptureResults mCaptureResults;
 
 private:
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 96cc333..79864e0 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -15,6 +15,8 @@
  */
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include <sys/types.h>
+#include <cstdint>
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
@@ -31,26 +33,22 @@
         LayerTransactionTest::SetUp();
         ASSERT_EQ(NO_ERROR, mClient->initCheck());
 
-        const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
-        ASSERT_FALSE(ids.empty());
-        mDisplayToken = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
-        ASSERT_FALSE(mDisplayToken == nullptr);
-
-        ui::DisplayMode mode;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &mode));
-        const ui::Size& resolution = mode.resolution;
-
-        mDisplaySize = resolution;
+        // Root surface
+        mRootSurfaceControl =
+                createLayer(String8("RootTestSurface"), mDisplayWidth, mDisplayHeight, 0);
+        ASSERT_TRUE(mRootSurfaceControl != nullptr);
+        ASSERT_TRUE(mRootSurfaceControl->isValid());
 
         // Background surface
-        mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(),
-                                        resolution.getHeight(), 0);
+        mBGSurfaceControl = createLayer(String8("BG Test Surface"), mDisplayWidth, mDisplayHeight,
+                                        0, mRootSurfaceControl.get());
         ASSERT_TRUE(mBGSurfaceControl != nullptr);
         ASSERT_TRUE(mBGSurfaceControl->isValid());
         TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
 
         // Foreground surface
-        mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0);
+        mFGSurfaceControl =
+                createLayer(String8("FG Test Surface"), 64, 64, 0, mRootSurfaceControl.get());
 
         ASSERT_TRUE(mFGSurfaceControl != nullptr);
         ASSERT_TRUE(mFGSurfaceControl->isValid());
@@ -58,7 +56,7 @@
         TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
 
         asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(mDisplayToken, ui::DEFAULT_LAYER_STACK);
+            t.setDisplayLayerStack(mDisplay, ui::DEFAULT_LAYER_STACK);
 
             t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
 
@@ -66,25 +64,22 @@
                     .setPosition(mFGSurfaceControl, 64, 64)
                     .show(mFGSurfaceControl);
         });
+
+        mCaptureArgs.sourceCrop = mDisplayRect;
+        mCaptureArgs.layerHandle = mRootSurfaceControl->getHandle();
     }
 
     virtual void TearDown() {
         LayerTransactionTest::TearDown();
         mBGSurfaceControl = 0;
         mFGSurfaceControl = 0;
-
-        // Restore display rotation
-        asTransaction([&](Transaction& t) {
-            Rect displayBounds{mDisplaySize};
-            t.setDisplayProjection(mDisplayToken, ui::ROTATION_0, displayBounds, displayBounds);
-        });
     }
 
+    sp<SurfaceControl> mRootSurfaceControl;
     sp<SurfaceControl> mBGSurfaceControl;
     sp<SurfaceControl> mFGSurfaceControl;
     std::unique_ptr<ScreenCapture> mCapture;
-    sp<IBinder> mDisplayToken;
-    ui::Size mDisplaySize;
+    LayerCaptureArgs mCaptureArgs;
 };
 
 TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) {
@@ -92,7 +87,8 @@
     ASSERT_NO_FATAL_FAILURE(
             layer = createLayer("test", 32, 32,
                                 ISurfaceComposerClient::eSecure |
-                                        ISurfaceComposerClient::eFXSurfaceBufferQueue));
+                                        ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                mRootSurfaceControl.get()));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
 
     Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
@@ -100,14 +96,14 @@
     {
         // Ensure the UID is not root because root has all permissions
         UIDFaker f(AID_APP_START);
-        ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+        ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     }
 
     UIDFaker f(AID_SYSTEM);
 
     // By default the system can capture screenshots with secure layers but they
     // will be blacked out
-    ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults));
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
 
     {
         SCOPED_TRACE("as system");
@@ -117,10 +113,8 @@
 
     // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
     // to receive them...we are expected to take care with the results.
-    DisplayCaptureArgs args;
-    args.displayToken = mDisplay;
-    args.captureSecureLayers = true;
-    ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults));
+    mCaptureArgs.captureSecureLayers = true;
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
     ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
     sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
@@ -131,7 +125,8 @@
     ASSERT_NO_FATAL_FAILURE(
             parentLayer = createLayer("parent-test", 32, 32,
                                       ISurfaceComposerClient::eSecure |
-                                              ISurfaceComposerClient::eFXSurfaceBufferQueue));
+                                              ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                      mRootSurfaceControl.get()));
     ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parentLayer, Color::RED, 32, 32));
 
     sp<SurfaceControl> childLayer;
@@ -152,10 +147,8 @@
 
     // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
     // to receive them...we are expected to take care with the results.
-    DisplayCaptureArgs args;
-    args.displayToken = mDisplay;
-    args.captureSecureLayers = true;
-    ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults));
+    mCaptureArgs.captureSecureLayers = true;
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, mCaptureResults));
     ASSERT_TRUE(mCaptureResults.capturedSecureLayers);
     ScreenCapture sc(mCaptureResults.buffer, mCaptureResults.capturedHdrLayers);
     sc.expectColor(Rect(0, 0, 10, 10), Color::BLUE);
@@ -232,7 +225,7 @@
 
 TEST_F(ScreenCaptureTest, CaptureLayerExcludeThroughDisplayArgs) {
     mCaptureArgs.excludeHandles = {mFGSurfaceControl->getHandle()};
-    ScreenCapture::captureDisplay(&mCapture, mCaptureArgs);
+    ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
     mCapture->expectBGColor(0, 0);
     // Doesn't capture FG layer which is at 64, 64
     mCapture->expectBGColor(64, 64);
@@ -605,60 +598,55 @@
     mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED);
 }
 
-TEST_F(ScreenCaptureTest, CaptureDisplayWithUid) {
-    uid_t fakeUid = 12345;
+TEST_F(ScreenCaptureTest, ScreenshotProtectedBuffer) {
+    const uint32_t bufferWidth = 60;
+    const uint32_t bufferHeight = 60;
 
-    DisplayCaptureArgs captureArgs;
-    captureArgs.displayToken = mDisplay;
+    sp<SurfaceControl> layer =
+            createLayer(String8("Colored surface"), bufferWidth, bufferHeight,
+                        ISurfaceComposerClient::eFXSurfaceBufferState, mRootSurfaceControl.get());
 
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
-                                                ISurfaceComposerClient::eFXSurfaceBufferQueue,
-                                                mBGSurfaceControl.get()));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+    Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true);
 
-    Transaction().show(layer).setLayer(layer, INT32_MAX).apply();
+    sp<Surface> surface = layer->getSurface();
+    ASSERT_TRUE(surface != nullptr);
+    sp<ANativeWindow> anw(surface);
 
-    // Make sure red layer with the background layer is screenshot.
-    ScreenCapture::captureDisplay(&mCapture, captureArgs);
-    mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    mCapture->expectBorder(Rect(0, 0, 32, 32), {63, 63, 195, 255});
+    ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU));
+    ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), GRALLOC_USAGE_PROTECTED));
 
-    // From non system uid, can't request screenshot without a specified uid.
-    UIDFaker f(fakeUid);
-    ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(captureArgs, mCaptureResults));
+    int fenceFd;
+    ANativeWindowBuffer* buf = nullptr;
 
-    // Make screenshot request with current uid set. No layers were created with the current
-    // uid so screenshot will be black.
-    captureArgs.uid = fakeUid;
-    ScreenCapture::captureDisplay(&mCapture, captureArgs);
-    mCapture->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
-    mCapture->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+    // End test if device does not support USAGE_PROTECTED
+    // b/309965549 This check does not exit the test when running on AVDs
+    status_t err = anw->dequeueBuffer(anw.get(), &buf, &fenceFd);
+    if (err) {
+        return;
+    }
+    anw->queueBuffer(anw.get(), buf, fenceFd);
 
-    sp<SurfaceControl> layerWithFakeUid;
-    // Create a new layer with the current uid
-    ASSERT_NO_FATAL_FAILURE(layerWithFakeUid =
-                                    createLayer("new test layer", 32, 32,
-                                                ISurfaceComposerClient::eFXSurfaceBufferQueue,
-                                                mBGSurfaceControl.get()));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerWithFakeUid, Color::GREEN, 32, 32));
-    Transaction()
-            .show(layerWithFakeUid)
-            .setLayer(layerWithFakeUid, INT32_MAX)
-            .setPosition(layerWithFakeUid, 128, 128)
-            .apply();
+    // USAGE_PROTECTED buffer is read as a black screen
+    ScreenCaptureResults captureResults;
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, captureResults));
 
-    // Screenshot from the fakeUid caller with the uid requested allows the layer
-    // with that uid to be screenshotted. Everything else is black
-    ScreenCapture::captureDisplay(&mCapture, captureArgs);
-    mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN);
-    mCapture->expectBorder(Rect(128, 128, 160, 160), Color::BLACK);
+    ScreenCapture sc(captureResults.buffer, captureResults.capturedHdrLayers);
+    sc.expectColor(Rect(0, 0, bufferWidth, bufferHeight), Color::BLACK);
+
+    // Reading color data will expectedly result in crash, only check usage bit
+    // b/309965549 Checking that the usage bit is protected does not work for
+    // devices that do not support usage protected.
+    mCaptureArgs.allowProtected = true;
+    ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(mCaptureArgs, captureResults));
+    // ASSERT_EQ(GRALLOC_USAGE_PROTECTED, GRALLOC_USAGE_PROTECTED &
+    // captureResults.buffer->getUsage());
 }
 
-TEST_F(ScreenCaptureTest, CaptureDisplayPrimaryDisplayOnly) {
+TEST_F(ScreenCaptureTest, CaptureLayer) {
     sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect));
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 0, 0,
+                                                ISurfaceComposerClient::eFXSurfaceEffect,
+                                                mRootSurfaceControl.get()));
 
     const Color layerColor = Color::RED;
     const Rect bounds = Rect(10, 10, 40, 40);
@@ -666,17 +654,13 @@
     Transaction()
             .show(layer)
             .hide(mFGSurfaceControl)
-            .setLayerStack(layer, ui::DEFAULT_LAYER_STACK)
             .setLayer(layer, INT32_MAX)
             .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
             .setCrop(layer, bounds)
             .apply();
 
-    DisplayCaptureArgs captureArgs;
-    captureArgs.displayToken = mDisplay;
-
     {
-        ScreenCapture::captureDisplay(&mCapture, captureArgs);
+        ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
         mCapture->expectColor(bounds, layerColor);
         mCapture->expectBorder(bounds, {63, 63, 195, 255});
     }
@@ -689,17 +673,18 @@
     {
         // Can't screenshot test layer since it now has flag
         // eLayerSkipScreenshot
-        ScreenCapture::captureDisplay(&mCapture, captureArgs);
+        ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
         mCapture->expectColor(bounds, {63, 63, 195, 255});
         mCapture->expectBorder(bounds, {63, 63, 195, 255});
     }
 }
 
-TEST_F(ScreenCaptureTest, CaptureDisplayChildPrimaryDisplayOnly) {
+TEST_F(ScreenCaptureTest, CaptureLayerChild) {
     sp<SurfaceControl> layer;
     sp<SurfaceControl> childLayer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect));
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 0, 0,
+                                                ISurfaceComposerClient::eFXSurfaceEffect,
+                                                mRootSurfaceControl.get()));
     ASSERT_NO_FATAL_FAILURE(childLayer = createLayer("test layer", 0, 0,
                                                      ISurfaceComposerClient::eFXSurfaceEffect,
                                                      layer.get()));
@@ -713,7 +698,6 @@
             .show(layer)
             .show(childLayer)
             .hide(mFGSurfaceControl)
-            .setLayerStack(layer, ui::DEFAULT_LAYER_STACK)
             .setLayer(layer, INT32_MAX)
             .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255})
             .setColor(childLayer, {childColor.r / 255, childColor.g / 255, childColor.b / 255})
@@ -721,11 +705,8 @@
             .setCrop(childLayer, childBounds)
             .apply();
 
-    DisplayCaptureArgs captureArgs;
-    captureArgs.displayToken = mDisplay;
-
     {
-        ScreenCapture::captureDisplay(&mCapture, captureArgs);
+        ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
         mCapture->expectColor(childBounds, childColor);
         mCapture->expectBorder(childBounds, layerColor);
         mCapture->expectBorder(bounds, {63, 63, 195, 255});
@@ -739,7 +720,7 @@
     {
         // Can't screenshot child layer since the parent has the flag
         // eLayerSkipScreenshot
-        ScreenCapture::captureDisplay(&mCapture, captureArgs);
+        ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
         mCapture->expectColor(childBounds, {63, 63, 195, 255});
         mCapture->expectBorder(childBounds, {63, 63, 195, 255});
         mCapture->expectBorder(bounds, {63, 63, 195, 255});
@@ -860,14 +841,10 @@
 
     Transaction().show(layer).hide(mFGSurfaceControl).reparent(layer, nullptr).apply();
 
-    DisplayCaptureArgs displayCaptureArgs;
-    displayCaptureArgs.displayToken = mDisplay;
-
     {
         // Validate that the red layer is not on screen
-        ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
-        mCapture->expectColor(Rect(0, 0, mDisplaySize.width, mDisplaySize.height),
-                              {63, 63, 195, 255});
+        ScreenCapture::captureLayers(&mCapture, mCaptureArgs);
+        mCapture->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), {63, 63, 195, 255});
     }
 
     LayerCaptureArgs captureArgs;
@@ -878,42 +855,6 @@
     mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
 }
 
-TEST_F(ScreenCaptureTest, CaptureDisplayWith90DegRotation) {
-    asTransaction([&](Transaction& t) {
-        Rect newDisplayBounds{mDisplaySize.height, mDisplaySize.width};
-        t.setDisplayProjection(mDisplayToken, ui::ROTATION_90, newDisplayBounds, newDisplayBounds);
-    });
-
-    DisplayCaptureArgs displayCaptureArgs;
-    displayCaptureArgs.displayToken = mDisplayToken;
-    displayCaptureArgs.width = mDisplaySize.width;
-    displayCaptureArgs.height = mDisplaySize.height;
-    displayCaptureArgs.useIdentityTransform = true;
-    ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
-
-    mCapture->expectBGColor(0, 0);
-    mCapture->expectFGColor(mDisplaySize.width - 65, 65);
-}
-
-TEST_F(ScreenCaptureTest, CaptureDisplayWith270DegRotation) {
-    asTransaction([&](Transaction& t) {
-        Rect newDisplayBounds{mDisplaySize.height, mDisplaySize.width};
-        t.setDisplayProjection(mDisplayToken, ui::ROTATION_270, newDisplayBounds, newDisplayBounds);
-    });
-
-    DisplayCaptureArgs displayCaptureArgs;
-    displayCaptureArgs.displayToken = mDisplayToken;
-    displayCaptureArgs.width = mDisplaySize.width;
-    displayCaptureArgs.height = mDisplaySize.height;
-    displayCaptureArgs.useIdentityTransform = true;
-    ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
-
-    std::this_thread::sleep_for(std::chrono::seconds{5});
-
-    mCapture->expectBGColor(mDisplayWidth - 1, mDisplaySize.height - 1);
-    mCapture->expectFGColor(65, mDisplaySize.height - 65);
-}
-
 TEST_F(ScreenCaptureTest, CaptureNonHdrLayer) {
     sp<SurfaceControl> layer;
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
diff --git a/services/surfaceflinger/tests/TextureFiltering_test.cpp b/services/surfaceflinger/tests/TextureFiltering_test.cpp
index d0ab105..c5d118c 100644
--- a/services/surfaceflinger/tests/TextureFiltering_test.cpp
+++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp
@@ -80,29 +80,22 @@
     sp<SurfaceControl> mParent;
     sp<SurfaceControl> mLayer;
     std::unique_ptr<ScreenCapture> mCapture;
+    gui::LayerCaptureArgs captureArgs;
 };
 
 TEST_F(TextureFilteringTest, NoFiltering) {
-    gui::DisplayCaptureArgs captureArgs;
-    captureArgs.displayToken = mDisplay;
-    captureArgs.width = 100;
-    captureArgs.height = 100;
-    captureArgs.sourceCrop = Rect{100, 100};
-    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+    captureArgs.sourceCrop = Rect{0, 0, 100, 100};
+    captureArgs.layerHandle = mParent->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
 
     mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
     mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
 }
 
 TEST_F(TextureFilteringTest, BufferCropNoFiltering) {
-    Transaction().setBufferCrop(mLayer, Rect{0, 0, 100, 100}).apply();
-
-    gui::DisplayCaptureArgs captureArgs;
-    captureArgs.displayToken = mDisplay;
-    captureArgs.width = 100;
-    captureArgs.height = 100;
     captureArgs.sourceCrop = Rect{0, 0, 100, 100};
-    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+    captureArgs.layerHandle = mParent->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
 
     mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED);
     mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE);
@@ -112,24 +105,20 @@
 TEST_F(TextureFilteringTest, BufferCropIsFiltered) {
     Transaction().setBufferCrop(mLayer, Rect{25, 25, 75, 75}).apply();
 
-    gui::DisplayCaptureArgs captureArgs;
-    captureArgs.displayToken = mDisplay;
-    captureArgs.width = 100;
-    captureArgs.height = 100;
     captureArgs.sourceCrop = Rect{0, 0, 100, 100};
-    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+    captureArgs.layerHandle = mParent->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
 
     expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
 }
 
 // Expect filtering because the output source crop is stretched to the output buffer's size.
 TEST_F(TextureFilteringTest, OutputSourceCropIsFiltered) {
-    gui::DisplayCaptureArgs captureArgs;
-    captureArgs.displayToken = mDisplay;
-    captureArgs.width = 100;
-    captureArgs.height = 100;
+    captureArgs.frameScaleX = 2;
+    captureArgs.frameScaleY = 2;
     captureArgs.sourceCrop = Rect{25, 25, 75, 75};
-    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+    captureArgs.layerHandle = mParent->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
 
     expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
 }
@@ -138,20 +127,17 @@
 // buffer's size.
 TEST_F(TextureFilteringTest, LayerCropOutputSourceCropIsFiltered) {
     Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
-
-    gui::DisplayCaptureArgs captureArgs;
-    captureArgs.displayToken = mDisplay;
-    captureArgs.width = 100;
-    captureArgs.height = 100;
+    captureArgs.frameScaleX = 2;
+    captureArgs.frameScaleY = 2;
     captureArgs.sourceCrop = Rect{25, 25, 75, 75};
-    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+    captureArgs.layerHandle = mParent->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
 
     expectFiltered({0, 0, 50, 100}, {50, 0, 100, 100});
 }
 
 // Expect filtering because the layer is scaled up.
 TEST_F(TextureFilteringTest, LayerCaptureWithScalingIsFiltered) {
-    LayerCaptureArgs captureArgs;
     captureArgs.layerHandle = mLayer->getHandle();
     captureArgs.frameScaleX = 2;
     captureArgs.frameScaleY = 2;
@@ -162,7 +148,6 @@
 
 // Expect no filtering because the output buffer's size matches the source crop.
 TEST_F(TextureFilteringTest, LayerCaptureOutputSourceCropNoFiltering) {
-    LayerCaptureArgs captureArgs;
     captureArgs.layerHandle = mLayer->getHandle();
     captureArgs.sourceCrop = Rect{25, 25, 75, 75};
     ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -176,7 +161,6 @@
 TEST_F(TextureFilteringTest, LayerCaptureWithCropNoFiltering) {
     Transaction().setCrop(mLayer, Rect{10, 10, 90, 90}).apply();
 
-    LayerCaptureArgs captureArgs;
     captureArgs.layerHandle = mLayer->getHandle();
     captureArgs.sourceCrop = Rect{25, 25, 75, 75};
     ScreenCapture::captureLayers(&mCapture, captureArgs);
@@ -187,12 +171,9 @@
 
 // Expect no filtering because the output source crop and output buffer are the same size.
 TEST_F(TextureFilteringTest, OutputSourceCropDisplayFrameMatchNoFiltering) {
-    gui::DisplayCaptureArgs captureArgs;
-    captureArgs.displayToken = mDisplay;
-    captureArgs.width = 50;
-    captureArgs.height = 50;
+    captureArgs.layerHandle = mLayer->getHandle();
     captureArgs.sourceCrop = Rect{25, 25, 75, 75};
-    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
 
     mCapture->expectColor(Rect{0, 0, 25, 50}, Color::RED);
     mCapture->expectColor(Rect{25, 0, 50, 50}, Color::BLUE);
@@ -202,9 +183,8 @@
 TEST_F(TextureFilteringTest, LayerCropDisplayFrameMatchNoFiltering) {
     Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply();
 
-    gui::DisplayCaptureArgs captureArgs;
-    captureArgs.displayToken = mDisplay;
-    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+    captureArgs.layerHandle = mLayer->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
 
     mCapture->expectColor(Rect{25, 25, 50, 75}, Color::RED);
     mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
@@ -214,9 +194,8 @@
 TEST_F(TextureFilteringTest, ParentCropNoFiltering) {
     Transaction().setCrop(mParent, Rect{25, 25, 75, 75}).apply();
 
-    gui::DisplayCaptureArgs captureArgs;
-    captureArgs.displayToken = mDisplay;
-    ScreenCapture::captureDisplay(&mCapture, captureArgs);
+    captureArgs.layerHandle = mLayer->getHandle();
+    ScreenCapture::captureLayers(&mCapture, captureArgs);
 
     mCapture->expectColor(Rect{25, 25, 50, 75}, Color::RED);
     mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE);
@@ -226,7 +205,6 @@
 TEST_F(TextureFilteringTest, ParentHasTransformNoFiltering) {
     Transaction().setPosition(mParent, 100, 100).apply();
 
-    LayerCaptureArgs captureArgs;
     captureArgs.layerHandle = mParent->getHandle();
     captureArgs.sourceCrop = Rect{0, 0, 100, 100};
     ScreenCapture::captureLayers(&mCapture, captureArgs);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 5a3bca1..6e6c6d8 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -45,7 +45,7 @@
 cc_aconfig_library {
     name: "libsurfaceflingerflags_test",
     aconfig_declarations: "surfaceflinger_flags",
-    test: true,
+    mode: "test",
 }
 
 cc_test {
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 58d7a40..788fa51 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -38,9 +38,13 @@
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/Hal.h"
 #include "DisplayIdentificationTestHelpers.h"
+#include "FlagManager.h"
+#include "FlagUtils.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockHWC2.h"
 
+#include <com_android_graphics_surfaceflinger_flags.h>
+
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
 
@@ -57,7 +61,6 @@
 using hal::IComposerClient;
 using ::testing::_;
 using ::testing::DoAll;
-using ::testing::ElementsAreArray;
 using ::testing::Return;
 using ::testing::SetArgPointee;
 using ::testing::StrictMock;
@@ -217,7 +220,102 @@
     }
 }
 
-TEST_F(HWComposerTest, getModesWithDisplayConfigurations) {
+TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_OFF) {
+    // if vrr_config is off, getDisplayConfigurationsSupported() is off as well
+    // then getModesWithLegacyDisplayConfigs should be called instead
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::vrr_config, false);
+    ASSERT_FALSE(FlagManager::getInstance().vrr_config());
+
+    constexpr hal::HWDisplayId kHwcDisplayId = 2;
+    constexpr hal::HWConfigId kConfigId = 42;
+    constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps
+
+    expectHotplugConnect(kHwcDisplayId);
+    const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+    ASSERT_TRUE(info);
+
+    EXPECT_CALL(*mHal, getDisplayConfigurationsSupported()).WillRepeatedly(Return(false));
+
+    {
+        EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
+                .WillOnce(Return(HalError::BAD_DISPLAY));
+        EXPECT_TRUE(mHwc.getModes(info->id, kMaxFrameIntervalNs).empty());
+    }
+    {
+        constexpr int32_t kWidth = 480;
+        constexpr int32_t kHeight = 720;
+        constexpr int32_t kConfigGroup = 1;
+        constexpr int32_t kVsyncPeriod = 16666667;
+
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::WIDTH,
+                                        _))
+                .WillRepeatedly(DoAll(SetArgPointee<3>(kWidth), Return(HalError::NONE)));
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId,
+                                        IComposerClient::Attribute::HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<3>(kHeight), Return(HalError::NONE)));
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId,
+                                        IComposerClient::Attribute::CONFIG_GROUP, _))
+                .WillRepeatedly(DoAll(SetArgPointee<3>(kConfigGroup), Return(HalError::NONE)));
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId,
+                                        IComposerClient::Attribute::VSYNC_PERIOD, _))
+                .WillRepeatedly(DoAll(SetArgPointee<3>(kVsyncPeriod), Return(HalError::NONE)));
+
+        // Optional Parameters UNSUPPORTED
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_X,
+                                        _))
+                .WillOnce(Return(HalError::UNSUPPORTED));
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_Y,
+                                        _))
+                .WillOnce(Return(HalError::UNSUPPORTED));
+
+        EXPECT_CALL(*mHal, getDisplayConfigs(kHwcDisplayId, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(std::vector<hal::HWConfigId>{kConfigId}),
+                                      Return(HalError::NONE)));
+
+        auto modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+        EXPECT_EQ(modes.size(), size_t{1});
+        EXPECT_EQ(modes.front().hwcId, kConfigId);
+        EXPECT_EQ(modes.front().width, kWidth);
+        EXPECT_EQ(modes.front().height, kHeight);
+        EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+        EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+        EXPECT_EQ(modes.front().dpiX, -1);
+        EXPECT_EQ(modes.front().dpiY, -1);
+
+        // Optional parameters are supported
+        constexpr int32_t kDpi = 320;
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_X,
+                                        _))
+                .WillOnce(DoAll(SetArgPointee<3>(kDpi), Return(HalError::NONE)));
+        EXPECT_CALL(*mHal,
+                    getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::DPI_Y,
+                                        _))
+                .WillOnce(DoAll(SetArgPointee<3>(kDpi), Return(HalError::NONE)));
+
+        modes = mHwc.getModes(info->id, kMaxFrameIntervalNs);
+        EXPECT_EQ(modes.size(), size_t{1});
+        EXPECT_EQ(modes.front().hwcId, kConfigId);
+        EXPECT_EQ(modes.front().width, kWidth);
+        EXPECT_EQ(modes.front().height, kHeight);
+        EXPECT_EQ(modes.front().configGroup, kConfigGroup);
+        EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
+        // DPI values are scaled by 1000 in the legacy implementation.
+        EXPECT_EQ(modes.front().dpiX, kDpi / 1000.f);
+        EXPECT_EQ(modes.front().dpiY, kDpi / 1000.f);
+    }
+}
+
+TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) {
+    SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::vrr_config, true);
+    ASSERT_TRUE(FlagManager::getInstance().vrr_config());
+
     constexpr hal::HWDisplayId kHwcDisplayId = 2;
     constexpr hal::HWConfigId kConfigId = 42;
     constexpr int32_t kMaxFrameIntervalNs = 50000000; // 20Fps
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 190c0e8..befef48 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -839,6 +839,81 @@
     ASSERT_EQ(30_Hz, summary[0].desiredRefreshRate);
 }
 
+TEST_F(LayerHistoryIntegrationTest, hidingLayerUpdatesLayerHistory) {
+    createLegacyAndFrontedEndLayer(1);
+    nsecs_t time = systemTime();
+    updateLayerSnapshotsAndLayerHistory(time);
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    setBuffer(1);
+    updateLayerSnapshotsAndLayerHistory(time);
+    auto summary = summarizeLayerHistory(time);
+    ASSERT_EQ(1u, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+
+    hideLayer(1);
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    summary = summarizeLayerHistory(time);
+    EXPECT_TRUE(summary.empty());
+    EXPECT_EQ(0u, activeLayerCount());
+}
+
+TEST_F(LayerHistoryIntegrationTest, showingLayerUpdatesLayerHistory) {
+    createLegacyAndFrontedEndLayer(1);
+    nsecs_t time = systemTime();
+    updateLayerSnapshotsAndLayerHistory(time);
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+    hideLayer(1);
+    setBuffer(1);
+    updateLayerSnapshotsAndLayerHistory(time);
+    auto summary = summarizeLayerHistory(time);
+    EXPECT_TRUE(summary.empty());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    showLayer(1);
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    summary = summarizeLayerHistory(time);
+    ASSERT_EQ(1u, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+}
+
+TEST_F(LayerHistoryIntegrationTest, updatingGeometryUpdatesWeight) {
+    createLegacyAndFrontedEndLayer(1);
+    nsecs_t time = systemTime();
+    updateLayerSnapshotsAndLayerHistory(time);
+    EXPECT_EQ(1u, layerCount());
+    EXPECT_EQ(0u, activeLayerCount());
+
+    setBuffer(1,
+              std::make_shared<
+                      renderengine::mock::FakeExternalTexture>(100U /*width*/, 100U /*height*/, 1,
+                                                               HAL_PIXEL_FORMAT_RGBA_8888,
+                                                               GRALLOC_USAGE_PROTECTED /*usage*/));
+    mFlinger.setLayerHistoryDisplayArea(100 * 100);
+    updateLayerSnapshotsAndLayerHistory(time);
+    auto summary = summarizeLayerHistory(time);
+    ASSERT_EQ(1u, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+
+    auto startingWeight = summary[0].weight;
+
+    setMatrix(1, 0.1f, 0.f, 0.f, 0.1f);
+    updateLayerSnapshotsAndLayerHistory(time);
+
+    summary = summarizeLayerHistory(time);
+    ASSERT_EQ(1u, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[0].vote);
+    EXPECT_EQ(1u, activeLayerCount());
+    EXPECT_GT(startingWeight, summary[0].weight);
+}
+
 class LayerHistoryIntegrationTestParameterized
       : public LayerHistoryIntegrationTest,
         public testing::WithParamInterface<std::chrono::nanoseconds> {};
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 57babaf..2cc6dc7 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -789,6 +789,143 @@
     EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy,
               scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
     EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    // ROOT
+    // ├── 1
+    // │   ├── 11
+    // │   │   └── 111
+    // │   ├── 12 (frame rate set to default with strategy default)
+    // │   │   ├── 121
+    // │   │   └── 122 (frame rate set to 123.f)
+    // │   │       └── 1221
+    // │   └── 13
+    // └── 2
+    setFrameRate(12, -1.f, 0, 0);
+    setFrameRateSelectionStrategy(12, 0 /* Default */);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    // verify parent 1 gets no vote
+    EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
+              scheduler::FrameRateCompatibility::NoVote);
+    EXPECT_FALSE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    // verify layer 12 and all descendants (121, 122, 1221) get the requested vote
+    EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
+    EXPECT_EQ(getSnapshot({.id = 12})->frameRate.vote.type,
+              scheduler::FrameRateCompatibility::NoVote);
+    EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+    EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    EXPECT_FALSE(getSnapshot({.id = 121})->frameRate.vote.rate.isValid());
+    EXPECT_EQ(getSnapshot({.id = 121})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+    EXPECT_EQ(getSnapshot({.id = 121})->frameRate.vote.type,
+              scheduler::FrameRateCompatibility::Default);
+    EXPECT_TRUE(getSnapshot({.id = 121})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 123.f);
+    EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+    EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.rate.getValue(), 123.f);
+    EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+    EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
+}
+
+TEST_F(LayerSnapshotTest, frameRateSelectionStrategyWithCategory) {
+    // ROOT
+    // ├── 1
+    // │   ├── 11
+    // │   │   └── 111
+    // │   ├── 12 (frame rate category set to high with strategy OverrideChildren)
+    // │   │   ├── 121
+    // │   │   └── 122 (frame rate set to 123.f but should be overridden by layer 12)
+    // │   │       └── 1221
+    // │   └── 13
+    // └── 2
+    setFrameRateCategory(12, 4 /* high */);
+    setFrameRate(122, 123.f, 0, 0);
+    setFrameRateSelectionStrategy(12, 1 /* OverrideChildren */);
+
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    // verify parent 1 gets no vote
+    EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
+              scheduler::FrameRateCompatibility::NoVote);
+    EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    // verify layer 12 and all descendants (121, 122, 1221) get the requested vote
+    EXPECT_EQ(getSnapshot({.id = 12})->frameRate.category, FrameRateCategory::High);
+    EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+    EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    EXPECT_EQ(getSnapshot({.id = 121})->frameRate.category, FrameRateCategory::High);
+    EXPECT_EQ(getSnapshot({.id = 121})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+    EXPECT_TRUE(getSnapshot({.id = 121})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    EXPECT_EQ(getSnapshot({.id = 122})->frameRate.category, FrameRateCategory::High);
+    EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+    EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.category, FrameRateCategory::High);
+    EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::OverrideChildren);
+    EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    // ROOT
+    // ├── 1
+    // │   ├── 11
+    // │   │   └── 111
+    // │   ├── 12 (frame rate category to default with strategy default)
+    // │   │   ├── 121
+    // │   │   └── 122 (frame rate set to 123.f)
+    // │   │       └── 1221
+    // │   └── 13
+    // └── 2
+    setFrameRateCategory(12, 0 /* default */);
+    setFrameRateSelectionStrategy(12, 0 /* Default */);
+    UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+    // verify parent 1 gets no vote
+    EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
+              scheduler::FrameRateCompatibility::NoVote);
+    EXPECT_EQ(getSnapshot({.id = 1})->frameRate.category, FrameRateCategory::Default);
+    EXPECT_FALSE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    // verify layer 12 and all descendants (121, 122, 1221) get the requested vote
+    EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
+    EXPECT_EQ(getSnapshot({.id = 12})->frameRate.vote.type,
+              scheduler::FrameRateCompatibility::NoVote);
+    EXPECT_EQ(getSnapshot({.id = 12})->frameRate.category, FrameRateCategory::Default);
+    EXPECT_EQ(getSnapshot({.id = 12})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+    EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
+    EXPECT_EQ(getSnapshot({.id = 121})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+    EXPECT_EQ(getSnapshot({.id = 121})->frameRate.category, FrameRateCategory::Default);
+    EXPECT_EQ(getSnapshot({.id = 121})->frameRate.vote.type,
+              scheduler::FrameRateCompatibility::Default);
+    EXPECT_TRUE(getSnapshot({.id = 121})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 123.f);
+    EXPECT_EQ(getSnapshot({.id = 122})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+    EXPECT_EQ(getSnapshot({.id = 122})->frameRate.category, FrameRateCategory::Default);
+    EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
+
+    EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.rate.getValue(), 123.f);
+    EXPECT_EQ(getSnapshot({.id = 1221})->frameRateSelectionStrategy,
+              scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
+    EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.category, FrameRateCategory::Default);
+    EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
 }
 
 TEST_F(LayerSnapshotTest, skipRoundCornersWhenProtected) {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index d0b2199..bca14f5 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -623,6 +623,9 @@
 
     void releaseLegacyLayer(uint32_t sequence) { mFlinger->mLegacyLayers.erase(sequence); };
 
+    auto setLayerHistoryDisplayArea(uint32_t displayArea) {
+        return mFlinger->mScheduler->onActiveDisplayAreaChanged(displayArea);
+    };
     auto updateLayerHistory(nsecs_t now) { return mFlinger->updateLayerHistory(now); };
     auto setDaltonizerType(ColorBlindnessType type) {
         mFlinger->mDaltonizer.setType(type);
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 5d7a4aa..cae51a5 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -183,8 +183,12 @@
                 .library_namespace = library_namespace,
             };
             so = android_dlopen_ext(lib_name.c_str(), LIB_DL_FLAGS, &dlextinfo);
-            ALOGE("Could not load %s from updatable gfx driver namespace: %s.",
-                  lib_name.c_str(), dlerror());
+            if (!so) {
+                ALOGE(
+                    "Could not load %s from updatable gfx driver namespace: "
+                    "%s.",
+                    lib_name.c_str(), dlerror());
+            }
         } else {
             // load built-in driver
             so = android_load_sphal_library(lib_name.c_str(), LIB_DL_FLAGS);