Merge "Implement host service manager"
diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp
index bc99f4d..a5f98c2 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -142,12 +142,10 @@
         }
     }
 
-    PipeRelay relay(out, err, interfaceName, instanceName);
-
-    if (relay.initCheck() != OK) {
-        std::string msg = "PipeRelay::initCheck() FAILED w/ " + std::to_string(relay.initCheck());
-        err << msg << std::endl;
-        LOG(ERROR) << msg;
+    auto relay = PipeRelay::create(out, err, interfaceName + "/" + instanceName);
+    if (!relay.ok()) {
+        err << "Unable to create PipeRelay: " << relay.error() << std::endl;
+        LOG(ERROR) << "Unable to create PipeRelay: " << relay.error();
         return IO_ERROR;
     }
 
@@ -155,7 +153,7 @@
         native_handle_create(1 /* numFds */, 0 /* numInts */),
         native_handle_delete);
 
-    fdHandle->data[0] = relay.fd();
+    fdHandle->data[0] = relay.value()->fd().get();
 
     hardware::Return<void> ret = base->debug(fdHandle.get(), convert(options));
 
diff --git a/cmds/lshal/PipeRelay.cpp b/cmds/lshal/PipeRelay.cpp
index 4e97636..0c3fb96 100644
--- a/cmds/lshal/PipeRelay.cpp
+++ b/cmds/lshal/PipeRelay.cpp
@@ -16,143 +16,93 @@
 
 #include "PipeRelay.h"
 
-#include <sys/select.h>
-#include <sys/time.h>
+#include <sys/poll.h>
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <atomic>
+#include <chrono>
+#include <optional>
 
