Loading include/input/Resampler.h +28 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,17 @@ 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. */ std::optional<Sample> mLastRealSample; /** * Latest prediction. Used to overwrite motion event samples if a set of conditions is met. */ std::optional<Sample> mPreviousPrediction; /** * Adds up to mLatestSamples.capacity() of motionEvent's latest samples to mLatestSamples. If * motionEvent has fewer samples than mLatestSamples.capacity(), then the available samples are Loading Loading @@ -144,6 +155,23 @@ private: */ std::optional<Sample> attemptExtrapolation(std::chrono::nanoseconds resampleTime) const; /** * Iterates through motion event samples, and calls overwriteStillPointers on each sample. */ void overwriteMotionEventSamples(MotionEvent& motionEvent) const; /** * Overwrites with resampled data the pointer coordinates that did not move between motion event * samples, that is, both x and y values are identical to mLastRealSample. */ void overwriteStillPointers(MotionEvent& motionEvent, size_t sampleIndex) const; /** * Overwrites the pointer coordinates of a sample with event time older than * that of mPreviousPrediction. */ void overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const; inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent); }; } // namespace android libs/binder/ndk/include_cpp/android/binder_interface_utils.h +3 −5 Original line number Diff line number Diff line Loading @@ -30,7 +30,7 @@ #include <android/binder_auto_utils.h> #include <android/binder_ibinder.h> #if defined(__ANDROID_VENDOR__) #if defined(__ANDROID_VENDOR_API__) #include <android/llndk-versioning.h> #elif !defined(API_LEVEL_AT_LEAST) #if defined(__BIONIC__) Loading @@ -39,7 +39,7 @@ #else #define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true) #endif // __BIONIC__ #endif // __ANDROID_VENDOR__ #endif // __ANDROID_VENDOR_API__ #if __has_include(<android/binder_shell.h>) #include <android/binder_shell.h> Loading Loading @@ -297,9 +297,7 @@ AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor, } #endif // TODO(b/368559337): fix versioning on product partition #if !defined(__ANDROID_PRODUCT__) && \ (defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36) #if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36 if API_LEVEL_AT_LEAST (36, 202504) { if (codeToFunction != nullptr) { AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, codeToFunction, Loading libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h +3 −3 Original line number Diff line number Diff line Loading @@ -22,8 +22,8 @@ #include <set> #include <sstream> // Include llndk-versioning.h only for vendor build as it is not available for NDK headers. #if defined(__ANDROID_VENDOR__) // Include llndk-versioning.h only for non-system build as it is not available for NDK headers. #if defined(__ANDROID_VENDOR_API__) #include <android/llndk-versioning.h> #elif !defined(API_LEVEL_AT_LEAST) #if defined(__BIONIC__) Loading @@ -32,7 +32,7 @@ #else #define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true) #endif // __BIONIC__ #endif // __ANDROID_VENDOR__ #endif // __ANDROID_VENDOR_API__ namespace aidl::android::os { Loading libs/gui/libgui_flags.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -108,6 +108,14 @@ flag { is_fixed_read_only: true } # wb_libcameraservice flag { name: "wb_unlimited_slots" namespace: "core_graphics" description: "Adds APIs and updates the implementation of bufferqueues to have a user-defined slot count." bug: "341359814" is_fixed_read_only: true } # wb_unlimited_slots flag { name: "bq_producer_throttles_only_async_mode" namespace: "core_graphics" Loading libs/input/Resampler.cpp +82 −9 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <algorithm> #include <chrono> #include <ostream> #include <android-base/logging.h> #include <android-base/properties.h> Loading @@ -26,10 +27,7 @@ #include <input/Resampler.h> #include <utils/Timers.h> using std::chrono::nanoseconds; namespace android { namespace { const bool IS_DEBUGGABLE_BUILD = Loading @@ -49,6 +47,8 @@ bool debugResampling() { return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO); } using std::chrono::nanoseconds; constexpr std::chrono::milliseconds RESAMPLE_LATENCY{5}; constexpr std::chrono::milliseconds RESAMPLE_MIN_DELTA{2}; Loading @@ -75,6 +75,31 @@ PointerCoords calculateResampledCoords(const PointerCoords& a, const PointerCoor resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(a.getY(), b.getY(), alpha)); return resampledCoords; } bool equalXY(const PointerCoords& a, const PointerCoords& b) { return (a.getX() == b.getX()) && (a.getY() == b.getY()); } void setMotionEventPointerCoords(MotionEvent& motionEvent, size_t sampleIndex, size_t pointerIndex, const PointerCoords& pointerCoords) { // Ideally, we should not cast away const. In this particular case, it's safe to cast away const // and dereference getHistoricalRawPointerCoords returned pointer because motionEvent is a // nonconst reference to a MotionEvent object, so mutating the object should not be undefined // behavior; moreover, the invoked method guarantees to return a valid pointer. Otherwise, it // fatally logs. Alternatively, we could've created a new MotionEvent from scratch, but this // approach is simpler and more efficient. PointerCoords& motionEventCoords = const_cast<PointerCoords&>( *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex))); motionEventCoords.setAxisValue(AMOTION_EVENT_AXIS_X, pointerCoords.getX()); motionEventCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, pointerCoords.getY()); motionEventCoords.isResampled = pointerCoords.isResampled; } std::ostream& operator<<(std::ostream& os, const PointerCoords& pointerCoords) { os << "(" << pointerCoords.getX() << ", " << pointerCoords.getY() << ")"; return os; } } // namespace void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) { Loading @@ -85,12 +110,9 @@ void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) { std::vector<Pointer> pointers; const size_t numPointers = motionEvent.getPointerCount(); for (size_t pointerIndex = 0; pointerIndex < numPointers; ++pointerIndex) { // getSamplePointerCoords is the vector representation of a getHistorySize by // getPointerCount matrix. const PointerCoords& pointerCoords = motionEvent.getSamplePointerCoords()[sampleIndex * numPointers + pointerIndex]; pointers.push_back( Pointer{*motionEvent.getPointerProperties(pointerIndex), pointerCoords}); pointers.push_back(Pointer{*(motionEvent.getPointerProperties(pointerIndex)), *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex))}); } mLatestSamples.pushBack( Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointers}); Loading Loading @@ -245,6 +267,47 @@ nanoseconds LegacyResampler::getResampleLatency() const { return RESAMPLE_LATENCY; } void LegacyResampler::overwriteMotionEventSamples(MotionEvent& motionEvent) const { const size_t numSamples = motionEvent.getHistorySize() + 1; for (size_t sampleIndex = 0; sampleIndex < numSamples; ++sampleIndex) { overwriteStillPointers(motionEvent, sampleIndex); overwriteOldPointers(motionEvent, sampleIndex); } } void LegacyResampler::overwriteStillPointers(MotionEvent& motionEvent, size_t sampleIndex) const { for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount(); ++pointerIndex) { const PointerCoords& pointerCoords = *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex)); if (equalXY(mLastRealSample->pointers[pointerIndex].coords, pointerCoords)) { LOG_IF(INFO, debugResampling()) << "Pointer ID: " << motionEvent.getPointerId(pointerIndex) << " did not move. Overwriting its coordinates from " << pointerCoords << " to " << mLastRealSample->pointers[pointerIndex].coords; setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex, mPreviousPrediction->pointers[pointerIndex].coords); } } } void LegacyResampler::overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const { if (!mPreviousPrediction.has_value()) { 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."; for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount(); ++pointerIndex) { setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex, mPreviousPrediction->pointers[pointerIndex].coords); } } } void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& motionEvent, const InputMessage* futureSample) { const nanoseconds resampleTime = frameTime - RESAMPLE_LATENCY; Loading @@ -261,6 +324,16 @@ void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& mo : (attemptExtrapolation(resampleTime)); if (sample.has_value()) { addSampleToMotionEvent(*sample, motionEvent); if (mPreviousPrediction.has_value()) { overwriteMotionEventSamples(motionEvent); } // mPreviousPrediction is only updated whenever extrapolation occurs because extrapolation // is about predicting upcoming scenarios. if (futureSample == nullptr) { mPreviousPrediction = sample; } } mLastRealSample = *(mLatestSamples.end() - 1); } } // namespace android Loading
include/input/Resampler.h +28 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,17 @@ 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. */ std::optional<Sample> mLastRealSample; /** * Latest prediction. Used to overwrite motion event samples if a set of conditions is met. */ std::optional<Sample> mPreviousPrediction; /** * Adds up to mLatestSamples.capacity() of motionEvent's latest samples to mLatestSamples. If * motionEvent has fewer samples than mLatestSamples.capacity(), then the available samples are Loading Loading @@ -144,6 +155,23 @@ private: */ std::optional<Sample> attemptExtrapolation(std::chrono::nanoseconds resampleTime) const; /** * Iterates through motion event samples, and calls overwriteStillPointers on each sample. */ void overwriteMotionEventSamples(MotionEvent& motionEvent) const; /** * Overwrites with resampled data the pointer coordinates that did not move between motion event * samples, that is, both x and y values are identical to mLastRealSample. */ void overwriteStillPointers(MotionEvent& motionEvent, size_t sampleIndex) const; /** * Overwrites the pointer coordinates of a sample with event time older than * that of mPreviousPrediction. */ void overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const; inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent); }; } // namespace android
libs/binder/ndk/include_cpp/android/binder_interface_utils.h +3 −5 Original line number Diff line number Diff line Loading @@ -30,7 +30,7 @@ #include <android/binder_auto_utils.h> #include <android/binder_ibinder.h> #if defined(__ANDROID_VENDOR__) #if defined(__ANDROID_VENDOR_API__) #include <android/llndk-versioning.h> #elif !defined(API_LEVEL_AT_LEAST) #if defined(__BIONIC__) Loading @@ -39,7 +39,7 @@ #else #define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true) #endif // __BIONIC__ #endif // __ANDROID_VENDOR__ #endif // __ANDROID_VENDOR_API__ #if __has_include(<android/binder_shell.h>) #include <android/binder_shell.h> Loading Loading @@ -297,9 +297,7 @@ AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor, } #endif // TODO(b/368559337): fix versioning on product partition #if !defined(__ANDROID_PRODUCT__) && \ (defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36) #if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36 if API_LEVEL_AT_LEAST (36, 202504) { if (codeToFunction != nullptr) { AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, codeToFunction, Loading
libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h +3 −3 Original line number Diff line number Diff line Loading @@ -22,8 +22,8 @@ #include <set> #include <sstream> // Include llndk-versioning.h only for vendor build as it is not available for NDK headers. #if defined(__ANDROID_VENDOR__) // Include llndk-versioning.h only for non-system build as it is not available for NDK headers. #if defined(__ANDROID_VENDOR_API__) #include <android/llndk-versioning.h> #elif !defined(API_LEVEL_AT_LEAST) #if defined(__BIONIC__) Loading @@ -32,7 +32,7 @@ #else #define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true) #endif // __BIONIC__ #endif // __ANDROID_VENDOR__ #endif // __ANDROID_VENDOR_API__ namespace aidl::android::os { Loading
libs/gui/libgui_flags.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -108,6 +108,14 @@ flag { is_fixed_read_only: true } # wb_libcameraservice flag { name: "wb_unlimited_slots" namespace: "core_graphics" description: "Adds APIs and updates the implementation of bufferqueues to have a user-defined slot count." bug: "341359814" is_fixed_read_only: true } # wb_unlimited_slots flag { name: "bq_producer_throttles_only_async_mode" namespace: "core_graphics" Loading
libs/input/Resampler.cpp +82 −9 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <algorithm> #include <chrono> #include <ostream> #include <android-base/logging.h> #include <android-base/properties.h> Loading @@ -26,10 +27,7 @@ #include <input/Resampler.h> #include <utils/Timers.h> using std::chrono::nanoseconds; namespace android { namespace { const bool IS_DEBUGGABLE_BUILD = Loading @@ -49,6 +47,8 @@ bool debugResampling() { return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO); } using std::chrono::nanoseconds; constexpr std::chrono::milliseconds RESAMPLE_LATENCY{5}; constexpr std::chrono::milliseconds RESAMPLE_MIN_DELTA{2}; Loading @@ -75,6 +75,31 @@ PointerCoords calculateResampledCoords(const PointerCoords& a, const PointerCoor resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(a.getY(), b.getY(), alpha)); return resampledCoords; } bool equalXY(const PointerCoords& a, const PointerCoords& b) { return (a.getX() == b.getX()) && (a.getY() == b.getY()); } void setMotionEventPointerCoords(MotionEvent& motionEvent, size_t sampleIndex, size_t pointerIndex, const PointerCoords& pointerCoords) { // Ideally, we should not cast away const. In this particular case, it's safe to cast away const // and dereference getHistoricalRawPointerCoords returned pointer because motionEvent is a // nonconst reference to a MotionEvent object, so mutating the object should not be undefined // behavior; moreover, the invoked method guarantees to return a valid pointer. Otherwise, it // fatally logs. Alternatively, we could've created a new MotionEvent from scratch, but this // approach is simpler and more efficient. PointerCoords& motionEventCoords = const_cast<PointerCoords&>( *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex))); motionEventCoords.setAxisValue(AMOTION_EVENT_AXIS_X, pointerCoords.getX()); motionEventCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, pointerCoords.getY()); motionEventCoords.isResampled = pointerCoords.isResampled; } std::ostream& operator<<(std::ostream& os, const PointerCoords& pointerCoords) { os << "(" << pointerCoords.getX() << ", " << pointerCoords.getY() << ")"; return os; } } // namespace void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) { Loading @@ -85,12 +110,9 @@ void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) { std::vector<Pointer> pointers; const size_t numPointers = motionEvent.getPointerCount(); for (size_t pointerIndex = 0; pointerIndex < numPointers; ++pointerIndex) { // getSamplePointerCoords is the vector representation of a getHistorySize by // getPointerCount matrix. const PointerCoords& pointerCoords = motionEvent.getSamplePointerCoords()[sampleIndex * numPointers + pointerIndex]; pointers.push_back( Pointer{*motionEvent.getPointerProperties(pointerIndex), pointerCoords}); pointers.push_back(Pointer{*(motionEvent.getPointerProperties(pointerIndex)), *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex))}); } mLatestSamples.pushBack( Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointers}); Loading Loading @@ -245,6 +267,47 @@ nanoseconds LegacyResampler::getResampleLatency() const { return RESAMPLE_LATENCY; } void LegacyResampler::overwriteMotionEventSamples(MotionEvent& motionEvent) const { const size_t numSamples = motionEvent.getHistorySize() + 1; for (size_t sampleIndex = 0; sampleIndex < numSamples; ++sampleIndex) { overwriteStillPointers(motionEvent, sampleIndex); overwriteOldPointers(motionEvent, sampleIndex); } } void LegacyResampler::overwriteStillPointers(MotionEvent& motionEvent, size_t sampleIndex) const { for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount(); ++pointerIndex) { const PointerCoords& pointerCoords = *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex)); if (equalXY(mLastRealSample->pointers[pointerIndex].coords, pointerCoords)) { LOG_IF(INFO, debugResampling()) << "Pointer ID: " << motionEvent.getPointerId(pointerIndex) << " did not move. Overwriting its coordinates from " << pointerCoords << " to " << mLastRealSample->pointers[pointerIndex].coords; setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex, mPreviousPrediction->pointers[pointerIndex].coords); } } } void LegacyResampler::overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const { if (!mPreviousPrediction.has_value()) { 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."; for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount(); ++pointerIndex) { setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex, mPreviousPrediction->pointers[pointerIndex].coords); } } } void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& motionEvent, const InputMessage* futureSample) { const nanoseconds resampleTime = frameTime - RESAMPLE_LATENCY; Loading @@ -261,6 +324,16 @@ void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& mo : (attemptExtrapolation(resampleTime)); if (sample.has_value()) { addSampleToMotionEvent(*sample, motionEvent); if (mPreviousPrediction.has_value()) { overwriteMotionEventSamples(motionEvent); } // mPreviousPrediction is only updated whenever extrapolation occurs because extrapolation // is about predicting upcoming scenarios. if (futureSample == nullptr) { mPreviousPrediction = sample; } } mLastRealSample = *(mLatestSamples.end() - 1); } } // namespace android