Merge "SF: Optionally skip slow unittests"
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 645e211..25e5247 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -332,8 +332,8 @@
             MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s");
 
         // If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat.
-        const char* dex2oat_bin = "/apex/com.android.runtime/bin/dex2oat";
-        constexpr const char* kDex2oatDebugPath = "/apex/com.android.runtime/bin/dex2oatd";
+        const char* dex2oat_bin = "/system/bin/dex2oat";
+        constexpr const char* kDex2oatDebugPath = "/system/bin/dex2oatd";
         // Do not use dex2oatd for release candidates (give dex2oat more soak time).
         bool is_release = android::base::GetProperty("ro.build.version.codename", "") == "REL";
         if (is_debug_runtime() ||
@@ -662,9 +662,7 @@
                   const std::vector<std::string>& dex_locations,
                   bool copy_and_update) {
         const char* profman_bin =
-            is_debug_runtime()
-                ? "/apex/com.android.runtime/bin/profmand"
-                : "/apex/com.android.runtime/bin/profman";
+                is_debug_runtime() ? "/system/bin/profmand" : "/system/bin/profman";
 
         if (copy_and_update) {
             CHECK_EQ(1u, profile_fds.size());
@@ -1466,9 +1464,9 @@
                     const char* class_loader_context) {
         CHECK_GE(zip_fd, 0);
         const char* dexoptanalyzer_bin =
-            is_debug_runtime()
-                ? "/apex/com.android.runtime/bin/dexoptanalyzerd"
-                : "/apex/com.android.runtime/bin/dexoptanalyzer";
+                is_debug_runtime()
+                        ? "/system/bin/dexoptanalyzerd"
+                        : "/system/bin/dexoptanalyzer";
 
         std::string dex_file_arg = "--dex-file=" + dex_file;
         std::string oat_fd_arg = "--oat-fd=" + std::to_string(oat_fd);
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index d161407..b2e7047 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -440,7 +440,7 @@
                           const char* isa) const {
         // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc.
         std::vector<std::string> cmd;
-        cmd.push_back("/apex/com.android.runtime/bin/dex2oat");
+        cmd.push_back("/system/bin/dex2oat");
         cmd.push_back(StringPrintf("--image=%s", art_path.c_str()));
         for (const std::string& boot_part : Split(boot_cp, ":")) {
             cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str()));
diff --git a/headers/media_plugin/media/openmax/OMX_AsString.h b/headers/media_plugin/media/openmax/OMX_AsString.h
index dc25ded..fe2e2ac 100644
--- a/headers/media_plugin/media/openmax/OMX_AsString.h
+++ b/headers/media_plugin/media/openmax/OMX_AsString.h
@@ -189,6 +189,7 @@
     switch (i) {
         case OMX_AUDIO_CodingAndroidAC3:  return "AndroidAC3";
         case OMX_AUDIO_CodingAndroidOPUS: return "AndroidOPUS";
+        case OMX_AUDIO_CodingAndroidAC4:  return "AndroidAC4";
         default:                          return asString((OMX_AUDIO_CODINGTYPE)i, def);
     }
 }
@@ -536,6 +537,7 @@
         case OMX_IndexParamAudioAndroidOpus:            return "ParamAudioAndroidOpus";
         case OMX_IndexParamAudioAndroidAacPresentation: return "ParamAudioAndroidAacPresentation";
         case OMX_IndexParamAudioAndroidEac3:            return "ParamAudioAndroidEac3";
+        case OMX_IndexParamAudioAndroidAc4:             return "ParamAudioAndroidAc4";
         case OMX_IndexParamAudioProfileQuerySupported:  return "ParamAudioProfileQuerySupported";
 //      case OMX_IndexParamNalStreamFormatSupported:    return "ParamNalStreamFormatSupported";
 //      case OMX_IndexParamNalStreamFormat:             return "ParamNalStreamFormat";
diff --git a/headers/media_plugin/media/openmax/OMX_AudioExt.h b/headers/media_plugin/media/openmax/OMX_AudioExt.h
index 8409553..1ea740f 100644
--- a/headers/media_plugin/media/openmax/OMX_AudioExt.h
+++ b/headers/media_plugin/media/openmax/OMX_AudioExt.h
@@ -48,6 +48,7 @@
     OMX_AUDIO_CodingAndroidAC3,         /**< AC3 encoded data */
     OMX_AUDIO_CodingAndroidOPUS,        /**< OPUS encoded data */
     OMX_AUDIO_CodingAndroidEAC3,        /**< EAC3 encoded data */
+    OMX_AUDIO_CodingAndroidAC4,         /**< AC4 encoded data */
 } OMX_AUDIO_CODINGEXTTYPE;
 
 typedef struct OMX_AUDIO_PARAM_ANDROID_AC3TYPE {
@@ -68,6 +69,15 @@
                                         variable or unknown sampling rate. */
 } OMX_AUDIO_PARAM_ANDROID_EAC3TYPE;
 