-#include <utils/Thread.h>
+#include <android-base/unique_fd.h>
+
+using android::base::borrowed_fd;
+using android::base::Result;
+using android::base::unique_fd;
+using std::chrono_literals::operator""ms;
 
 namespace android {
 namespace lshal {
-
-static constexpr struct timeval READ_TIMEOUT { .tv_sec = 1, .tv_usec = 0 };
-
-static std::string getThreadName(std::string interfaceName, const std::string &instanceName) {
-    auto dot = interfaceName.rfind(".");
-    if (dot != std::string::npos) interfaceName = interfaceName.substr(dot + 1);
-    return "RelayThread_" + interfaceName + "_" + instanceName;
+Result<std::unique_ptr<PipeRelay>> PipeRelay::create(std::ostream& os,
+                                                     const NullableOStream<std::ostream>& err,
+                                                     const std::string& fqName) {
+    auto pipeRelay = std::unique_ptr<PipeRelay>(new PipeRelay());
+    unique_fd rfd;
+    if (!android::base::Pipe(&rfd, &pipeRelay->mWrite)) {
+        return android::base::ErrnoError() << "pipe()";
+    }
+    // Workaround for b/111997867: need a separate FD trigger because rfd can't receive POLLHUP
+    // when the write end is closed after the write end was sent through hwbinder.
+    unique_fd rfdTrigger;
+    if (!android::base::Pipe(&rfdTrigger, &pipeRelay->mWriteTrigger)) {
+        return android::base::ErrnoError() << "pipe() for trigger";
+    }
+    pipeRelay->mThread =
+            std::make_unique<std::thread>(&PipeRelay::thread, std::move(rfd), std::move(rfdTrigger),
+                                          &os, &err, fqName);
+    return pipeRelay;
 }
 
-struct PipeRelay::RelayThread : public Thread {
-    explicit RelayThread(int fd, std::ostream &os, const NullableOStream<std::ostream> &err,
-                         const std::string &fqName);
+void PipeRelay::thread(unique_fd rfd, unique_fd rfdTrigger, std::ostream* out,
+                       const NullableOStream<std::ostream>* err, std::string fqName) {
+    while (true) {
+        pollfd pfd[2];
+        pfd[0] = {.fd = rfd.get(), .events = POLLIN};
+        pfd[1] = {.fd = rfdTrigger.get(), .events = 0};
 
-    bool threadLoop() override;
-    void setFinished();
-
-private:
-    int mFd;
-    std::ostream &mOutStream;
-    NullableOStream<std::ostream> mErrStream;
-
-    // If we were to use requestExit() and exitPending() instead, threadLoop()
-    // may not run at all by the time ~PipeRelay is called (i.e. debug() has
-    // returned from HAL). By using our own flag, we ensure that select() and
-    // read() are executed until data are drained.
-    std::atomic_bool mFinished;
-
-    std::string mFqName;
-
-    DISALLOW_COPY_AND_ASSIGN(RelayThread);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os,
-                                    const NullableOStream<std::ostream> &err,
-                                    const std::string &fqName)
-      : mFd(fd), mOutStream(os), mErrStream(err), mFinished(false), mFqName(fqName) {}
-
-bool PipeRelay::RelayThread::threadLoop() {
-    char buffer[1024];
-
-    fd_set set;
-    FD_ZERO(&set);
-    FD_SET(mFd, &set);
-
-    struct timeval timeout = READ_TIMEOUT;
-
-    int res = TEMP_FAILURE_RETRY(select(mFd + 1, &set, nullptr, nullptr, &timeout));
-    if (res < 0) {
-        mErrStream << "debug " << mFqName << ": select() failed";
-        return false;
-    }
-
-    if (res == 0 || !FD_ISSET(mFd, &set)) {
-        if (mFinished) {
-            mErrStream << "debug " << mFqName
-                       << ": timeout reading from pipe, output may be truncated.";
-            return false;
+        int pollRes = poll(pfd, arraysize(pfd), -1 /* infinite timeout */);
+        if (pollRes < 0) {
+            int savedErrno = errno;
+            (*err) << "debug " << fqName << ": poll() failed: " << strerror(savedErrno)
+                   << std::endl;
+            break;
         }
-        // timeout, but debug() has not returned, so wait for HAL to finish.
-        return true;
-    }
 
-    // FD_ISSET(mFd, &set) == true. Data available, start reading
-    ssize_t n = TEMP_FAILURE_RETRY(read(mFd, buffer, sizeof(buffer)));
-
-    if (n < 0) {
-        mErrStream << "debug " << mFqName << ": read() failed";
-    }
-
-    if (n <= 0) {
-        return false;
-    }
-
-    mOutStream.write(buffer, n);
-
-    return true;
-}
-
-void PipeRelay::RelayThread::setFinished() {
-    mFinished = true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-PipeRelay::PipeRelay(std::ostream &os, const NullableOStream<std::ostream> &err,
-                     const std::string &interfaceName, const std::string &instanceName)
-      : mInitCheck(NO_INIT) {
-    int res = pipe(mFds);
-
-    if (res < 0) {
-        mInitCheck = -errno;
-        return;
-    }
-
-    mThread = new RelayThread(mFds[0], os, err, interfaceName + "/" + instanceName);
-    mInitCheck = mThread->run(getThreadName(interfaceName, instanceName).c_str());
-}
-
-void PipeRelay::CloseFd(int *fd) {
-    if (*fd >= 0) {
-        close(*fd);
-        *fd = -1;
+        if (pfd[0].revents & POLLIN) {
+            char buffer[1024];
+            ssize_t n = TEMP_FAILURE_RETRY(read(rfd.get(), buffer, sizeof(buffer)));
+            if (n < 0) {
+                int savedErrno = errno;
+                (*err) << "debug " << fqName << ": read() failed: " << strerror(savedErrno)
+                       << std::endl;
+                break;
+            }
+            if (n == 0) {
+                (*err) << "Warning: debug " << fqName << ": poll() indicates POLLIN but no data"
+                       << std::endl;
+                continue;
+            }
+            out->write(buffer, n);
+        }
+        if (pfd[0].revents & POLLHUP) {
+            break;
+        }
+        if (pfd[1].revents & POLLHUP) {
+            // ~PipeRelay is called on the main thread. |mWrite| has been flushed and closed.
+            // Ensure that our read end of the pipe doesn't have pending data, then exit.
+            if ((pfd[0].revents & POLLIN) == 0) {
+                break;
+            }
+        }
     }
 }
 
 PipeRelay::~PipeRelay() {
-    CloseFd(&mFds[1]);
-
-    if (mThread != nullptr) {
-        mThread->setFinished();
+    mWrite.reset();
+    mWriteTrigger.reset();
+    if (mThread != nullptr && mThread->joinable()) {
         mThread->join();
-        mThread.clear();
     }
-
-    CloseFd(&mFds[0]);
 }
 
-status_t PipeRelay::initCheck() const {
-    return mInitCheck;
-}
-
-int PipeRelay::fd() const {
-    return mFds[1];
-}
-
-}  // namespace lshal
-}  // namespace android
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/PipeRelay.h b/cmds/lshal/PipeRelay.h
index bd994b4..45ba982 100644
--- a/cmds/lshal/PipeRelay.h
+++ b/cmds/lshal/PipeRelay.h
@@ -16,42 +16,43 @@
 
 #pragma once
 
+#include <thread>
+
 #include <android-base/macros.h>
-#include <ostream>
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
+#include <ostream>
 
 #include "NullableOStream.h"
 
 namespace android {
 namespace lshal {
 
-/* Creates an AF_UNIX socketpair and spawns a thread that relays any data
+/**
+ * Creates a pipe and spawns a thread that relays any data
  * written to the "write"-end of the pair to the specified output stream "os".
  */
 struct PipeRelay {
-    explicit PipeRelay(std::ostream& os,
-                       const NullableOStream<std::ostream>& err,
-                       const std::string& interfaceName,
-                       const std::string& instanceName);
+    static android::base::Result<std::unique_ptr<PipeRelay>> create(
+            std::ostream& os, const NullableOStream<std::ostream>& err, const std::string& fqName);
     ~PipeRelay();
 
-    status_t initCheck() const;
-
     // Returns the file descriptor corresponding to the "write"-end of the
     // connection.
-    int fd() const;
+    android::base::borrowed_fd fd() const { return mWrite; }
 
 private:
-    struct RelayThread;
-
-    status_t mInitCheck;
-    int mFds[2];
-    sp<RelayThread> mThread;
-
-    static void CloseFd(int *fd);
-
+    PipeRelay() = default;
     DISALLOW_COPY_AND_ASSIGN(PipeRelay);
+    static void thread(android::base::unique_fd rfd, android::base::unique_fd rfdTrigger,
+                       std::ostream* out, const NullableOStream<std::ostream>* err,
+                       std::string fqName);
+
+    android::base::unique_fd mWrite;
+    android::base::unique_fd mWriteTrigger;
+    std::unique_ptr<std::thread> mThread;
 };
 
 }  // namespace lshal
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 6165911..f66993f 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -40,9 +40,9 @@
 
     void setActiveServicesCallback(const std::function<bool(bool)>& activeServicesCallback);
 
-    bool tryUnregister();
+    bool tryUnregisterLocked();
 
-    void reRegister();
+    void reRegisterLocked();
 
 protected:
     Status onClients(const sp<IBinder>& service, bool clients) override;
@@ -59,6 +59,9 @@
         bool registered = true;
     };
 
+    bool registerServiceLocked(const sp<IBinder>& service, const std::string& name,
+                               bool allowIsolated, int dumpFlags);
+
     /**
      * Looks up a service guaranteed to be registered (service from onClients).
      */
@@ -68,7 +71,7 @@
      * Unregisters all services that we can. If we can't unregister all, re-register other
      * services.
      */
-    void tryShutdown();
+    void tryShutdownLocked();
 
     /**
      * Try to shutdown the process, unless:
@@ -76,7 +79,10 @@
      * - The active services count callback returns 'true', or
      * - Some services have clients.
      */
-    void maybeTryShutdown();
+    void maybeTryShutdownLocked();
+
+    // for below
+    std::mutex mMutex;
 
     // count of services with clients
     size_t mNumConnectedServices;
@@ -117,6 +123,13 @@
 
 bool ClientCounterCallbackImpl::registerService(const sp<IBinder>& service, const std::string& name,
                                             bool allowIsolated, int dumpFlags) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return registerServiceLocked(service, name, allowIsolated, dumpFlags);
+}
+
+bool ClientCounterCallbackImpl::registerServiceLocked(const sp<IBinder>& service,
+                                                      const std::string& name, bool allowIsolated,
+                                                      int dumpFlags) {
     auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
 
     bool reRegister = mRegisteredServices.count(name) > 0;
@@ -164,14 +177,15 @@
 }
 
 void ClientCounterCallbackImpl::forcePersist(bool persist) {
+    std::lock_guard<std::mutex> lock(mMutex);
     mForcePersist = persist;
     if (!mForcePersist) {
         // Attempt a shutdown in case the number of clients hit 0 while the flag was on
-        maybeTryShutdown();
+        maybeTryShutdownLocked();
     }
 }
 
-bool ClientCounterCallbackImpl::tryUnregister() {
+bool ClientCounterCallbackImpl::tryUnregisterLocked() {
     auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
 
     for (auto& [name, entry] : mRegisteredServices) {
@@ -187,15 +201,14 @@
     return true;
 }
 
-void ClientCounterCallbackImpl::reRegister() {
+void ClientCounterCallbackImpl::reRegisterLocked() {
     for (auto& [name, entry] : mRegisteredServices) {
         // re-register entry if not already registered
         if (entry.registered) {
             continue;
         }
 
-        if (!registerService(entry.service, name, entry.allowIsolated,
-                             entry.dumpFlags)) {
+        if (!registerServiceLocked(entry.service, name, entry.allowIsolated, entry.dumpFlags)) {
             // Must restart. Otherwise, clients will never be able to get a hold of this service.
             LOG_ALWAYS_FATAL("Bad state: could not re-register services");
         }
@@ -204,7 +217,7 @@
     }
 }
 
-void ClientCounterCallbackImpl::maybeTryShutdown() {
+void ClientCounterCallbackImpl::maybeTryShutdownLocked() {
     if (mForcePersist) {
         ALOGI("Shutdown prevented by forcePersist override flag.");
         return;
@@ -223,15 +236,12 @@
     // client count change event, try to shutdown the process if its services
     // have no clients.
     if (!handledInCallback && mNumConnectedServices == 0) {
-        tryShutdown();
+        tryShutdownLocked();
     }
 }
 
-/**
- * onClients is oneway, so no need to worry about multi-threading. Note that this means multiple
- * invocations could occur on different threads however.
- */
 Status ClientCounterCallbackImpl::onClients(const sp<IBinder>& service, bool clients) {
+    std::lock_guard<std::mutex> lock(mMutex);
     auto & [name, registered] = *assertRegisteredService(service);
     if (registered.clients == clients) {
         LOG_ALWAYS_FATAL("Process already thought %s had clients: %d but servicemanager has "
@@ -252,23 +262,24 @@
     ALOGI("Process has %zu (of %zu available) client(s) in use after notification %s has clients: %d",
           mNumConnectedServices, mRegisteredServices.size(), name.c_str(), clients);
 
-    maybeTryShutdown();
+    maybeTryShutdownLocked();
     return Status::ok();
 }
 
- void ClientCounterCallbackImpl::tryShutdown() {
-     ALOGI("Trying to shut down the service. No clients in use for any service in process.");
+void ClientCounterCallbackImpl::tryShutdownLocked() {
+    ALOGI("Trying to shut down the service. No clients in use for any service in process.");
 
-    if (tryUnregister()) {
-         ALOGI("Unregistered all clients and exiting");
-         exit(EXIT_SUCCESS);
-     }
+    if (tryUnregisterLocked()) {
+        ALOGI("Unregistered all clients and exiting");
+        exit(EXIT_SUCCESS);
+    }
 
-    reRegister();
+    reRegisterLocked();
 }
 
 void ClientCounterCallbackImpl::setActiveServicesCallback(const std::function<bool(bool)>&
                                                           activeServicesCallback) {
+    std::lock_guard<std::mutex> lock(mMutex);
     mActiveServicesCallback = activeServicesCallback;
 }
 
@@ -291,11 +302,15 @@
 }
 
 bool ClientCounterCallback::tryUnregister() {
-    return mImpl->tryUnregister();
+    // see comments in header, this should only be called from the active
+    // services callback, see also b/191781736
+    return mImpl->tryUnregisterLocked();
 }
 
 void ClientCounterCallback::reRegister() {
-    mImpl->reRegister();
+    // see comments in header, this should only be called from the active
+    // services callback, see also b/191781736
+    mImpl->reRegisterLocked();
 }
 
 }  // namespace internal
diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp
index a8f3fa8..2a87ae4 100644
--- a/libs/binder/RpcServer.cpp
+++ b/libs/binder/RpcServer.cpp
@@ -278,7 +278,7 @@
                 return;
             }
 
-            RpcAddress sessionId = RpcAddress::zero();
+            sessionId = RpcAddress::zero();
             size_t tries = 0;
             do {
                 // don't block if there is some entropy issue
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 4f55eef..ee5e8bb 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -198,6 +198,8 @@
     uint8_t* buffer = reinterpret_cast<uint8_t*>(data);
     uint8_t* end = buffer + size;
 
+    MAYBE_WAIT_IN_FLAKE_MODE;
+
     status_t status;
     while ((status = triggerablePollRead(fd)) == OK) {
         ssize_t readSize = TEMP_FAILURE_RETRY(recv(fd.get(), buffer, end - buffer, MSG_NOSIGNAL));
diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp
index fd2eff6..b5eaaa3 100644
--- a/libs/binder/RpcState.cpp
+++ b/libs/binder/RpcState.cpp
@@ -26,12 +26,28 @@
 #include "Debug.h"
 #include "RpcWireFormat.h"
 
+#include <random>
+
 #include <inttypes.h>
 
 namespace android {
 
 using base::ScopeGuard;
 
+#ifdef RPC_FLAKE_PRONE
+void rpcMaybeWaitToFlake() {
+    static std::random_device r;
+    static std::mutex m;
+
+    unsigned num;
+    {
+        std::lock_guard<std::mutex> lock(m);
+        num = r();
+    }
+    if (num % 10 == 0) usleep(num % 1000);
+}
+#endif
+
 RpcState::RpcState() {}
 RpcState::~RpcState() {}
 
@@ -260,6 +276,8 @@
     LOG_RPC_DETAIL("Sending %s on fd %d: %s", what, connection->fd.get(),
                    hexString(data, size).c_str());
 
+    MAYBE_WAIT_IN_FLAKE_MODE;
+
     if (size > std::numeric_limits<ssize_t>::max()) {
         ALOGE("Cannot send %s at size %zu (too big)", what, size);
         (void)session->shutdownAndWait(false);
@@ -483,7 +501,7 @@
     delete[] const_cast<uint8_t*>(data - offsetof(RpcWireReply, data));
     (void)dataSize;
     LOG_ALWAYS_FATAL_IF(objects != nullptr);
-    LOG_ALWAYS_FATAL_IF(objectsCount, 0);
+    LOG_ALWAYS_FATAL_IF(objectsCount != 0, "%zu objects remaining", objectsCount);
 }
 
 status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& connection,
diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h
index 529dee5..8201eba 100644
--- a/libs/binder/RpcState.h
+++ b/libs/binder/RpcState.h
@@ -42,6 +42,15 @@
 #define LOG_RPC_DETAIL(...) ALOGV(__VA_ARGS__) // for type checking
 #endif
 
+#define RPC_FLAKE_PRONE false
+
+#ifdef RPC_FLAKE_PRONE
+void rpcMaybeWaitToFlake();
+#define MAYBE_WAIT_IN_FLAKE_MODE rpcMaybeWaitToFlake()
+#else
+#define MAYBE_WAIT_IN_FLAKE_MODE do {} while (false)
+#endif
+
 /**
  * Abstracts away management of ref counts and the wire format from
  * RpcSession
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 59f0ba6..e04e326 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -31,7 +31,7 @@
       "name": "binderStabilityTest"
     },
     {
-      "name": "binderUtilsTest"
+      "name": "binderUtilsHostTest"
     },
     {
       "name": "libbinder_ndk_unit_test"
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index f3ba830..2e22b84 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -79,9 +79,10 @@
       */
      void setActiveServicesCallback(const std::function<bool(bool)>& activeServicesCallback);
 
-    /**
+     /**
       * Try to unregister all services previously registered with 'registerService'.
-      * Returns 'true' if successful.
+      * Returns 'true' if successful. This should only be called within the callback registered by
+      * setActiveServicesCallback.
       */
      bool tryUnregister();
 
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index d5990f7..d5a2b61 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -342,4 +342,9 @@
         "libgmock",
     ],
     test_suites: ["general-tests"],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 }
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index a7cf39a..df308d8 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -1621,6 +1621,26 @@
     return NO_ERROR;
 }
 
+status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+                                                  Rect* outRect, uint32_t* outTransform) {
+    ATRACE_CALL();
+    BQ_LOGV("getLastQueuedBuffer");
+
+    std::lock_guard<std::mutex> lock(mCore->mMutex);
+    if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) {
+        *outBuffer = nullptr;
+        *outFence = Fence::NO_FENCE;
+        return NO_ERROR;
+    }
+
+    *outBuffer = mSlots[mCore->mLastQueuedSlot].mGraphicBuffer;
+    *outFence = mLastQueueBufferFence;
+    *outRect = mLastQueuedCrop;
+    *outTransform = mLastQueuedTransform;
+
+    return NO_ERROR;
+}
+
 void BufferQueueProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
     addAndGetFrameTimestamps(nullptr, outDelta);
 }
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index ad00939..6c48534 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -74,6 +74,7 @@
     GET_CONSUMER_USAGE,
     SET_LEGACY_BUFFER_DROP,
     SET_AUTO_PREROTATION,
+    GET_LAST_QUEUED_BUFFER2,
 };
 
 class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -491,6 +492,56 @@
         return result;
     }
 
+    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+                                         Rect* outRect, uint32_t* outTransform) override {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        status_t result = remote()->transact(GET_LAST_QUEUED_BUFFER2, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getLastQueuedBuffer failed to transact: %d", result);
+            return result;
+        }
+        status_t remoteError = NO_ERROR;
+        result = reply.readInt32(&remoteError);
+        if (result != NO_ERROR) {
+            ALOGE("getLastQueuedBuffer failed to read status: %d", result);
+            return result;
+        }
+        if (remoteError != NO_ERROR) {
+            return remoteError;
+        }
+        bool hasBuffer = false;
+        result = reply.readBool(&hasBuffer);
+        if (result != NO_ERROR) {
+            ALOGE("getLastQueuedBuffer failed to read buffer: %d", result);
+            return result;
+        }
+        sp<GraphicBuffer> buffer;
+        if (hasBuffer) {
+            buffer = new GraphicBuffer();
+            result = reply.read(*buffer);
+            if (result == NO_ERROR) {
+                result = reply.read(*outRect);
+            }
+            if (result == NO_ERROR) {
+                result = reply.readUint32(outTransform);
+            }
+        }
+        if (result != NO_ERROR) {
+            ALOGE("getLastQueuedBuffer failed to read buffer: %d", result);
+            return result;
+        }
+        sp<Fence> fence(new Fence);
+        result = reply.read(*fence);
+        if (result != NO_ERROR) {
+            ALOGE("getLastQueuedBuffer failed to read fence: %d", result);
+            return result;
+        }
+        *outBuffer = buffer;
+        *outFence = fence;
+        return result;
+    }
+
     virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(
@@ -676,6 +727,11 @@
                 outBuffer, outFence, outTransformMatrix);
     }
 
+    status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, Rect* outRect,
+                                 uint32_t* outTransform) override {
+        return mBase->getLastQueuedBuffer(outBuffer, outFence, outRect, outTransform);
+    }
+
     void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override {
         return mBase->getFrameTimestamps(outDelta);
     }
@@ -1025,6 +1081,45 @@
             }
             return NO_ERROR;
         }
+        case GET_LAST_QUEUED_BUFFER2: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            sp<GraphicBuffer> buffer(nullptr);
+            sp<Fence> fence(Fence::NO_FENCE);
+            Rect crop;
+            uint32_t transform;
+            status_t result = getLastQueuedBuffer(&buffer, &fence, &crop, &transform);
+            reply->writeInt32(result);
+            if (result != NO_ERROR) {
+                return result;
+            }
+            if (!buffer.get()) {
+                reply->writeBool(false);
+            } else {
+                reply->writeBool(true);
+                result = reply->write(*buffer);
+                if (result == NO_ERROR) {
+                    result = reply->write(crop);
+                }
+                if (result == NO_ERROR) {
+                    result = reply->writeUint32(transform);
+                }
+            }
+            if (result != NO_ERROR) {
+                ALOGE("getLastQueuedBuffer failed to write buffer: %d", result);
+                return result;
+            }
+            if (fence == nullptr) {
+                ALOGE("getLastQueuedBuffer returned a NULL fence, setting to Fence::NO_FENCE");
+                fence = Fence::NO_FENCE;
+            }
+            result = reply->write(*fence);
+            if (result != NO_ERROR) {
+                ALOGE("getLastQueuedBuffer failed to write fence: %d", result);
+                return result;
+            }
+            return NO_ERROR;
+        }
+
         case GET_FRAME_TIMESTAMPS: {
             CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
             FrameEventHistoryDelta frameTimestamps;
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index d6f9e63..8a64a4f 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1207,6 +1207,9 @@
     case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER:
         res = dispatchGetLastQueuedBuffer(args);
         break;
+    case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2:
+        res = dispatchGetLastQueuedBuffer2(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -1513,6 +1516,39 @@
     return result;
 }
 
+int Surface::dispatchGetLastQueuedBuffer2(va_list args) {
+    AHardwareBuffer** buffer = va_arg(args, AHardwareBuffer**);
+    int* fence = va_arg(args, int*);
+    ARect* crop = va_arg(args, ARect*);
+    uint32_t* transform = va_arg(args, uint32_t*);
+    sp<GraphicBuffer> graphicBuffer;
+    sp<Fence> spFence;
+
+    Rect r;
+    int result =
+            mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, &r, transform);
+
+    if (graphicBuffer != nullptr) {
+        *buffer = graphicBuffer->toAHardwareBuffer();
+        AHardwareBuffer_acquire(*buffer);
+
+        // Avoid setting crop* unless buffer is valid (matches IGBP behavior)
+        crop->left = r.left;
+        crop->top = r.top;
+        crop->right = r.right;
+        crop->bottom = r.bottom;
+    } else {
+        *buffer = nullptr;
+    }
+
+    if (spFence != nullptr) {
+        *fence = spFence->dup();
+    } else {
+        *fence = -1;
+    }
+    return result;
+}
+
 bool Surface::transformToDisplayInverse() {
     return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
             NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index a7f7d1d..0ad3075 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -186,6 +186,10 @@
     virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]) override;
 
