Merge "Surface: Release references to BlastBufferQueue and SurfaceControl on Surface#destroy" into sc-v2-dev
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index d54de49..7f0cac5 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -343,80 +343,6 @@
     return nullptr;
 }
 
-bool GraphicsEnv::checkAngleRules(void* so) {
-    auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET");
-    auto model = base::GetProperty("ro.product.model", "UNSET");
-
-    auto ANGLEGetFeatureSupportUtilAPIVersion =
-            (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so,
-                                                          "ANGLEGetFeatureSupportUtilAPIVersion");
-
-    if (!ANGLEGetFeatureSupportUtilAPIVersion) {
-        ALOGW("Cannot find ANGLEGetFeatureSupportUtilAPIVersion function");
-        return false;
-    }
-
-    // Negotiate the interface version by requesting most recent known to the platform
-    unsigned int versionToUse = CURRENT_ANGLE_API_VERSION;
-    if (!(ANGLEGetFeatureSupportUtilAPIVersion)(&versionToUse)) {
-        ALOGW("Cannot use ANGLE feature-support library, it is older than supported by EGL, "
-              "requested version %u",
-              versionToUse);
-        return false;
-    }
-
-    // Add and remove versions below as needed
-    bool useAngle = false;
-    switch (versionToUse) {
-        case 2: {
-            ALOGV("Using version %d of ANGLE feature-support library", versionToUse);
-            void* rulesHandle = nullptr;
-            int rulesVersion = 0;
-            void* systemInfoHandle = nullptr;
-
-            // Get the symbols for the feature-support-utility library:
-#define GET_SYMBOL(symbol)                                                 \
-    fp##symbol symbol = (fp##symbol)dlsym(so, #symbol);                    \
-    if (!symbol) {                                                         \
-        ALOGW("Cannot find " #symbol " in ANGLE feature-support library"); \
-        break;                                                             \
-    }
-            GET_SYMBOL(ANGLEAndroidParseRulesString);
-            GET_SYMBOL(ANGLEGetSystemInfo);
-            GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo);
-            GET_SYMBOL(ANGLEShouldBeUsedForApplication);
-            GET_SYMBOL(ANGLEFreeRulesHandle);
-            GET_SYMBOL(ANGLEFreeSystemInfoHandle);
-
-            // Parse the rules, obtain the SystemInfo, and evaluate the
-            // application against the rules:
-            if (!(ANGLEAndroidParseRulesString)(mRulesBuffer.data(), &rulesHandle, &rulesVersion)) {
-                ALOGW("ANGLE feature-support library cannot parse rules file");
-                break;
-            }
-            if (!(ANGLEGetSystemInfo)(&systemInfoHandle)) {
-                ALOGW("ANGLE feature-support library cannot obtain SystemInfo");
-                break;
-            }
-            if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(),
-                                                  systemInfoHandle)) {
-                ALOGW("ANGLE feature-support library cannot add device info to SystemInfo");
-                break;
-            }
-            useAngle = (ANGLEShouldBeUsedForApplication)(rulesHandle, rulesVersion,
-                                                         systemInfoHandle, mAngleAppName.c_str());
-            (ANGLEFreeRulesHandle)(rulesHandle);
-            (ANGLEFreeSystemInfoHandle)(systemInfoHandle);
-        } break;
-
-        default:
-            ALOGW("Version %u of ANGLE feature-support library is NOT supported.", versionToUse);
-    }
-
-    ALOGV("Close temporarily-loaded ANGLE opt-in/out logic");
-    return useAngle;
-}
-
 bool GraphicsEnv::shouldUseAngle(std::string appName) {
     if (appName != mAngleAppName) {
         // Make sure we are checking the app we were init'ed for
@@ -444,31 +370,20 @@
     const char* ANGLE_PREFER_ANGLE = "angle";
     const char* ANGLE_PREFER_NATIVE = "native";
 
+    mUseAngle = NO;
     if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) {
         ALOGV("User set \"Developer Options\" to force the use of ANGLE");
         mUseAngle = YES;
     } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) {
         ALOGV("User set \"Developer Options\" to force the use of Native");
-        mUseAngle = NO;
     } else {
-        // The "Developer Options" value wasn't set to force the use of ANGLE.  Need to temporarily
-        // load ANGLE and call the updatable opt-in/out logic:
-        void* featureSo = loadLibrary("feature_support");
-        if (featureSo) {
-            ALOGV("loaded ANGLE's opt-in/out logic from namespace");
-            mUseAngle = checkAngleRules(featureSo) ? YES : NO;
-            dlclose(featureSo);
-            featureSo = nullptr;
-        } else {
-            ALOGV("Could not load the ANGLE opt-in/out logic, cannot use ANGLE.");
-        }
+        ALOGV("User set invalid \"Developer Options\": '%s'", mAngleDeveloperOptIn.c_str());
     }
 }
 
 void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName,
                                const std::string developerOptIn,
