Merge "Avoid unnecessary WindowInfo copies" into main
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index d24edc4..b012243 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1841,6 +1841,11 @@
         RunCommand("DUMP VENDOR RIL LOGS", {"vril-dump"}, options.Build());
     }
 
+    /* Dump USB information */
+    RunCommand("typec_connector_class", {"typec_connector_class"},
+               CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
+    RunCommand("lsusb", {"lsusb"}, CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
+
     printf("========================================================\n");
     printf("== Android Framework Services\n");
     printf("========================================================\n");
diff --git a/libs/battery/LongArrayMultiStateCounter.cpp b/libs/battery/LongArrayMultiStateCounter.cpp
index 125cfaf..35c40ab 100644
--- a/libs/battery/LongArrayMultiStateCounter.cpp
+++ b/libs/battery/LongArrayMultiStateCounter.cpp
@@ -21,59 +21,135 @@
 namespace android {
 namespace battery {
 
+Uint64ArrayRW::Uint64ArrayRW(const Uint64Array &copy) : Uint64Array(copy.size()) {
+    if (mSize != 0 && copy.data() != nullptr) {
+        mData = new uint64_t[mSize];
+        memcpy(mData, copy.data(), mSize * sizeof(uint64_t));
+    } else {
+        mData = nullptr;
+    }
+}
+
+uint64_t *Uint64ArrayRW::dataRW() {
+    if (mData == nullptr) {
+        mData = new uint64_t[mSize];
+        memset(mData, 0, mSize * sizeof(uint64_t));
+    }
+    return mData;
+}
+
+Uint64ArrayRW &Uint64ArrayRW::operator=(const Uint64Array &t) {
+    if (t.size() != mSize) {
+        delete[] mData;
+        mSize = t.size();
+        mData = nullptr;
+    }
+    if (mSize != 0) {
+        if (t.data() != nullptr) {
+            mData = new uint64_t[mSize];
+            memcpy(mData, t.data(), mSize * sizeof(uint64_t));
+        } else {
+            mData = nullptr;
+        }
+    }
+    return *this;
+}
+
+std::ostream &operator<<(std::ostream &os, const Uint64Array &v) {
+    os << "{";
+    const uint64_t *data = v.data();
+    if (data != nullptr) {
+        bool first = true;
+        for (size_t i = 0; i < v.size(); i++) {
+            if (!first) {
+                os << ", ";
+            }
+            os << data[i];
+            first = false;
+        }
+    }
+    os << "}";
+    return os;
+}
+
+// Convenience constructor for tests
+Uint64ArrayRW::Uint64ArrayRW(std::initializer_list<uint64_t> init) : Uint64Array(init.size()) {
+    mData = new uint64_t[mSize];
+    memcpy(mData, init.begin(), mSize * sizeof(uint64_t));
+}
+
+// Used in tests only.
+bool Uint64Array::operator==(const Uint64Array &other) const {
+    if (size() != other.size()) {
+        return false;
+    }
+    const uint64_t* thisData = data();
+    const uint64_t* thatData = other.data();
+    for (size_t i = 0; i < mSize; i++) {
+        const uint64_t v1 = thisData != nullptr ? thisData[i] : 0;
+        const uint64_t v2 = thatData != nullptr ? thatData[i] : 0;
+        if (v1 != v2) {
+            return false;
+        }
+    }
+    return true;
+}
+
 template <>
-bool LongArrayMultiStateCounter::delta(const std::vector<uint64_t>& previousValue,
-                                       const std::vector<uint64_t>& newValue,
-                                       std::vector<uint64_t>* outValue) const {
+void LongArrayMultiStateCounter::add(Uint64ArrayRW *value1, const Uint64Array &value2,
+                                     const uint64_t numerator, const uint64_t denominator) const {
+    const uint64_t* data2 = value2.data();
+    if (data2 == nullptr) {
+        return;
+    }
+
+    uint64_t* data1 = value1->dataRW();
+    size_t size = value2.size();
+    if (numerator != denominator) {
+        for (size_t i = 0; i < size; i++) {
+            // The caller ensures that denominator != 0
+            data1[i] += data2[i] * numerator / denominator;
+        }
+    } else {
+        for (size_t i = 0; i < size; i++) {
+            data1[i] += data2[i];
+        }
+    }
+}
+
+template<>
+bool LongArrayMultiStateCounter::delta(const Uint64ArrayRW &previousValue,
+                                       const Uint64Array &newValue, Uint64ArrayRW *outValue) const {
     size_t size = previousValue.size();
     if (newValue.size() != size) {
-        ALOGE("Incorrect array size: %d, should be %d", (int)newValue.size(), (int)size);
+        ALOGE("Incorrect array size: %d, should be %d", (int) newValue.size(), (int) size);
+        return false;
+    }
+    if (outValue->size() != size) {
+        ALOGE("Incorrect outValue size: %d, should be %d", (int) outValue->size(), (int) size);
         return false;
     }
 
     bool is_delta_valid = true;
-    for (int i = size - 1; i >= 0; i--) {
-        if (newValue[i] >= previousValue[i]) {
-            (*outValue)[i] = newValue[i] - previousValue[i];
-        } else {
-            (*outValue)[i] = 0;
+    const uint64_t *prevData = previousValue.data();
+    const uint64_t *newData = newValue.data();
+    uint64_t *outData = outValue->dataRW();
+    for (size_t i = 0; i < size; i++) {
+        if (prevData == nullptr) {
+            if (newData == nullptr) {
+                outData[i] = 0;
+            } else {
+                outData[i] = newData[i];
+            }
+        } else if (newData == nullptr || newData[i] < prevData[i]) {
+            outData[i] = 0;
             is_delta_valid = false;
+        } else {
+            outData[i] = newData[i] - prevData[i];
         }
     }
     return is_delta_valid;
 }
 
-template <>
-void LongArrayMultiStateCounter::add(std::vector<uint64_t>* value1,
-                                     const std::vector<uint64_t>& value2, const uint64_t numerator,
-                                     const uint64_t denominator) const {
-    if (numerator != denominator) {
-        for (int i = value2.size() - 1; i >= 0; i--) {
-            // The caller ensures that denominator != 0
-            (*value1)[i] += value2[i] * numerator / denominator;
-        }
-    } else {
-        for (int i = value2.size() - 1; i >= 0; i--) {
-            (*value1)[i] += value2[i];
-        }
-    }
-}
-
-template <>
-std::string LongArrayMultiStateCounter::valueToString(const std::vector<uint64_t>& v) const {
-    std::stringstream s;
-    s << "{";
-    bool first = true;
-    for (uint64_t n : v) {
-        if (!first) {
-            s << ", ";
-        }
-        s << n;
-        first = false;
-    }
-    s << "}";
-    return s.str();
-}
-
 } // namespace battery
 } // namespace android
diff --git a/libs/battery/LongArrayMultiStateCounter.h b/libs/battery/LongArrayMultiStateCounter.h
index f3439f6..e00c968 100644
--- a/libs/battery/LongArrayMultiStateCounter.h
+++ b/libs/battery/LongArrayMultiStateCounter.h
@@ -23,7 +23,66 @@
 namespace android {
 namespace battery {
 
-typedef MultiStateCounter<std::vector<uint64_t>> LongArrayMultiStateCounter;
+/**
+ * Wrapper for an array of uint64's.
+ */
+class Uint64Array {
+  protected:
+    size_t mSize;
+
+  public:
+    Uint64Array() : Uint64Array(0) {}
+
+    Uint64Array(size_t size) : mSize(size) {}
+
+    virtual ~Uint64Array() {}
+
+    size_t size() const { return mSize; }
+
+    /**
+     * Returns the wrapped array.
+     *
+     * Nullable! Null should be interpreted the same as an array of zeros
+     */
+    virtual const uint64_t *data() const { return nullptr; }
+
+    friend std::ostream &operator<<(std::ostream &os, const Uint64Array &v);
+
+    // Test API
+    bool operator==(const Uint64Array &other) const;
+};
+
+/**
+ * Mutable version of Uint64Array.
+ */
+class Uint64ArrayRW: public Uint64Array {
+    uint64_t* mData;
+
+public:
+    Uint64ArrayRW() : Uint64ArrayRW(0) {}
+
+    Uint64ArrayRW(size_t size) : Uint64Array(size), mData(nullptr) {}
+
+    Uint64ArrayRW(const Uint64Array &copy);
+
+    // Need an explicit copy constructor. In the initialization context C++ does not understand that
+    // a Uint64ArrayRW is a Uint64Array.
+    Uint64ArrayRW(const Uint64ArrayRW &copy) : Uint64ArrayRW((const Uint64Array &) copy) {}
+
+    // Test API
+    Uint64ArrayRW(std::initializer_list<uint64_t> init);
+
+    ~Uint64ArrayRW() override { delete[] mData; }
+
+    const uint64_t *data() const override { return mData; }
+
+    // NonNull. Will initialize the wrapped array if it is null.
+    uint64_t *dataRW();
+
+    Uint64ArrayRW &operator=(const Uint64Array &t);
+};
+
+typedef MultiStateCounter<Uint64ArrayRW, Uint64Array> LongArrayMultiStateCounter;
 
 } // namespace battery
 } // namespace android
diff --git a/libs/battery/LongArrayMultiStateCounterTest.cpp b/libs/battery/LongArrayMultiStateCounterTest.cpp
index e4e6b2a..1c74e3f 100644
--- a/libs/battery/LongArrayMultiStateCounterTest.cpp
+++ b/libs/battery/LongArrayMultiStateCounterTest.cpp
@@ -24,25 +24,25 @@
 class LongArrayMultiStateCounterTest : public testing::Test {};
 
 TEST_F(LongArrayMultiStateCounterTest, stateChange) {
-    LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
-    testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+    LongArrayMultiStateCounter testCounter(2, Uint64Array(4));
+    testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000);
     testCounter.setState(0, 1000);
     testCounter.setState(1, 2000);
-    testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+    testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000);
 
     // Time was split in half between the two states, so the counts will be split 50:50 too
-    EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(0));
-    EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(1));
+    EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(0));
+    EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(1));
 }
 
 TEST_F(LongArrayMultiStateCounterTest, accumulation) {
-    LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
-    testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+    LongArrayMultiStateCounter testCounter(2, Uint64Array(4));
+    testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000);
     testCounter.setState(0, 1000);
     testCounter.setState(1, 2000);