+typedef struct OMX_AUDIO_PARAM_ANDROID_AC4TYPE {
+    OMX_U32 nSize;                 /**< size of the structure in bytes */
+    OMX_VERSIONTYPE nVersion;      /**< OMX specification version information */
+    OMX_U32 nPortIndex;            /**< port that this structure applies to */
+    OMX_U32 nChannels;             /**< Number of channels */
+    OMX_U32 nSampleRate;           /**< Sampling rate of the source data.  Use 0 for
+                                        variable or unknown sampling rate. */
+} OMX_AUDIO_PARAM_ANDROID_AC4TYPE;
+
 typedef struct OMX_AUDIO_PARAM_ANDROID_OPUSTYPE {
     OMX_U32 nSize;            /**< size of the structure in bytes */
     OMX_VERSIONTYPE nVersion; /**< OMX specification version information */
diff --git a/headers/media_plugin/media/openmax/OMX_IndexExt.h b/headers/media_plugin/media/openmax/OMX_IndexExt.h
index 716d959..9fc3ef3 100644
--- a/headers/media_plugin/media/openmax/OMX_IndexExt.h
+++ b/headers/media_plugin/media/openmax/OMX_IndexExt.h
@@ -64,6 +64,7 @@
     OMX_IndexParamAudioAndroidEac3,                 /**< reference: OMX_AUDIO_PARAM_ANDROID_EAC3TYPE */
     OMX_IndexParamAudioProfileQuerySupported,       /**< reference: OMX_AUDIO_PARAM_ANDROID_PROFILETYPE */
     OMX_IndexParamAudioAndroidAacDrcPresentation,   /**< reference: OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE */
+    OMX_IndexParamAudioAndroidAc4,                  /**< reference: OMX_AUDIO_PARAM_ANDROID_AC4TYPE */
     OMX_IndexExtAudioEndUnused,
 
     /* Image parameters and configurations */
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
index e747ee1..3816c1b 100644
--- a/libs/ui/BufferHubBuffer.cpp
+++ b/libs/ui/BufferHubBuffer.cpp
@@ -61,6 +61,16 @@
 // to use Binder.
 static constexpr char kBufferHubClientPath[] = "system/buffer_hub/client";
 
+using dvr::BufferHubDefs::AnyClientAcquired;
+using dvr::BufferHubDefs::AnyClientGained;
+using dvr::BufferHubDefs::AnyClientPosted;
+using dvr::BufferHubDefs::IsClientAcquired;
+using dvr::BufferHubDefs::IsClientGained;
+using dvr::BufferHubDefs::IsClientPosted;
+using dvr::BufferHubDefs::IsClientReleased;
+using dvr::BufferHubDefs::kHighBitsMask;
+using dvr::BufferHubDefs::kMetadataHeaderSize;
+
 } // namespace
 
 BufferHubClient::BufferHubClient() : Client(ClientChannelFactory::Create(kBufferHubClientPath)) {}
@@ -71,7 +81,7 @@
 BufferHubClient::~BufferHubClient() {}
 
 bool BufferHubClient::IsValid() const {
-  return IsConnected() && GetChannelHandle().valid();
+    return IsConnected() && GetChannelHandle().valid();
 }
 
 LocalChannelHandle BufferHubClient::TakeChannelHandle() {
@@ -151,11 +161,17 @@
     }
 
     size_t metadataSize = static_cast<size_t>(bufferTraits.metadata_size());
