Add SpatializerPoseController

This class encapsulates the logic for pose processing, intended for
driving a spatializer effect.

Bug: 188502620
Test: Manual testing of pose processing using logging.
Change-Id: I84d47926ff469d7c09895aba55aaf2e6c890ec00
diff --git a/services/audiopolicy/service/SpatializerPoseController.h b/services/audiopolicy/service/SpatializerPoseController.h
new file mode 100644
index 0000000..619dc7b
--- /dev/null
+++ b/services/audiopolicy/service/SpatializerPoseController.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <chrono>
+#include <condition_variable>
+#include <limits>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include <media/HeadTrackingProcessor.h>
+#include <media/SensorPoseProvider.h>
+
+namespace android {
+
+/**
+ * This class encapsulates the logic for pose processing, intended for driving a spatializer effect.
+ * This includes integration with the Sensor sub-system for retrieving sensor data, doing all the
+ * necessary processing, etc.
+ *
+ * Calculations happen on a dedicated thread and published to the client via the Listener interface.
+ * A calculation may be triggered in one of two ways:
+ * - By calling calculateAsync() - calculation will be kicked off in the background.
+ * - By setting a timeout in the ctor, a calculation will be triggered after the timeout elapsed
+ *   from the last calculateAsync() call.
+ *
+ * This class is thread-safe. Callbacks are invoked with the lock held, so it is illegal to call
+ * into this module from the callbacks.
+ */
+class SpatializerPoseController : private media::SensorPoseProvider::Listener {
+  public:
+    /**
+     * Listener interface for getting pose and mode updates.
+     * Methods will always be invoked from a designated thread. Calling into the parent class from
+     * within the callbacks is disallowed (will result in a deadlock).
+     */
+    class Listener {
+      public:
+        virtual ~Listener() = default;
+
+        virtual void onHeadToStagePose(const media::Pose3f&) = 0;
+        virtual void onActualModeChange(media::HeadTrackingMode) = 0;
+    };
+
+    /**
+     * Ctor.
+     * sensorPeriod determines how often to receive updates from the sensors (input rate).
+     * maxUpdatePeriod determines how often to produce an output when calculateAsync() isn't
+     * invoked.
+     */
+    SpatializerPoseController(Listener* listener, std::chrono::microseconds sensorPeriod,
+                               std::chrono::microseconds maxUpdatePeriod);
+
+    /** Dtor. */
+    ~SpatializerPoseController();
+
+    /**
+     * Set the sensor that is to be used for head-tracking.
+     * nullptr can be used to disable head-tracking.
+     */
+    void setHeadSensor(const ASensor* sensor);
+
+    /**
+     * Set the sensor that is to be used for screen-tracking.
+     * nullptr can be used to disable screen-tracking.
+     */
+    void setScreenSensor(const ASensor* sensor);
+
+    /** Sets the desired head-tracking mode. */
+    void setDesiredMode(media::HeadTrackingMode mode);
+
+    /**
+     * Set the screen-to-stage pose, used in all modes.
+     */
+    void setScreenToStagePose(const media::Pose3f& screenToStage);
+
+    /**
+     * Sets the display orientation.
+     * Orientation is expressed in the angle of rotation from the physical "up" side of the screen
+     * to the logical "up" side of the content displayed the screen. Counterclockwise angles, as
+     * viewed while facing the screen are positive.
+     */
+    void setDisplayOrientation(float physicalToLogicalAngle);
+
+    /**
+     * This causes the current poses for both the head and screen to be considered "center".
+     */
+    void recenter();
+
+    /**
+     * This call triggers the recalculation of the output and the invocation of the relevant
+     * callbacks. This call is async and the callbacks will be triggered shortly after.
+     */
+    void calculateAsync();
+
+    /**
+     * Blocks until calculation and invocation of the respective callbacks has happened at least
+     * once.
+     */
+    void waitUntilCalculated();
+
+  private:
+    mutable std::mutex mMutex;
+    Listener* const mListener;
+    const std::chrono::microseconds mSensorPeriod;
+    std::unique_ptr<media::SensorPoseProvider> mPoseProvider;
+    std::unique_ptr<media::HeadTrackingProcessor> mProcessor;
+    int32_t mHeadSensor = media::SensorPoseProvider::INVALID_HANDLE;
+    int32_t mScreenSensor = media::SensorPoseProvider::INVALID_HANDLE;
+    std::optional<media::HeadTrackingMode> mActualMode;
+    std::thread mThread;
+    std::condition_variable mCondVar;
+    bool mShouldCalculate = true;
+    bool mShouldExit = false;
+    bool mCalculated = false;
+
+    void onPose(int64_t timestamp, int32_t sensor, const media::Pose3f& pose,
+                const std::optional<media::Twist3f>& twist) override;
+
+    void calculate_l();
+};
+
+}  // namespace android