Merge "SF: Do not duplicate fences per layer per frame" into tm-dev
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index b4aa88e..a49f563 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -1957,17 +1957,43 @@
 #endif // GRANULAR_LOCKS
             FTS *fts;
             FTSENT *p;
+
+            // Create a list of data paths whose children have cache directories
             auto ce_path = create_data_user_ce_path(uuid_, userId);
             auto de_path = create_data_user_de_path(uuid_, userId);
             auto media_path = findDataMediaPath(uuid, userId) + "/Android/data/";
-            char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(),
-                    (char*) media_path.c_str(), nullptr };
+            auto ce_sdk_path = create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/true, userId);
+            auto de_sdk_path = create_data_misc_sdk_sandbox_path(uuid_, /*isCeData=*/false, userId);
+
+            std::vector<std::string> dataPaths = {ce_path, de_path, media_path};
+            foreach_subdir(ce_sdk_path, [&ce_sdk_path, &dataPaths](const std::string subDir) {
+                const auto fullpath = ce_sdk_path + "/" + subDir;
+                dataPaths.push_back(fullpath);
+            });
+            foreach_subdir(de_sdk_path, [&de_sdk_path, &dataPaths](const std::string subDir) {
+                const auto fullpath = de_sdk_path + "/" + subDir;
+                dataPaths.push_back((char*)fullpath.c_str());
+            });
+
+            char* argv[dataPaths.size() + 1];
+            for (unsigned int i = 0; i < dataPaths.size(); i++) {
+                argv[i] = (char*)dataPaths[i].c_str();
+            }
+            argv[dataPaths.size()] = nullptr;
+
             if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) {
                 return error("Failed to fts_open");
             }
             while ((p = fts_read(fts)) != nullptr) {
                 if (p->fts_info == FTS_D && p->fts_level == 1) {
                     uid_t uid = p->fts_statp->st_uid;
+
+                    // If uid belongs to sdk sandbox, then the cache should be attributed to the
+                    // original client app.
+                    const auto client_uid = multiuser_convert_sdk_sandbox_to_app_uid(uid);
+                    const bool isSandboxUid = (client_uid != (uid_t)-1);
+                    if (isSandboxUid) uid = client_uid;
+
                     if (multiuser_get_app_id(uid) == AID_MEDIA_RW) {
                         uid = (multiuser_get_app_id(p->fts_statp->st_gid) - AID_EXT_GID_START)
                                 + AID_APP_START;
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 6112d5f..eef69f4 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -267,7 +267,8 @@
      * {@link ASENSOR_TYPE_HEAD_TRACKER}
      * reporting-mode: continuous
      *
-     * Measures the orientation and rotational velocity of a user's head.
+     * Measures the orientation and rotational velocity of a user's head. Only for internal use
+     * within the Android system.
      */
     ASENSOR_TYPE_HEAD_TRACKER = 37,
     /**
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index 68b99b0..61e6657 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -196,35 +196,6 @@
 status_t validateMetadataType(InputHidlVec* input, const MetadataType& expectedMetadataType);
 
 /**
- * Private helper functions
- */
-template <class T>
-status_t encodeInteger(const T& input, OutputHidlVec* output) {
-    static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
-                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
-                  std::is_same<T, float>::value || std::is_same<T, double>::value);
-    if (!output) {
-        return BAD_VALUE;
-    }
-
-    const uint8_t* tmp = reinterpret_cast<const uint8_t*>(&input);
-    return output->encode(tmp, sizeof(input));
-}
-
-template <class T>
-status_t decodeInteger(InputHidlVec* input, T* output) {
-    static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
-                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
-                  std::is_same<T, float>::value || std::is_same<T, double>::value);
-    if (!output) {
-        return BAD_VALUE;
-    }
-
-    uint8_t* tmp = reinterpret_cast<uint8_t*>(output);
-    return input->decode(tmp, sizeof(*output));
-}
-
-/**
  * encode/encodeMetadata are the main encoding functions. They take in T and uses the encodeHelper
  * function to turn T into the hidl_vec byte stream.
  *
@@ -280,45 +251,10 @@
 template <class T>
 status_t encodeOptionalMetadata(const MetadataType& metadataType, const std::optional<T>& input,
                         hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
-    OutputHidlVec outputHidlVec{output};
-
-    status_t err = encodeMetadataType(metadataType, &outputHidlVec);
-    if (err) {
-        return err;
+    if (!input) {
+        return NO_ERROR;
     }
-
-    err = encodeInteger<uint32_t>(input.has_value() ? 1 : 0, &outputHidlVec);
-    if (err) {
-        return err;
-    }
-
-    if (input) {
-        err = encodeHelper(*input, &outputHidlVec);
-        if (err) {
-            return err;
-        }
-    }
-
-    err = outputHidlVec.resize();
-    if (err) {
-        return err;
-    }
-
-    err = encodeMetadataType(metadataType, &outputHidlVec);
-    if (err) {
-        return err;
-    }
-
-    err = encodeInteger<uint32_t>(input.has_value() ? 1 : 0, &outputHidlVec);
-    if (err) {
-        return err;
-    }
-
-    if (input) {
-        return encodeHelper(*input, &outputHidlVec);
-    }
-
-    return NO_ERROR;
+    return encodeMetadata(metadataType, *input, output, encodeHelper);
 }
 
 /**
@@ -379,36 +315,45 @@
     if (!output) {
         return BAD_VALUE;
     }
-
-    InputHidlVec inputHidlVec{&input};
-
-    status_t err = validateMetadataType(&inputHidlVec, metadataType);
-    if (err) {
-        return err;
+    if (input.size() <= 0) {
+        output->reset();
+        return NO_ERROR;
     }
-
-    uint32_t present = 0;
-    err = decodeInteger<uint32_t>(&inputHidlVec, &present);
-    if (err) {
-        return err;
-    }
-
-    if (present) {
-        T tmp;
-        err = decodeHelper(&inputHidlVec, &tmp);
-        if (err) {
-            return err;
-        }
-
+    T tmp;
+    status_t err = decodeMetadata(metadataType, input, &tmp, decodeHelper);
+    if (!err) {
         *output = tmp;
     }
+    return err;
+}
 
-    err = inputHidlVec.hasRemainingData();
-    if (err) {
+/**
+ * Private helper functions
+ */
+template <class T>
+status_t encodeInteger(const T& input, OutputHidlVec* output) {
+    static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
+                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+                  std::is_same<T, float>::value || std::is_same<T, double>::value);
+    if (!output) {
         return BAD_VALUE;
     }
 
-    return NO_ERROR;
+    const uint8_t* tmp = reinterpret_cast<const uint8_t*>(&input);
+    return output->encode(tmp, sizeof(input));
+}
+
+template <class T>
+status_t decodeInteger(InputHidlVec* input, T* output) {
+    static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
+                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+                  std::is_same<T, float>::value || std::is_same<T, double>::value);
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    uint8_t* tmp = reinterpret_cast<uint8_t*>(output);
+    return input->decode(tmp, sizeof(*output));
 }
 
 status_t encodeString(const std::string& input, OutputHidlVec* output) {
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index c2793ac..dbccf30 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -165,6 +165,17 @@
     mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
     mNumAcquired = 0;
     mNumFrameAvailable = 0;
+
+    TransactionCompletedListener::getInstance()->addQueueStallListener(
+        [&]() {
+            std::function<void(bool)> callbackCopy;
+            {
+                std::unique_lock _lock{mMutex};
+                callbackCopy = mTransactionHangCallback;
+            }
+            if (callbackCopy) callbackCopy(true);
+        }, this);
+
     BQA_LOGV("BLASTBufferQueue created");
 }
 
@@ -175,6 +186,7 @@
 }
 
 BLASTBufferQueue::~BLASTBufferQueue() {
+    TransactionCompletedListener::getInstance()->removeQueueStallListener(this);
     if (mPendingTransactions.empty()) {
         return;
     }
@@ -1113,4 +1125,9 @@
     return SurfaceControl::isSameSurface(mSurfaceControl, surfaceControl);
 }
 
+void BLASTBufferQueue::setTransactionHangCallback(std::function<void(bool)> callback) {
+    std::unique_lock _lock{mMutex};
+    mTransactionHangCallback = callback;
+}
+
 } // namespace android
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index f7392d4..e4b8bad 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -29,6 +29,7 @@
 enum class Tag : uint32_t {
     ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION,
     ON_RELEASE_BUFFER,
+    ON_TRANSACTION_QUEUE_STALLED,
     LAST = ON_RELEASE_BUFFER,
 };
 
@@ -277,6 +278,11 @@
                                                                   callbackId, releaseFence,
                                                                   currentMaxAcquiredBufferCount);
     }
+
+    void onTransactionQueueStalled() override {
+        callRemoteAsync<decltype(&ITransactionCompletedListener::onTransactionQueueStalled)>(
+            Tag::ON_TRANSACTION_QUEUE_STALLED);
+    }
 };
 
 // Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
@@ -297,6 +303,9 @@
                                   &ITransactionCompletedListener::onTransactionCompleted);
         case Tag::ON_RELEASE_BUFFER:
             return callLocalAsync(data, reply, &ITransactionCompletedListener::onReleaseBuffer);
+        case Tag::ON_TRANSACTION_QUEUE_STALLED:
+            return callLocalAsync(data, reply,
+                                  &ITransactionCompletedListener::onTransactionQueueStalled);
     }
 }
 
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 6642ec6..501f8cf 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -447,6 +447,27 @@
     }
 }
 
+void TransactionCompletedListener::onTransactionQueueStalled() {
+      std::unordered_map<void*, std::function<void()>> callbackCopy;
+      {
+          std::scoped_lock<std::mutex> lock(mMutex);
+          callbackCopy = mQueueStallListeners;
+      }
+      for (auto const& it : callbackCopy) {
+          it.second();
+      }
+}
+
+void TransactionCompletedListener::addQueueStallListener(std::function<void()> stallListener,
+                                                         void* id) {
+    std::scoped_lock<std::mutex> lock(mMutex);
+    mQueueStallListeners[id] = stallListener;
+}
+void TransactionCompletedListener::removeQueueStallListener(void *id) {
+    std::scoped_lock<std::mutex> lock(mMutex);
+    mQueueStallListeners.erase(id);
+}
+
 void TransactionCompletedListener::onReleaseBuffer(ReleaseCallbackId callbackId,
                                                    sp<Fence> releaseFence,
                                                    uint32_t currentMaxAcquiredBufferCount) {
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 65fc04d..9328a54 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -113,6 +113,14 @@
     uint64_t getLastAcquiredFrameNum();
     void abandon();
 
+    /**
+     * Set a callback to be invoked when we are hung. The boolean parameter
+     * indicates whether the hang is due to an unfired fence.
+     * TODO: The boolean is always true atm, unfired fence is
+     * the only case we detect.
+     */
+    void setTransactionHangCallback(std::function<void(bool)> callback);
+
     virtual ~BLASTBufferQueue();
 
 private:
@@ -269,6 +277,8 @@
     // transaction that will be applied by some sync consumer.
     bool mAppliedLastTransaction = false;
     uint64_t mLastAppliedFrameNumber = 0;
+
+    std::function<void(bool)> mTransactionHangCallback;
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index a791c66..cc136bb 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -194,6 +194,7 @@
 
     virtual void onReleaseBuffer(ReleaseCallbackId callbackId, sp<Fence> releaseFence,
                                  uint32_t currentMaxAcquiredBufferCount) = 0;
+    virtual void onTransactionQueueStalled() = 0;
 };
 
 class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> {
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0cc43d8..efbdb36 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -772,6 +772,7 @@
     // This is protected by mSurfaceStatsListenerMutex, but GUARDED_BY isn't supported for
     // std::recursive_mutex
     std::multimap<int32_t, SurfaceStatsCallbackEntry> mSurfaceStatsListeners;
+    std::unordered_map<void*, std::function<void()>> mQueueStallListeners;
 
 public:
     static sp<TransactionCompletedListener> getInstance();
@@ -789,6 +790,9 @@
             const sp<SurfaceControl>& surfaceControl,
             const std::unordered_set<CallbackId, CallbackIdHash>& callbackIds);
 
+    void addQueueStallListener(std::function<void()> stallListener, void* id);
+    void removeQueueStallListener(void *id);
+
     /*
      * Adds a jank listener to be informed about SurfaceFlinger's jank classification for a specific
      * surface. Jank classifications arrive as part of the transaction callbacks about previous
@@ -817,6 +821,8 @@
     // For Testing Only
     static void setInstance(const sp<TransactionCompletedListener>&);
 
+    void onTransactionQueueStalled() override;
+
 private:
     ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&);
     static sp<TransactionCompletedListener> sInstance;
diff --git a/libs/input/android/os/InputEventInjectionResult.aidl b/libs/input/android/os/InputEventInjectionResult.aidl
index 34f10ec..3bc7068 100644
--- a/libs/input/android/os/InputEventInjectionResult.aidl
+++ b/libs/input/android/os/InputEventInjectionResult.aidl
@@ -29,9 +29,8 @@
     /* Injection succeeded. */
     SUCCEEDED = 0,
 
-    /* Injection failed because the injector did not have permission to inject
-     * into the application with input focus. */
-    PERMISSION_DENIED = 1,
+    /* Injection failed because the injected event did not target the appropriate window. */
+    TARGET_MISMATCH = 1,
 
     /* Injection failed because there were no available input targets. */
     FAILED = 2,
diff --git a/libs/renderengine/TEST_MAPPING b/libs/renderengine/TEST_MAPPING
index 995dba1..db00118 100644
--- a/libs/renderengine/TEST_MAPPING
+++ b/libs/renderengine/TEST_MAPPING
@@ -3,5 +3,11 @@
     {
       "name": "librenderengine_test"
     }
