Snap for 7483611 from ab3c8d659cf4319f2c0b9bf8f9e37cc4eaf8c729 to mainline-adbd-release

Change-Id: I3fc3ace22e985c33ab1efc19205707929661b1c9
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index a27fd10..e847626 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -324,6 +324,12 @@
 
     AddRuntimeArg(MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s"));
     AddRuntimeArg(MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s"));
+
+    // Enable compiling dex files in isolation on low ram devices.
+    // It takes longer but reduces the memory footprint.
+    if (GetBoolProperty("ro.config.low_ram", false)) {
+      AddArg("--compile-individually");
+    }
 }
 
 void RunDex2Oat::Exec(int exit_code) {
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 52d3fa3..364c939 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -151,6 +151,7 @@
                                                       1, false);
     static int32_t id = 0;
     auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id);
+    mPendingBufferTrace = "PendingBuffer - " + mName + "BLAST#" + std::to_string(id);
     id++;
     mBufferItemConsumer->setName(String8(consumerName.c_str()));
     mBufferItemConsumer->setFrameAvailableListener(this);
@@ -168,7 +169,6 @@
           .setFlags(surface, layer_state_t::eEnableBackpressure,
                     layer_state_t::eEnableBackpressure)
           .apply();
-
     mNumAcquired = 0;
     mNumFrameAvailable = 0;
 }
@@ -205,6 +205,11 @@
         applyTransaction = true;
     }
 
+    if (mSurfaceControl != nullptr) {
+        mTransformHint = mSurfaceControl->getTransformHint();
+        mBufferItemConsumer->setTransformHint(mTransformHint);
+    }
+
     ui::Size newSize(width, height);
     if (mRequestedSize != newSize) {
         mRequestedSize.set(newSize);
@@ -259,15 +264,18 @@
 
                 mTransformHint = stat.transformHint;
                 mBufferItemConsumer->setTransformHint(mTransformHint);
-                mBufferItemConsumer
-                        ->updateFrameTimestamps(stat.frameEventStats.frameNumber,
-                                                stat.frameEventStats.refreshStartTime,
-                                                stat.frameEventStats.gpuCompositionDoneFence,
-                                                stat.presentFence, stat.previousReleaseFence,
-                                                stat.frameEventStats.compositorTiming,
-                                                stat.latchTime,
-                                                stat.frameEventStats.dequeueReadyTime);
-
+                // Update frametime stamps if the frame was latched and presented, indicated by a
+                // valid latch time.
+                if (stat.latchTime > 0) {
+                    mBufferItemConsumer
+                            ->updateFrameTimestamps(stat.frameEventStats.frameNumber,
+                                                    stat.frameEventStats.refreshStartTime,
+                                                    stat.frameEventStats.gpuCompositionDoneFence,
+                                                    stat.presentFence, stat.previousReleaseFence,
+                                                    stat.frameEventStats.compositorTiming,
+                                                    stat.latchTime,
+                                                    stat.frameEventStats.dequeueReadyTime);
+                }
                 currFrameNumber = stat.frameEventStats.frameNumber;
 
                 if (mTransactionCompleteCallback &&
@@ -361,6 +369,7 @@
     ATRACE_INT("PendingRelease", mPendingRelease.size());
 
     mNumAcquired--;
+    ATRACE_INT(mPendingBufferTrace.c_str(), mNumFrameAvailable + mNumAcquired);
     processNextBufferLocked(false /* useNextTransaction */);
     mCallbackCV.notify_all();
 }
@@ -529,6 +538,7 @@
     }
     // add to shadow queue
     mNumFrameAvailable++;
+    ATRACE_INT(mPendingBufferTrace.c_str(), mNumFrameAvailable + mNumAcquired);
 
     BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
              toString(nextTransactionSet));
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 0981e76..cb0e65e 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -125,6 +125,7 @@
     static PixelFormat convertBufferFormat(PixelFormat& format);
 
     std::string mName;
+    std::string mPendingBufferTrace;
     sp<SurfaceControl> mSurfaceControl;
 
     std::mutex mMutex;
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
index 0750080..dd3de58 100644
--- a/libs/gui/include/gui/FrameTimestamps.h
+++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -131,6 +131,7 @@
     // Public for testing.
     static nsecs_t snapToNextTick(
             nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval);
+    nsecs_t getReportedCompositeDeadline() const { return mCompositorTiming.deadline; };
 
     nsecs_t getNextCompositeDeadline(const nsecs_t now) const;
     nsecs_t getCompositeInterval() const { return mCompositorTiming.interval; }
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 06660b8..6ff67aa 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -948,21 +948,22 @@
 class BLASTFrameEventHistoryTest : public BLASTBufferQueueTest {
 public:
     void setUpAndQueueBuffer(const sp<IGraphicBufferProducer>& igbProducer,
-                             nsecs_t* requestedPresentTime, nsecs_t* postedTime,
+                             nsecs_t* outRequestedPresentTime, nsecs_t* postedTime,
                              IGraphicBufferProducer::QueueBufferOutput* qbOutput,
-                             bool getFrameTimestamps, nsecs_t requestedPresentTimeDelay = 0) {
+                             bool getFrameTimestamps, nsecs_t requestedPresentTime = systemTime()) {
         int slot;
         sp<Fence> fence;
         sp<GraphicBuffer> buf;
         auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
                                               PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
                                               nullptr, nullptr);
-        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
-        ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+        if (IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION == ret) {
+            ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+        }
 
-        nsecs_t requestedTime = systemTime() + requestedPresentTimeDelay;
-        if (requestedPresentTime) *requestedPresentTime = requestedTime;
-        IGraphicBufferProducer::QueueBufferInput input(requestedTime, false, HAL_DATASPACE_UNKNOWN,
+        *outRequestedPresentTime = requestedPresentTime;
+        IGraphicBufferProducer::QueueBufferInput input(requestedPresentTime, false,
+                                                       HAL_DATASPACE_UNKNOWN,
                                                        Rect(mDisplayWidth, mDisplayHeight),
                                                        NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
                                                        Fence::NO_FENCE, /*sticky*/ 0,
@@ -1034,9 +1035,11 @@
     IGraphicBufferProducer::QueueBufferOutput qbOutput;
     nsecs_t requestedPresentTimeA = 0;
     nsecs_t postedTimeA = 0;
-    nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
+    // Present the frame sometime in the future so we can add two frames to the queue so the older
+    // one will be dropped.
+    nsecs_t presentTime = systemTime() + std::chrono::nanoseconds(500ms).count();
     setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true,
-                        presentTimeDelay);
+                        presentTime);
     history.applyDelta(qbOutput.frameTimestamps);
 
     FrameEvents* events = nullptr;
@@ -1049,7 +1052,10 @@
     // queue another buffer so the first can be dropped
     nsecs_t requestedPresentTimeB = 0;
     nsecs_t postedTimeB = 0;
-    setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true);
+    adapter.setTransactionCompleteCallback(2);
+    presentTime = systemTime() + std::chrono::nanoseconds(1ms).count();
+    setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true,
+                        presentTime);
     history.applyDelta(qbOutput.frameTimestamps);
     events = history.getFrame(1);
     ASSERT_NE(nullptr, events);
