Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 91a7c9d4 authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 12543847 from 64ac2f9e to 25Q1-release

Change-Id: I9a29aebf8e5a90663ea69447b7831a8481eab8fb
parents 5fc02039 64ac2f9e
Loading
Loading
Loading
Loading
+1 −9
Original line number Diff line number Diff line
@@ -3,9 +3,6 @@
    {
      "name": "SurfaceFlinger_test",
      "options": [
        {
          "include-filter": "*"
        },
        {
          // TODO(b/305717998): Deflake and re-enable
          "exclude-filter": "*ChildLayerTest*"
@@ -23,12 +20,7 @@
  ],
  "hwasan-postsubmit": [
    {
      "name": "SurfaceFlinger_test",
      "options": [
        {
          "include-filter": "*"
        }
      ]
      "name": "SurfaceFlinger_test"
    }
  ]
}
+127 −8
Original line number Diff line number Diff line
@@ -16,10 +16,14 @@

#pragma once

#include <array>
#include <chrono>
#include <iterator>
#include <optional>
#include <vector>

#include <android-base/logging.h>
#include <ftl/mixins.h>
#include <input/Input.h>
#include <input/InputTransport.h>
#include <input/RingBuffer.h>
@@ -79,13 +83,127 @@ private:
        PointerCoords coords;
    };

    /**
     * Container that stores pointers as an associative array, supporting O(1) lookup by pointer id,
     * as well as forward iteration in the order in which the pointer or pointers were inserted in
     * the container. PointerMap has a maximum capacity equal to MAX_POINTERS.
     */
    class PointerMap {
    public:
        struct PointerId : ftl::DefaultConstructible<PointerId, int32_t>,
                           ftl::Equatable<PointerId> {
            using DefaultConstructible::DefaultConstructible;
        };

        /**
         * Custom iterator to enable use of range-based for loops.
         */
        template <typename T>
        class iterator {
        public:
            using iterator_category = std::forward_iterator_tag;
            using value_type = T;
            using difference_type = std::ptrdiff_t;
            using pointer = T*;
            using reference = T&;

            explicit iterator(pointer element) : mElement{element} {}

            friend bool operator==(const iterator& lhs, const iterator& rhs) {
                return lhs.mElement == rhs.mElement;
            }

            friend bool operator!=(const iterator& lhs, const iterator& rhs) {
                return !(lhs == rhs);
            }

            iterator operator++() {
                ++mElement;
                return *this;
            }

            reference operator*() const { return *mElement; }

        private:
            pointer mElement;
        };

        PointerMap() {
            idToIndex.fill(std::nullopt);
            for (Pointer& pointer : pointers) {
                pointer.properties.clear();
                pointer.coords.clear();
            }
        }

        /**
         * Forward iterators to traverse the pointers in `pointers`. The order of the pointers is
         * determined by the order in which they were inserted (not by id).
         */
        iterator<Pointer> begin() { return iterator<Pointer>{&pointers[0]}; }

        iterator<const Pointer> begin() const { return iterator<const Pointer>{&pointers[0]}; }

        iterator<Pointer> end() { return iterator<Pointer>{&pointers[nextPointerIndex]}; }

        iterator<const Pointer> end() const {
            return iterator<const Pointer>{&pointers[nextPointerIndex]};
        }

        /**
         * Inserts the given pointer into the PointerMap. Precondition: The current number of
         * contained pointers must be less than MAX_POINTERS when this function is called. It
         * fatally logs if the user tries to insert more than MAX_POINTERS, or if pointer id is out
         * of bounds.
         */
        void insert(const Pointer& pointer) {
            LOG_IF(FATAL, nextPointerIndex >= pointers.size())
                    << "Cannot insert more than " << MAX_POINTERS << " in PointerMap.";
            LOG_IF(FATAL, (pointer.properties.id < 0) || (pointer.properties.id > MAX_POINTER_ID))
                    << "Invalid pointer id.";
            idToIndex[pointer.properties.id] = std::optional<size_t>{nextPointerIndex};
            pointers[nextPointerIndex] = pointer;
            ++nextPointerIndex;
        }

        /**
         * Returns the pointer associated with the provided id if it exists.
         * Otherwise, std::nullopt is returned.
         */
        std::optional<Pointer> find(PointerId id) const {
            const int32_t idValue = ftl::to_underlying(id);
            LOG_IF(FATAL, (idValue < 0) || (idValue > MAX_POINTER_ID)) << "Invalid pointer id.";
            const std::optional<size_t> index = idToIndex[idValue];
            return index.has_value() ? std::optional{pointers[*index]} : std::nullopt;
        }

    private:
        /**
         * The index at which a pointer is inserted in `pointers`. Likewise, it represents the
         * number of pointers in PointerMap.
         */
        size_t nextPointerIndex{0};

        /**
         * Sequentially stores pointers. Each pointer's position is determined by the value of
         * nextPointerIndex at insertion time.
         */
        std::array<Pointer, MAX_POINTERS + 1> pointers;

        /**
         * Maps each pointer id to its associated index in pointers. If no pointer with the id
         * exists in pointers, the mapped value is std::nullopt.
         */
        std::array<std::optional<size_t>, MAX_POINTER_ID + 1> idToIndex;
    };

    struct Sample {
        std::chrono::nanoseconds eventTime;
        std::vector<Pointer> pointers;
        PointerMap pointerMap;

        std::vector<PointerCoords> asPointerCoords() const {
            std::vector<PointerCoords> pointersCoords;
            for (const Pointer& pointer : pointers) {
            for (const Pointer& pointer : pointerMap) {
                pointersCoords.push_back(pointer.coords);
            }
            return pointersCoords;
@@ -100,13 +218,12 @@ private:
    RingBuffer<Sample> mLatestSamples{/*capacity=*/2};

    /**
     * Latest sample in mLatestSamples after resampling motion event. Used to compare if a pointer
     * does not move between samples.
     * Latest sample in mLatestSamples after resampling motion event.
     */
    std::optional<Sample> mLastRealSample;

    /**
     * Latest prediction. Used to overwrite motion event samples if a set of conditions is met.
     * Latest prediction. That is, the latest extrapolated sample.
     */
    std::optional<Sample> mPreviousPrediction;

@@ -134,12 +251,12 @@ private:
    bool canInterpolate(const InputMessage& futureSample) const;

    /**
     * Returns a sample interpolated between the latest sample of mLatestSamples and futureSample,
     * Returns a sample interpolated between the latest sample of mLatestSamples and futureMessage,
     * if the conditions from canInterpolate are satisfied. Otherwise, returns nullopt.
     * mLatestSamples must have at least one sample when attemptInterpolation is called.
     */
    std::optional<Sample> attemptInterpolation(std::chrono::nanoseconds resampleTime,
                                               const InputMessage& futureSample) const;
                                               const InputMessage& futureMessage) const;

    /**
     * Checks if there are necessary conditions to extrapolate. That is, there are at least two
@@ -156,7 +273,8 @@ private:
    std::optional<Sample> attemptExtrapolation(std::chrono::nanoseconds resampleTime) const;

    /**
     * Iterates through motion event samples, and calls overwriteStillPointers on each sample.
     * Iterates through motion event samples, and replaces real coordinates with resampled
     * coordinates to avoid jerkiness in certain conditions.
     */
    void overwriteMotionEventSamples(MotionEvent& motionEvent) const;

@@ -174,4 +292,5 @@ private:

    inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
};

} // namespace android
+109 −56
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