-    testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+    testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000);
     testCounter.setState(0, 4000);
-    testCounter.updateValue(std::vector<uint64_t>({200, 300, 400, 500}), 8000);
+    testCounter.updateValue(Uint64ArrayRW({200, 300, 400, 500}), 8000);
 
     // The first delta is split 50:50:
     //   0: {50, 100, 150, 200}
@@ -50,16 +50,16 @@
     // The second delta is split 4:1
     //   0: {80, 80, 80, 80}
     //   1: {20, 20, 20, 20}
-    EXPECT_EQ(std::vector<uint64_t>({130, 180, 230, 280}), testCounter.getCount(0));
-    EXPECT_EQ(std::vector<uint64_t>({70, 120, 170, 220}), testCounter.getCount(1));
+    EXPECT_EQ(Uint64ArrayRW({130, 180, 230, 280}), testCounter.getCount(0));
+    EXPECT_EQ(Uint64ArrayRW({70, 120, 170, 220}), testCounter.getCount(1));
 }
 
 TEST_F(LongArrayMultiStateCounterTest, toString) {
-    LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4));
-    testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000);
+    LongArrayMultiStateCounter testCounter(2, Uint64Array(4));
+    testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000);
     testCounter.setState(0, 1000);
     testCounter.setState(1, 2000);