@@ -1059,20 +1065,75 @@
     ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
     ASSERT_GE(events->postedTime, postedTimeA);
 
-    // a valid latchtime should not be set
+    // a valid latchtime and pre and post composition info should not be set for the dropped frame
     ASSERT_FALSE(events->hasLatchInfo());
     ASSERT_FALSE(events->hasDequeueReadyInfo());
+    ASSERT_FALSE(events->hasGpuCompositionDoneInfo());
+    ASSERT_FALSE(events->hasDisplayPresentInfo());
+    ASSERT_FALSE(events->hasReleaseInfo());
 
-    ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
-    ASSERT_NE(nullptr, events->displayPresentFence);
-    ASSERT_NE(nullptr, events->releaseFence);
+    // wait for the last transaction to be completed.
+    adapter.waitForCallback(2);
 
-    // we should also have gotten the initial values for the next frame
+    // queue another buffer so we query for frame event deltas
+    nsecs_t requestedPresentTimeC = 0;
+    nsecs_t postedTimeC = 0;
+    setUpAndQueueBuffer(igbProducer, &requestedPresentTimeC, &postedTimeC, &qbOutput, true);
+    history.applyDelta(qbOutput.frameTimestamps);
+
+    // frame number, requestedPresentTime, and postTime should not have changed
+    ASSERT_EQ(1, events->frameNumber);
+    ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
+    ASSERT_GE(events->postedTime, postedTimeA);
+
+    // a valid latchtime and pre and post composition info should not be set for the dropped frame
+    ASSERT_FALSE(events->hasLatchInfo());
+    ASSERT_FALSE(events->hasDequeueReadyInfo());
+    ASSERT_FALSE(events->hasGpuCompositionDoneInfo());
+    ASSERT_FALSE(events->hasDisplayPresentInfo());
+    ASSERT_FALSE(events->hasReleaseInfo());
+
+    // we should also have gotten values for the presented frame
     events = history.getFrame(2);
     ASSERT_NE(nullptr, events);
     ASSERT_EQ(2, events->frameNumber);
     ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
     ASSERT_GE(events->postedTime, postedTimeB);
+    ASSERT_GE(events->latchTime, postedTimeB);
+    ASSERT_GE(events->dequeueReadyTime, events->latchTime);
+    ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
+    ASSERT_NE(nullptr, events->displayPresentFence);
+    ASSERT_NE(nullptr, events->releaseFence);
+
+    // wait for any callbacks that have not been received
+    adapter.waitForCallbacks();
+}
+
+TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_CompositorTimings) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> igbProducer;
+    ProducerFrameEventHistory history;
+    setUpProducer(adapter, igbProducer);
+
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    nsecs_t requestedPresentTimeA = 0;
+    nsecs_t postedTimeA = 0;
+    adapter.setTransactionCompleteCallback(1);
+    setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true);
+    history.applyDelta(qbOutput.frameTimestamps);
+    adapter.waitForCallback(1);
+
+    // queue another buffer so we query for frame event deltas
+    nsecs_t requestedPresentTimeB = 0;
+    nsecs_t postedTimeB = 0;
+    setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true);
+    history.applyDelta(qbOutput.frameTimestamps);
+
+    // check for a valid compositor deadline
+    ASSERT_NE(0, history.getReportedCompositeDeadline());
+
+    // wait for any callbacks that have not been received
+    adapter.waitForCallbacks();
 }
 
 } // namespace android
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
index 2db8c13..fb19435 100644
--- a/services/inputflinger/dispatcher/FocusResolver.cpp
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -216,4 +216,9 @@
     return dump;
 }
 
+void FocusResolver::displayRemoved(int32_t displayId) {
+    mFocusRequestByDisplay.erase(displayId);
+    mLastFocusResultByDisplay.erase(displayId);
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h
index dc5eeeb..afe16b3 100644
--- a/services/inputflinger/dispatcher/FocusResolver.h
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -62,6 +62,9 @@
     std::optional<FocusResolver::FocusChanges> setFocusedWindow(
             const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows);
 
+    // Display has been removed from the system, clean up old references.
+    void displayRemoved(int32_t displayId);
+
     // exposed for debugging
     bool hasFocusedWindowTokens() const { return !mFocusedWindowTokenByDisplay.empty(); }
     std::string dumpFocusedWindows() const;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index d2b8739..c0010ab 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -4613,30 +4613,34 @@
     }
     { // acquire lock
         std::scoped_lock _l(mLock);
-
-        std::shared_ptr<InputApplicationHandle> oldFocusedApplicationHandle =
-                getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
-
-        if (sharedPointersEqual(oldFocusedApplicationHandle, inputApplicationHandle)) {
-            return; // This application is already focused. No need to wake up or change anything.
-        }
-
-        // Set the new application handle.
-        if (inputApplicationHandle != nullptr) {
-            mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
-        } else {
-            mFocusedApplicationHandlesByDisplay.erase(displayId);
-        }
-
-        // No matter what the old focused application was, stop waiting on it because it is
-        // no longer focused.
-        resetNoFocusedWindowTimeoutLocked();
+        setFocusedApplicationLocked(displayId, inputApplicationHandle);
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
     mLooper->wake();
 }
 
