Merge changes I58e1c380,I3aa1f225,Ifb73050e into sc-v2-dev

* changes:
  Fix display orientation handling
  Better support for head and screen sensors being the same one
  Refine the recenter operation
diff --git a/media/libheadtracking/HeadTrackingProcessor-test.cpp b/media/libheadtracking/HeadTrackingProcessor-test.cpp
index 1739c6d..299192f 100644
--- a/media/libheadtracking/HeadTrackingProcessor-test.cpp
+++ b/media/libheadtracking/HeadTrackingProcessor-test.cpp
@@ -53,10 +53,10 @@
     processor->setWorldToHeadPose(0, Pose3f(), Twist3f());
     processor->setWorldToScreenPose(0, Pose3f());
 
+    processor->setDisplayOrientation(physicalToLogical);
     processor->setWorldToHeadPose(0, worldToHead, Twist3f());
     processor->setWorldToScreenPose(0, worldToScreen);
     processor->setScreenToStagePose(screenToStage);
-    processor->setDisplayOrientation(physicalToLogical);
     processor->calculate(0);
     ASSERT_EQ(processor->getActualMode(), HeadTrackingMode::SCREEN_RELATIVE);
     EXPECT_EQ(processor->getHeadToStagePose(), worldToHead.inverse() * worldToScreen *
diff --git a/media/libheadtracking/HeadTrackingProcessor.cpp b/media/libheadtracking/HeadTrackingProcessor.cpp
index ee60fa5..47f7cf0 100644
--- a/media/libheadtracking/HeadTrackingProcessor.cpp
+++ b/media/libheadtracking/HeadTrackingProcessor.cpp
@@ -57,7 +57,14 @@
     }
 
     void setWorldToScreenPose(int64_t timestamp, const Pose3f& worldToScreen) override {
-        mScreenPoseDriftCompensator.setInput(timestamp, worldToScreen);
+        if (mPhysicalToLogicalAngle != mPendingPhysicalToLogicalAngle) {
+            // We're introducing an artificial discontinuity. Enable the rate limiter.
+            mRateLimiter.enable();
+            mPhysicalToLogicalAngle = mPendingPhysicalToLogicalAngle;
+        }
+
+        mScreenPoseDriftCompensator.setInput(
+                timestamp, worldToScreen * Pose3f(rotateY(-mPhysicalToLogicalAngle)));
         mWorldToScreenTimestamp = timestamp;
     }
 
@@ -66,10 +73,7 @@
     }
 
     void setDisplayOrientation(float physicalToLogicalAngle) override {
-        if (mPhysicalToLogicalAngle != physicalToLogicalAngle) {
-            mRateLimiter.enable();
-        }
-        mPhysicalToLogicalAngle = physicalToLogicalAngle;
+        mPendingPhysicalToLogicalAngle = physicalToLogicalAngle;
     }
 
     void calculate(int64_t timestamp) override {
@@ -80,8 +84,7 @@
         }
 
         if (mWorldToScreenTimestamp.has_value()) {
-            const Pose3f worldToLogicalScreen = mScreenPoseDriftCompensator.getOutput() *
-                                                Pose3f(rotateY(-mPhysicalToLogicalAngle));
+            const Pose3f worldToLogicalScreen = mScreenPoseDriftCompensator.getOutput();
             mScreenHeadFusion.setWorldToScreenPose(mWorldToScreenTimestamp.value(),
                                                    worldToLogicalScreen);
         }
@@ -108,15 +111,30 @@
 
     HeadTrackingMode getActualMode() const override { return mModeSelector.getActualMode(); }
 