-    testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000);
+    testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000);
 
     EXPECT_STREQ("[0: {50, 100, 150, 200}, 1: {50, 100, 150, 200}] updated: 3000 currentState: 1",
                  testCounter.toString().c_str());
diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h
index 04b7186..fadc4ff 100644
--- a/libs/battery/MultiStateCounter.h
+++ b/libs/battery/MultiStateCounter.h
@@ -35,12 +35,12 @@
 
 typedef uint16_t state_t;
 
-template <class T>
+template <class T, class V>
 class MultiStateCounter {
-    uint16_t stateCount;
+    const uint16_t stateCount;
+    const V emptyValue;
     state_t currentState;
     time_t lastStateChangeTimestamp;
-    T emptyValue;
     T lastValue;
     time_t lastUpdateTimestamp;
     T deltaValue;
@@ -54,7 +54,7 @@
     State* states;
 
 public:
-    MultiStateCounter(uint16_t stateCount, const T& emptyValue);
+    MultiStateCounter(uint16_t stateCount, const V& emptyValue);
 
     virtual ~MultiStateCounter();
 
@@ -66,35 +66,35 @@
      * Copies the current state and accumulated times-in-state from the source. Resets
      * the accumulated value.
      */
-    void copyStatesFrom(const MultiStateCounter<T>& source);
+    void copyStatesFrom(const MultiStateCounter<T, V> &source);
 
-    void setValue(state_t state, const T& value);
+    void setValue(state_t state, const V& value);
 
     /**
      * Updates the value by distributing the delta from the previously set value
      * among states according to their respective time-in-state.
      * Returns the delta from the previously set value.
      */
-    const T& updateValue(const T& value, time_t timestamp);
+    const V& updateValue(const V& value, time_t timestamp);
 
     /**
      * Updates the value by distributing the specified increment among states according
      * to their respective time-in-state.
      */
-    void incrementValue(const T& increment, time_t timestamp);
+    void incrementValue(const V& increment, time_t timestamp);
 
     /**
      * Adds the specified increment to the value for the current state, without affecting
      * the last updated value or timestamp.  Ignores partial time-in-state: the entirety of
      * the increment is given to the current state.
      */
-    void addValue(const T& increment);
+    void addValue(const V& increment);
 
     void reset();
 
     uint16_t getStateCount();
 
-    const T& getCount(state_t state);
+    const V& getCount(state_t state);
 
     std::string toString();
 
@@ -104,27 +104,25 @@
      * Returns true iff the combination of previousValue and newValue is valid
      * (newValue >= prevValue)
      */
-    bool delta(const T& previousValue, const T& newValue, T* outValue) const;
+    bool delta(const T& previousValue, const V& newValue, T* outValue) const;
 
     /**
      * Adds value2 to value1 and stores the result in value1.  Denominator is
      * guaranteed to be non-zero.
      */
-    void add(T* value1, const T& value2, const uint64_t numerator,
+    void add(T* value1, const V& value2, const uint64_t numerator,
              const uint64_t denominator) const;
-
-    std::string valueToString(const T& value) const;
 };
 
 // ---------------------- MultiStateCounter Implementation -------------------------
 // Since MultiStateCounter is a template, the implementation must be inlined.
 
-template <class T>
-MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue)
+template <class T, class V>
+MultiStateCounter<T, V>::MultiStateCounter(uint16_t stateCount, const V& emptyValue)
       : stateCount(stateCount),