-    if (metadataSize < dvr::BufferHubDefs::kMetadataHeaderSize) {
+    if (metadataSize < kMetadataHeaderSize) {
         ALOGE("BufferHubBuffer::ImportGraphicBuffer: metadata too small: %zu", metadataSize);
         return -EINVAL;
     }
 
+    // Populate shortcuts to the atomics in metadata.
+    auto metadata_header = mMetadata.metadata_header();
+    buffer_state_ = &metadata_header->buffer_state;
+    fence_state_ = &metadata_header->fence_state;
+    active_clients_bit_mask_ = &metadata_header->active_clients_bit_mask;
+
     // Import the buffer: We only need to hold on the native_handle_t here so that
     // GraphicBuffer instance can be created in future.
     mBufferHandle = bufferTraits.take_buffer_handle();
@@ -176,7 +192,93 @@
 
     // TODO(b/112012161) Set up shared fences.
     ALOGD("BufferHubBuffer::ImportGraphicBuffer: id=%d, buffer_state=%" PRIx64 ".", id(),
-          mMetadata.metadata_header()->buffer_state.load(std::memory_order_acquire));
+          buffer_state_->load(std::memory_order_acquire));
+    return 0;
+}
+
+int BufferHubBuffer::Gain() {
+    uint64_t current_buffer_state = buffer_state_->load(std::memory_order_acquire);
+    if (IsClientGained(current_buffer_state, mClientStateMask)) {
+        ALOGV("%s: Buffer is already gained by this client %" PRIx64 ".", __FUNCTION__,
+              mClientStateMask);
+        return 0;
+    }
+    do {
+        if (AnyClientGained(current_buffer_state & (~mClientStateMask)) ||
+            AnyClientAcquired(current_buffer_state)) {
+            ALOGE("%s: Buffer is in use, id=%d mClientStateMask=%" PRIx64 " state=%" PRIx64 ".",
+                  __FUNCTION__, mId, mClientStateMask, current_buffer_state);
+            return -EBUSY;
+        }
+        // Change the buffer state to gained state, whose value happens to be the same as
+        // mClientStateMask.
+    } while (!buffer_state_->compare_exchange_weak(current_buffer_state, mClientStateMask,
+                                                   std::memory_order_acq_rel,
+                                                   std::memory_order_acquire));
+    // TODO(b/119837586): Update fence state and return GPU fence.
+    return 0;
+}
+
+int BufferHubBuffer::Post() {
+    uint64_t current_buffer_state = buffer_state_->load(std::memory_order_acquire);
+    uint64_t current_active_clients_bit_mask = 0ULL;
+    uint64_t updated_buffer_state = 0ULL;
+    do {
+        if (!IsClientGained(current_buffer_state, mClientStateMask)) {
+            ALOGE("%s: Cannot post a buffer that is not gained by this client. buffer_id=%d "
+                  "mClientStateMask=%" PRIx64 " state=%" PRIx64 ".",
+                  __FUNCTION__, mId, mClientStateMask, current_buffer_state);
+            return -EBUSY;
+        }
+        // Set the producer client buffer state to released, other clients' buffer state to posted.
+        current_active_clients_bit_mask = active_clients_bit_mask_->load(std::memory_order_acquire);
+        updated_buffer_state =
+                current_active_clients_bit_mask & (~mClientStateMask) & kHighBitsMask;
+    } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state,
+                                                   std::memory_order_acq_rel,
+                                                   std::memory_order_acquire));
+    // TODO(b/119837586): Update fence state and return GPU fence if needed.
+    return 0;
+}
+
+int BufferHubBuffer::Acquire() {
+    uint64_t current_buffer_state = buffer_state_->load(std::memory_order_acquire);
+    if (IsClientAcquired(current_buffer_state, mClientStateMask)) {
+        ALOGV("%s: Buffer is already acquired by this client %" PRIx64 ".", __FUNCTION__,
+              mClientStateMask);
+        return 0;
+    }
+    uint64_t updated_buffer_state = 0ULL;
+    do {
+        if (!IsClientPosted(current_buffer_state, mClientStateMask)) {
+            ALOGE("%s: Cannot acquire a buffer that is not in posted state. buffer_id=%d "
+                  "mClientStateMask=%" PRIx64 " state=%" PRIx64 ".",
+                  __FUNCTION__, mId, mClientStateMask, current_buffer_state);
+            return -EBUSY;
+        }
+        // Change the buffer state for this consumer from posted to acquired.
+        updated_buffer_state = current_buffer_state ^ mClientStateMask;
+    } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state,
+                                                   std::memory_order_acq_rel,
+                                                   std::memory_order_acquire));
+    // TODO(b/119837586): Update fence state and return GPU fence.
+    return 0;
+}
+
+int BufferHubBuffer::Release() {
+    uint64_t current_buffer_state = buffer_state_->load(std::memory_order_acquire);
+    if (IsClientReleased(current_buffer_state, mClientStateMask)) {
+        ALOGV("%s: Buffer is already released by this client %" PRIx64 ".", __FUNCTION__,
+              mClientStateMask);
+        return 0;
+    }
+    uint64_t updated_buffer_state = 0ULL;
+    do {
+        updated_buffer_state = current_buffer_state & (~mClientStateMask);
+    } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state,
+                                                   std::memory_order_acq_rel,
+                                                   std::memory_order_acquire));
+    // TODO(b/119837586): Update fence state and return GPU fence if needed.
     return 0;
 }
 
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index 37cf617..20f27c5 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -42,9 +42,6 @@
         for (const auto bit : hardware::hidl_enum_range<BufferUsage>()) {
             bits = bits | bit;
         }
-        // TODO(b/72323293, b/72703005): Remove these additional bits
-        bits = bits | (1 << 10) | (1 << 13);
-
         return bits;
     }();
     return valid10UsageBits;
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index a2f1783..f56e6b9 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -101,6 +101,9 @@
     if (layerCount < 1)
         layerCount = 1;
 
+    // TODO(b/72323293, b/72703005): Remove these invalid bits from callers
+    usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
+
     Gralloc2::IMapper::BufferDescriptorInfo info = {};
     info.width = width;
     info.height = height;
diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h
index 6850b43..03d10e7 100644
--- a/libs/ui/include/ui/BufferHubBuffer.h
+++ b/libs/ui/include/ui/BufferHubBuffer.h
@@ -103,6 +103,27 @@
     // to read from and/or write into.
     bool IsValid() const { return mBufferHandle.IsValid(); }
 