#include <algorithm>
#include <chrono>
#include <iomanip>
#include <ostream>

#include <android-base/logging.h>
@@ -37,6 +38,11 @@ const bool IS_DEBUGGABLE_BUILD =
        true;
#endif

/**
 * Log debug messages about timestamp and coordinates of event resampling.
 * Enable this via "adb shell setprop log.tag.LegacyResamplerResampling DEBUG"
 * (requires restart)
 */
bool debugResampling() {
    if (!IS_DEBUGGABLE_BUILD) {
        static const bool DEBUG_TRANSPORT_RESAMPLING =
@@ -107,46 +113,44 @@ void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) {
    const size_t latestIndex = numSamples - 1;
    const size_t secondToLatestIndex = (latestIndex > 0) ? (latestIndex - 1) : 0;
    for (size_t sampleIndex = secondToLatestIndex; sampleIndex < numSamples; ++sampleIndex) {
        std::vector<Pointer> pointers;
        const size_t numPointers = motionEvent.getPointerCount();
        for (size_t pointerIndex = 0; pointerIndex < numPointers; ++pointerIndex) {
            pointers.push_back(Pointer{*(motionEvent.getPointerProperties(pointerIndex)),
        PointerMap pointerMap;
        for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
             ++pointerIndex) {
            pointerMap.insert(Pointer{*(motionEvent.getPointerProperties(pointerIndex)),
                                      *(motionEvent.getHistoricalRawPointerCoords(pointerIndex,
                                                                                  sampleIndex))});
        }
        mLatestSamples.pushBack(
                Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointers});
                Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointerMap});
    }
}