+void InputDispatcher::setFocusedApplicationLocked(
+        int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
+    std::shared_ptr<InputApplicationHandle> oldFocusedApplicationHandle =
+            getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
+
+    if (sharedPointersEqual(oldFocusedApplicationHandle, inputApplicationHandle)) {
+        return; // This application is already focused. No need to wake up or change anything.
+    }
+
+    // Set the new application handle.
+    if (inputApplicationHandle != nullptr) {
+        mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
+    } else {
+        mFocusedApplicationHandlesByDisplay.erase(displayId);
+    }
+
+    // No matter what the old focused application was, stop waiting on it because it is
+    // no longer focused.
+    resetNoFocusedWindowTimeoutLocked();
+}
+
 /**
  * Sets the focused display, which is responsible for receiving focus-dispatched input events where
  * the display not specified.
@@ -6208,4 +6212,19 @@
     mLock.lock();
 }
 
+void InputDispatcher::displayRemoved(int32_t displayId) {
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+        // Set an empty list to remove all handles from the specific display.
+        setInputWindowsLocked(/* window handles */ {}, displayId);
+        setFocusedApplicationLocked(displayId, nullptr);
+        // Call focus resolver to clean up stale requests. This must be called after input windows
+        // have been removed for the removed display.
+        mFocusResolver.displayRemoved(displayId);
+    } // release lock
+
+    // Wake up poll loop since it may need to make new input dispatching choices.
+    mLooper->wake();
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index bb3f3e6..9edf41c 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -139,6 +139,8 @@
 
     std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
 
+    void displayRemoved(int32_t displayId) override;
+
 private:
     enum class DropReason {
         NOT_DROPPED,
@@ -343,6 +345,9 @@
     std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
     std::unique_ptr<DragState> mDragState GUARDED_BY(mLock);
 
+    void setFocusedApplicationLocked(
+            int32_t displayId,
+            const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) REQUIRES(mLock);
     // Focused applications.
     std::unordered_map<int32_t, std::shared_ptr<InputApplicationHandle>>
             mFocusedApplicationHandlesByDisplay GUARDED_BY(mLock);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 7f85e53..43428a0 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -209,6 +209,11 @@
      * Returns true on success.
      */
     virtual bool flushSensor(int deviceId, InputDeviceSensorType sensorType) = 0;
+
+    /**
+     * Called when a display has been removed from the system.
+     */
+    virtual void displayRemoved(int32_t displayId) = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
index 17efb5b..9051ff1 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -256,5 +256,37 @@
     // dropped.
     ASSERT_FALSE(changes);
 }
+TEST(FocusResolverTest, FocusRequestsAreClearedWhenWindowIsRemoved) {
+    sp<IBinder> windowToken = new BBinder();
+    std::vector<sp<InputWindowHandle>> windows;
+
+    sp<FakeWindowHandle> window = new FakeWindowHandle("Test Window", windowToken,
+                                                       true /* focusable */, true /* visible */);
+    windows.push_back(window);
+
+    FocusRequest request;
+    request.displayId = 42;
+    request.token = windowToken;
+    FocusResolver focusResolver;
+    std::optional<FocusResolver::FocusChanges> changes =
+            focusResolver.setFocusedWindow(request, windows);
+    ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+    ASSERT_EQ(request.displayId, changes->displayId);
+
+    // Start with a focused window
+    window->setFocusable(true);
+    changes = focusResolver.setInputWindows(request.displayId, windows);
+    ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+
+    // When a display is removed, all windows are removed from the display
+    // and our focused window loses focus
+    changes = focusResolver.setInputWindows(request.displayId, {});
+    ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
+    focusResolver.displayRemoved(request.displayId);
+
+    // When a display is readded, the window does not get focus since the request was cleared.
+    changes = focusResolver.setInputWindows(request.displayId, windows);
+    ASSERT_FALSE(changes);
+}
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index d51acce..77ca12c 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -2688,6 +2688,23 @@
     window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
 }
 