+  ],
+
+  "imports": [
+    {
+      "path": "frameworks/native/services/surfaceflinger"
+    }
   ]
 }
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
index f0d45c2..62745dc 100644
--- a/libs/shaders/shaders.cpp
+++ b/libs/shaders/shaders.cpp
@@ -78,7 +78,7 @@
             shader.append(R"(
 
                 float EOTF_sRGB(float srgb) {
-                    return srgb <= 0.08125 ? srgb / 4.50 : pow((srgb + 0.099) / 1.099, 0.45);
+                    return srgb <= 0.08125 ? srgb / 4.50 : pow((srgb + 0.099) / 1.099, 1 / 0.45);
                 }
 
                 float3 EOTF_sRGB(float3 srgb) {
diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp
index beca7f1..86c788d 100644
--- a/opengl/libs/EGL/BlobCache.cpp
+++ b/opengl/libs/EGL/BlobCache.cpp
@@ -52,35 +52,37 @@
     ALOGV("initializing random seed using %lld", (unsigned long long)now);
 }
 
-void BlobCache::set(const void* key, size_t keySize, const void* value, size_t valueSize) {
+BlobCache::InsertResult BlobCache::set(const void* key, size_t keySize, const void* value,
+                                       size_t valueSize) {
     if (mMaxKeySize < keySize) {
         ALOGV("set: not caching because the key is too large: %zu (limit: %zu)", keySize,
               mMaxKeySize);
-        return;
+        return InsertResult::kKeyTooBig;
     }
     if (mMaxValueSize < valueSize) {
         ALOGV("set: not caching because the value is too large: %zu (limit: %zu)", valueSize,
               mMaxValueSize);
-        return;
+        return InsertResult::kValueTooBig;
     }
     if (mMaxTotalSize < keySize + valueSize) {
         ALOGV("set: not caching because the combined key/value size is too "
               "large: %zu (limit: %zu)",
               keySize + valueSize, mMaxTotalSize);
-        return;
+        return InsertResult::kCombinedTooBig;
     }
     if (keySize == 0) {
         ALOGW("set: not caching because keySize is 0");
-        return;
+        return InsertResult::kInvalidKeySize;
     }
-    if (valueSize <= 0) {
+    if (valueSize == 0) {
         ALOGW("set: not caching because valueSize is 0");
-        return;
+        return InsertResult::kInvalidValueSize;
     }
 
     std::shared_ptr<Blob> cacheKey(new Blob(key, keySize, false));
     CacheEntry cacheEntry(cacheKey, nullptr);
 
+    bool didClean = false;
     while (true) {
         auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), cacheEntry);
         if (index == mCacheEntries.end() || cacheEntry < *index) {
@@ -92,13 +94,14 @@
                 if (isCleanable()) {
                     // Clean the cache and try again.
                     clean();
+                    didClean = true;
                     continue;
                 } else {
                     ALOGV("set: not caching new key/value pair because the "
                           "total cache size limit would be exceeded: %zu "
                           "(limit: %zu)",
                           keySize + valueSize, mMaxTotalSize);
-                    break;
+                    return InsertResult::kNotEnoughSpace;
                 }
             }
             mCacheEntries.insert(index, CacheEntry(keyBlob, valueBlob));
@@ -114,12 +117,13 @@
                 if (isCleanable()) {
                     // Clean the cache and try again.
                     clean();
+                    didClean = true;
                     continue;
                 } else {
                     ALOGV("set: not caching new value because the total cache "
                           "size limit would be exceeded: %zu (limit: %zu)",
                           keySize + valueSize, mMaxTotalSize);
-                    break;
+                    return InsertResult::kNotEnoughSpace;
                 }
             }
             index->setValue(valueBlob);
@@ -128,7 +132,7 @@
                   "value",
                   keySize, valueSize);
         }
-        break;
+        return didClean ? InsertResult::kDidClean : InsertResult::kInserted;
     }
 }
 
diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h
index 50b4e4c..ff03d30 100644
--- a/opengl/libs/EGL/BlobCache.h
+++ b/opengl/libs/EGL/BlobCache.h
@@ -39,6 +39,26 @@
     // (key sizes plus value sizes) will not exceed maxTotalSize.
     BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize);
 
+    // Return value from set(), below.
+    enum class InsertResult {
+        // The key is larger than maxKeySize specified in the constructor.
+        kKeyTooBig,
+        // The value is larger than maxValueSize specified in the constructor.
+        kValueTooBig,
+        // The combined key + value is larger than maxTotalSize specified in the constructor.
+        kCombinedTooBig,
+        // keySize is 0
+        kInvalidKeySize,
+        // valueSize is 0
+        kInvalidValueSize,
+        // Unable to free enough space to fit the new entry.
+        kNotEnoughSpace,
+        // The new entry was inserted, but an old entry had to be evicted.
+        kDidClean,
+        // There was enough room in the cache and the new entry was inserted.
+        kInserted,
+
+    };
     // set inserts a new binary value into the cache and associates it with the
     // given binary key.  If the key or value are too large for the cache then
     // the cache remains unchanged.  This includes the case where a different
@@ -54,7 +74,7 @@
     //   0 < keySize
     //   value != NULL
     //   0 < valueSize
-    void set(const void* key, size_t keySize, const void* value, size_t valueSize);
+    InsertResult set(const void* key, size_t keySize, const void* value, size_t valueSize);
 
     // get retrieves from the cache the binary value associated with a given
     // binary key.  If the key is present in the cache then the length of the
diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp
index d31373b..ceea0fb 100644
--- a/opengl/libs/EGL/BlobCache_test.cpp
+++ b/opengl/libs/EGL/BlobCache_test.cpp
@@ -49,7 +49,7 @@
 
 TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
     unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
-    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(BlobCache::InsertResult::kInserted, mBC->set("abcd", 4, "efgh", 4));
     ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
     ASSERT_EQ('e', buf[0]);
     ASSERT_EQ('f', buf[1]);
@@ -59,8 +59,8 @@
 
 TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
     unsigned char buf[2] = {0xee, 0xee};
-    mBC->set("ab", 2, "cd", 2);
-    mBC->set("ef", 2, "gh", 2);
+    ASSERT_EQ(BlobCache::InsertResult::kInserted, mBC->set("ab", 2, "cd", 2));
+    ASSERT_EQ(BlobCache::InsertResult::kInserted, mBC->set("ef", 2, "gh", 2));
     ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
     ASSERT_EQ('c', buf[0]);
     ASSERT_EQ('d', buf[1]);
@@ -71,7 +71,7 @@
 
 TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
     unsigned char buf[6] = {0xee, 0xee, 0xee, 0xee, 0xee, 0xee};
-    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(BlobCache::InsertResult::kInserted, mBC->set("abcd", 4, "efgh", 4));
     ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf + 1, 4));
     ASSERT_EQ(0xee, buf[0]);
     ASSERT_EQ('e', buf[1]);
@@ -83,7 +83,7 @@
 
 TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
     unsigned char buf[3] = {0xee, 0xee, 0xee};
-    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(BlobCache::InsertResult::kInserted, mBC->set("abcd", 4, "efgh", 4));
     ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
     ASSERT_EQ(0xee, buf[0]);
     ASSERT_EQ(0xee, buf[1]);
@@ -91,14 +91,14 @@
 }
 
 TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
-    mBC->set("abcd", 4, "efgh", 4);
+    ASSERT_EQ(BlobCache::InsertResult::kInserted, mBC->set("abcd", 4, "efgh", 4));
     ASSERT_EQ(size_t(4), mBC->get("abcd", 4, nullptr, 0));
 }
 
 TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
     unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee};
-    mBC->set("abcd", 4, "efgh", 4);
-    mBC->set("abcd", 4, "ijkl", 4);
+    ASSERT_EQ(BlobCache::InsertResult::kInserted, mBC->set("abcd", 4, "efgh", 4));
+    ASSERT_EQ(BlobCache::InsertResult::kInserted, mBC->set("abcd", 4, "ijkl", 4));
     ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
     ASSERT_EQ('i', buf[0]);
     ASSERT_EQ('j', buf[1]);
@@ -108,8 +108,8 @@
 
 TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
     unsigned char buf[MAX_VALUE_SIZE + 1] = {0xee, 0xee, 0xee, 0xee};
-    mBC->set("abcd", 4, "efgh", 4);
-    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE + 1);
+    ASSERT_EQ(BlobCache::InsertResult::kInserted, mBC->set("abcd", 4, "efgh", 4));
+    ASSERT_EQ(BlobCache::InsertResult::kValueTooBig, mBC->set("abcd", 4, buf, MAX_VALUE_SIZE + 1));
     ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
     ASSERT_EQ('e', buf[0]);
     ASSERT_EQ('f', buf[1]);
@@ -123,7 +123,7 @@
     for (int i = 0; i < MAX_KEY_SIZE + 1; i++) {
         key[i] = 'a';
     }
-    mBC->set(key, MAX_KEY_SIZE + 1, "bbbb", 4);
+    ASSERT_EQ(BlobCache::InsertResult::kKeyTooBig, mBC->set(key, MAX_KEY_SIZE + 1, "bbbb", 4));
     ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE + 1, buf, 4));
     ASSERT_EQ(0xee, buf[0]);
     ASSERT_EQ(0xee, buf[1]);
@@ -136,7 +136,7 @@
     for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) {
         buf[i] = 'b';
     }
-    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE + 1);
+    ASSERT_EQ(BlobCache::InsertResult::kValueTooBig, mBC->set("abcd", 4, buf, MAX_VALUE_SIZE + 1));
     for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) {
         buf[i] = 0xee;
     }
@@ -163,7 +163,8 @@
         buf[i] = 'b';
     }
 
-    mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE);
+    ASSERT_EQ(BlobCache::InsertResult::kCombinedTooBig,
+              mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE));
     ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, nullptr, 0));
 }
 
@@ -173,7 +174,7 @@
     for (int i = 0; i < MAX_KEY_SIZE; i++) {
         key[i] = 'a';
     }
-    mBC->set(key, MAX_KEY_SIZE, "wxyz", 4);
+    ASSERT_EQ(BlobCache::InsertResult::kInserted, mBC->set(key, MAX_KEY_SIZE, "wxyz", 4));
     ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4));
     ASSERT_EQ('w', buf[0]);
     ASSERT_EQ('x', buf[1]);
@@ -186,7 +187,7 @@
     for (int i = 0; i < MAX_VALUE_SIZE; i++) {
         buf[i] = 'b';
     }
-    mBC->set("abcd", 4, buf, MAX_VALUE_SIZE);
+    ASSERT_EQ(BlobCache::InsertResult::kInserted, mBC->set("abcd", 4, buf, MAX_VALUE_SIZE));
     for (int i = 0; i < MAX_VALUE_SIZE; i++) {
         buf[i] = 0xee;
     }
@@ -212,13 +213,45 @@
         buf[i] = 'b';
     }
 
-    mBC->set(key, MAX_KEY_SIZE, buf, bufSize);
+    ASSERT_EQ(BlobCache::InsertResult::kInserted, mBC->set(key, MAX_KEY_SIZE, buf, bufSize));
     ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, nullptr, 0));
 }
 
+// Verify that kNotEnoughSpace is returned from BlobCache::set when expected.
+// Note: This relies on internal knowledge of how BlobCache works.
+TEST_F(BlobCacheTest, NotEnoughSpace) {
+    // Insert a small entry into the cache.
+    ASSERT_EQ(BlobCache::InsertResult::kInserted, mBC->set("x", 1, "y", 1));
+
+    // Attempt to put a max size entry into the cache. If the cache were empty,
+    // as in CacheMaxKeyValuePairSizeSucceeds, this would succeed. Based on the
+    // current logic of BlobCache, the small entry is not big enough to allow it
+    // to be cleaned to insert the new entry.
+    ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
+
+    enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE };
+
+    char key[MAX_KEY_SIZE];
+    char buf[bufSize];
+    for (int i = 0; i < MAX_KEY_SIZE; i++) {
+        key[i] = 'a';
+    }
+    for (int i = 0; i < bufSize; i++) {
+        buf[i] = 'b';
+    }
+
+    ASSERT_EQ(BlobCache::InsertResult::kNotEnoughSpace, mBC->set(key, MAX_KEY_SIZE, buf, bufSize));
+    ASSERT_EQ(0, mBC->get(key, MAX_KEY_SIZE, nullptr, 0));
+
+    // The original entry remains in the cache.
+    unsigned char buf2[1] = {0xee};
+    ASSERT_EQ(size_t(1), mBC->get("x", 1, buf2, 1));
+    ASSERT_EQ('y', buf2[0]);
+}
+
 TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
     unsigned char buf[1] = {0xee};
-    mBC->set("x", 1, "y", 1);
+    ASSERT_EQ(BlobCache::InsertResult::kInserted, mBC->set("x", 1, "y", 1));
     ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
     ASSERT_EQ('y', buf[0]);
 }
@@ -243,12 +276,12 @@
     const int maxEntries = MAX_TOTAL_SIZE / 2;
     for (int i = 0; i < maxEntries; i++) {
         uint8_t k = i;
-        mBC->set(&k, 1, "x", 1);
+        ASSERT_EQ(BlobCache::InsertResult::kInserted, mBC->set(&k, 1, "x", 1));
     }
     // Insert one more entry, causing a cache overflow.
     {
         uint8_t k = maxEntries;
-        mBC->set(&k, 1, "x", 1);
+        ASSERT_EQ(BlobCache::InsertResult::kDidClean, mBC->set(&k, 1, "x", 1));
     }
     // Count the number of entries in the cache.
     int numCached = 0;
@@ -261,6 +294,14 @@
     ASSERT_EQ(maxEntries / 2 + 1, numCached);
 }
 