-                               const std::vector<std::string> eglFeatures, const int rulesFd,
-                               const long rulesOffset, const long rulesLength) {
+                               const std::vector<std::string> eglFeatures) {
     if (mUseAngle != UNKNOWN) {
         // We've already figured out an answer for this app, so just return.
         ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(),
@@ -485,22 +400,6 @@
     ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str());
     mAngleDeveloperOptIn = developerOptIn;
 
-    lseek(rulesFd, rulesOffset, SEEK_SET);
-    mRulesBuffer = std::vector<char>(rulesLength + 1);
-    ssize_t numBytesRead = read(rulesFd, mRulesBuffer.data(), rulesLength);
-    if (numBytesRead < 0) {
-        ALOGE("Cannot read rules file: numBytesRead = %zd", numBytesRead);
-        numBytesRead = 0;
-    } else if (numBytesRead == 0) {
-        ALOGW("Empty rules file");
-    }
-    if (numBytesRead != rulesLength) {
-        ALOGW("Did not read all of the necessary bytes from the rules file."
-              "expected: %ld, got: %zd",
-              rulesLength, numBytesRead);
-    }
-    mRulesBuffer[numBytesRead] = '\0';
-
     // Update the current status of whether we should use ANGLE or not
     updateUseAngle();
 }
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 900fc49..56d1139 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -97,8 +97,7 @@
     // in the search path must have a '!' after the zip filename, e.g.
     //     /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a
     void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn,
-                      const std::vector<std::string> eglFeatures, const int rulesFd,
-                      const long rulesOffset, const long rulesLength);
+                      const std::vector<std::string> eglFeatures);
     // Get the ANGLE driver namespace.
     android_namespace_t* getAngleNamespace();
     // Get the app name for ANGLE debug message.
@@ -129,8 +128,6 @@
 
     // Load requested ANGLE library.
     void* loadLibrary(std::string name);
-    // Check ANGLE support with the rules.
-    bool checkAngleRules(void* so);
     // Update whether ANGLE should be used.
     void updateUseAngle();
     // Link updatable driver namespace with llndk and vndk-sp libs.
@@ -159,8 +156,6 @@
     std::string mAngleDeveloperOptIn;
     // ANGLE EGL features;
     std::vector<std::string> mAngleEglFeatures;
-    // ANGLE rules.
-    std::vector<char> mRulesBuffer;
     // Use ANGLE flag.
     UseAngle mUseAngle = UNKNOWN;
     // Vulkan debug layers libs.
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index b681a0f..d1f57b0 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -118,12 +118,12 @@
 }
 
 void BLASTBufferItemConsumer::setBlastBufferQueue(BLASTBufferQueue* blastbufferqueue) {
-    Mutex::Autolock lock(mMutex);
+    std::scoped_lock lock(mBufferQueueMutex);
     mBLASTBufferQueue = blastbufferqueue;
 }
 
 void BLASTBufferItemConsumer::onSidebandStreamChanged() {
-    Mutex::Autolock lock(mMutex);
+    std::scoped_lock lock(mBufferQueueMutex);
     if (mBLASTBufferQueue != nullptr) {
         sp<NativeHandle> stream = getSidebandStream();
         mBLASTBufferQueue->setSidebandStream(stream);
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index ea9b1c6..6c5b2aa 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -62,11 +62,12 @@
     uint64_t mCurrentFrameNumber = 0;
 
     Mutex mMutex;
+    std::mutex mBufferQueueMutex;
     ConsumerFrameEventHistory mFrameEventHistory GUARDED_BY(mMutex);
     std::queue<uint64_t> mDisconnectEvents GUARDED_BY(mMutex);
     bool mCurrentlyConnected GUARDED_BY(mMutex);
     bool mPreviouslyConnected GUARDED_BY(mMutex);
-    BLASTBufferQueue* mBLASTBufferQueue GUARDED_BY(mMutex);
+    BLASTBufferQueue* mBLASTBufferQueue GUARDED_BY(mBufferQueueMutex);
 };
 
 class BLASTBufferQueue
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index af02844..f6fa7a1 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -470,6 +470,23 @@
     getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientationAware"),
                                                          mParameters.orientationAware);
 
+    mParameters.orientation = Parameters::Orientation::ORIENTATION_0;
+    String8 orientationString;
+    if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientation"),
+                                                             orientationString)) {
+        if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
+            ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
+        } else if (orientationString == "ORIENTATION_90") {
+            mParameters.orientation = Parameters::Orientation::ORIENTATION_90;
+        } else if (orientationString == "ORIENTATION_180") {
+            mParameters.orientation = Parameters::Orientation::ORIENTATION_180;
+        } else if (orientationString == "ORIENTATION_270") {
+            mParameters.orientation = Parameters::Orientation::ORIENTATION_270;
+        } else if (orientationString != "ORIENTATION_0") {
+            ALOGW("Invalid value for touch.orientation: '%s'", orientationString.string());
+        }
+    }
+
     mParameters.hasAssociatedDisplay = false;
     mParameters.associatedDisplayIsExternal = false;
     if (mParameters.orientationAware ||
@@ -508,6 +525,7 @@
                          toString(mParameters.associatedDisplayIsExternal),
                          mParameters.uniqueDisplayId.c_str());
     dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