+TEST_F(InputDispatcherTest, DisplayRemoved) {
+    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "window", ADISPLAY_ID_DEFAULT);
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    // window is granted focus.
+    window->setFocusable(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    setFocusedWindow(window);
+    window->consumeFocusEvent(true);
+
+    // When a display is removed window loses focus.
+    mDispatcher->displayRemoved(ADISPLAY_ID_DEFAULT);
+    window->consumeFocusEvent(false);
+}
+
 /**
  * Launch two windows, with different owners. One window (slipperyExitWindow) has Flag::SLIPPERY,
  * and overlaps the other window, slipperyEnterWindow. The window 'slipperyExitWindow' is on top
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index a3b7b13..6b5cf04 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -540,10 +540,16 @@
 
 bool BufferStateLayer::setSidebandStream(const sp<NativeHandle>& sidebandStream) {
     if (mDrawingState.sidebandStream == sidebandStream) return false;
+
+    if (mDrawingState.sidebandStream != nullptr && sidebandStream == nullptr) {
+        mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
+    } else if (sidebandStream != nullptr) {
+        mFlinger->mTunnelModeEnabledReporter->incrementTunnelModeCount();
+    }
+
     mDrawingState.sidebandStream = sidebandStream;
     mDrawingState.modified = true;
     setTransactionFlags(eTransactionNeeded);
-
     if (!mSidebandStreamChanged.exchange(true)) {
         // mSidebandStreamChanged was false
         mFlinger->signalLayerUpdate();
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index b819dbe..ad31b3f 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -70,6 +70,7 @@
 #include "MonitoredProducer.h"
 #include "SurfaceFlinger.h"
 #include "TimeStats/TimeStats.h"
+#include "TunnelModeEnabledReporter.h"
 #include "input/InputWindow.h"
 
 #define DEBUG_RESIZE 0
@@ -172,6 +173,10 @@
 
     mFrameTracker.logAndResetStats(mName);
     mFlinger->onLayerDestroyed(this);
+
+    if (mDrawingState.sidebandStream != nullptr) {
+        mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount();
+    }
 }
 
 LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
@@ -679,11 +684,6 @@
 uint32_t Layer::doTransaction(uint32_t flags) {
     ATRACE_CALL();
 
-    if (mChildrenChanged) {
-        flags |= eVisibleRegion;
-        mChildrenChanged = false;
-    }
-
     // TODO: This is unfortunate.
     mDrawingStateModified = mDrawingState.modified;
     mDrawingState.modified = false;
@@ -705,11 +705,6 @@
         mNeedsFiltering = getActiveTransform(s).needsBilinearFiltering();
     }
 
-    if (mDrawingState.inputInfoChanged) {
-        flags |= eInputInfoChanged;
-        mDrawingState.inputInfoChanged = false;
-    }
-
     commitTransaction(mDrawingState);
 
     return flags;
@@ -782,6 +777,8 @@
     mDrawingState.z = z;
     mDrawingState.modified = true;
 
+    mFlinger->mSomeChildrenChanged = true;
+
     // Discard all relative layering.
     if (mDrawingState.zOrderRelativeOf != nullptr) {
         sp<Layer> strongRelative = mDrawingState.zOrderRelativeOf.promote();
@@ -832,6 +829,8 @@
         return false;
     }
 
+    mFlinger->mSomeChildrenChanged = true;
+
     mDrawingState.sequence++;
     mDrawingState.modified = true;
     mDrawingState.z = relativeZ;
@@ -1562,7 +1561,7 @@
 }
 
 void Layer::addChild(const sp<Layer>& layer) {
-    mChildrenChanged = true;
+    mFlinger->mSomeChildrenChanged = true;
     setTransactionFlags(eTransactionNeeded);
 
     mCurrentChildren.add(layer);
@@ -1572,7 +1571,7 @@
 }
 
 ssize_t Layer::removeChild(const sp<Layer>& layer) {
-    mChildrenChanged = true;
+    mFlinger->mSomeChildrenChanged = true;
     setTransactionFlags(eTransactionNeeded);
 
     layer->setParent(nullptr);
@@ -1991,7 +1990,7 @@
     mDrawingState.inputInfo = info;
     mDrawingState.touchableRegionCrop = extractLayerFromBinder(info.touchableRegionCropHandle);
     mDrawingState.modified = true;
-    mDrawingState.inputInfoChanged = true;
+    mFlinger->mInputInfoChanged = true;
     setTransactionFlags(eTransactionNeeded);
 }
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 9b30fcb..ec9bb7c 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -186,7 +186,6 @@
         float cornerRadius;
         int backgroundBlurRadius;
 
-        bool inputInfoChanged;
         InputWindowInfo inputInfo;
         wp<Layer> touchableRegionCrop;
 
@@ -997,15 +996,12 @@
     // This layer can be a cursor on some displays.
     bool mPotentialCursor{false};
 
-    LayerVector mCurrentChildren{LayerVector::StateSet::Drawing};
+    LayerVector mCurrentChildren{LayerVector::StateSet::Current};
     LayerVector mDrawingChildren{LayerVector::StateSet::Drawing};
 
     wp<Layer> mCurrentParent;
     wp<Layer> mDrawingParent;
 
-    // Can only be accessed with the SF state lock held.
-    bool mChildrenChanged{false};
-
     // Window types from WindowManager.LayoutParams
     const InputWindowInfo::Type mWindowType;
 
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index 18a2891..6b2d745 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -140,6 +140,11 @@
             outTransformMatrix);
 }
 
+status_t MonitoredProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+                                                Rect* outRect, uint32_t* outTransform) {
+    return mProducer->getLastQueuedBuffer(outBuffer, outFence, outRect, outTransform);
+}
+
 void MonitoredProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) {
     mProducer->getFrameTimestamps(outDelta);
 }
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index 788919b..3778277 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -64,6 +64,8 @@
     virtual status_t setLegacyBufferDrop(bool drop) override;
     virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
             sp<Fence>* outFence, float outTransformMatrix[16]) override;
+    virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
+                                         Rect* outRect, uint32_t* outTransform) override;
     virtual IBinder* onAsBinder();
     virtual status_t setSharedBufferMode(bool sharedBufferMode) override;
     virtual status_t setAutoRefresh(bool autoRefresh) override;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index c6841ed..a022a8e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -342,6 +342,7 @@
         mEventQueue(mFactory.createMessageQueue()),
         mCompositionEngine(mFactory.createCompositionEngine()),
         mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)),
+        mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()),
         mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
         mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {
     ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
@@ -2231,18 +2232,15 @@
 
     getBE().mDisplayTimeline.push(mPreviousPresentFences[0].fenceTime);
 
+    nsecs_t now = systemTime();
+
     // Set presentation information before calling Layer::releasePendingBuffer, such that jank
     // information from previous' frame classification is already available when sending jank info
     // to clients, so they get jank classification as early as possible.
-    mFrameTimeline->setSfPresent(systemTime(), mPreviousPresentFences[0].fenceTime,
+    mFrameTimeline->setSfPresent(/* sfPresentTime */ now, mPreviousPresentFences[0].fenceTime,
                                  glCompositionDoneFenceTime);
 
-    nsecs_t dequeueReadyTime = systemTime();
-    for (const auto& layer : mLayersWithQueuedFrames) {
-        layer->releasePendingBuffer(dequeueReadyTime);
-    }
-
-    const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(systemTime());
+    const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(now);
 
     // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
     // be sampled a little later than when we started doing work for this frame,
@@ -2259,6 +2257,7 @@
         const bool frameLatched =
                 layer->onPostComposition(display, glCompositionDoneFenceTime,
                                          mPreviousPresentFences[0].fenceTime, compositorTiming);
+        layer->releasePendingBuffer(/*dequeueReadyTime*/ now);
         if (frameLatched) {
             recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
         }
@@ -2859,7 +2858,9 @@
             (currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
             display->setProjection(currentState.orientation, currentState.layerStackSpaceRect,
                                    currentState.orientedDisplaySpaceRect);
-            mDefaultDisplayTransformHint = display->getTransformHint();
+            if (display->isPrimary()) {
+                mDefaultDisplayTransformHint = display->getTransformHint();
+            }
         }
         if (currentState.width != drawingState.width ||
             currentState.height != drawingState.height) {
@@ -2923,23 +2924,12 @@
         processDisplayChangesLocked();
         processDisplayHotplugEventsLocked();
     }
+    mForceTraversal = false;
+    mForceTransactionDisplayChange = displayTransactionNeeded;
 