+TEST_F(BlobCacheTest, InvalidKeySize) {
+    ASSERT_EQ(BlobCache::InsertResult::kInvalidKeySize, mBC->set("", 0, "efgh", 4));
+}
+
+TEST_F(BlobCacheTest, InvalidValueSize) {
+    ASSERT_EQ(BlobCache::InsertResult::kInvalidValueSize, mBC->set("abcd", 4, "", 0));
+}
+
 class BlobCacheFlattenTest : public BlobCacheTest {
 protected:
     virtual void SetUp() {
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 9b72ff4..b4b617e 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -24,6 +24,14 @@
       "name": "libinputservice_test"
     },
     {
+      "name": "CtsHardwareTestCases",
+      "options": [
+        {
+          "include-filter": "android.hardware.input.cts.tests"
+        }
+      ]
+    },
+    {
       "name": "CtsInputTestCases"
     },
     {
@@ -31,6 +39,7 @@
       "options": [
         {
           "include-filter": "android.view.cts.MotionEventTest",
+          "include-filter": "android.view.cts.PointerCaptureTest",
           "include-filter": "android.view.cts.VerifyInputEventTest"
         }
       ]
@@ -45,6 +54,14 @@
       ]
     },
     {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.input"
+        }
+      ]
+    },
+    {
       "name": "CtsSecurityTestCases",
       "options": [
         {
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 32eec29..a2e60c4 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -31,11 +31,11 @@
 namespace android::inputdispatcher {
 
 // An arbitrary device id.
-static const int32_t DEVICE_ID = 1;
+constexpr int32_t DEVICE_ID = 1;
 
-// An arbitrary injector pid / uid pair that has permission to inject events.
-static const int32_t INJECTOR_PID = 999;
-static const int32_t INJECTOR_UID = 1001;
+// The default pid and uid for windows created by the test.
+constexpr int32_t WINDOW_PID = 999;
+constexpr int32_t WINDOW_UID = 1001;
 
 static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 5s;
 static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms;
@@ -108,8 +108,6 @@
 
     void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
 
-    bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
-
     void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
 
     void setPointerCapture(const PointerCaptureRequest&) override {}
@@ -196,8 +194,8 @@
         mInfo.globalScaleFactor = 1.0;
         mInfo.touchableRegion.clear();
         mInfo.addTouchableRegion(mFrame);
-        mInfo.ownerPid = INJECTOR_PID;
-        mInfo.ownerUid = INJECTOR_UID;
+        mInfo.ownerPid = WINDOW_PID;
+        mInfo.ownerUid = WINDOW_UID;
         mInfo.displayId = ADISPLAY_ID_DEFAULT;
     }
 
@@ -310,14 +308,14 @@
     for (auto _ : state) {
         MotionEvent event = generateMotionEvent();
         // Send ACTION_DOWN
-        dispatcher.injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                    InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT,
+        dispatcher.injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+                                    INJECT_EVENT_TIMEOUT,
                                     POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
 
         // Send ACTION_UP
         event.setAction(AMOTION_EVENT_ACTION_UP);
-        dispatcher.injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                    InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT,
+        dispatcher.injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+                                    INJECT_EVENT_TIMEOUT,
                                     POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
 
         window->consumeEvent();
diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h
index 4636820..d1c8b8a 100644
--- a/services/inputflinger/dispatcher/DragState.h
+++ b/services/inputflinger/dispatcher/DragState.h
@@ -26,7 +26,8 @@
 namespace inputdispatcher {
 
 struct DragState {
-    DragState(const sp<android::gui::WindowInfoHandle>& windowHandle) : dragWindow(windowHandle) {}
+    DragState(const sp<android::gui::WindowInfoHandle>& windowHandle, int32_t pointerId)
+          : dragWindow(windowHandle), pointerId(pointerId) {}
     void dump(std::string& dump, const char* prefix = "");
 
     // The window being dragged.
@@ -37,6 +38,8 @@
     bool isStartDrag = false;
     // Indicate if the stylus button is down at the start of the drag.
     bool isStylusButtonDownAtStart = false;
+    // Indicate which pointer id is tracked by the drag and drop.
+    const int32_t pointerId;
 };
 
 } // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp
index c8024a6..c2d3ad6 100644
--- a/services/inputflinger/dispatcher/InjectionState.cpp
+++ b/services/inputflinger/dispatcher/InjectionState.cpp
@@ -20,10 +20,9 @@
 
 namespace android::inputdispatcher {
 
-InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid)
+InjectionState::InjectionState(const std::optional<int32_t>& targetUid)
       : refCount(1),
-        injectorPid(injectorPid),
-        injectorUid(injectorUid),
+        targetUid(targetUid),
         injectionResult(android::os::InputEventInjectionResult::PENDING),
         injectionIsAsync(false),
         pendingForegroundDispatches(0) {}
diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h
index 0bfafb1..90cf150 100644
--- a/services/inputflinger/dispatcher/InjectionState.h
+++ b/services/inputflinger/dispatcher/InjectionState.h
@@ -27,13 +27,12 @@
 struct InjectionState {
     mutable int32_t refCount;
 
-    int32_t injectorPid;
-    int32_t injectorUid;
+    std::optional<int32_t> targetUid;
     android::os::InputEventInjectionResult injectionResult; // initially PENDING
     bool injectionIsAsync;               // set to true if injection is not waiting for the result
     int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress
 
-    InjectionState(int32_t injectorPid, int32_t injectorUid);
+    explicit InjectionState(const std::optional<int32_t>& targetUid);
     void release();
 
 private:
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 5d8df5f..b5a3825 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -578,6 +578,27 @@
     return false;
 }
 
+// Checks targeted injection using the window's owner's uid.
+// Returns an empty string if an entry can be sent to the given window, or an error message if the
+// entry is a targeted injection whose uid target doesn't match the window owner.
+std::optional<std::string> verifyTargetedInjection(const sp<WindowInfoHandle>& window,
+                                                   const EventEntry& entry) {
+    if (entry.injectionState == nullptr || !entry.injectionState->targetUid) {
+        // The event was not injected, or the injected event does not target a window.
+        return {};
+    }
+    const int32_t uid = *entry.injectionState->targetUid;
+    if (window == nullptr) {
+        return StringPrintf("No valid window target for injection into uid %d.", uid);
+    }
+    if (entry.injectionState->targetUid != window->getInfo()->ownerUid) {
+        return StringPrintf("Injected event targeted at uid %d would be dispatched to window '%s' "
+                            "owned by uid %d.",
+                            uid, window->getName().c_str(), window->getInfo()->ownerUid);
+    }
+    return {};
+}
+
 } // namespace
 
 // --- InputDispatcher ---
@@ -1036,6 +1057,8 @@
 
     switch (entry.type) {
         case EventEntry::Type::KEY: {
+            LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0,
+                                "Unexpected untrusted event.");
             // Optimize app switch latency.
             // If the application takes too long to catch up then we drop all events preceding
             // the app switch key.
@@ -1073,6 +1096,8 @@
         }
 
         case EventEntry::Type::MOTION: {
+            LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0,
+                                "Unexpected untrusted event.");
             if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) {
                 mNextUnblockedEvent = mInboundQueue.back();
                 needWake = true;
@@ -1720,8 +1745,7 @@
     }
 
     setInjectionResult(*entry, injectionResult);
-    if (injectionResult == InputEventInjectionResult::PERMISSION_DENIED) {
-        ALOGW("Permission denied, dropping the motion (isPointer=%s)", toString(isPointerEvent));
+    if (injectionResult == InputEventInjectionResult::TARGET_MISMATCH) {
         return true;
     }
     if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
@@ -1747,18 +1771,12 @@
 }
 
 void InputDispatcher::enqueueDragEventLocked(const sp<WindowInfoHandle>& windowHandle,
-                                             bool isExiting, const MotionEntry& motionEntry) {
-    // If the window needs enqueue a drag event, the pointerCount should be 1 and the action should
-    // be AMOTION_EVENT_ACTION_MOVE, that could guarantee the first pointer is always valid.
-    LOG_ALWAYS_FATAL_IF(motionEntry.pointerCount != 1);
-    PointerCoords pointerCoords;
-    pointerCoords.copyFrom(motionEntry.pointerCoords[0]);
-    pointerCoords.transform(windowHandle->getInfo()->transform);
-
+                                             bool isExiting, const int32_t rawX,
+                                             const int32_t rawY) {
+    const vec2 xy = windowHandle->getInfo()->transform.transform(vec2(rawX, rawY));
     std::unique_ptr<DragEntry> dragEntry =
-            std::make_unique<DragEntry>(mIdGenerator.nextId(), motionEntry.eventTime,
-                                        windowHandle->getToken(), isExiting, pointerCoords.getX(),
-                                        pointerCoords.getY());
+            std::make_unique<DragEntry>(mIdGenerator.nextId(), now(), windowHandle->getToken(),
+                                        isExiting, xy.x, xy.y);
 
     enqueueInboundEventLocked(std::move(dragEntry));
 }
@@ -1978,9 +1996,10 @@
     // we have a valid, non-null focused window
     resetNoFocusedWindowTimeoutLocked();
 
-    // Check permissions.
-    if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
-        return InputEventInjectionResult::PERMISSION_DENIED;
+    // Verify targeted injection.
+    if (const auto err = verifyTargetedInjection(focusedWindowHandle, entry); err) {
+        ALOGW("Dropping injected event: %s", (*err).c_str());
+        return InputEventInjectionResult::TARGET_MISMATCH;
     }
 
     if (focusedWindowHandle->getInfo()->inputConfig.test(
@@ -2046,11 +2065,6 @@
         nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
         nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) {
     ATRACE_CALL();
-    enum InjectionPermission {
-        INJECTION_PERMISSION_UNKNOWN,
-        INJECTION_PERMISSION_GRANTED,
-        INJECTION_PERMISSION_DENIED
-    };
 
     // For security reasons, we defer updating the touch state until we are sure that
     // event injection will be allowed.
@@ -2060,7 +2074,6 @@
 
     // Update the touch state as needed based on the properties of the touch event.
     InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING;
-    InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
     sp<WindowInfoHandle> newHoverWindowHandle(mLastHoverWindowHandle);
     sp<WindowInfoHandle> newTouchedWindowHandle;
 
@@ -2109,7 +2122,7 @@
               "in display %" PRId32,
               displayId);
         // TODO: test multiple simultaneous input streams.
-        injectionResult = InputEventInjectionResult::PERMISSION_DENIED;
+        injectionResult = InputEventInjectionResult::FAILED;
         switchedDevice = false;
         wrongDevice = true;
         goto Failed;
@@ -2142,6 +2155,14 @@
             newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
         }
 
+        // Verify targeted injection.
+        if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
+            ALOGW("Dropping injected touch event: %s", (*err).c_str());
+            injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
+            newTouchedWindowHandle = nullptr;
+            goto Failed;
+        }
+
         // Figure out whether splitting will be allowed for this window.
         if (newTouchedWindowHandle != nullptr) {
             if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
@@ -2185,6 +2206,11 @@
         for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
             const WindowInfo& info = *windowHandle->getInfo();
 
+            // Skip spy window targets that are not valid for targeted injection.
+            if (const auto err = verifyTargetedInjection(windowHandle, entry); err) {
+                continue;
+            }
+
             if (info.inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) {
                 ALOGI("Not sending touch event to %s because it is paused",
                       windowHandle->getName().c_str());
@@ -2278,6 +2304,14 @@
             newTouchedWindowHandle =
                     findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus);
 
+            // Verify targeted injection.
+            if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
+                ALOGW("Dropping injected event: %s", (*err).c_str());
+                injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
+                newTouchedWindowHandle = nullptr;
+                goto Failed;
+            }
+
             // Drop touch events if requested by input feature
             if (newTouchedWindowHandle != nullptr &&
                 shouldDropInput(entry, newTouchedWindowHandle)) {
@@ -2369,19 +2403,26 @@
         goto Failed;
     }
 
-    // Check permission to inject into all touched foreground windows.
-    if (std::any_of(tempTouchState.windows.begin(), tempTouchState.windows.end(),
-                    [this, &entry](const TouchedWindow& touchedWindow) {
-                        return (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0 &&
-                                !checkInjectionPermission(touchedWindow.windowHandle,
-                                                          entry.injectionState);
-                    })) {
-        injectionResult = InputEventInjectionResult::PERMISSION_DENIED;
-        injectionPermission = INJECTION_PERMISSION_DENIED;
-        goto Failed;
+    // Ensure that all touched windows are valid for injection.
+    if (entry.injectionState != nullptr) {
+        std::string errs;
+        for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
+            if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+                // Allow ACTION_OUTSIDE events generated by targeted injection to be
+                // dispatched to any uid, since the coords will be zeroed out later.
+                continue;
+            }
+            const auto err = verifyTargetedInjection(touchedWindow.windowHandle, entry);
+            if (err) errs += "\n  - " + *err;
+        }
+        if (!errs.empty()) {
+            ALOGW("Dropping targeted injection: At least one touched window is not owned by uid "
+                  "%d:%s",
+                  *entry.injectionState->targetUid, errs.c_str());
+            injectionResult = InputEventInjectionResult::TARGET_MISMATCH;
+            goto Failed;
+        }
     }
-    // Permission granted to inject into all touched foreground windows.
-    injectionPermission = INJECTION_PERMISSION_GRANTED;
 
     // Check whether windows listening for outside touches are owned by the same UID. If it is
     // set the policy flag that we will not reveal coordinate information to this window.
@@ -2447,19 +2488,6 @@
     tempTouchState.filterNonAsIsTouchWindows();
 
 Failed:
-    // Check injection permission once and for all.
-    if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
-        if (checkInjectionPermission(nullptr, entry.injectionState)) {
-            injectionPermission = INJECTION_PERMISSION_GRANTED;
-        } else {
-            injectionPermission = INJECTION_PERMISSION_DENIED;
-        }
-    }
-
-    if (injectionPermission != INJECTION_PERMISSION_GRANTED) {
-        return injectionResult;
-    }
-
     // Update final pieces of touch state if the injector had permission.
     if (!wrongDevice) {
         if (switchedDevice) {
@@ -2546,13 +2574,14 @@
         vec2 local = dropWindow->getInfo()->transform.transform(x, y);
         sendDropWindowCommandLocked(dropWindow->getToken(), local.x, local.y);
     } else {
+        ALOGW("No window found when drop.");
         sendDropWindowCommandLocked(nullptr, 0, 0);
     }
     mDragState.reset();
 }
 
 void InputDispatcher::addDragEventLocked(const MotionEntry& entry) {
-    if (entry.pointerCount != 1 || !mDragState) {
+    if (!mDragState) {
         return;
     }
 
@@ -2562,42 +2591,75 @@
                 (entry.buttonState & AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) != 0;
     }
 
-    int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
-    int32_t x = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
-    int32_t y = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
-    if (maskedAction == AMOTION_EVENT_ACTION_MOVE) {
-        // Handle the special case : stylus button no longer pressed.
-        bool isStylusButtonDown = (entry.buttonState & AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) != 0;
-        if (mDragState->isStylusButtonDownAtStart && !isStylusButtonDown) {
-            finishDragAndDrop(entry.displayId, x, y);
-            return;
+    // Find the pointer index by id.
+    int32_t pointerIndex = 0;
+    for (; static_cast<uint32_t>(pointerIndex) < entry.pointerCount; pointerIndex++) {
+        const PointerProperties& pointerProperties = entry.pointerProperties[pointerIndex];
+        if (pointerProperties.id == mDragState->pointerId) {
+            break;
         }
+    }
 
-        // Prevent stylus interceptor windows from affecting drag and drop behavior for now, until
-        // we have an explicit reason to support it.
-        constexpr bool isStylus = false;
-
-        const sp<WindowInfoHandle> hoverWindowHandle =
-                findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/, isStylus,
-                                          false /*addOutsideTargets*/, true /*ignoreDragWindow*/);
-        // enqueue drag exit if needed.
-        if (hoverWindowHandle != mDragState->dragHoverWindowHandle &&
-            !haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) {
-            if (mDragState->dragHoverWindowHandle != nullptr) {
-                enqueueDragEventLocked(mDragState->dragHoverWindowHandle, true /*isExiting*/,
-                                       entry);
-            }
-            mDragState->dragHoverWindowHandle = hoverWindowHandle;
-        }
-        // enqueue drag location if needed.
-        if (hoverWindowHandle != nullptr) {
-            enqueueDragEventLocked(hoverWindowHandle, false /*isExiting*/, entry);
-        }
-    } else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
-        finishDragAndDrop(entry.displayId, x, y);
-    } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+    if (uint32_t(pointerIndex) == entry.pointerCount) {
+        LOG_ALWAYS_FATAL("Should find a valid pointer index by id %d", mDragState->pointerId);
         sendDropWindowCommandLocked(nullptr, 0, 0);
         mDragState.reset();
+        return;
+    }
+
+    const int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
+    const int32_t x = entry.pointerCoords[pointerIndex].getX();
+    const int32_t y = entry.pointerCoords[pointerIndex].getY();
+
+    switch (maskedAction) {
+        case AMOTION_EVENT_ACTION_MOVE: {
+            // Handle the special case : stylus button no longer pressed.
+            bool isStylusButtonDown =
+                    (entry.buttonState & AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) != 0;
+            if (mDragState->isStylusButtonDownAtStart && !isStylusButtonDown) {
+                finishDragAndDrop(entry.displayId, x, y);
+                return;
+            }
+
+            // Prevent stylus interceptor windows from affecting drag and drop behavior for now,
+            // until we have an explicit reason to support it.
+            constexpr bool isStylus = false;
+
+            const sp<WindowInfoHandle> hoverWindowHandle =
+                    findTouchedWindowAtLocked(entry.displayId, x, y, nullptr /*touchState*/,
+                                              isStylus, false /*addOutsideTargets*/,
+                                              true /*ignoreDragWindow*/);
+            // enqueue drag exit if needed.
+            if (hoverWindowHandle != mDragState->dragHoverWindowHandle &&
+                !haveSameToken(hoverWindowHandle, mDragState->dragHoverWindowHandle)) {
+                if (mDragState->dragHoverWindowHandle != nullptr) {
+                    enqueueDragEventLocked(mDragState->dragHoverWindowHandle, true /*isExiting*/, x,
+                                           y);
+                }
+                mDragState->dragHoverWindowHandle = hoverWindowHandle;
+            }
+            // enqueue drag location if needed.
+            if (hoverWindowHandle != nullptr) {
+                enqueueDragEventLocked(hoverWindowHandle, false /*isExiting*/, x, y);
+            }
+            break;
+        }
+
+        case AMOTION_EVENT_ACTION_POINTER_UP:
+            if (getMotionEventActionPointerIndex(entry.action) != pointerIndex) {
+                break;
+            }
+            // The drag pointer is up.
+            [[fallthrough]];
+        case AMOTION_EVENT_ACTION_UP:
+            finishDragAndDrop(entry.displayId, x, y);
+            break;
+        case AMOTION_EVENT_ACTION_CANCEL: {
+            ALOGD("Receiving cancel when drag and drop.");
+            sendDropWindowCommandLocked(nullptr, 0, 0);
+            mDragState.reset();
+            break;
+        }
     }
 }
 