+        emptyValue(emptyValue),
         currentState(0),
         lastStateChangeTimestamp(-1),
-        emptyValue(emptyValue),
         lastValue(emptyValue),
         lastUpdateTimestamp(-1),
         deltaValue(emptyValue),
@@ -136,13 +134,13 @@
     }
 }
 
-template <class T>
-MultiStateCounter<T>::~MultiStateCounter() {
+template <class T, class V>
+MultiStateCounter<T, V>::~MultiStateCounter() {
     delete[] states;
 };
 
-template <class T>
-void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) {
+template <class T, class V>
+void MultiStateCounter<T, V>::setEnabled(bool enabled, time_t timestamp) {
     if (enabled == isEnabled) {
         return;
     }
@@ -167,8 +165,8 @@
     }
 }
 
-template <class T>
-void MultiStateCounter<T>::setState(state_t state, time_t timestamp) {
+template <class T, class V>
+void MultiStateCounter<T, V>::setState(state_t state, time_t timestamp) {
     if (isEnabled && lastStateChangeTimestamp >= 0 && lastUpdateTimestamp >= 0) {
         // If the update arrived out-of-order, just push back the timestamp to
         // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate
@@ -198,8 +196,8 @@
     lastStateChangeTimestamp = timestamp;
 }
 
-template <class T>
-void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) {
+template <class T, class V>
+void MultiStateCounter<T, V>::copyStatesFrom(const MultiStateCounter<T, V>& source) {
     if (stateCount != source.stateCount) {
         ALOGE("State count mismatch: %u vs. %u\n", stateCount, source.stateCount);
         return;
@@ -214,14 +212,14 @@
     lastUpdateTimestamp = source.lastUpdateTimestamp;
 }
 
-template <class T>
-void MultiStateCounter<T>::setValue(state_t state, const T& value) {
+template <class T, class V>
+void MultiStateCounter<T, V>::setValue(state_t state, const V& value) {
     states[state].counter = value;
 }
 
-template <class T>
-const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) {
-    T* returnValue = &emptyValue;
+template <class T, class V>
+const V& MultiStateCounter<T, V>::updateValue(const V& value, time_t timestamp) {
+    const V* returnValue = &emptyValue;
 
     // If the counter is disabled, we ignore the update, except when the counter got disabled after
     // the previous update, in which case we still need to pick up the residual delta.
@@ -250,8 +248,8 @@
                     }
                 } else {
                     std::stringstream str;
-                    str << "updateValue is called with a value " << valueToString(value)
-                        << ", which is lower than the previous value " << valueToString(lastValue)
+                    str << "updateValue is called with a value " << value
+                        << ", which is lower than the previous value " << lastValue
                         << "\n";
                     ALOGE("%s", str.str().c_str());
 
@@ -276,23 +274,25 @@
     return *returnValue;
 }
 
-template <class T>
-void MultiStateCounter<T>::incrementValue(const T& increment, time_t timestamp) {
+template <class T, class V>
+void MultiStateCounter<T, V>::incrementValue(const V& increment, time_t timestamp) {
+//    T newValue;
+//    newValue = lastValue; // Copy assignment, not initialization.
     T newValue = lastValue;
     add(&newValue, increment, 1 /* numerator */, 1 /* denominator */);
     updateValue(newValue, timestamp);
 }
 
-template <class T>
-void MultiStateCounter<T>::addValue(const T& value) {
+template <class T, class V>
+void MultiStateCounter<T, V>::addValue(const V& value) {
     if (!isEnabled) {
         return;
     }
     add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
 }
 
-template <class T>
-void MultiStateCounter<T>::reset() {
+template <class T, class V>
+void MultiStateCounter<T, V>::reset() {
     lastStateChangeTimestamp = -1;
     lastUpdateTimestamp = -1;
     for (int i = 0; i < stateCount; i++) {
@@ -301,25 +301,26 @@
     }
 }
 
-template <class T>
-uint16_t MultiStateCounter<T>::getStateCount() {
+template <class T, class V>
+uint16_t MultiStateCounter<T, V>::getStateCount() {
     return stateCount;
 }
 
-template <class T>
-const T& MultiStateCounter<T>::getCount(state_t state) {
+template <class T, class V>
+const V& MultiStateCounter<T, V>::getCount(state_t state) {
     return states[state].counter;
 }
 
-template <class T>
-std::string MultiStateCounter<T>::toString() {
+template <class T, class V>
+std::string MultiStateCounter<T, V>::toString() {
     std::stringstream str;
+//    str << "LAST VALUE: " << valueToString(lastValue);
     str << "[";
     for (int i = 0; i < stateCount; i++) {
         if (i != 0) {
             str << ", ";
         }
-        str << i << ": " << valueToString(states[i].counter);
+        str << i << ": " << states[i].counter;
         if (states[i].timeInStateSinceUpdate > 0) {
             str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate;
         }
diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp
index a51a38a..589b7fe 100644
--- a/libs/battery/MultiStateCounterTest.cpp
+++ b/libs/battery/MultiStateCounterTest.cpp
@@ -21,7 +21,7 @@
 namespace android {
 namespace battery {
 
-typedef MultiStateCounter<double> DoubleMultiStateCounter;
+typedef MultiStateCounter<double, double> DoubleMultiStateCounter;
 
 template <>
 bool DoubleMultiStateCounter::delta(const double& previousValue, const double& newValue,
@@ -41,11 +41,6 @@
     }
 }
 
-template <>
-std::string DoubleMultiStateCounter::valueToString(const double& v) const {
-    return std::to_string(v);
-}
-
 class MultiStateCounterTest : public testing::Test {};
 
 TEST_F(MultiStateCounterTest, constructor) {
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 66e7ddd..e41f9bb 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -2735,8 +2735,8 @@
 
 bool Surface::waitForNextFrame(uint64_t lastFrame, nsecs_t timeout) {
     Mutex::Autolock lock(mMutex);
-    if (mNextFrameNumber > lastFrame) {
-      return true;
+    if (mLastFrameNumber > lastFrame) {
+        return true;
     }
     return mQueueBufferCondition.waitRelative(mMutex, timeout) == OK;
 }
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 6ef4a3d..305feab 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -242,6 +242,9 @@
     // context (a.k.a. "right") clicks.
     bool touchpadRightClickZoneEnabled;
 
+    // True to use three-finger tap as a customizable shortcut; false to use it as a middle-click.
+    bool touchpadThreeFingerTapShortcutEnabled;
+
     // The set of currently disabled input devices.
     std::set<int32_t> disabledDevices;
 
@@ -293,6 +296,7 @@
             touchpadTapDraggingEnabled(false),
             shouldNotifyTouchpadHardwareState(false),
             touchpadRightClickZoneEnabled(false),
+            touchpadThreeFingerTapShortcutEnabled(false),
             stylusButtonMotionEventsEnabled(true),
             stylusPointerIconEnabled(false),
             mouseReverseVerticalScrollingEnabled(false),
@@ -496,6 +500,9 @@
     /* Sends the Info of gestures that happen on the touchpad. */
     virtual void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) = 0;
 
+    /* Notifies the policy that the user has performed a three-finger touchpad tap. */
+    virtual void notifyTouchpadThreeFingerTap() = 0;
+
     /* Gets the keyboard layout for a particular input device. */
     virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
             const InputDeviceIdentifier& identifier,
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 9a36bfb..ca8266b 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -375,6 +375,9 @@
         mPropertyProvider.getProperty("Button Right Click Zone Enable")
                 .setBoolValues({config.touchpadRightClickZoneEnabled});
         mTouchpadHardwareStateNotificationsEnabled = config.shouldNotifyTouchpadHardwareState;
+
+        mGestureConverter.setThreeFingerTapShortcutEnabled(
+                config.touchpadThreeFingerTapShortcutEnabled);
     }
     std::list<NotifyArgs> out;
     if ((!changes.any() && config.pointerCaptureRequest.isEnable()) ||
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index da2c683..1959423 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -261,6 +261,14 @@
     }
 
     const uint32_t buttonsPressed = gesture.details.buttons.down;
+    const uint32_t buttonsReleased = gesture.details.buttons.up;
+
+    if (mThreeFingerTapShortcutEnabled && gesture.details.buttons.is_tap &&
+        buttonsPressed == GESTURES_BUTTON_MIDDLE && buttonsReleased == GESTURES_BUTTON_MIDDLE) {
+        mReaderContext.getPolicy()->notifyTouchpadThreeFingerTap();
+        return out;
+    }
+
     bool pointerDown = isPointerDown(mButtonState) ||
             buttonsPressed &
                     (GESTURES_BUTTON_LEFT | GESTURES_BUTTON_MIDDLE | GESTURES_BUTTON_RIGHT);
@@ -291,7 +299,6 @@
     // changes: a set of buttons going down, followed by a set of buttons going up.
     mButtonState = newButtonState;
 
-    const uint32_t buttonsReleased = gesture.details.buttons.up;
     for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) {
         if (buttonsReleased & button) {
             uint32_t actionButton = gesturesButtonToMotionEventButton(button);
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index c9a35c1..ad40721 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -55,6 +55,10 @@
 
     void setBoundsInLogicalDisplay(FloatRect bounds) { mBoundsInLogicalDisplay = bounds; }
 
+    void setThreeFingerTapShortcutEnabled(bool enabled) {
+        mThreeFingerTapShortcutEnabled = enabled;
+    }
+
     void populateMotionRanges(InputDeviceInfo& info) const;
 
     [[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime,
@@ -101,6 +105,8 @@
     const bool mEnableFlingStop;
     const bool mEnableNoFocusChange;
 
+    bool mThreeFingerTapShortcutEnabled;
+
     std::optional<ui::LogicalDisplayId> mDisplayId;
     FloatRect mBoundsInLogicalDisplay{};
     ui::Rotation mOrientation = ui::ROTATION_0;
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 7c5f350..67b1e8c 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -80,6 +80,17 @@
     ASSERT_TRUE(success) << "Timed out waiting for hardware state to be notified";
 }
 
+void FakeInputReaderPolicy::assertTouchpadThreeFingerTapNotified() {
+    std::unique_lock lock(mLock);
+    base::ScopedLockAssertion assumeLocked(mLock);
+
+    const bool success =
+            mTouchpadThreeFingerTapNotified.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+                return mTouchpadThreeFingerTapHasBeenReported;
+            });
+    ASSERT_TRUE(success) << "Timed out waiting for three-finger tap to be notified";
+}
+
 void FakeInputReaderPolicy::clearViewports() {
     mViewports.clear();
     mConfig.setDisplayViewports(mViewports);
@@ -259,6 +270,12 @@
     std::scoped_lock lock(mLock);
 }
 
+void FakeInputReaderPolicy::notifyTouchpadThreeFingerTap() {
+    std::scoped_lock lock(mLock);
+    mTouchpadThreeFingerTapHasBeenReported = true;
+    mTouchpadThreeFingerTapNotified.notify_all();
+}
+
 std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay(
         const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) {
     return nullptr;
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 3a2b4e9..42c9567 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -43,6 +43,7 @@
     void assertStylusGestureNotified(int32_t deviceId);
     void assertStylusGestureNotNotified();
     void assertTouchpadHardwareStateNotified();
+    void assertTouchpadThreeFingerTapNotified();
 
     virtual void clearViewports();
     std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const;
@@ -86,6 +87,7 @@
     void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
                                      int32_t deviceId) override;
     void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override;
+    void notifyTouchpadThreeFingerTap() override;
     std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
             const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override;
     std::string getDeviceAlias(const InputDeviceIdentifier&) override;
@@ -109,6 +111,9 @@
     std::condition_variable mTouchpadHardwareStateNotified;
     std::optional<SelfContainedHardwareState> mTouchpadHardwareState GUARDED_BY(mLock){};
 
+    std::condition_variable mTouchpadThreeFingerTapNotified;
+    bool mTouchpadThreeFingerTapHasBeenReported{false};
+
     uint32_t mNextPointerCaptureSequenceNumber{0};
 };
 
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 225ae0f..fad8f05 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -1272,6 +1272,27 @@
                               WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
 }
 
+TEST_F(GestureConverterTest, ThreeFingerTap_TriggersShortcut) {
+    InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+    GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+    converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
+    converter.setThreeFingerTapShortcutEnabled(true);
+
+    Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*vx=*/0,
+                         /*vy=*/0, GESTURES_FLING_TAP_DOWN);
+    std::list<NotifyArgs> args =
+            converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
+    // We don't need to check args here, since it's covered by the FlingTapDown test.
+
+    Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+                       /*down=*/GESTURES_BUTTON_MIDDLE, /*up=*/GESTURES_BUTTON_MIDDLE,
+                       /*is_tap=*/true);
+    args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapGesture);
+
+    ASSERT_TRUE(args.empty());
+    mFakePolicy->assertTouchpadThreeFingerTapNotified();
+}
+
 TEST_F(GestureConverterTest, Click) {
     // Click should produce button press/release events
     InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 60c676d..a1da39a 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -288,6 +288,7 @@
     void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
                                      int32_t deviceId) override {}
     void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override {}
+    void notifyTouchpadThreeFingerTap() override {}
     std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
             const InputDeviceIdentifier& identifier,
             const std::optional<KeyboardLayoutInfo> layoutInfo) override {
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 402a3d2..c743ea2 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -201,6 +201,10 @@
     return mPowerMode != hal::PowerMode::OFF;
 }
 
+bool DisplayDevice::isRefreshable() const {
+    return mPowerMode == hal::PowerMode::DOZE || mPowerMode == hal::PowerMode::ON;
+}
+
 ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
     return mCompositionDisplay->getState().dataspace;
 }
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 3e3f558..d09a6b3 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -173,6 +173,7 @@
     hardware::graphics::composer::hal::PowerMode getPowerMode() const;
     void setPowerMode(hardware::graphics::composer::hal::PowerMode);
     bool isPoweredOn() const;
+    bool isRefreshable() const;
     void tracePowerMode();
 
     // Enables layer caching on this DisplayDevice
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index e385f18..7729671 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -420,6 +420,16 @@
     mCondition.notify_all();
 }
 
+void EventThread::omitVsyncDispatching(bool omitted) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!mVSyncState || mVSyncState->omitted == omitted) {
+        return;
+    }
+
+    mVSyncState->omitted = omitted;
+    mCondition.notify_all();
+}
+
 void EventThread::onVsync(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
     std::lock_guard<std::mutex> lock(mMutex);
     mLastVsyncCallbackTime = TimePoint::fromNs(vsyncTime);
@@ -521,7 +531,17 @@
         }
 
         if (mVSyncState && vsyncRequested) {
-            mState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
+            const bool vsyncOmitted =
+                    FlagManager::getInstance().no_vsyncs_on_screen_off() && mVSyncState->omitted;
+            if (vsyncOmitted) {
+                mState = State::Idle;
+                SFTRACE_INT("VsyncPendingScreenOn", 1);
+            } else {
+                mState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
+                if (FlagManager::getInstance().no_vsyncs_on_screen_off()) {
+                    SFTRACE_INT("VsyncPendingScreenOn", 0);
+                }
+            }
         } else {
             ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected");
             mState = State::Idle;
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index c3c7eb0..2daf126 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -106,6 +106,8 @@
     // Feed clients with fake VSYNC, e.g. while the display is off.
     virtual void enableSyntheticVsync(bool) = 0;
 
+    virtual void omitVsyncDispatching(bool) = 0;
+
     virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
 
     virtual void onHotplugConnectionError(int32_t connectionError) = 0;
@@ -165,6 +167,8 @@
 
     void enableSyntheticVsync(bool) override;
 
+    void omitVsyncDispatching(bool) override;
+
     void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
 
     void onHotplugConnectionError(int32_t connectionError) override;
@@ -240,6 +244,9 @@
 
         // True if VSYNC should be faked, e.g. when display is off.
         bool synthetic = false;
+
+        // True if VSYNC should not be delivered to apps. Used when the display is off.
+        bool omitted = false;
     };
 
     // TODO(b/74619554): Create per-display threads waiting on respective VSYNC signals,
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index b83ff19..b8b1f59 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -405,6 +405,14 @@
     eventThreadFor(Cycle::Render).enableSyntheticVsync(enable);
 }
 
+void Scheduler::omitVsyncDispatching(bool omitted) {
+    eventThreadFor(Cycle::Render).omitVsyncDispatching(omitted);
+    // Note: If we don't couple Cycle::LastComposite event thread, there is a black screen
+    // after boot. This is most likely sysui or system_server dependency on sf instance
+    // Choreographer
+    eventThreadFor(Cycle::LastComposite).omitVsyncDispatching(omitted);
+}
+
 void Scheduler::onFrameRateOverridesChanged() {
     const auto [pacesetterId, supportsFrameRateOverrideByContent] = [this] {
         std::scoped_lock lock(mDisplayLock);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index c88b563..d029488 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -154,6 +154,7 @@
     bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&) EXCLUDES(mPolicyLock);
 
     void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext);
+    void omitVsyncDispatching(bool) REQUIRES(kMainThreadContext);
 
     void onHdcpLevelsChanged(Cycle, PhysicalDisplayId, int32_t, int32_t);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8b204a2..cf787cf 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -3665,6 +3665,26 @@
     return display;
 }
 