+    // Gains the buffer for exclusive write permission. Read permission is implied once a buffer is
+    // gained.
+    // The buffer can be gained as long as there is no other client in acquired or gained state.
+    int Gain();
+
+    // Posts the gained buffer for other buffer clients to use the buffer.
+    // The buffer can be posted iff the buffer state for this client is gained.
+    // After posting the buffer, this client is put to released state and does not have access to
+    // the buffer for this cycle of the usage of the buffer.
+    int Post();
+
+    // Acquires the buffer for shared read permission.
+    // The buffer can be acquired iff the buffer state for this client is posted.
+    int Acquire();
+
+    // Releases the buffer.
+    // The buffer can be released from any buffer state.
+    // After releasing the buffer, this client no longer have any permissions to the buffer for the
+    // current cycle of the usage of the buffer.
+    int Release();
+
     // Returns the event mask for all the events that are pending on this buffer (see sys/poll.h for
     // all possible bits).
     pdx::Status<int> GetEventMask(int events) {
@@ -130,6 +151,9 @@
 
     // Global id for the buffer that is consistent across processes.
     int mId = -1;
+
+    // Client state mask of this BufferHubBuffer object. It is unique amoung all
+    // clients/users of the buffer.
     uint64_t mClientStateMask = 0;
 
     // Stores ground truth of the buffer.
@@ -141,6 +165,10 @@
     // An ashmem-based metadata object. The same shared memory are mapped to the
     // bufferhubd daemon and all buffer clients.
     BufferHubMetadata mMetadata;
+    // Shortcuts to the atomics inside the header of mMetadata.
+    std::atomic<uint64_t>* buffer_state_{nullptr};
+    std::atomic<uint64_t>* fence_state_{nullptr};
+    std::atomic<uint64_t>* active_clients_bit_mask_{nullptr};
 
     // PDX backend.
     BufferHubClient mClient;
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
index 6c7d06b..e33acf6 100644
--- a/libs/ui/tests/BufferHubBuffer_test.cpp
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -36,7 +36,14 @@
 const int kUsage = 0;
 const size_t kUserMetadataSize = 0;
 
+using dvr::BufferHubDefs::AnyClientAcquired;
+using dvr::BufferHubDefs::AnyClientGained;
+using dvr::BufferHubDefs::AnyClientPosted;
 using dvr::BufferHubDefs::IsBufferReleased;
+using dvr::BufferHubDefs::IsClientAcquired;
+using dvr::BufferHubDefs::IsClientGained;
+using dvr::BufferHubDefs::IsClientPosted;
+using dvr::BufferHubDefs::IsClientReleased;
 using dvr::BufferHubDefs::kFirstClientBitMask;
 using dvr::BufferHubDefs::kMetadataHeaderSize;
 using frameworks::bufferhub::V1_0::BufferHubStatus;
@@ -48,9 +55,40 @@
 using pdx::LocalChannelHandle;
 
 class BufferHubBufferTest : public ::testing::Test {
+protected:
     void SetUp() override { android::hardware::ProcessState::self()->startThreadPool(); }
 };
 
+class BufferHubBufferStateTransitionTest : public BufferHubBufferTest {
+protected:
+    void SetUp() override {
+        BufferHubBufferTest::SetUp();
+        CreateTwoClientsOfABuffer();
+    }
+
+    std::unique_ptr<BufferHubBuffer> b1;
+    uint64_t b1ClientMask = 0ULL;
+    std::unique_ptr<BufferHubBuffer> b2;
+    uint64_t b2ClientMask = 0ULL;
+
+private:
+    // Creates b1 and b2 as the clients of the same buffer for testing.
+    void CreateTwoClientsOfABuffer();
+};
+
+void BufferHubBufferStateTransitionTest::CreateTwoClientsOfABuffer() {
+    b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize);
+    b1ClientMask = b1->client_state_mask();
+    ASSERT_NE(b1ClientMask, 0ULL);
+    auto statusOrHandle = b1->Duplicate();
+    ASSERT_TRUE(statusOrHandle);
+    LocalChannelHandle h2 = statusOrHandle.take();
+    b2 = BufferHubBuffer::Import(std::move(h2));
+    b2ClientMask = b2->client_state_mask();
+    ASSERT_NE(b2ClientMask, 0ULL);
+    ASSERT_NE(b2ClientMask, b1ClientMask);
+}
+
 TEST_F(BufferHubBufferTest, CreateBufferHubBufferFails) {
     // Buffer Creation will fail: BLOB format requires height to be 1.
     auto b1 = BufferHubBuffer::Create(kWidth, /*height=*/2, kLayerCount,
@@ -315,5 +353,180 @@
     EXPECT_EQ(nullptr, client2.get());
 }
 
+TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromReleasedState) {
+    ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
+
+    // Successful gaining the buffer should change the buffer state bit of b1 to
+    // gained state, other client state bits to released state.
+    EXPECT_EQ(b1->Gain(), 0);
+    EXPECT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask));
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromGainedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    auto current_buffer_state = b1->buffer_state();
+    ASSERT_TRUE(IsClientGained(current_buffer_state, b1ClientMask));
+
+    // Gaining from gained state by the same client should not return error.
+    EXPECT_EQ(b1->Gain(), 0);
+
+    // Gaining from gained state by another client should return error.
+    EXPECT_EQ(b2->Gain(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromAcquiredState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_EQ(b2->Acquire(), 0);
+    ASSERT_TRUE(AnyClientAcquired(b1->buffer_state()));
+
+    // Gaining from acquired state should fail.
+    EXPECT_EQ(b1->Gain(), -EBUSY);
+    EXPECT_EQ(b2->Gain(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromOtherClientInPostedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_TRUE(AnyClientPosted(b1->buffer_state()));
+
+    // Gaining a buffer who has other posted client should succeed.
+    EXPECT_EQ(b1->Gain(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromSelfInPostedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_TRUE(AnyClientPosted(b1->buffer_state()));
+
+    // A posted client should be able to gain the buffer when there is no other clients in
+    // acquired state.
+    EXPECT_EQ(b2->Gain(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromOtherInGainedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask));
+
+    EXPECT_EQ(b2->Post(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromSelfInGainedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask));
+
+    EXPECT_EQ(b1->Post(), 0);
+    auto current_buffer_state = b1->buffer_state();
+    EXPECT_TRUE(IsClientReleased(current_buffer_state, b1ClientMask));
+    EXPECT_TRUE(IsClientPosted(current_buffer_state, b2ClientMask));
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromPostedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_TRUE(AnyClientPosted(b1->buffer_state()));
+
+    // Post from posted state should fail.
+    EXPECT_EQ(b1->Post(), -EBUSY);
+    EXPECT_EQ(b2->Post(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromAcquiredState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_EQ(b2->Acquire(), 0);
+    ASSERT_TRUE(AnyClientAcquired(b1->buffer_state()));
+
+    // Posting from acquired state should fail.
+    EXPECT_EQ(b1->Post(), -EBUSY);
+    EXPECT_EQ(b2->Post(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromReleasedState) {
+    ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
+
+    // Posting from released state should fail.
+    EXPECT_EQ(b1->Post(), -EBUSY);
+    EXPECT_EQ(b2->Post(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInPostedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_TRUE(IsClientPosted(b1->buffer_state(), b2ClientMask));
+
+    // Acquire from posted state should pass.
+    EXPECT_EQ(b2->Acquire(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromOtherInPostedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_TRUE(IsClientPosted(b1->buffer_state(), b2ClientMask));
+
+    // Acquire from released state should fail, although there are other clients
+    // in posted state.
+    EXPECT_EQ(b1->Acquire(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInAcquiredState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_EQ(b2->Acquire(), 0);
+    auto current_buffer_state = b1->buffer_state();
+    ASSERT_TRUE(IsClientAcquired(current_buffer_state, b2ClientMask));
+
+    // Acquiring from acquired state by the same client should not error out.
+    EXPECT_EQ(b2->Acquire(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromReleasedState) {
+    ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
+
+    // Acquiring form released state should fail.
+    EXPECT_EQ(b1->Acquire(), -EBUSY);
+    EXPECT_EQ(b2->Acquire(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromGainedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_TRUE(AnyClientGained(b1->buffer_state()));
+
+    // Acquiring from gained state should fail.
+    EXPECT_EQ(b1->Acquire(), -EBUSY);
+    EXPECT_EQ(b2->Acquire(), -EBUSY);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInReleasedState) {
+    ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
+
+    EXPECT_EQ(b1->Release(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInGainedState) {
+    ASSERT_TRUE(IsBufferReleased(b1->buffer_state()));
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_TRUE(AnyClientGained(b1->buffer_state()));
+
+    EXPECT_EQ(b1->Release(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInPostedState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_TRUE(AnyClientPosted(b1->buffer_state()));
+
+    EXPECT_EQ(b2->Release(), 0);
+}
+
+TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInAcquiredState) {
+    ASSERT_EQ(b1->Gain(), 0);
+    ASSERT_EQ(b1->Post(), 0);
+    ASSERT_EQ(b2->Acquire(), 0);
+    ASSERT_TRUE(AnyClientAcquired(b1->buffer_state()));
+
+    EXPECT_EQ(b2->Release(), 0);
+}
+
 } // namespace
 } // namespace android
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 4433af0..c100db7 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -378,7 +378,8 @@
         if (wideColorBoardConfig && hasColorSpaceSupport) {
             mExtensionString.append(
                     "EGL_EXT_gl_colorspace_scrgb EGL_EXT_gl_colorspace_scrgb_linear "
-                    "EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 ");
+                    "EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 "
+                    "EGL_EXT_gl_colorspace_display_p3_passthrough ");
         }
 
         bool hasHdrBoardConfig =
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 547a669..4ebd8bd 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -466,6 +466,8 @@
         return HAL_DATASPACE_DISPLAY_P3;
     } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT) {
         return HAL_DATASPACE_DISPLAY_P3_LINEAR;
+    } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT) {
+        return HAL_DATASPACE_DISPLAY_P3;
     } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_EXT) {
         return HAL_DATASPACE_V0_SCRGB;
     } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) {
@@ -510,6 +512,9 @@
     if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_linear")) {
         colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT);
     }
+    if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_passthrough")) {
+        colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT);
+    }
     return colorSpaces;
 }
 
@@ -527,6 +532,7 @@
                 case EGL_GL_COLORSPACE_LINEAR_KHR:
                 case EGL_GL_COLORSPACE_SRGB_KHR:
                 case EGL_GL_COLORSPACE_DISPLAY_P3_EXT:
+                case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT:
                 case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT:
                 case EGL_GL_COLORSPACE_SCRGB_EXT:
                 case EGL_GL_COLORSPACE_BT2020_LINEAR_EXT:
diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index 459b135..cca91c3 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -217,6 +217,7 @@
     // Test that display-p3 extensions exist
     ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3"));
     ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_linear"));
+    ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_passthrough"));
 
     // Use 8-bit to keep forcus on Display-P3 aspect
     EGLint attrs[] = {
@@ -289,6 +290,59 @@
     EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface));
 }
 
+TEST_F(EGLTest, EGLDisplayP3Passthrough) {
+    EGLConfig config;
+    EGLBoolean success;
+
+    if (!hasWideColorDisplay) {
+        // skip this test if device does not have wide-color display
+        std::cerr << "[          ] Device does not support wide-color, test skipped" << std::endl;
+        return;
+    }
+
+    // Test that display-p3 extensions exist
+    ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3"));
+    ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_linear"));
+    ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_passthrough"));
+
+    get8BitConfig(config);
+
+    struct DummyConsumer : public BnConsumerListener {
+        void onFrameAvailable(const BufferItem& /* item */) override {}
+        void onBuffersReleased() override {}
+        void onSidebandStreamChanged() override {}
+    };
+
+    // Create a EGLSurface
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+    consumer->consumerConnect(new DummyConsumer, false);
+    sp<Surface> mSTC = new Surface(producer);
+    sp<ANativeWindow> mANW = mSTC;
+    EGLint winAttrs[] = {
+            // clang-format off
+            EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT,
+            EGL_NONE,              EGL_NONE
+            // clang-format on
+    };
+
+    EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, config, mANW.get(), winAttrs);
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    ASSERT_NE(EGL_NO_SURFACE, eglSurface);
+
+    android_dataspace dataspace =
+        static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(mANW.get()));
+    ASSERT_EQ(dataspace, HAL_DATASPACE_DISPLAY_P3);
+
+    EGLint value;
+    success = eglQuerySurface(mEglDisplay, eglSurface, EGL_GL_COLORSPACE_KHR, &value);
+    ASSERT_EQ(EGL_UNSIGNED_TRUE, success);
+    ASSERT_EQ(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT, value);
+
+    EXPECT_TRUE(eglDestroySurface(mEglDisplay, eglSurface));
+}
+
 TEST_F(EGLTest, EGLDisplayP31010102) {
     EGLint numConfigs;
     EGLConfig config;
@@ -303,6 +357,7 @@
     // Test that display-p3 extensions exist
     ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3"));
     ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_linear"));
+    ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_EXT_gl_colorspace_display_p3_passthrough"));
 
     // Use 8-bit to keep forcus on Display-P3 aspect
     EGLint attrs[] = {
diff --git a/opengl/tests/lib/include/EGLUtils.h b/opengl/tests/lib/include/EGLUtils.h
index eb9571d..cfa378f 100644
--- a/opengl/tests/lib/include/EGLUtils.h
+++ b/opengl/tests/lib/include/EGLUtils.h
@@ -280,6 +280,8 @@
             return String8("EGL_GL_COLORSPACE_SRGB_KHR");
         case EGL_GL_COLORSPACE_DISPLAY_P3_EXT:
             return String8("EGL_GL_COLORSPACE_DISPLAY_P3_EXT");
+        case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT:
+            return String8("EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT");
         case  EGL_GL_COLORSPACE_LINEAR_KHR:
             return String8("EGL_GL_COLORSPACE_LINEAR_KHR");
         default:
diff --git a/opengl/tools/glgen2/registry/egl.xml b/opengl/tools/glgen2/registry/egl.xml
index 26771e1..8d661ae 100644
--- a/opengl/tools/glgen2/registry/egl.xml
+++ b/opengl/tools/glgen2/registry/egl.xml
@@ -894,6 +894,11 @@
             <unused start="0x3481" end="0x348F"/>
     </enums>
 
+    <enums namespace="EGL" start="0x3490" end="0x349F" vendor="EXT" comment="Reserved for Courtney Goeltzenleuchter - Android (gitlab EGL bug 69)">
+        <enum value="0x3490" name="EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT"/>
+            <unused start="0x3491" end="0x349F"/>
+    </enums>
+
 <!-- Please remember that new enumerant allocations must be obtained by
      request to the Khronos API registrar (see comments at the top of this
      file) File requests in the Khronos Bugzilla, EGL project, Registry
@@ -903,8 +908,8 @@
 
 <!-- Reservable for future use. To generate a new range, allocate multiples
      of 16 starting at the lowest available point in this block. -->
-    <enums namespace="EGL" start="0x3490" end="0x3FFF" vendor="KHR" comment="Reserved for future use">
-            <unused start="0x3490" end="0x3FFF"/>
+    <enums namespace="EGL" start="0x34A0" end="0x3FFF" vendor="KHR" comment="Reserved for future use">
+            <unused start="0x34A0" end="0x3FFF"/>
     </enums>
 
     <enums namespace="EGL" start="0x8F70" end="0x8F7F" vendor="HI" comment="For Mark Callow, Khronos bug 4055. Shared with GL.">
@@ -2250,6 +2255,11 @@
                 <enum name="EGL_GL_COLORSPACE_DISPLAY_P3_EXT"/>
             </require>
         </extension>
+        <extension name="EGL_EXT_gl_colorspace_display_p3_passthrough" supported="egl">
+            <require>
+                <enum name="EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT"/>
+            </require>
+        </extension>
         <extension name="EGL_EXT_image_dma_buf_import" supported="egl">
             <require>
                 <enum name="EGL_LINUX_DMA_BUF_EXT"/>
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 9e748d8..9ba4140 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -2638,6 +2638,7 @@
                 mOrientation = internalViewport->orientation;
             }
         }
+        getPolicy()->updatePointerDisplay();
         bumpGeneration();
     }
 }
@@ -2783,7 +2784,7 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
-        displayId = ADISPLAY_ID_DEFAULT;
+        displayId = mPointerController->getDisplayId();
     } else {
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
@@ -3601,6 +3602,9 @@
             (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
         if (mPointerController == nullptr) {
             mPointerController = getPolicy()->obtainPointerController(getDeviceId());
+            getPolicy()->updatePointerDisplay();
+        } else if (viewportChanged) {
+            getPolicy()->updatePointerDisplay();
         }
     } else {
         mPointerController.clear();
@@ -5387,7 +5391,8 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
 
-        NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags,
+        int32_t displayId = mPointerController->getDisplayId();
+        NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags,
                 AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
                 metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                 /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
@@ -6294,6 +6299,7 @@
 void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags,
         bool down, bool hovering) {
     int32_t metaState = getContext()->getGlobalMetaState();
+    int32_t displayId = mViewport.displayId;
 
     if (mPointerController != nullptr) {
         if (down || hovering) {
@@ -6304,13 +6310,14 @@
         } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
             mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
         }
+        displayId = mPointerController->getDisplayId();
     }
 
     if (mPointerSimple.down && !down) {
         mPointerSimple.down = false;
 
         // Send up.
-        NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags,
+        NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags,
                  AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, 0,
                  /* deviceTimestamp */ 0,
                  1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
@@ -6323,7 +6330,7 @@
         mPointerSimple.hovering = false;
 
         // Send hover exit.
-        NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags,
+        NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags,
                 AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState, 0,
                 /* deviceTimestamp */ 0,
                 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
@@ -6338,7 +6345,7 @@
             mPointerSimple.downTime = when;
 
             // Send down.
-            NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags,
+            NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags,
                     AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState, 0,
                     /* deviceTimestamp */ 0,
                     1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
@@ -6348,7 +6355,7 @@
         }
 
         // Send move.
-        NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags,
+        NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags,
                 AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, 0,
                 /* deviceTimestamp */ 0,
                 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
@@ -6362,7 +6369,7 @@
             mPointerSimple.hovering = true;
 
             // Send hover enter.
-            NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags,
+            NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags,
                     AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState,
                     mCurrentRawState.buttonState, 0,
                     /* deviceTimestamp */ 0,
@@ -6373,7 +6380,7 @@
         }
 
         // Send hover move.
-        NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags,
+        NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags,
                 AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
                 mCurrentRawState.buttonState, 0,
                 /* deviceTimestamp */ 0,
@@ -6395,7 +6402,7 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-        NotifyMotionArgs args(when, getDeviceId(), mSource, mViewport.displayId, policyFlags,
+        NotifyMotionArgs args(when, getDeviceId(), mSource, displayId, policyFlags,
                 AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState, 0,
                 /* deviceTimestamp */ 0,
                 1, &mPointerSimple.currentProperties, &pointerCoords,
@@ -6457,8 +6464,9 @@
             ALOG_ASSERT(false);
         }
     }
-
-    NotifyMotionArgs args(when, getDeviceId(), source, mViewport.displayId, policyFlags,
+    int32_t displayId = mPointerController != nullptr ?
+            mPointerController->getDisplayId() : mViewport.displayId;
+    NotifyMotionArgs args(when, getDeviceId(), source, displayId, policyFlags,
             action, actionButton, flags, metaState, buttonState, edgeFlags,
             deviceTimestamp, pointerCount, pointerProperties, pointerCoords,
             xPrecision, yPrecision, downTime);
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index fe1c50b..5a78df7 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -342,6 +342,9 @@
     /* Gets the affine calibration associated with the specified device. */
     virtual TouchAffineTransformation getTouchAffineTransformation(
             const std::string& inputDeviceDescriptor, int32_t surfaceRotation) = 0;
+
+    /* Update the pointer controller associated with the specified display. */
+    virtual void updatePointerDisplay() = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index e94dd94..e60b3f4 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -58,6 +58,9 @@
     /* Gets the absolute location of the pointer. */
     virtual void getPosition(float* outX, float* outY) const = 0;
 
+    /* Gets the id of the display where the pointer should be shown. */
+    virtual int32_t getDisplayId() const = 0;
+
     enum Transition {
         // Fade/unfade immediately.
         TRANSITION_IMMEDIATE,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 04b87d5..b18cae3 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -57,6 +57,7 @@
     float mMinX, mMinY, mMaxX, mMaxY;
     float mX, mY;
     int32_t mButtonState;
+    int32_t mDisplayId;
 
 protected:
     virtual ~FakePointerController() { }
@@ -64,7 +65,7 @@
 public:
     FakePointerController() :
         mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mX(0), mY(0),
-        mButtonState(0) {
+        mButtonState(0), mDisplayId(ADISPLAY_ID_DEFAULT) {
     }
 
     void setBounds(float minX, float minY, float maxX, float maxY) {
@@ -75,6 +76,10 @@
         mMaxY = maxY;
     }
 
+    void setDisplayId(int32_t displayId) {
+        mDisplayId = displayId;
+    }
+
     virtual void setPosition(float x, float y) {
         mX = x;
         mY = y;
@@ -93,6 +98,10 @@
         *outY = mY;
     }
 
+    virtual int32_t getDisplayId() const {
+        return mDisplayId;
+    }
+
 private:
     virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const {
         *outMinX = mMinX;
@@ -262,6 +271,9 @@
     virtual std::string getDeviceAlias(const InputDeviceIdentifier&) {
         return "";
     }
+
+    virtual void updatePointerDisplay() {
+    }
 };
 
 
@@ -3127,6 +3139,30 @@
     ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
 }
 
+TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addMapperAndConfigure(mapper);
+
+    // Setup PointerController for second display.
+    constexpr int32_t SECOND_DISPLAY_ID = 1;
+    mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+    mFakePointerController->setPosition(100, 200);
+    mFakePointerController->setButtonState(0);
+    mFakePointerController->setDisplayId(SECOND_DISPLAY_ID);
+
+    NotifyMotionArgs args;
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
+            110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
+    ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f));
+    ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId);
+}
+
 
 // --- TouchInputMapperTest ---
 
@@ -4625,7 +4661,6 @@
             toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
 }
 