@@ -2657,26 +2719,6 @@
     }
 }
 
-bool InputDispatcher::checkInjectionPermission(const sp<WindowInfoHandle>& windowHandle,
-                                               const InjectionState* injectionState) {
-    if (injectionState &&
-        (windowHandle == nullptr ||
-         windowHandle->getInfo()->ownerUid != injectionState->injectorUid) &&
-        !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) {
-        if (windowHandle != nullptr) {
-            ALOGW("Permission denied: injecting event from pid %d uid %d to window %s "
-                  "owned by uid %d",
-                  injectionState->injectorPid, injectionState->injectorUid,
-                  windowHandle->getName().c_str(), windowHandle->getInfo()->ownerUid);
-        } else {
-            ALOGW("Permission denied: injecting event from pid %d uid %d",
-                  injectionState->injectorPid, injectionState->injectorUid);
-        }
-        return false;
-    }
-    return true;
-}
-
 /**
  * Indicate whether one window handle should be considered as obscuring
  * another window handle. We only check a few preconditions. Actually
@@ -4195,20 +4237,20 @@
     }
 }
 
-InputEventInjectionResult InputDispatcher::injectInputEvent(
-        const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
-        InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) {
+InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* event,
+                                                            std::optional<int32_t> targetUid,
+                                                            InputEventInjectionSync syncMode,
+                                                            std::chrono::milliseconds timeout,
+                                                            uint32_t policyFlags) {
     if (DEBUG_INBOUND_EVENT_DETAILS) {
-        ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
-              "syncMode=%d, timeout=%lld, policyFlags=0x%08x",
-              event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags);
+        ALOGD("injectInputEvent - eventType=%d, targetUid=%s, syncMode=%d, timeout=%lld, "
+              "policyFlags=0x%08x",
+              event->getType(), targetUid ? std::to_string(*targetUid).c_str() : "none", syncMode,
+              timeout.count(), policyFlags);
     }
     nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
 
-    policyFlags |= POLICY_FLAG_INJECTED;
-    if (hasInjectionPermission(injectorPid, injectorUid)) {
-        policyFlags |= POLICY_FLAG_TRUSTED;
-    }
+    policyFlags |= POLICY_FLAG_INJECTED | POLICY_FLAG_TRUSTED;
 
     // For all injected events, set device id = VIRTUAL_KEYBOARD_ID. The only exception is events
     // that have gone through the InputFilter. If the event passed through the InputFilter, assign
@@ -4349,7 +4391,7 @@
             return InputEventInjectionResult::FAILED;
     }
 
-    InjectionState* injectionState = new InjectionState(injectorPid, injectorUid);
+    InjectionState* injectionState = new InjectionState(targetUid);
     if (syncMode == InputEventInjectionSync::NONE) {
         injectionState->injectionIsAsync = true;
     }
@@ -4421,8 +4463,7 @@
     } // release lock
 
     if (DEBUG_INJECTION) {
-        ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d",
-              injectionResult, injectorPid, injectorUid);
+        ALOGD("injectInputEvent - Finished with result %d.", injectionResult);
     }
 
     return injectionResult;
@@ -4461,19 +4502,12 @@
     return result;
 }
 
-bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) {
-    return injectorUid == 0 ||
-            mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
-}
-
 void InputDispatcher::setInjectionResult(EventEntry& entry,
                                          InputEventInjectionResult injectionResult) {
     InjectionState* injectionState = entry.injectionState;
     if (injectionState) {
         if (DEBUG_INJECTION) {
-            ALOGD("Setting input event injection result to %d.  "
-                  "injectorPid=%d, injectorUid=%d",
-                  injectionResult, injectionState->injectorPid, injectionState->injectorUid);
+            ALOGD("Setting input event injection result to %d.", injectionResult);
         }
 
         if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
@@ -4482,12 +4516,12 @@
                 case InputEventInjectionResult::SUCCEEDED:
                     ALOGV("Asynchronous input event injection succeeded.");
                     break;
+                case InputEventInjectionResult::TARGET_MISMATCH:
+                    ALOGV("Asynchronous input event injection target mismatch.");
+                    break;
                 case InputEventInjectionResult::FAILED:
                     ALOGW("Asynchronous input event injection failed.");
                     break;
-                case InputEventInjectionResult::PERMISSION_DENIED:
-                    ALOGW("Asynchronous input event injection permission denied.");
-                    break;
                 case InputEventInjectionResult::TIMED_OUT:
                     ALOGW("Asynchronous input event injection timed out.");
                     break;
@@ -5117,7 +5151,15 @@
 
         // Store the dragging window.
         if (isDragDrop) {
-            mDragState = std::make_unique<DragState>(toWindowHandle);
+            if (pointerIds.count() > 1) {
+                ALOGW("The drag and drop cannot be started when there is more than 1 pointer on the"
+                      " window.");
+                return false;
+            }
+            // If the window didn't not support split or the source is mouse, the pointerIds count
+            // would be 0, so we have to track the pointer 0.
+            const int32_t id = pointerIds.count() == 0 ? 0 : pointerIds.firstMarkedBit();
+            mDragState = std::make_unique<DragState>(toWindowHandle, id);
         }
 
         // Synthesize cancel for old window and down for new window.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index f3dac19..ed89ed0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -104,7 +104,7 @@
     void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
 
     android::os::InputEventInjectionResult injectInputEvent(
-            const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
+            const InputEvent* event, std::optional<int32_t> targetUid,
             android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
             uint32_t policyFlags) override;
 
@@ -221,7 +221,8 @@
                                  const std::string& reason) REQUIRES(mLock);
     // Enqueues a drag event.
     void enqueueDragEventLocked(const sp<android::gui::WindowInfoHandle>& windowToken,
-                                bool isExiting, const MotionEntry& motionEntry) REQUIRES(mLock);
+                                bool isExiting, const int32_t rawX, const int32_t rawY)
+            REQUIRES(mLock);
 
     // Adds an event to a queue of recent events for debugging purposes.
     void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
@@ -278,7 +279,6 @@
 
     // Event injection and synchronization.
     std::condition_variable mInjectionResultAvailable;
-    bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
     void setInjectionResult(EventEntry& entry,
                             android::os::InputEventInjectionResult injectionResult);
     void transformMotionEntryForInjectionLocked(MotionEntry&,
@@ -554,8 +554,6 @@
     void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId)
             REQUIRES(mLock);
     void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
-    bool checkInjectionPermission(const sp<android::gui::WindowInfoHandle>& windowHandle,
-                                  const InjectionState* injectionState);
     // Enqueue a drag event if needed, and update the touch state.
     // Uses findTouchedWindowTargetsLocked to make the decision
     void addDragEventLocked(const MotionEntry& entry) REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index d7bc5fb..67fed8b 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -68,10 +68,16 @@
      * input injection to proceed.
      * Returns one of the INPUT_EVENT_INJECTION_XXX constants.
      *
-     * This method may be called on any thread (usually by the input manager).
+     * If a targetUid is provided, InputDispatcher will only consider injecting the input event into
+     * windows owned by the provided uid. If the input event is targeted at a window that is not
+     * owned by the provided uid, input injection will fail. If no targetUid is provided, the input
+     * event will be dispatched as-is.
+     *
+     * This method may be called on any thread (usually by the input manager). The caller must
+     * perform all necessary permission checks prior to injecting events.
      */
     virtual android::os::InputEventInjectionResult injectInputEvent(
-            const InputEvent* event, int32_t injectorPid, int32_t injectorUid,
+            const InputEvent* event, std::optional<int32_t> targetUid,
             android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout,
             uint32_t policyFlags) = 0;
 
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index de0b6da..575b3d7 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -125,15 +125,6 @@
     /* Poke user activity for an event dispatched to a window. */
     virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) = 0;
 
-    /* Checks whether a given application pid/uid has permission to inject input events
-     * into other applications.
-     *
-     * This method is special in that its implementation promises to be non-reentrant and
-     * is safe to call while holding other locks.  (Most other methods make no such guarantees!)
-     */
-    virtual bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid,
-                                                         int32_t injectorUid) = 0;
-
     /* Notifies the policy that a pointer down event has occurred outside the current focused
      * window.
      *
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index b688898..91a666c 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -45,10 +45,10 @@
 using namespace ftl::flag_operators;
 
 // An arbitrary time value.
-static const nsecs_t ARBITRARY_TIME = 1234;
+static constexpr nsecs_t ARBITRARY_TIME = 1234;
 
 // An arbitrary device id.
-static const int32_t DEVICE_ID = 1;
+static constexpr int32_t DEVICE_ID = 1;
 
 // An arbitrary display id.
 static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
@@ -61,9 +61,12 @@
 static constexpr int32_t POINTER_1_UP =
         AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
 
-// An arbitrary injector pid / uid pair that has permission to inject events.
-static const int32_t INJECTOR_PID = 999;
-static const int32_t INJECTOR_UID = 1001;
+// The default pid and uid for windows created by the test.
+static constexpr int32_t WINDOW_PID = 999;
+static constexpr int32_t WINDOW_UID = 1001;
+
+// The default policy flags to use for event injection by tests.
+static constexpr uint32_t DEFAULT_POLICY_FLAGS = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
 
 // An arbitrary pid of the gesture monitor window
 static constexpr int32_t MONITOR_PID = 2001;
@@ -472,10 +475,6 @@
 
     void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
 
-    bool checkInjectEventsPermissionNonReentrant(int32_t pid, int32_t uid) override {
-        return pid == INJECTOR_PID && uid == INJECTOR_UID;
-    }
-
     void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
         std::scoped_lock lock(mLock);
         mOnPointerDownToken = newToken;
@@ -560,8 +559,8 @@
                      /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
                      ARBITRARY_TIME);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
-              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            InputEventInjectionSync::NONE, 0ms, 0))
+              mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+                                            0ms, 0))
             << "Should reject key events with undefined action.";
 
     // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
@@ -569,8 +568,8 @@
                      INVALID_HMAC, AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0,
                      ARBITRARY_TIME, ARBITRARY_TIME);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
-              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            InputEventInjectionSync::NONE, 0ms, 0))
+              mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+                                            0ms, 0))
             << "Should reject key events with ACTION_MULTIPLE.";
 }
 
@@ -599,8 +598,8 @@
                      ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
-              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            InputEventInjectionSync::NONE, 0ms, 0))
+              mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+                                            0ms, 0))
             << "Should reject motion events with undefined action.";
 
     // Rejects pointer down with invalid index.
@@ -611,8 +610,8 @@
                      ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
-              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            InputEventInjectionSync::NONE, 0ms, 0))
+              mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+                                            0ms, 0))
             << "Should reject motion events with pointer down index too large.";
 
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
@@ -623,8 +622,8 @@
                      identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
-              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            InputEventInjectionSync::NONE, 0ms, 0))
+              mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+                                            0ms, 0))
             << "Should reject motion events with pointer down index too small.";
 
     // Rejects pointer up with invalid index.
@@ -635,8 +634,8 @@
                      ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
-              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            InputEventInjectionSync::NONE, 0ms, 0))
+              mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+                                            0ms, 0))
             << "Should reject motion events with pointer up index too large.";
 
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
@@ -647,8 +646,8 @@
                      identityTransform, ARBITRARY_TIME, ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
-              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            InputEventInjectionSync::NONE, 0ms, 0))
+              mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+                                            0ms, 0))
             << "Should reject motion events with pointer up index too small.";
 
     // Rejects motion events with invalid number of pointers.
@@ -659,8 +658,8 @@
                      ARBITRARY_TIME,
                      /*pointerCount*/ 0, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
-              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            InputEventInjectionSync::NONE, 0ms, 0))
+              mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+                                            0ms, 0))
             << "Should reject motion events with 0 pointers.";
 
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
@@ -670,8 +669,8 @@
                      ARBITRARY_TIME,
                      /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
-              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            InputEventInjectionSync::NONE, 0ms, 0))
+              mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+                                            0ms, 0))
             << "Should reject motion events with more than MAX_POINTERS pointers.";
 
     // Rejects motion events with invalid pointer ids.
@@ -683,8 +682,8 @@
                      ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
-              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            InputEventInjectionSync::NONE, 0ms, 0))
+              mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+                                            0ms, 0))
             << "Should reject motion events with pointer ids less than 0.";
 
     pointerProperties[0].id = MAX_POINTER_ID + 1;
@@ -695,8 +694,8 @@
                      ARBITRARY_TIME,
                      /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
-              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            InputEventInjectionSync::NONE, 0ms, 0))
+              mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+                                            0ms, 0))
             << "Should reject motion events with pointer ids greater than MAX_POINTER_ID.";
 
     // Rejects motion events with duplicate pointer ids.