-    // Commit layer transactions. This needs to happen after display transactions are
-    // committed because some geometry logic relies on display orientation.
-    if ((transactionFlags & eTraversalNeeded) || mForceTraversal || displayTransactionNeeded) {
-        mForceTraversal = false;
-        mCurrentState.traverse([&](Layer* layer) {
-            uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
-            if (!trFlags && !displayTransactionNeeded) return;
-
-            const uint32_t flags = layer->doTransaction(0);
-            if (flags & Layer::eVisibleRegion)
-                mVisibleRegionsDirty = true;
-
-            if (flags & Layer::eInputInfoChanged) {
-                mInputInfoChanged = true;
-            }
-        });
+    if (mSomeChildrenChanged) {
+        mVisibleRegionsDirty = true;
+        mSomeChildrenChanged = false;
     }
 
     // Update transform hint
@@ -3164,7 +3154,6 @@
     mRegionSamplingThread =
             new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables());
     mFpsReporter = new FpsReporter(*mFrameTimeline, *this);
-    mTunnelModeEnabledReporter = new TunnelModeEnabledReporter(*this);
     // Dispatch a mode change request for the primary display on scheduler
     // initialization, so that the EventThreads always contain a reference to a
     // prior configuration.
@@ -3235,9 +3224,12 @@
     // clear the "changed" flags in current state
     mCurrentState.colorMatrixChanged = false;
 
-    for (const auto& rootLayer : mDrawingState.layersSortedByZ) {
-        rootLayer->commitChildList();
+    if (mVisibleRegionsDirty) {
+        for (const auto& rootLayer : mDrawingState.layersSortedByZ) {
+            rootLayer->commitChildList();
+        }
     }
+
     // TODO(b/163019109): See if this traversal is needed at all...
     if (!mOffscreenLayers.empty()) {
         mDrawingState.traverse([&](Layer* layer) {
@@ -3296,7 +3288,14 @@
     // Display is now waiting on Layer 1's frame, which is behind layer 0's
     // second frame. But layer 0's second frame could be waiting on display.
     mDrawingState.traverse([&](Layer* layer) {
-        if (layer->hasReadyFrame()) {
+         uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
+         if (trFlags || mForceTransactionDisplayChange) {
+             const uint32_t flags = layer->doTransaction(0);
+             if (flags & Layer::eVisibleRegion)
+                 mVisibleRegionsDirty = true;
+         }
+
+         if (layer->hasReadyFrame()) {
             frameQueued = true;
             if (layer->shouldPresentNow(expectedPresentTime)) {
                 mLayersWithQueuedFrames.emplace(layer);
@@ -3304,10 +3303,11 @@
                 ATRACE_NAME("!layer->shouldPresentNow()");
                 layer->useEmptyDamage();
             }
-        } else {
+         } else {
             layer->useEmptyDamage();
         }
     });
+    mForceTransactionDisplayChange = false;
 
     // The client can continue submitting buffers for offscreen layers, but they will not
     // be shown on screen. Therefore, we need to latch and release buffers of offscreen
@@ -4468,10 +4468,11 @@
     d.height = 0;
     displays.add(d);
 
+    nsecs_t now = systemTime();
     // It should be on the main thread, apply it directly.
     applyTransactionState(FrameTimelineInfo{}, state, displays, 0, mInputWindowCommands,
-                          systemTime(), true, {}, systemTime(), true, false, {}, getpid(), getuid(),
-                          0 /* Undefined transactionId */);
+                          /* desiredPresentTime */ now, true, {}, /* postTime */ now, true, false,
+                          {}, getpid(), getuid(), 0 /* Undefined transactionId */);
 
     setPowerModeInternal(display, hal::PowerMode::ON);
     const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
@@ -6908,6 +6909,8 @@
         parent->addChild(layer);
     }
 
+    layer->updateTransformHint(mDefaultDisplayTransformHint);
+
     if (state->initialProducer != nullptr) {
         mGraphicBufferProducerList.insert(state->initialProducer);
         LOG_ALWAYS_FATAL_IF(mGraphicBufferProducerList.size() > mMaxGraphicBufferProducerListSize,
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 22d17eb..f33df86 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -364,6 +364,7 @@
     // For unit tests
     friend class TestableSurfaceFlinger;
     friend class TransactionApplicationTest;
+    friend class TunnelModeEnabledReporterTest;
 
     using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
     using VsyncModulator = scheduler::VsyncModulator;
@@ -1228,8 +1229,14 @@
     // don't need synchronization
     State mDrawingState{LayerVector::StateSet::Drawing};
     bool mVisibleRegionsDirty = false;
-    // Set during transaction commit stage to track if the input info for a layer has changed.
+
+    // Set during transaction application stage to track if the input info or children
+    // for a layer has changed.
+    // TODO: Also move visibleRegions over to a boolean system.
     bool mInputInfoChanged = false;
+    bool mSomeChildrenChanged;
+    bool mForceTransactionDisplayChange = false;
+
     bool mGeometryInvalid = false;
     bool mAnimCompositionPending = false;
 
diff --git a/services/surfaceflinger/TunnelModeEnabledReporter.cpp b/services/surfaceflinger/TunnelModeEnabledReporter.cpp
index 48e3216..4497caf 100644
--- a/services/surfaceflinger/TunnelModeEnabledReporter.cpp
+++ b/services/surfaceflinger/TunnelModeEnabledReporter.cpp
@@ -26,17 +26,10 @@
 
 namespace android {
 
-TunnelModeEnabledReporter::TunnelModeEnabledReporter(SurfaceFlinger& flinger) : mFlinger(flinger) {}
+TunnelModeEnabledReporter::TunnelModeEnabledReporter() {}
 
 void TunnelModeEnabledReporter::updateTunnelModeStatus() {
-    bool tunnelModeEnabled = false;
-    mFlinger.mCurrentState.traverse([&](Layer* layer) {
-        auto& state = layer->getDrawingState();
-        if (state.sidebandStream != nullptr) {
-            tunnelModeEnabled = true;
-            return;
-        }
-    });
+    bool tunnelModeEnabled = mTunnelModeCount > 0;
     dispatchTunnelModeEnabled(tunnelModeEnabled);
 }
 
diff --git a/services/surfaceflinger/TunnelModeEnabledReporter.h b/services/surfaceflinger/TunnelModeEnabledReporter.h
index d55507a..935502a 100644
--- a/services/surfaceflinger/TunnelModeEnabledReporter.h
+++ b/services/surfaceflinger/TunnelModeEnabledReporter.h
@@ -29,7 +29,7 @@
 
 class TunnelModeEnabledReporter : public IBinder::DeathRecipient {
 public:
-    TunnelModeEnabledReporter(SurfaceFlinger& flinger);
+    TunnelModeEnabledReporter();
 
     // Checks if there is a tunnel mode enabled state change and if so, dispatches the updated
     // tunnel mode enabled/disabled state to the registered listeners
@@ -49,6 +49,9 @@
     // Deregisters a TunnelModeEnabled listener
     void removeListener(const sp<gui::ITunnelModeEnabledListener>& listener);
 
+    inline void incrementTunnelModeCount() { mTunnelModeCount++; }
+    inline void decrementTunnelModeCount() { mTunnelModeCount--; }
+
 private:
     mutable std::mutex mMutex;
     struct WpHash {
@@ -57,10 +60,10 @@
         }
     };
 
-    SurfaceFlinger& mFlinger;
     std::unordered_map<wp<IBinder>, sp<gui::ITunnelModeEnabledListener>, WpHash> mListeners
             GUARDED_BY(mMutex);
     bool mTunnelModeEnabled GUARDED_BY(mMutex) = false;
+    uint32_t mTunnelModeCount = 0;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
index d7d7ea7..e4f7469 100644
--- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
@@ -27,6 +27,7 @@
 #include "TunnelModeEnabledReporter.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/MockEventThread.h"
+#include "mock/MockMessageQueue.h"
 
 namespace android {
 
@@ -71,15 +72,20 @@
     sp<TestableTunnelModeEnabledListener> mTunnelModeEnabledListener =
             new TestableTunnelModeEnabledListener();
     sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter =
-            new TunnelModeEnabledReporter(*(mFlinger.flinger()));
+            new TunnelModeEnabledReporter();
+
+    mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
 };
 
 TunnelModeEnabledReporterTest::TunnelModeEnabledReporterTest() {
     const ::testing::TestInfo* const test_info =
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+    mFlinger.mutableEventQueue().reset(mMessageQueue);
     setupScheduler();
     mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+    mFlinger.flinger()->mTunnelModeEnabledReporter = mTunnelModeEnabledReporter;
     mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(false);
 }
 
@@ -156,16 +162,18 @@
     sp<NativeHandle> stream =
             NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
                                  false);
-    mFlinger.setLayerSidebandStream(layer, stream);
+    layer->setSidebandStream(stream);
     mFlinger.mutableCurrentState().layersSortedByZ.add(layer);
     mTunnelModeEnabledReporter->updateTunnelModeStatus();
     mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener);
     EXPECT_EQ(true, mTunnelModeEnabledListener->mTunnelModeEnabled);
     mTunnelModeEnabledReporter->removeListener(mTunnelModeEnabledListener);
-
     mFlinger.mutableCurrentState().layersSortedByZ.remove(layer);
+    layer = nullptr;
+
     mTunnelModeEnabledReporter->updateTunnelModeStatus();
     mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener);
+
     EXPECT_EQ(false, mTunnelModeEnabledListener->mTunnelModeEnabled);
 }
 
@@ -178,7 +186,7 @@
     sp<NativeHandle> stream =
             NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
                                  false);
-    mFlinger.setLayerSidebandStream(layerWithSidebandStream, stream);
+    layerWithSidebandStream->setSidebandStream(stream);
 
     mFlinger.mutableCurrentState().layersSortedByZ.add(simpleLayer);
     mFlinger.mutableCurrentState().layersSortedByZ.add(layerWithSidebandStream);
@@ -186,6 +194,7 @@
     EXPECT_EQ(true, mTunnelModeEnabledListener->mTunnelModeEnabled);
 
     mFlinger.mutableCurrentState().layersSortedByZ.remove(layerWithSidebandStream);
+    layerWithSidebandStream = nullptr;
     mTunnelModeEnabledReporter->updateTunnelModeStatus();
     EXPECT_EQ(false, mTunnelModeEnabledListener->mTunnelModeEnabled);
 }
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index f15a963..a375808 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -135,6 +135,18 @@
     if (mInfoCache.mSupportedBraking.isFailed()) {
         mInfoCache.mSupportedBraking = getSupportedBrakingInternal();
     }