+    // See IGraphicBufferProducer::getLastQueuedBuffer
+    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+                                         Rect* outRect, uint32_t* outTransform) override;
+
     // See IGraphicBufferProducer::getFrameTimestamps
     virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override;
 
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 45e0a13..4905b74 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -619,6 +619,22 @@
     virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]) = 0;
 
+    // Returns the last queued buffer along with a fence which must signal
+    // before the contents of the buffer are read. If there are no buffers in
+    // the queue, outBuffer will be populated with nullptr and outFence will be
+    // populated with Fence::NO_FENCE
+    //
+    // outRect & outTransform are not modified if outBuffer is null.
+    //
+    // Returns NO_ERROR or the status of the Binder transaction
+    virtual status_t getLastQueuedBuffer([[maybe_unused]] sp<GraphicBuffer>* outBuffer,
+                                         [[maybe_unused]] sp<Fence>* outFence,
+                                         [[maybe_unused]] Rect* outRect,
+                                         [[maybe_unused]] uint32_t* outTransform) {
+        // Too many things implement IGraphicBufferProducer...
+        return UNKNOWN_TRANSACTION;
+    }
+
     // Gets the frame events that haven't already been retrieved.
     virtual void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) {}
 
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 55b4101..10f4df9 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -265,6 +265,7 @@
     int dispatchAddQueueInterceptor(va_list args);
     int dispatchAddQueryInterceptor(va_list args);
     int dispatchGetLastQueuedBuffer(va_list args);