@@ -709,8 +708,8 @@
                      ARBITRARY_TIME,
                      /*pointerCount*/ 2, pointerProperties, pointerCoords);
     ASSERT_EQ(InputEventInjectionResult::FAILED,
-              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
-                                            InputEventInjectionSync::NONE, 0ms, 0))
+              mDispatcher->injectInputEvent(&event, {} /*targetUid*/, InputEventInjectionSync::NONE,
+                                            0ms, 0))
             << "Should reject motion events with duplicate pointer ids.";
 }
 
@@ -1013,8 +1012,8 @@
         mInfo.globalScaleFactor = 1.0;
         mInfo.touchableRegion.clear();
         mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
-        mInfo.ownerPid = INJECTOR_PID;
-        mInfo.ownerUid = INJECTOR_UID;
+        mInfo.ownerPid = WINDOW_PID;
+        mInfo.ownerUid = WINDOW_UID;
         mInfo.displayId = displayId;
         mInfo.inputConfig = WindowInfo::InputConfig::DEFAULT;
     }
@@ -1296,7 +1295,8 @@
         int32_t displayId = ADISPLAY_ID_NONE,
         InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT,
         std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
-        bool allowKeyRepeat = true) {
+        bool allowKeyRepeat = true, std::optional<int32_t> targetUid = {},
+        uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
     KeyEvent event;
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
@@ -1305,13 +1305,11 @@
                      INVALID_HMAC, action, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE,
                      repeatCount, currentTime, currentTime);
 
-    int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
     if (!allowKeyRepeat) {
         policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
     }
     // Inject event until dispatch out.
-    return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, syncMode,
-                                        injectionTimeout, policyFlags);
+    return dispatcher->injectInputEvent(&event, targetUid, syncMode, injectionTimeout, policyFlags);
 }
 
 static InputEventInjectionResult injectKeyDown(const std::unique_ptr<InputDispatcher>& dispatcher,
@@ -1454,10 +1452,10 @@
 static InputEventInjectionResult injectMotionEvent(
         const std::unique_ptr<InputDispatcher>& dispatcher, const MotionEvent& event,
         std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
-        InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT) {
-    return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
-                                        injectionTimeout,
-                                        POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+        InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT,
+        std::optional<int32_t> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
+    return dispatcher->injectInputEvent(&event, targetUid, injectionMode, injectionTimeout,
+                                        policyFlags);
 }
 
 static InputEventInjectionResult injectMotionEvent(
@@ -1467,7 +1465,8 @@
                                         AMOTION_EVENT_INVALID_CURSOR_POSITION},
         std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
         InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT,
-        nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) {
+        nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC),
+        std::optional<int32_t> targetUid = {}, uint32_t policyFlags = DEFAULT_POLICY_FLAGS) {
     MotionEvent event = MotionEventBuilder(action, source)
                                 .displayId(displayId)
                                 .eventTime(eventTime)
@@ -1479,7 +1478,8 @@
                                 .build();
 
     // Inject event until dispatch out.
-    return injectMotionEvent(dispatcher, event, injectionTimeout, injectionMode);
+    return injectMotionEvent(dispatcher, event, injectionTimeout, injectionMode, targetUid,
+                             policyFlags);
 }
 
 static InputEventInjectionResult injectMotionDown(
@@ -3574,8 +3574,8 @@
  * FLAG_WINDOW_IS_PARTIALLY_OBSCURED.
  */
 TEST_F(InputDispatcherTest, SlipperyWindow_SetsFlagPartiallyObscured) {
-    constexpr int32_t SLIPPERY_PID = INJECTOR_PID + 1;
-    constexpr int32_t SLIPPERY_UID = INJECTOR_UID + 1;
+    constexpr int32_t SLIPPERY_PID = WINDOW_PID + 1;
+    constexpr int32_t SLIPPERY_UID = WINDOW_UID + 1;
 
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
@@ -4102,7 +4102,7 @@
         const int32_t additionalPolicyFlags =
                 POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_DISABLE_KEY_REPEAT;
         ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
-                  mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                  mDispatcher->injectInputEvent(&event, {} /*targetUid*/,
                                                 InputEventInjectionSync::WAIT_FOR_RESULT, 10ms,
                                                 policyFlags | additionalPolicyFlags));
 
@@ -4137,7 +4137,7 @@
 
         const int32_t additionalPolicyFlags = POLICY_FLAG_PASS_TO_USER;
         ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
-                  mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                  mDispatcher->injectInputEvent(&event, {} /*targetUid*/,
                                                 InputEventInjectionSync::WAIT_FOR_RESULT, 10ms,
                                                 policyFlags | additionalPolicyFlags));
 
@@ -4644,7 +4644,7 @@
     const int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
 
     InputEventInjectionResult result =
-            mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+            mDispatcher->injectInputEvent(&event, {} /* targetUid */,
                                           InputEventInjectionSync::WAIT_FOR_RESULT,
                                           INJECT_EVENT_TIMEOUT, policyFlags);
     ASSERT_EQ(InputEventInjectionResult::FAILED, result)
@@ -6110,8 +6110,7 @@
         mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
     }
 
-    // Start performing drag, we will create a drag window and transfer touch to it.
-    void performDrag() {
+    void injectDown() {
         ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
                   injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
                                    {50, 50}))
@@ -6119,6 +6118,15 @@
 
         // Window should receive motion event.
         mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    }
+
+    // Start performing drag, we will create a drag window and transfer touch to it.
+    // @param sendDown : if true, send a motion down on first window before perform drag and drop.
+    // Returns true on success.
+    bool performDrag(bool sendDown = true) {
+        if (sendDown) {
+            injectDown();
+        }
 
         // The drag window covers the entire display
         mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
@@ -6126,10 +6134,14 @@
                 {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
 
         // Transfer touch focus to the drag window
-        mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(),
-                                        true /* isDragDrop */);
-        mWindow->consumeMotionCancel();
-        mDragWindow->consumeMotionDown();
+        bool transferred =
+                mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(),
+                                                true /* isDragDrop */);
+        if (transferred) {
+            mWindow->consumeMotionCancel();
+            mDragWindow->consumeMotionDown();
+        }
+        return transferred;
     }
 
     // Start performing drag, we will create a drag window and transfer touch to it.
@@ -6276,7 +6288,7 @@
     mSecondWindow->assertNoEvents();
 }
 
-TEST_F(InputDispatcherDragTests, DragAndDrop_InvalidWindow) {
+TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) {
     performDrag();
 
     // Set second window invisible.
@@ -6312,6 +6324,89 @@
     mSecondWindow->assertNoEvents();
 }
 
+TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) {
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {50, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    const MotionEvent secondFingerDownEvent =
+            MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .displayId(ADISPLAY_ID_DEFAULT)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+                    .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(75).y(50))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mWindow->consumeMotionPointerDown(1 /* pointerIndex */);
+
+    // Should not perform drag and drop when window has multi fingers.
+    ASSERT_FALSE(performDrag(false));
+}
+
+TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
+    // First down on second window.
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {150, 50}))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+    mSecondWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    // Second down on first window.
+    const MotionEvent secondFingerDownEvent =
+            MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+                    .displayId(ADISPLAY_ID_DEFAULT)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(
+                            PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(150).y(50))
+                    .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT))
+            << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+    mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    // Perform drag and drop from first window.
+    ASSERT_TRUE(performDrag(false));
+
+    // Move on window.
+    const MotionEvent secondFingerMoveEvent =
+            MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(
+                            PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(150).y(50))
+                    .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT));
+    mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+    mWindow->consumeDragEvent(false, 50, 50);
+    mSecondWindow->consumeMotionMove();
+
+    // Release the drag pointer should perform drop.
+    const MotionEvent secondFingerUpEvent =
+            MotionEventBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+                    .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+                    .pointer(
+                            PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(150).y(50))
+                    .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+                    .build();
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
+                                InputEventInjectionSync::WAIT_FOR_RESULT));
+    mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+    mFakePolicy->assertDropTargetEquals(mWindow->getToken());
+    mWindow->assertNoEvents();
+    mSecondWindow->consumeMotionMove();
+}
+
 class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
 
 TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
@@ -6463,8 +6558,8 @@
         mWindow->consumeFocusEvent(true);
 
         // Set initial touch mode to InputDispatcher::kDefaultInTouchMode.
-        if (mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, INJECTOR_PID,
-                                        INJECTOR_UID, /* hasPermission */ true)) {
+        if (mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, WINDOW_PID,
+                                        WINDOW_UID, /* hasPermission */ true)) {
             mWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
             mSecondWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
         }
@@ -7095,4 +7190,149 @@
     window->assertNoEvents();
 }
 
+struct User {
+    int32_t mPid;
+    int32_t mUid;
+    uint32_t mPolicyFlags{DEFAULT_POLICY_FLAGS};
+    std::unique_ptr<InputDispatcher>& mDispatcher;
+
+    User(std::unique_ptr<InputDispatcher>& dispatcher, int32_t pid, int32_t uid)
+          : mPid(pid), mUid(uid), mDispatcher(dispatcher) {}
+
+    InputEventInjectionResult injectTargetedMotion(int32_t action) const {
+        return injectMotionEvent(mDispatcher, action, AINPUT_SOURCE_TOUCHSCREEN,
+                                 ADISPLAY_ID_DEFAULT, {100, 200},
+                                 {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                  AMOTION_EVENT_INVALID_CURSOR_POSITION},
+                                 INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT,
+                                 systemTime(SYSTEM_TIME_MONOTONIC), {mUid}, mPolicyFlags);
+    }
+
+    InputEventInjectionResult injectTargetedKey(int32_t action) const {
+        return inputdispatcher::injectKey(mDispatcher, action, 0 /* repeatCount*/, ADISPLAY_ID_NONE,
+                                          InputEventInjectionSync::WAIT_FOR_RESULT,
+                                          INJECT_EVENT_TIMEOUT, false /*allowKeyRepeat*/, {mUid},
+                                          mPolicyFlags);
+    }
+
+    sp<FakeWindowHandle> createWindow() const {
+        std::shared_ptr<FakeApplicationHandle> overlayApplication =
+                std::make_shared<FakeApplicationHandle>();
+        sp<FakeWindowHandle> window = new FakeWindowHandle(overlayApplication, mDispatcher,
+                                                           "Owned Window", ADISPLAY_ID_DEFAULT);
+        window->setOwnerInfo(mPid, mUid);
+        return window;
+    }
+};
+
+using InputDispatcherTargetedInjectionTest = InputDispatcherTest;
+
+TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoOwnedWindow) {
+    auto owner = User(mDispatcher, 10, 11);
+    auto window = owner.createWindow();
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
+              owner.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
+    window->consumeMotionDown();
+
+    setFocusedWindow(window);
+    window->consumeFocusEvent(true);
+
+    EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
+              owner.injectTargetedKey(AKEY_EVENT_ACTION_DOWN));
+    window->consumeKeyDown(ADISPLAY_ID_NONE);
+}
+
+TEST_F(InputDispatcherTargetedInjectionTest, CannotInjectIntoUnownedWindow) {
+    auto owner = User(mDispatcher, 10, 11);
+    auto window = owner.createWindow();
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    auto rando = User(mDispatcher, 20, 21);
+    EXPECT_EQ(InputEventInjectionResult::TARGET_MISMATCH,
+              rando.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
+
+    setFocusedWindow(window);
+    window->consumeFocusEvent(true);
+
+    EXPECT_EQ(InputEventInjectionResult::TARGET_MISMATCH,
+              rando.injectTargetedKey(AKEY_EVENT_ACTION_DOWN));
+    window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoOwnedSpyWindow) {
+    auto owner = User(mDispatcher, 10, 11);
+    auto window = owner.createWindow();
+    auto spy = owner.createWindow();
+    spy->setSpy(true);
+    spy->setTrustedOverlay(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+
+    EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
+              owner.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
+    spy->consumeMotionDown();
+    window->consumeMotionDown();
+}
+
+TEST_F(InputDispatcherTargetedInjectionTest, CannotInjectIntoUnownedSpyWindow) {
+    auto owner = User(mDispatcher, 10, 11);
+    auto window = owner.createWindow();
+
+    auto rando = User(mDispatcher, 20, 21);
+    auto randosSpy = rando.createWindow();
+    randosSpy->setSpy(true);
+    randosSpy->setTrustedOverlay(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {randosSpy, window}}});
+
+    // The event is targeted at owner's window, so injection should succeed, but the spy should
+    // not receive the event.
+    EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
+              owner.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
+    randosSpy->assertNoEvents();
+    window->consumeMotionDown();
+}
+
+TEST_F(InputDispatcherTargetedInjectionTest, CanInjectIntoAnyWindowWhenNotTargeting) {
+    auto owner = User(mDispatcher, 10, 11);
+    auto window = owner.createWindow();
+
+    auto rando = User(mDispatcher, 20, 21);
+    auto randosSpy = rando.createWindow();
+    randosSpy->setSpy(true);
+    randosSpy->setTrustedOverlay(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {randosSpy, window}}});
+
+    // A user that has injection permission can inject into any window.
+    EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                ADISPLAY_ID_DEFAULT));
+    randosSpy->consumeMotionDown();
+    window->consumeMotionDown();
+
+    setFocusedWindow(randosSpy);
+    randosSpy->consumeFocusEvent(true);
+
+    EXPECT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher));
+    randosSpy->consumeKeyDown(ADISPLAY_ID_NONE);
+    window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTargetedInjectionTest, CanGenerateActionOutsideToOtherUids) {
+    auto owner = User(mDispatcher, 10, 11);
+    auto window = owner.createWindow();
+
+    auto rando = User(mDispatcher, 20, 21);
+    auto randosWindow = rando.createWindow();
+    randosWindow->setFrame(Rect{-10, -10, -5, -5});
+    randosWindow->setWatchOutsideTouch(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {randosWindow, window}}});
+
+    // We allow generation of ACTION_OUTSIDE events into windows owned by different uids.
+    EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
+              owner.injectTargetedMotion(AMOTION_EVENT_ACTION_DOWN));
+    window->consumeMotionDown();
+    randosWindow->consumeMotionOutside();
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/sensorservice/BatteryService.cpp b/services/sensorservice/BatteryService.cpp
index 14f9a12..94de55c 100644
--- a/services/sensorservice/BatteryService.cpp
+++ b/services/sensorservice/BatteryService.cpp
@@ -74,23 +74,6 @@
     }
 }
 