+void SurfaceFlinger::incRefreshableDisplays() {
+    if (FlagManager::getInstance().no_vsyncs_on_screen_off()) {
+        mRefreshableDisplays++;
+        if (mRefreshableDisplays == 1) {
+            ftl::FakeGuard guard(kMainThreadContext);
+            mScheduler->omitVsyncDispatching(false);
+        }
+    }
+}
+
+void SurfaceFlinger::decRefreshableDisplays() {
+    if (FlagManager::getInstance().no_vsyncs_on_screen_off()) {
+        mRefreshableDisplays--;
+        if (mRefreshableDisplays == 0) {
+            ftl::FakeGuard guard(kMainThreadContext);
+            mScheduler->omitVsyncDispatching(true);
+        }
+    }
+}
+
 void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken,
                                          const DisplayDeviceState& state) {
     ui::Size resolution(0, 0);
@@ -3756,6 +3776,10 @@
         display->adjustRefreshRate(mScheduler->getPacesetterRefreshRate());
     }
 
+    if (display->isRefreshable()) {
+        incRefreshableDisplays();
+    }
+
     mDisplays.try_emplace(displayToken, std::move(display));
 
     // For an external display, loadDisplayModes already attempted to select the same mode
@@ -3790,6 +3814,10 @@
         } else {
             mScheduler->unregisterDisplay(display->getPhysicalId(), mActiveDisplayId);
         }