+    if (mInfoCache.mPrimitiveDelayMax.isFailed()) {
+        mInfoCache.mPrimitiveDelayMax = getPrimitiveDelayMaxInternal();
+    }
+    if (mInfoCache.mPwlePrimitiveDurationMax.isFailed()) {
+        mInfoCache.mPwlePrimitiveDurationMax = getPrimitiveDurationMaxInternal();
+    }
+    if (mInfoCache.mCompositionSizeMax.isFailed()) {
+        mInfoCache.mCompositionSizeMax = getCompositionSizeMaxInternal();
+    }
+    if (mInfoCache.mPwleSizeMax.isFailed()) {
+        mInfoCache.mPwleSizeMax = getPwleSizeMaxInternal();
+    }
     if (mInfoCache.mMinFrequency.isFailed()) {
         mInfoCache.mMinFrequency = getMinFrequencyInternal();
     }
@@ -209,6 +221,26 @@
     return HalResult<std::vector<milliseconds>>::unsupported();
 }
 
+HalResult<milliseconds> HalWrapper::getPrimitiveDelayMaxInternal() {
+    ALOGV("Skipped getPrimitiveDelayMaxInternal because it's not available in Vibrator HAL");
+    return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<milliseconds> HalWrapper::getPrimitiveDurationMaxInternal() {
+    ALOGV("Skipped getPrimitiveDurationMaxInternal because it's not available in Vibrator HAL");
+    return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<int32_t> HalWrapper::getCompositionSizeMaxInternal() {
+    ALOGV("Skipped getCompositionSizeMaxInternal because it's not available in Vibrator HAL");
+    return HalResult<int32_t>::unsupported();
+}
+
+HalResult<int32_t> HalWrapper::getPwleSizeMaxInternal() {
+    ALOGV("Skipped getPwleSizeMaxInternal because it's not available in Vibrator HAL");
+    return HalResult<int32_t>::unsupported();
+}
+
 HalResult<float> HalWrapper::getMinFrequencyInternal() {
     ALOGV("Skipped getMinFrequency because it's not available in Vibrator HAL");
     return HalResult<float>::unsupported();
@@ -383,6 +415,30 @@
     return HalResult<std::vector<milliseconds>>::ok(durations);
 }
 
+HalResult<milliseconds> AidlHalWrapper::getPrimitiveDelayMaxInternal() {
+    int32_t delay = 0;
+    auto result = getHal()->getCompositionDelayMax(&delay);
+    return HalResult<milliseconds>::fromStatus(result, milliseconds(delay));
+}
+
+HalResult<milliseconds> AidlHalWrapper::getPrimitiveDurationMaxInternal() {
+    int32_t delay = 0;
+    auto result = getHal()->getPwlePrimitiveDurationMax(&delay);
+    return HalResult<milliseconds>::fromStatus(result, milliseconds(delay));
+}
+
+HalResult<int32_t> AidlHalWrapper::getCompositionSizeMaxInternal() {
+    int32_t size = 0;
+    auto result = getHal()->getCompositionSizeMax(&size);
+    return HalResult<int32_t>::fromStatus(result, size);
+}
+
+HalResult<int32_t> AidlHalWrapper::getPwleSizeMaxInternal() {
+    int32_t size = 0;
+    auto result = getHal()->getPwleCompositionSizeMax(&size);
+    return HalResult<int32_t>::fromStatus(result, size);
+}
+
 HalResult<float> AidlHalWrapper::getMinFrequencyInternal() {
     float minFrequency = 0;
     auto result = getHal()->getFrequencyMinimum(&minFrequency);
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 87bc34e..68d6647 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -182,6 +182,10 @@
     const HalResult<std::vector<hardware::vibrator::Braking>> supportedBraking;
     const HalResult<std::vector<hardware::vibrator::CompositePrimitive>> supportedPrimitives;
     const HalResult<std::vector<std::chrono::milliseconds>> primitiveDurations;
+    const HalResult<std::chrono::milliseconds> primitiveDelayMax;
+    const HalResult<std::chrono::milliseconds> pwlePrimitiveDurationMax;
+    const HalResult<int32_t> compositionSizeMax;
+    const HalResult<int32_t> pwleSizeMax;
     const HalResult<float> minFrequency;
     const HalResult<float> resonantFrequency;
     const HalResult<float> frequencyResolution;
@@ -194,6 +198,10 @@
                 supportedBraking.checkAndLogFailure("getSupportedBraking") ||
                 supportedPrimitives.checkAndLogFailure("getSupportedPrimitives") ||
                 primitiveDurations.checkAndLogFailure("getPrimitiveDuration") ||
+                primitiveDelayMax.checkAndLogFailure("getPrimitiveDelayMax") ||
+                pwlePrimitiveDurationMax.checkAndLogFailure("getPwlePrimitiveDurationMax") ||
+                compositionSizeMax.checkAndLogFailure("getCompositionSizeMax") ||
+                pwleSizeMax.checkAndLogFailure("getPwleSizeMax") ||
                 minFrequency.checkAndLogFailure("getMinFrequency") ||
                 resonantFrequency.checkAndLogFailure("getResonantFrequency") ||
                 frequencyResolution.checkAndLogFailure("getFrequencyResolution") ||
@@ -205,9 +213,19 @@
 class InfoCache {
 public:
     Info get() {
-        return {mCapabilities,        mSupportedEffects,    mSupportedBraking,
-                mSupportedPrimitives, mPrimitiveDurations,  mMinFrequency,
-                mResonantFrequency,   mFrequencyResolution, mQFactor,
+        return {mCapabilities,
+                mSupportedEffects,
+                mSupportedBraking,
+                mSupportedPrimitives,
+                mPrimitiveDurations,
+                mPrimitiveDelayMax,
+                mPwlePrimitiveDurationMax,
+                mCompositionSizeMax,
+                mPwleSizeMax,
+                mMinFrequency,
+                mResonantFrequency,
+                mFrequencyResolution,
+                mQFactor,
                 mMaxAmplitudes};
     }
 
@@ -222,6 +240,12 @@
             HalResult<std::vector<hardware::vibrator::CompositePrimitive>>::failed(MSG);
     HalResult<std::vector<std::chrono::milliseconds>> mPrimitiveDurations =
             HalResult<std::vector<std::chrono::milliseconds>>::failed(MSG);
+    HalResult<std::chrono::milliseconds> mPrimitiveDelayMax =
+            HalResult<std::chrono::milliseconds>::failed(MSG);
+    HalResult<std::chrono::milliseconds> mPwlePrimitiveDurationMax =
+            HalResult<std::chrono::milliseconds>::failed(MSG);
+    HalResult<int32_t> mCompositionSizeMax = HalResult<int>::failed(MSG);
+    HalResult<int32_t> mPwleSizeMax = HalResult<int>::failed(MSG);
     HalResult<float> mMinFrequency = HalResult<float>::failed(MSG);
     HalResult<float> mResonantFrequency = HalResult<float>::failed(MSG);
     HalResult<float> mFrequencyResolution = HalResult<float>::failed(MSG);
@@ -285,6 +309,10 @@
     getSupportedPrimitivesInternal();
     virtual HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal(
             const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives);
+    virtual HalResult<std::chrono::milliseconds> getPrimitiveDelayMaxInternal();
+    virtual HalResult<std::chrono::milliseconds> getPrimitiveDurationMaxInternal();
+    virtual HalResult<int32_t> getCompositionSizeMaxInternal();
+    virtual HalResult<int32_t> getPwleSizeMaxInternal();
     virtual HalResult<float> getMinFrequencyInternal();
     virtual HalResult<float> getResonantFrequencyInternal();
     virtual HalResult<float> getFrequencyResolutionInternal();
@@ -347,6 +375,10 @@
     HalResult<std::vector<std::chrono::milliseconds>> getPrimitiveDurationsInternal(
             const std::vector<hardware::vibrator::CompositePrimitive>& supportedPrimitives)
             override final;
+    HalResult<std::chrono::milliseconds> getPrimitiveDelayMaxInternal() override final;
+    HalResult<std::chrono::milliseconds> getPrimitiveDurationMaxInternal() override final;
+    HalResult<int32_t> getCompositionSizeMaxInternal() override final;
+    HalResult<int32_t> getPwleSizeMaxInternal() override final;
     HalResult<float> getMinFrequencyInternal() override final;
     HalResult<float> getResonantFrequencyInternal() override final;
     HalResult<float> getFrequencyResolutionInternal() override final;
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index 7813303..03c9e77 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -301,6 +301,10 @@
     constexpr float F0 = 123.f;
     constexpr float F_RESOLUTION = 0.5f;
     constexpr float Q_FACTOR = 123.f;
+    constexpr int32_t COMPOSITION_SIZE_MAX = 10;
+    constexpr int32_t PWLE_SIZE_MAX = 20;
+    constexpr int32_t PRIMITIVE_DELAY_MAX = 100;
+    constexpr int32_t PWLE_DURATION_MAX = 200;
     std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK};
     std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK};
     std::vector<Braking> supportedBraking = {Braking::CLAB};
@@ -331,6 +335,22 @@
     EXPECT_CALL(*mMockHal.get(), getPrimitiveDuration(Eq(CompositePrimitive::CLICK), _))
             .Times(Exactly(1))
             .WillRepeatedly(DoAll(SetArgPointee<1>(10), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getCompositionSizeMax(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(COMPOSITION_SIZE_MAX), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getCompositionDelayMax(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(PRIMITIVE_DELAY_MAX), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getPwlePrimitiveDurationMax(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_DURATION_MAX), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getPwleCompositionSizeMax(_))
+            .Times(Exactly(2))
+            .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_SIZE_MAX), Return(Status())));
     EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_))
             .Times(Exactly(2))
             .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY)))