-void BatteryService::cleanupImpl(uid_t uid) {
-    if (checkService()) {
-        Mutex::Autolock _l(mActivationsLock);
-        int64_t identity = IPCThreadState::self()->clearCallingIdentity();
-        for (size_t i=0 ; i<mActivations.size() ; ) {
-            const Info& info(mActivations[i]);
-            if (info.uid == uid) {
-                mBatteryStatService->noteStopSensor(info.uid, info.handle);
-                mActivations.removeAt(i);
-            } else {
-              i++;
-            }
-        }
-        IPCThreadState::self()->restoreCallingIdentity(identity);
-    }
-}
-
 bool BatteryService::checkService() {
     if (mBatteryStatService == nullptr) {
         const sp<IServiceManager> sm(defaultServiceManager());
diff --git a/services/sensorservice/BatteryService.h b/services/sensorservice/BatteryService.h
index 09eb2c1..13fc58a 100644
--- a/services/sensorservice/BatteryService.h
+++ b/services/sensorservice/BatteryService.h
@@ -32,7 +32,6 @@
 
     void enableSensorImpl(uid_t uid, int handle);
     void disableSensorImpl(uid_t uid, int handle);
-    void cleanupImpl(uid_t uid);
 
     struct Info {
         uid_t uid;
@@ -58,9 +57,6 @@
     static void disableSensor(uid_t uid, int handle) {
         BatteryService::getInstance().disableSensorImpl(uid, handle);
     }
-    static void cleanup(uid_t uid) {
-        BatteryService::getInstance().cleanupImpl(uid);
-    }
 };
 
 // ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 53a3025..de050e0 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -612,8 +612,10 @@
     return SENSORS_DEVICE_API_VERSION_1_4;
 }
 
-status_t SensorDevice::flush(void* /*ident*/, int handle) {
+status_t SensorDevice::flush(void* ident, int handle) {
     if (mHalWrapper == nullptr) return NO_INIT;
+    if (isClientDisabled(ident)) return INVALID_OPERATION;
+    ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w flush %d", handle);
     return mHalWrapper->flush(handle);
 }
 
@@ -754,6 +756,13 @@
 
 status_t SensorDevice::injectSensorData(const sensors_event_t* injected_sensor_event) {
     if (mHalWrapper == nullptr) return NO_INIT;
+    ALOGD_IF(DEBUG_CONNECTIONS,
+             "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f",
+             injected_sensor_event->sensor, injected_sensor_event->timestamp,
+             injected_sensor_event->data[0], injected_sensor_event->data[1],
+             injected_sensor_event->data[2], injected_sensor_event->data[3],
+             injected_sensor_event->data[4], injected_sensor_event->data[5]);
+
     return mHalWrapper->injectSensorData(injected_sensor_event);
 }
 
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index fae16f6..2dd12e9 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -157,7 +157,7 @@
     }
 
     const Sensor& s = si->getSensor();
-    if (!SensorService::canAccessSensor(s, "config direct channel", mOpPackageName)) {
+    if (!mService->canAccessSensor(s, "config direct channel", mOpPackageName)) {
         return PERMISSION_DENIED;
     }
 
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 6948895..f06f947 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -162,7 +162,8 @@
     Mutex::Autolock _l(mConnectionLock);
     sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
     if (si == nullptr ||
-        !canAccessSensor(si->getSensor(), "Add to SensorEventConnection: ", mOpPackageName) ||
+        !mService->canAccessSensor(si->getSensor(), "Add to SensorEventConnection: ",
+                                   mOpPackageName) ||
         mSensorInfo.count(handle) > 0) {
         return false;
     }
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 8b81d48..948692b 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -814,6 +814,12 @@
         return handleResetUidState(args, err);
     } else if (args[0] == String16("get-uid-state")) {
         return handleGetUidState(args, out, err);
+    } else if (args[0] == String16("unrestrict-ht")) {
+        mHtRestricted = false;
+        return NO_ERROR;
+    } else if (args[0] == String16("restrict-ht")) {
+        mHtRestricted = true;
+        return NO_ERROR;
     } else if (args.size() == 1 && args[0] == String16("help")) {
         printHelp(out);
         return NO_ERROR;
@@ -1338,11 +1344,11 @@
 Vector<Sensor> SensorService::getDynamicSensorList(const String16& opPackageName) {
     Vector<Sensor> accessibleSensorList;
     mSensors.forEachSensor(
-            [&opPackageName, &accessibleSensorList] (const Sensor& sensor) -> bool {
+            [this, &opPackageName, &accessibleSensorList] (const Sensor& sensor) -> bool {
                 if (sensor.isDynamicSensor()) {
-                    if (canAccessSensor(sensor, "getDynamicSensorList", opPackageName)) {
+                    if (canAccessSensor(sensor, "can't see", opPackageName)) {
                         accessibleSensorList.add(sensor);
-                    } else {
+                    } else if (sensor.getType() != SENSOR_TYPE_HEAD_TRACKER) {
                         ALOGI("Skipped sensor %s because it requires permission %s and app op %" PRId32,
                               sensor.getName().string(),
                               sensor.getRequiredPermission().string(),
@@ -1611,7 +1617,9 @@
             } else {
                 ALOGE("sensor interface of handle=0x%08x is null!", handle);
             }
-            c->removeSensor(handle);
+            if (c->removeSensor(handle)) {
+                BatteryService::disableSensor(c->getUid(), handle);
+            }
         }
         SensorRecord* rec = mActiveSensors.valueAt(i);
         ALOGE_IF(!rec, "mActiveSensors[%zu] is null (handle=0x%08x)!", i, handle);
@@ -1631,7 +1639,6 @@
     }
     c->updateLooperRegistration(mLooper);
     mConnectionHolder.removeEventConnection(connection);
-    BatteryService::cleanup(c->getUid());
     if (c->needsWakeLock()) {
         checkWakeLockStateLocked(&connLock);
     }
@@ -1988,6 +1995,20 @@
 
 bool SensorService::canAccessSensor(const Sensor& sensor, const char* operation,
         const String16& opPackageName) {
+    // Special case for Head Tracker sensor type: currently restricted to system usage only, unless
+    // the restriction is specially lifted for testing
+    if (sensor.getType() == SENSOR_TYPE_HEAD_TRACKER &&
+            !isAudioServerOrSystemServerUid(IPCThreadState::self()->getCallingUid())) {
+        if (!mHtRestricted) {
+            ALOGI("Permitting access to HT sensor type outside system (%s)",
+                  String8(opPackageName).string());
+        } else {
+            ALOGW("%s %s a sensor (%s) as a non-system client", String8(opPackageName).string(),
+                  operation, sensor.getName().string());
+            return false;
+        }
+    }
+
     // Check if a permission is required for this sensor
     if (sensor.getRequiredPermission().length() <= 0) {
         return true;
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index b18d204..234dc9c 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -373,7 +373,7 @@
     status_t cleanupWithoutDisableLocked(const sp<SensorEventConnection>& connection, int handle);
     void cleanupAutoDisabledSensorLocked(const sp<SensorEventConnection>& connection,
             sensors_event_t const* buffer, const int count);
-    static bool canAccessSensor(const Sensor& sensor, const char* operation,
+    bool canAccessSensor(const Sensor& sensor, const char* operation,
             const String16& opPackageName);
     static bool hasPermissionForSensor(const Sensor& sensor);
     static int getTargetSdkVersion(const String16& opPackageName);
@@ -492,6 +492,10 @@
     std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent;
     Mode mCurrentOperatingMode;
 
+    // true if the head tracker sensor type is currently restricted to system usage only
+    // (can only be unrestricted for testing, via shell cmd)
+    bool mHtRestricted = true;
+
     // This packagaName is set when SensorService is in RESTRICTED or DATA_INJECTION mode. Only
     // applications with this packageName are allowed to activate/deactivate or call flush on
     // sensors. To run CTS this is can be set to ".cts." and only CTS tests will get access to
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 635b088..f8c53c3 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -315,11 +315,7 @@
     compositionState->sidebandStreamHasFrame = false;
 }
 
-bool BufferLayer::onPreComposition(nsecs_t refreshStartTime) {
-    if (mBufferInfo.mBuffer != nullptr) {
-        Mutex::Autolock lock(mFrameEventHistoryMutex);
-        mFrameEventHistory.addPreComposition(mCurrentFrameNumber, refreshStartTime);
-    }
+bool BufferLayer::onPreComposition(nsecs_t) {
     return hasReadyFrame();
 }
 namespace {
@@ -365,12 +361,7 @@
     mAlreadyDisplayedThisCompose = false;
 
     // Update mFrameEventHistory.
-    {
-        Mutex::Autolock lock(mFrameEventHistoryMutex);
-        mFrameEventHistory.addPostComposition(mCurrentFrameNumber, glDoneFence, presentFence,
-                                              compositorTiming);
-        finalizeFrameEventHistory(glDoneFence, compositorTiming);
-    }
+    finalizeFrameEventHistory(glDoneFence, compositorTiming);
 
     // Update mFrameTracker.
     nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
@@ -500,7 +491,7 @@
         return false;
     }
 
-    err = updateFrameNumber(latchTime);
+    err = updateFrameNumber();
     if (err != NO_ERROR) {
         return false;
     }
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 3e70493..4c70eb5 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -192,7 +192,7 @@
                                     nsecs_t expectedPresentTime) = 0;
 
     virtual status_t updateActiveBuffer() = 0;
-    virtual status_t updateFrameNumber(nsecs_t latchTime) = 0;
+    virtual status_t updateFrameNumber() = 0;
 
     // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they
     // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 9ae45fc..7361a4f 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -471,18 +471,6 @@
     }
 }
 
-void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
-                                                   FrameEventHistoryDelta* outDelta) {
-    Mutex::Autolock lock(mMutex);
-
-    if (mAbandoned) {
-        // Nothing to do if we're already abandoned.
-        return;
-    }
-
-    mLayer->addAndGetFrameTimestamps(newTimestamps, outDelta);
-}
-
 void BufferLayerConsumer::abandonLocked() {
     BLC_LOGV("abandonLocked");
     mCurrentTextureBuffer = nullptr;
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index 9ed80b4..23ad2a3 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -236,8 +236,7 @@
     // IConsumerListener interface
     void onDisconnect() override;
     void onSidebandStreamChanged() override;
-    void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
-                                  FrameEventHistoryDelta* outDelta) override;
+    void addAndGetFrameTimestamps(const NewFrameEventsEntry*, FrameEventHistoryDelta*) override {}
 
     // computeCurrentTransformMatrixLocked computes the transform matrix for the
     // current texture.  It uses mCurrentTransform and the current GraphicBuffer
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 6a1a38b..f487aab 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -66,20 +66,10 @@
     mConsumer->setTransformHint(mTransformHint);
 }
 
-void BufferQueueLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
+void BufferQueueLayer::releasePendingBuffer(nsecs_t) {
     if (!mConsumer->releasePendingBuffer()) {
         return;
     }
-
-    auto releaseFenceTime = std::make_shared<FenceTime>(mConsumer->getPrevFinalReleaseFence());
-    mReleaseTimeline.updateSignalTimes();
-    mReleaseTimeline.push(releaseFenceTime);
-
-    Mutex::Autolock lock(mFrameEventHistoryMutex);
-    if (mPreviousFrameNumber != 0) {
-        mFrameEventHistory.addRelease(mPreviousFrameNumber, dequeueReadyTime,
-                                      std::move(releaseFenceTime));
-    }
 }
 
 void BufferQueueLayer::setDefaultBufferSize(uint32_t w, uint32_t h) {
@@ -319,14 +309,9 @@
     return NO_ERROR;
 }
 
-status_t BufferQueueLayer::updateFrameNumber(nsecs_t latchTime) {
+status_t BufferQueueLayer::updateFrameNumber() {
     mPreviousFrameNumber = mCurrentFrameNumber;
     mCurrentFrameNumber = mConsumer->getFrameNumber();
-
-    {
-        Mutex::Autolock lock(mFrameEventHistoryMutex);
-        mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime);
-    }
     return NO_ERROR;
 }
 
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 4587b5e..649da2c 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -102,7 +102,7 @@
                             nsecs_t expectedPresentTime) override;
 
     status_t updateActiveBuffer() override;
-    status_t updateFrameNumber(nsecs_t latchTime) override;
+    status_t updateFrameNumber() override;
     void setFrameTimelineInfoForBuffer(const FrameTimelineInfo& frameTimelineInfo) override;
 
     sp<Layer> createClone() override;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index c885110..e58b65e 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -178,15 +178,6 @@
     }
 
     mDrawingState.callbackHandles = {};
-
-    std::shared_ptr<FenceTime> releaseFenceTime = std::make_shared<FenceTime>(releaseFence);
-    {
-        Mutex::Autolock lock(mFrameEventHistoryMutex);
-        if (mPreviousFrameNumber != 0) {
-            mFrameEventHistory.addRelease(mPreviousFrameNumber, dequeueReadyTime,
-                                          std::move(releaseFenceTime));
-        }
-    }
 }
 
 void BufferStateLayer::finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
@@ -346,19 +337,6 @@
     return true;
 }
 
-bool BufferStateLayer::addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime,
-                                     nsecs_t desiredPresentTime) {
-    Mutex::Autolock lock(mFrameEventHistoryMutex);
-    mAcquireTimeline.updateSignalTimes();
-    std::shared_ptr<FenceTime> acquireFenceTime =
-            std::make_shared<FenceTime>((acquireFence ? acquireFence : Fence::NO_FENCE));
-    NewFrameEventsEntry newTimestamps = {mDrawingState.frameNumber, postedTime, desiredPresentTime,
-                                         acquireFenceTime};
-    mFrameEventHistory.setProducerWantsEvents();
-    mFrameEventHistory.addQueue(newTimestamps);
-    return true;
-}
-
 bool BufferStateLayer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
                                  const BufferData& bufferData, nsecs_t postTime,
                                  nsecs_t desiredPresentTime, bool isAutoTimestamp,
@@ -445,8 +423,6 @@
     using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
     mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer);
 
-    addFrameEvent(mDrawingState.acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime);
-
     setFrameTimelineVsyncForBufferTransaction(info, postTime);
 
     if (dequeueTime && *dequeueTime != 0) {
@@ -726,14 +702,10 @@
     return NO_ERROR;
 }
 
-status_t BufferStateLayer::updateFrameNumber(nsecs_t latchTime) {
+status_t BufferStateLayer::updateFrameNumber() {
     // TODO(marissaw): support frame history events
     mPreviousFrameNumber = mCurrentFrameNumber;
     mCurrentFrameNumber = mDrawingState.frameNumber;
-    {
-        Mutex::Autolock lock(mFrameEventHistoryMutex);
-        mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime);
-    }
     return NO_ERROR;
 }
 
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index cc510d8..bbf058c 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -66,8 +66,6 @@
     bool setApi(int32_t api) override;
     bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override;
     bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
-    bool addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime,
-                       nsecs_t requestedPresentTime) override;
     bool setPosition(float /*x*/, float /*y*/) override;
     bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/);
 
@@ -124,7 +122,7 @@
                             nsecs_t expectedPresentTime) override;
 
     status_t updateActiveBuffer() override;
-    status_t updateFrameNumber(nsecs_t latchTime) override;
+    status_t updateFrameNumber() override;
 
     sp<Layer> createClone() override;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index c65d467..7709b96 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -164,6 +164,9 @@
 
     bool treat170mAsSrgb = false;
 