LegacyResampler::Sample LegacyResampler::messageToSample(const InputMessage& message) {
    std::vector<Pointer> pointers;
    PointerMap pointerMap;
    for (uint32_t i = 0; i < message.body.motion.pointerCount; ++i) {
        pointers.push_back(Pointer{message.body.motion.pointers[i].properties,
        pointerMap.insert(Pointer{message.body.motion.pointers[i].properties,
                                  message.body.motion.pointers[i].coords});
    }
    return Sample{nanoseconds{message.body.motion.eventTime}, pointers};
    return Sample{nanoseconds{message.body.motion.eventTime}, pointerMap};
}

bool LegacyResampler::pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary) {
    if (target.pointers.size() > auxiliary.pointers.size()) {
    for (const Pointer& pointer : target.pointerMap) {
        const std::optional<Pointer> auxiliaryPointer =
                auxiliary.pointerMap.find(PointerMap::PointerId{pointer.properties.id});
        if (!auxiliaryPointer.has_value()) {
            LOG_IF(INFO, debugResampling())
                << "Not resampled. Auxiliary sample has fewer pointers than target sample.";
                    << "Not resampled. Auxiliary sample does not contain all pointers from target.";
            return false;
        }
    for (size_t i = 0; i < target.pointers.size(); ++i) {
        if (target.pointers[i].properties.id != auxiliary.pointers[i].properties.id) {
            LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ID mismatch.";
            return false;
        }
        if (target.pointers[i].properties.toolType != auxiliary.pointers[i].properties.toolType) {
        if (pointer.properties.toolType != auxiliaryPointer->properties.toolType) {
            LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ToolType mismatch.";
            return false;
        }
        if (!canResampleTool(target.pointers[i].properties.toolType)) {
        if (!canResampleTool(pointer.properties.toolType)) {
            LOG_IF(INFO, debugResampling())
                    << "Not resampled. Cannot resample "
                    << ftl::enum_string(target.pointers[i].properties.toolType) << " ToolType.";
                    << ftl::enum_string(pointer.properties.toolType) << " ToolType.";
            return false;
        }
    }
@@ -166,35 +170,40 @@ bool LegacyResampler::canInterpolate(const InputMessage& message) const {

    const nanoseconds delta = futureSample.eventTime - pastSample.eventTime;
    if (delta < RESAMPLE_MIN_DELTA) {
        LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
        LOG_IF(INFO, debugResampling())
                << "Not resampled. Delta is too small: " << std::setprecision(3)
                << std::chrono::duration<double, std::milli>{delta}.count() << "ms";
        return false;
    }
    return true;
}

std::optional<LegacyResampler::Sample> LegacyResampler::attemptInterpolation(
        nanoseconds resampleTime, const InputMessage& futureSample) const {
    if (!canInterpolate(futureSample)) {
        nanoseconds resampleTime, const InputMessage& futureMessage) const {
    if (!canInterpolate(futureMessage)) {
        return std::nullopt;
    }
    LOG_IF(FATAL, mLatestSamples.empty())
            << "Not resampled. mLatestSamples must not be empty to interpolate.";

    const Sample& pastSample = *(mLatestSamples.end() - 1);
    const Sample& futureSample = messageToSample(futureMessage);

    const nanoseconds delta =
            nanoseconds{futureSample.body.motion.eventTime} - pastSample.eventTime;
    const nanoseconds delta = nanoseconds{futureSample.eventTime} - pastSample.eventTime;
    const float alpha =
            std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta;
            std::chrono::duration<float, std::nano>(resampleTime - pastSample.eventTime) / delta;

    std::vector<Pointer> resampledPointers;
    for (size_t i = 0; i < pastSample.pointers.size(); ++i) {
    PointerMap resampledPointerMap;
    for (const Pointer& pointer : pastSample.pointerMap) {
        if (std::optional<Pointer> futureSamplePointer =
                    futureSample.pointerMap.find(PointerMap::PointerId{pointer.properties.id});
            futureSamplePointer.has_value()) {
            const PointerCoords& resampledCoords =
                calculateResampledCoords(pastSample.pointers[i].coords,
                                         futureSample.body.motion.pointers[i].coords, alpha);
        resampledPointers.push_back(Pointer{pastSample.pointers[i].properties, resampledCoords});
                    calculateResampledCoords(pointer.coords, futureSamplePointer->coords, alpha);
            resampledPointerMap.insert(Pointer{pointer.properties, resampledCoords});
        }
    return Sample{resampleTime, resampledPointers};
    }
    return Sample{resampleTime, resampledPointerMap};
}

bool LegacyResampler::canExtrapolate() const {
@@ -212,10 +221,14 @@ bool LegacyResampler::canExtrapolate() const {

    const nanoseconds delta = presentSample.eventTime - pastSample.eventTime;
    if (delta < RESAMPLE_MIN_DELTA) {
        LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
        LOG_IF(INFO, debugResampling())
                << "Not resampled. Delta is too small: " << std::setprecision(3)
                << std::chrono::duration<double, std::milli>{delta}.count() << "ms";
        return false;
    } else if (delta > RESAMPLE_MAX_DELTA) {
        LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too large: " << delta << "ns.";
        LOG_IF(INFO, debugResampling())
                << "Not resampled. Delta is too large: " << std::setprecision(3)
                << std::chrono::duration<double, std::milli>{delta}.count() << "ms";
        return false;
    }
    return true;
@@ -241,20 +254,28 @@ std::optional<LegacyResampler::Sample> LegacyResampler::attemptExtrapolation(
            (resampleTime > farthestPrediction) ? (farthestPrediction) : (resampleTime);
    LOG_IF(INFO, debugResampling() && newResampleTime == farthestPrediction)
            << "Resample time is too far in the future. Adjusting prediction from "
            << (resampleTime - presentSample.eventTime) << " to "
            << (farthestPrediction - presentSample.eventTime) << "ns.";
            << std::setprecision(3)
            << std::chrono::duration<double, std::milli>{resampleTime - presentSample.eventTime}
                       .count()
            << "ms to "
            << std::chrono::duration<double, std::milli>{farthestPrediction -
                                                         presentSample.eventTime}
                       .count()
            << "ms";
    const float alpha =
            std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) /
            delta;
            std::chrono::duration<float, std::nano>(newResampleTime - pastSample.eventTime) / delta;

    std::vector<Pointer> resampledPointers;
    for (size_t i = 0; i < presentSample.pointers.size(); ++i) {
    PointerMap resampledPointerMap;
    for (const Pointer& pointer : presentSample.pointerMap) {
        if (std::optional<Pointer> pastSamplePointer =
                    pastSample.pointerMap.find(PointerMap::PointerId{pointer.properties.id});
            pastSamplePointer.has_value()) {
            const PointerCoords& resampledCoords =
                calculateResampledCoords(pastSample.pointers[i].coords,
                                         presentSample.pointers[i].coords, alpha);
        resampledPointers.push_back(Pointer{presentSample.pointers[i].properties, resampledCoords});
                    calculateResampledCoords(pastSamplePointer->coords, pointer.coords, alpha);
            resampledPointerMap.insert(Pointer{pointer.properties, resampledCoords});
        }
    }
    return Sample{newResampleTime, resampledPointers};
    return Sample{newResampleTime, resampledPointerMap};
}

inline void LegacyResampler::addSampleToMotionEvent(const Sample& sample,
@@ -267,6 +288,12 @@ nanoseconds LegacyResampler::getResampleLatency() const {
    return RESAMPLE_LATENCY;
}

/**
 * The resampler is unaware of ACTION_DOWN. Thus, it needs to constantly check for pointer IDs
 * occurrences. This problem could be fixed if the resampler has access to the entire stream of
 * MotionEvent actions. That way, both ACTION_DOWN and ACTION_UP will be visible; therefore,
 * facilitating pointer tracking between samples.
 */
void LegacyResampler::overwriteMotionEventSamples(MotionEvent& motionEvent) const {
    const size_t numSamples = motionEvent.getHistorySize() + 1;
    for (size_t sampleIndex = 0; sampleIndex < numSamples; ++sampleIndex) {
@@ -276,34 +303,59 @@ void LegacyResampler::overwriteMotionEventSamples(MotionEvent& motionEvent) cons
}

void LegacyResampler::overwriteStillPointers(MotionEvent& motionEvent, size_t sampleIndex) const {
    if (!mLastRealSample.has_value() || !mPreviousPrediction.has_value()) {
        LOG_IF(INFO, debugResampling()) << "Still pointers not overwritten. Not enough data.";
        return;
    }
    for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount(); ++pointerIndex) {
        const std::optional<Pointer> lastRealPointer = mLastRealSample->pointerMap.find(
                PointerMap::PointerId{motionEvent.getPointerId(pointerIndex)});
        const std::optional<Pointer> previousPointer = mPreviousPrediction->pointerMap.find(
                PointerMap::PointerId{motionEvent.getPointerId(pointerIndex)});
        // This could happen because resampler only receives ACTION_MOVE events.
        if (!lastRealPointer.has_value() || !previousPointer.has_value()) {
            continue;
        }
        const PointerCoords& pointerCoords =
                *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex));
        if (equalXY(mLastRealSample->pointers[pointerIndex].coords, pointerCoords)) {
        if (equalXY(pointerCoords, lastRealPointer->coords)) {
            LOG_IF(INFO, debugResampling())
                    << "Pointer ID: " << motionEvent.getPointerId(pointerIndex)
                    << " did not move. Overwriting its coordinates from " << pointerCoords << " to "
                    << mLastRealSample->pointers[pointerIndex].coords;
                    << previousPointer->coords;
            setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex,
                                        mPreviousPrediction->pointers[pointerIndex].coords);
                                        previousPointer->coords);
        }
    }
}

void LegacyResampler::overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const {
    if (!mPreviousPrediction.has_value()) {
        LOG_IF(INFO, debugResampling()) << "Old sample not overwritten. Not enough data.";
        return;
    }
    if (nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)} <
        mPreviousPrediction->eventTime) {
        LOG_IF(INFO, debugResampling())
                << "Motion event sample older than predicted sample. Overwriting event time from "
                << motionEvent.getHistoricalEventTime(sampleIndex) << "ns to "
                << mPreviousPrediction->eventTime.count() << "ns.";
                << std::setprecision(3)
                << std::chrono::duration<double,
                                         std::milli>{nanoseconds{motionEvent.getHistoricalEventTime(
                                                             sampleIndex)}}
                           .count()
                << "ms to "
                << std::chrono::duration<double, std::milli>{mPreviousPrediction->eventTime}.count()
                << "ms";
        for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
             ++pointerIndex) {
            const std::optional<Pointer> previousPointer = mPreviousPrediction->pointerMap.find(
                    PointerMap::PointerId{motionEvent.getPointerId(pointerIndex)});
            // This could happen because resampler only receives ACTION_MOVE events.
            if (!previousPointer.has_value()) {
                continue;
            }
            setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex,
                                        mPreviousPrediction->pointers[pointerIndex].coords);
                                        previousPointer->coords);
        }
    }
}
@@ -333,6 +385,7 @@ void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& mo
            mPreviousPrediction = sample;
        }
    }
    LOG_IF(FATAL, mLatestSamples.empty()) << "mLatestSamples must contain at least one sample.";
    mLastRealSample = *(mLatestSamples.end() - 1);
}