+
+        if (display->isRefreshable()) {
+            decRefreshableDisplays();
+        }
     }
 
     mDisplays.erase(displayToken);
@@ -3824,6 +3852,10 @@
             if (display->isVirtual()) {
                 releaseVirtualDisplay(display->getVirtualId());
             }
+
+            if (display->isRefreshable()) {
+                decRefreshableDisplays();
+            }
         }
 
         mDisplays.erase(displayToken);
@@ -5322,7 +5354,15 @@
                      activeDisplay->isPoweredOn(),
              "Trying to change power mode on inactive display without powering off active display");
 
+    const bool couldRefresh = display->isRefreshable();
     display->setPowerMode(mode);
+    const bool canRefresh = display->isRefreshable();
+
+    if (couldRefresh && !canRefresh) {
+        decRefreshableDisplays();
+    } else if (!couldRefresh && canRefresh) {
+        incRefreshableDisplays();
+    }
 
     const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr;
     if (currentMode == hal::PowerMode::OFF) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ad3106c..c96dc6f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1411,6 +1411,11 @@
     // Whether a display should be turned on when initialized
     bool mSkipPowerOnForQuiescent;
 
+    // used for omitting vsync callbacks to apps when the display is not updatable
+    int mRefreshableDisplays GUARDED_BY(mStateLock) = 0;
+    void incRefreshableDisplays() REQUIRES(mStateLock);
+    void decRefreshableDisplays() REQUIRES(mStateLock);
+
     frontend::LayerLifecycleManager mLayerLifecycleManager GUARDED_BY(kMainThreadContext);
     frontend::LayerHierarchyBuilder mLayerHierarchyBuilder GUARDED_BY(kMainThreadContext);
     frontend::LayerSnapshotBuilder mLayerSnapshotBuilder GUARDED_BY(kMainThreadContext);
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 1510036..57ef4c7 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -139,6 +139,7 @@
     DUMP_READ_ONLY_FLAG(vrr_bugfix_dropped_frame);
     DUMP_READ_ONLY_FLAG(restore_blur_step);
     DUMP_READ_ONLY_FLAG(dont_skip_on_early_ro);