-
 // --- MultiTouchInputMapperTest ---
 
 class MultiTouchInputMapperTest : public TouchInputMapperTest {
@@ -6246,4 +6281,30 @@
     ASSERT_EQ(DISPLAY_ID, args.displayId);
 }
 
+TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) {
+    // Setup PointerController for second display.
+    sp<FakePointerController> fakePointerController = new FakePointerController();
+    fakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+    fakePointerController->setPosition(100, 200);
+    fakePointerController->setButtonState(0);
+    fakePointerController->setDisplayId(SECONDARY_DISPLAY_ID);
+    mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+
+    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION);
+    addMapperAndConfigure(mapper);
+
+    // Check source is mouse that would obtain the PointerController.
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
+
+    NotifyMotionArgs motionArgs;
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
+    ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
+}
+
 } // namespace android
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 8dc80cc..776efab 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -32,8 +32,9 @@
         const String16& opPackageName, bool hasSensorAccess)
     : mService(service), mUid(uid), mWakeLockRefCount(0), mHasLooperCallbacks(false),
       mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(nullptr),
-      mCacheSize(0), mMaxCacheSize(0), mPackageName(packageName), mOpPackageName(opPackageName),
-      mDestroyed(false), mHasSensorAccess(hasSensorAccess) {
+      mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0),
+      mPackageName(packageName), mOpPackageName(opPackageName), mDestroyed(false),
+      mHasSensorAccess(hasSensorAccess) {
     mChannel = new BitTube(mService->mSocketBufferSize);
 #if DEBUG_CONNECTIONS
     mEventsReceived = mEventsSentFromCache = mEventsSent = 0;
@@ -405,9 +406,6 @@
         reAllocateCacheLocked(events, count);
     } else {
         // The events do not fit within the cache: drop the oldest events.
-        ALOGW("Dropping events from cache (%d / %d) to save %d newer events", mCacheSize,
-                mMaxCacheSize, count);
-
         int freeSpace = mMaxCacheSize - mCacheSize;
 
         // Drop up to the currently cached number of events to make room for new events
@@ -419,6 +417,18 @@
         // Determine the number of new events to copy into the cache
         int eventsToCopy = std::min(mMaxCacheSize, count);
 
+        constexpr nsecs_t kMinimumTimeBetweenDropLogNs = 2 * 1000 * 1000 * 1000; // 2 sec
+        if (events[0].timestamp - mTimeOfLastEventDrop > kMinimumTimeBetweenDropLogNs) {
+            ALOGW("Dropping %d cached events (%d/%d) to save %d/%d new events. %d events previously"
+                    " dropped", cachedEventsToDrop, mCacheSize, mMaxCacheSize, eventsToCopy,
+                    count, mEventsDropped);
+            mEventsDropped = 0;
+            mTimeOfLastEventDrop = events[0].timestamp;
+        } else {
+            // Record the number dropped
+            mEventsDropped += cachedEventsToDrop + newEventsToDrop;
+        }
+
         // Check for any flush complete events in the events that will be dropped
         countFlushCompleteEventsLocked(mEventCache, cachedEventsToDrop);
         countFlushCompleteEventsLocked(events, newEventsToDrop);
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index eefd81a..061809f 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -165,6 +165,8 @@
 
     sensors_event_t *mEventCache;
     int mCacheSize, mMaxCacheSize;
+    int64_t mTimeOfLastEventDrop;
+    int mEventsDropped;
     String8 mPackageName;
     const String16 mOpPackageName;
 #if DEBUG_CONNECTIONS
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 7eaf7b2..fed8481 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -39,6 +39,9 @@
         "-Wno-switch-enum",
         "-Wno-undef",
 
+        // Have clang emit complete debug_info.
+        "-fstandalone-debug",
+
         //"-DLOG_NDEBUG=0",
     ],