+7 −0
Original line number Diff line number Diff line
@@ -214,3 +214,10 @@ flag {
  description: "Enable telemetry for rotary input"
  bug: "370353565"
}

flag {
  name: "set_input_device_kernel_wake"
  namespace: "input"
  description: "Set input device's power/wakeup sysfs node"
  bug: "372812925"
}
+18 −2
Original line number Diff line number Diff line
@@ -648,7 +648,15 @@ TEST_F(ResamplerTest, MultiplePointerDifferentIdOrderInterpolation) {

    mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);

    assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
    assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
                                              {Pointer{.id = 0,
                                                       .x = 1.4f,
                                                       .y = 1.4f,
                                                       .isResampled = true},
                                               Pointer{.id = 1,
                                                       .x = 2.4f,
                                                       .y = 2.4f,
                                                       .isResampled = true}});
}

TEST_F(ResamplerTest, MultiplePointerDifferentIdOrderExtrapolation) {
@@ -670,7 +678,15 @@ TEST_F(ResamplerTest, MultiplePointerDifferentIdOrderExtrapolation) {

    mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr);

    assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent);
    assertMotionEventIsResampledAndCoordsNear(secondOriginalMotionEvent, secondMotionEvent,
                                              {Pointer{.id = 1,
                                                       .x = 4.4f,
                                                       .y = 4.4f,
                                                       .isResampled = true},
                                               Pointer{.id = 0,
                                                       .x = 3.4f,
                                                       .y = 3.4f,
                                                       .isResampled = true}});
}

TEST_F(ResamplerTest, MultiplePointerDifferentIdsInterpolation) {
Loading