+    DUMP_READ_ONLY_FLAG(no_vsyncs_on_screen_off);
     DUMP_READ_ONLY_FLAG(protected_if_client);
     DUMP_READ_ONLY_FLAG(idle_screen_refresh_rate_timeout);
     DUMP_READ_ONLY_FLAG(graphite_renderengine);
@@ -244,6 +245,7 @@
 FLAG_MANAGER_READ_ONLY_FLAG(renderable_buffer_usage, "")
 FLAG_MANAGER_READ_ONLY_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step")
 FLAG_MANAGER_READ_ONLY_FLAG(dont_skip_on_early_ro, "")
+FLAG_MANAGER_READ_ONLY_FLAG(no_vsyncs_on_screen_off, "debug.sf.no_vsyncs_on_screen_off")
 FLAG_MANAGER_READ_ONLY_FLAG(protected_if_client, "")
 FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_24q4, "");
 FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_dropped_frame, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 4877281..7716762 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -77,6 +77,7 @@
     bool renderable_buffer_usage() const;
     bool restore_blur_step() const;
     bool dont_skip_on_early_ro() const;
+    bool no_vsyncs_on_screen_off() const;
     bool protected_if_client() const;
     bool idle_screen_refresh_rate_timeout() const;
     bool graphite_renderengine() const;
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index 302a05a..ce334e4 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -166,6 +166,14 @@
 } # local_tonemap_screenshots
 
 flag {
+  name: "no_vsyncs_on_screen_off"
+  namespace: "core_graphics"
+  description: "Stop vsync / Choreographer callbacks to apps when the screen is off"
+  bug: "331636736"
+  is_fixed_read_only: true
+} # no_vsyncs_on_screen_off
+
+flag {
   name: "single_hop_screenshot"
   namespace: "window_surfaces"
   description: "Only access SF main thread once during a screenshot"
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 7398cbe..82500fe 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -30,6 +30,7 @@
     MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, (EventRegistrationFlags),
                 (const, override));
     MOCK_METHOD(void, enableSyntheticVsync, (bool), (override));
+    MOCK_METHOD(void, omitVsyncDispatching, (bool), (override));
     MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override));
     MOCK_METHOD(void, onHotplugConnectionError, (int32_t), (override));
     MOCK_METHOD(void, onModeChanged, (const scheduler::FrameRateMode&), (override));