+    dump += INDENT4 "Orientation: " + NamedEnum::string(mParameters.orientation) + "\n";
 }
 
 void TouchInputMapper::configureRawPointerAxes() {
@@ -669,7 +687,13 @@
             int32_t naturalPhysicalWidth, naturalPhysicalHeight;
             int32_t naturalPhysicalLeft, naturalPhysicalTop;
             int32_t naturalDeviceWidth, naturalDeviceHeight;
-            switch (mViewport.orientation) {
+
+            // Apply the inverse of the input device orientation so that the surface is configured
+            // in the same orientation as the device. The input device orientation will be
+            // re-applied to mSurfaceOrientation.
+            const int32_t naturalSurfaceOrientation =
+                    (mViewport.orientation - static_cast<int32_t>(mParameters.orientation) + 4) % 4;
+            switch (naturalSurfaceOrientation) {
                 case DISPLAY_ORIENTATION_90:
                     naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
                     naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
@@ -752,6 +776,10 @@
                 mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation
                                                                    : DISPLAY_ORIENTATION_0;
             }
+
+            // Apply the input device orientation for the device.
+            mSurfaceOrientation =
+                    (mSurfaceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4;
         } else {
             mPhysicalWidth = rawWidth;
             mPhysicalHeight = rawHeight;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 920f842..e104220 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -204,6 +204,15 @@
         bool hasAssociatedDisplay;
         bool associatedDisplayIsExternal;
         bool orientationAware;
+
+        enum class Orientation : int32_t {
+            ORIENTATION_0 = DISPLAY_ORIENTATION_0,
+            ORIENTATION_90 = DISPLAY_ORIENTATION_90,
+            ORIENTATION_180 = DISPLAY_ORIENTATION_180,
+            ORIENTATION_270 = DISPLAY_ORIENTATION_270,
+        };
+        Orientation orientation;
+
         bool hasButtonUnderPad;
         std::string uniqueDisplayId;
 
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 43f14bd..91da0d5 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -4659,6 +4659,8 @@
     void prepareLocationCalibration();
     int32_t toRawX(float displayX);
     int32_t toRawY(float displayY);
+    int32_t toRotatedRawX(float displayX);
+    int32_t toRotatedRawY(float displayY);
     float toCookedX(float rawX, float rawY);
     float toCookedY(float rawX, float rawY);
     float toDisplayX(int32_t rawX);
@@ -4741,6 +4743,14 @@
     return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_HEIGHT + RAW_Y_MIN);
 }
 
+int32_t TouchInputMapperTest::toRotatedRawX(float displayX) {
+    return int32_t(displayX * (RAW_X_MAX - RAW_X_MIN + 1) / DISPLAY_HEIGHT + RAW_X_MIN);
+}
+
+int32_t TouchInputMapperTest::toRotatedRawY(float displayY) {
+    return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_WIDTH + RAW_Y_MIN);
+}
+
 float TouchInputMapperTest::toCookedX(float rawX, float rawY) {
     AFFINE_TRANSFORM.applyTo(rawX, rawY);
     return rawX;
@@ -5487,6 +5497,172 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
 }
 
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation0_RotatesMotions) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareButtons();
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.orientationAware", "1");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_0");
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    NotifyMotionArgs args;
+
+    // Orientation 0.
+    processDown(mapper, toRawX(50), toRawY(75));
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation90_RotatesMotions) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareButtons();
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.orientationAware", "1");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_90");
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    NotifyMotionArgs args;
+
+    // Orientation 90.
+    processDown(mapper, RAW_X_MAX - toRotatedRawX(75) + RAW_X_MIN, toRotatedRawY(50));
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation180_RotatesMotions) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareButtons();
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.orientationAware", "1");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_180");
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    NotifyMotionArgs args;
+
+    // Orientation 180.
+    processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN);
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation270_RotatesMotions) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareButtons();
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.orientationAware", "1");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_270");
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+    NotifyMotionArgs args;
+
+    // Orientation 270.
+    processDown(mapper, toRotatedRawX(75), RAW_Y_MAX - toRotatedRawY(50) + RAW_Y_MIN);
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationSpecified_RotatesMotionWithDisplay) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareButtons();
+    prepareAxes(POSITION);
+    // Since InputReader works in the un-rotated coordinate space, only devices that are not
+    // orientation-aware are affected by display rotation.
+    addConfigurationProperty("touch.orientationAware", "0");
+    addConfigurationProperty("touch.orientation", "ORIENTATION_90");
+    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+    NotifyMotionArgs args;
+
+    // Orientation 90, Rotation 0.
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    processDown(mapper, RAW_X_MAX - toRotatedRawX(75) + RAW_X_MIN, toRotatedRawY(50));
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+
+    // Orientation 90, Rotation 90.
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_90);
+    processDown(mapper, toRotatedRawX(50), toRotatedRawY(75));
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+
+    // Orientation 90, Rotation 180.
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_180);
+    processDown(mapper, toRotatedRawX(75), RAW_Y_MAX - toRotatedRawY(50) + RAW_Y_MIN);
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+
+    // Orientation 90, Rotation 270.
+    clearViewports();
+    prepareDisplay(DISPLAY_ORIENTATION_270);
+    processDown(mapper, RAW_X_MAX - toRotatedRawX(50) + RAW_X_MIN,
+                RAW_Y_MAX - toRotatedRawY(75) + RAW_Y_MIN);
+    processSync(mapper);
+
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+    processUp(mapper);
+    processSync(mapper);
+    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
 TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index 8ec15ed..a040fa9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -38,35 +38,56 @@
 
 class Flattener {
 public:
-    struct CachedSetRenderSchedulingTunables {
-        // This default assumes that rendering a cached set takes about 3ms. That time is then cut
-        // in half - the next frame using the cached set would have the same workload, meaning that
-        // composition cost is the same. This is best illustrated with the following example:
-        //
-        // Suppose we're at a 120hz cadence so SurfaceFlinger is budgeted 8.3ms per-frame. If
-        // renderCachedSets costs 3ms, then two consecutive frames have timings:
-        //
-        // First frame: Start at 0ms, end at 6.8ms.
-        // renderCachedSets: Start at 6.8ms, end at 9.8ms.
-        // Second frame: Start at 9.8ms, end at 16.6ms.
-        //
-        // Now the second frame won't render a cached set afterwards, but the first frame didn't
-        // really steal time from the second frame.
-        static const constexpr std::chrono::nanoseconds kDefaultCachedSetRenderDuration = 1500us;
+    // Collection of tunables which are backed by sysprops
+    struct Tunables {
+        // Tunables that are specific to scheduling when a cached set should be rendered
+        struct RenderScheduling {
+            // This default assumes that rendering a cached set takes about 3ms. That time is then
+            // cut in half - the next frame using the cached set would have the same workload,
+            // meaning that composition cost is the same. This is best illustrated with the
+            // following example:
+            //
+            // Suppose we're at a 120hz cadence so SurfaceFlinger is budgeted 8.3ms per-frame. If
+            // renderCachedSets costs 3ms, then two consecutive frames have timings:
+            //
+            // First frame: Start at 0ms, end at 6.8ms.
+            // renderCachedSets: Start at 6.8ms, end at 9.8ms.
+            // Second frame: Start at 9.8ms, end at 16.6ms.
+            //
+            // Now the second frame won't render a cached set afterwards, but the first frame didn't
+            // really steal time from the second frame.
+            static const constexpr std::chrono::nanoseconds kDefaultCachedSetRenderDuration =
+                    1500us;
 
-        static const constexpr size_t kDefaultMaxDeferRenderAttempts = 240;
+            static const constexpr size_t kDefaultMaxDeferRenderAttempts = 240;
 
-        // Duration allocated for rendering a cached set. If we don't have enough time for rendering
-        // a cached set, then rendering is deferred to another frame.
-        const std::chrono::nanoseconds cachedSetRenderDuration;
-        // Maximum of times that we defer rendering a cached set. If we defer rendering a cached set
-        // too many times, then render it anyways so that future frames would benefit from the
-        // flattened cached set.
-        const size_t maxDeferRenderAttempts;
+            // Duration allocated for rendering a cached set. If we don't have enough time for
+            // rendering a cached set, then rendering is deferred to another frame.
+            const std::chrono::nanoseconds cachedSetRenderDuration;
+            // Maximum of times that we defer rendering a cached set. If we defer rendering a cached
+            // set too many times, then render it anyways so that future frames would benefit from
+            // the flattened cached set.
+            const size_t maxDeferRenderAttempts;
+        };
+
+        static const constexpr std::chrono::milliseconds kDefaultActiveLayerTimeout = 150ms;
+
+        static const constexpr bool kDefaultEnableHolePunch = true;
+
+        // Threshold for determing whether a layer is active. A layer whose properties, including
+        // the buffer, have not changed in at least this time is considered inactive and is
+        // therefore a candidate for flattening.
+        const std::chrono::milliseconds mActiveLayerTimeout;
+
+        // Toggles for scheduling when it's safe to render a cached set.
+        // See: RenderScheduling
+        const std::optional<RenderScheduling> mRenderScheduling;
+
+        // True if the hole punching feature should be enabled.
+        const bool mEnableHolePunch;
     };
-    Flattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch = false,
-              std::optional<CachedSetRenderSchedulingTunables> cachedSetRenderSchedulingTunables =
-                      std::nullopt);
+
+    Flattener(renderengine::RenderEngine& renderEngine, const Tunables& tunables);
 
     void setDisplaySize(ui::Size size) {
         mDisplaySize = size;
@@ -177,8 +198,7 @@
     void buildCachedSets(std::chrono::steady_clock::time_point now);
 
     renderengine::RenderEngine& mRenderEngine;
-    const bool mEnableHolePunch;
-    const std::optional<CachedSetRenderSchedulingTunables> mCachedSetRenderSchedulingTunables;
+    const Tunables mTunables;
 
     TexturePool mTexturePool;
 
@@ -202,9 +222,6 @@
     size_t mCachedSetCreationCount = 0;
     size_t mCachedSetCreationCost = 0;
     std::unordered_map<size_t, size_t> mInvalidatedCachedSetAges;
-    std::chrono::nanoseconds mActiveLayerTimeout = kActiveLayerTimeout;
-
-    static constexpr auto kActiveLayerTimeout = std::chrono::nanoseconds(150ms);
 };
 
 } // namespace compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