+    int dispatchGetLastQueuedBuffer2(va_list args);
     bool transformToDisplayInverse();
 
 protected:
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index b78fc5d..73b46b2 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -255,6 +255,7 @@
     NATIVE_WINDOW_ALLOCATE_BUFFERS                = 45,    /* private */
     NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER          = 46,    /* private */
     NATIVE_WINDOW_SET_QUERY_INTERCEPTOR           = 47,    /* private */
+    NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2         = 50,    /* private */
     // clang-format on
 };
 
@@ -1053,6 +1054,31 @@
 }
 
 /**
+ * Retrieves the last queued buffer for this window, along with the fence that
+ * fires when the buffer is ready to be read. The cropRect & transform should be applied to the
+ * buffer's content.
+ *
+ * If there was no buffer previously queued, then outBuffer will be NULL and
+ * the value of outFence will be -1.
+ *
+ * Note that if outBuffer is not NULL, then the caller will hold a reference
+ * onto the buffer. Accordingly, the caller must call AHardwareBuffer_release
+ * when the buffer is no longer needed so that the system may reclaim the
+ * buffer.
+ *
+ * \return NO_ERROR on success.
+ * \return NO_MEMORY if there was insufficient memory.
+ * \return STATUS_UNKNOWN_TRANSACTION if this ANativeWindow doesn't support this method, callers
+ *         should fall back to ANativeWindow_getLastQueuedBuffer instead.
+ */
+static inline int ANativeWindow_getLastQueuedBuffer2(ANativeWindow* window,
+                                                     AHardwareBuffer** outBuffer, int* outFence,
+                                                     ARect* outCropRect, uint32_t* outTransform) {
+    return window->perform(window, NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2, outBuffer, outFence,
+                           outCropRect, outTransform);
+}
+
+/**
  * Retrieves an identifier for the next frame to be queued by this window.
  *
  * \return the next frame id.
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index 18a2891..6b2d745 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -140,6 +140,11 @@
             outTransformMatrix);
 }
 
+status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+                                                Rect* outRect, uint32_t* outTransform) {
+    return mProducer->getLastQueuedBuffer(outBuffer, outFence, outRect, outTransform);
+}
+
 void MonitoredProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
     mProducer->getFrameTimestamps(outDelta);
 }
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index 788919b..3778277 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -64,6 +64,8 @@
     virtual status_t setLegacyBufferDrop(bool drop) override;
     virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]) override;
+    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+                                         Rect* outRect, uint32_t* outTransform) override;
     virtual IBinder* onAsBinder();
     virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
     virtual status_t setAutoRefresh(bool autoRefresh) override;