+    uint64_t lastOutputLayerHash = 0;
+    uint64_t outputLayerHash = 0;
+
     // Debugging
     void dump(std::string& result) const;
 };
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 742332c..2d2b3df 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -763,6 +763,7 @@
     bool includeGeometry = refreshArgs.updatingGeometryThisFrame;
     uint32_t z = 0;
     bool overrideZ = false;
+    uint64_t outputLayerHash = 0;
     for (auto* layer : getOutputLayersOrderedByZ()) {
         if (layer == peekThroughLayer) {
             // No longer needed, although it should not show up again, so
@@ -789,6 +790,10 @@
                     constexpr bool isPeekingThrough = true;
                     peekThroughLayer->writeStateToHWC(includeGeometry, false, z++, overrideZ,
                                                       isPeekingThrough);
+                    outputLayerHash ^= android::hashCombine(
+                            reinterpret_cast<uint64_t>(&peekThroughLayer->getLayerFE()),
+                            z, includeGeometry, overrideZ, isPeekingThrough,
+                            peekThroughLayer->requiresClientComposition());
                 }
 
                 previousOverride = overrideInfo.buffer->getBuffer();
@@ -797,7 +802,14 @@
 
         constexpr bool isPeekingThrough = false;
         layer->writeStateToHWC(includeGeometry, skipLayer, z++, overrideZ, isPeekingThrough);
+        if (!skipLayer) {
+            outputLayerHash ^= android::hashCombine(
+                    reinterpret_cast<uint64_t>(&layer->getLayerFE()),
+                    z, includeGeometry, overrideZ, isPeekingThrough,
+                    layer->requiresClientComposition());
+        }
     }