index 76d5e81..b7ebca6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h
@@ -39,6 +39,9 @@
 // heuristically determining the composition strategy of the current layer stack,
 // and flattens inactive layers into an override buffer so it can be used
 // as a more efficient representation of parts of the layer stack.
+// Implicitly, layer caching must also be enabled for the Planner to have any effect
+// E.g., setprop debug.sf.enable_layer_caching 1, or
+// adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>]
 class Planner {
 public:
     Planner(renderengine::RenderEngine& renderengine);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 3310a71..95ae5e5 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -789,6 +789,9 @@
         if (compState->sidebandStream != nullptr) {
             return nullptr;
         }
+        if (compState->isOpaque) {
+            continue;
+        }
         if (compState->backgroundBlurRadius > 0 || compState->blurRegions.size() > 0) {
             layerRequestingBgComposition = layer;
         }
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 8e2c182..ad5e931 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -60,19 +60,8 @@
 
 } // namespace
 
-Flattener::Flattener(
-        renderengine::RenderEngine& renderEngine, bool enableHolePunch,
-        std::optional<CachedSetRenderSchedulingTunables> cachedSetRenderSchedulingTunables)
-      : mRenderEngine(renderEngine),
-        mEnableHolePunch(enableHolePunch),
-        mCachedSetRenderSchedulingTunables(cachedSetRenderSchedulingTunables),
-        mTexturePool(mRenderEngine) {
-    const int timeoutInMs =
-            base::GetIntProperty(std::string("debug.sf.layer_caching_active_layer_timeout_ms"), 0);
-    if (timeoutInMs != 0) {
-        mActiveLayerTimeout = std::chrono::milliseconds(timeoutInMs);
-    }
-}
+Flattener::Flattener(renderengine::RenderEngine& renderEngine, const Tunables& tunables)
+      : mRenderEngine(renderEngine), mTunables(tunables), mTexturePool(mRenderEngine) {}
 
 NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers,
                                        NonBufferHash hash, time_point now) {
@@ -128,14 +117,14 @@
     // If we have a render deadline, and the flattener is configured to skip rendering if we don't
     // have enough time, then we skip rendering the cached set if we think that we'll steal too much
     // time from the next frame.
-    if (renderDeadline && mCachedSetRenderSchedulingTunables) {
+    if (renderDeadline && mTunables.mRenderScheduling) {
         if (const auto estimatedRenderFinish =
-                    now + mCachedSetRenderSchedulingTunables->cachedSetRenderDuration;
+                    now + mTunables.mRenderScheduling->cachedSetRenderDuration;
             estimatedRenderFinish > *renderDeadline) {
             mNewCachedSet->incrementSkipCount();
 
             if (mNewCachedSet->getSkipCount() <=
-                mCachedSetRenderSchedulingTunables->maxDeferRenderAttempts) {
+                mTunables.mRenderScheduling->maxDeferRenderAttempts) {
                 ATRACE_FORMAT("DeadlinePassed: exceeded deadline by: %d us",
                               std::chrono::duration_cast<std::chrono::microseconds>(
                                       estimatedRenderFinish - *renderDeadline)
@@ -420,8 +409,10 @@
     bool runHasFirstLayer = false;
 
     for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
-        const bool layerIsInactive = now - currentSet->getLastUpdate() > mActiveLayerTimeout;
+        const bool layerIsInactive =
+                now - currentSet->getLastUpdate() > mTunables.mActiveLayerTimeout;
         const bool layerHasBlur = currentSet->hasBlurBehind();
+
         if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) &&
             !currentSet->hasUnsupportedDataspace()) {
             if (isPartOfRun) {
@@ -522,7 +513,7 @@
         mNewCachedSet->addBackgroundBlurLayer(*bestRun->getBlurringLayer());
     }
 
-    if (mEnableHolePunch && bestRun->getHolePunchCandidate() &&
+    if (mTunables.mEnableHolePunch && bestRun->getHolePunchCandidate() &&
         bestRun->getHolePunchCandidate()->requiresHolePunch()) {
         // Add the pip layer to mNewCachedSet, but in a special way - it should
         // replace the buffer with a clear round rect.
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index f077470..f5b1cee 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -32,36 +32,46 @@
 
 namespace {
 
-std::optional<Flattener::CachedSetRenderSchedulingTunables> buildFlattenerTuneables() {
+std::optional<Flattener::Tunables::RenderScheduling> buildRenderSchedulingTunables() {
     if (!base::GetBoolProperty(std::string("debug.sf.enable_cached_set_render_scheduling"), true)) {
         return std::nullopt;
     }
 
-    auto renderDuration = std::chrono::nanoseconds(
+    const auto renderDuration = std::chrono::nanoseconds(
             base::GetUintProperty<uint64_t>(std::string("debug.sf.cached_set_render_duration_ns"),
-                                            Flattener::CachedSetRenderSchedulingTunables::
+                                            Flattener::Tunables::RenderScheduling::
                                                     kDefaultCachedSetRenderDuration.count()));
 
-    auto maxDeferRenderAttempts = base::GetUintProperty<
+    const auto maxDeferRenderAttempts = base::GetUintProperty<
             size_t>(std::string("debug.sf.cached_set_max_defer_render_attmpts"),
-                    Flattener::CachedSetRenderSchedulingTunables::kDefaultMaxDeferRenderAttempts);
+                    Flattener::Tunables::RenderScheduling::kDefaultMaxDeferRenderAttempts);
 
-    return std::make_optional<Flattener::CachedSetRenderSchedulingTunables>(
-            Flattener::CachedSetRenderSchedulingTunables{
+    return std::make_optional<Flattener::Tunables::RenderScheduling>(
+            Flattener::Tunables::RenderScheduling{
                     .cachedSetRenderDuration = renderDuration,
                     .maxDeferRenderAttempts = maxDeferRenderAttempts,
             });
 }
 
+Flattener::Tunables buildFlattenerTuneables() {
+    const auto activeLayerTimeout = std::chrono::milliseconds(
+            base::GetIntProperty<int32_t>(std::string(
+                                                  "debug.sf.layer_caching_active_layer_timeout_ms"),
+                                          Flattener::Tunables::kDefaultActiveLayerTimeout.count()));
+    const auto enableHolePunch =
+            base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"),
+                                  Flattener::Tunables::kDefaultEnableHolePunch);
+    return Flattener::Tunables{
+            .mActiveLayerTimeout = activeLayerTimeout,
+            .mRenderScheduling = buildRenderSchedulingTunables(),
+            .mEnableHolePunch = enableHolePunch,
+    };
+}
+
 } // namespace
 
 Planner::Planner(renderengine::RenderEngine& renderEngine)
-      // Implicitly, layer caching must also be enabled for the hole punch or
-      // predictor to have any effect.
-      // E.g., setprop debug.sf.enable_layer_caching 1, or
-      // adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>]
       : mFlattener(renderEngine,
-                   base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), true),
                    buildFlattenerTuneables()) {
     mPredictorEnabled =
             base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false);
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index ee73cfc..09f5a5e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3606,6 +3606,7 @@
       : public OutputComposeSurfacesTest_SetsExpensiveRendering {
     OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur() {
         mLayer.layerFEState.backgroundBlurRadius = 10;
+        mLayer.layerFEState.isOpaque = false;
         mOutput.editState().isEnabled = true;
 
         EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
@@ -4225,6 +4226,37 @@
                                                                 kDisplayDataspace));
 }
 
+TEST_F(OutputUpdateAndWriteCompositionStateTest, noBackgroundBlurWhenOpaque) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+
+    uint32_t z = 0;
+    // Layer requesting blur, or below, should request client composition, unless opaque.
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer,
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer,
+                writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++,
+                                /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
+
+    layer2.layerFEState.backgroundBlurRadius = 10;
+    layer2.layerFEState.isOpaque = true;
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+
+    mOutput->editState().isEnabled = true;
+
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = false;
+    mOutput->updateCompositionState(args);
+    mOutput->planComposition();
+    mOutput->writeCompositionState(args);
+}
+
 TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBackgroundBlurRequests) {
     InjectedLayer layer1;
     InjectedLayer layer2;
@@ -4246,6 +4278,7 @@
                                 /*zIsOverridden*/ false, /*isPeekingThrough*/ false));
 
     layer2.layerFEState.backgroundBlurRadius = 10;
+    layer2.layerFEState.isOpaque = false;
 
     injectOutputLayer(layer1);
     injectOutputLayer(layer2);
@@ -4283,6 +4316,7 @@
 
     BlurRegion region;
     layer2.layerFEState.blurRegions.push_back(region);
+    layer2.layerFEState.isOpaque = false;
 
     injectOutputLayer(layer1);
     injectOutputLayer(layer2);
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index f5cfd2f..a28fb2c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -47,23 +47,24 @@
 
 class TestableFlattener : public Flattener {
 public:
-    TestableFlattener(renderengine::RenderEngine& renderEngine, bool enableHolePunch,
-                      std::optional<Flattener::CachedSetRenderSchedulingTunables>
-                              cachedSetRenderSchedulingTunables = std::nullopt)
-          : Flattener(renderEngine, enableHolePunch, cachedSetRenderSchedulingTunables) {}
+    TestableFlattener(renderengine::RenderEngine& renderEngine, const Tunables& tunables)
+          : Flattener(renderEngine, tunables) {}
     const std::optional<CachedSet>& getNewCachedSetForTesting() const { return mNewCachedSet; }
 };
 
 class FlattenerTest : public testing::Test {
 public:
-    FlattenerTest() : FlattenerTest(std::nullopt) {}
+    FlattenerTest()
+          : FlattenerTest(Flattener::Tunables{
+                    .mActiveLayerTimeout = 100ms,
+                    .mRenderScheduling = std::nullopt,
+                    .mEnableHolePunch = true,
+            }) {}
     void SetUp() override;
 
 protected:
-    FlattenerTest(std::optional<Flattener::CachedSetRenderSchedulingTunables>
-                          cachedSetRenderSchedulingTunables)
-          : mFlattener(std::make_unique<TestableFlattener>(mRenderEngine, true,
-                                                           cachedSetRenderSchedulingTunables)) {}
+    FlattenerTest(const Flattener::Tunables& tunables)
+          : mFlattener(std::make_unique<TestableFlattener>(mRenderEngine, tunables)) {}
     void initializeOverrideBuffer(const std::vector<const LayerState*>& layers);
     void initializeFlattener(const std::vector<const LayerState*>& layers);
     void expectAllLayersFlattened(const std::vector<const LayerState*>& layers);
@@ -899,11 +900,13 @@
 public:
     FlattenerRenderSchedulingTest()
           : FlattenerTest(
-                    Flattener::CachedSetRenderSchedulingTunables{.cachedSetRenderDuration =
+                    Flattener::Tunables{.mActiveLayerTimeout = 100ms,
+                                        .mRenderScheduling = Flattener::Tunables::
+                                                RenderScheduling{.cachedSetRenderDuration =
                                                                          kCachedSetRenderDuration,
                                                                  .maxDeferRenderAttempts =
-                                                                         kMaxDeferRenderAttempts}) {
-    }
+                                                                         kMaxDeferRenderAttempts},
+                                        .mEnableHolePunch = true}) {}
 };
 
 TEST_F(FlattenerRenderSchedulingTest, flattenLayers_renderCachedSets_defersUpToMaxAttempts) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index cc7ff16..140436a 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2164,7 +2164,7 @@
     return getCroppedBufferSize(getDrawingState());
 }
 
-void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& toPhysicalDisplay) {
+void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& toNonRotatedDisplay) {
     // Transform layer size to screen space and inset it by surface insets.
     // If this is a portal window, set the touchableRegion to the layerBounds.
     Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE
@@ -2185,12 +2185,12 @@
     }
 
     ui::Transform layerToDisplay = getInputTransform();
