diff options
102 files changed, 4282 insertions, 346 deletions
diff --git a/cmds/evemu-record/main.rs b/cmds/evemu-record/main.rs index db3fd77520..e91e5daec4 100644 --- a/cmds/evemu-record/main.rs +++ b/cmds/evemu-record/main.rs @@ -50,8 +50,10 @@ enum TimestampBase { /// The first event received from the device. FirstEvent, - /// The time when the system booted. - Boot, + /// The Unix epoch (00:00:00 UTC on 1st January 1970), so that all timestamps are Unix + /// timestamps. This makes the events in the recording easier to match up with those from other + /// log sources. + Epoch, } fn get_choice(max: u32) -> u32 { @@ -188,7 +190,7 @@ fn print_events( // // [0]: https://gitlab.freedesktop.org/libevdev/evemu/-/commit/eba96a4d2be7260b5843e65c4b99c8b06a1f4c9d TimestampBase::FirstEvent => event.time - TimeVal::new(0, 1), - TimestampBase::Boot => TimeVal::new(0, 0), + TimestampBase::Epoch => TimeVal::new(0, 0), }; print_event(output, &event.offset_time_by(start_time))?; loop { diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index ef2fa4dff7..fa7cb64f3a 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -505,8 +505,9 @@ Status ServiceManager::addService(const std::string& name, const sp<IBinder>& bi return Status::fromExceptionCode(Status::EX_SECURITY, "App UIDs cannot add services."); } - if (!mAccess->canAdd(ctx, name)) { - return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied."); + std::optional<std::string> accessorName; + if (auto status = canAddService(ctx, name, &accessorName); !status.isOk()) { + return status; } if (binder == nullptr) { @@ -888,8 +889,9 @@ Status ServiceManager::registerClientCallback(const std::string& name, const sp< } auto ctx = mAccess->getCallingContext(); - if (!mAccess->canAdd(ctx, name)) { - return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied."); + std::optional<std::string> accessorName; + if (auto status = canAddService(ctx, name, &accessorName); !status.isOk()) { + return status; } auto serviceIt = mNameToService.find(name); @@ -1051,8 +1053,9 @@ Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IB } auto ctx = mAccess->getCallingContext(); - if (!mAccess->canAdd(ctx, name)) { - return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied."); + std::optional<std::string> accessorName; + if (auto status = canAddService(ctx, name, &accessorName); !status.isOk()) { + return status; } auto serviceIt = mNameToService.find(name); @@ -1110,6 +1113,23 @@ Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IB return Status::ok(); } +Status ServiceManager::canAddService(const Access::CallingContext& ctx, const std::string& name, + std::optional<std::string>* accessor) { + if (!mAccess->canAdd(ctx, name)) { + return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied for service."); + } +#ifndef VENDORSERVICEMANAGER + *accessor = getVintfAccessorName(name); +#endif + if (accessor->has_value()) { + if (!mAccess->canAdd(ctx, accessor->value())) { + return Status::fromExceptionCode(Status::EX_SECURITY, + "SELinux denied for the accessor of the service."); + } + } + return Status::ok(); +} + Status ServiceManager::canFindService(const Access::CallingContext& ctx, const std::string& name, std::optional<std::string>* accessor) { if (!mAccess->canFind(ctx, name)) { diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h index 0d666c6bce..c92141b393 100644 --- a/cmds/servicemanager/ServiceManager.h +++ b/cmds/servicemanager/ServiceManager.h @@ -115,6 +115,8 @@ private: os::Service tryGetService(const std::string& name, bool startIfNotFound); sp<IBinder> tryGetBinder(const std::string& name, bool startIfNotFound); + binder::Status canAddService(const Access::CallingContext& ctx, const std::string& name, + std::optional<std::string>* accessor); binder::Status canFindService(const Access::CallingContext& ctx, const std::string& name, std::optional<std::string>* accessor); diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index 97e4dc05dd..3f32a5abb3 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -53,6 +53,7 @@ */ #include <android/api-level.h> +#include <stdbool.h> #include <stdint.h> #include <unistd.h> @@ -84,7 +85,6 @@ typedef struct AWorkDuration AWorkDuration; /** * An opaque type representing a handle to a performance hint manager. - * It must be released after use. * * To use:<ul> * <li>Obtain the performance hint manager instance by calling diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 099a2bc6af..82caccaf68 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -373,6 +373,28 @@ void ASurfaceTransaction_setColor(ASurfaceTransaction* _Nonnull transaction, float b, float alpha, enum ADataSpace dataspace) __INTRODUCED_IN(29); +// These APIs (setGeometry and setCrop) were originally written in a +// C-incompatible form using references instead of pointers, and the OS shipped +// that version for years before it was noticed. Fortunately the compiled code +// for callers is the same regardless of whether it's a pointer or a reference, +// so we can declare this as a nonnull pointer for C and keep the existing C++ +// decl and definition. +// +// We could alternatively change the decl and the definition to both be a +// pointer (with an inline definition using references to preserve source compat +// for existing C++ callers), but that requires changing the definition of an +// API that has been in the OS for years. It's theoretically a safe change, but +// without being able to prove it that's a very big risk to take. By keeping the +// C-compatibility hack in the header, we can be sure that we haven't changed +// anything for existing callers. By definition there were no C users of the +// reference-based decl; if there were any C callers of the API at all, they were +// using the same workaround that is now used below. +// +// Even if this workaround turns out to not work for C, there's no permanent +// damage done to the platform (unlike if we were to change the definition). At +// worst it continues to work for C++ (since the preprocessed header as seen by +// C++ hasn't changed, nor has the definition) and continues to not work for C. + /** * \param source The sub-rect within the buffer's content to be rendered inside the surface's area * The surface's source rect is clipped by the bounds of its current buffer. The source rect's width @@ -383,7 +405,7 @@ void ASurfaceTransaction_setColor(ASurfaceTransaction* _Nonnull transaction, * clipped by the bounds of its parent. The destination rect's width and height must be > 0. * * \param transform The transform applied after the source rect is applied to the buffer. This - * parameter should be set to 0 for no transform. To specify a transfrom use the + * parameter should be set to 0 for no transform. To specify a transform use the * NATIVE_WINDOW_TRANSFORM_* enum. * * Available since API level 29. @@ -394,9 +416,14 @@ void ASurfaceTransaction_setColor(ASurfaceTransaction* _Nonnull transaction, * properties at once. */ void ASurfaceTransaction_setGeometry(ASurfaceTransaction* _Nonnull transaction, - ASurfaceControl* _Nonnull surface_control, const ARect& source, - const ARect& destination, int32_t transform) - __INTRODUCED_IN(29); + ASurfaceControl* _Nonnull surface_control, +#if defined(__cplusplus) + const ARect& source, const ARect& destination, +#else + const ARect* _Nonnull source, + const ARect* _Nonnull destination, +#endif + int32_t transform) __INTRODUCED_IN(29); /** * Bounds the surface and its children to the bounds specified. The crop and buffer size will be @@ -408,7 +435,12 @@ void ASurfaceTransaction_setGeometry(ASurfaceTransaction* _Nonnull transaction, * Available since API level 31. */ void ASurfaceTransaction_setCrop(ASurfaceTransaction* _Nonnull transaction, - ASurfaceControl* _Nonnull surface_control, const ARect& crop) + ASurfaceControl* _Nonnull surface_control, +#if defined(__cplusplus) + const ARect& crop) +#else + const ARect* _Nonnull crop) +#endif __INTRODUCED_IN(31); /** diff --git a/include/android/surface_control_input_receiver.h b/include/android/surface_control_input_receiver.h index bdc52490d5..f0503f6324 100644 --- a/include/android/surface_control_input_receiver.h +++ b/include/android/surface_control_input_receiver.h @@ -59,17 +59,13 @@ typedef bool (*AInputReceiver_onKeyEvent)(void *_Null_unspecified context, AInputEvent *_Nonnull keyEvent) __INTRODUCED_IN(__ANDROID_API_V__); -struct AInputReceiverCallbacks; - -struct AInputReceiver; +typedef struct AInputReceiverCallbacks AInputReceiverCallbacks; /** * The InputReceiver that holds the reference to the registered input channel. This must be released * using AInputReceiver_release - * - * Available since API level 35. */ -typedef struct AInputReceiver AInputReceiver __INTRODUCED_IN(__ANDROID_API_V__); +typedef struct AInputReceiver AInputReceiver; /** * Registers an input receiver for an ASurfaceControl that will receive batched input event. For diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h index ae8de5f411..65c2914b3c 100644 --- a/include/input/InputConsumerNoResampling.h +++ b/include/input/InputConsumerNoResampling.h @@ -17,6 +17,7 @@ #pragma once #include <input/InputTransport.h> +#include <input/Resampler.h> #include <utils/Looper.h> namespace android { @@ -47,13 +48,13 @@ public: /** * Consumes input events from an input channel. * - * This is a re-implementation of InputConsumer that does not have resampling at the current moment. - * A lot of the higher-level logic has been folded into this class, to make it easier to use. - * In the legacy class, InputConsumer, the consumption logic was partially handled in the jni layer, - * as well as various actions like adding the fd to the Choreographer. + * This is a re-implementation of InputConsumer. At the moment it only supports resampling for + * single pointer events. A lot of the higher-level logic has been folded into this class, to make + * it easier to use. In the legacy class, InputConsumer, the consumption logic was partially handled + * in the jni layer, as well as various actions like adding the fd to the Choreographer. * * TODO(b/297226446): use this instead of "InputConsumer": - * - Add resampling to this class + * - Add resampling for multiple pointer events. * - Allow various resampling strategies to be specified * - Delete the old "InputConsumer" and use this class instead, renaming it to "InputConsumer". * - Add tracing @@ -64,8 +65,18 @@ public: */ class InputConsumerNoResampling final { public: + /** + * @param callbacks are used to interact with InputConsumerNoResampling. They're called whenever + * the event is ready to consume. + * @param looper needs to be sp and not shared_ptr because it inherits from + * RefBase + * @param resampler the resampling strategy to use. If null, no resampling will be + * performed. + */ explicit InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel, - sp<Looper> looper, InputConsumerCallbacks& callbacks); + sp<Looper> looper, InputConsumerCallbacks& callbacks, + std::unique_ptr<Resampler> resampler); + ~InputConsumerNoResampling(); /** @@ -99,6 +110,7 @@ private: std::shared_ptr<InputChannel> mChannel; sp<Looper> mLooper; InputConsumerCallbacks& mCallbacks; + std::unique_ptr<Resampler> mResampler; // Looper-related infrastructure /** diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h index 25d35e9fe7..55e058332d 100644 --- a/include/input/InputEventBuilders.h +++ b/include/input/InputEventBuilders.h @@ -127,7 +127,7 @@ public: return *this; } - MotionEvent build() { + MotionEvent build() const { std::vector<PointerProperties> pointerProperties; std::vector<PointerCoords> pointerCoords; for (const PointerBuilder& pointer : mPointers) { @@ -135,20 +135,22 @@ public: pointerCoords.push_back(pointer.buildCoords()); } + auto [xCursorPosition, yCursorPosition] = + std::make_pair(mRawXCursorPosition, mRawYCursorPosition); // Set mouse cursor position for the most common cases to avoid boilerplate. if (mSource == AINPUT_SOURCE_MOUSE && - !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) { - mRawXCursorPosition = pointerCoords[0].getX(); - mRawYCursorPosition = pointerCoords[0].getY(); + !MotionEvent::isValidCursorPosition(xCursorPosition, yCursorPosition)) { + xCursorPosition = pointerCoords[0].getX(); + yCursorPosition = pointerCoords[0].getY(); } MotionEvent event; event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC, mAction, mActionButton, mFlags, /*edgeFlags=*/0, AMETA_NONE, mButtonState, MotionClassification::NONE, mTransform, - /*xPrecision=*/0, /*yPrecision=*/0, mRawXCursorPosition, - mRawYCursorPosition, mRawTransform, mDownTime, mEventTime, - mPointers.size(), pointerProperties.data(), pointerCoords.data()); + /*xPrecision=*/0, /*yPrecision=*/0, xCursorPosition, yCursorPosition, + mRawTransform, mDownTime, mEventTime, mPointers.size(), + pointerProperties.data(), pointerCoords.data()); return event; } diff --git a/include/input/OWNERS b/include/input/OWNERS index c88bfe97ca..21d208f577 100644 --- a/include/input/OWNERS +++ b/include/input/OWNERS @@ -1 +1,2 @@ +# Bug component: 136048 include platform/frameworks/base:/INPUT_OWNERS diff --git a/include/input/Resampler.h b/include/input/Resampler.h new file mode 100644 index 0000000000..ff9c4b0868 --- /dev/null +++ b/include/input/Resampler.h @@ -0,0 +1,115 @@ +/** + * Copyright 2024 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 <optional> + +#include <input/Input.h> +#include <input/InputTransport.h> +#include <input/RingBuffer.h> +#include <utils/Timers.h> + +namespace android { + +/** + * Resampler is an interface for resampling MotionEvents. Every resampling implementation + * must use this interface to enable resampling inside InputConsumer's logic. + */ +struct Resampler { + virtual ~Resampler() = default; + + /** + * Tries to resample motionEvent at resampleTime. The provided resampleTime must be greater than + * the latest sample time of motionEvent. It is not guaranteed that resampling occurs at + * resampleTime. Interpolation may occur is futureSample is available. Otherwise, motionEvent + * may be resampled by another method, or not resampled at all. Furthermore, it is the + * implementer's responsibility to guarantee the following: + * - If resampling occurs, a single additional sample should be added to motionEvent. That is, + * if motionEvent had N samples before being passed to Resampler, then it will have N + 1 + * samples by the end of the resampling. No other field of motionEvent should be modified. + * - If resampling does not occur, then motionEvent must not be modified in any way. + */ + virtual void resampleMotionEvent(const std::chrono::nanoseconds resampleTime, + MotionEvent& motionEvent, + const InputMessage* futureSample) = 0; +}; + +class LegacyResampler final : public Resampler { +public: + /** + * Tries to resample `motionEvent` at `resampleTime` by adding a resampled sample at the end of + * `motionEvent` with eventTime equal to `resampleTime` and pointer coordinates determined by + * linear interpolation or linear extrapolation. An earlier `resampleTime` will be used if + * extrapolation takes place and `resampleTime` is too far in the future. If `futureSample` is + * not null, interpolation will occur. If `futureSample` is null and there is enough historical + * data, LegacyResampler will extrapolate. Otherwise, no resampling takes place and + * `motionEvent` is unmodified. + */ + void resampleMotionEvent(const std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent, + const InputMessage* futureSample) override; + +private: + struct Pointer { + PointerProperties properties; + PointerCoords coords; + }; + + struct Sample { + std::chrono::nanoseconds eventTime; + Pointer pointer; + + Sample(const std::chrono::nanoseconds eventTime, const PointerProperties& properties, + const PointerCoords& coords) + : eventTime{eventTime}, pointer{properties, coords} {} + }; + + /** + * Keeps track of the previous MotionEvent deviceId to enable comparison between the previous + * and the current deviceId. + */ + std::optional<DeviceId> mPreviousDeviceId; + + /** + * Up to two latest samples from MotionEvent. Updated every time resampleMotionEvent is called. + * Note: We store up to two samples in order to simplify the implementation. Although, + * calculations are possible with only one previous sample. + */ + RingBuffer<Sample> mLatestSamples{/*capacity=*/2}; + + /** + * 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 + * added to mLatestSamples.) + */ + void updateLatestSamples(const MotionEvent& motionEvent); + + /** + * May add a sample at the end of motionEvent with eventTime equal to resampleTime, and + * interpolated coordinates between the latest motionEvent sample and futureSample. + */ + void interpolate(const std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent, + const InputMessage& futureSample) const; + + /** + * May add a sample at the end of motionEvent by extrapolating from the latest two samples. The + * added sample either has eventTime equal to resampleTime, or an earlier time if resampleTime + * is too far in the future. + */ + void extrapolate(const std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent) const; +}; +} // namespace android
\ No newline at end of file diff --git a/include/input/VirtualInputDevice.h b/include/input/VirtualInputDevice.h index dabe45ce21..b6c630529c 100644 --- a/include/input/VirtualInputDevice.h +++ b/include/input/VirtualInputDevice.h @@ -17,14 +17,30 @@ #pragma once #include <android-base/unique_fd.h> +#include <input/Input.h> +#include <map> namespace android { +enum class DeviceType { + KEYBOARD, + MOUSE, + TOUCHSCREEN, + DPAD, + STYLUS, + ROTARY_ENCODER, +}; + +android::base::unique_fd openUinput(const char* readableName, int32_t vendorId, int32_t productId, + const char* phys, DeviceType deviceType, int32_t screenHeight, + int32_t screenWidth); + enum class UinputAction { RELEASE = 0, PRESS = 1, MOVE = 2, CANCEL = 3, + ftl_last = CANCEL, }; class VirtualInputDevice { diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index c57c9cdd62..53bd08d420 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -143,6 +143,22 @@ status_t IBinder::getExtension(sp<IBinder>* out) { return reply.readNullableStrongBinder(out); } +status_t IBinder::addFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) { + BpBinder* proxy = this->remoteBinder(); + if (proxy != nullptr) { + return proxy->addFrozenStateChangeCallback(callback); + } + return INVALID_OPERATION; +} + +status_t IBinder::removeFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) { + BpBinder* proxy = this->remoteBinder(); + if (proxy != nullptr) { + return proxy->removeFrozenStateChangeCallback(callback); + } + return INVALID_OPERATION; +} + status_t IBinder::getDebugPid(pid_t* out) { BBinder* local = this->localBinder(); if (local != nullptr) { diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 6594aa6309..eae844ca03 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -160,11 +160,12 @@ void BpBinder::ObjectManager::kill() // --------------------------------------------------------------------------- -sp<BpBinder> BpBinder::create(int32_t handle) { +sp<BpBinder> BpBinder::create(int32_t handle, std::function<void()>* postTask) { if constexpr (!kEnableKernelIpc) { LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time"); return nullptr; } + LOG_ALWAYS_FATAL_IF(postTask == nullptr, "BAD STATE"); int32_t trackedUid = -1; if (sCountByUidEnabled) { @@ -183,7 +184,11 @@ sp<BpBinder> BpBinder::create(int32_t handle) { ALOGE("Still too many binder proxy objects sent to uid %d from uid %d (%d proxies " "held)", getuid(), trackedUid, trackedValue); - if (sLimitCallback) sLimitCallback(trackedUid); + + if (sLimitCallback) { + *postTask = [=]() { sLimitCallback(trackedUid); }; + } + sLastLimitCallbackMap[trackedUid] = trackedValue; } } else { @@ -197,7 +202,11 @@ sp<BpBinder> BpBinder::create(int32_t handle) { ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)", getuid(), trackedUid, trackedValue); sTrackingMap[trackedUid] |= LIMIT_REACHED_MASK; - if (sLimitCallback) sLimitCallback(trackedUid); + + if (sLimitCallback) { + *postTask = [=]() { sLimitCallback(trackedUid); }; + } + sLastLimitCallbackMap[trackedUid] = trackedValue & COUNTING_VALUE_MASK; if (sBinderProxyThrottleCreate) { ALOGI("Throttling binder proxy creates from uid %d in uid %d until binder proxy" @@ -557,6 +566,123 @@ void BpBinder::sendObituary() } } +status_t BpBinder::addFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) { + LOG_ALWAYS_FATAL_IF(isRpcBinder(), + "addFrozenStateChangeCallback() is not supported for RPC Binder."); + LOG_ALWAYS_FATAL_IF(!kEnableKernelIpc, "Binder kernel driver disabled at build time"); + LOG_ALWAYS_FATAL_IF(ProcessState::self()->getThreadPoolMaxTotalThreadCount() == 0, + "addFrozenStateChangeCallback on %s but there are no threads " + "(yet?) listening to incoming transactions. See " + "ProcessState::startThreadPool " + "and ProcessState::setThreadPoolMaxThreadCount. Generally you should " + "setup the binder threadpool before other initialization steps.", + String8(getInterfaceDescriptor()).c_str()); + LOG_ALWAYS_FATAL_IF(callback == nullptr, + "addFrozenStateChangeCallback(): callback must be non-NULL"); + + const sp<FrozenStateChangeCallback> strongCallback = callback.promote(); + if (strongCallback == nullptr) { + return BAD_VALUE; + } + + { + RpcMutexUniqueLock _l(mLock); + if (!mFrozen) { + ALOGV("Requesting freeze notification: %p handle %d\n", this, binderHandle()); + IPCThreadState* self = IPCThreadState::self(); + status_t status = self->addFrozenStateChangeCallback(binderHandle(), this); + if (status != NO_ERROR) { + // Avoids logspam if kernel does not support freeze + // notification. + if (status != INVALID_OPERATION) { + ALOGE("IPCThreadState.addFrozenStateChangeCallback " + "failed with %s. %p handle %d\n", + statusToString(status).c_str(), this, binderHandle()); + } + return status; + } + mFrozen = std::make_unique<FrozenStateChange>(); + if (!mFrozen) { + std::ignore = + IPCThreadState::self()->removeFrozenStateChangeCallback(binderHandle(), + this); + return NO_MEMORY; + } + } + if (mFrozen->initialStateReceived) { + strongCallback->onStateChanged(wp<BpBinder>::fromExisting(this), + mFrozen->isFrozen + ? FrozenStateChangeCallback::State::FROZEN + : FrozenStateChangeCallback::State::UNFROZEN); + } + ssize_t res = mFrozen->callbacks.add(callback); + if (res < 0) { + return res; + } + return NO_ERROR; + } +} + +status_t BpBinder::removeFrozenStateChangeCallback(const wp<FrozenStateChangeCallback>& callback) { + LOG_ALWAYS_FATAL_IF(isRpcBinder(), + "removeFrozenStateChangeCallback() is not supported for RPC Binder."); + LOG_ALWAYS_FATAL_IF(!kEnableKernelIpc, "Binder kernel driver disabled at build time"); + + RpcMutexUniqueLock _l(mLock); + + const size_t N = mFrozen ? mFrozen->callbacks.size() : 0; + for (size_t i = 0; i < N; i++) { + if (mFrozen->callbacks.itemAt(i) == callback) { + mFrozen->callbacks.removeAt(i); + if (mFrozen->callbacks.size() == 0) { + ALOGV("Clearing freeze notification: %p handle %d\n", this, binderHandle()); + status_t status = + IPCThreadState::self()->removeFrozenStateChangeCallback(binderHandle(), + this); + if (status != NO_ERROR) { + ALOGE("Unexpected error from " + "IPCThreadState.removeFrozenStateChangeCallback: %s. " + "%p handle %d\n", + statusToString(status).c_str(), this, binderHandle()); + } + mFrozen.reset(); + } + return NO_ERROR; + } + } + + return NAME_NOT_FOUND; +} + +void BpBinder::onFrozenStateChanged(bool isFrozen) { + LOG_ALWAYS_FATAL_IF(isRpcBinder(), "onFrozenStateChanged is not supported for RPC Binder."); + LOG_ALWAYS_FATAL_IF(!kEnableKernelIpc, "Binder kernel driver disabled at build time"); + + ALOGV("Sending frozen state change notification for proxy %p handle %d, isFrozen=%s\n", this, + binderHandle(), isFrozen ? "true" : "false"); + + RpcMutexUniqueLock _l(mLock); + if (!mFrozen) { + return; + } + bool stateChanged = !mFrozen->initialStateReceived || mFrozen->isFrozen != isFrozen; + if (stateChanged) { + mFrozen->isFrozen = isFrozen; + mFrozen->initialStateReceived = true; + for (size_t i = 0; i < mFrozen->callbacks.size();) { + sp<FrozenStateChangeCallback> callback = mFrozen->callbacks.itemAt(i).promote(); + if (callback != nullptr) { + callback->onStateChanged(wp<BpBinder>::fromExisting(this), + isFrozen ? FrozenStateChangeCallback::State::FROZEN + : FrozenStateChangeCallback::State::UNFROZEN); + i++; + } else { + mFrozen->callbacks.removeItemsAt(i); + } + } + } +} + void BpBinder::reportOneDeath(const Obituary& obit) { sp<DeathRecipient> recipient = obit.recipient.promote(); @@ -686,6 +812,10 @@ void BpBinder::onLastStrongRef(const void* /*id*/) { if (ipc) ipc->clearDeathNotification(binderHandle(), this); mObituaries = nullptr; } + if (mFrozen != nullptr) { + std::ignore = IPCThreadState::self()->removeFrozenStateChangeCallback(binderHandle(), this); + mFrozen.reset(); + } mLock.unlock(); if (obits != nullptr) { diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 984c93d370..1d26d8543d 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -89,26 +89,33 @@ static const char* kReturnStrings[] = { "BR_FROZEN_REPLY", "BR_ONEWAY_SPAM_SUSPECT", "BR_TRANSACTION_PENDING_FROZEN", + "BR_FROZEN_BINDER", + "BR_CLEAR_FREEZE_NOTIFICATION_DONE", }; -static const char *kCommandStrings[] = { - "BC_TRANSACTION", - "BC_REPLY", - "BC_ACQUIRE_RESULT", - "BC_FREE_BUFFER", - "BC_INCREFS", - "BC_ACQUIRE", - "BC_RELEASE", - "BC_DECREFS", - "BC_INCREFS_DONE", - "BC_ACQUIRE_DONE", - "BC_ATTEMPT_ACQUIRE", - "BC_REGISTER_LOOPER", - "BC_ENTER_LOOPER", - "BC_EXIT_LOOPER", - "BC_REQUEST_DEATH_NOTIFICATION", - "BC_CLEAR_DEATH_NOTIFICATION", - "BC_DEAD_BINDER_DONE" +static const char* kCommandStrings[] = { + "BC_TRANSACTION", + "BC_REPLY", + "BC_ACQUIRE_RESULT", + "BC_FREE_BUFFER", + "BC_INCREFS", + "BC_ACQUIRE", + "BC_RELEASE", + "BC_DECREFS", + "BC_INCREFS_DONE", + "BC_ACQUIRE_DONE", + "BC_ATTEMPT_ACQUIRE", + "BC_REGISTER_LOOPER", + "BC_ENTER_LOOPER", + "BC_EXIT_LOOPER", + "BC_REQUEST_DEATH_NOTIFICATION", + "BC_CLEAR_DEATH_NOTIFICATION", + "BC_DEAD_BINDER_DONE", + "BC_TRANSACTION_SG", + "BC_REPLY_SG", + "BC_REQUEST_FREEZE_NOTIFICATION", + "BC_CLEAR_FREEZE_NOTIFICATION", + "BC_FREEZE_NOTIFICATION_DONE", }; static const int64_t kWorkSourcePropagatedBitIndex = 32; @@ -203,6 +210,18 @@ static const void* printReturnCommand(std::ostream& out, const void* _cmd) { out << ": death cookie " << (void*)(uint64_t)c; } break; + case BR_FROZEN_BINDER: { + const int32_t c = *cmd++; + const int32_t h = *cmd++; + const int32_t isFrozen = *cmd++; + out << ": freeze cookie " << (void*)(uint64_t)c << " isFrozen: " << isFrozen; + } break; + + case BR_CLEAR_FREEZE_NOTIFICATION_DONE: { + const int32_t c = *cmd++; + out << ": freeze cookie " << (void*)(uint64_t)c; + } break; + default: // no details to show for: BR_OK, BR_DEAD_REPLY, // BR_TRANSACTION_COMPLETE, BR_FINISHED @@ -270,11 +289,23 @@ static const void* printCommand(std::ostream& out, const void* _cmd) { out << ": handle=" << h << " (death cookie " << (void*)(uint64_t)c << ")"; } break; + case BC_REQUEST_FREEZE_NOTIFICATION: + case BC_CLEAR_FREEZE_NOTIFICATION: { + const int32_t h = *cmd++; + const int32_t c = *cmd++; + out << ": handle=" << h << " (freeze cookie " << (void*)(uint64_t)c << ")"; + } break; + case BC_DEAD_BINDER_DONE: { const int32_t c = *cmd++; out << ": death cookie " << (void*)(uint64_t)c; } break; + case BC_FREEZE_NOTIFICATION_DONE: { + const int32_t c = *cmd++; + out << ": freeze cookie " << (void*)(uint64_t)c; + } break; + default: // no details to show for: BC_REGISTER_LOOPER, BC_ENTER_LOOPER, // BC_EXIT_LOOPER @@ -953,6 +984,33 @@ status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) return NO_ERROR; } +status_t IPCThreadState::addFrozenStateChangeCallback(int32_t handle, BpBinder* proxy) { + static bool isSupported = + ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION); + if (!isSupported) { + return INVALID_OPERATION; + } + proxy->getWeakRefs()->incWeak(proxy); + mOut.writeInt32(BC_REQUEST_FREEZE_NOTIFICATION); + mOut.writeInt32((int32_t)handle); + mOut.writePointer((uintptr_t)proxy); + flushCommands(); + return NO_ERROR; +} + +status_t IPCThreadState::removeFrozenStateChangeCallback(int32_t handle, BpBinder* proxy) { + static bool isSupported = + ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION); + if (!isSupported) { + return INVALID_OPERATION; + } + mOut.writeInt32(BC_CLEAR_FREEZE_NOTIFICATION); + mOut.writeInt32((int32_t)handle); + mOut.writePointer((uintptr_t)proxy); + flushCommands(); + return NO_ERROR; +} + IPCThreadState::IPCThreadState() : mProcess(ProcessState::self()), mServingStackPointer(nullptr), @@ -1487,6 +1545,26 @@ status_t IPCThreadState::executeCommand(int32_t cmd) proxy->getWeakRefs()->decWeak(proxy); } break; + case BR_FROZEN_BINDER: { + const struct binder_frozen_state_info* data = + reinterpret_cast<const struct binder_frozen_state_info*>( + mIn.readInplace(sizeof(struct binder_frozen_state_info))); + if (data == nullptr) { + result = UNKNOWN_ERROR; + break; + } + BpBinder* proxy = (BpBinder*)data->cookie; + bool isFrozen = mIn.readInt32() > 0; + proxy->getPrivateAccessor().onFrozenStateChanged(data->is_frozen); + mOut.writeInt32(BC_FREEZE_NOTIFICATION_DONE); + mOut.writePointer(data->cookie); + } break; + + case BR_CLEAR_FREEZE_NOTIFICATION_DONE: { + BpBinder* proxy = (BpBinder*)mIn.readPointer(); + proxy->getWeakRefs()->decWeak(proxy); + } break; + case BR_FINISHED: result = TIMED_OUT; break; diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 8b80aed630..333f956556 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -253,8 +253,11 @@ bool checkPermission(const String16& permission, pid_t pid, uid_t uid, bool logP } } +#endif //__ANDROID_VNDK__ + void* openDeclaredPassthroughHal(const String16& interface, const String16& instance, int flag) { -#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__) && !defined(__ANDROID_NATIVE_BRIDGE__) +#if defined(__ANDROID__) && !defined(__ANDROID_VENDOR__) && !defined(__ANDROID_RECOVERY__) && \ + !defined(__ANDROID_NATIVE_BRIDGE__) sp<IServiceManager> sm = defaultServiceManager(); String16 name = interface + String16("/") + instance; if (!sm->isDeclared(name)) { @@ -274,8 +277,6 @@ void* openDeclaredPassthroughHal(const String16& interface, const String16& inst #endif } -#endif //__ANDROID_VNDK__ - // ---------------------------------------------------------------------- ServiceManagerShim::ServiceManagerShim(const sp<AidlServiceManager>& impl) { diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index a42ede29a2..5e7f1510bc 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -24,7 +24,6 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/Stability.h> -#include <cutils/atomic.h> #include <utils/AndroidThreads.h> #include <utils/String8.h> #include <utils/Thread.h> @@ -57,6 +56,25 @@ const char* kDefaultDriver = "/dev/binder"; // ------------------------------------------------------------------------- +namespace { +bool readDriverFeatureFile(const char* filename) { + int fd = open(filename, O_RDONLY | O_CLOEXEC); + char on; + if (fd == -1) { + ALOGE_IF(errno != ENOENT, "%s: cannot open %s: %s", __func__, filename, strerror(errno)); + return false; + } + if (read(fd, &on, sizeof(on)) == -1) { + ALOGE("%s: error reading to %s: %s", __func__, filename, strerror(errno)); + close(fd); + return false; + } + close(fd); + return on == '1'; +} + +} // namespace + namespace android { using namespace android::binder::impl; @@ -311,6 +329,7 @@ extern sp<BBinder> the_context_object; sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) { sp<IBinder> result; + std::function<void()> postTask; std::unique_lock<std::mutex> _l(mLock); @@ -358,7 +377,7 @@ sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) return nullptr; } - sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle); + sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle, &postTask); e->binder = b.get(); if (b) e->refs = b->getWeakRefs(); result = b; @@ -371,6 +390,10 @@ sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) } } + _l.unlock(); + + if (postTask) postTask(); + return result; } @@ -387,7 +410,7 @@ void ProcessState::expungeHandle(int32_t handle, IBinder* binder) } String8 ProcessState::makeBinderThreadName() { - int32_t s = android_atomic_add(1, &mThreadPoolSeq); + int32_t s = mThreadPoolSeq.fetch_add(1, std::memory_order_release); pid_t pid = getpid(); std::string_view driverName = mDriverName.c_str(); @@ -429,8 +452,17 @@ status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) { } size_t ProcessState::getThreadPoolMaxTotalThreadCount() const { + // Need to read `mKernelStartedThreads` before `mThreadPoolStarted` (with + // non-relaxed memory ordering) to avoid a race like the following: + // + // thread A: if (mThreadPoolStarted) { // evaluates false + // thread B: mThreadPoolStarted = true; + // thread B: mKernelStartedThreads++; + // thread A: size_t kernelStarted = mKernelStartedThreads; + // thread A: LOG_ALWAYS_FATAL_IF(kernelStarted != 0, ...); + size_t kernelStarted = mKernelStartedThreads; + if (mThreadPoolStarted) { - size_t kernelStarted = mKernelStartedThreads; size_t max = mMaxThreads; size_t current = mCurrentThreads; @@ -460,7 +492,6 @@ size_t ProcessState::getThreadPoolMaxTotalThreadCount() const { // must not be initialized or maybe has poll thread setup, we // currently don't track this in libbinder - size_t kernelStarted = mKernelStartedThreads; LOG_ALWAYS_FATAL_IF(kernelStarted != 0, "Expecting 0 kernel started threads but have %zu", kernelStarted); return mCurrentThreads; @@ -472,27 +503,20 @@ bool ProcessState::isThreadPoolStarted() const { #define DRIVER_FEATURES_PATH "/dev/binderfs/features/" bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) { - static const char* const names[] = { - [static_cast<int>(DriverFeature::ONEWAY_SPAM_DETECTION)] = - DRIVER_FEATURES_PATH "oneway_spam_detection", - [static_cast<int>(DriverFeature::EXTENDED_ERROR)] = - DRIVER_FEATURES_PATH "extended_error", - }; - int fd = open(names[static_cast<int>(feature)], O_RDONLY | O_CLOEXEC); - char on; - if (fd == -1) { - ALOGE_IF(errno != ENOENT, "%s: cannot open %s: %s", __func__, - names[static_cast<int>(feature)], strerror(errno)); - return false; + // Use static variable to cache the results. + if (feature == DriverFeature::ONEWAY_SPAM_DETECTION) { + static bool enabled = readDriverFeatureFile(DRIVER_FEATURES_PATH "oneway_spam_detection"); + return enabled; } - if (read(fd, &on, sizeof(on)) == -1) { - ALOGE("%s: error reading to %s: %s", __func__, - names[static_cast<int>(feature)], strerror(errno)); - close(fd); - return false; + if (feature == DriverFeature::EXTENDED_ERROR) { + static bool enabled = readDriverFeatureFile(DRIVER_FEATURES_PATH "extended_error"); + return enabled; } - close(fd); - return on == '1'; + if (feature == DriverFeature::FREEZE_NOTIFICATION) { + static bool enabled = readDriverFeatureFile(DRIVER_FEATURES_PATH "freeze_notification"); + return enabled; + } + return false; } status_t ProcessState::enableOnewaySpamDetection(bool enable) { @@ -577,7 +601,7 @@ ProcessState::ProcessState(const char* driver) #ifdef __ANDROID__ LOG_ALWAYS_FATAL_IF(!opened.ok(), "Binder driver '%s' could not be opened. Error: %s. Terminating.", - error.c_str(), driver); + driver, error.c_str()); #endif if (opened.ok()) { diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h index b3a2d9ec28..65cdcd7735 100644 --- a/libs/binder/binder_module.h +++ b/libs/binder/binder_module.h @@ -32,4 +32,34 @@ #include <linux/android/binder.h> #include <sys/ioctl.h> +struct binder_frozen_state_info { + binder_uintptr_t cookie; + __u32 is_frozen; +}; + +#ifndef BR_FROZEN_BINDER +// Temporary definition of BR_FROZEN_BINDER until UAPI binder.h includes it. +#define BR_FROZEN_BINDER _IOR('r', 21, struct binder_frozen_state_info) +#endif // BR_FROZEN_BINDER + +#ifndef BR_CLEAR_FREEZE_NOTIFICATION_DONE +// Temporary definition of BR_CLEAR_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it. +#define BR_CLEAR_FREEZE_NOTIFICATION_DONE _IOR('r', 22, binder_uintptr_t) +#endif // BR_CLEAR_FREEZE_NOTIFICATION_DONE + +#ifndef BC_REQUEST_FREEZE_NOTIFICATION +// Temporary definition of BC_REQUEST_FREEZE_NOTIFICATION until UAPI binder.h includes it. +#define BC_REQUEST_FREEZE_NOTIFICATION _IOW('c', 19, struct binder_handle_cookie) +#endif // BC_REQUEST_FREEZE_NOTIFICATION + +#ifndef BC_CLEAR_FREEZE_NOTIFICATION +// Temporary definition of BC_CLEAR_FREEZE_NOTIFICATION until UAPI binder.h includes it. +#define BC_CLEAR_FREEZE_NOTIFICATION _IOW('c', 20, struct binder_handle_cookie) +#endif // BC_CLEAR_FREEZE_NOTIFICATION + +#ifndef BC_FREEZE_NOTIFICATION_DONE +// Temporary definition of BC_FREEZE_NOTIFICATION_DONE until UAPI binder.h includes it. +#define BC_FREEZE_NOTIFICATION_DONE _IOW('c', 21, binder_uintptr_t) +#endif // BC_FREEZE_NOTIFICATION_DONE + #endif // _BINDER_MODULE_H_ diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index d7f74c4152..7518044ce6 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -29,6 +29,7 @@ // --------------------------------------------------------------------------- namespace android { +class IPCThreadState; class RpcSession; class RpcState; namespace internal { @@ -66,6 +67,12 @@ public: void* cookie = nullptr, uint32_t flags = 0, wp<DeathRecipient>* outRecipient = nullptr); + [[nodiscard]] status_t addFrozenStateChangeCallback( + const wp<FrozenStateChangeCallback>& recipient); + + [[nodiscard]] status_t removeFrozenStateChangeCallback( + const wp<FrozenStateChangeCallback>& recipient); + LIBBINDER_EXPORTED virtual void* attachObject(const void* objectID, void* object, void* cleanupCookie, object_cleanup_func func) final; @@ -75,7 +82,6 @@ public: LIBBINDER_EXPORTED sp<IBinder> lookupOrCreateWeak(const void* objectID, IBinder::object_make_func make, const void* makeArgs); - LIBBINDER_EXPORTED virtual BpBinder* remoteBinder(); LIBBINDER_EXPORTED void sendObituary(); @@ -132,9 +138,14 @@ public: friend class ::android::ProcessState; friend class ::android::RpcSession; friend class ::android::RpcState; - explicit PrivateAccessor(const BpBinder* binder) : mBinder(binder) {} + friend class ::android::IPCThreadState; + explicit PrivateAccessor(const BpBinder* binder) + : mBinder(binder), mMutableBinder(nullptr) {} + explicit PrivateAccessor(BpBinder* binder) : mBinder(binder), mMutableBinder(binder) {} - static sp<BpBinder> create(int32_t handle) { return BpBinder::create(handle); } + static sp<BpBinder> create(int32_t handle, std::function<void()>* postTask) { + return BpBinder::create(handle, postTask); + } static sp<BpBinder> create(const sp<RpcSession>& session, uint64_t address) { return BpBinder::create(session, address); } @@ -146,17 +157,22 @@ public: uint64_t rpcAddress() const { return mBinder->rpcAddress(); } const sp<RpcSession>& rpcSession() const { return mBinder->rpcSession(); } + void onFrozenStateChanged(bool isFrozen) { mMutableBinder->onFrozenStateChanged(isFrozen); } const BpBinder* mBinder; + BpBinder* mMutableBinder; }; + LIBBINDER_EXPORTED const PrivateAccessor getPrivateAccessor() const { return PrivateAccessor(this); } + PrivateAccessor getPrivateAccessor() { return PrivateAccessor(this); } + private: friend PrivateAccessor; friend class sp<BpBinder>; - static sp<BpBinder> create(int32_t handle); + static sp<BpBinder> create(int32_t handle, std::function<void()>* postTask); static sp<BpBinder> create(const sp<RpcSession>& session, uint64_t address); struct BinderHandle { @@ -192,6 +208,14 @@ private: uint32_t flags; }; + void onFrozenStateChanged(bool isFrozen); + + struct FrozenStateChange { + bool isFrozen = false; + Vector<wp<FrozenStateChangeCallback>> callbacks; + bool initialStateReceived = false; + }; + void reportOneDeath(const Obituary& obit); bool isDescriptorCached() const; @@ -199,6 +223,7 @@ private: volatile int32_t mAlive; volatile int32_t mObitsSent; Vector<Obituary>* mObituaries; + std::unique_ptr<FrozenStateChange> mFrozen; ObjectManager mObjects; mutable String16 mDescriptorCache; int32_t mTrackedUid; diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h index 4eb1c082e2..1ed7c91de6 100644 --- a/libs/binder/include/binder/IBinder.h +++ b/libs/binder/include/binder/IBinder.h @@ -202,9 +202,18 @@ public: virtual void binderDied(const wp<IBinder>& who) = 0; }; - #if defined(__clang__) - #pragma clang diagnostic pop - #endif + class FrozenStateChangeCallback : public virtual RefBase { + public: + enum class State { + FROZEN, + UNFROZEN, + }; + virtual void onStateChanged(const wp<IBinder>& who, State state) = 0; + }; + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif /** * Register the @a recipient for a notification if this binder @@ -253,6 +262,48 @@ public: uint32_t flags = 0, wp<DeathRecipient>* outRecipient = nullptr) = 0; + /** + * addFrozenStateChangeCallback provides a callback mechanism to notify + * about process frozen/unfrozen events. Upon registration and any + * subsequent state changes, the callback is invoked with the latest process + * frozen state. + * + * If the listener process (the one using this API) is itself frozen, state + * change events might be combined into a single one with the latest state. + * (meaning 'frozen, unfrozen' might just be 'unfrozen'). This single event + * would then be delivered when the listener process becomes unfrozen. + * Similarly, if an event happens before the previous event is consumed, + * they might be combined. This means the callback might not be called for + * every single state change, so don't rely on this API to count how many + * times the state has changed. + * + * @note When all references to the binder are dropped, the callback is + * automatically removed. So, you must hold onto a binder in order to + * receive notifications about it. + * + * @note You will only receive freeze notifications for remote binders, as + * local binders by definition can't be frozen without you being frozen as + * well. Trying to use this function on a local binder will result in an + * INVALID_OPERATION code being returned and nothing happening. + * + * @note This binder always holds a weak reference to the callback. + * + * @note You will only receive a weak reference to the binder object. You + * should not try to promote this to a strong reference. (Nor should you + * need to, as there is nothing useful you can directly do with it now that + * it has passed on.) + */ + [[nodiscard]] status_t addFrozenStateChangeCallback( + const wp<FrozenStateChangeCallback>& callback); + + /** + * Remove a previously registered freeze callback. + * The @a callback will no longer be called if this object + * changes its frozen state. + */ + [[nodiscard]] status_t removeFrozenStateChangeCallback( + const wp<FrozenStateChangeCallback>& callback); + virtual bool checkSubclass(const void* subclassID) const; typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie); diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index 09ab442c7d..9ef4e694dd 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -174,6 +174,8 @@ public: LIBBINDER_EXPORTED static void expungeHandle(int32_t handle, IBinder* binder); LIBBINDER_EXPORTED status_t requestDeathNotification(int32_t handle, BpBinder* proxy); LIBBINDER_EXPORTED status_t clearDeathNotification(int32_t handle, BpBinder* proxy); + [[nodiscard]] status_t addFrozenStateChangeCallback(int32_t handle, BpBinder* proxy); + [[nodiscard]] status_t removeFrozenStateChangeCallback(int32_t handle, BpBinder* proxy); LIBBINDER_EXPORTED static void shutdown(); @@ -210,13 +212,14 @@ private: IPCThreadState(); ~IPCThreadState(); - status_t sendReply(const Parcel& reply, uint32_t flags); - status_t waitForResponse(Parcel* reply, status_t* acquireResult = nullptr); - status_t talkWithDriver(bool doReceive = true); - status_t writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle, uint32_t code, - const Parcel& data, status_t* statusBuffer); - status_t getAndExecuteCommand(); - status_t executeCommand(int32_t command); + [[nodiscard]] status_t sendReply(const Parcel& reply, uint32_t flags); + [[nodiscard]] status_t waitForResponse(Parcel* reply, status_t* acquireResult = nullptr); + [[nodiscard]] status_t talkWithDriver(bool doReceive = true); + [[nodiscard]] status_t writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle, + uint32_t code, const Parcel& data, + status_t* statusBuffer); + [[nodiscard]] status_t getAndExecuteCommand(); + [[nodiscard]] status_t executeCommand(int32_t command); void processPendingDerefs(); void processPostWriteDerefs(); diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index 021bd58e21..21bfd42fcc 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -133,6 +133,7 @@ public: enum class DriverFeature { ONEWAY_SPAM_DETECTION, EXTENDED_ERROR, + FREEZE_NOTIFICATION, }; // Determine whether a feature is supported by the binder driver. LIBBINDER_EXPORTED static bool isDriverFeatureEnabled(const DriverFeature feature); @@ -188,8 +189,8 @@ private: Vector<handle_entry> mHandleToObject; bool mForked; - bool mThreadPoolStarted; - volatile int32_t mThreadPoolSeq; + std::atomic_bool mThreadPoolStarted; + std::atomic_int32_t mThreadPoolSeq; CallRestriction mCallRestriction; }; diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs index 33dfe19fe9..7f70396882 100644 --- a/libs/binder/rust/src/parcel/parcelable.rs +++ b/libs/binder/rust/src/parcel/parcelable.rs @@ -1333,7 +1333,7 @@ mod tests { let vec = Vec::<u8>::deserialize(parcel.borrowed_ref()).unwrap(); assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]); - let u16s = [u16::max_value(), 12_345, 42, 117]; + let u16s = [u16::MAX, 12_345, 42, 117]; // SAFETY: start is less than the current size of the parcel data buffer, because we haven't // made it any shorter since we got the position. @@ -1348,7 +1348,7 @@ mod tests { } assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value() + assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::MAX assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345 assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117 @@ -1361,9 +1361,9 @@ mod tests { let vec = Vec::<u16>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]); + assert_eq!(vec, [u16::MAX, 12_345, 42, 117]); - let i16s = [i16::max_value(), i16::min_value(), 42, -117]; + let i16s = [i16::MAX, i16::MIN, 42, -117]; // SAFETY: start is less than the current size of the parcel data buffer, because we haven't // made it any shorter since we got the position. @@ -1378,8 +1378,8 @@ mod tests { } assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value() - assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value() + assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::MAX + assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::MIN assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117 @@ -1391,9 +1391,9 @@ mod tests { let vec = Vec::<i16>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]); + assert_eq!(vec, [i16::MAX, i16::MIN, 42, -117]); - let u32s = [u32::max_value(), 12_345, 42, 117]; + let u32s = [u32::MAX, 12_345, 42, 117]; // SAFETY: start is less than the current size of the parcel data buffer, because we haven't // made it any shorter since we got the position. @@ -1408,7 +1408,7 @@ mod tests { } assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value() + assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::MAX assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345 assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117 @@ -1421,9 +1421,9 @@ mod tests { let vec = Vec::<u32>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]); + assert_eq!(vec, [u32::MAX, 12_345, 42, 117]); - let i32s = [i32::max_value(), i32::min_value(), 42, -117]; + let i32s = [i32::MAX, i32::MIN, 42, -117]; // SAFETY: start is less than the current size of the parcel data buffer, because we haven't // made it any shorter since we got the position. @@ -1438,8 +1438,8 @@ mod tests { } assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value() - assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value() + assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::MAX + assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::MIN assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117 @@ -1451,9 +1451,9 @@ mod tests { let vec = Vec::<i32>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]); + assert_eq!(vec, [i32::MAX, i32::MIN, 42, -117]); - let u64s = [u64::max_value(), 12_345, 42, 117]; + let u64s = [u64::MAX, 12_345, 42, 117]; // SAFETY: start is less than the current size of the parcel data buffer, because we haven't // made it any shorter since we got the position. @@ -1469,9 +1469,9 @@ mod tests { let vec = Vec::<u64>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]); + assert_eq!(vec, [u64::MAX, 12_345, 42, 117]); - let i64s = [i64::max_value(), i64::min_value(), 42, -117]; + let i64s = [i64::MAX, i64::MIN, 42, -117]; // SAFETY: start is less than the current size of the parcel data buffer, because we haven't // made it any shorter since we got the position. @@ -1487,9 +1487,9 @@ mod tests { let vec = Vec::<i64>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]); + assert_eq!(vec, [i64::MAX, i64::MIN, 42, -117]); - let f32s = [std::f32::NAN, std::f32::INFINITY, 1.23456789, std::f32::EPSILON]; + let f32s = [f32::NAN, f32::INFINITY, 1.23456789, f32::EPSILON]; // SAFETY: start is less than the current size of the parcel data buffer, because we haven't // made it any shorter since we got the position. @@ -1509,7 +1509,7 @@ mod tests { assert!(vec[0].is_nan()); assert_eq!(vec[1..], f32s[1..]); - let f64s = [std::f64::NAN, std::f64::INFINITY, 1.234567890123456789, std::f64::EPSILON]; + let f64s = [f64::NAN, f64::INFINITY, 1.234567890123456789, f64::EPSILON]; // SAFETY: start is less than the current size of the parcel data buffer, because we haven't // made it any shorter since we got the position. diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs index 2b6c282530..a902e96739 100644 --- a/libs/binder/rust/tests/serialization.rs +++ b/libs/binder/rust/tests/serialization.rs @@ -124,7 +124,7 @@ fn on_transact( bindings::Transaction_TEST_BYTE => { assert_eq!(parcel.read::<i8>()?, 0); assert_eq!(parcel.read::<i8>()?, 1); - assert_eq!(parcel.read::<i8>()?, i8::max_value()); + assert_eq!(parcel.read::<i8>()?, i8::MAX); // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::<Vec<i8>>()?, unsafe { bindings::TESTDATA_I8 }); // SAFETY: Just reading an extern constant. @@ -133,7 +133,7 @@ fn on_transact( reply.write(&0i8)?; reply.write(&1i8)?; - reply.write(&i8::max_value())?; + reply.write(&i8::MAX)?; // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_I8 }[..])?; // SAFETY: Just reading an extern constant. @@ -143,14 +143,14 @@ fn on_transact( bindings::Transaction_TEST_U16 => { assert_eq!(parcel.read::<u16>()?, 0); assert_eq!(parcel.read::<u16>()?, 1); - assert_eq!(parcel.read::<u16>()?, u16::max_value()); + assert_eq!(parcel.read::<u16>()?, u16::MAX); // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::<Vec<u16>>()?, unsafe { bindings::TESTDATA_CHARS }); assert_eq!(parcel.read::<Option<Vec<u16>>>()?, None); reply.write(&0u16)?; reply.write(&1u16)?; - reply.write(&u16::max_value())?; + reply.write(&u16::MAX)?; // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_CHARS }[..])?; reply.write(&(None as Option<Vec<u16>>))?; @@ -158,14 +158,14 @@ fn on_transact( bindings::Transaction_TEST_I32 => { assert_eq!(parcel.read::<i32>()?, 0); assert_eq!(parcel.read::<i32>()?, 1); - assert_eq!(parcel.read::<i32>()?, i32::max_value()); + assert_eq!(parcel.read::<i32>()?, i32::MAX); // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::<Vec<i32>>()?, unsafe { bindings::TESTDATA_I32 }); assert_eq!(parcel.read::<Option<Vec<i32>>>()?, None); reply.write(&0i32)?; reply.write(&1i32)?; - reply.write(&i32::max_value())?; + reply.write(&i32::MAX)?; // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_I32 }[..])?; reply.write(&(None as Option<Vec<i32>>))?; @@ -173,14 +173,14 @@ fn on_transact( bindings::Transaction_TEST_I64 => { assert_eq!(parcel.read::<i64>()?, 0); assert_eq!(parcel.read::<i64>()?, 1); - assert_eq!(parcel.read::<i64>()?, i64::max_value()); + assert_eq!(parcel.read::<i64>()?, i64::MAX); // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::<Vec<i64>>()?, unsafe { bindings::TESTDATA_I64 }); assert_eq!(parcel.read::<Option<Vec<i64>>>()?, None); reply.write(&0i64)?; reply.write(&1i64)?; - reply.write(&i64::max_value())?; + reply.write(&i64::MAX)?; // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_I64 }[..])?; reply.write(&(None as Option<Vec<i64>>))?; @@ -188,14 +188,14 @@ fn on_transact( bindings::Transaction_TEST_U64 => { assert_eq!(parcel.read::<u64>()?, 0); assert_eq!(parcel.read::<u64>()?, 1); - assert_eq!(parcel.read::<u64>()?, u64::max_value()); + assert_eq!(parcel.read::<u64>()?, u64::MAX); // SAFETY: Just reading an extern constant. assert_eq!(parcel.read::<Vec<u64>>()?, unsafe { bindings::TESTDATA_U64 }); assert_eq!(parcel.read::<Option<Vec<u64>>>()?, None); reply.write(&0u64)?; reply.write(&1u64)?; - reply.write(&u64::max_value())?; + reply.write(&u64::MAX)?; // SAFETY: Just reading an extern constant. reply.write(&unsafe { bindings::TESTDATA_U64 }[..])?; reply.write(&(None as Option<Vec<u64>>))?; diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index dd50fbdc38..21c32acb0a 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -42,6 +42,9 @@ cc_test { defaults: ["binder_test_defaults"], header_libs: ["libbinder_headers"], srcs: ["binderDriverInterfaceTest.cpp"], + shared_libs: [ + "libbinder", + ], test_suites: [ "device-tests", "vts", @@ -127,6 +130,7 @@ cc_test { "libbase", "libbinder", "liblog", + "libprocessgroup", "libutils", ], static_libs: [ diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp index 7be4f21cee..af8286008f 100644 --- a/libs/binder/tests/binderDriverInterfaceTest.cpp +++ b/libs/binder/tests/binderDriverInterfaceTest.cpp @@ -20,12 +20,15 @@ #include <stdlib.h> #include <binder/IBinder.h> +#include <binder/ProcessState.h> #include <gtest/gtest.h> #include <linux/android/binder.h> #include <poll.h> #include <sys/ioctl.h> #include <sys/mman.h> +#include "../binder_module.h" + #define BINDER_DEV_NAME "/dev/binder" testing::Environment* binder_env; @@ -365,6 +368,251 @@ TEST_F(BinderDriverInterfaceTest, RequestDeathNotification) { binderTestReadEmpty(); } +TEST_F(BinderDriverInterfaceTest, RequestFrozenNotification) { + if (!android::ProcessState::isDriverFeatureEnabled( + android::ProcessState::DriverFeature::FREEZE_NOTIFICATION)) { + GTEST_SKIP() << "Skipping test for kernels that support freeze notification"; + return; + } + binder_uintptr_t cookie = 1234; + struct { + uint32_t cmd0; + uint32_t arg0; + uint32_t cmd1; + struct binder_handle_cookie arg1; + } __attribute__((packed)) bc1 = { + .cmd0 = BC_INCREFS, + .arg0 = 0, + .cmd1 = BC_REQUEST_FREEZE_NOTIFICATION, + .arg1 = + { + .handle = 0, + .cookie = cookie, + }, + }; + struct { + uint32_t cmd0; + // Expecting a BR_FROZEN_BINDER since BC_REQUEST_FREEZE_NOTIFICATION + // above should lead to an immediate notification of the current state. + uint32_t cmd1; + struct binder_frozen_state_info arg1; + uint32_t pad[16]; + } __attribute__((packed)) br1; + struct { + uint32_t cmd2; + binder_uintptr_t arg2; + uint32_t cmd3; + struct binder_handle_cookie arg3; + uint32_t cmd4; + uint32_t arg4; + } __attribute__((packed)) bc2 = { + // Tell kernel that userspace has done handling BR_FROZEN_BINDER. + .cmd2 = BC_FREEZE_NOTIFICATION_DONE, + .arg2 = cookie, + .cmd3 = BC_CLEAR_FREEZE_NOTIFICATION, + .arg3 = + { + .handle = 0, + .cookie = cookie, + }, + .cmd4 = BC_DECREFS, + .arg4 = 0, + }; + struct { + uint32_t cmd2; + uint32_t cmd3; + binder_uintptr_t arg3; + uint32_t pad[16]; + } __attribute__((packed)) br2; + + struct binder_write_read bwr1 = binder_write_read(); + bwr1.write_buffer = (uintptr_t)&bc1; + bwr1.write_size = sizeof(bc1); + bwr1.read_buffer = (uintptr_t)&br1; + bwr1.read_size = sizeof(br1); + binderTestIoctl(BINDER_WRITE_READ, &bwr1); + EXPECT_EQ(sizeof(bc1), bwr1.write_consumed); + EXPECT_EQ(sizeof(br1) - sizeof(br1.pad), bwr1.read_consumed); + EXPECT_EQ(BR_NOOP, br1.cmd0); + ASSERT_EQ(BR_FROZEN_BINDER, br1.cmd1); + EXPECT_FALSE(br1.arg1.is_frozen); + + struct binder_write_read bwr2 = binder_write_read(); + bwr2.write_buffer = (uintptr_t)&bc2; + bwr2.write_size = sizeof(bc2); + bwr2.read_buffer = (uintptr_t)&br2; + bwr2.read_size = sizeof(br2); + binderTestIoctl(BINDER_WRITE_READ, &bwr2); + EXPECT_EQ(sizeof(bc2), bwr2.write_consumed); + EXPECT_EQ(sizeof(br2) - sizeof(br2.pad), bwr2.read_consumed); + EXPECT_EQ(BR_NOOP, br2.cmd2); + EXPECT_EQ(BR_CLEAR_FREEZE_NOTIFICATION_DONE, br2.cmd3); + EXPECT_EQ(cookie, br2.arg3); + + binderTestReadEmpty(); +} + +TEST_F(BinderDriverInterfaceTest, OverwritePendingFrozenNotification) { + if (!android::ProcessState::isDriverFeatureEnabled( + android::ProcessState::DriverFeature::FREEZE_NOTIFICATION)) { + GTEST_SKIP() << "Skipping test for kernels that support freeze notification"; + return; + } + binder_uintptr_t cookie = 1234; + struct { + uint32_t cmd0; + uint32_t arg0; + uint32_t cmd1; + struct binder_handle_cookie arg1; + uint32_t cmd2; + struct binder_handle_cookie arg2; + uint32_t cmd3; + uint32_t arg3; + } __attribute__((packed)) bc = { + .cmd0 = BC_INCREFS, + .arg0 = 0, + .cmd1 = BC_REQUEST_FREEZE_NOTIFICATION, + // This BC_REQUEST_FREEZE_NOTIFICATION should lead to a pending + // frozen notification inserted into the queue. + .arg1 = + { + .handle = 0, + .cookie = cookie, + }, + // Send BC_CLEAR_FREEZE_NOTIFICATION before the above frozen + // notification has a chance of being sent. The notification should + // be overwritten. Userspace is expected to only receive + // BR_CLEAR_FREEZE_NOTIFICATION_DONE. + .cmd2 = BC_CLEAR_FREEZE_NOTIFICATION, + .arg2 = + { + .handle = 0, + .cookie = cookie, + }, + .cmd3 = BC_DECREFS, + .arg3 = 0, + }; + struct { + uint32_t cmd0; + uint32_t cmd1; + binder_uintptr_t arg1; + uint32_t pad[16]; + } __attribute__((packed)) br; + struct binder_write_read bwr = binder_write_read(); + + bwr.write_buffer = (uintptr_t)&bc; + bwr.write_size = sizeof(bc); + bwr.read_buffer = (uintptr_t)&br; + bwr.read_size = sizeof(br); + + binderTestIoctl(BINDER_WRITE_READ, &bwr); + EXPECT_EQ(sizeof(bc), bwr.write_consumed); + EXPECT_EQ(sizeof(br) - sizeof(br.pad), bwr.read_consumed); + EXPECT_EQ(BR_NOOP, br.cmd0); + EXPECT_EQ(BR_CLEAR_FREEZE_NOTIFICATION_DONE, br.cmd1); + EXPECT_EQ(cookie, br.arg1); + binderTestReadEmpty(); +} + +TEST_F(BinderDriverInterfaceTest, ResendFrozenNotification) { + if (!android::ProcessState::isDriverFeatureEnabled( + android::ProcessState::DriverFeature::FREEZE_NOTIFICATION)) { + GTEST_SKIP() << "Skipping test for kernels that support freeze notification"; + return; + } + binder_uintptr_t cookie = 1234; + struct { + uint32_t cmd0; + uint32_t arg0; + uint32_t cmd1; + struct binder_handle_cookie arg1; + } __attribute__((packed)) bc1 = { + .cmd0 = BC_INCREFS, + .arg0 = 0, + .cmd1 = BC_REQUEST_FREEZE_NOTIFICATION, + .arg1 = + { + .handle = 0, + .cookie = cookie, + }, + }; + struct { + uint32_t cmd0; + uint32_t cmd1; + struct binder_frozen_state_info arg1; + uint32_t pad[16]; + } __attribute__((packed)) br1; + struct { + uint32_t cmd2; + struct binder_handle_cookie arg2; + } __attribute__((packed)) bc2 = { + // Clear the notification before acknowledging the in-flight + // BR_FROZEN_BINDER. Kernel should hold off sending + // BR_CLEAR_FREEZE_NOTIFICATION_DONE until the acknowledgement + // reaches kernel. + .cmd2 = BC_CLEAR_FREEZE_NOTIFICATION, + .arg2 = + { + .handle = 0, + .cookie = cookie, + }, + }; + struct { + uint32_t pad[16]; + } __attribute__((packed)) br2; + struct { + uint32_t cmd3; + binder_uintptr_t arg3; + uint32_t cmd4; + uint32_t arg4; + } __attribute__((packed)) bc3 = { + // Send the acknowledgement. Now the kernel should send out + // BR_CLEAR_FREEZE_NOTIFICATION_DONE. + .cmd3 = BC_FREEZE_NOTIFICATION_DONE, + .arg3 = cookie, + .cmd4 = BC_DECREFS, + .arg4 = 0, + }; + struct { + uint32_t cmd2; + uint32_t cmd3; + binder_uintptr_t arg3; + uint32_t pad[16]; + } __attribute__((packed)) br3; + + struct binder_write_read bwr1 = binder_write_read(); + bwr1.write_buffer = (uintptr_t)&bc1; + bwr1.write_size = sizeof(bc1); + bwr1.read_buffer = (uintptr_t)&br1; + bwr1.read_size = sizeof(br1); + binderTestIoctl(BINDER_WRITE_READ, &bwr1); + EXPECT_EQ(sizeof(bc1), bwr1.write_consumed); + EXPECT_EQ(sizeof(br1) - sizeof(br1.pad), bwr1.read_consumed); + EXPECT_EQ(BR_NOOP, br1.cmd0); + ASSERT_EQ(BR_FROZEN_BINDER, br1.cmd1); + EXPECT_FALSE(br1.arg1.is_frozen); + + struct binder_write_read bwr2 = binder_write_read(); + bwr2.write_buffer = (uintptr_t)&bc2; + bwr2.write_size = sizeof(bc2); + bwr2.read_buffer = (uintptr_t)&br2; + bwr2.read_size = sizeof(br2); + binderTestIoctlSuccessOrError(BINDER_WRITE_READ, &bwr2, EAGAIN); + binderTestReadEmpty(); + + struct binder_write_read bwr3 = binder_write_read(); + bwr3.write_buffer = (uintptr_t)&bc3; + bwr3.write_size = sizeof(bc3); + bwr3.read_buffer = (uintptr_t)&br3; + bwr3.read_size = sizeof(br3); + binderTestIoctl(BINDER_WRITE_READ, &bwr3); + EXPECT_EQ(sizeof(bc3), bwr3.write_consumed); + EXPECT_EQ(sizeof(br3) - sizeof(br3.pad), bwr3.read_consumed); + EXPECT_EQ(BR_CLEAR_FREEZE_NOTIFICATION_DONE, br3.cmd3); + EXPECT_EQ(cookie, br3.arg3); + binderTestReadEmpty(); +} + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 9b1ba01146..bcab6decca 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -27,6 +27,7 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <android-base/logging.h> #include <android-base/properties.h> #include <android-base/result-gmock.h> #include <binder/Binder.h> @@ -39,6 +40,8 @@ #include <binder/RpcSession.h> #include <binder/Status.h> #include <binder/unique_fd.h> +#include <input/BlockingQueue.h> +#include <processgroup/processgroup.h> #include <utils/Flattenable.h> #include <linux/sched.h> @@ -57,6 +60,7 @@ using namespace std::chrono_literals; using android::base::testing::HasValue; using android::binder::Status; using android::binder::unique_fd; +using std::chrono_literals::operator""ms; using testing::ExplainMatchResult; using testing::Matcher; using testing::Not; @@ -115,6 +119,8 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, BINDER_LIB_TEST_GETPID, BINDER_LIB_TEST_GETUID, + BINDER_LIB_TEST_LISTEN_FOR_FROZEN_STATE_CHANGE, + BINDER_LIB_TEST_CONSUME_STATE_CHANGE_EVENTS, BINDER_LIB_TEST_ECHO_VECTOR, BINDER_LIB_TEST_GET_NON_BLOCKING_FD, BINDER_LIB_TEST_REJECT_OBJECTS, @@ -247,6 +253,43 @@ class BinderLibTestEnv : public ::testing::Environment { sp<IBinder> m_server; }; +class TestFrozenStateChangeCallback : public IBinder::FrozenStateChangeCallback { +public: + BlockingQueue<std::pair<const wp<IBinder>, State>> events; + + virtual void onStateChanged(const wp<IBinder>& who, State state) { + events.push(std::make_pair(who, state)); + } + + void ensureFrozenEventReceived() { + auto event = events.popWithTimeout(500ms); + ASSERT_TRUE(event.has_value()); + EXPECT_EQ(State::FROZEN, event->second); // isFrozen should be true + EXPECT_EQ(0u, events.size()); + } + + void ensureUnfrozenEventReceived() { + auto event = events.popWithTimeout(500ms); + ASSERT_TRUE(event.has_value()); + EXPECT_EQ(State::UNFROZEN, event->second); // isFrozen should be false + EXPECT_EQ(0u, events.size()); + } + + std::vector<bool> getAllAndClear() { + std::vector<bool> results; + while (true) { + auto event = events.popWithTimeout(0ms); + if (!event.has_value()) { + break; + } + results.push_back(event->second == State::FROZEN); + } + return results; + } + + sp<IBinder> binder; +}; + class BinderLibTest : public ::testing::Test { public: virtual void SetUp() { @@ -291,6 +334,51 @@ class BinderLibTest : public ::testing::Test { EXPECT_EQ(1, ret); } + bool checkFreezeSupport() { + std::ifstream freezer_file("/sys/fs/cgroup/uid_0/cgroup.freeze"); + // Pass test on devices where the cgroup v2 freezer is not supported + if (freezer_file.fail()) { + return false; + } + return IPCThreadState::self()->freeze(getpid(), false, 0) == NO_ERROR; + } + + bool checkFreezeAndNotificationSupport() { + if (!checkFreezeSupport()) { + return false; + } + return ProcessState::isDriverFeatureEnabled( + ProcessState::DriverFeature::FREEZE_NOTIFICATION); + } + + bool getBinderPid(int32_t* pid, sp<IBinder> server) { + Parcel data, replypid; + if (server->transact(BINDER_LIB_TEST_GETPID, data, &replypid) != NO_ERROR) { + ALOGE("BINDER_LIB_TEST_GETPID failed"); + return false; + } + *pid = replypid.readInt32(); + if (*pid <= 0) { + ALOGE("pid should be greater than zero"); + return false; + } + return true; + } + + void freezeProcess(int32_t pid) { + EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000)); + } + + void unfreezeProcess(int32_t pid) { + EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, false, 0)); + } + + void removeCallbackAndValidateNoEvent(sp<IBinder> binder, + sp<TestFrozenStateChangeCallback> callback) { + EXPECT_THAT(binder->removeFrozenStateChangeCallback(callback), StatusEq(NO_ERROR)); + EXPECT_EQ(0u, callback->events.size()); + } + sp<IBinder> m_server; }; @@ -516,29 +604,18 @@ TEST_F(BinderLibTest, NopTransactionClear) { } TEST_F(BinderLibTest, Freeze) { - Parcel data, reply, replypid; - std::ifstream freezer_file("/sys/fs/cgroup/uid_0/cgroup.freeze"); - - // Pass test on devices where the cgroup v2 freezer is not supported - if (freezer_file.fail()) { - GTEST_SKIP(); + if (!checkFreezeSupport()) { + GTEST_SKIP() << "Skipping test for kernels that do not support proceess freezing"; return; } - + Parcel data, reply, replypid; EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid), StatusEq(NO_ERROR)); int32_t pid = replypid.readInt32(); for (int i = 0; i < 10; i++) { EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, data, &reply, TF_ONE_WAY)); } - // Pass test on devices where BINDER_FREEZE ioctl is not supported - int ret = IPCThreadState::self()->freeze(pid, false, 0); - if (ret == -EINVAL) { - GTEST_SKIP(); - return; - } - EXPECT_EQ(NO_ERROR, ret); - + EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, false, 0)); EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0)); // b/268232063 - succeeds ~0.08% of the time @@ -835,6 +912,199 @@ TEST_F(BinderLibTest, DeathNotificationThread) EXPECT_THAT(callback->getResult(), StatusEq(NO_ERROR)); } +TEST_F(BinderLibTest, ReturnErrorIfKernelDoesNotSupportFreezeNotification) { + if (ProcessState::isDriverFeatureEnabled(ProcessState::DriverFeature::FREEZE_NOTIFICATION)) { + GTEST_SKIP() << "Skipping test for kernels that support FREEZE_NOTIFICATION"; + return; + } + sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make(); + sp<IBinder> binder = addServer(); + ASSERT_NE(nullptr, binder); + ASSERT_EQ(nullptr, binder->localBinder()); + EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(INVALID_OPERATION)); +} + +TEST_F(BinderLibTest, FrozenStateChangeNotificatiion) { + if (!checkFreezeAndNotificationSupport()) { + GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION"; + return; + } + sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make(); + sp<IBinder> binder = addServer(); + ASSERT_NE(nullptr, binder); + int32_t pid; + ASSERT_TRUE(getBinderPid(&pid, binder)); + + EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR)); + // Expect current state (unfrozen) to be delivered immediately. + callback->ensureUnfrozenEventReceived(); + // Check that the process hasn't died otherwise there's a risk of freezing + // the wrong process. + EXPECT_EQ(OK, binder->pingBinder()); + freezeProcess(pid); + callback->ensureFrozenEventReceived(); + unfreezeProcess(pid); + callback->ensureUnfrozenEventReceived(); + removeCallbackAndValidateNoEvent(binder, callback); +} + +TEST_F(BinderLibTest, AddFrozenCallbackWhenFrozen) { + if (!checkFreezeAndNotificationSupport()) { + GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION"; + return; + } + sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make(); + sp<IBinder> binder = addServer(); + ASSERT_NE(nullptr, binder); + int32_t pid; + ASSERT_TRUE(getBinderPid(&pid, binder)); + + // Check that the process hasn't died otherwise there's a risk of freezing + // the wrong process. + EXPECT_EQ(OK, binder->pingBinder()); + freezeProcess(pid); + // Add the callback while the target process is frozen. + EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR)); + callback->ensureFrozenEventReceived(); + unfreezeProcess(pid); + callback->ensureUnfrozenEventReceived(); + removeCallbackAndValidateNoEvent(binder, callback); + + // Check that the process hasn't died otherwise there's a risk of freezing + // the wrong process. + EXPECT_EQ(OK, binder->pingBinder()); + freezeProcess(pid); + unfreezeProcess(pid); + // Make sure no callback happens since the listener has been removed. + EXPECT_EQ(0u, callback->events.size()); +} + +TEST_F(BinderLibTest, NoFrozenNotificationAfterCallbackRemoval) { + if (!checkFreezeAndNotificationSupport()) { + GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION"; + return; + } + sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make(); + sp<IBinder> binder = addServer(); + ASSERT_NE(nullptr, binder); + int32_t pid; + ASSERT_TRUE(getBinderPid(&pid, binder)); + + EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR)); + callback->ensureUnfrozenEventReceived(); + removeCallbackAndValidateNoEvent(binder, callback); + + // Make sure no callback happens after the listener is removed. + freezeProcess(pid); + unfreezeProcess(pid); + EXPECT_EQ(0u, callback->events.size()); +} + +TEST_F(BinderLibTest, MultipleFrozenStateChangeCallbacks) { + if (!checkFreezeAndNotificationSupport()) { + GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION"; + return; + } + sp<TestFrozenStateChangeCallback> callback1 = sp<TestFrozenStateChangeCallback>::make(); + sp<TestFrozenStateChangeCallback> callback2 = sp<TestFrozenStateChangeCallback>::make(); + sp<IBinder> binder = addServer(); + ASSERT_NE(nullptr, binder); + int32_t pid; + ASSERT_TRUE(getBinderPid(&pid, binder)); + + EXPECT_THAT(binder->addFrozenStateChangeCallback(callback1), StatusEq(NO_ERROR)); + // Expect current state (unfrozen) to be delivered immediately. + callback1->ensureUnfrozenEventReceived(); + + EXPECT_THAT(binder->addFrozenStateChangeCallback(callback2), StatusEq(NO_ERROR)); + // Expect current state (unfrozen) to be delivered immediately. + callback2->ensureUnfrozenEventReceived(); + + freezeProcess(pid); + callback1->ensureFrozenEventReceived(); + callback2->ensureFrozenEventReceived(); + + removeCallbackAndValidateNoEvent(binder, callback1); + unfreezeProcess(pid); + EXPECT_EQ(0u, callback1->events.size()); + callback2->ensureUnfrozenEventReceived(); + removeCallbackAndValidateNoEvent(binder, callback2); + + freezeProcess(pid); + EXPECT_EQ(0u, callback2->events.size()); +} + +TEST_F(BinderLibTest, RemoveThenAddFrozenStateChangeCallbacks) { + if (!checkFreezeAndNotificationSupport()) { + GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION"; + return; + } + sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make(); + sp<IBinder> binder = addServer(); + ASSERT_NE(nullptr, binder); + int32_t pid; + ASSERT_TRUE(getBinderPid(&pid, binder)); + + EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR)); + // Expect current state (unfrozen) to be delivered immediately. + callback->ensureUnfrozenEventReceived(); + removeCallbackAndValidateNoEvent(binder, callback); + + EXPECT_THAT(binder->addFrozenStateChangeCallback(callback), StatusEq(NO_ERROR)); + callback->ensureUnfrozenEventReceived(); +} + +TEST_F(BinderLibTest, CoalesceFreezeCallbacksWhenListenerIsFrozen) { + if (!checkFreezeAndNotificationSupport()) { + GTEST_SKIP() << "Skipping test for kernels that do not support FREEZE_NOTIFICATION"; + return; + } + sp<IBinder> binder = addServer(); + sp<IBinder> listener = addServer(); + ASSERT_NE(nullptr, binder); + ASSERT_NE(nullptr, listener); + int32_t pid, listenerPid; + ASSERT_TRUE(getBinderPid(&pid, binder)); + ASSERT_TRUE(getBinderPid(&listenerPid, listener)); + + // Ask the listener process to register for state change callbacks. + { + Parcel data, reply; + data.writeStrongBinder(binder); + ASSERT_THAT(listener->transact(BINDER_LIB_TEST_LISTEN_FOR_FROZEN_STATE_CHANGE, data, + &reply), + StatusEq(NO_ERROR)); + } + // Freeze the listener process. + freezeProcess(listenerPid); + createProcessGroup(getuid(), listenerPid); + ASSERT_TRUE(SetProcessProfiles(getuid(), listenerPid, {"Frozen"})); + // Repeatedly flip the target process between frozen and unfrozen states. + for (int i = 0; i < 1000; i++) { + usleep(50); + unfreezeProcess(pid); + usleep(50); + freezeProcess(pid); + } + // Unfreeze the listener process. Now it should receive the frozen state + // change notifications. + ASSERT_TRUE(SetProcessProfiles(getuid(), listenerPid, {"Unfrozen"})); + unfreezeProcess(listenerPid); + // Wait for 500ms to give the process enough time to wake up and handle + // notifications. + usleep(500 * 1000); + { + std::vector<bool> events; + Parcel data, reply; + ASSERT_THAT(listener->transact(BINDER_LIB_TEST_CONSUME_STATE_CHANGE_EVENTS, data, &reply), + StatusEq(NO_ERROR)); + reply.readBoolVector(&events); + // There should only be one single state change notifications delievered. + ASSERT_EQ(1u, events.size()); + EXPECT_TRUE(events[0]); + } +} + TEST_F(BinderLibTest, PassFile) { int ret; int pipefd[2]; @@ -1981,6 +2251,26 @@ public: reply->writeInt32(param.sched_priority); return NO_ERROR; } + case BINDER_LIB_TEST_LISTEN_FOR_FROZEN_STATE_CHANGE: { + sp<IBinder> binder = data.readStrongBinder(); + frozenStateChangeCallback = sp<TestFrozenStateChangeCallback>::make(); + // Hold an strong pointer to the binder object so it doesn't go + // away. + frozenStateChangeCallback->binder = binder; + int ret = binder->addFrozenStateChangeCallback(frozenStateChangeCallback); + if (ret != NO_ERROR) { + return ret; + } + auto event = frozenStateChangeCallback->events.popWithTimeout(10ms); + if (!event.has_value()) { + return NOT_ENOUGH_DATA; + } + return NO_ERROR; + } + case BINDER_LIB_TEST_CONSUME_STATE_CHANGE_EVENTS: { + reply->writeBoolVector(frozenStateChangeCallback->getAllAndClear()); + return NO_ERROR; + } case BINDER_LIB_TEST_ECHO_VECTOR: { std::vector<uint64_t> vector; auto err = data.readUint64Vector(&vector); @@ -2067,6 +2357,7 @@ private: sp<IBinder> m_callback; bool m_exitOnDestroy; std::mutex m_blockMutex; + sp<TestFrozenStateChangeCallback> frozenStateChangeCallback; }; int run_server(int index, int readypipefd, bool usePoll) diff --git a/libs/binder/tests/binderRecordReplayTest.cpp b/libs/binder/tests/binderRecordReplayTest.cpp index b975fad2c9..f867b42b1c 100644 --- a/libs/binder/tests/binderRecordReplayTest.cpp +++ b/libs/binder/tests/binderRecordReplayTest.cpp @@ -99,12 +99,12 @@ public: GENERATE_GETTER_SETTER(SingleDataParcelableArray, std::vector<SingleDataParcelable>); Status setFileDescriptor(unique_fd input) { - mFd = std::move(unique_fd(dup(input))); + mFd = unique_fd(dup(input)); return Status::ok(); } Status getFileDescriptor(unique_fd* output) { - *output = std::move(unique_fd(dup(mFd))); + *output = unique_fd(dup(mFd)); return Status::ok(); } unique_fd mFd; @@ -117,7 +117,7 @@ std::vector<uint8_t> retrieveData(borrowed_fd fd) { std::vector<uint8_t> buffer(fdStat.st_size); auto readResult = android::base::ReadFully(fd, buffer.data(), fdStat.st_size); EXPECT_TRUE(readResult != 0); - return std::move(buffer); + return buffer; } void replayFuzzService(const sp<BpBinder>& binder, const RecordedTransaction& transaction) { @@ -387,8 +387,8 @@ TEST_F(BinderRecordReplayTest, ReplayFd) { // When fds are replayed, it will be replaced by /dev/null..reading from it should yield // null data - recordReplay(&IBinderRecordReplayTest::setFileDescriptor, std::move(unique_fd(dup(saved))), - &IBinderRecordReplayTest::getFileDescriptor, std::move(unique_fd(dup(changed)))); + recordReplay(&IBinderRecordReplayTest::setFileDescriptor, unique_fd(dup(saved)), + &IBinderRecordReplayTest::getFileDescriptor, unique_fd(dup(changed))); } int main(int argc, char** argv) { diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp index f4807806ad..2cec243c38 100644 --- a/libs/binder/tests/binderRpcUniversalTests.cpp +++ b/libs/binder/tests/binderRpcUniversalTests.cpp @@ -323,6 +323,22 @@ TEST_P(BinderRpc, CannotSendSocketBinderOverRegularBinder) { // END TESTS FOR LIMITATIONS OF SOCKET BINDER +class TestFrozenStateChangeCallback : public IBinder::FrozenStateChangeCallback { +public: + virtual void onStateChanged(const wp<IBinder>&, State) {} +}; + +TEST_P(BinderRpc, RpcBinderShouldFailOnFrozenStateCallbacks) { + auto proc = createRpcTestSocketServerProcess({}); + + sp<IBinder> a; + sp<TestFrozenStateChangeCallback> callback = sp<TestFrozenStateChangeCallback>::make(); + EXPECT_OK(proc.rootIface->alwaysGiveMeTheSameBinder(&a)); + EXPECT_DEATH_IF_SUPPORTED( + { std::ignore = a->addFrozenStateChangeCallback(callback); }, + "addFrozenStateChangeCallback\\(\\) is not supported for RPC Binder."); +} + TEST_P(BinderRpc, RepeatRootObject) { auto proc = createRpcTestSocketServerProcess({}); diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp index fd9777a916..0ed8a554ac 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp @@ -55,7 +55,7 @@ std::vector<uint8_t> reverseBytes(T min, T max, T val) { offset += CHAR_BIT; } - return std::move(reverseData); + return reverseData; } template <typename T> diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h index 23f583bda0..7f45581da9 100644 --- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h +++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h @@ -125,6 +125,11 @@ public: VULKAN_DEVICE_EXTENSION = 9, }; + enum GLTelemetryHints { + NO_HINT = 0, + SKIP_TELEMETRY = 1, + }; + GpuStatsInfo() = default; GpuStatsInfo(const GpuStatsInfo&) = default; virtual ~GpuStatsInfo() = default; diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 51d2e5305a..1243b214d3 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -255,6 +255,7 @@ filegroup { "BitTube.cpp", "BLASTBufferQueue.cpp", "BufferItemConsumer.cpp", + "BufferReleaseChannel.cpp", "Choreographer.cpp", "CompositorTiming.cpp", "ConsumerBase.cpp", diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 739c3c2a41..7f0e80e0f0 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -39,7 +39,6 @@ #include <private/gui/ComposerServiceAIDL.h> #include <android-base/thread_annotations.h> -#include <chrono> #include <com_android_graphics_libgui_flags.h> @@ -179,8 +178,6 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinati // explicitly so that dequeueBuffer will block mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max()); - // safe default, most producers are expected to override this - mProducer->setMaxDequeuedBufferCount(2); mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer, GraphicBuffer::USAGE_HW_COMPOSER | GraphicBuffer::USAGE_HW_TEXTURE, @@ -236,6 +233,11 @@ BLASTBufferQueue::~BLASTBufferQueue() { } } +void BLASTBufferQueue::onFirstRef() { + // safe default, most producers are expected to override this + mProducer->setMaxDequeuedBufferCount(2); +} + void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format) { LOG_ALWAYS_FATAL_IF(surface == nullptr, "BLASTBufferQueue: mSurfaceControl must not be NULL"); @@ -499,7 +501,13 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, callbackId.to_string().c_str()); return; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + if (!it->second.mIsStale) { + mNumAcquired--; + } +#else mNumAcquired--; +#endif BBQ_TRACE("frame=%" PRIu64, callbackId.framenumber); BQA_LOGV("released %s", callbackId.to_string().c_str()); mBufferItemConsumer->releaseBuffer(it->second, releaseFence); @@ -761,6 +769,9 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { } // add to shadow queue +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + mNumDequeued--; +#endif mNumFrameAvailable++; if (waitForTransactionCallback && mNumFrameAvailable >= 2) { acquireAndReleaseBuffer(); @@ -815,9 +826,18 @@ void BLASTBufferQueue::onFrameDequeued(const uint64_t bufferId) { }; void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) { - std::lock_guard _lock{mTimestampMutex}; - mDequeueTimestamps.erase(bufferId); -}; + { + std::lock_guard _lock{mTimestampMutex}; + mDequeueTimestamps.erase(bufferId); + } + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + { + std::lock_guard lock{mMutex}; + mNumDequeued--; + } +#endif +} bool BLASTBufferQueue::syncNextTransaction( std::function<void(SurfaceComposerClient::Transaction*)> callback, @@ -1116,30 +1136,143 @@ public: producerControlledByApp, output); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + status_t disconnect(int api, DisconnectMode mode) override { + sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); + if (!bbq) { + return BufferQueueProducer::disconnect(api, mode); + } + + std::lock_guard lock{bbq->mMutex}; + if (status_t status = BufferQueueProducer::disconnect(api, mode); status != OK) { + return status; + } + + bbq->mNumDequeued = 0; + bbq->mNumAcquired = 0; + for (auto& [releaseId, bufferItem] : bbq->mSubmitted) { + bufferItem.mIsStale = true; + } + + return OK; + } + + status_t setAsyncMode(bool asyncMode) override { + if (status_t status = BufferQueueProducer::setAsyncMode(asyncMode); status != OK) { + return status; + } + + sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); + if (!bbq) { + return OK; + } + + { + std::lock_guard lock{bbq->mMutex}; + bbq->mAsyncMode = asyncMode; + } + + return OK; + } + + status_t setSharedBufferMode(bool sharedBufferMode) override { + if (status_t status = BufferQueueProducer::setSharedBufferMode(sharedBufferMode); + status != OK) { + return status; + } + + sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); + if (!bbq) { + return OK; + } + + { + std::lock_guard lock{bbq->mMutex}; + bbq->mSharedBufferMode = sharedBufferMode; + } + + return OK; + } + + status_t detachBuffer(int slot) override { + if (status_t status = BufferQueueProducer::detachBuffer(slot); status != OK) { + return status; + } + + sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); + if (!bbq) { + return OK; + } + + { + std::lock_guard lock{bbq->mMutex}; + bbq->mNumDequeued--; + } + + return OK; + } + + status_t dequeueBuffer(int* outSlot, sp<Fence>* outFence, uint32_t width, uint32_t height, + PixelFormat format, uint64_t usage, uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps) override { + sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); + if (!bbq) { + return BufferQueueProducer::dequeueBuffer(outSlot, outFence, width, height, format, + usage, outBufferAge, outTimestamps); + } + + { + std::lock_guard lock{bbq->mMutex}; + bbq->mNumDequeued++; + } + + status_t status = + BufferQueueProducer::dequeueBuffer(outSlot, outFence, width, height, format, usage, + outBufferAge, outTimestamps); + if (status < 0) { + std::lock_guard lock{bbq->mMutex}; + bbq->mNumDequeued--; + } + return status; + } +#endif + // We want to resize the frame history when changing the size of the buffer queue status_t setMaxDequeuedBufferCount(int maxDequeuedBufferCount) override { int maxBufferCount; - status_t status = BufferQueueProducer::setMaxDequeuedBufferCount(maxDequeuedBufferCount, - &maxBufferCount); + if (status_t status = BufferQueueProducer::setMaxDequeuedBufferCount(maxDequeuedBufferCount, + &maxBufferCount); + status != OK) { + return status; + } + + sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); + if (!bbq) { + return OK; + } + // if we can't determine the max buffer count, then just skip growing the history size - if (status == OK) { - size_t newFrameHistorySize = maxBufferCount + 2; // +2 because triple buffer rendering - // optimize away resizing the frame history unless it will grow - if (newFrameHistorySize > FrameEventHistory::INITIAL_MAX_FRAME_HISTORY) { - sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); - if (bbq != nullptr) { - ALOGV("increasing frame history size to %zu", newFrameHistorySize); - bbq->resizeFrameEventHistory(newFrameHistorySize); - } - } + size_t newFrameHistorySize = maxBufferCount + 2; // +2 because triple buffer rendering + // optimize away resizing the frame history unless it will grow + if (newFrameHistorySize > FrameEventHistory::INITIAL_MAX_FRAME_HISTORY) { + ALOGV("increasing frame history size to %zu", newFrameHistorySize); + bbq->resizeFrameEventHistory(newFrameHistorySize); } - return status; + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + { + std::lock_guard lock{bbq->mMutex}; + bbq->mMaxDequeuedBuffers = maxDequeuedBufferCount; + } +#endif + + return OK; } int query(int what, int* value) override { if (what == NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER) { *value = 1; - return NO_ERROR; + return OK; } return BufferQueueProducer::query(what, value); } diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp new file mode 100644 index 0000000000..27367aa83f --- /dev/null +++ b/libs/gui/BufferReleaseChannel.cpp @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2024 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. + */ + +#define LOG_TAG "BufferReleaseChannel" + +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <android-base/result.h> +#include <android/binder_status.h> +#include <binder/Parcel.h> +#include <utils/Flattenable.h> + +#include <gui/BufferReleaseChannel.h> +#include <private/gui/ParcelUtils.h> + +using android::base::Result; + +namespace android::gui { + +namespace { + +template <typename T> +static void readAligned(const void*& buffer, size_t& size, T& value) { + size -= FlattenableUtils::align<alignof(T)>(buffer); + FlattenableUtils::read(buffer, size, value); +} + +template <typename T> +static void writeAligned(void*& buffer, size_t& size, T value) { + size -= FlattenableUtils::align<alignof(T)>(buffer); + FlattenableUtils::write(buffer, size, value); +} + +template <typename T> +static void addAligned(size_t& size, T /* value */) { + size = FlattenableUtils::align<sizeof(T)>(size); + size += sizeof(T); +} + +template <typename T> +static inline constexpr uint32_t low32(const T n) { + return static_cast<uint32_t>(static_cast<uint64_t>(n)); +} + +template <typename T> +static inline constexpr uint32_t high32(const T n) { + return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32); +} + +template <typename T> +static inline constexpr T to64(const uint32_t lo, const uint32_t hi) { + return static_cast<T>(static_cast<uint64_t>(hi) << 32 | lo); +} + +} // namespace + +size_t BufferReleaseChannel::Message::getPodSize() const { + size_t size = 0; + addAligned(size, low32(releaseCallbackId.bufferId)); + addAligned(size, high32(releaseCallbackId.bufferId)); + addAligned(size, low32(releaseCallbackId.framenumber)); + addAligned(size, high32(releaseCallbackId.framenumber)); + addAligned(size, maxAcquiredBufferCount); + return size; +} + +size_t BufferReleaseChannel::Message::getFlattenedSize() const { + size_t size = releaseFence->getFlattenedSize(); + size = FlattenableUtils::align<4>(size); + size += getPodSize(); + return size; +} + +status_t BufferReleaseChannel::Message::flatten(void*& buffer, size_t& size, int*& fds, + size_t& count) const { + if (status_t err = releaseFence->flatten(buffer, size, fds, count); err != OK) { + return err; + } + size -= FlattenableUtils::align<4>(buffer); + + // Check we still have enough space + if (size < getPodSize()) { + return NO_MEMORY; + } + + writeAligned(buffer, size, low32(releaseCallbackId.bufferId)); + writeAligned(buffer, size, high32(releaseCallbackId.bufferId)); + writeAligned(buffer, size, low32(releaseCallbackId.framenumber)); + writeAligned(buffer, size, high32(releaseCallbackId.framenumber)); + writeAligned(buffer, size, maxAcquiredBufferCount); + return OK; +} + +status_t BufferReleaseChannel::Message::unflatten(void const*& buffer, size_t& size, + int const*& fds, size_t& count) { + releaseFence = new Fence(); + if (status_t err = releaseFence->unflatten(buffer, size, fds, count); err != OK) { + return err; + } + size -= FlattenableUtils::align<4>(buffer); + + // Check we still have enough space + if (size < getPodSize()) { + return OK; + } + + uint32_t bufferIdLo = 0, bufferIdHi = 0; + uint32_t frameNumberLo = 0, frameNumberHi = 0; + + readAligned(buffer, size, bufferIdLo); + readAligned(buffer, size, bufferIdHi); + releaseCallbackId.bufferId = to64<int64_t>(bufferIdLo, bufferIdHi); + readAligned(buffer, size, frameNumberLo); + readAligned(buffer, size, frameNumberHi); + releaseCallbackId.framenumber = to64<uint64_t>(frameNumberLo, frameNumberHi); + readAligned(buffer, size, maxAcquiredBufferCount); + + return OK; +} + +status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( + ReleaseCallbackId& outReleaseCallbackId, sp<Fence>& outReleaseFence, + uint32_t& outMaxAcquiredBufferCount) { + Message message; + mFlattenedBuffer.resize(message.getFlattenedSize()); + std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer; + + iovec iov{ + .iov_base = mFlattenedBuffer.data(), + .iov_len = mFlattenedBuffer.size(), + }; + + msghdr msg{ + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = controlMessageBuffer.data(), + .msg_controllen = controlMessageBuffer.size(), + }; + + int result; + do { + result = recvmsg(mFd, &msg, 0); + } while (result == -1 && errno == EINTR); + if (result == -1) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + return WOULD_BLOCK; + } + ALOGE("Error reading release fence from socket: error %#x (%s)", errno, strerror(errno)); + return UNKNOWN_ERROR; + } + + if (msg.msg_iovlen != 1) { + ALOGE("Error reading release fence from socket: bad data length"); + return UNKNOWN_ERROR; + } + + if (msg.msg_controllen % sizeof(int) != 0) { + ALOGE("Error reading release fence from socket: bad fd length"); + return UNKNOWN_ERROR; + } + + size_t dataLen = msg.msg_iov->iov_len; + const void* data = static_cast<const void*>(msg.msg_iov->iov_base); + if (!data) { + ALOGE("Error reading release fence from socket: no buffer data"); + return UNKNOWN_ERROR; + } + + size_t fdCount = 0; + const int* fdData = nullptr; + if (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg)) { + fdData = reinterpret_cast<const int*>(CMSG_DATA(cmsg)); + fdCount = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + } + + if (status_t err = message.unflatten(data, dataLen, fdData, fdCount); err != OK) { + return err; + } + + outReleaseCallbackId = message.releaseCallbackId; + outReleaseFence = std::move(message.releaseFence); + outMaxAcquiredBufferCount = message.maxAcquiredBufferCount; + + return OK; +} + +int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId, + const sp<Fence>& fence, + uint32_t maxAcquiredBufferCount) { + Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount}; + mFlattenedBuffer.resize(message.getFlattenedSize()); + int flattenedFd; + { + // Make copies of needed items since flatten modifies them, and we don't + // want to send anything if there's an error during flatten. + void* flattenedBufferPtr = mFlattenedBuffer.data(); + size_t flattenedBufferSize = mFlattenedBuffer.size(); + int* flattenedFdPtr = &flattenedFd; + size_t flattenedFdCount = 1; + if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr, + flattenedFdCount); + err != OK) { + ALOGE("Failed to flatten BufferReleaseChannel message."); + return err; + } + } + + iovec iov{ + .iov_base = mFlattenedBuffer.data(), + .iov_len = mFlattenedBuffer.size(), + }; + + msghdr msg{ + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer; + if (fence && fence->isValid()) { + msg.msg_control = controlMessageBuffer.data(); + msg.msg_controllen = controlMessageBuffer.size(); + + cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &flattenedFd, sizeof(int)); + } + + int result; + do { + result = sendmsg(mFd, &msg, 0); + } while (result == -1 && errno == EINTR); + if (result == -1) { + ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno)); + return -errno; + } + + return OK; +} + +status_t BufferReleaseChannel::ProducerEndpoint::readFromParcel(const android::Parcel* parcel) { + if (!parcel) return STATUS_BAD_VALUE; + SAFE_PARCEL(parcel->readUtf8FromUtf16, &mName); + SAFE_PARCEL(parcel->readUniqueFileDescriptor, &mFd); + return STATUS_OK; +} + +status_t BufferReleaseChannel::ProducerEndpoint::writeToParcel(android::Parcel* parcel) const { + if (!parcel) return STATUS_BAD_VALUE; + SAFE_PARCEL(parcel->writeUtf8AsUtf16, mName); + SAFE_PARCEL(parcel->writeUniqueFileDescriptor, mFd); + return STATUS_OK; +} + +status_t BufferReleaseChannel::open(std::string name, + std::unique_ptr<ConsumerEndpoint>& outConsumer, + std::shared_ptr<ProducerEndpoint>& outProducer) { + outConsumer.reset(); + outProducer.reset(); + + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { + ALOGE("[%s] Failed to create socket pair. errorno=%d message='%s'", name.c_str(), errno, + strerror(errno)); + return -errno; + } + + android::base::unique_fd consumerFd(sockets[0]); + android::base::unique_fd producerFd(sockets[1]); + + // Socket buffer size. The default is typically about 128KB, which is much larger than + // we really need. + size_t bufferSize = 32 * 1024; + if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) == + -1) { + ALOGE("[%s] Failed to set consumer socket send buffer size. errno=%d message='%s'", + name.c_str(), errno, strerror(errno)); + return -errno; + } + if (setsockopt(consumerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) == + -1) { + ALOGE("[%s] Failed to set consumer socket receive buffer size. errno=%d " + "message='%s'", + name.c_str(), errno, strerror(errno)); + return -errno; + } + if (setsockopt(producerFd.get(), SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)) == + -1) { + ALOGE("[%s] Failed to set producer socket send buffer size. errno=%d message='%s'", + name.c_str(), errno, strerror(errno)); + return -errno; + } + if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)) == + -1) { + ALOGE("[%s] Failed to set producer socket receive buffer size. errno=%d " + "message='%s'", + name.c_str(), errno, strerror(errno)); + return -errno; + } + + // Configure the consumer socket to be non-blocking. + int flags = fcntl(consumerFd.get(), F_GETFL, 0); + if (flags == -1) { + ALOGE("[%s] Failed to get consumer socket flags. errno=%d message='%s'", name.c_str(), + errno, strerror(errno)); + return -errno; + } + if (fcntl(consumerFd.get(), F_SETFL, flags | O_NONBLOCK) == -1) { + ALOGE("[%s] Failed to set consumer socket to non-blocking mode. errno=%d " + "message='%s'", + name.c_str(), errno, strerror(errno)); + return -errno; + } + + // Configure a timeout for the producer socket. + const timeval timeout{.tv_sec = 1, .tv_usec = 0}; + if (setsockopt(producerFd.get(), SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeval)) == -1) { + ALOGE("[%s] Failed to set producer socket timeout. errno=%d message='%s'", name.c_str(), + errno, strerror(errno)); + return -errno; + } + + // Make the consumer read-only + if (shutdown(consumerFd.get(), SHUT_WR) == -1) { + ALOGE("[%s] Failed to shutdown writing on consumer socket. errno=%d message='%s'", + name.c_str(), errno, strerror(errno)); + return -errno; + } + + // Make the producer write-only + if (shutdown(producerFd.get(), SHUT_RD) == -1) { + ALOGE("[%s] Failed to shutdown reading on producer socket. errno=%d message='%s'", + name.c_str(), errno, strerror(errno)); + return -errno; + } + + outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd)); + outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd)); + return STATUS_OK; +} + +} // namespace android::gui
\ No newline at end of file diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 307ae3990e..0714fd9501 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -17,7 +17,6 @@ #define LOG_TAG "LayerState" #include <cinttypes> -#include <cmath> #include <android/gui/ISurfaceComposerClient.h> #include <android/native_window.h> @@ -194,6 +193,13 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeFloat, currentHdrSdrRatio); SAFE_PARCEL(output.writeFloat, desiredHdrSdrRatio); SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint)); + + const bool hasBufferReleaseChannel = (bufferReleaseChannel != nullptr); + SAFE_PARCEL(output.writeBool, hasBufferReleaseChannel); + if (hasBufferReleaseChannel) { + SAFE_PARCEL(output.writeParcelable, *bufferReleaseChannel); + } + return NO_ERROR; } @@ -339,6 +345,13 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readInt32, &tmpInt32); cachingHint = static_cast<gui::CachingHint>(tmpInt32); + bool hasBufferReleaseChannel; + SAFE_PARCEL(input.readBool, &hasBufferReleaseChannel); + if (hasBufferReleaseChannel) { + bufferReleaseChannel = std::make_shared<gui::BufferReleaseChannel::ProducerEndpoint>(); + SAFE_PARCEL(input.readParcelable, bufferReleaseChannel.get()); + } + return NO_ERROR; } @@ -718,6 +731,10 @@ void layer_state_t::merge(const layer_state_t& other) { if (other.what & eFlushJankData) { what |= eFlushJankData; } + if (other.what & eBufferReleaseChannelChanged) { + what |= eBufferReleaseChannelChanged; + bufferReleaseChannel = other.bufferReleaseChannel; + } if ((other.what & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " "other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64, @@ -797,6 +814,7 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { CHECK_DIFF(diff, eColorChanged, other, color.rgb); CHECK_DIFF(diff, eColorSpaceAgnosticChanged, other, colorSpaceAgnostic); CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled); + if (other.what & eBufferReleaseChannelChanged) diff |= eBufferReleaseChannelChanged; return diff; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index f663600f9e..b5d9366185 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -2390,6 +2390,22 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDropI return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferReleaseChannel( + const sp<SurfaceControl>& sc, + const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eBufferReleaseChannelChanged; + s->bufferReleaseChannel = channel; + + registerSurfaceControlForCallback(sc); + return *this; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 0e1a505c69..266729abe5 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -28,7 +28,6 @@ #include <utils/RefBase.h> #include <system/window.h> -#include <thread> #include <queue> #include <com_android_graphics_libgui_flags.h> @@ -131,6 +130,8 @@ public: virtual ~BLASTBufferQueue(); + void onFirstRef() override; + private: friend class BLASTBufferQueueHelper; friend class BBQBufferQueueProducer; @@ -170,8 +171,16 @@ private: // BufferQueue internally allows 1 more than // the max to be acquired - int32_t mMaxAcquiredBuffers = 1; + int32_t mMaxAcquiredBuffers GUARDED_BY(mMutex) = 1; +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + int32_t mMaxDequeuedBuffers GUARDED_BY(mMutex) = 1; + static constexpr int32_t kMaxBufferCount = BufferQueueDefs::NUM_BUFFER_SLOTS; + + bool mAsyncMode GUARDED_BY(mMutex) = false; + bool mSharedBufferMode GUARDED_BY(mMutex) = false; + int32_t mNumDequeued GUARDED_BY(mMutex) = 0; +#endif int32_t mNumFrameAvailable GUARDED_BY(mMutex) = 0; int32_t mNumAcquired GUARDED_BY(mMutex) = 0; diff --git a/libs/gui/include/gui/BufferReleaseChannel.h b/libs/gui/include/gui/BufferReleaseChannel.h new file mode 100644 index 0000000000..51fe0b6fab --- /dev/null +++ b/libs/gui/include/gui/BufferReleaseChannel.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2024 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 <string> +#include <vector> + +#include <android-base/unique_fd.h> + +#include <binder/Parcelable.h> +#include <gui/ITransactionCompletedListener.h> +#include <ui/Fence.h> +#include <utils/Errors.h> + +namespace android::gui { + +/** + * IPC wrapper to pass release fences from SurfaceFlinger to apps via a local unix domain socket. + */ +class BufferReleaseChannel { +private: + class Endpoint { + public: + Endpoint(std::string name, android::base::unique_fd fd) + : mName(std::move(name)), mFd(std::move(fd)) {} + Endpoint() {} + + Endpoint(Endpoint&&) noexcept = default; + Endpoint& operator=(Endpoint&&) noexcept = default; + + Endpoint(const Endpoint&) = delete; + void operator=(const Endpoint&) = delete; + + const android::base::unique_fd& getFd() const { return mFd; } + + protected: + std::string mName; + android::base::unique_fd mFd; + }; + +public: + class ConsumerEndpoint : public Endpoint { + public: + ConsumerEndpoint(std::string name, android::base::unique_fd fd) + : Endpoint(std::move(name), std::move(fd)) {} + + /** + * Reads a release fence from the BufferReleaseChannel. + * + * Returns OK on success. + * Returns WOULD_BLOCK if there is no fence present. + * Other errors probably indicate that the channel is broken. + */ + status_t readReleaseFence(ReleaseCallbackId& outReleaseCallbackId, + sp<Fence>& outReleaseFence, uint32_t& maxAcquiredBufferCount); + + private: + std::vector<uint8_t> mFlattenedBuffer; + }; + + class ProducerEndpoint : public Endpoint, public Parcelable { + public: + ProducerEndpoint(std::string name, android::base::unique_fd fd) + : Endpoint(std::move(name), std::move(fd)) {} + ProducerEndpoint() {} + + status_t readFromParcel(const android::Parcel* parcel) override; + status_t writeToParcel(android::Parcel* parcel) const override; + + status_t writeReleaseFence(const ReleaseCallbackId&, const sp<Fence>& releaseFence, + uint32_t maxAcquiredBufferCount); + + private: + std::vector<uint8_t> mFlattenedBuffer; + }; + + /** + * Create two endpoints that make up the BufferReleaseChannel. + * + * Return OK on success. + */ + static status_t open(const std::string name, std::unique_ptr<ConsumerEndpoint>& outConsumer, + std::shared_ptr<ProducerEndpoint>& outProducer); + + struct Message : public Flattenable<Message> { + ReleaseCallbackId releaseCallbackId; + sp<Fence> releaseFence = Fence::NO_FENCE; + uint32_t maxAcquiredBufferCount; + + Message() = default; + Message(ReleaseCallbackId releaseCallbackId, sp<Fence> releaseFence, + uint32_t maxAcquiredBufferCount) + : releaseCallbackId{releaseCallbackId}, + releaseFence{std::move(releaseFence)}, + maxAcquiredBufferCount{maxAcquiredBufferCount} {} + + // Flattenable protocol + size_t getFlattenedSize() const; + + size_t getFdCount() const { return releaseFence->getFdCount(); } + + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + + private: + size_t getPodSize() const; + }; +}; + +} // namespace android::gui diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 3fb1894585..d41994589f 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -34,6 +34,7 @@ #include <android/gui/TrustedOverlay.h> #include <ftl/flags.h> +#include <gui/BufferReleaseChannel.h> #include <gui/DisplayCaptureArgs.h> #include <gui/ISurfaceComposer.h> #include <gui/LayerCaptureArgs.h> @@ -220,6 +221,7 @@ struct layer_state_t { eDropInputModeChanged = 0x8000'00000000, eExtendedRangeBrightnessChanged = 0x10000'00000000, eEdgeExtensionChanged = 0x20000'00000000, + eBufferReleaseChannelChanged = 0x40000'00000000, }; layer_state_t(); @@ -412,6 +414,8 @@ struct layer_state_t { TrustedPresentationThresholds trustedPresentationThresholds; TrustedPresentationListener trustedPresentationListener; + + std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel; }; class ComposerState { diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 61a65bb29f..4f9af16826 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -45,6 +45,7 @@ #include <android/gui/BnJankListener.h> #include <android/gui/ISurfaceComposerClient.h> +#include <gui/BufferReleaseChannel.h> #include <gui/CpuConsumer.h> #include <gui/ISurfaceComposer.h> #include <gui/ITransactionCompletedListener.h> @@ -762,6 +763,10 @@ public: const Rect& destinationFrame); Transaction& setDropInputMode(const sp<SurfaceControl>& sc, gui::DropInputMode mode); + Transaction& setBufferReleaseChannel( + const sp<SurfaceControl>& sc, + const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel); + status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index b1b385d1b2..c367e75065 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -85,9 +85,9 @@ flag { } # buffer_release_channel flag { - name: "wb_surface_connect_methods" + name: "wb_ring_buffer" namespace: "core_graphics" - description: "Remove redundant connect methods in Surface." - bug: "354273690" + description: "Remove slot dependency in the Ring Buffer Consumer." + bug: "342197847" is_fixed_read_only: true -} # wb_surface_connect_methods +} # wb_ring_buffer diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index b342a7db0d..9558edab87 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -33,6 +33,7 @@ cc_test { "BLASTBufferQueue_test.cpp", "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", + "BufferReleaseChannel_test.cpp", "Choreographer_test.cpp", "CompositorTiming_test.cpp", "CpuConsumer_test.cpp", diff --git a/libs/gui/tests/BufferReleaseChannel_test.cpp b/libs/gui/tests/BufferReleaseChannel_test.cpp new file mode 100644 index 0000000000..11d122b525 --- /dev/null +++ b/libs/gui/tests/BufferReleaseChannel_test.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2024 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. + */ + +#include <string> +#include <vector> + +#include <gtest/gtest.h> +#include <gui/BufferReleaseChannel.h> + +using namespace std::string_literals; +using android::gui::BufferReleaseChannel; + +namespace android { + +namespace { + +// Helper function to check if two file descriptors point to the same file. +bool is_same_file(int fd1, int fd2) { + struct stat stat1; + if (fstat(fd1, &stat1) != 0) { + return false; + } + struct stat stat2; + if (fstat(fd2, &stat2) != 0) { + return false; + } + return (stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino); +} + +} // namespace + +TEST(BufferReleaseChannelTest, MessageFlattenable) { + ReleaseCallbackId releaseCallbackId{1, 2}; + sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); + uint32_t maxAcquiredBufferCount = 5; + + std::vector<uint8_t> dataBuffer; + std::vector<int> fdBuffer; + + // Verify that we can flatten a message + { + BufferReleaseChannel::Message message{releaseCallbackId, releaseFence, + maxAcquiredBufferCount}; + + dataBuffer.resize(message.getFlattenedSize()); + void* dataPtr = dataBuffer.data(); + size_t dataSize = dataBuffer.size(); + + fdBuffer.resize(message.getFdCount()); + int* fdPtr = fdBuffer.data(); + size_t fdSize = fdBuffer.size(); + + ASSERT_EQ(OK, message.flatten(dataPtr, dataSize, fdPtr, fdSize)); + + // Fence's unique_fd uses fdsan to check ownership of the file descriptor. Normally the file + // descriptor is passed through the Unix socket and duplicated (and sent to another process) + // so there's no problem with duplicate file descriptor ownership. For this unit test, we + // need to set up a duplicate file descriptor to avoid crashing due to duplicate ownership. + ASSERT_EQ(releaseFence->get(), fdBuffer[0]); + fdBuffer[0] = message.releaseFence->dup(); + } + + // Verify that we can unflatten a message + { + BufferReleaseChannel::Message message; + + const void* dataPtr = dataBuffer.data(); + size_t dataSize = dataBuffer.size(); + + const int* fdPtr = fdBuffer.data(); + size_t fdSize = fdBuffer.size(); + + ASSERT_EQ(OK, message.unflatten(dataPtr, dataSize, fdPtr, fdSize)); + ASSERT_EQ(releaseCallbackId, message.releaseCallbackId); + ASSERT_TRUE(is_same_file(releaseFence->get(), message.releaseFence->get())); + ASSERT_EQ(maxAcquiredBufferCount, message.maxAcquiredBufferCount); + } +} + +// Verify that the BufferReleaseChannel consume returns WOULD_BLOCK when there's no message +// available. +TEST(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) { + std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer; + std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer; + ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); + + ReleaseCallbackId releaseCallbackId; + sp<Fence> releaseFence; + uint32_t maxAcquiredBufferCount; + ASSERT_EQ(WOULD_BLOCK, + consumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount)); +} + +// Verify that we can write a message to the BufferReleaseChannel producer and read that message +// using the BufferReleaseChannel consumer. +TEST(BufferReleaseChannelTest, ProduceAndConsume) { + std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer; + std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer; + ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); + + sp<Fence> fence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); + + for (uint64_t i = 0; i < 64; i++) { + ReleaseCallbackId producerId{i, i + 1}; + uint32_t maxAcquiredBufferCount = i + 2; + ASSERT_EQ(OK, producer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount)); + } + + for (uint64_t i = 0; i < 64; i++) { + ReleaseCallbackId expectedId{i, i + 1}; + uint32_t expectedMaxAcquiredBufferCount = i + 2; + + ReleaseCallbackId consumerId; + sp<Fence> consumerFence; + uint32_t maxAcquiredBufferCount; + ASSERT_EQ(OK, + consumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount)); + + ASSERT_EQ(expectedId, consumerId); + ASSERT_TRUE(is_same_file(fence->get(), consumerFence->get())); + ASSERT_EQ(expectedMaxAcquiredBufferCount, maxAcquiredBufferCount); + } +} + +} // namespace android
\ No newline at end of file diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 8fbf5c6a2e..e4e81adf58 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -232,6 +232,7 @@ cc_library { "MotionPredictorMetricsManager.cpp", "PrintTools.cpp", "PropertyMap.cpp", + "Resampler.cpp", "TfLiteMotionPredictor.cpp", "TouchVideoFrame.cpp", "VelocityControl.cpp", diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp index c145d5c286..99ffa683dd 100644 --- a/libs/input/InputConsumerNoResampling.cpp +++ b/libs/input/InputConsumerNoResampling.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "InputTransport" #define ATRACE_TAG ATRACE_TAG_INPUT +#include <chrono> + #include <inttypes.h> #include <android-base/logging.h> @@ -168,6 +170,10 @@ InputMessage createTimelineMessage(int32_t inputEventId, nsecs_t gpuCompletedTim return msg; } +bool isPointerEvent(const MotionEvent& motionEvent) { + return (motionEvent.getSource() & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER; +} + } // namespace using android::base::Result; @@ -177,8 +183,13 @@ using android::base::StringPrintf; InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel, sp<Looper> looper, - InputConsumerCallbacks& callbacks) - : mChannel(channel), mLooper(looper), mCallbacks(callbacks), mFdEvents(0) { + InputConsumerCallbacks& callbacks, + std::unique_ptr<Resampler> resampler) + : mChannel(channel), + mLooper(looper), + mCallbacks(callbacks), + mResampler(std::move(resampler)), + mFdEvents(0) { LOG_ALWAYS_FATAL_IF(mLooper == nullptr); mCallback = sp<LooperEventCallback>::make( std::bind(&InputConsumerNoResampling::handleReceiveCallback, this, @@ -463,6 +474,15 @@ InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t frameTime, } messages.pop(); } + // Check if resampling should be performed. + if (motionEvent != nullptr && isPointerEvent(*motionEvent) && mResampler != nullptr) { + InputMessage* futureSample = nullptr; + if (!messages.empty()) { + futureSample = &messages.front(); + } + mResampler->resampleMotionEvent(static_cast<std::chrono::nanoseconds>(frameTime), + *motionEvent, futureSample); + } return std::make_pair(std::move(motionEvent), firstSeqForBatch); } diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp new file mode 100644 index 0000000000..af8354c70c --- /dev/null +++ b/libs/input/Resampler.cpp @@ -0,0 +1,151 @@ +/** + * Copyright 2024 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. + */ + +#define LOG_TAG "LegacyResampler" + +#include <algorithm> +#include <chrono> + +#include <android-base/logging.h> +#include <android-base/properties.h> + +#include <input/Resampler.h> +#include <utils/Timers.h> + +using std::chrono::nanoseconds; + +namespace android { + +namespace { + +const bool IS_DEBUGGABLE_BUILD = +#if defined(__ANDROID__) + android::base::GetBoolProperty("ro.debuggable", false); +#else + true; +#endif + +bool debugResampling() { + if (!IS_DEBUGGABLE_BUILD) { + static const bool DEBUG_TRANSPORT_RESAMPLING = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", + ANDROID_LOG_INFO); + return DEBUG_TRANSPORT_RESAMPLING; + } + return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO); +} + +constexpr std::chrono::milliseconds RESAMPLE_LATENCY{5}; + +constexpr std::chrono::milliseconds RESAMPLE_MIN_DELTA{2}; + +constexpr std::chrono::milliseconds RESAMPLE_MAX_DELTA{20}; + +constexpr std::chrono::milliseconds RESAMPLE_MAX_PREDICTION{8}; + +inline float lerp(float a, float b, float alpha) { + return a + alpha * (b - a); +} + +const PointerCoords calculateResampledCoords(const PointerCoords& a, const PointerCoords& b, + const float alpha) { + // Ensure the struct PointerCoords is initialized. + PointerCoords resampledCoords{}; + resampledCoords.isResampled = true; + resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, lerp(a.getX(), b.getX(), alpha)); + resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(a.getY(), b.getY(), alpha)); + return resampledCoords; +} +} // namespace + +void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) { + const size_t motionEventSampleSize = motionEvent.getHistorySize() + 1; + for (size_t i = 0; i < motionEventSampleSize; ++i) { + Sample sample{static_cast<nanoseconds>(motionEvent.getHistoricalEventTime(i)), + *motionEvent.getPointerProperties(0), + motionEvent.getSamplePointerCoords()[i]}; + mLatestSamples.pushBack(sample); + } +} + +void LegacyResampler::interpolate(const nanoseconds resampleTime, MotionEvent& motionEvent, + const InputMessage& futureSample) const { + const Sample pastSample = mLatestSamples.back(); + const nanoseconds delta = + static_cast<nanoseconds>(futureSample.body.motion.eventTime) - pastSample.eventTime; + if (delta < RESAMPLE_MIN_DELTA) { + LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns."; + return; + } + const float alpha = + std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta; + + const PointerCoords resampledCoords = + calculateResampledCoords(pastSample.pointer.coords, + futureSample.body.motion.pointers[0].coords, alpha); + motionEvent.addSample(resampleTime.count(), &resampledCoords, motionEvent.getId()); +} + +void LegacyResampler::extrapolate(const nanoseconds resampleTime, MotionEvent& motionEvent) const { + if (mLatestSamples.size() < 2) { + return; + } + const Sample pastSample = *(mLatestSamples.end() - 2); + const Sample presentSample = *(mLatestSamples.end() - 1); + const nanoseconds delta = + static_cast<nanoseconds>(presentSample.eventTime - pastSample.eventTime); + if (delta < RESAMPLE_MIN_DELTA) { + LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns."; + return; + } else if (delta > RESAMPLE_MAX_DELTA) { + LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too large: " << delta << "ns."; + return; + } + // The farthest future time to which we can extrapolate. If the given resampleTime exceeds this, + // we use this value as the resample time target. + const nanoseconds farthestPrediction = static_cast<nanoseconds>(presentSample.eventTime) + + std::min<nanoseconds>(delta / 2, RESAMPLE_MAX_PREDICTION); + const nanoseconds newResampleTime = + (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."; + const float alpha = + std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) / + delta; + + const PointerCoords resampledCoords = + calculateResampledCoords(pastSample.pointer.coords, presentSample.pointer.coords, + alpha); + motionEvent.addSample(newResampleTime.count(), &resampledCoords, motionEvent.getId()); +} + +void LegacyResampler::resampleMotionEvent(const nanoseconds resampleTime, MotionEvent& motionEvent, + const InputMessage* futureSample) { + if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) { + mLatestSamples.clear(); + } + mPreviousDeviceId = motionEvent.getDeviceId(); + updateLatestSamples(motionEvent); + if (futureSample) { + interpolate(resampleTime, motionEvent, *futureSample); + } else { + extrapolate(resampleTime, motionEvent); + } + LOG_IF(INFO, debugResampling()) << "Not resampled. Not enough data."; +} +} // namespace android diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp index 0579967698..51edbf1533 100644 --- a/libs/input/VirtualInputDevice.cpp +++ b/libs/input/VirtualInputDevice.cpp @@ -16,6 +16,7 @@ #define LOG_TAG "VirtualInputDevice" +#include <android-base/logging.h> #include <android/input.h> #include <android/keycodes.h> #include <android_companion_virtualdevice_flags.h> @@ -23,26 +24,259 @@ #include <input/Input.h> #include <input/VirtualInputDevice.h> #include <linux/uinput.h> -#include <math.h> -#include <utils/Log.h> -#include <map> #include <string> using android::base::unique_fd; +namespace { + /** * Log debug messages about native virtual input devices. * Enable this via "adb shell setprop log.tag.VirtualInputDevice DEBUG" */ -static bool isDebug() { +bool isDebug() { return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO); } +unique_fd invalidFd() { + return unique_fd(-1); +} + +} // namespace + namespace android { namespace vd_flags = android::companion::virtualdevice::flags; +/** Creates a new uinput device and assigns a file descriptor. */ +unique_fd openUinput(const char* readableName, int32_t vendorId, int32_t productId, + const char* phys, DeviceType deviceType, int32_t screenHeight, + int32_t screenWidth) { + unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK))); + if (fd < 0) { + ALOGE("Error creating uinput device: %s", strerror(errno)); + return invalidFd(); + } + + ioctl(fd, UI_SET_PHYS, phys); + + ioctl(fd, UI_SET_EVBIT, EV_KEY); + ioctl(fd, UI_SET_EVBIT, EV_SYN); + switch (deviceType) { + case DeviceType::DPAD: + for (const auto& [_, keyCode] : VirtualDpad::DPAD_KEY_CODE_MAPPING) { + ioctl(fd, UI_SET_KEYBIT, keyCode); + } + break; + case DeviceType::KEYBOARD: + for (const auto& [_, keyCode] : VirtualKeyboard::KEY_CODE_MAPPING) { + ioctl(fd, UI_SET_KEYBIT, keyCode); + } + break; + case DeviceType::MOUSE: + ioctl(fd, UI_SET_EVBIT, EV_REL); + ioctl(fd, UI_SET_KEYBIT, BTN_LEFT); + ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT); + ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE); + ioctl(fd, UI_SET_KEYBIT, BTN_BACK); + ioctl(fd, UI_SET_KEYBIT, BTN_FORWARD); + ioctl(fd, UI_SET_RELBIT, REL_X); + ioctl(fd, UI_SET_RELBIT, REL_Y); + ioctl(fd, UI_SET_RELBIT, REL_WHEEL); + ioctl(fd, UI_SET_RELBIT, REL_HWHEEL); + if (vd_flags::high_resolution_scroll()) { + ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES); + ioctl(fd, UI_SET_RELBIT, REL_HWHEEL_HI_RES); + } + break; + case DeviceType::TOUCHSCREEN: + ioctl(fd, UI_SET_EVBIT, EV_ABS); + ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR); + ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE); + ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT); + break; + case DeviceType::STYLUS: + ioctl(fd, UI_SET_EVBIT, EV_ABS); + ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH); + ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS); + ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2); + ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_PEN); + ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_RUBBER); + ioctl(fd, UI_SET_ABSBIT, ABS_X); + ioctl(fd, UI_SET_ABSBIT, ABS_Y); + ioctl(fd, UI_SET_ABSBIT, ABS_TILT_X); + ioctl(fd, UI_SET_ABSBIT, ABS_TILT_Y); + ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE); + ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT); + break; + case DeviceType::ROTARY_ENCODER: + ioctl(fd, UI_SET_EVBIT, EV_REL); + ioctl(fd, UI_SET_RELBIT, REL_WHEEL); + if (vd_flags::high_resolution_scroll()) { + ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES); + } + break; + default: + ALOGE("Invalid input device type %d", static_cast<int32_t>(deviceType)); + return invalidFd(); + } + + int version; + if (ioctl(fd, UI_GET_VERSION, &version) == 0 && version >= 5) { + uinput_setup setup; + memset(&setup, 0, sizeof(setup)); + std::strncpy(setup.name, readableName, UINPUT_MAX_NAME_SIZE); + setup.id.version = 1; + setup.id.bustype = BUS_VIRTUAL; + setup.id.vendor = vendorId; + setup.id.product = productId; + if (deviceType == DeviceType::TOUCHSCREEN) { + uinput_abs_setup xAbsSetup; + xAbsSetup.code = ABS_MT_POSITION_X; + xAbsSetup.absinfo.maximum = screenWidth - 1; + xAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) { + ALOGE("Error creating touchscreen uinput x axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup yAbsSetup; + yAbsSetup.code = ABS_MT_POSITION_Y; + yAbsSetup.absinfo.maximum = screenHeight - 1; + yAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) { + ALOGE("Error creating touchscreen uinput y axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup majorAbsSetup; + majorAbsSetup.code = ABS_MT_TOUCH_MAJOR; + majorAbsSetup.absinfo.maximum = screenWidth - 1; + majorAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &majorAbsSetup) != 0) { + ALOGE("Error creating touchscreen uinput major axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup pressureAbsSetup; + pressureAbsSetup.code = ABS_MT_PRESSURE; + pressureAbsSetup.absinfo.maximum = 255; + pressureAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) { + ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup slotAbsSetup; + slotAbsSetup.code = ABS_MT_SLOT; + slotAbsSetup.absinfo.maximum = MAX_POINTERS - 1; + slotAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &slotAbsSetup) != 0) { + ALOGE("Error creating touchscreen uinput slots: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup trackingIdAbsSetup; + trackingIdAbsSetup.code = ABS_MT_TRACKING_ID; + trackingIdAbsSetup.absinfo.maximum = MAX_POINTERS - 1; + trackingIdAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &trackingIdAbsSetup) != 0) { + ALOGE("Error creating touchscreen uinput tracking ids: %s", strerror(errno)); + return invalidFd(); + } + } else if (deviceType == DeviceType::STYLUS) { + uinput_abs_setup xAbsSetup; + xAbsSetup.code = ABS_X; + xAbsSetup.absinfo.maximum = screenWidth - 1; + xAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) { + ALOGE("Error creating stylus uinput x axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup yAbsSetup; + yAbsSetup.code = ABS_Y; + yAbsSetup.absinfo.maximum = screenHeight - 1; + yAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) { + ALOGE("Error creating stylus uinput y axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup tiltXAbsSetup; + tiltXAbsSetup.code = ABS_TILT_X; + tiltXAbsSetup.absinfo.maximum = 90; + tiltXAbsSetup.absinfo.minimum = -90; + if (ioctl(fd, UI_ABS_SETUP, &tiltXAbsSetup) != 0) { + ALOGE("Error creating stylus uinput tilt x axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup tiltYAbsSetup; + tiltYAbsSetup.code = ABS_TILT_Y; + tiltYAbsSetup.absinfo.maximum = 90; + tiltYAbsSetup.absinfo.minimum = -90; + if (ioctl(fd, UI_ABS_SETUP, &tiltYAbsSetup) != 0) { + ALOGE("Error creating stylus uinput tilt y axis: %s", strerror(errno)); + return invalidFd(); + } + uinput_abs_setup pressureAbsSetup; + pressureAbsSetup.code = ABS_PRESSURE; + pressureAbsSetup.absinfo.maximum = 255; + pressureAbsSetup.absinfo.minimum = 0; + if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) { + ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno)); + return invalidFd(); + } + } + if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) { + ALOGE("Error creating uinput device: %s", strerror(errno)); + return invalidFd(); + } + } else { + // UI_DEV_SETUP was not introduced until version 5. Try setting up manually. + ALOGI("Falling back to version %d manual setup", version); + uinput_user_dev fallback; + memset(&fallback, 0, sizeof(fallback)); + std::strncpy(fallback.name, readableName, UINPUT_MAX_NAME_SIZE); + fallback.id.version = 1; + fallback.id.bustype = BUS_VIRTUAL; + fallback.id.vendor = vendorId; + fallback.id.product = productId; + if (deviceType == DeviceType::TOUCHSCREEN) { + fallback.absmin[ABS_MT_POSITION_X] = 0; + fallback.absmax[ABS_MT_POSITION_X] = screenWidth - 1; + fallback.absmin[ABS_MT_POSITION_Y] = 0; + fallback.absmax[ABS_MT_POSITION_Y] = screenHeight - 1; + fallback.absmin[ABS_MT_TOUCH_MAJOR] = 0; + fallback.absmax[ABS_MT_TOUCH_MAJOR] = screenWidth - 1; + fallback.absmin[ABS_MT_PRESSURE] = 0; + fallback.absmax[ABS_MT_PRESSURE] = 255; + } else if (deviceType == DeviceType::STYLUS) { + fallback.absmin[ABS_X] = 0; + fallback.absmax[ABS_X] = screenWidth - 1; + fallback.absmin[ABS_Y] = 0; + fallback.absmax[ABS_Y] = screenHeight - 1; + fallback.absmin[ABS_TILT_X] = -90; + fallback.absmax[ABS_TILT_X] = 90; + fallback.absmin[ABS_TILT_Y] = -90; + fallback.absmax[ABS_TILT_Y] = 90; + fallback.absmin[ABS_PRESSURE] = 0; + fallback.absmax[ABS_PRESSURE] = 255; + } + if (TEMP_FAILURE_RETRY(write(fd, &fallback, sizeof(fallback))) != sizeof(fallback)) { + ALOGE("Error creating uinput device: %s", strerror(errno)); + return invalidFd(); + } + } + + if (ioctl(fd, UI_DEV_CREATE) != 0) { + ALOGE("Error creating uinput device: %s", strerror(errno)); + return invalidFd(); + } + + return fd; +} + VirtualInputDevice::VirtualInputDevice(unique_fd fd) : mFd(std::move(fd)) {} VirtualInputDevice::~VirtualInputDevice() { diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index e9d799ed3f..132866bd99 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -23,6 +23,7 @@ cc_test { "InputVerifier_test.cpp", "MotionPredictor_test.cpp", "MotionPredictorMetricsManager_test.cpp", + "Resampler_test.cpp", "RingBuffer_test.cpp", "TfLiteMotionPredictor_test.cpp", "TouchResampling_test.cpp", diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp index e7106135fd..467c3b46ce 100644 --- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp @@ -396,8 +396,9 @@ void InputPublisherAndConsumerNoResamplingTest::handleMessage(const Message& mes break; } case LooperMessage::CREATE_CONSUMER: { - mConsumer = std::make_unique<InputConsumerNoResampling>(std::move(mClientChannel), - mLooper, *this); + mConsumer = + std::make_unique<InputConsumerNoResampling>(std::move(mClientChannel), mLooper, + *this, /*resampler=*/nullptr); break; } case LooperMessage::DESTROY_CONSUMER: { diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp new file mode 100644 index 0000000000..e160ca06d0 --- /dev/null +++ b/libs/input/tests/Resampler_test.cpp @@ -0,0 +1,417 @@ +/** + * Copyright 2024 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. + */ + +#include <input/Resampler.h> + +#include <gtest/gtest.h> + +#include <chrono> +#include <memory> +#include <vector> + +#include <input/Input.h> +#include <input/InputEventBuilders.h> +#include <input/InputTransport.h> +#include <utils/Timers.h> + +namespace android { + +namespace { + +using namespace std::literals::chrono_literals; + +constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; + +struct Pointer { + int32_t id{0}; + ToolType toolType{ToolType::FINGER}; + float x{0.0f}; + float y{0.0f}; + bool isResampled{false}; + /** + * Converts from Pointer to PointerCoords. Enables calling LegacyResampler methods and + * assertions only with the relevant data for tests. + */ + operator PointerCoords() const; +}; + +Pointer::operator PointerCoords() const { + PointerCoords pointerCoords; + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + pointerCoords.isResampled = isResampled; + return pointerCoords; +} + +struct InputSample { + std::chrono::milliseconds eventTime{0}; + std::vector<Pointer> pointers{}; + /** + * Converts from InputSample to InputMessage. Enables calling LegacyResampler methods only with + * the relevant data for tests. + */ + operator InputMessage() const; +}; + +InputSample::operator InputMessage() const { + InputMessage message; + message.header.type = InputMessage::Type::MOTION; + message.body.motion.pointerCount = pointers.size(); + message.body.motion.eventTime = static_cast<std::chrono::nanoseconds>(eventTime).count(); + message.body.motion.source = AINPUT_SOURCE_CLASS_POINTER; + message.body.motion.downTime = 0; + const uint32_t pointerCount = message.body.motion.pointerCount; + for (uint32_t i = 0; i < pointerCount; ++i) { + message.body.motion.pointers[i].properties.id = pointers[i].id; + message.body.motion.pointers[i].properties.toolType = pointers[i].toolType; + message.body.motion.pointers[i].coords.setAxisValue(AMOTION_EVENT_AXIS_X, pointers[i].x); + message.body.motion.pointers[i].coords.setAxisValue(AMOTION_EVENT_AXIS_Y, pointers[i].y); + message.body.motion.pointers[i].coords.isResampled = pointers[i].isResampled; + } + return message; +} + +struct InputStream { + std::vector<InputSample> samples{}; + int32_t action{0}; + DeviceId deviceId{0}; + /** + * Converts from InputStream to MotionEvent. Enables calling LegacyResampler methods only with + * the relevant data for tests. + */ + operator MotionEvent() const; +}; + +InputStream::operator MotionEvent() const { + const InputSample& firstSample{*samples.begin()}; + MotionEventBuilder motionEventBuilder = + MotionEventBuilder(action, AINPUT_SOURCE_CLASS_POINTER) + .downTime(0) + .eventTime(static_cast<std::chrono::nanoseconds>(firstSample.eventTime).count()) + .deviceId(deviceId); + for (const Pointer& pointer : firstSample.pointers) { + const PointerBuilder pointerBuilder = + PointerBuilder(pointer.id, pointer.toolType).x(pointer.x).y(pointer.y); + motionEventBuilder.pointer(pointerBuilder); + } + MotionEvent motionEvent = motionEventBuilder.build(); + const size_t numSamples = samples.size(); + for (size_t i = 1; i < numSamples; ++i) { + std::vector<PointerCoords> pointersCoords{samples[i].pointers.begin(), + samples[i].pointers.end()}; + motionEvent.addSample(static_cast<std::chrono::nanoseconds>(samples[i].eventTime).count(), + pointersCoords.data(), motionEvent.getId()); + } + return motionEvent; +} + +} // namespace + +class ResamplerTest : public testing::Test { +protected: + ResamplerTest() : mResampler(std::make_unique<LegacyResampler>()) {} + + ~ResamplerTest() override {} + + void SetUp() override {} + + void TearDown() override {} + + std::unique_ptr<Resampler> mResampler; + + MotionEvent buildMotionEvent(const int32_t action, const nsecs_t eventTime, + const std::vector<PointerBuilder>& pointers); + + InputMessage createMessage(const uint32_t pointerCount, const nsecs_t eventTime, + const int32_t action, + const std::vector<PointerProperties>& properties, + const std::vector<PointerCoords>& coords); + + /** + * Checks that beforeCall and afterCall are equal except for the mutated attributes by addSample + * member function. + * @param beforeCall MotionEvent before passing it to resampleMotionEvent + * @param afterCall MotionEvent after passing it to resampleMotionEvent + */ + void assertMotionEventMetaDataDidNotMutate(const MotionEvent& beforeCall, + const MotionEvent& afterCall); + + /** + * Asserts the MotionEvent is resampled by checking an increment in history size and that the + * resampled coordinates are near the expected ones. + */ + void assertMotionEventIsResampledAndCoordsNear(const MotionEvent& original, + const MotionEvent& resampled, + const PointerCoords& expectedCoords); + + void assertMotionEventIsNotResampled(const MotionEvent& original, + const MotionEvent& notResampled); +}; + +MotionEvent ResamplerTest::buildMotionEvent(const int32_t action, const nsecs_t eventTime, + const std::vector<PointerBuilder>& pointerBuilders) { + MotionEventBuilder motionEventBuilder = MotionEventBuilder(action, AINPUT_SOURCE_CLASS_POINTER) + .downTime(0) + .eventTime(eventTime); + for (const PointerBuilder& pointerBuilder : pointerBuilders) { + motionEventBuilder.pointer(pointerBuilder); + } + return motionEventBuilder.build(); +} + +InputMessage ResamplerTest::createMessage(const uint32_t pointerCount, const nsecs_t eventTime, + const int32_t action, + const std::vector<PointerProperties>& properties, + const std::vector<PointerCoords>& coords) { + InputMessage message; + message.header.type = InputMessage::Type::MOTION; + message.body.motion.pointerCount = pointerCount; + message.body.motion.eventTime = eventTime; + message.body.motion.source = AINPUT_SOURCE_CLASS_POINTER; + message.body.motion.downTime = 0; + for (uint32_t i = 0; i < pointerCount; ++i) { + message.body.motion.pointers[i].properties = properties[i]; + message.body.motion.pointers[i].coords = coords[i]; + } + return message; +} + +void ResamplerTest::assertMotionEventMetaDataDidNotMutate(const MotionEvent& beforeCall, + const MotionEvent& afterCall) { + EXPECT_EQ(beforeCall.getDeviceId(), afterCall.getDeviceId()); + EXPECT_EQ(beforeCall.getAction(), afterCall.getAction()); + EXPECT_EQ(beforeCall.getActionButton(), afterCall.getActionButton()); + EXPECT_EQ(beforeCall.getButtonState(), afterCall.getButtonState()); + EXPECT_EQ(beforeCall.getFlags(), afterCall.getFlags()); + EXPECT_EQ(beforeCall.getEdgeFlags(), afterCall.getEdgeFlags()); + EXPECT_EQ(beforeCall.getClassification(), afterCall.getClassification()); + EXPECT_EQ(beforeCall.getPointerCount(), afterCall.getPointerCount()); + EXPECT_EQ(beforeCall.getMetaState(), afterCall.getMetaState()); + EXPECT_EQ(beforeCall.getSource(), afterCall.getSource()); + EXPECT_EQ(beforeCall.getXPrecision(), afterCall.getXPrecision()); + EXPECT_EQ(beforeCall.getYPrecision(), afterCall.getYPrecision()); + EXPECT_EQ(beforeCall.getDownTime(), afterCall.getDownTime()); + EXPECT_EQ(beforeCall.getDisplayId(), afterCall.getDisplayId()); +} + +void ResamplerTest::assertMotionEventIsResampledAndCoordsNear(const MotionEvent& original, + const MotionEvent& resampled, + const PointerCoords& expectedCoords) { + assertMotionEventMetaDataDidNotMutate(original, resampled); + const size_t originalSampleSize = original.getHistorySize() + 1; + const size_t resampledSampleSize = resampled.getHistorySize() + 1; + EXPECT_EQ(originalSampleSize + 1, resampledSampleSize); + const PointerCoords& resampledCoords = + resampled.getSamplePointerCoords()[resampled.getHistorySize()]; + EXPECT_TRUE(resampledCoords.isResampled); + EXPECT_NEAR(expectedCoords.getX(), resampledCoords.getX(), EPSILON); + EXPECT_NEAR(expectedCoords.getY(), resampledCoords.getY(), EPSILON); +} + +void ResamplerTest::assertMotionEventIsNotResampled(const MotionEvent& original, + const MotionEvent& notResampled) { + assertMotionEventMetaDataDidNotMutate(original, notResampled); + const size_t originalSampleSize = original.getHistorySize() + 1; + const size_t notResampledSampleSize = notResampled.getHistorySize() + 1; + EXPECT_EQ(originalSampleSize, notResampledSampleSize); +} + +TEST_F(ResamplerTest, SinglePointerNotEnoughDataToResample) { + MotionEvent motionEvent = + InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE, + .deviceId = 0}; + const MotionEvent originalMotionEvent = motionEvent; + mResampler->resampleMotionEvent(11ms, motionEvent, nullptr); + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, SinglePointerDifferentDeviceIdBetweenMotionEvents) { + MotionEvent motionFromFirstDevice = + InputStream{{{4ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, + {8ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE, + .deviceId = 0}; + mResampler->resampleMotionEvent(10ms, motionFromFirstDevice, nullptr); + MotionEvent motionFromSecondDevice = + InputStream{{{11ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE, + .deviceId = 1}; + const MotionEvent originalMotionEvent = motionFromSecondDevice; + mResampler->resampleMotionEvent(12ms, motionFromSecondDevice, nullptr); + // The MotionEvent should not be resampled because the second event came from a different device + // than the previous event. + assertMotionEventIsNotResampled(originalMotionEvent, motionFromSecondDevice); +} + +// Increments of 16 ms for display refresh rate +// Increments of 6 ms for input frequency +// Resampling latency is known to be 5 ms +// Therefore, first resampling time will be 11 ms + +/** + * Timeline + * ----+----------------------+---------+---------+---------+---------- + * 0ms 10ms 11ms 15ms 16ms + * DOWN MOVE | MSG | + * resample frame + * Resampling occurs at 11ms. It is possible to interpolate because there is a sample available + * after the resample time. It is assumed that the InputMessage frequency is 100Hz, and the frame + * frequency is 60Hz. This means the time between InputMessage samples is 10ms, and the time between + * frames is ~16ms. Resample time is frameTime - RESAMPLE_LATENCY. The resampled sample must be the + * last one in the batch to consume. + */ +TEST_F(ResamplerTest, SinglePointerSingleSampleInterpolation) { + MotionEvent motionEvent = + InputStream{{{10ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + const InputMessage futureSample = + InputSample{15ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + Pointer{.id = 0, + .x = 1.2f, + .y = 1.2f, + .isResampled = true}); +} + +TEST_F(ResamplerTest, SinglePointerDeltaTooSmallInterpolation) { + MotionEvent motionEvent = + InputStream{{{10ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + const InputMessage futureSample = + InputSample{11ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(10'500'000ns, motionEvent, &futureSample); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +/** + * Tests extrapolation given two MotionEvents with a single sample. + */ +TEST_F(ResamplerTest, SinglePointerSingleSampleExtrapolation) { + MotionEvent previousMotionEvent = + InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + mResampler->resampleMotionEvent(10ms, previousMotionEvent, nullptr); + + MotionEvent motionEvent = + InputStream{{{10ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(11ms, motionEvent, nullptr); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + Pointer{.id = 0, + .x = 1.0f, + .y = 1.0f, + .isResampled = true}); + // Integrity of the whole motionEvent + // History size should increment by 1 + // Check if the resampled value is the last one + // Check if the resampleTime is correct + // Check if the PointerCoords are consistent with the other computations +} + +TEST_F(ResamplerTest, SinglePointerMultipleSampleInterpolation) { + MotionEvent motionEvent = + InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, + {10ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + const InputMessage futureSample = + InputSample{15ms, {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + Pointer{.id = 0, + .x = 2.2f, + .y = 2.2f, + .isResampled = true}); +} + +TEST_F(ResamplerTest, SinglePointerMultipleSampleExtrapolation) { + MotionEvent motionEvent = + InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, + {10ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(11ms, motionEvent, nullptr); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + Pointer{.id = 0, + .x = 2.2f, + .y = 2.2f, + .isResampled = true}); +} + +TEST_F(ResamplerTest, SinglePointerDeltaTooSmallExtrapolation) { + MotionEvent motionEvent = + InputStream{{{9ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, + {10ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(11ms, motionEvent, nullptr); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, SinglePointerDeltaTooLargeExtrapolation) { + MotionEvent motionEvent = + InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, + {26ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(27ms, motionEvent, nullptr); + + assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); +} + +TEST_F(ResamplerTest, SinglePointerResampleTimeTooFarExtrapolation) { + MotionEvent motionEvent = + InputStream{{{5ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}}, + {25ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}}, + AMOTION_EVENT_ACTION_MOVE}; + + const MotionEvent originalMotionEvent = motionEvent; + + mResampler->resampleMotionEvent(43ms, motionEvent, nullptr); + + assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, + Pointer{.id = 0, + .x = 2.4f, + .y = 2.4f, + .isResampled = true}); +} +} // namespace android diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h index 0dac662e97..7278d2d3e1 100644 --- a/libs/math/include/math/TVecHelpers.h +++ b/libs/math/include/math/TVecHelpers.h @@ -620,15 +620,10 @@ public: } // namespace details } // namespace android -namespace std { - template<template<typename T> class VECTOR, typename T> - struct hash<VECTOR<T>> { - static constexpr bool IS_VECTOR = - std::is_base_of<android::details::TVecUnaryOperators<VECTOR, T>, VECTOR<T>>::value; - - typename std::enable_if<IS_VECTOR, size_t>::type - operator()(const VECTOR<T>& v) const { - return v.hash(); - } - }; -} +#define TVECHELPERS_STD_HASH(VECTOR) \ + template <typename T> \ + struct std::hash<VECTOR<T>> { \ + size_t operator()(const VECTOR<T>& v) const { \ + return v.hash(); \ + } \ + } diff --git a/libs/math/include/math/mat2.h b/libs/math/include/math/mat2.h index 3e6cd4c794..24c2bad420 100644 --- a/libs/math/include/math/mat2.h +++ b/libs/math/include/math/mat2.h @@ -373,5 +373,7 @@ typedef details::TMat22<float> mat2f; // ---------------------------------------------------------------------------------------- } // namespace android +TVECHELPERS_STD_HASH(android::details::TMat22); + #undef PURE #undef CONSTEXPR diff --git a/libs/math/include/math/mat3.h b/libs/math/include/math/mat3.h index 5c8a9b2573..4647a60917 100644 --- a/libs/math/include/math/mat3.h +++ b/libs/math/include/math/mat3.h @@ -436,5 +436,7 @@ typedef details::TMat33<float> mat3f; // ---------------------------------------------------------------------------------------- } // namespace android +TVECHELPERS_STD_HASH(android::details::TMat33); + #undef PURE #undef CONSTEXPR diff --git a/libs/math/include/math/mat4.h b/libs/math/include/math/mat4.h index c630d972b2..c9e118ab34 100644 --- a/libs/math/include/math/mat4.h +++ b/libs/math/include/math/mat4.h @@ -590,5 +590,7 @@ typedef details::TMat44<float> mat4f; // ---------------------------------------------------------------------------------------- } // namespace android +TVECHELPERS_STD_HASH(android::details::TMat44); + #undef PURE #undef CONSTEXPR diff --git a/libs/math/include/math/quat.h b/libs/math/include/math/quat.h index 07573c5ecf..43c8038cc1 100644 --- a/libs/math/include/math/quat.h +++ b/libs/math/include/math/quat.h @@ -187,6 +187,8 @@ constexpr inline quatd operator"" _kd(unsigned long long v) { // NOLINT // ---------------------------------------------------------------------------------------- } // namespace android +TVECHELPERS_STD_HASH(android::details::TQuaternion); + #pragma clang diagnostic pop #undef PURE diff --git a/libs/math/include/math/vec2.h b/libs/math/include/math/vec2.h index e0adb7f6cc..909c77eb28 100644 --- a/libs/math/include/math/vec2.h +++ b/libs/math/include/math/vec2.h @@ -122,4 +122,6 @@ typedef details::TVec2<bool> bool2; // ---------------------------------------------------------------------------------------- } // namespace android +TVECHELPERS_STD_HASH(android::details::TVec2); + #pragma clang diagnostic pop diff --git a/libs/math/include/math/vec3.h b/libs/math/include/math/vec3.h index 21fb684efc..ff2b3e4f54 100644 --- a/libs/math/include/math/vec3.h +++ b/libs/math/include/math/vec3.h @@ -128,4 +128,6 @@ typedef details::TVec3<bool> bool3; // ---------------------------------------------------------------------------------------- } // namespace android +TVECHELPERS_STD_HASH(android::details::TVec3); + #pragma clang diagnostic pop diff --git a/libs/math/include/math/vec4.h b/libs/math/include/math/vec4.h index 1e279fe3f2..16509c91a1 100644 --- a/libs/math/include/math/vec4.h +++ b/libs/math/include/math/vec4.h @@ -125,4 +125,6 @@ typedef details::TVec4<bool> bool4; // ---------------------------------------------------------------------------------------- } // namespace android +TVECHELPERS_STD_HASH(android::details::TVec4); + #pragma clang diagnostic pop diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp index 84852eabec..bec9255fd7 100644 --- a/libs/sensor/SensorEventQueue.cpp +++ b/libs/sensor/SensorEventQueue.cpp @@ -42,10 +42,11 @@ namespace android { // ---------------------------------------------------------------------------- SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection, - SensorManager& sensorManager) + SensorManager& sensorManager, String8 packageName) : mSensorEventConnection(connection), mRecBuffer(nullptr), mSensorManager(sensorManager), + mPackageName(packageName), mAvailable(0), mConsumed(0), mNumAcksToSend(0) { @@ -92,8 +93,11 @@ ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) { mSensorManager.getSensorNameByHandle(events->sensor); if (sensorName.has_value()) { char buffer[UINT8_MAX]; - std::snprintf(buffer, sizeof(buffer), "Sensor event from %s", - sensorName.value().data()); + IPCThreadState* thread = IPCThreadState::self(); + pid_t pid = (thread != nullptr) ? thread->getCallingPid() : -1; + std::snprintf(buffer, sizeof(buffer), + "Sensor event from %s to %s PID: %d (%zu/%zu)", + sensorName.value().data(), mPackageName.c_str(), pid, i, count); ATRACE_INSTANT_FOR_TRACK(LOG_TAG, buffer); } } diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index 3ca6f0f904..56736783f2 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -73,8 +73,6 @@ int getDeviceIdForUid(uid_t uid) { return deviceId; } } - } else { - ALOGW("Cannot get virtualdevice_native service"); } return DEVICE_ID_DEFAULT; } @@ -403,7 +401,7 @@ sp<SensorEventQueue> SensorManager::createEventQueue( ALOGE("createEventQueue: connection is NULL."); return nullptr; } - queue = new SensorEventQueue(connection, *this); + queue = new SensorEventQueue(connection, *this, packageName); break; } return queue; diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h index 0bcaadc7c0..d31def7425 100644 --- a/libs/sensor/include/sensor/SensorEventQueue.h +++ b/libs/sensor/include/sensor/SensorEventQueue.h @@ -20,9 +20,10 @@ #include <sys/types.h> #include <utils/Errors.h> +#include <utils/Mutex.h> #include <utils/RefBase.h> +#include <utils/String8.h> #include <utils/Timers.h> -#include <utils/Mutex.h> #include <sensor/BitTube.h> @@ -67,7 +68,7 @@ public: static constexpr int32_t SENSOR_DELAY_NORMAL = 200000; explicit SensorEventQueue(const sp<ISensorEventConnection>& connection, - SensorManager& sensorManager); + SensorManager& sensorManager, String8 packageName); virtual ~SensorEventQueue(); virtual void onFirstRef(); @@ -110,6 +111,7 @@ private: mutable sp<Looper> mLooper; ASensorEvent* mRecBuffer; SensorManager& mSensorManager; + String8 mPackageName; size_t mAvailable; size_t mConsumed; uint32_t mNumAcksToSend; diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp index 42dd85e959..23249fa4a9 100644 --- a/libs/ui/Transform.cpp +++ b/libs/ui/Transform.cpp @@ -110,10 +110,12 @@ const vec3& Transform::operator [] (size_t i) const { return mMatrix[i]; } +// x translate float Transform::tx() const { return mMatrix[2][0]; } +// y translate float Transform::ty() const { return mMatrix[2][1]; } @@ -167,11 +169,15 @@ void Transform::set(float tx, float ty) { } } -void Transform::set(float a, float b, float c, float d) { +// x and y are the coordinates in the destination (i.e. the screen) +// s and t are the coordinates in the source (i.e. the texture) +// d means derivative +// dsdx means ds/dx derivative of s with respect to x, etc. +void Transform::set(float dsdx, float dtdy, float dtdx, float dsdy) { mat33& M(mMatrix); - M[0][0] = a; M[1][0] = b; - M[0][1] = c; M[1][1] = d; - M[0][2] = 0; M[1][2] = 0; + M[0][0] = dsdx; M[1][0] = dtdy; + M[0][1] = dtdx; M[1][1] = dsdy; + M[0][2] = 0; M[1][2] = 0; mType = UNKNOWN_TYPE; } diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp index 2af51a722c..d3b3a734f9 100644 --- a/libs/vibrator/Android.bp +++ b/libs/vibrator/Android.bp @@ -24,6 +24,10 @@ package { cc_defaults { name: "libvibrator_defaults", + defaults: [ + "aconfig_lib_cc_shared_link.defaults", + ], + cflags: [ "-Wall", "-Werror", @@ -50,9 +54,11 @@ cc_library { "libbinder", "liblog", "libutils", + "server_configurable_flags", ], whole_static_libs: [ + "android.os.vibrator.flags-aconfig-cc", "libvibratorutils", ], @@ -79,8 +85,14 @@ cc_library { vendor_available: true, double_loadable: true, + static_libs: [ + "android.os.vibrator.flags-aconfig-cc", + ], + shared_libs: [ + "liblog", "libutils", + "server_configurable_flags", ], srcs: [ @@ -89,6 +101,7 @@ cc_library { visibility: [ "//frameworks/native/libs/vibrator", + "//frameworks/native/libs/vibrator/tests", "//frameworks/av/media/libeffects/hapticgenerator", ], } diff --git a/libs/vibrator/ExternalVibration.cpp b/libs/vibrator/ExternalVibration.cpp index c97e496083..cae2de273e 100644 --- a/libs/vibrator/ExternalVibration.cpp +++ b/libs/vibrator/ExternalVibration.cpp @@ -93,8 +93,8 @@ os::HapticScale ExternalVibration::externalVibrationScaleToHapticScale( externalVibrationScale.scaleLevel); } - return {/*level=*/scaleLevel, /*adaptiveScaleFactor=*/ - externalVibrationScale.adaptiveHapticsScale}; + return os::HapticScale(scaleLevel, externalVibrationScale.scaleFactor, + externalVibrationScale.adaptiveHapticsScale); } } // namespace os diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp index 761ac1bf3b..54afb71a97 100644 --- a/libs/vibrator/ExternalVibrationUtils.cpp +++ b/libs/vibrator/ExternalVibrationUtils.cpp @@ -13,10 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#define LOG_TAG "ExternalVibrationUtils" + #include <cstring> +#include <android_os_vibrator.h> + +#include <algorithm> #include <math.h> +#include <log/log.h> #include <vibrator/ExternalVibrationUtils.h> namespace android::os { @@ -25,8 +31,10 @@ namespace { static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f; static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f; static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f; +static constexpr float SCALE_GAMMA = 0.65f; // Same as VibrationEffect.SCALE_GAMMA +static constexpr float SCALE_LEVEL_GAIN = 1.4f; // Same as VibrationConfig.DEFAULT_SCALE_LEVEL_GAIN -float getHapticScaleGamma(HapticLevel level) { +float getOldHapticScaleGamma(HapticLevel level) { switch (level) { case HapticLevel::VERY_LOW: return 2.0f; @@ -41,7 +49,7 @@ float getHapticScaleGamma(HapticLevel level) { } } -float getHapticMaxAmplitudeRatio(HapticLevel level) { +float getOldHapticMaxAmplitudeRatio(HapticLevel level) { switch (level) { case HapticLevel::VERY_LOW: return HAPTIC_SCALE_VERY_LOW_RATIO; @@ -56,6 +64,85 @@ float getHapticMaxAmplitudeRatio(HapticLevel level) { } } +/* Same as VibrationScaler.getScaleFactor */ +float getHapticScaleFactor(HapticScale scale) { + if (android_os_vibrator_haptics_scale_v2_enabled()) { + if (scale.getScaleFactor() >= 0) { + // ExternalVibratorService provided the scale factor, use it. + return scale.getScaleFactor(); + } + + HapticLevel level = scale.getLevel(); + switch (level) { + case HapticLevel::MUTE: + return 0.0f; + case HapticLevel::NONE: + return 1.0f; + default: + float scaleFactor = powf(SCALE_LEVEL_GAIN, static_cast<int32_t>(level)); + if (scaleFactor <= 0) { + ALOGE("Invalid scale factor %.2f for level %d, using fallback to 1.0", + scaleFactor, static_cast<int32_t>(level)); + scaleFactor = 1.0f; + } + return scaleFactor; + } + } + // Same as VibrationScaler.SCALE_FACTOR_* + switch (scale.getLevel()) { + case HapticLevel::MUTE: + return 0.0f; + case HapticLevel::VERY_LOW: + return 0.6f; + case HapticLevel::LOW: + return 0.8f; + case HapticLevel::HIGH: + return 1.2f; + case HapticLevel::VERY_HIGH: + return 1.4f; + default: + return 1.0f; + } +} + +float applyOldHapticScale(float value, float gamma, float maxAmplitudeRatio) { + float sign = value >= 0 ? 1.0 : -1.0; + return powf(fabsf(value / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma) + * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign; +} + +float applyNewHapticScale(float value, float scaleFactor) { + if (android_os_vibrator_haptics_scale_v2_enabled()) { + if (scaleFactor <= 1 || value == 0) { + return value * scaleFactor; + } else { + // Using S * x / (1 + (S - 1) * x^2) as the scale up function to converge to 1.0. + return (value * scaleFactor) / (1 + (scaleFactor - 1) * value * value); + } + } + float scale = powf(scaleFactor, 1.0f / SCALE_GAMMA); + if (scaleFactor <= 1) { + // Scale down is simply a gamma corrected application of scaleFactor to the intensity. + // Scale up requires a different curve to ensure the intensity will not become > 1. + return value * scale; + } + + float sign = value >= 0 ? 1.0f : -1.0f; + float extraScale = powf(scaleFactor, 4.0f - scaleFactor); + float x = fabsf(value) * scale * extraScale; + float maxX = scale * extraScale; // scaled x for intensity == 1 + + float expX = expf(x); + float expMaxX = expf(maxX); + + // Using f = tanh as the scale up function so the max value will converge. + // a = 1/f(maxX), used to scale f so that a*f(maxX) = 1 (the value will converge to 1). + float a = (expMaxX + 1.0f) / (expMaxX - 1.0f); + float fx = (expX - 1.0f) / (expX + 1.0f); + + return sign * std::clamp(a * fx, 0.0f, 1.0f); +} + void applyHapticScale(float* buffer, size_t length, HapticScale scale) { if (scale.isScaleMute()) { memset(buffer, 0, length * sizeof(float)); @@ -65,15 +152,19 @@ void applyHapticScale(float* buffer, size_t length, HapticScale scale) { return; } HapticLevel hapticLevel = scale.getLevel(); + float scaleFactor = getHapticScaleFactor(scale); float adaptiveScaleFactor = scale.getAdaptiveScaleFactor(); - float gamma = getHapticScaleGamma(hapticLevel); - float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(hapticLevel); + float oldGamma = getOldHapticScaleGamma(hapticLevel); + float oldMaxAmplitudeRatio = getOldHapticMaxAmplitudeRatio(hapticLevel); for (size_t i = 0; i < length; i++) { if (hapticLevel != HapticLevel::NONE) { - float sign = buffer[i] >= 0 ? 1.0 : -1.0; - buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma) - * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign; + if (android_os_vibrator_fix_audio_coupled_haptics_scaling() || + android_os_vibrator_haptics_scale_v2_enabled()) { + buffer[i] = applyNewHapticScale(buffer[i], scaleFactor); + } else { + buffer[i] = applyOldHapticScale(buffer[i], oldGamma, oldMaxAmplitudeRatio); + } } if (adaptiveScaleFactor != 1.0f) { diff --git a/libs/vibrator/TEST_MAPPING b/libs/vibrator/TEST_MAPPING new file mode 100644 index 0000000000..d782b43b57 --- /dev/null +++ b/libs/vibrator/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit": [ + { + "name": "libvibrator_test" + } + ] +} diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h index d9a2b81ddf..322a2ac899 100644 --- a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h +++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h @@ -17,9 +17,13 @@ #ifndef ANDROID_EXTERNAL_VIBRATION_UTILS_H #define ANDROID_EXTERNAL_VIBRATION_UTILS_H +#include <cstring> +#include <sstream> +#include <string> + namespace android::os { -enum class HapticLevel { +enum class HapticLevel : int32_t { MUTE = -100, VERY_LOW = -2, LOW = -1, @@ -31,32 +35,42 @@ enum class HapticLevel { class HapticScale { private: HapticLevel mLevel = HapticLevel::NONE; +float mScaleFactor = -1.0f; // undefined, use haptic level to define scale factor float mAdaptiveScaleFactor = 1.0f; public: -constexpr HapticScale(HapticLevel level, float adaptiveScaleFactor) - : mLevel(level), mAdaptiveScaleFactor(adaptiveScaleFactor) {} -constexpr HapticScale(HapticLevel level) : mLevel(level) {} -constexpr HapticScale() {} + explicit HapticScale(HapticLevel level, float scaleFactor, float adaptiveScaleFactor) + : mLevel(level), mScaleFactor(scaleFactor), mAdaptiveScaleFactor(adaptiveScaleFactor) {} + explicit HapticScale(HapticLevel level) : mLevel(level) {} + constexpr HapticScale() {} -HapticLevel getLevel() const { return mLevel; } -float getAdaptiveScaleFactor() const { return mAdaptiveScaleFactor; } + HapticLevel getLevel() const { return mLevel; } + float getScaleFactor() const { return mScaleFactor; } + float getAdaptiveScaleFactor() const { return mAdaptiveScaleFactor; } -bool operator==(const HapticScale& other) const { - return mLevel == other.mLevel && mAdaptiveScaleFactor == other.mAdaptiveScaleFactor; -} + bool operator==(const HapticScale& other) const { + return mLevel == other.mLevel && mScaleFactor == other.mScaleFactor && + mAdaptiveScaleFactor == other.mAdaptiveScaleFactor; + } bool isScaleNone() const { - return mLevel == HapticLevel::NONE && mAdaptiveScaleFactor == 1.0f; + return (mLevel == HapticLevel::NONE || mScaleFactor == 1.0f) && mAdaptiveScaleFactor == 1.0f; } -bool isScaleMute() const { - return mLevel == HapticLevel::MUTE; -} +bool isScaleMute() const { return mLevel == HapticLevel::MUTE || mScaleFactor == 0; } -static HapticScale mute() { - return {/*level=*/os::HapticLevel::MUTE}; +std::string toString() const { + std::ostringstream os; + os << "HapticScale { level: " << static_cast<int>(mLevel); + os << ", scaleFactor: " << mScaleFactor; + os << ", adaptiveScaleFactor: " << mAdaptiveScaleFactor; + os << "}"; + return os.str(); } + +static HapticScale mute() { return os::HapticScale(os::HapticLevel::MUTE); } + +static HapticScale none() { return os::HapticScale(os::HapticLevel::NONE); } }; bool isValidHapticScale(HapticScale scale); diff --git a/libs/vibrator/tests/Android.bp b/libs/vibrator/tests/Android.bp new file mode 100644 index 0000000000..2921a6275b --- /dev/null +++ b/libs/vibrator/tests/Android.bp @@ -0,0 +1,53 @@ +// Copyright (C) 2024 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. +package { + default_team: "trendy_team_haptics_framework", + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_test { + name: "libvibrator_test", + test_suites: ["general-tests"], + defaults: [ + "aconfig_lib_cc_shared_link.defaults", + ], + srcs: [ + "ExternalVibrationTest.cpp", + "ExternalVibrationUtilsTest.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + static_libs: [ + "android.os.vibrator.flags-aconfig-cc", + "libflagtest", + "libgtest", + "liblog", + "libvibrator", + "libvibratorutils", + ], + shared_libs: [ + "libbase", + "libbinder", + "libutils", + "server_configurable_flags", + ], +} diff --git a/libs/vibrator/tests/ExternalVibrationTest.cpp b/libs/vibrator/tests/ExternalVibrationTest.cpp new file mode 100644 index 0000000000..4133836785 --- /dev/null +++ b/libs/vibrator/tests/ExternalVibrationTest.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2024 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. + */ + +#include <binder/Parcel.h> +#include <gtest/gtest.h> +#include <vibrator/ExternalVibration.h> + +using namespace android; +using namespace testing; + +using HapticLevel = os::HapticLevel; +using ScaleLevel = os::ExternalVibrationScale::ScaleLevel; + +class TestVibrationController : public os::IExternalVibrationController { +public: + explicit TestVibrationController() {} + IBinder *onAsBinder() override { return nullptr; } + binder::Status mute(/*out*/ bool *ret) override { + *ret = false; + return binder::Status::ok(); + }; + binder::Status unmute(/*out*/ bool *ret) override { + *ret = false; + return binder::Status::ok(); + }; +}; + +class ExternalVibrationTest : public Test { +protected: + HapticLevel toHapticLevel(ScaleLevel level) { + os::ExternalVibrationScale externalVibrationScale; + externalVibrationScale.scaleLevel = level; + os::HapticScale hapticScale = + os::ExternalVibration::externalVibrationScaleToHapticScale(externalVibrationScale); + return hapticScale.getLevel(); + } +}; + +TEST_F(ExternalVibrationTest, TestReadAndWriteToParcel) { + int32_t uid = 1; + std::string pkg("package.name"); + audio_attributes_t originalAttrs; + originalAttrs.content_type = AUDIO_CONTENT_TYPE_SONIFICATION; + originalAttrs.usage = AUDIO_USAGE_ASSISTANCE_SONIFICATION; + originalAttrs.source = AUDIO_SOURCE_VOICE_COMMUNICATION; + originalAttrs.flags = AUDIO_FLAG_BYPASS_MUTE; + + sp<TestVibrationController> vibrationController = new TestVibrationController(); + ASSERT_NE(vibrationController, nullptr); + + sp<os::ExternalVibration> original = + new os::ExternalVibration(uid, pkg, originalAttrs, vibrationController); + ASSERT_NE(original, nullptr); + + EXPECT_EQ(original->getUid(), uid); + EXPECT_EQ(original->getPackage(), pkg); + EXPECT_EQ(original->getAudioAttributes().content_type, originalAttrs.content_type); + EXPECT_EQ(original->getAudioAttributes().usage, originalAttrs.usage); + EXPECT_EQ(original->getAudioAttributes().source, originalAttrs.source); + EXPECT_EQ(original->getAudioAttributes().flags, originalAttrs.flags); + EXPECT_EQ(original->getController(), vibrationController); + + audio_attributes_t defaultAttrs; + defaultAttrs.content_type = AUDIO_CONTENT_TYPE_UNKNOWN; + defaultAttrs.usage = AUDIO_USAGE_UNKNOWN; + defaultAttrs.source = AUDIO_SOURCE_DEFAULT; + defaultAttrs.flags = AUDIO_FLAG_NONE; + + sp<os::ExternalVibration> parceled = + new os::ExternalVibration(0, std::string(""), defaultAttrs, nullptr); + ASSERT_NE(parceled, nullptr); + + Parcel parcel; + original->writeToParcel(&parcel); + parcel.setDataPosition(0); + parceled->readFromParcel(&parcel); + + EXPECT_EQ(parceled->getUid(), uid); + EXPECT_EQ(parceled->getPackage(), pkg); + EXPECT_EQ(parceled->getAudioAttributes().content_type, originalAttrs.content_type); + EXPECT_EQ(parceled->getAudioAttributes().usage, originalAttrs.usage); + EXPECT_EQ(parceled->getAudioAttributes().source, originalAttrs.source); + EXPECT_EQ(parceled->getAudioAttributes().flags, originalAttrs.flags); + // TestVibrationController does not implement onAsBinder, skip controller parcel in this test. +} + +TEST_F(ExternalVibrationTest, TestExternalVibrationScaleToHapticScale) { + os::ExternalVibrationScale externalVibrationScale; + externalVibrationScale.scaleLevel = ScaleLevel::SCALE_HIGH; + externalVibrationScale.scaleFactor = 0.5f; + externalVibrationScale.adaptiveHapticsScale = 0.8f; + + os::HapticScale hapticScale = + os::ExternalVibration::externalVibrationScaleToHapticScale(externalVibrationScale); + + // Check scale factors are forwarded. + EXPECT_EQ(hapticScale.getLevel(), HapticLevel::HIGH); + EXPECT_EQ(hapticScale.getScaleFactor(), 0.5f); + EXPECT_EQ(hapticScale.getAdaptiveScaleFactor(), 0.8f); + + // Check conversion for all levels. + EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_MUTE), HapticLevel::MUTE); + EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_VERY_LOW), HapticLevel::VERY_LOW); + EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_LOW), HapticLevel::LOW); + EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_NONE), HapticLevel::NONE); + EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_HIGH), HapticLevel::HIGH); + EXPECT_EQ(toHapticLevel(ScaleLevel::SCALE_VERY_HIGH), HapticLevel::VERY_HIGH); +} diff --git a/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp new file mode 100644 index 0000000000..7adc9c33de --- /dev/null +++ b/libs/vibrator/tests/ExternalVibrationUtilsTest.cpp @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2024 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. + */ + +#include <android_os_vibrator.h> +#include <flag_macros.h> +#include <gtest/gtest.h> +#include <vibrator/ExternalVibrationUtils.h> + +#include "test_utils.h" + +#define FLAG_NS android::os::vibrator + +using namespace android; +using namespace testing; + +using HapticScale = os::HapticScale; +using HapticLevel = os::HapticLevel; + +static constexpr float TEST_TOLERANCE = 1e-2f; +static constexpr size_t TEST_BUFFER_LENGTH = 4; +static float TEST_BUFFER[TEST_BUFFER_LENGTH] = { 1, -1, 0.5f, -0.2f }; + +class ExternalVibrationUtilsTest : public Test { +public: + void SetUp() override { + std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(mBuffer)); + } + +protected: + void scaleBuffer(HapticLevel hapticLevel) { + scaleBuffer(HapticScale(hapticLevel)); + } + + void scaleBuffer(HapticLevel hapticLevel, float adaptiveScaleFactor) { + scaleBuffer(hapticLevel, adaptiveScaleFactor, 0 /* limit */); + } + + void scaleBuffer(HapticLevel hapticLevel, float adaptiveScaleFactor, float limit) { + scaleBuffer(HapticScale(hapticLevel, -1 /* scaleFactor */, adaptiveScaleFactor), limit); + } + + void scaleBuffer(HapticScale hapticScale) { + scaleBuffer(hapticScale, 0 /* limit */); + } + + void scaleBuffer(HapticScale hapticScale, float limit) { + std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(mBuffer)); + os::scaleHapticData(&mBuffer[0], TEST_BUFFER_LENGTH, hapticScale, limit); + } + + float mBuffer[TEST_BUFFER_LENGTH]; +}; + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleMute, + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling), + ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + float expected[TEST_BUFFER_LENGTH]; + std::fill(std::begin(expected), std::end(expected), 0); + + scaleBuffer(HapticLevel::MUTE); + EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleMute, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)), + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + float expected[TEST_BUFFER_LENGTH]; + std::fill(std::begin(expected), std::end(expected), 0); + + scaleBuffer(HapticLevel::MUTE); + EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS( + ExternalVibrationUtilsTest, TestScaleV2Mute, + // Value of fix_audio_coupled_haptics_scaling is not important, should work with either + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + float expected[TEST_BUFFER_LENGTH]; + std::fill(std::begin(expected), std::end(expected), 0); + + scaleBuffer(HapticLevel::MUTE); + EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleNone, + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling), + ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + float expected[TEST_BUFFER_LENGTH]; + std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected)); + + scaleBuffer(HapticLevel::NONE); + EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleNone, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)), + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + float expected[TEST_BUFFER_LENGTH]; + std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected)); + + scaleBuffer(HapticLevel::NONE); + EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS( + ExternalVibrationUtilsTest, TestScaleV2None, + // Value of fix_audio_coupled_haptics_scaling is not important, should work with either + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + float expected[TEST_BUFFER_LENGTH]; + std::copy(std::begin(TEST_BUFFER), std::end(TEST_BUFFER), std::begin(expected)); + + scaleBuffer(HapticLevel::NONE); + EXPECT_FLOATS_NEARLY_EQ(expected, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLegacyScaleToHapticLevel, + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling), + ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.84f, -0.66f }; + scaleBuffer(HapticLevel::VERY_HIGH); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.7f, -0.44f }; + scaleBuffer(HapticLevel::HIGH); + EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedLow[TEST_BUFFER_LENGTH] = { 0.75f, -0.75f, 0.26f, -0.06f }; + scaleBuffer(HapticLevel::LOW); + EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.66f, -0.66f, 0.16f, -0.02f }; + scaleBuffer(HapticLevel::VERY_LOW); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestFixedScaleToHapticLevel, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)), + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.79f, -0.39f }; + scaleBuffer(HapticLevel::VERY_HIGH); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.62f, -0.27f }; + scaleBuffer(HapticLevel::HIGH); + EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedLow[TEST_BUFFER_LENGTH] = { 0.70f, -0.70f, 0.35f, -0.14f }; + scaleBuffer(HapticLevel::LOW); + EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.45f, -0.45f, 0.22f, -0.09f }; + scaleBuffer(HapticLevel::VERY_LOW); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS( + ExternalVibrationUtilsTest, TestScaleV2ToHapticLevel, + // Value of fix_audio_coupled_haptics_scaling is not important, should work with either + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.8f, -0.38f }; + scaleBuffer(HapticLevel::VERY_HIGH); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.63f, -0.27f }; + scaleBuffer(HapticLevel::HIGH); + EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedLow[TEST_BUFFER_LENGTH] = { 0.71f, -0.71f, 0.35f, -0.14f }; + scaleBuffer(HapticLevel::LOW); + EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.51f, -0.51f, 0.25f, -0.1f }; + scaleBuffer(HapticLevel::VERY_LOW); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS( + ExternalVibrationUtilsTest, TestScaleV2ToScaleFactorIgnoresLevel, + // Value of fix_audio_coupled_haptics_scaling is not important, should work with either + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + constexpr float adaptiveScaleNone = 1.0f; + + float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 1, -1, 1, -0.55f }; + scaleBuffer(HapticScale(HapticLevel::LOW, 3.0f /* scaleFactor */, adaptiveScaleNone)); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedHigh[TEST_BUFFER_LENGTH] = { 1, -1, 0.66f, -0.29f }; + scaleBuffer(HapticScale(HapticLevel::LOW, 1.5f /* scaleFactor */, adaptiveScaleNone)); + EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedLow[TEST_BUFFER_LENGTH] = { 0.8f, -0.8f, 0.4f, -0.16f }; + scaleBuffer(HapticScale(HapticLevel::HIGH, 0.8f /* scaleFactor */, adaptiveScaleNone)); + EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.4f, -0.4f, 0.2f, -0.08f }; + scaleBuffer(HapticScale(HapticLevel::HIGH, 0.4f /* scaleFactor */, adaptiveScaleNone)); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterLegacyScale, + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling), + ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + // Haptic level scale up then adaptive scale down + float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.16f, -0.13f }; + scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Haptic level scale up then adaptive scale up + float expectedHigh[TEST_BUFFER_LENGTH] = { 1.5f, -1.5f, 1.06f, -0.67f }; + scaleBuffer(HapticLevel::HIGH, 1.5f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Haptic level scale down then adaptive scale down + float expectedLow[TEST_BUFFER_LENGTH] = { 0.45f, -0.45f, 0.15f, -0.04f }; + scaleBuffer(HapticLevel::LOW, 0.6f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Haptic level scale down then adaptive scale up + float expectedVeryLow[TEST_BUFFER_LENGTH] = { 1.33f, -1.33f, 0.33f, -0.05f }; + scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterFixedScale, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)), + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + // Haptic level scale up then adaptive scale down + float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.16f, -0.07f }; + scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Haptic level scale up then adaptive scale up + float expectedHigh[TEST_BUFFER_LENGTH] = { 1.5f, -1.5f, 0.93f, -0.41f }; + scaleBuffer(HapticLevel::HIGH, 1.5f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Haptic level scale down then adaptive scale down + float expectedLow[TEST_BUFFER_LENGTH] = { 0.42f, -0.42f, 0.21f, -0.08f }; + scaleBuffer(HapticLevel::LOW, 0.6f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Haptic level scale down then adaptive scale up + float expectedVeryLow[TEST_BUFFER_LENGTH] = { 0.91f, -0.91f, 0.45f, -0.18f }; + scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS( + ExternalVibrationUtilsTest, TestAdaptiveScaleFactorAppliedAfterScaleV2, + // Value of fix_audio_coupled_haptics_scaling is not important, should work with either + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + // Haptic level scale up then adaptive scale down + float expectedVeryHigh[TEST_BUFFER_LENGTH] = { 0.2, -0.2, 0.15f, -0.07f }; + scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Haptic level scale up then adaptive scale up + float expectedHigh[TEST_BUFFER_LENGTH] = { 1.5f, -1.5f, 0.95f, -0.41f }; + scaleBuffer(HapticLevel::HIGH, 1.5f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Haptic level scale down then adaptive scale down + float expectedLow[TEST_BUFFER_LENGTH] = { 0.42f, -0.42f, 0.21f, -0.08f }; + scaleBuffer(HapticLevel::LOW, 0.6f /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Haptic level scale down then adaptive scale up + float expectedVeryLow[TEST_BUFFER_LENGTH] = { 1.02f, -1.02f, 0.51f, -0.2f }; + scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */); + EXPECT_FLOATS_NEARLY_EQ(expectedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLimitAppliedAfterLegacyScale, + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling), + ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + // Scaled = { 0.2, -0.2, 0.16f, -0.13f }; + float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.13f }; + scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */); + EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Scaled = { 1, -1, 0.5f, -0.2f }; + float expectedClippedVeryLow[TEST_BUFFER_LENGTH] = { 0.7f, -0.7f, 0.33f, -0.05f }; + scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */, 0.7f /* limit */); + EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS(ExternalVibrationUtilsTest, TestLimitAppliedAfterFixedScale, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, fix_audio_coupled_haptics_scaling)), + REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + // Scaled = { 0.2, -0.2, 0.16f, -0.13f }; + float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.07f }; + scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */); + EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Scaled = { 1, -1, 0.5f, -0.2f }; + float expectedClippedVeryLow[TEST_BUFFER_LENGTH] = { 0.7f, -0.7f, 0.45f, -0.18f }; + scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */, 0.7f /* limit */); + EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} + +TEST_F_WITH_FLAGS( + ExternalVibrationUtilsTest, TestLimitAppliedAfterScaleV2, + // Value of fix_audio_coupled_haptics_scaling is not important, should work with either + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(FLAG_NS, haptics_scale_v2_enabled))) { + // Scaled = { 0.2, -0.2, 0.15f, -0.07f }; + float expectedClippedVeryHigh[TEST_BUFFER_LENGTH] = { 0.15f, -0.15f, 0.15f, -0.07f }; + scaleBuffer(HapticLevel::VERY_HIGH, 0.2f /* adaptiveScaleFactor */, 0.15f /* limit */); + EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryHigh, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); + + // Scaled = { 1.02f, -1.02f, 0.51f, -0.2f } + float expectedClippedVeryLow[TEST_BUFFER_LENGTH] = { 0.7f, -0.7f, 0.51f, -0.2f }; + scaleBuffer(HapticLevel::VERY_LOW, 2 /* adaptiveScaleFactor */, 0.7f /* limit */); + EXPECT_FLOATS_NEARLY_EQ(expectedClippedVeryLow, mBuffer, TEST_BUFFER_LENGTH, TEST_TOLERANCE); +} diff --git a/libs/vibrator/tests/test_utils.h b/libs/vibrator/tests/test_utils.h new file mode 100644 index 0000000000..f491ea1f49 --- /dev/null +++ b/libs/vibrator/tests/test_utils.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 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. + */ + +#ifndef LIBVIBRATOR_TEST_UTILS_H +#define LIBVIBRATOR_TEST_UTILS_H + +#include <gtest/gtest.h> + +#if !defined(EXPECT_FLOATS_NEARLY_EQ) +#define EXPECT_FLOATS_NEARLY_EQ(expected, actual, length, epsilon) \ + for (size_t i = 0; i < length; i++) { \ + EXPECT_NEAR(expected[i], actual[i], epsilon) << " at Index: " << i; \ + } +#else +#error Macro EXPECT_FLOATS_NEARLY_EQ already defined +#endif + +#endif //LIBVIBRATOR_TEST_UTILS_H diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index ec7b190c74..bf0e38e986 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -270,7 +270,7 @@ void* Loader::open(egl_connection_t* cnx) { hnd = attempt_to_load_updated_driver(cnx); // If updated driver apk is set but fail to load, abort here. - LOG_ALWAYS_FATAL_IF(android::GraphicsEnv::getInstance().getDriverNamespace(), + LOG_ALWAYS_FATAL_IF(android::GraphicsEnv::getInstance().getDriverNamespace() && !hnd, "couldn't find an OpenGL ES implementation from %s", android::GraphicsEnv::getInstance().getDriverPath().c_str()); } diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp index 37db05cb20..6e35041662 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -916,42 +916,72 @@ EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config, EGLContext sha egl_context_t* const c = get_context(share_list); share_list = c->context; } + + bool skip_telemetry = false; + + auto findAttribute = [](const EGLint* attrib_ptr, GLint attribute, GLint* value) { + while (attrib_ptr && *attrib_ptr != EGL_NONE) { + GLint attr = *attrib_ptr++; + GLint val = *attrib_ptr++; + if (attr == attribute) { + if (value) { + *value = val; + } + return true; + } + } + return false; + }; + + std::vector<EGLint> replacement_attrib_list; + GLint telemetry_value; + if (findAttribute(attrib_list, EGL_TELEMETRY_HINT_ANDROID, &telemetry_value)) { + skip_telemetry = (telemetry_value == android::GpuStatsInfo::SKIP_TELEMETRY); + + // We need to remove EGL_TELEMETRY_HINT_ANDROID or the underlying drivers will + // complain about an unexpected attribute + const EGLint* attrib_ptr = attrib_list; + while (attrib_ptr && *attrib_ptr != EGL_NONE) { + GLint attr = *attrib_ptr++; + GLint val = *attrib_ptr++; + if (attr != EGL_TELEMETRY_HINT_ANDROID) { + replacement_attrib_list.push_back(attr); + replacement_attrib_list.push_back(val); + } + } + replacement_attrib_list.push_back(EGL_NONE); + attrib_list = replacement_attrib_list.data(); + } // b/111083885 - If we are presenting EGL 1.4 interface to apps // error out on robust access attributes that are invalid // in EGL 1.4 as the driver may be fine with them but dEQP expects // tests to fail according to spec. if (attrib_list && (cnx->driverVersion < EGL_MAKE_VERSION(1, 5, 0))) { - const EGLint* attrib_ptr = attrib_list; - while (*attrib_ptr != EGL_NONE) { - GLint attr = *attrib_ptr++; - GLint value = *attrib_ptr++; - if (attr == EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR) { - // We are GL ES context with EGL 1.4, this is an invalid - // attribute - return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT); - } - }; + if (findAttribute(attrib_list, EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + nullptr)) { + // We are GL ES context with EGL 1.4, this is an invalid attribute + return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT); + } } EGLContext context = cnx->egl.eglCreateContext(dp->disp.dpy, config, share_list, attrib_list); if (context != EGL_NO_CONTEXT) { // figure out if it's a GLESv1 or GLESv2 int version = egl_connection_t::GLESv1_INDEX; - if (attrib_list) { - while (*attrib_list != EGL_NONE) { - GLint attr = *attrib_list++; - GLint value = *attrib_list++; - if (attr == EGL_CONTEXT_CLIENT_VERSION && (value == 2 || value == 3)) { - version = egl_connection_t::GLESv2_INDEX; - } - }; + GLint version_value; + if (findAttribute(attrib_list, EGL_CONTEXT_CLIENT_VERSION, &version_value)) { + if (version_value == 2 || version_value == 3) { + version = egl_connection_t::GLESv2_INDEX; + } } if (version == egl_connection_t::GLESv1_INDEX) { android::GraphicsEnv::getInstance().setTargetStats( android::GpuStatsInfo::Stats::GLES_1_IN_USE); } - android::GraphicsEnv::getInstance().setTargetStats( - android::GpuStatsInfo::Stats::CREATED_GLES_CONTEXT); + if (!skip_telemetry) { + android::GraphicsEnv::getInstance().setTargetStats( + android::GpuStatsInfo::Stats::CREATED_GLES_CONTEXT); + } egl_context_t* c = new egl_context_t(dpy, context, config, cnx, version); return c; } diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 70801dccb0..cb220abd80 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -172,9 +172,8 @@ cc_library_shared { export_static_lib_headers: [ "libinputdispatcher", ], - export_include_dirs: [ - ".", - "include", + export_shared_lib_headers: [ + "libinputflinger_base", ], } @@ -185,7 +184,16 @@ cc_library_shared { cc_library_headers { name: "libinputflinger_headers", host_supported: true, - export_include_dirs: ["include"], + export_include_dirs: [ + "include", + ".", + ], + header_libs: [ + "libchrome-gestures_headers", + ], + export_header_lib_headers: [ + "libchrome-gestures_headers", + ], } filegroup { diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING index 018de5dd07..af9d2ebeda 100644 --- a/services/inputflinger/TEST_MAPPING +++ b/services/inputflinger/TEST_MAPPING @@ -293,6 +293,9 @@ }, { "name": "monkey_test" + }, + { + "name": "CtsInputRootTestCases" } ], "staged-platinum-postsubmit": [ diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index af4a04d9eb..0384257c35 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -693,7 +693,8 @@ std::optional<nsecs_t> getDownTime(const EventEntry& eventEntry) { */ std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState, const TouchState& newTouchState, - const MotionEntry& entry) { + const MotionEntry& entry, + std::function<void()> dump) { const int32_t maskedAction = MotionEvent::getActionMasked(entry.action); if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) { @@ -741,6 +742,7 @@ std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState, // crashing the device with FATAL. severity = android::base::LogSeverity::ERROR; } + dump(); LOG(severity) << "Expected ACTION_HOVER_MOVE instead of " << entry.getDescription(); } touchedWindow.dispatchMode = InputTarget::DispatchMode::AS_IS; @@ -2683,7 +2685,7 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio // Check if the wallpaper window should deliver the corresponding event. slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle, - tempTouchState, entry.deviceId, pointer, targets); + tempTouchState, entry, targets); tempTouchState.removeTouchingPointerFromWindow(entry.deviceId, pointer.id, oldTouchedWindowHandle); } @@ -2710,7 +2712,8 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio // Update dispatching for hover enter and exit. { std::vector<TouchedWindow> hoveringWindows = - getHoveringWindowsLocked(oldState, tempTouchState, entry); + getHoveringWindowsLocked(oldState, tempTouchState, entry, + [this]() REQUIRES(mLock) { logDispatchStateLocked(); }); // Hardcode to single hovering pointer for now. std::bitset<MAX_POINTER_ID + 1> pointerIds; pointerIds.set(entry.pointerProperties[0].id); @@ -4879,6 +4882,10 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev logDispatchStateLocked(); LOG(ERROR) << "Inconsistent event: " << motionEvent << ", reason: " << result.error(); + if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) { + mLock.unlock(); + return InputEventInjectionResult::FAILED; + } } } @@ -7096,9 +7103,11 @@ void InputDispatcher::setMonitorDispatchingTimeoutForTest(std::chrono::nanosecon void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags, const sp<WindowInfoHandle>& oldWindowHandle, const sp<WindowInfoHandle>& newWindowHandle, - TouchState& state, DeviceId deviceId, - const PointerProperties& pointerProperties, + TouchState& state, const MotionEntry& entry, std::vector<InputTarget>& targets) const { + LOG_IF(FATAL, entry.getPointerCount() != 1) << "Entry not eligible for slip: " << entry; + const DeviceId deviceId = entry.deviceId; + const PointerProperties& pointerProperties = entry.pointerProperties[0]; std::vector<PointerProperties> pointers{pointerProperties}; const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); @@ -7125,7 +7134,7 @@ void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFl state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::SLIPPERY_ENTER, InputTarget::Flags::WINDOW_IS_OBSCURED | InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED, - deviceId, pointers); + deviceId, pointers, entry.eventTime); } } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 698bdba458..87dfd1d8a4 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -709,11 +709,23 @@ private: sp<InputReporterInterface> mReporter; + /** + * Slip the wallpaper touch if necessary. + * + * @param targetFlags the target flags + * @param oldWindowHandle the old window that the touch slipped out of + * @param newWindowHandle the new window that the touch is slipping into + * @param state the current touch state. This will be updated if necessary to reflect the new + * windows that are receiving touch. + * @param deviceId the device id of the current motion being processed + * @param pointerProperties the pointer properties of the current motion being processed + * @param targets the current targets to add the walpaper ones to + * @param eventTime the new downTime for the wallpaper target + */ void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags, const sp<android::gui::WindowInfoHandle>& oldWindowHandle, const sp<android::gui::WindowInfoHandle>& newWindowHandle, - TouchState& state, DeviceId deviceId, - const PointerProperties& pointerProperties, + TouchState& state, const MotionEntry& entry, std::vector<InputTarget>& targets) const REQUIRES(mLock); void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags, ftl::Flags<InputTarget::Flags> newTargetFlags, diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index 9d4bb3d943..5a70dd5e2c 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -47,7 +47,7 @@ struct TouchState { const sp<android::gui::WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers, - std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt); + std::optional<nsecs_t> firstDownTimeInTarget); void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle, DeviceId deviceId, const PointerProperties& pointer); void removeHoveringPointer(DeviceId deviceId, int32_t pointerId); diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h index cb525a4a92..96e619c10d 100644 --- a/services/inputflinger/dispatcher/trace/InputTracer.h +++ b/services/inputflinger/dispatcher/trace/InputTracer.h @@ -65,10 +65,10 @@ private: void onEventProcessingComplete(nsecs_t processingTimestamp); InputTracer& tracer; - std::vector<const TracedEvent> events; + std::vector<TracedEvent> events; bool isEventProcessingComplete{false}; // A queue to hold dispatch args from being traced until event processing is complete. - std::vector<const WindowDispatchArgs> pendingDispatchArgs; + std::vector<WindowDispatchArgs> pendingDispatchArgs; // The metadata should not be modified after event processing is complete. TracedEventMetadata metadata{}; }; diff --git a/services/inputflinger/include/TouchpadHardwareState.h b/services/inputflinger/include/TouchpadHardwareState.h new file mode 100644 index 0000000000..a8dd44c82e --- /dev/null +++ b/services/inputflinger/include/TouchpadHardwareState.h @@ -0,0 +1,32 @@ +/* + * Copyright 2024 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 <include/gestures.h> +#include <vector> + +namespace android { + +// A Gestures library HardwareState struct (from libchrome-gestures), but bundled +// with a vector to contain its FingerStates, so you don't have to worry about where +// that memory is allocated. +struct SelfContainedHardwareState { + HardwareState state; + std::vector<FingerState> fingers; +}; + +} // namespace android diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp index 7cc8940379..4cd37d7e4a 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp @@ -33,7 +33,7 @@ uint32_t ExternalStylusInputMapper::getSources() const { void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo& info) { InputMapper::populateDeviceInfo(info); - if (mRawPressureAxis) { + if (mRawPressureAxis || mTouchButtonAccumulator.hasButtonTouch()) { info.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); } diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 984e217efc..5c90cbb6ce 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -149,7 +149,10 @@ uint32_t TouchInputMapper::getSources() const { // The SOURCE_BLUETOOTH_STYLUS is added to events dynamically if the current stream is modified // by the external stylus state. That's why we don't add it directly to mSource during // configuration. - return mSource | (hasExternalStylus() ? AINPUT_SOURCE_BLUETOOTH_STYLUS : 0); + return mSource | + (mExternalStylusPresence == ExternalStylusPresence::TOUCH_FUSION + ? AINPUT_SOURCE_BLUETOOTH_STYLUS + : 0); } void TouchInputMapper::populateDeviceInfo(InputDeviceInfo& info) { @@ -270,8 +273,8 @@ void TouchInputMapper::dump(std::string& dump) { } dump += INDENT3 "Stylus Fusion:\n"; - dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n", - toString(mExternalStylusConnected)); + dump += StringPrintf(INDENT4 "ExternalStylusPresence: %s\n", + ftl::enum_string(mExternalStylusPresence).c_str()); dump += StringPrintf(INDENT4 "Fused External Stylus Pointer ID: %s\n", toString(mFusedStylusPointerId).c_str()); dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n", @@ -356,11 +359,19 @@ std::list<NotifyArgs> TouchInputMapper::reconfigure(nsecs_t when, void TouchInputMapper::resolveExternalStylusPresence() { std::vector<InputDeviceInfo> devices; getContext()->getExternalStylusDevices(devices); - mExternalStylusConnected = !devices.empty(); - - if (!mExternalStylusConnected) { + if (devices.empty()) { + mExternalStylusPresence = ExternalStylusPresence::NONE; resetExternalStylus(); + return; } + mExternalStylusPresence = + std::any_of(devices.begin(), devices.end(), + [](const auto& info) { + return info.getMotionRange(AMOTION_EVENT_AXIS_PRESSURE, + AINPUT_SOURCE_STYLUS) != nullptr; + }) + ? ExternalStylusPresence::TOUCH_FUSION + : ExternalStylusPresence::BUTTON_FUSION; } TouchInputMapper::Parameters TouchInputMapper::computeParameters( @@ -520,7 +531,7 @@ void TouchInputMapper::dumpRawPointerAxes(std::string& dump) { } bool TouchInputMapper::hasExternalStylus() const { - return mExternalStylusConnected; + return mExternalStylusPresence != ExternalStylusPresence::NONE; } /** diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 87b72afe7c..ef0e02f40c 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -365,6 +365,16 @@ protected: RawState mLastRawState; CookedState mLastCookedState; + enum class ExternalStylusPresence { + // No external stylus connected. + NONE, + // An external stylus that can report touch/pressure that can be fused with the touchscreen. + TOUCH_FUSION, + // An external stylus that can only report buttons. + BUTTON_FUSION, + ftl_last = BUTTON_FUSION, + }; + ExternalStylusPresence mExternalStylusPresence{ExternalStylusPresence::NONE}; // State provided by an external stylus StylusState mExternalStylusState; // If an external stylus is capable of reporting pointer-specific data like pressure, we will @@ -460,8 +470,6 @@ private: float mTiltYCenter; float mTiltYScale; - bool mExternalStylusConnected; - // Oriented motion ranges for input device info. struct OrientedRanges { InputDeviceInfo::MotionRange x; diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h index 07e62c6eba..66d62f856b 100644 --- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h +++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h @@ -26,18 +26,12 @@ #include "accumulator/CursorButtonAccumulator.h" #include "accumulator/MultiTouchMotionAccumulator.h" #include "accumulator/TouchButtonAccumulator.h" +#include "include/TouchpadHardwareState.h" #include "include/gestures.h" namespace android { -// A HardwareState struct, but bundled with a vector to contain its FingerStates, so you don't have -// to worry about where that memory is allocated. -struct SelfContainedHardwareState { - HardwareState state; - std::vector<FingerState> fingers; -}; - // Converts RawEvents into the HardwareState structs used by the gestures library. class HardwareStateConverter { public: diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index e5058507eb..c2f174f6b4 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -5037,6 +5037,54 @@ TEST_F_WITH_FLAGS(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash, } /** + * Invalid events injected by input filter are rejected. + */ +TEST_F(InputDispatcherTest, InvalidA11yEventsGetRejected) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window", + ui::LogicalDisplayId::DEFAULT); + + mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application); + + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + + // a11y sets 'POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY' policy flag during injection, so define + // a custom injection function here for convenience. + auto injectFromAccessibility = [&](int32_t action, float x, float y) { + MotionEvent event = MotionEventBuilder(action, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(x).y(y)) + .addFlag(AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT) + .build(); + return injectMotionEvent(*mDispatcher, event, 100ms, + InputEventInjectionSync::WAIT_FOR_RESULT, /*targetUid=*/{}, + POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_FILTERED | + POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY); + }; + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectFromAccessibility(ACTION_DOWN, /*x=*/300, /*y=*/400)); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectFromAccessibility(ACTION_MOVE, /*x=*/310, /*y=*/420)); + window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + window->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + // finger is still down, so a new DOWN event should be rejected! + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectFromAccessibility(ACTION_DOWN, /*x=*/340, /*y=*/410)); + + // if the gesture is correctly finished, new down event will succeed + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectFromAccessibility(ACTION_MOVE, /*x=*/320, /*y=*/430)); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectFromAccessibility(ACTION_UP, /*x=*/320, /*y=*/430)); + window->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + window->consumeMotionEvent(WithMotionAction(ACTION_UP)); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectFromAccessibility(ACTION_DOWN, /*x=*/350, /*y=*/460)); + window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); +} + +/** * If mouse is hovering when the touch goes down, the hovering should be stopped via HOVER_EXIT. */ TEST_F(InputDispatcherTest, TouchDownAfterMouseHover_legacy) { @@ -5663,6 +5711,273 @@ TEST_F(InputDispatcherTest, SplittableAndNonSplittableWindows) { } /** + * Three windows: + * 1) A window on the left, with flag dup_to_wallpaper + * 2) A window on the right, with flag slippery + * 3) A wallpaper window under the left window + * When touch slips from right window to left, the wallpaper should receive a similar slippery + * enter event. Later on, when another device becomes active, the wallpaper should receive + * consistent streams from the new device, and also from the old device. + * This test attempts to reproduce a crash in the dispatcher where the wallpaper target's downTime + * was not getting set during slippery entrance. + */ +TEST_F(InputDispatcherTest, WallpaperWindowWhenSlipperyAndMultiWindowMultiTouch) { + SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true); + std::shared_ptr<FakeApplicationHandle> application1 = std::make_shared<FakeApplicationHandle>(); + std::shared_ptr<FakeApplicationHandle> application2 = std::make_shared<FakeApplicationHandle>(); + std::shared_ptr<FakeApplicationHandle> application3 = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> wallpaper = + sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper", + ui::LogicalDisplayId::DEFAULT); + wallpaper->setIsWallpaper(true); + wallpaper->setPreventSplitting(true); + wallpaper->setTouchable(false); + + sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left", + ui::LogicalDisplayId::DEFAULT); + leftWindow->setTouchableRegion(Region{{0, 0, 100, 100}}); + leftWindow->setDupTouchToWallpaper(true); + + sp<FakeWindowHandle> rightWindow = + sp<FakeWindowHandle>::make(application3, mDispatcher, "Right", + ui::LogicalDisplayId::DEFAULT); + rightWindow->setTouchableRegion(Region{{100, 0, 200, 100}}); + rightWindow->setSlippery(true); + rightWindow->setWatchOutsideTouch(true); + rightWindow->setTrustedOverlay(true); + + mDispatcher->onWindowInfosChanged( + {{*rightWindow->getInfo(), *leftWindow->getInfo(), *wallpaper->getInfo()}, {}, 0, 0}); + + const DeviceId deviceA = 3; + const DeviceId deviceB = 9; + + // First finger from device A into right window + NotifyMotionArgs deviceADownArgs = + MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50)) + .deviceId(deviceA) + .build(); + + mDispatcher->notifyMotion(deviceADownArgs); + rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Move the finger of device A from right window into left window. It should slip. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(80).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + + leftWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + rightWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + wallpaper->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Finger from device B down into left window + NotifyMotionArgs deviceBDownArgs = + MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40)) + .deviceId(deviceB) + .build(); + mDispatcher->notifyMotion(deviceBDownArgs); + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN))); + + rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_OUTSIDE))); + + // Move finger from device B, still keeping it in the left window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50)) + .deviceId(deviceB) + .downTime(deviceBDownArgs.downTime) + .build()); + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE))); + + // Lift the finger from device B + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50)) + .deviceId(deviceB) + .downTime(deviceBDownArgs.downTime) + .build()); + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP))); + + // Move the finger of device A, keeping it in the left window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE))); + + // Second finger down from device A, into the right window. It should be split into: + // MOVE for the left window (due to existing implementation) + a DOWN into the right window + // Wallpaper will not receive this new pointer, and it will only get the MOVE event. + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + auto firstFingerMoveFromDeviceA = AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_MOVE), + WithPointerCount(1), WithPointerId(0, 0)); + leftWindow->consumeMotionEvent(firstFingerMoveFromDeviceA); + wallpaper->consumeMotionEvent(firstFingerMoveFromDeviceA); + rightWindow->consumeMotionEvent( + AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_DOWN), WithPointerId(0, 1))); + + // Lift up the second finger. + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + + rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP))); + leftWindow->consumeMotionEvent(firstFingerMoveFromDeviceA); + wallpaper->consumeMotionEvent(firstFingerMoveFromDeviceA); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP))); + rightWindow->assertNoEvents(); +} + +/** + * Same test as above, but with enable_multi_device_same_window_stream flag set to false. + */ +TEST_F(InputDispatcherTest, WallpaperWindowWhenSlipperyAndMultiWindowMultiTouch_legacy) { + SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false); + std::shared_ptr<FakeApplicationHandle> application1 = std::make_shared<FakeApplicationHandle>(); + std::shared_ptr<FakeApplicationHandle> application2 = std::make_shared<FakeApplicationHandle>(); + std::shared_ptr<FakeApplicationHandle> application3 = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> wallpaper = + sp<FakeWindowHandle>::make(application1, mDispatcher, "wallpaper", + ui::LogicalDisplayId::DEFAULT); + wallpaper->setIsWallpaper(true); + wallpaper->setPreventSplitting(true); + wallpaper->setTouchable(false); + + sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application2, mDispatcher, "Left", + ui::LogicalDisplayId::DEFAULT); + leftWindow->setTouchableRegion(Region{{0, 0, 100, 100}}); + leftWindow->setDupTouchToWallpaper(true); + + sp<FakeWindowHandle> rightWindow = + sp<FakeWindowHandle>::make(application3, mDispatcher, "Right", + ui::LogicalDisplayId::DEFAULT); + rightWindow->setTouchableRegion(Region{{100, 0, 200, 100}}); + rightWindow->setSlippery(true); + rightWindow->setWatchOutsideTouch(true); + rightWindow->setTrustedOverlay(true); + + mDispatcher->onWindowInfosChanged( + {{*rightWindow->getInfo(), *leftWindow->getInfo(), *wallpaper->getInfo()}, {}, 0, 0}); + + const DeviceId deviceA = 3; + const DeviceId deviceB = 9; + + // First finger from device A into right window + NotifyMotionArgs deviceADownArgs = + MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50)) + .deviceId(deviceA) + .build(); + + mDispatcher->notifyMotion(deviceADownArgs); + rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Move the finger of device A from right window into left window. It should slip. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(80).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + + leftWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + rightWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + wallpaper->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Finger from device B down into left window + NotifyMotionArgs deviceBDownArgs = + MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(40)) + .deviceId(deviceB) + .build(); + mDispatcher->notifyMotion(deviceBDownArgs); + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_CANCEL))); + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_CANCEL))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_DOWN))); + + rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_OUTSIDE))); + + // Move finger from device B, still keeping it in the left window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50)) + .deviceId(deviceB) + .downTime(deviceBDownArgs.downTime) + .build()); + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_MOVE))); + + // Lift the finger from device B + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(40).y(50)) + .deviceId(deviceB) + .downTime(deviceBDownArgs.downTime) + .build()); + leftWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP))); + wallpaper->consumeMotionEvent(AllOf(WithDeviceId(deviceB), WithMotionAction(ACTION_UP))); + + // Move the finger of device A, keeping it in the left window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + // This device was already canceled, so MOVE events will not be arriving to the windows from it. + + // Second finger down from device A, into the right window. It should be split into: + // MOVE for the left window (due to existing implementation) + a DOWN into the right window + // Wallpaper will not receive this new pointer, and it will only get the MOVE event. + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + rightWindow->consumeMotionEvent( + AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_DOWN), WithPointerId(0, 1))); + + // Lift up the second finger. + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(140).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + + rightWindow->consumeMotionEvent(AllOf(WithDeviceId(deviceA), WithMotionAction(ACTION_UP))); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(70).y(50)) + .deviceId(deviceA) + .downTime(deviceADownArgs.downTime) + .build()); + rightWindow->assertNoEvents(); +} + +/** * Two windows: left and right. The left window has PREVENT_SPLITTING input config. Device A sends a * down event to the right window. Device B sends a down event to the left window, and then a * POINTER_DOWN event to the right window. However, since the left window prevents splitting, the diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 48fd717631..4a9e89319d 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -6675,15 +6675,27 @@ INSTANTIATE_TEST_SUITE_P(TouchscreenPrecisionTests, TouchscreenPrecisionTestsFix class ExternalStylusFusionTest : public SingleTouchInputMapperTest { public: - SingleTouchInputMapper& initializeInputMapperWithExternalStylus() { + void SetUp() override { + SingleTouchInputMapperTest::SetUp(); + mExternalStylusDeviceInfo = {}; + mStylusState = {}; + } + + SingleTouchInputMapper& initializeInputMapperWithExternalStylus(bool supportsPressure = true) { addConfigurationProperty("touch.deviceType", "touchScreen"); prepareDisplay(ui::ROTATION_0); prepareButtons(); prepareAxes(POSITION); auto& mapper = constructAndAddMapper<SingleTouchInputMapper>(); + if (supportsPressure) { + mExternalStylusDeviceInfo.addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, + AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f); + mStylusState.pressure = 0.f; + } + mStylusState.when = ARBITRARY_TIME; - mStylusState.pressure = 0.f; mStylusState.toolType = ToolType::STYLUS; mReader->getContext()->setExternalStylusDevices({mExternalStylusDeviceInfo}); configureDevice(InputReaderConfiguration::Change::EXTERNAL_STYLUS_PRESENCE); @@ -6791,11 +6803,17 @@ private: InputDeviceInfo mExternalStylusDeviceInfo{}; }; -TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSource) { +TEST_F(ExternalStylusFusionTest, UsesBluetoothStylusSourceWithPressure) { SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); ASSERT_EQ(STYLUS_FUSION_SOURCE, mapper.getSources()); } +TEST_F(ExternalStylusFusionTest, DoesNotUseBluetoothStylusSourceWithoutPressure) { + SingleTouchInputMapper& mapper = + initializeInputMapperWithExternalStylus(/*supportsPressure=*/false); + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources()); +} + TEST_F(ExternalStylusFusionTest, UnsuccessfulFusion) { SingleTouchInputMapper& mapper = initializeInputMapperWithExternalStylus(); ASSERT_NO_FATAL_FAILURE(testUnsuccessfulFusionGesture(mapper)); diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp index 555b80aed3..33724a93b5 100644 --- a/services/sensorservice/SensorDirectConnection.cpp +++ b/services/sensorservice/SensorDirectConnection.cpp @@ -26,12 +26,17 @@ namespace android { using util::ProtoOutputStream; -SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorService>& service, - uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle, - const String16& opPackageName, int deviceId) - : mService(service), mUid(uid), mMem(*mem), +SensorService::SensorDirectConnection::SensorDirectConnection( + const sp<SensorService>& service, uid_t uid, pid_t pid, const sensors_direct_mem_t* mem, + int32_t halChannelHandle, const String16& opPackageName, int deviceId) + : mService(service), + mUid(uid), + mPid(pid), + mMem(*mem), mHalChannelHandle(halChannelHandle), - mOpPackageName(opPackageName), mDeviceId(deviceId), mDestroyed(false) { + mOpPackageName(opPackageName), + mDeviceId(deviceId), + mDestroyed(false) { mUserId = multiuser_get_user_id(mUid); ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection"); } @@ -62,10 +67,21 @@ void SensorService::SensorDirectConnection::onFirstRef() { void SensorService::SensorDirectConnection::dump(String8& result) const { Mutex::Autolock _l(mConnectionLock); - result.appendFormat("\tPackage %s, HAL channel handle %d, total sensor activated %zu\n", - String8(mOpPackageName).c_str(), getHalChannelHandle(), mActivated.size()); - for (auto &i : mActivated) { - result.appendFormat("\t\tSensor %#08x, rate %d\n", i.first, i.second); + result.appendFormat("\t%s | HAL channel handle %d | uid %d | pid %d\n", + String8(mOpPackageName).c_str(), getHalChannelHandle(), mUid, mPid); + result.appendFormat("\tActivated sensor count: %zu\n", mActivated.size()); + dumpSensorInfoWithLock(result, mActivated); + + result.appendFormat("\tBackup sensor (opened but UID idle) count: %zu\n", + mActivatedBackup.size()); + dumpSensorInfoWithLock(result, mActivatedBackup); +} + +void SensorService::SensorDirectConnection::dumpSensorInfoWithLock( + String8& result, std::unordered_map<int, int> sensors) const { + for (auto& i : sensors) { + result.appendFormat("\t\t%s 0x%08x | rate %d\n", mService->getSensorName(i.first).c_str(), + i.first, i.second); } } diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h index bfaf811330..9f21731fa9 100644 --- a/services/sensorservice/SensorDirectConnection.h +++ b/services/sensorservice/SensorDirectConnection.h @@ -17,9 +17,10 @@ #ifndef ANDROID_SENSOR_DIRECT_CONNECTION_H #define ANDROID_SENSOR_DIRECT_CONNECTION_H -#include <optional> +#include <android-base/thread_annotations.h> #include <stdint.h> #include <sys/types.h> +#include <optional> #include <binder/BinderService.h> @@ -37,15 +38,15 @@ class BitTube; class SensorService::SensorDirectConnection: public BnSensorEventConnection { public: - SensorDirectConnection(const sp<SensorService>& service, uid_t uid, - const sensors_direct_mem_t *mem, int32_t halChannelHandle, - const String16& opPackageName, int deviceId); + SensorDirectConnection(const sp<SensorService>& service, uid_t uid, pid_t pid, + const sensors_direct_mem_t* mem, int32_t halChannelHandle, + const String16& opPackageName, int deviceId); void dump(String8& result) const; void dump(util::ProtoOutputStream* proto) const; uid_t getUid() const { return mUid; } const String16& getOpPackageName() const { return mOpPackageName; } int32_t getHalChannelHandle() const; - bool isEquivalent(const sensors_direct_mem_t *mem) const; + bool isEquivalent(const sensors_direct_mem_t* mem) const; // Invoked when access to sensors for this connection has changed, e.g. lost or // regained due to changes in the sensor restricted/privacy mode or the @@ -94,8 +95,13 @@ private: // Recover sensor requests previously capped by capRates(). void uncapRates(); + // Dumps a set of sensor infos. + void dumpSensorInfoWithLock(String8& result, std::unordered_map<int, int> sensors) const + EXCLUSIVE_LOCKS_REQUIRED(mConnectionLock); + const sp<SensorService> mService; const uid_t mUid; + const pid_t mPid; const sensors_direct_mem_t mMem; const int32_t mHalChannelHandle; const String16 mOpPackageName; diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp index 3446f58b96..130c112fd2 100644 --- a/services/sensorservice/SensorEventConnection.cpp +++ b/services/sensorservice/SensorEventConnection.cpp @@ -90,15 +90,14 @@ void SensorService::SensorEventConnection::dump(String8& result) { result.append("NORMAL\n"); } result.appendFormat("\t %s | WakeLockRefCount %d | uid %d | cache size %d | " - "max cache size %d\n", mPackageName.c_str(), mWakeLockRefCount, mUid, mCacheSize, - mMaxCacheSize); + "max cache size %d | has sensor access: %s\n", + mPackageName.c_str(), mWakeLockRefCount, mUid, mCacheSize, mMaxCacheSize, + hasSensorAccess() ? "true" : "false"); for (auto& it : mSensorInfo) { const FlushInfo& flushInfo = it.second; - result.appendFormat("\t %s 0x%08x | status: %s | pending flush events %d \n", - mService->getSensorName(it.first).c_str(), - it.first, - flushInfo.mFirstFlushPending ? "First flush pending" : - "active", + result.appendFormat("\t %s 0x%08x | first flush pending: %s | pending flush events %d \n", + mService->getSensorName(it.first).c_str(), it.first, + flushInfo.mFirstFlushPending ? "true" : "false", flushInfo.mPendingFlushEventsToSend); } #if DEBUG_CONNECTIONS diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 31b7f8886c..3895ffe55e 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -682,14 +682,14 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled"); const auto& activeConnections = connLock.getActiveConnections(); - result.appendFormat("%zd active connections\n", activeConnections.size()); + result.appendFormat("%zd open event connections\n", activeConnections.size()); for (size_t i=0 ; i < activeConnections.size() ; i++) { result.appendFormat("Connection Number: %zu \n", i); activeConnections[i]->dump(result); } const auto& directConnections = connLock.getDirectConnections(); - result.appendFormat("%zd direct connections\n", directConnections.size()); + result.appendFormat("%zd open direct connections\n", directConnections.size()); for (size_t i = 0 ; i < directConnections.size() ; i++) { result.appendFormat("Direct connection %zu:\n", i); directConnections[i]->dump(result); @@ -1729,7 +1729,10 @@ sp<ISensorEventConnection> SensorService::createSensorDirectConnection( ALOGE("SensorDevice::registerDirectChannel returns %d", channelHandle); } else { mem.handle = clone; - conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName, deviceId); + IPCThreadState* thread = IPCThreadState::self(); + pid_t pid = (thread != nullptr) ? thread->getCallingPid() : -1; + conn = new SensorDirectConnection(this, uid, pid, &mem, channelHandle, opPackageName, + deviceId); } if (conn == nullptr) { diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index b826466edc..ed4c47dc87 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -48,6 +48,7 @@ cc_defaults { "libtonemap", "libaidlcommonsupport", "libprocessgroup", + "libprocessgroup_util", "libcgrouprc", "libjsoncpp", "libcgrouprc_format", diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index c5008d8dd5..7c4aa7530d 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -209,7 +209,7 @@ sp<GraphicBuffer> allocateClearSlotBuffer() { if (!buffer || buffer->initCheck() != ::android::OK) { return nullptr; } - return std::move(buffer); + return buffer; } } // anonymous namespace diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.cpp b/services/surfaceflinger/HdrSdrRatioOverlay.cpp index dfb1c1e251..20886358b5 100644 --- a/services/surfaceflinger/HdrSdrRatioOverlay.cpp +++ b/services/surfaceflinger/HdrSdrRatioOverlay.cpp @@ -114,7 +114,7 @@ HdrSdrRatioOverlay::HdrSdrRatioOverlay(ConstructorTag) ALOGE("%s: Failed to create buffer state layer", __func__); return; } - SurfaceComposerClient::Transaction() + createTransaction() .setLayer(mSurfaceControl->get(), INT32_MAX - 2) .setTrustedOverlay(mSurfaceControl->get(), true) .apply(); @@ -130,7 +130,7 @@ void HdrSdrRatioOverlay::changeHdrSdrRatio(float currentHdrSdrRatio) { } void HdrSdrRatioOverlay::setLayerStack(ui::LayerStack stack) { - SurfaceComposerClient::Transaction().setLayerStack(mSurfaceControl->get(), stack).apply(); + createTransaction().setLayerStack(mSurfaceControl->get(), stack).apply(); } void HdrSdrRatioOverlay::setViewport(ui::Size viewport) { @@ -141,7 +141,7 @@ void HdrSdrRatioOverlay::setViewport(ui::Size viewport) { // set the ratio frame to the top right of the screen frame.offsetBy(viewport.width - frame.width(), height >> 4); - SurfaceComposerClient::Transaction() + createTransaction() .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth), 0, 0, frame.getHeight() / static_cast<float>(kBufferHeight)) .setPosition(mSurfaceControl->get(), frame.left, frame.top) @@ -167,7 +167,7 @@ auto HdrSdrRatioOverlay::getOrCreateBuffers(float currentHdrSdrRatio) -> const s } }(); - SurfaceComposerClient::Transaction().setTransform(mSurfaceControl->get(), transform).apply(); + createTransaction().setTransform(mSurfaceControl->get(), transform).apply(); constexpr SkColor kMinRatioColor = SK_ColorBLUE; constexpr SkColor kMaxRatioColor = SK_ColorGREEN; @@ -194,9 +194,21 @@ auto HdrSdrRatioOverlay::getOrCreateBuffers(float currentHdrSdrRatio) -> const s void HdrSdrRatioOverlay::animate() { if (!std::isfinite(mCurrentHdrSdrRatio) || mCurrentHdrSdrRatio < 1.0f) return; - SurfaceComposerClient::Transaction() + createTransaction() .setBuffer(mSurfaceControl->get(), getOrCreateBuffers(mCurrentHdrSdrRatio)) .apply(); } +SurfaceComposerClient::Transaction HdrSdrRatioOverlay::createTransaction() const { + constexpr float kFrameRate = 0.f; + constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE; + constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS; + + const sp<SurfaceControl>& surface = mSurfaceControl->get(); + + SurfaceComposerClient::Transaction transaction; + transaction.setFrameRate(surface, kFrameRate, kCompatibility, kSeamlessness); + return transaction; +} + } // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.h b/services/surfaceflinger/HdrSdrRatioOverlay.h index 72d401d444..ba88252011 100644 --- a/services/surfaceflinger/HdrSdrRatioOverlay.h +++ b/services/surfaceflinger/HdrSdrRatioOverlay.h @@ -53,5 +53,7 @@ private: size_t mIndex = 0; std::array<sp<GraphicBuffer>, 2> mRingBuffer; + + SurfaceComposerClient::Transaction createTransaction() const; }; } // namespace android diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 04491a25c3..4a7cff58dd 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -89,7 +89,9 @@ nsecs_t VSyncPredictor::idealPeriod() const { } bool VSyncPredictor::validate(nsecs_t timestamp) const { + SFTRACE_CALL(); if (mLastTimestampIndex < 0 || mTimestamps.empty()) { + SFTRACE_INSTANT("timestamp valid (first)"); return true; } @@ -98,7 +100,11 @@ bool VSyncPredictor::validate(nsecs_t timestamp) const { (timestamp - aValidTimestamp) % idealPeriod() * kMaxPercent / idealPeriod(); if (percent >= kOutlierTolerancePercent && percent <= (kMaxPercent - kOutlierTolerancePercent)) { - SFTRACE_FORMAT_INSTANT("timestamp is not aligned with model"); + SFTRACE_FORMAT_INSTANT("timestamp not aligned with model. aValidTimestamp %.2fms ago" + ", timestamp %.2fms ago, idealPeriod=%.2 percent=%d", + (mClock->now() - aValidTimestamp) / 1e6f, + (mClock->now() - timestamp) / 1e6f, + idealPeriod() / 1e6f, percent); return false; } @@ -148,7 +154,10 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { // Add the timestamp to mTimestamps before clearing it so we could // update mKnownTimestamp based on the new timestamp. mTimestamps.push_back(timestamp); - clearTimestamps(); + + // Do not clear timelines as we don't want to break the phase while + // we are still learning. + clearTimestamps(/* clearTimelines */ false); } else if (!mTimestamps.empty()) { mKnownTimestamp = std::max(timestamp, *std::max_element(mTimestamps.begin(), mTimestamps.end())); @@ -235,7 +244,7 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { if (CC_UNLIKELY(bottom == 0)) { it->second = {idealPeriod(), 0}; - clearTimestamps(); + clearTimestamps(/* clearTimelines */ true); return false; } @@ -245,7 +254,7 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { auto const percent = std::abs(anticipatedPeriod - idealPeriod()) * kMaxPercent / idealPeriod(); if (percent >= kOutlierTolerancePercent) { it->second = {idealPeriod(), 0}; - clearTimestamps(); + clearTimestamps(/* clearTimelines */ true); return false; } @@ -425,6 +434,9 @@ void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) { timeout ? std::to_string(timeout->timeoutNs).c_str() : "N/A"); std::lock_guard lock(mMutex); + // do not clear the timelines on VRR displays if we didn't change the mode + const bool isVrr = modePtr->getVrrConfig().has_value(); + const bool clearTimelines = !isVrr || mDisplayModePtr->getId() != modePtr->getId(); mDisplayModePtr = modePtr; mNumVsyncsForFrame = numVsyncsPerFrame(mDisplayModePtr); traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs()); @@ -438,8 +450,10 @@ void VSyncPredictor::setDisplayModePtr(ftl::NonNull<DisplayModePtr> modePtr) { mRateMap[idealPeriod()] = {idealPeriod(), 0}; } - mTimelines.clear(); - clearTimestamps(); + if (clearTimelines) { + mTimelines.clear(); + } + clearTimestamps(clearTimelines); } Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime, @@ -556,15 +570,19 @@ VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const { return mRateMap.find(idealPeriod())->second; } -void VSyncPredictor::clearTimestamps() { - SFTRACE_CALL(); +void VSyncPredictor::clearTimestamps(bool clearTimelines) { + SFTRACE_FORMAT("%s: clearTimelines=%d", __func__, clearTimelines); if (!mTimestamps.empty()) { auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end()); if (mKnownTimestamp) { mKnownTimestamp = std::max(*mKnownTimestamp, maxRb); + SFTRACE_FORMAT_INSTANT("mKnownTimestamp was %.2fms ago", + (mClock->now() - *mKnownTimestamp) / 1e6f); } else { mKnownTimestamp = maxRb; + SFTRACE_FORMAT_INSTANT("mKnownTimestamp (maxRb) was %.2fms ago", + (mClock->now() - *mKnownTimestamp) / 1e6f); } mTimestamps.clear(); @@ -575,7 +593,7 @@ void VSyncPredictor::clearTimestamps() { if (mTimelines.empty()) { mLastCommittedVsync = TimePoint::fromNs(0); mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt); - } else { + } else if (clearTimelines) { while (mTimelines.size() > 1) { mTimelines.pop_front(); } @@ -596,9 +614,10 @@ bool VSyncPredictor::needsMoreSamples() const { } void VSyncPredictor::resetModel() { + SFTRACE_CALL(); std::lock_guard lock(mMutex); mRateMap[idealPeriod()] = {idealPeriod(), 0}; - clearTimestamps(); + clearTimestamps(/* clearTimelines */ true); } void VSyncPredictor::dump(std::string& result) const { diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index 6c8a2f29f9..2df3d0465f 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -127,7 +127,7 @@ private: VSyncPredictor(VSyncPredictor const&) = delete; VSyncPredictor& operator=(VSyncPredictor const&) = delete; - void clearTimestamps() REQUIRES(mMutex); + void clearTimestamps(bool clearTimelines) REQUIRES(mMutex); const std::unique_ptr<Clock> mClock; const PhysicalDisplayId mId; diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index 2455822c7d..b974cd2b04 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -140,7 +140,9 @@ void VSyncReactor::onDisplayModeChanged(ftl::NonNull<DisplayModePtr> modePtr, bo std::lock_guard lock(mMutex); mLastHwVsync.reset(); - if (!mSupportKernelIdleTimer && mTracker.isCurrentMode(modePtr) && !force) { + // kernel idle timer is not applicable for VRR + const bool supportKernelIdleTimer = mSupportKernelIdleTimer && !modePtr->getVrrConfig(); + if (!supportKernelIdleTimer && mTracker.isCurrentMode(modePtr) && !force) { endPeriodTransition(); setIgnorePresentFencesInternal(false); mMoreSamplesNeeded = false; diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp index 8adf2a6366..3ee1e541c3 100644 --- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp +++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp @@ -69,7 +69,10 @@ bool FrameTarget::wouldPresentEarly(Period vsyncPeriod, Period minFramePeriod) c const FenceTimePtr& FrameTarget::presentFenceForPreviousFrame() const { if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) { - return mPresentFences.back().fenceTime; + if (mPresentFences.size() > 0) { + return mPresentFences.back().fenceTime; + } + return FenceTime::NO_FENCE; } return mPresentFencesLegacy.front().fenceTime; diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp index 0bafb71f3f..b1895989ec 100644 --- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp +++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp @@ -62,12 +62,12 @@ perfetto::protos::TransactionState TransactionProtoParser::toProto(const Transac proto.mutable_layer_changes()->Reserve(static_cast<int32_t>(t.states.size())); for (auto& layerState : t.states) { - proto.mutable_layer_changes()->Add(std::move(toProto(layerState))); + proto.mutable_layer_changes()->Add(toProto(layerState)); } proto.mutable_display_changes()->Reserve(static_cast<int32_t>(t.displays.size())); for (auto& displayState : t.displays) { - proto.mutable_display_changes()->Add(std::move(toProto(displayState))); + proto.mutable_display_changes()->Add(toProto(displayState)); } proto.mutable_merged_transaction_ids()->Reserve( diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp index 696f3489bf..bc9f8094f2 100644 --- a/services/surfaceflinger/Tracing/TransactionTracing.cpp +++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp @@ -244,7 +244,7 @@ void TransactionTracing::addEntry(const std::vector<CommittedUpdates>& committed static_cast<int32_t>(update.createdLayers.size())); for (const auto& args : update.createdLayers) { - entryProto.mutable_added_layers()->Add(std::move(mProtoParser.toProto(args))); + entryProto.mutable_added_layers()->Add(mProtoParser.toProto(args)); } entryProto.mutable_destroyed_layers()->Reserve( @@ -276,7 +276,7 @@ void TransactionTracing::addEntry(const std::vector<CommittedUpdates>& committed static_cast<int32_t>(update.displayInfos.size())); for (auto& [layerStack, displayInfo] : update.displayInfos) { entryProto.mutable_displays()->Add( - std::move(mProtoParser.toProto(displayInfo, layerStack.id))); + mProtoParser.toProto(displayInfo, layerStack.id)); } } diff --git a/services/surfaceflinger/common/include/common/trace.h b/services/surfaceflinger/common/include/common/trace.h index 0d7ac9b30e..dc5716ba05 100644 --- a/services/surfaceflinger/common/include/common/trace.h +++ b/services/surfaceflinger/common/include/common/trace.h @@ -48,7 +48,7 @@ ::tracing_perfetto::traceAsyncBegin(ATRACE_TAG, name, cookie) #define SFTRACE_ASYNC_END(name, cookie) ::tracing_perfetto::traceAsyncEnd(ATRACE_TAG, name, cookie) #define SFTRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie) \ - ::tracing_perfetto::traceAsyncBeginForTrack(ATRACE_TAG, track_name, name, cookie) + ::tracing_perfetto::traceAsyncBeginForTrack(ATRACE_TAG, name, track_name, cookie) #define SFTRACE_ASYNC_FOR_TRACK_END(track_name, cookie) \ ::tracing_perfetto::traceAsyncEndForTrack(ATRACE_TAG, track_name, cookie) #define SFTRACE_INSTANT(name) ::tracing_perfetto::traceInstant(ATRACE_TAG, name) diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp index fb4ef70450..7bf167498b 100644 --- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp @@ -131,14 +131,14 @@ protected: // add layers and add some layer transaction { frontend::Update update; - update.layerCreationArgs.emplace_back(std::move( + update.layerCreationArgs.emplace_back( getLayerCreationArgs(mParentLayerId, /*parentId=*/UNASSIGNED_LAYER_ID, /*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/123, - /*addToRoot=*/true))); - update.layerCreationArgs.emplace_back(std::move( + /*addToRoot=*/true)); + update.layerCreationArgs.emplace_back( getLayerCreationArgs(mChildLayerId, mParentLayerId, /*layerIdToMirror=*/UNASSIGNED_LAYER_ID, /*flags=*/456, - /*addToRoot=*/true))); + /*addToRoot=*/true)); TransactionState transaction; transaction.id = 50; ResolvedComposerState layerState; diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index 3f89960e32..ef213f0c7a 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -339,10 +339,13 @@ void Hal::UnloadBuiltinDriver() { ALOGD("Unload builtin Vulkan driver."); - // Close the opened device - int err = hal_.dev_->common.close( - const_cast<struct hw_device_t*>(&hal_.dev_->common)); - ALOG_ASSERT(!err, "hw_device_t::close() failed."); + if (hal_.dev_->common.close != nullptr) + { + // Close the opened device + int err = hal_.dev_->common.close( + const_cast<struct hw_device_t*>(&hal_.dev_->common)); + ALOG_ASSERT(!err, "hw_device_t::close() failed."); + } // Close the opened shared library in the hw_module_t android_unload_sphal_library(hal_.dev_->common.module->dso); |