+    editState().outputLayerHash = outputLayerHash;
 }
 
 compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition() const {
@@ -1486,6 +1498,10 @@
 }
 
 bool Output::canPredictCompositionStrategy(const CompositionRefreshArgs& refreshArgs) {
+    uint64_t lastOutputLayerHash = getState().lastOutputLayerHash;
+    uint64_t outputLayerHash = getState().outputLayerHash;
+    editState().lastOutputLayerHash = outputLayerHash;
+
     if (!getState().isEnabled || !mHwComposerAsyncWorker) {
         ALOGV("canPredictCompositionStrategy disabled");
         return false;
@@ -1506,6 +1522,11 @@
         return false;
     }
 
+    if (lastOutputLayerHash != outputLayerHash) {
+        ALOGV("canPredictCompositionStrategy output layers changed");
+        return false;
+    }
+
     // If no layer uses clientComposition, then don't predict composition strategy
     // because we have less work to do in parallel.
     if (!anyLayersRequireClientComposition()) {
@@ -1513,12 +1534,7 @@
         return false;
     }
 
-    if (!refreshArgs.updatingOutputGeometryThisFrame) {
-        return true;
-    }
-
-    ALOGV("canPredictCompositionStrategy updatingOutputGeometryThisFrame");
-    return false;
+    return true;
 }
 
 bool Output::anyLayersRequireClientComposition() const {
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 784abea..b907129 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -850,14 +850,20 @@
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
     EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
     EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
     EXPECT_CALL(*layer3.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
 
     injectOutputLayer(layer1);
     injectOutputLayer(layer2);
@@ -884,14 +890,20 @@
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
     EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
     EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer3.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
 
     injectOutputLayer(layer1);
     injectOutputLayer(layer2);
@@ -917,14 +929,20 @@
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
     EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
     EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer3.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
 
     injectOutputLayer(layer1);
     injectOutputLayer(layer2);
@@ -950,6 +968,8 @@
     InSequence seq;
     EXPECT_CALL(*layer0.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+        EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
+                .WillRepeatedly(Return(false));
     EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
 
@@ -957,6 +977,9 @@
     EXPECT_CALL(*layer0.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer0.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
+
 
     // After calling planComposition (which clears overrideInfo), this test sets
     // layer3 to be the peekThroughLayer for layer1 and layer2. As a result, it
@@ -966,12 +989,18 @@
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ true, /*isPeekingThrough*/
                                 true));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ true, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, z++,
                                 /*zIsOverridden*/ true, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
 
     injectOutputLayer(layer0);
     injectOutputLayer(layer1);
@@ -3354,7 +3383,6 @@
     static constexpr float kDefaultMaxLuminance = 0.9f;
     static constexpr float kDefaultAvgLuminance = 0.7f;
     static constexpr float kDefaultMinLuminance = 0.1f;
-    static constexpr float kUnknownLuminance = -1.f;
     static constexpr float kDisplayLuminance = 400.f;
     static constexpr float kClientTargetLuminanceNits = 200.f;
     static constexpr float kClientTargetBrightness = 0.5f;
@@ -3752,7 +3780,7 @@
 TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrMixedComposition) {
     verify().ifMixedCompositionIs(true)
             .andIfUsesHdr(true)
-            .withDisplayBrightnessNits(kUnknownLuminance)
+            .withDisplayBrightnessNits(kDisplayLuminance)
             .withDimmingStage(aidl::android::hardware::graphics::composer3::DimmingStage::LINEAR)
             .withRenderIntent(
                     aidl::android::hardware::graphics::composer3::RenderIntent::COLORIMETRIC)
@@ -3761,7 +3789,7 @@
                     {.physicalDisplay = kDefaultOutputDestinationClip,
                      .clip = kDefaultOutputViewport,
                      .maxLuminance = kDefaultMaxLuminance,
-                     .currentLuminanceNits = kDefaultMaxLuminance,
+                     .currentLuminanceNits = kDisplayLuminance,
                      .outputDataspace = kDefaultOutputDataspace,
                      .colorTransform = kDefaultColorTransformMat,
                      .deviceHandlesColorTransform = true,
@@ -3806,7 +3834,7 @@
        forHdrMixedCompositionWithDimmingStage) {
     verify().ifMixedCompositionIs(true)
             .andIfUsesHdr(true)
-            .withDisplayBrightnessNits(kUnknownLuminance)
+            .withDisplayBrightnessNits(kDisplayLuminance)
             .withDimmingStage(
                     aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF)
             .withRenderIntent(
@@ -3816,7 +3844,7 @@
                     {.physicalDisplay = kDefaultOutputDestinationClip,
                      .clip = kDefaultOutputViewport,
                      .maxLuminance = kDefaultMaxLuminance,
-                     .currentLuminanceNits = kDefaultMaxLuminance,
+                     .currentLuminanceNits = kDisplayLuminance,
                      .outputDataspace = kDefaultOutputDataspace,
                      .colorTransform = kDefaultColorTransformMat,
                      .deviceHandlesColorTransform = true,
@@ -3834,7 +3862,7 @@
        forHdrMixedCompositionWithRenderIntent) {
     verify().ifMixedCompositionIs(true)
             .andIfUsesHdr(true)
-            .withDisplayBrightnessNits(kUnknownLuminance)
+            .withDisplayBrightnessNits(kDisplayLuminance)
             .withDimmingStage(aidl::android::hardware::graphics::composer3::DimmingStage::LINEAR)
             .withRenderIntent(aidl::android::hardware::graphics::composer3::RenderIntent::ENHANCE)
             .andIfSkipColorTransform(false)
@@ -3842,7 +3870,7 @@
                     {.physicalDisplay = kDefaultOutputDestinationClip,
                      .clip = kDefaultOutputViewport,
                      .maxLuminance = kDefaultMaxLuminance,
-                     .currentLuminanceNits = kDefaultMaxLuminance,
+                     .currentLuminanceNits = kDisplayLuminance,
                      .outputDataspace = kDefaultOutputDataspace,
                      .colorTransform = kDefaultColorTransformMat,
                      .deviceHandlesColorTransform = true,
@@ -3859,7 +3887,7 @@
 TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrMixedComposition) {
     verify().ifMixedCompositionIs(true)
             .andIfUsesHdr(false)
-            .withDisplayBrightnessNits(kUnknownLuminance)
+            .withDisplayBrightnessNits(kDisplayLuminance)
             .withDimmingStage(aidl::android::hardware::graphics::composer3::DimmingStage::LINEAR)
             .withRenderIntent(
                     aidl::android::hardware::graphics::composer3::RenderIntent::COLORIMETRIC)
@@ -3868,7 +3896,7 @@
                     {.physicalDisplay = kDefaultOutputDestinationClip,
                      .clip = kDefaultOutputViewport,
                      .maxLuminance = kDefaultMaxLuminance,
-                     .currentLuminanceNits = kDefaultMaxLuminance,
+                     .currentLuminanceNits = kDisplayLuminance,
                      .outputDataspace = kDefaultOutputDataspace,
                      .colorTransform = kDefaultColorTransformMat,
                      .deviceHandlesColorTransform = true,
@@ -3885,7 +3913,7 @@
 TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrOnlyClientComposition) {
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(true)
-            .withDisplayBrightnessNits(kUnknownLuminance)
+            .withDisplayBrightnessNits(kDisplayLuminance)
             .withDimmingStage(aidl::android::hardware::graphics::composer3::DimmingStage::LINEAR)
             .withRenderIntent(
                     aidl::android::hardware::graphics::composer3::RenderIntent::COLORIMETRIC)
@@ -3894,7 +3922,7 @@
                     {.physicalDisplay = kDefaultOutputDestinationClip,
                      .clip = kDefaultOutputViewport,
                      .maxLuminance = kDefaultMaxLuminance,
-                     .currentLuminanceNits = kDefaultMaxLuminance,
+                     .currentLuminanceNits = kDisplayLuminance,
                      .outputDataspace = kDefaultOutputDataspace,
                      .colorTransform = kDefaultColorTransformMat,
                      .deviceHandlesColorTransform = false,
@@ -3911,7 +3939,7 @@
 TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrOnlyClientComposition) {
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(false)
-            .withDisplayBrightnessNits(kUnknownLuminance)
+            .withDisplayBrightnessNits(kDisplayLuminance)
             .withDimmingStage(aidl::android::hardware::graphics::composer3::DimmingStage::LINEAR)
             .withRenderIntent(
                     aidl::android::hardware::graphics::composer3::RenderIntent::COLORIMETRIC)
@@ -3920,7 +3948,7 @@
                     {.physicalDisplay = kDefaultOutputDestinationClip,
                      .clip = kDefaultOutputViewport,
                      .maxLuminance = kDefaultMaxLuminance,
-                     .currentLuminanceNits = kDefaultMaxLuminance,
+                     .currentLuminanceNits = kDisplayLuminance,
                      .outputDataspace = kDefaultOutputDataspace,
                      .colorTransform = kDefaultColorTransformMat,
                      .deviceHandlesColorTransform = false,
@@ -3938,7 +3966,7 @@
        usesExpectedDisplaySettingsForHdrOnlyClientCompositionWithSkipClientTransform) {
     verify().ifMixedCompositionIs(false)
             .andIfUsesHdr(true)
-            .withDisplayBrightnessNits(kUnknownLuminance)
+            .withDisplayBrightnessNits(kDisplayLuminance)
             .withDimmingStage(aidl::android::hardware::graphics::composer3::DimmingStage::LINEAR)
             .withRenderIntent(
                     aidl::android::hardware::graphics::composer3::RenderIntent::COLORIMETRIC)
@@ -3947,7 +3975,7 @@
                     {.physicalDisplay = kDefaultOutputDestinationClip,
                      .clip = kDefaultOutputViewport,
                      .maxLuminance = kDefaultMaxLuminance,
-                     .currentLuminanceNits = kDefaultMaxLuminance,
+                     .currentLuminanceNits = kDisplayLuminance,
                      .outputDataspace = kDefaultOutputDataspace,
                      .colorTransform = kDefaultColorTransformMat,
                      .deviceHandlesColorTransform = true,
@@ -4785,10 +4813,14 @@
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
     EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
 
     layer2.layerFEState.backgroundBlurRadius = 10;
     layer2.layerFEState.isOpaque = true;
@@ -4817,14 +4849,20 @@
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
     EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
     EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer3.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
 
     layer2.layerFEState.backgroundBlurRadius = 10;
     layer2.layerFEState.isOpaque = false;
@@ -4854,14 +4892,20 @@
     EXPECT_CALL(*layer1.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer1.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
     EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer2.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer2.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
     EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer3.outputLayer,
                 writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer3.outputLayer, requiresClientComposition())
+            .WillRepeatedly(Return(false));
 
     BlurRegion region;
     layer2.layerFEState.blurRegions.push_back(region);
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 659efd8..bfdf667 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -80,22 +80,33 @@
 
 } // namespace
 
-PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger)
-      : mFlinger(flinger),
-        mUseScreenUpdateTimer(getUpdateTimeout() > 0),
-        mScreenUpdateTimer(
-                "UpdateImminentTimer", OneShotTimer::Interval(getUpdateTimeout()),
-                /* resetCallback */ [this] { mSendUpdateImminent.store(false); },
-                /* timeoutCallback */
-                [this] {
-                    mSendUpdateImminent.store(true);
-                    mFlinger.disableExpensiveRendering();
-                }) {}
+PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger) : mFlinger(flinger) {
+    if (getUpdateTimeout()) {
+        mScreenUpdateTimer.emplace("UpdateImminentTimer",
+                                   OneShotTimer::Interval(getUpdateTimeout()),
+                                   /* resetCallback */ nullptr,
+                                   /* timeoutCallback */
+                                   [this] {
+                                       const nsecs_t timeSinceLastUpdate =
+                                               systemTime() - mLastScreenUpdatedTime.load();
+                                       if (timeSinceLastUpdate < getUpdateTimeout()) {
+                                           // We may try to disable expensive rendering and allow
+                                           // for sending DISPLAY_UPDATE_IMMINENT hints too early if
+                                           // we idled very shortly after updating the screen, so
+                                           // make sure we wait enough time.
+                                           std::this_thread::sleep_for(std::chrono::nanoseconds(
+                                                   getUpdateTimeout() - timeSinceLastUpdate));
+                                       }
+                                       mSendUpdateImminent.store(true);
+                                       mFlinger.disableExpensiveRendering();
+                                   });
+    }
+}
 
 void PowerAdvisor::init() {
     // Defer starting the screen update timer until SurfaceFlinger finishes construction.
-    if (mUseScreenUpdateTimer) {
-        mScreenUpdateTimer.start();
+    if (mScreenUpdateTimer) {
+        mScreenUpdateTimer->start();
     }
 }
 
@@ -135,7 +146,7 @@
         return;
     }
 
-    if (mSendUpdateImminent.load()) {
+    if (mSendUpdateImminent.exchange(false)) {
         std::lock_guard lock(mPowerHalMutex);
         HalWrapper* const halWrapper = getPowerHal();
         if (halWrapper == nullptr) {
@@ -147,10 +158,18 @@
             mReconnectPowerHal = true;
             return;
         }
+
+        if (mScreenUpdateTimer) {
+            mScreenUpdateTimer->reset();
+        } else {
+            // If we don't have a screen update timer, then we don't throttle power hal calls so
+            // flip this bit back to allow for calling into power hal again.
+            mSendUpdateImminent.store(true);
+        }
     }
 
-    if (mUseScreenUpdateTimer) {
-        mScreenUpdateTimer.reset();
+    if (mScreenUpdateTimer) {
+        mLastScreenUpdatedTime.store(systemTime());
     }
 }
 
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 61bb32b..7c10e19 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -114,9 +114,9 @@
     bool mNotifiedExpensiveRendering = false;
 
     SurfaceFlinger& mFlinger;
-    const bool mUseScreenUpdateTimer;
     std::atomic_bool mSendUpdateImminent = true;
-    scheduler::OneShotTimer mScreenUpdateTimer;
+    std::atomic<nsecs_t> mLastScreenUpdatedTime = 0;
+    std::optional<scheduler::OneShotTimer> mScreenUpdateTimer;
 };
 
 class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 2298f03..39686e0 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -151,10 +151,8 @@
         mDrawingState.color.g = -1.0_hf;
         mDrawingState.color.b = -1.0_hf;
     }
-
     CompositorTiming compositorTiming;
     args.flinger->getCompositorTiming(&compositorTiming);
-    mFrameEventHistory.initializeCompositorTiming(compositorTiming);
     mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval);
 
     mCallingPid = args.callingPid;
@@ -1531,53 +1529,17 @@
     mFrameTracker.getStats(outStats);
 }
 
-void Layer::dumpFrameEvents(std::string& result) {
-    StringAppendF(&result, "- Layer %s (%s, %p)\n", getName().c_str(), getType(), this);
-    Mutex::Autolock lock(mFrameEventHistoryMutex);
-    mFrameEventHistory.checkFencesForCompletion();
-    mFrameEventHistory.dump(result);
-}
-
 void Layer::dumpCallingUidPid(std::string& result) const {
     StringAppendF(&result, "Layer %s (%s) callingPid:%d callingUid:%d ownerUid:%d\n",
                   getName().c_str(), getType(), mCallingPid, mCallingUid, mOwnerUid);
 }
 
 void Layer::onDisconnect() {
-    Mutex::Autolock lock(mFrameEventHistoryMutex);
-    mFrameEventHistory.onDisconnect();
     const int32_t layerId = getSequence();
     mFlinger->mTimeStats->onDestroy(layerId);
     mFlinger->mFrameTracer->onDestroy(layerId);
 }
 
-void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
-                                     FrameEventHistoryDelta* outDelta) {
-    if (newTimestamps) {
-        mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
-                                          getName().c_str(), mOwnerUid, newTimestamps->postedTime,
-                                          getGameMode());
-        mFlinger->mTimeStats->setAcquireFence(getSequence(), newTimestamps->frameNumber,
-                                              newTimestamps->acquireFence);
-    }
-
-    Mutex::Autolock lock(mFrameEventHistoryMutex);
-    if (newTimestamps) {
-        // If there are any unsignaled fences in the aquire timeline at this
-        // point, the previously queued frame hasn't been latched yet. Go ahead
-        // and try to get the signal time here so the syscall is taken out of
-        // the main thread's critical path.
-        mAcquireTimeline.updateSignalTimes();
-        // Push the new fence after updating since it's likely still pending.
-        mAcquireTimeline.push(newTimestamps->acquireFence);
-        mFrameEventHistory.addQueue(*newTimestamps);
-    }
-
-    if (outDelta) {
-        mFrameEventHistory.getAndResetDelta(outDelta);
-    }
-}
-
 size_t Layer::getChildrenCount() const {
     size_t count = 0;
     for (const sp<Layer>& child : mCurrentChildren) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 1007043..ca46abc 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -432,10 +432,6 @@
     virtual bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/) { return false; };
     virtual bool setTransactionCompletedListeners(
             const std::vector<sp<CallbackHandle>>& /*handles*/);
-    virtual bool addFrameEvent(const sp<Fence>& /*acquireFence*/, nsecs_t /*postedTime*/,
-                               nsecs_t /*requestedPresentTime*/) {
-        return false;
-    }
     virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
     virtual bool setColorSpaceAgnostic(const bool agnostic);
     virtual bool setDimmingEnabled(const bool dimmingEnabled);
@@ -742,14 +738,11 @@
 
     void miniDump(std::string& result, const DisplayDevice&) const;
     void dumpFrameStats(std::string& result) const;
-    void dumpFrameEvents(std::string& result);
     void dumpCallingUidPid(std::string& result) const;
     void clearFrameStats();
     void logFrameStats();
     void getFrameStats(FrameStats* outStats) const;
     void onDisconnect();
-    void addAndGetFrameTimestamps(const NewFrameEventsEntry* newEntry,
-                                  FrameEventHistoryDelta* outDelta);
 
     ui::Transform getTransform() const;
     bool isTransformValid() const;
@@ -994,13 +987,6 @@
     // Timestamp history for UIAutomation. Thread safe.
     FrameTracker mFrameTracker;
 
-    // Timestamp history for the consumer to query.
-    // Accessed by both consumer and producer on main and binder threads.
-    Mutex mFrameEventHistoryMutex;
-    ConsumerFrameEventHistory mFrameEventHistory;
-    FenceTimeline mAcquireTimeline;
-    FenceTimeline mReleaseTimeline;
-
     // main thread
     sp<NativeHandle> mSidebandStream;
     // False if the buffer and its contents have been previously used for GPU
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 3aa0a5f..37f0fec 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -167,18 +167,19 @@
 impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const {
     return [this](uid_t uid) {
         const Fps refreshRate = holdRefreshRateConfigs()->getActiveMode()->getFps();
-        const nsecs_t basePeriod = refreshRate.getPeriodNsecs();
+        const auto currentPeriod =
+                mVsyncSchedule->getTracker().currentPeriod() ?: refreshRate.getPeriodNsecs();
 
         const auto frameRate = getFrameRateOverride(uid);
         if (!frameRate.has_value()) {
-            return basePeriod;
+            return currentPeriod;
         }
 
         const auto divisor = RefreshRateConfigs::getFrameRateDivisor(refreshRate, *frameRate);
         if (divisor <= 1) {
-            return basePeriod;
+            return currentPeriod;
         }
-        return basePeriod * divisor;
+        return currentPeriod * divisor;
     };
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d8a2696..787ccd7 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2415,27 +2415,22 @@
 
     const auto* display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
 
-    getBE().mGlCompositionDoneTimeline.updateSignalTimes();
     std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
     if (display && display->getCompositionDisplay()->getState().usesClientComposition) {
         glCompositionDoneFenceTime =
                 std::make_shared<FenceTime>(display->getCompositionDisplay()
                                                     ->getRenderSurface()
                                                     ->getClientTargetAcquireFence());
-        getBE().mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
     } else {
         glCompositionDoneFenceTime = FenceTime::NO_FENCE;
     }
 
-    getBE().mDisplayTimeline.updateSignalTimes();
     mPreviousPresentFences[1] = mPreviousPresentFences[0];
     mPreviousPresentFences[0].fence =
             display ? getHwComposer().getPresentFence(display->getPhysicalId()) : Fence::NO_FENCE;
     mPreviousPresentFences[0].fenceTime =
             std::make_shared<FenceTime>(mPreviousPresentFences[0].fence);
 
-    getBE().mDisplayTimeline.push(mPreviousPresentFences[0].fenceTime);
-
     nsecs_t now = systemTime();
 
     // Set presentation information before calling Layer::releasePendingBuffer, such that jank
@@ -3721,12 +3716,13 @@
 
             auto& transaction = transactionQueue.front();
             const auto ready =
-                    transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
-                                                  transaction.isAutoTimestamp,
-                                                  transaction.desiredPresentTime,
-                                                  transaction.originUid, transaction.states,
-                                                  bufferLayersReadyToPresent, transactions.size(),
-                                                  tryApplyUnsignaled);
+                transactionIsReadyToBeApplied(transaction,
+                                              transaction.frameTimelineInfo,
+                                              transaction.isAutoTimestamp,
+                                              transaction.desiredPresentTime,
+                                              transaction.originUid, transaction.states,
+                                              bufferLayersReadyToPresent, transactions.size(),
+                                              tryApplyUnsignaled);
             ATRACE_INT("TransactionReadiness", static_cast<int>(ready));
             if (ready == TransactionReadiness::NotReady) {
                 setTransactionFlags(eTransactionFlushNeeded);
@@ -3803,7 +3799,7 @@
                         return TransactionReadiness::NotReady;
                     }
 
-                    return transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
+                    return transactionIsReadyToBeApplied(transaction, transaction.frameTimelineInfo,
                                                          transaction.isAutoTimestamp,
                                                          transaction.desiredPresentTime,
                                                          transaction.originUid, transaction.states,
@@ -3965,7 +3961,7 @@
     return true;
 }
 
-auto SurfaceFlinger::transactionIsReadyToBeApplied(
+auto SurfaceFlinger::transactionIsReadyToBeApplied(TransactionState& transaction,
         const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
         uid_t originUid, const Vector<ComposerState>& states,
         const std::unordered_map<
@@ -3994,8 +3990,10 @@
     }
 
     bool fenceUnsignaled = false;
+    auto queueProcessTime = systemTime();
     for (const ComposerState& state : states) {
         const layer_state_t& s = state.state;
+
         sp<Layer> layer = nullptr;
         if (s.surface) {
             layer = fromHandle(s.surface).promote();
@@ -4031,6 +4029,15 @@
                  s.bufferData->acquireFence->getStatus() == Fence::Status::Unsignaled);
 
         if (fenceUnsignaled && !allowLatchUnsignaled) {
+            if (!transaction.sentFenceTimeoutWarning &&
+                queueProcessTime - transaction.queueTime > std::chrono::nanoseconds(4s).count()) {
+                transaction.sentFenceTimeoutWarning = true;
+                auto listener = s.bufferData->releaseBufferListener;
+                if (listener) {
+                    listener->onTransactionQueueStalled();
+                }
+            }
+
             ATRACE_NAME("fence unsignaled");
             return TransactionReadiness::NotReady;
         }
@@ -4050,6 +4057,8 @@
 }
 
 void SurfaceFlinger::queueTransaction(TransactionState& state) {
+    state.queueTime = systemTime();
+
     Mutex::Autolock lock(mQueueLock);
 
     // Generate a CountDownLatch pending state if this is a synchronous transaction.
@@ -4960,7 +4969,6 @@
                 {"--displays"s, dumper(&SurfaceFlinger::dumpDisplays)},
                 {"--dispsync"s, dumper([this](std::string& s) { mScheduler->dumpVsync(s); })},
                 {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
-                {"--frame-events"s, dumper(&SurfaceFlinger::dumpFrameEventsLocked)},
                 {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
                 {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
                 {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
@@ -5127,13 +5135,6 @@
                   bucketTimeSec, percent);
 }
 
-void SurfaceFlinger::dumpFrameEventsLocked(std::string& result) {
-    result.append("Layer frame timestamps:\n");
-    // Traverse all layers to dump frame-events for each layer
-    mCurrentState.traverseInZOrder(
-        [&] (Layer* layer) { layer->dumpFrameEvents(result); });
-}
-
 void SurfaceFlinger::dumpCompositionDisplays(std::string& result) const {
     for (const auto& [token, display] : mDisplays) {
         display->getCompositionDisplay()->dump(result);
@@ -6785,7 +6786,7 @@
                                        BlurSetting::Disabled
                              : compositionengine::LayerFE::ClientCompositionTargetSettings::
                                        BlurSetting::Enabled,
-                isHdrDataspace(dataspace) ? displayBrightnessNits : sdrWhitePointNits,
+                isHdrLayer(layer) ? displayBrightnessNits : sdrWhitePointNits,
 
         };
         std::vector<compositionengine::LayerFE::LayerSettings> results =
@@ -6800,7 +6801,7 @@
                 if (regionSampling) {
                     settings.backgroundBlurRadius = 0;
                 }
-                captureResults.capturedHdrLayers |= isHdrDataspace(settings.sourceDataspace);
+                captureResults.capturedHdrLayers |= isHdrLayer(layer);
             }
 
             clientCompositionLayers.insert(clientCompositionLayers.end(),
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index dc54ac2..aeaa01e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -167,9 +167,6 @@
 using DisplayColorSetting = compositionengine::OutputColorSetting;
 
 struct SurfaceFlingerBE {
-    FenceTimeline mGlCompositionDoneTimeline;
-    FenceTimeline mDisplayTimeline;
-
     // protected by mCompositorTimingLock;
     mutable std::mutex mCompositorTimingLock;
     CompositorTiming mCompositorTiming;
@@ -810,7 +807,7 @@
         Ready,
         ReadyUnsignaled,
     };
-    TransactionReadiness transactionIsReadyToBeApplied(
+    TransactionReadiness transactionIsReadyToBeApplied(TransactionState& state,
             const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
             uid_t originUid, const Vector<ComposerState>& states,
             const std::unordered_map<
@@ -1100,8 +1097,6 @@
 
     void dumpVSync(std::string& result) const REQUIRES(mStateLock);
     void dumpStaticScreenStats(std::string& result) const;
-    // Not const because each Layer needs to query Fences and cache timestamps.
-    void dumpFrameEventsLocked(std::string& result);
 
     void dumpCompositionDisplays(std::string& result) const REQUIRES(mStateLock);
     void dumpDisplays(std::string& result) const REQUIRES(mStateLock);
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index bab5326..900d566 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -98,6 +98,8 @@
     int originUid;
     uint64_t id;
     std::shared_ptr<CountDownLatch> transactionCommittedSignal;
+    int64_t queueTime = 0;
+    bool sentFenceTimeoutWarning = false;
 };
 
 class CountDownLatch {
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
index a80aca2..867a198 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -456,9 +456,6 @@
         mFlinger->dumpStaticScreenStats(result);
 
         result = fdp->ConsumeRandomLengthString().c_str();
-        mFlinger->dumpFrameEventsLocked(result);
-
-        result = fdp->ConsumeRandomLengthString().c_str();
         mFlinger->dumpRawDisplayIdentificationData(dumpArgs, result);
 
         LayersProto layersProto = mFlinger->dumpDrawingStateProto(fdp->ConsumeIntegral<uint32_t>());
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
index bd8081f..34cf906 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp
@@ -147,8 +147,6 @@
     const bool ownsHandle = mFdp.ConsumeBool();
     sp<NativeHandle> nativeHandle = sp<NativeHandle>::make(testHandle, ownsHandle);
     layer->setSidebandStream(nativeHandle);
-    layer->addFrameEvent(fence, mFdp.ConsumeIntegral<int64_t>() /*postedTime*/,
-                         mFdp.ConsumeIntegral<int64_t>() /*requestedTime*/);
     layer->computeSourceBounds(getFuzzedFloatRect(&mFdp));
 
     layer->fenceHasSignaled();