-    // Transform that takes window coordinates to unrotated display coordinates
-    ui::Transform t = toPhysicalDisplay * layerToDisplay;
+    // Transform that takes window coordinates to non-rotated display coordinates
+    ui::Transform t = toNonRotatedDisplay * layerToDisplay;
     int32_t xSurfaceInset = info.surfaceInset;
     int32_t ySurfaceInset = info.surfaceInset;
-    // Bring screenBounds into unrotated space
-    Rect screenBounds = toPhysicalDisplay.transform(Rect{mScreenBounds});
+    // Bring screenBounds into non-rotated space
+    Rect screenBounds = toNonRotatedDisplay.transform(Rect{mScreenBounds});
 
     const float xScale = t.getScaleX();
     const float yScale = t.getScaleY();
@@ -2283,19 +2283,33 @@
     info.id = sequence;
     info.displayId = getLayerStack();
 
-    // Transform that goes from "logical(rotated)" display to physical/unrotated display.
-    // This is for when inputflinger operates in physical display-space.
-    ui::Transform toPhysicalDisplay;
+    // Transform that goes from "logical(rotated)" display to the non-rotated display.
+    ui::Transform toNonRotatedDisplay;
     if (display) {
-        toPhysicalDisplay = display->getTransform();
-        // getOrientation() without masking can contain more-significant bits (eg. ROT_INVALID).
-        static constexpr uint32_t ALL_ROTATIONS_MASK =
-                ui::Transform::ROT_90 | ui::Transform::ROT_180 | ui::Transform::ROT_270;
-        info.displayOrientation = toPhysicalDisplay.getOrientation() & ALL_ROTATIONS_MASK;
-        info.displayWidth = display->getWidth();
-        info.displayHeight = display->getHeight();
+        // The physical orientation is set when the orientation of the display panel is different
+        // than the default orientation of the device. We do not need to expose the physical
+        // orientation of the panel outside of SurfaceFlinger.
+        const ui::Rotation inversePhysicalOrientation =
+                ui::ROTATION_0 - display->getPhysicalOrientation();
+        auto width = display->getWidth();
+        auto height = display->getHeight();
+        if (inversePhysicalOrientation == ui::ROTATION_90 ||
+            inversePhysicalOrientation == ui::ROTATION_270) {
+            std::swap(width, height);
+        }
+        const auto rotationFlags = ui::Transform::toRotationFlags(inversePhysicalOrientation);
+        const ui::Transform undoPhysicalOrientation(rotationFlags, width, height);
+        toNonRotatedDisplay = undoPhysicalOrientation * display->getTransform();
+
+        // Send the inverse of the display orientation so that input can transform points back to
+        // the rotated display space.
+        const ui::Rotation inverseOrientation = ui::ROTATION_0 - display->getOrientation();
+        info.displayOrientation = ui::Transform::toRotationFlags(inverseOrientation);
+
+        info.displayWidth = width;
+        info.displayHeight = height;
     }