@@ -358,6 +378,10 @@
     ASSERT_TRUE(failed.supportedBraking.isFailed());
     ASSERT_TRUE(failed.supportedPrimitives.isFailed());
     ASSERT_TRUE(failed.primitiveDurations.isFailed());
+    ASSERT_TRUE(failed.primitiveDelayMax.isFailed());
+    ASSERT_TRUE(failed.pwlePrimitiveDurationMax.isFailed());
+    ASSERT_TRUE(failed.compositionSizeMax.isFailed());
+    ASSERT_TRUE(failed.pwleSizeMax.isFailed());
     ASSERT_TRUE(failed.minFrequency.isFailed());
     ASSERT_TRUE(failed.resonantFrequency.isFailed());
     ASSERT_TRUE(failed.frequencyResolution.isFailed());
@@ -370,6 +394,11 @@
     ASSERT_EQ(supportedBraking, successful.supportedBraking.value());
     ASSERT_EQ(supportedPrimitives, successful.supportedPrimitives.value());
     ASSERT_EQ(primitiveDurations, successful.primitiveDurations.value());
+    ASSERT_EQ(std::chrono::milliseconds(PRIMITIVE_DELAY_MAX), successful.primitiveDelayMax.value());
+    ASSERT_EQ(std::chrono::milliseconds(PWLE_DURATION_MAX),
+              successful.pwlePrimitiveDurationMax.value());
+    ASSERT_EQ(COMPOSITION_SIZE_MAX, successful.compositionSizeMax.value());
+    ASSERT_EQ(PWLE_SIZE_MAX, successful.pwleSizeMax.value());
     ASSERT_EQ(F_MIN, successful.minFrequency.value());
     ASSERT_EQ(F0, successful.resonantFrequency.value());
     ASSERT_EQ(F_RESOLUTION, successful.frequencyResolution.value());