-    void recenter() override {
-        mHeadPoseDriftCompensator.recenter();
-        mScreenPoseDriftCompensator.recenter();
-        mRateLimiter.enable();
+    void recenter(bool recenterHead, bool recenterScreen) override {
+        if (recenterHead) {
+            mHeadPoseDriftCompensator.recenter();
+        }
+        if (recenterScreen) {
+            mScreenPoseDriftCompensator.recenter();
+        }
+
+        // If a sensor being recentered is included in the current mode, apply rate limiting to
+        // avoid discontinuities.
+        HeadTrackingMode mode = mModeSelector.getActualMode();
+        if ((recenterHead && (mode == HeadTrackingMode::WORLD_RELATIVE ||
+                              mode == HeadTrackingMode::SCREEN_RELATIVE)) ||
+            (recenterScreen && mode == HeadTrackingMode::SCREEN_RELATIVE)) {
+            mRateLimiter.enable();
+        }
     }
 
   private:
     const Options mOptions;
     float mPhysicalToLogicalAngle = 0;
+    // We store the physical to logical angle as "pending" until the next world-to-screen sample it
+    // applies to arrives.
+    float mPendingPhysicalToLogicalAngle = 0;
     std::optional<int64_t> mWorldToHeadTimestamp;
     std::optional<int64_t> mWorldToScreenTimestamp;
     Pose3f mHeadToStagePose;
diff --git a/media/libheadtracking/PoseProcessingGraph.png b/media/libheadtracking/PoseProcessingGraph.png
index 8e6dfd2..0363068 100644
--- a/media/libheadtracking/PoseProcessingGraph.png
+++ b/media/libheadtracking/PoseProcessingGraph.png
Binary files differ
diff --git a/media/libheadtracking/include/media/HeadTrackingProcessor.h b/media/libheadtracking/include/media/HeadTrackingProcessor.h
index 23de540..9fea273 100644
--- a/media/libheadtracking/include/media/HeadTrackingProcessor.h
+++ b/media/libheadtracking/include/media/HeadTrackingProcessor.h
@@ -89,9 +89,9 @@
     virtual HeadTrackingMode getActualMode() const = 0;
 
     /**
-     * This causes the current poses for both the head and screen to be considered "center".
+     * This causes the current poses for both the head and/or screen to be considered "center".
      */
-    virtual void recenter() = 0;
+    virtual void recenter(bool recenterHead = true, bool recenterScreen = true) = 0;
 };
 
 /**
diff --git a/media/libheadtracking/include/media/SensorPoseProvider.h b/media/libheadtracking/include/media/SensorPoseProvider.h
index 7b79704..3e9c107 100644
--- a/media/libheadtracking/include/media/SensorPoseProvider.h
+++ b/media/libheadtracking/include/media/SensorPoseProvider.h
@@ -77,11 +77,13 @@
 
     /**
      * Start receiving pose updates from a given sensor.
+     * Attempting to start a sensor that has already been started results in undefined behavior.
      * @param sensor The sensor to subscribe to.
      * @param samplingPeriod Sampling interval, in microseconds. Actual rate might be slightly
      * different.
-     * @return A handle, which can be later used for stopSensor(). INVALID_HANDLE would be returned
-     * in case of error.
+     * @return The sensor handle, which can be later used for stopSensor(). INVALID_HANDLE would be
+     * returned in case of error. This is guaranteed to be the same handle as the one returned by
+     * ASensor_getHandle(sensor).
      */
     virtual int32_t startSensor(const ASensor* sensor,
                                 std::chrono::microseconds samplingPeriod) = 0;
diff --git a/services/audiopolicy/service/SpatializerPoseController.cpp b/services/audiopolicy/service/SpatializerPoseController.cpp
index f0d7f7c..3d79c9d 100644
--- a/services/audiopolicy/service/SpatializerPoseController.cpp
+++ b/services/audiopolicy/service/SpatializerPoseController.cpp
@@ -117,26 +117,46 @@
 
 void SpatializerPoseController::setHeadSensor(const ASensor* sensor) {
     std::lock_guard lock(mMutex);
-    // Stop current sensor, if valid.
-    if (mHeadSensor != SensorPoseProvider::INVALID_HANDLE) {
+    // Stop current sensor, if valid and different from the other sensor.
+    if (mHeadSensor != SensorPoseProvider::INVALID_HANDLE && mHeadSensor != mScreenSensor) {
         mPoseProvider->stopSensor(mHeadSensor);
     }
-    // Start new sensor, if valid.
-    mHeadSensor = sensor != nullptr ? mPoseProvider->startSensor(sensor, mSensorPeriod)
-                                    : SensorPoseProvider::INVALID_HANDLE;
-    mProcessor->recenter();
+
+    if (sensor != nullptr) {
+        if (ASensor_getHandle(sensor) != mScreenSensor) {
+            // Start new sensor.
+            mHeadSensor = mPoseProvider->startSensor(sensor, mSensorPeriod);
+        } else {
+            // Sensor is already enabled.
+            mHeadSensor = mScreenSensor;
+        }
+    } else {
+        mHeadSensor = SensorPoseProvider::INVALID_HANDLE;
+    }
+
+    mProcessor->recenter(true, false);
 }
 
 void SpatializerPoseController::setScreenSensor(const ASensor* sensor) {
     std::lock_guard lock(mMutex);
-    // Stop current sensor, if valid.
-    if (mScreenSensor != SensorPoseProvider::INVALID_HANDLE) {
+    // Stop current sensor, if valid and different from the other sensor.
+    if (mScreenSensor != SensorPoseProvider::INVALID_HANDLE && mScreenSensor != mHeadSensor) {
         mPoseProvider->stopSensor(mScreenSensor);
     }
-    // Start new sensor, if valid.
-    mScreenSensor = sensor != nullptr ? mPoseProvider->startSensor(sensor, mSensorPeriod)
-                                      : SensorPoseProvider::INVALID_HANDLE;
-    mProcessor->recenter();
+
+    if (sensor != nullptr) {
+        if (ASensor_getHandle(sensor) != mHeadSensor) {
+            // Start new sensor.
+            mScreenSensor = mPoseProvider->startSensor(sensor, mSensorPeriod);
+        } else {
+            // Sensor is already enabled.
+            mScreenSensor = mHeadSensor;
+        }
+    } else {
+        mScreenSensor = SensorPoseProvider::INVALID_HANDLE;
+    }
+
+    mProcessor->recenter(false, true);
 }
 
 void SpatializerPoseController::setDesiredMode(HeadTrackingMode mode) {
@@ -188,7 +208,8 @@
     std::lock_guard lock(mMutex);
     if (sensor == mHeadSensor) {
         mProcessor->setWorldToHeadPose(timestamp, pose, twist.value_or(Twist3f()));
-    } else if (sensor == mScreenSensor) {
+    }
+    if (sensor == mScreenSensor) {
         mProcessor->setWorldToScreenPose(timestamp, pose);
     }
 }