-    fillInputFrameInfo(info, toPhysicalDisplay);
+    fillInputFrameInfo(info, toNonRotatedDisplay);
 
     // For compatibility reasons we let layers which can receive input
     // receive input before they have actually submitted a buffer. Because
@@ -2312,14 +2326,14 @@
     auto cropLayer = mDrawingState.touchableRegionCrop.promote();
     if (info.replaceTouchableRegionWithCrop) {
         if (cropLayer == nullptr) {
-            info.touchableRegion = Region(toPhysicalDisplay.transform(Rect{mScreenBounds}));
+            info.touchableRegion = Region(toNonRotatedDisplay.transform(Rect{mScreenBounds}));
         } else {
             info.touchableRegion =
-                    Region(toPhysicalDisplay.transform(Rect{cropLayer->mScreenBounds}));
+                    Region(toNonRotatedDisplay.transform(Rect{cropLayer->mScreenBounds}));
         }
     } else if (cropLayer != nullptr) {
         info.touchableRegion = info.touchableRegion.intersect(
-                toPhysicalDisplay.transform(Rect{cropLayer->mScreenBounds}));
+                toNonRotatedDisplay.transform(Rect{cropLayer->mScreenBounds}));
     }
 
     // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state
@@ -2332,7 +2346,7 @@
     if (isClone()) {
         sp<Layer> clonedRoot = getClonedRoot();
         if (clonedRoot != nullptr) {
-            Rect rect = toPhysicalDisplay.transform(Rect{clonedRoot->mScreenBounds});
+            Rect rect = toNonRotatedDisplay.transform(Rect{clonedRoot->mScreenBounds});
             info.touchableRegion = info.touchableRegion.intersect(rect);
         }
     }
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 1e5429a..470103c 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -1073,7 +1073,7 @@
     void fillTouchOcclusionMode(gui::WindowInfo& info);
 
     // Fills in the frame and transform info for the gui::WindowInfo