@@ -380,6 +409,10 @@
 TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) {
     constexpr float F_MIN = 100.f;
     constexpr float F0 = 123.f;
+    constexpr int32_t COMPOSITION_SIZE_MAX = 10;
+    constexpr int32_t PWLE_SIZE_MAX = 20;
+    constexpr int32_t PRIMITIVE_DELAY_MAX = 100;
+    constexpr int32_t PWLE_DURATION_MAX = 200;
     std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK};
 
     EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
@@ -395,6 +428,18 @@
     EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_))
             .Times(Exactly(1))
             .WillRepeatedly(Return(Status::fromStatusT(UNKNOWN_TRANSACTION)));
+    EXPECT_CALL(*mMockHal.get(), getCompositionSizeMax(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(COMPOSITION_SIZE_MAX), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getCompositionDelayMax(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(PRIMITIVE_DELAY_MAX), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getPwlePrimitiveDurationMax(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_DURATION_MAX), Return(Status())));
+    EXPECT_CALL(*mMockHal.get(), getPwleCompositionSizeMax(_))
+            .Times(Exactly(1))
+            .WillRepeatedly(DoAll(SetArgPointee<0>(PWLE_SIZE_MAX), Return(Status())));
     EXPECT_CALL(*mMockHal.get(), getFrequencyMinimum(_))
             .Times(Exactly(1))
             .WillRepeatedly(DoAll(SetArgPointee<0>(F_MIN), Return(Status())));
@@ -426,6 +471,10 @@
     ASSERT_TRUE(info.supportedBraking.isUnsupported());
     ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
     ASSERT_TRUE(info.primitiveDurations.isUnsupported());
+    ASSERT_EQ(std::chrono::milliseconds(PRIMITIVE_DELAY_MAX), info.primitiveDelayMax.value());
+    ASSERT_EQ(std::chrono::milliseconds(PWLE_DURATION_MAX), info.pwlePrimitiveDurationMax.value());
+    ASSERT_EQ(COMPOSITION_SIZE_MAX, info.compositionSizeMax.value());
+    ASSERT_EQ(PWLE_SIZE_MAX, info.pwleSizeMax.value());
     ASSERT_EQ(F_MIN, info.minFrequency.value());
     ASSERT_EQ(F0, info.resonantFrequency.value());
     ASSERT_TRUE(info.frequencyResolution.isUnsupported());
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
index 96b2582..0c27fc7 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -206,6 +206,10 @@
     ASSERT_TRUE(info.supportedBraking.isUnsupported());
     ASSERT_TRUE(info.supportedPrimitives.isUnsupported());
     ASSERT_TRUE(info.primitiveDurations.isUnsupported());
+    ASSERT_TRUE(info.primitiveDelayMax.isUnsupported());
+    ASSERT_TRUE(info.pwlePrimitiveDurationMax.isUnsupported());
+    ASSERT_TRUE(info.compositionSizeMax.isUnsupported());
+    ASSERT_TRUE(info.pwleSizeMax.isUnsupported());
     ASSERT_TRUE(info.minFrequency.isUnsupported());
     ASSERT_TRUE(info.resonantFrequency.isUnsupported());
     ASSERT_TRUE(info.frequencyResolution.isUnsupported());