-    void fillInputFrameInfo(gui::WindowInfo& info, const ui::Transform& toPhysicalDisplay);
+    void fillInputFrameInfo(gui::WindowInfo& info, const ui::Transform& toNonRotatedDisplay);
 
     // Cached properties computed from drawing state
     // Effective transform taking into account parent transforms and any parent scaling, which is
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index c38cd68..81a669a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -910,7 +910,10 @@
 int RefreshRateConfigs::getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate) {
     // This calculation needs to be in sync with the java code
     // in DisplayManagerService.getDisplayInfoForFrameRateOverride
-    constexpr float kThreshold = 0.1f;
+
+    // The threshold must be smaller than 0.001 in order to differentiate
+    // between the fractional pairs (e.g. 59.94 and 60).
+    constexpr float kThreshold = 0.0009f;
     const auto numPeriods = displayFrameRate.getValue() / layerFrameRate.getValue();
     const auto numPeriodsRounded = std::round(numPeriods);
     if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index f808981..80b2504 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -395,6 +395,14 @@
         return;
     }
 
+    // If the mode is not the current mode, this means that a
+    // mode change is in progress. In that case we shouldn't dispatch an event
+    // as it will be dispatched when the current mode changes.
+    if (std::scoped_lock lock(mRefreshRateConfigsLock);
+        mRefreshRateConfigs->getCurrentRefreshRate().getMode() != mFeatures.mode) {
+        return;
+    }
+
     // If there is no change from cached mode, there is no need to dispatch an event
     if (mFeatures.mode == mFeatures.cachedModeChangedParams->mode) {
         return;
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 6870fd4..c1dba2b 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -2116,7 +2116,11 @@
     refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90);
     displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps();
     EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.5f)));
-    EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.6f)));
+
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(24.f), Fps(25.f)));
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(24.f), Fps(23.976f)));
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(30.f), Fps(29.97f)));
+    EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(60.f), Fps(59.94f)));
 }
 
 TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) {
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 2715587..3ed3eba 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1231,6 +1231,12 @@
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
+    // In shared mode the num_images must be one regardless of how many
+    // buffers were allocated for the buffer queue.
+    if (swapchain_image_usage & VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID) {
+        num_images = 1;
+    }
+
     int32_t legacy_usage = 0;
     if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
         uint64_t consumer_usage, producer_usage;