diff options
247 files changed, 7647 insertions, 3016 deletions
diff --git a/cmds/bugreport/OWNERS b/cmds/bugreport/OWNERS index 2a9b681318..5f56531754 100644 --- a/cmds/bugreport/OWNERS +++ b/cmds/bugreport/OWNERS @@ -1,4 +1,5 @@ set noparent +gavincorkery@google.com nandana@google.com jsharkey@android.com diff --git a/cmds/bugreportz/OWNERS b/cmds/bugreportz/OWNERS index 2a9b681318..5f56531754 100644 --- a/cmds/bugreportz/OWNERS +++ b/cmds/bugreportz/OWNERS @@ -1,4 +1,5 @@ set noparent +gavincorkery@google.com nandana@google.com jsharkey@android.com diff --git a/cmds/dumpstate/OWNERS b/cmds/dumpstate/OWNERS index 2a9b681318..5f56531754 100644 --- a/cmds/dumpstate/OWNERS +++ b/cmds/dumpstate/OWNERS @@ -1,4 +1,5 @@ set noparent +gavincorkery@google.com nandana@google.com jsharkey@android.com diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 336c5a2b3d..25e6dc95c7 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1641,6 +1641,10 @@ static Dumpstate::RunStatus dumpstate() { MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n"); } else { RunCommand("LSMOD", {"lsmod"}); + RunCommand("MODULES INFO", + {"sh", "-c", "cat /proc/modules | cut -d' ' -f1 | " + " while read MOD ; do echo modinfo:$MOD ; modinfo $MOD ; " + "done"}, CommandOptions::AS_ROOT); } if (android::base::GetBoolProperty("ro.logd.kernel", false)) { @@ -1692,6 +1696,12 @@ static Dumpstate::RunStatus dumpstate() { RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysHigh); + // The dump mechanism in connectivity is refactored due to modularization work. Connectivity can + // only register with a default priority(NORMAL priority). Dumpstate has to call connectivity + // dump with priority parameters to dump high priority information. + RunDumpsys("SERVICE HIGH connectivity", {"connectivity", "--dump-priority", "HIGH"}, + CommandOptions::WithTimeout(10).Build()); + RunCommand("SYSTEM PROPERTIES", {"getprop"}); RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"}); @@ -1960,6 +1970,8 @@ static void DumpstateTelephonyOnly(const std::string& calling_package) { RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + RunDumpsys("DUMPSYS", {"vcn_management"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); if (include_sensitive_info) { // Carrier apps' services will be dumped below in dumpsys activity service all-non-platform. RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(), @@ -3183,6 +3195,11 @@ Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented(int32_t calling_uid // Since we do not have user consent to share the bugreport it does not get // copied over to the calling app but remains in the internal directory from // where the user can manually pull it. + std::string final_path = GetPath(".zip"); + bool copy_succeeded = android::os::CopyFileToFile(path_, final_path); + if (copy_succeeded) { + android::os::UnlinkAndLogOnError(path_); + } return Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT; } // Unknown result; must be a programming error. diff --git a/cmds/dumpsys/OWNERS b/cmds/dumpsys/OWNERS index 4f6a89ebe5..97a63ca8fe 100644 --- a/cmds/dumpsys/OWNERS +++ b/cmds/dumpsys/OWNERS @@ -1,5 +1,6 @@ set noparent +gavincorkery@google.com nandana@google.com jsharkey@android.com diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS index fc745d0cf7..d6807ffe26 100644 --- a/cmds/installd/OWNERS +++ b/cmds/installd/OWNERS @@ -9,3 +9,4 @@ narayan@google.com ngeoffray@google.com rpl@google.com toddke@google.com +patb@google.com diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h index c3d3a4b35a..fb7d09c04d 100644 --- a/include/android/imagedecoder.h +++ b/include/android/imagedecoder.h @@ -837,9 +837,11 @@ void AImageDecoderFrameInfo_delete( * is the current frame. * * If the image only has one frame, this will fill the {@link - * AImageDecoderFrameInfo} with the encoded info, if any, or reasonable + * AImageDecoderFrameInfo} with the encoded info and reasonable * defaults. * + * If {@link AImageDecoder_advanceFrame} succeeded, this will succeed as well. + * * @param decoder Opaque object representing the decoder. * @param info Opaque object to hold frame information. On success, will be * filled with information regarding the current frame. @@ -861,7 +863,7 @@ int AImageDecoder_getFrameInfo(AImageDecoder* _Nonnull decoder, * Introduced in API 31. * * Errors: - * - returns 0 if |info| is null. + * - returns {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if |info| is null. */ int64_t AImageDecoderFrameInfo_getDuration( const AImageDecoderFrameInfo* _Nonnull info) __INTRODUCED_IN(31); @@ -896,19 +898,25 @@ ARect AImageDecoderFrameInfo_getFrameRect( * * Introduced in API 31. * - * Note that this may differ from whether the composed frame has - * alpha. If this frame does not fill the entire image dimensions - * (see {@link AImageDecoderFrameInfo_getFrameRect}) or it blends - * with an opaque frame, for example, the composed frame’s alpha - * may not match. It is also conservative; for example, if a color - * index-based frame has a color with alpha but does not use it, - * this will still return true. + * Unless this frame is independent (see {@link AImageDecoder_decodeImage}), + * a single call to {@link AImageDecoder_decodeImage} will decode an updated + * rectangle of pixels and then blend it with the existing pixels in the + * |pixels| buffer according to {@link AImageDecoderFrameInfo_getBlendOp}. This + * method returns whether the updated rectangle has alpha, prior to blending. + * The return value is conservative; for example, if a color-index-based frame + * has a color with alpha but does not use it, this will still return true. * * This, along with other information in AImageDecoderFrameInfo, * can be useful for determining whether a frame is independent, but * the decoder handles blending frames, so a simple * sequential client does not need this. * + * Note that this may differ from whether the composed frame (that is, the + * resulting image after blending) has alpha. If this frame does not fill the + * entire image dimensions (see {@link AImageDecoderFrameInfo_getFrameRect}) + * or it blends with an opaque frame, for example, the composed frame’s alpha + * may not match. + * * Errors: * - returns false if |info| is null. */ diff --git a/include/android/surface_control.h b/include/android/surface_control.h index f6c2e55c2d..059bc41f9a 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -133,6 +133,9 @@ typedef struct ASurfaceTransactionStats ASurfaceTransactionStats; * ASurfaceTransaction_OnComplete callback can be used to be notified when a frame * including the updates in a transaction was presented. * + * Buffers which are replaced or removed from the scene in the transaction invoking + * this callback may be reused after this point. + * * \param context Optional context provided by the client that is passed into * the callback. * @@ -153,6 +156,13 @@ typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactio * are ready to be presented. This callback will be invoked before the * ASurfaceTransaction_OnComplete callback. * + * This callback does not mean buffers have been released! It simply means that any new + * transactions applied will not overwrite the transaction for which we are receiving + * a callback and instead will be included in the next frame. If you are trying to avoid + * dropping frames (overwriting transactions), and unable to use timestamps (Which provide + * a more efficient solution), then this method provides a method to pace your transaction + * application. + * * \param context Optional context provided by the client that is passed into the callback. * * \param stats Opaque handle that can be passed to ASurfaceTransactionStats functions to query @@ -358,6 +368,11 @@ void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction, * enum. * * Available since API level 29. + * + * @deprecated Use setCrop, setPosition, setBufferTransform, and setScale instead. Those functions + * provide well defined behavior and allows for more control by the apps. It also allows the caller + * to set different properties at different times, instead of having to specify all the desired + * properties at once. */ void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, const ARect& source, @@ -526,6 +541,9 @@ void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction, * callback timings, and changes to the time interval at which the system releases buffers back to * the application. * + * You can register for changes in the refresh rate using + * \a AChoreographer_registerRefreshRateCallback. + * * \param frameRate is the intended frame rate of this surface, in frames per second. 0 is a special * value that indicates the app will accept the system's choice for the display frame rate, which is * the default behavior if this function isn't called. The frameRate param does <em>not</em> need to diff --git a/include/input/Input.h b/include/input/Input.h index 7b522bbc1e..d4defa8269 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -70,6 +70,14 @@ enum { */ AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8, + /** + * This flag indicates that the event will not cause a focus change if it is directed to an + * unfocused window, even if it an ACTION_DOWN. This is typically used with pointer + * gestures to allow the user to direct gestures to an unfocused window without bringing it + * into focus. + */ + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40, + /* Motion event is inconsistent with previously sent motion events. */ AMOTION_EVENT_FLAG_TAINTED = 0x80000000, }; @@ -746,10 +754,14 @@ public: void scale(float globalScaleFactor); - // Apply 3x3 perspective matrix transformation. + // Set 3x3 perspective matrix transformation. // Matrix is in row-major form and compatible with SkMatrix. void transform(const std::array<float, 9>& matrix); + // Apply 3x3 perspective matrix transformation only to content (do not modify mTransform). + // Matrix is in row-major form and compatible with SkMatrix. + void applyTransform(const std::array<float, 9>& matrix); + #ifdef __linux__ status_t readFromParcel(Parcel* parcel); status_t writeToParcel(Parcel* parcel) const; diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 712adfa57b..1fec08027f 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -101,7 +101,7 @@ enum class InputDeviceSensorReportingMode : int32_t { }; enum class InputDeviceLightType : int32_t { - SINGLE = 0, + MONO = 0, PLAYER_ID = 1, RGB = 2, MULTI_COLOR = 3, diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index ff3367839d..360dfbfd73 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -229,7 +229,7 @@ public: InputChannel(const InputChannel& other) : mName(other.mName), mFd(::dup(other.mFd)), mToken(other.mToken){}; InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token); - virtual ~InputChannel(); + ~InputChannel() override; /** * Create a pair of input channels. * The two returned input channels are equivalent, and are labeled as "server" and "client" diff --git a/include/input/LatencyStatistics.h b/include/input/LatencyStatistics.h deleted file mode 100644 index bd86266901..0000000000 --- a/include/input/LatencyStatistics.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2019 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 _UI_INPUT_STATISTICS_H -#define _UI_INPUT_STATISTICS_H - -#include <android-base/chrono_utils.h> - -#include <stddef.h> - -namespace android { - -class LatencyStatistics { -private: - /* Minimum sample recorded */ - float mMin; - /* Maximum sample recorded */ - float mMax; - /* Sum of all samples recorded */ - float mSum; - /* Sum of all the squares of samples recorded */ - float mSum2; - /* Count of all samples recorded */ - size_t mCount; - /* The last time statistics were reported */ - std::chrono::steady_clock::time_point mLastReportTime; - /* Statistics Report Frequency */ - const std::chrono::seconds mReportPeriod; - -public: - LatencyStatistics(std::chrono::seconds period); - - void addValue(float); - void reset(); - bool shouldReport(); - - float getMean(); - float getMin(); - float getMax(); - float getStDev(); - size_t getCount(); -}; - -} // namespace android - -#endif // _UI_INPUT_STATISTICS_H diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index a97cf87de3..be260e856d 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -112,7 +112,7 @@ cc_library { "PersistableBundle.cpp", "ProcessState.cpp", "RpcAddress.cpp", - "RpcConnection.cpp", + "RpcSession.cpp", "RpcServer.cpp", "RpcState.cpp", "Static.cpp", diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index fdcf94acfa..1dcb94c80f 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -21,7 +21,7 @@ #include <binder/IPCThreadState.h> #include <binder/IResultReceiver.h> -#include <binder/RpcConnection.h> +#include <binder/RpcSession.h> #include <binder/Stability.h> #include <cutils/compiler.h> #include <utils/Log.h> @@ -136,15 +136,15 @@ sp<BpBinder> BpBinder::create(int32_t handle) { return sp<BpBinder>::make(BinderHandle{handle}, trackedUid); } -sp<BpBinder> BpBinder::create(const sp<RpcConnection>& connection, const RpcAddress& address) { - LOG_ALWAYS_FATAL_IF(connection == nullptr, "BpBinder::create null connection"); +sp<BpBinder> BpBinder::create(const sp<RpcSession>& session, const RpcAddress& address) { + LOG_ALWAYS_FATAL_IF(session == nullptr, "BpBinder::create null session"); // These are not currently tracked, since there is no UID or other // identifier to track them with. However, if similar functionality is - // needed, connection objects keep track of all BpBinder objects on a - // per-connection basis. + // needed, session objects keep track of all BpBinder objects on a + // per-session basis. - return sp<BpBinder>::make(SocketHandle{connection, address}); + return sp<BpBinder>::make(RpcHandle{session, address}); } BpBinder::BpBinder(Handle&& handle) @@ -165,20 +165,20 @@ BpBinder::BpBinder(BinderHandle&& handle, int32_t trackedUid) : BpBinder(Handle( IPCThreadState::self()->incWeakHandle(this->binderHandle(), this); } -BpBinder::BpBinder(SocketHandle&& handle) : BpBinder(Handle(handle)) { - LOG_ALWAYS_FATAL_IF(rpcConnection() == nullptr, "BpBinder created w/o connection object"); +BpBinder::BpBinder(RpcHandle&& handle) : BpBinder(Handle(handle)) { + LOG_ALWAYS_FATAL_IF(rpcSession() == nullptr, "BpBinder created w/o session object"); } bool BpBinder::isRpcBinder() const { - return std::holds_alternative<SocketHandle>(mHandle); + return std::holds_alternative<RpcHandle>(mHandle); } const RpcAddress& BpBinder::rpcAddress() const { - return std::get<SocketHandle>(mHandle).address; + return std::get<RpcHandle>(mHandle).address; } -const sp<RpcConnection>& BpBinder::rpcConnection() const { - return std::get<SocketHandle>(mHandle).connection; +const sp<RpcSession>& BpBinder::rpcSession() const { + return std::get<RpcHandle>(mHandle).session; } int32_t BpBinder::binderHandle() const { @@ -273,7 +273,7 @@ status_t BpBinder::transact( status_t status; if (CC_UNLIKELY(isRpcBinder())) { - status = rpcConnection()->transact(rpcAddress(), code, data, reply, flags); + status = rpcSession()->transact(rpcAddress(), code, data, reply, flags); } else { status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags); } @@ -479,7 +479,7 @@ void BpBinder::onLastStrongRef(const void* /*id*/) { ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, binderHandle()); if (CC_UNLIKELY(isRpcBinder())) { - (void)rpcConnection()->sendDecStrong(rpcAddress()); + (void)rpcSession()->sendDecStrong(rpcAddress()); return; } IF_ALOGV() { diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 6fb1227f63..c605e67853 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -29,8 +29,6 @@ #include <utils/SystemClock.h> #include <utils/threads.h> -#include <private/binder/binder_module.h> - #include <atomic> #include <errno.h> #include <inttypes.h> @@ -43,6 +41,7 @@ #include <unistd.h> #include "Static.h" +#include "binder_module.h" #if LOG_NDEBUG @@ -490,14 +489,16 @@ void IPCThreadState::flushCommands() bool IPCThreadState::flushIfNeeded() { - if (mIsLooper || mServingStackPointer != nullptr) { + if (mIsLooper || mServingStackPointer != nullptr || mIsFlushing) { return false; } + mIsFlushing = true; // In case this thread is not a looper and is not currently serving a binder transaction, // there's no guarantee that this thread will call back into the kernel driver any time // soon. Therefore, flush pending commands such as BC_FREE_BUFFER, to prevent them from getting // stuck in this thread's out buffer. flushCommands(); + mIsFlushing = false; return true; } @@ -848,15 +849,15 @@ status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) } IPCThreadState::IPCThreadState() - : mProcess(ProcessState::self()), - mServingStackPointer(nullptr), - mWorkSource(kUnsetWorkSource), - mPropagateWorkSource(false), - mIsLooper(false), - mStrictModePolicy(0), - mLastTransactionBinderFlags(0), - mCallRestriction(mProcess->mCallRestriction) -{ + : mProcess(ProcessState::self()), + mServingStackPointer(nullptr), + mWorkSource(kUnsetWorkSource), + mPropagateWorkSource(false), + mIsLooper(false), + mIsFlushing(false), + mStrictModePolicy(0), + mLastTransactionBinderFlags(0), + mCallRestriction(mProcess->mCallRestriction) { pthread_setspecific(gTLS, this); clearCaller(); mIn.setDataCapacity(256); diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp index b503bebc5e..616591182a 100644 --- a/libs/binder/LazyServiceRegistrar.cpp +++ b/libs/binder/LazyServiceRegistrar.cpp @@ -123,16 +123,20 @@ bool ClientCounterCallbackImpl::registerService(const sp<IBinder>& service, cons std::string regStr = (reRegister) ? "Re-registering" : "Registering"; ALOGI("%s service %s", regStr.c_str(), name.c_str()); - if (!manager->addService(name.c_str(), service, allowIsolated, dumpFlags).isOk()) { - ALOGE("Failed to register service %s", name.c_str()); + if (Status status = manager->addService(name.c_str(), service, allowIsolated, dumpFlags); + !status.isOk()) { + ALOGE("Failed to register service %s (%s)", name.c_str(), status.toString8().c_str()); return false; } if (!reRegister) { - if (!manager->registerClientCallback(name, service, - sp<android::os::IClientCallback>::fromExisting(this)) - .isOk()) { - ALOGE("Failed to add client callback for service %s", name.c_str()); + if (Status status = + manager->registerClientCallback(name, service, + sp<android::os::IClientCallback>::fromExisting( + this)); + !status.isOk()) { + ALOGE("Failed to add client callback for service %s (%s)", name.c_str(), + status.toString8().c_str()); return false; } @@ -171,10 +175,10 @@ bool ClientCounterCallbackImpl::tryUnregister() { auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager())); for (auto& [name, entry] : mRegisteredServices) { - bool success = manager->tryUnregisterService(name, entry.service).isOk(); + Status status = manager->tryUnregisterService(name, entry.service); - if (!success) { - ALOGI("Failed to unregister service %s", name.c_str()); + if (!status.isOk()) { + ALOGI("Failed to unregister service %s (%s)", name.c_str(), status.toString8().c_str()); return false; } entry.registered = false; diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index cbe590fad2..ee834ea43c 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -48,10 +48,10 @@ #include <utils/String8.h> #include <utils/misc.h> -#include <private/binder/binder_module.h> #include "RpcState.h" #include "Static.h" #include "Utils.h" +#include "binder_module.h" #define LOG_REFS(...) //#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) @@ -202,7 +202,7 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) status_t status = writeInt32(1); // non-null if (status != OK) return status; RpcAddress address = RpcAddress::zero(); - status = mConnection->state()->onBinderLeaving(mConnection, binder, &address); + status = mSession->state()->onBinderLeaving(mSession, binder, &address); if (status != OK) return status; status = address.writeToParcel(this); if (status != OK) return status; @@ -273,8 +273,7 @@ status_t Parcel::flattenBinder(const sp<IBinder>& binder) status_t Parcel::unflattenBinder(sp<IBinder>* out) const { if (isForRpc()) { - LOG_ALWAYS_FATAL_IF(mConnection == nullptr, - "RpcConnection required to read from remote parcel"); + LOG_ALWAYS_FATAL_IF(mSession == nullptr, "RpcSession required to read from remote parcel"); int32_t isNull; status_t status = readInt32(&isNull); @@ -286,7 +285,7 @@ status_t Parcel::unflattenBinder(sp<IBinder>* out) const auto addr = RpcAddress::zero(); status_t status = addr.readFromParcel(*this); if (status != OK) return status; - binder = mConnection->state()->onBinderEntering(mConnection, addr); + binder = mSession->state()->onBinderEntering(mSession, addr); } return finishUnflattenBinder(binder, out); @@ -568,20 +567,20 @@ void Parcel::markForBinder(const sp<IBinder>& binder) { LOG_ALWAYS_FATAL_IF(mData != nullptr, "format must be set before data is written"); if (binder && binder->remoteBinder() && binder->remoteBinder()->isRpcBinder()) { - markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcConnection()); + markForRpc(binder->remoteBinder()->getPrivateAccessorForId().rpcSession()); } } -void Parcel::markForRpc(const sp<RpcConnection>& connection) { +void Parcel::markForRpc(const sp<RpcSession>& session) { LOG_ALWAYS_FATAL_IF(mData != nullptr && mOwner == nullptr, "format must be set before data is written OR on IPC data"); - LOG_ALWAYS_FATAL_IF(connection == nullptr, "markForRpc requires connection"); - mConnection = connection; + LOG_ALWAYS_FATAL_IF(session == nullptr, "markForRpc requires session"); + mSession = session; } bool Parcel::isForRpc() const { - return mConnection != nullptr; + return mSession != nullptr; } void Parcel::updateWorkSourceRequestHeaderPosition() const { @@ -2499,7 +2498,7 @@ void Parcel::initState() mDataPos = 0; ALOGV("initState Setting data size of %p to %zu", this, mDataSize); ALOGV("initState Setting data pos of %p to %zu", this, mDataPos); - mConnection = nullptr; + mSession = nullptr; mObjects = nullptr; mObjectsSize = 0; mObjectsCapacity = 0; diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 1d3beb4736..4fd0dc7715 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -27,8 +27,8 @@ #include <utils/String8.h> #include <utils/threads.h> -#include <private/binder/binder_module.h> #include "Static.h" +#include "binder_module.h" #include <errno.h> #include <fcntl.h> diff --git a/libs/binder/RpcConnection.cpp b/libs/binder/RpcConnection.cpp deleted file mode 100644 index 95eba87b68..0000000000 --- a/libs/binder/RpcConnection.cpp +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (C) 2020 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 "RpcConnection" - -#include <binder/RpcConnection.h> - -#include <inttypes.h> -#include <unistd.h> - -#include <string_view> - -#include <binder/Parcel.h> -#include <binder/Stability.h> -#include <utils/String8.h> - -#include "RpcSocketAddress.h" -#include "RpcState.h" -#include "RpcWireFormat.h" - -#ifdef __GLIBC__ -extern "C" pid_t gettid(); -#endif - -namespace android { - -using base::unique_fd; - -RpcConnection::RpcConnection() { - LOG_RPC_DETAIL("RpcConnection created %p", this); - - mState = std::make_unique<RpcState>(); -} -RpcConnection::~RpcConnection() { - LOG_RPC_DETAIL("RpcConnection destroyed %p", this); - - std::lock_guard<std::mutex> _l(mSocketMutex); - LOG_ALWAYS_FATAL_IF(mServers.size() != 0, - "Should not be able to destroy a connection with servers in use."); -} - -sp<RpcConnection> RpcConnection::make() { - return sp<RpcConnection>::make(); -} - -bool RpcConnection::setupUnixDomainClient(const char* path) { - return setupSocketClient(UnixSocketAddress(path)); -} - -#ifdef __BIONIC__ - -bool RpcConnection::setupVsockClient(unsigned int cid, unsigned int port) { - return setupSocketClient(VsockSocketAddress(cid, port)); -} - -#endif // __BIONIC__ - -bool RpcConnection::setupInetClient(const char* addr, unsigned int port) { - auto aiStart = InetSocketAddress::getAddrInfo(addr, port); - if (aiStart == nullptr) return false; - for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) { - InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, addr, port); - if (setupSocketClient(socketAddress)) return true; - } - ALOGE("None of the socket address resolved for %s:%u can be added as inet client.", addr, port); - return false; -} - -bool RpcConnection::addNullDebuggingClient() { - unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC))); - - if (serverFd == -1) { - ALOGE("Could not connect to /dev/null: %s", strerror(errno)); - return false; - } - - addClient(std::move(serverFd)); - return true; -} - -sp<IBinder> RpcConnection::getRootObject() { - ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT); - return state()->getRootObject(socket.fd(), sp<RpcConnection>::fromExisting(this)); -} - -status_t RpcConnection::getMaxThreads(size_t* maxThreads) { - ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT); - return state()->getMaxThreads(socket.fd(), sp<RpcConnection>::fromExisting(this), maxThreads); -} - -status_t RpcConnection::transact(const RpcAddress& address, uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags) { - ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), - (flags & IBinder::FLAG_ONEWAY) ? SocketUse::CLIENT_ASYNC - : SocketUse::CLIENT); - return state()->transact(socket.fd(), address, code, data, - sp<RpcConnection>::fromExisting(this), reply, flags); -} - -status_t RpcConnection::sendDecStrong(const RpcAddress& address) { - ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT_REFCOUNT); - return state()->sendDecStrong(socket.fd(), address); -} - -status_t RpcConnection::readId() { - { - std::lock_guard<std::mutex> _l(mSocketMutex); - LOG_ALWAYS_FATAL_IF(mForServer != nullptr, "Can only update ID for client."); - } - - int32_t id; - - ExclusiveSocket socket(sp<RpcConnection>::fromExisting(this), SocketUse::CLIENT); - status_t status = - state()->getConnectionId(socket.fd(), sp<RpcConnection>::fromExisting(this), &id); - if (status != OK) return status; - - LOG_RPC_DETAIL("RpcConnection %p has id %d", this, id); - mId = id; - return OK; -} - -void RpcConnection::join(unique_fd client) { - // must be registered to allow arbitrary client code executing commands to - // be able to do nested calls (we can't only read from it) - sp<ConnectionSocket> socket = assignServerToThisThread(std::move(client)); - - while (true) { - status_t error = - state()->getAndExecuteCommand(socket->fd, sp<RpcConnection>::fromExisting(this)); - - if (error != OK) { - ALOGI("Binder socket thread closing w/ status %s", statusToString(error).c_str()); - break; - } - } - - LOG_ALWAYS_FATAL_IF(!removeServerSocket(socket), - "bad state: socket object guaranteed to be in list"); -} - -wp<RpcServer> RpcConnection::server() { - return mForServer; -} - -bool RpcConnection::setupSocketClient(const RpcSocketAddress& addr) { - { - std::lock_guard<std::mutex> _l(mSocketMutex); - LOG_ALWAYS_FATAL_IF(mClients.size() != 0, - "Must only setup connection once, but already has %zu clients", - mClients.size()); - } - - if (!setupOneSocketClient(addr)) return false; - - // TODO(b/185167543): we should add additional connections dynamically - // instead of all at once. - // TODO(b/186470974): first risk of blocking - size_t numThreadsAvailable; - if (status_t status = getMaxThreads(&numThreadsAvailable); status != OK) { - ALOGE("Could not get max threads after initial connection to %s: %s", - addr.toString().c_str(), statusToString(status).c_str()); - return false; - } - - if (status_t status = readId(); status != OK) { - ALOGE("Could not get connection id after initial connection to %s; %s", - addr.toString().c_str(), statusToString(status).c_str()); - return false; - } - - // we've already setup one client - for (size_t i = 0; i + 1 < numThreadsAvailable; i++) { - // TODO(b/185167543): avoid race w/ accept4 not being called on server - for (size_t tries = 0; tries < 5; tries++) { - if (setupOneSocketClient(addr)) break; - usleep(10000); - } - } - - return true; -} - -bool RpcConnection::setupOneSocketClient(const RpcSocketAddress& addr) { - unique_fd serverFd( - TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0))); - if (serverFd == -1) { - int savedErrno = errno; - ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); - return false; - } - - if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) { - int savedErrno = errno; - ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); - return false; - } - - LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get()); - - addClient(std::move(serverFd)); - return true; -} - -void RpcConnection::addClient(unique_fd fd) { - std::lock_guard<std::mutex> _l(mSocketMutex); - sp<ConnectionSocket> connection = sp<ConnectionSocket>::make(); - connection->fd = std::move(fd); - mClients.push_back(connection); -} - -void RpcConnection::setForServer(const wp<RpcServer>& server, int32_t connectionId) { - mId = connectionId; - mForServer = server; -} - -sp<RpcConnection::ConnectionSocket> RpcConnection::assignServerToThisThread(unique_fd fd) { - std::lock_guard<std::mutex> _l(mSocketMutex); - sp<ConnectionSocket> connection = sp<ConnectionSocket>::make(); - connection->fd = std::move(fd); - connection->exclusiveTid = gettid(); - mServers.push_back(connection); - - return connection; -} - -bool RpcConnection::removeServerSocket(const sp<ConnectionSocket>& socket) { - std::lock_guard<std::mutex> _l(mSocketMutex); - if (auto it = std::find(mServers.begin(), mServers.end(), socket); it != mServers.end()) { - mServers.erase(it); - return true; - } - return false; -} - -RpcConnection::ExclusiveSocket::ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use) - : mConnection(connection) { - pid_t tid = gettid(); - std::unique_lock<std::mutex> _l(mConnection->mSocketMutex); - - mConnection->mWaitingThreads++; - while (true) { - sp<ConnectionSocket> exclusive; - sp<ConnectionSocket> available; - - // CHECK FOR DEDICATED CLIENT SOCKET - // - // A server/looper should always use a dedicated connection if available - findSocket(tid, &exclusive, &available, mConnection->mClients, mConnection->mClientsOffset); - - // WARNING: this assumes a server cannot request its client to send - // a transaction, as mServers is excluded below. - // - // Imagine we have more than one thread in play, and a single thread - // sends a synchronous, then an asynchronous command. Imagine the - // asynchronous command is sent on the first client socket. Then, if - // we naively send a synchronous command to that same socket, the - // thread on the far side might be busy processing the asynchronous - // command. So, we move to considering the second available thread - // for subsequent calls. - if (use == SocketUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) { - mConnection->mClientsOffset = - (mConnection->mClientsOffset + 1) % mConnection->mClients.size(); - } - - // USE SERVING SOCKET (for nested transaction) - // - // asynchronous calls cannot be nested - if (use != SocketUse::CLIENT_ASYNC) { - // server sockets are always assigned to a thread - findSocket(tid, &exclusive, nullptr /*available*/, mConnection->mServers, - 0 /* index hint */); - } - - // if our thread is already using a connection, prioritize using that - if (exclusive != nullptr) { - mSocket = exclusive; - mReentrant = true; - break; - } else if (available != nullptr) { - mSocket = available; - mSocket->exclusiveTid = tid; - break; - } - - // in regular binder, this would usually be a deadlock :) - LOG_ALWAYS_FATAL_IF(mConnection->mClients.size() == 0, - "Not a client of any connection. You must create a connection to an " - "RPC server to make any non-nested (e.g. oneway or on another thread) " - "calls."); - - LOG_RPC_DETAIL("No available connection (have %zu clients and %zu servers). Waiting...", - mConnection->mClients.size(), mConnection->mServers.size()); - mConnection->mSocketCv.wait(_l); - } - mConnection->mWaitingThreads--; -} - -void RpcConnection::ExclusiveSocket::findSocket(pid_t tid, sp<ConnectionSocket>* exclusive, - sp<ConnectionSocket>* available, - std::vector<sp<ConnectionSocket>>& sockets, - size_t socketsIndexHint) { - LOG_ALWAYS_FATAL_IF(sockets.size() > 0 && socketsIndexHint >= sockets.size(), - "Bad index %zu >= %zu", socketsIndexHint, sockets.size()); - - if (*exclusive != nullptr) return; // consistent with break below - - for (size_t i = 0; i < sockets.size(); i++) { - sp<ConnectionSocket>& socket = sockets[(i + socketsIndexHint) % sockets.size()]; - - // take first available connection (intuition = caching) - if (available && *available == nullptr && socket->exclusiveTid == std::nullopt) { - *available = socket; - continue; - } - - // though, prefer to take connection which is already inuse by this thread - // (nested transactions) - if (exclusive && socket->exclusiveTid == tid) { - *exclusive = socket; - break; // consistent with return above - } - } -} - -RpcConnection::ExclusiveSocket::~ExclusiveSocket() { - // reentrant use of a connection means something less deep in the call stack - // is using this fd, and it retains the right to it. So, we don't give up - // exclusive ownership, and no thread is freed. - if (!mReentrant) { - std::unique_lock<std::mutex> _l(mConnection->mSocketMutex); - mSocket->exclusiveTid = std::nullopt; - if (mConnection->mWaitingThreads > 0) { - _l.unlock(); - mConnection->mSocketCv.notify_one(); - } - } -} - -} // namespace android diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp index 5f024caf32..9cc6e7fe04 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -22,6 +22,7 @@ #include <thread> #include <vector> +#include <android-base/scopeguard.h> #include <binder/Parcel.h> #include <binder/RpcServer.h> #include <log/log.h> @@ -32,6 +33,7 @@ namespace android { +using base::ScopeGuard; using base::unique_fd; RpcServer::RpcServer() {} @@ -49,8 +51,6 @@ bool RpcServer::setupUnixDomainServer(const char* path) { return setupSocketServer(UnixSocketAddress(path)); } -#ifdef __BIONIC__ - bool RpcServer::setupVsockServer(unsigned int port) { // realizing value w/ this type at compile time to avoid ubsan abort constexpr unsigned int kAnyCid = VMADDR_CID_ANY; @@ -58,8 +58,6 @@ bool RpcServer::setupVsockServer(unsigned int port) { return setupSocketServer(VsockSocketAddress(kAnyCid, port)); } -#endif // __BIONIC__ - bool RpcServer::setupInetServer(unsigned int port, unsigned int* assignedPort) { const char* kAddr = "127.0.0.1"; @@ -111,63 +109,131 @@ size_t RpcServer::getMaxThreads() { void RpcServer::setRootObject(const sp<IBinder>& binder) { std::lock_guard<std::mutex> _l(mLock); - mRootObject = binder; + mRootObjectWeak = mRootObject = binder; +} + +void RpcServer::setRootObjectWeak(const wp<IBinder>& binder) { + std::lock_guard<std::mutex> _l(mLock); + mRootObject.clear(); + mRootObjectWeak = binder; } sp<IBinder> RpcServer::getRootObject() { std::lock_guard<std::mutex> _l(mLock); - return mRootObject; + bool hasWeak = mRootObjectWeak.unsafe_get(); + sp<IBinder> ret = mRootObjectWeak.promote(); + ALOGW_IF(hasWeak && ret == nullptr, "RpcServer root object is freed, returning nullptr"); + return ret; } void RpcServer::join() { + while (true) { + (void)acceptOne(); + } +} + +bool RpcServer::acceptOne() { LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!"); + LOG_ALWAYS_FATAL_IF(!hasServer(), "RpcServer must be setup to join."); + + unique_fd clientFd( + TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, nullptr /*length*/, SOCK_CLOEXEC))); + + if (clientFd < 0) { + ALOGE("Could not accept4 socket: %s", strerror(errno)); + return false; + } + LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get()); - std::vector<std::thread> pool; { std::lock_guard<std::mutex> _l(mLock); - LOG_ALWAYS_FATAL_IF(mServer.get() == -1, "RpcServer must be setup to join."); - // TODO(b/185167543): support more than one client at once - mConnection = RpcConnection::make(); - mConnection->setForServer(sp<RpcServer>::fromExisting(this), 42 /*placeholder id*/); - - mStarted = true; - for (size_t i = 0; i < mMaxThreads; i++) { - pool.push_back(std::thread([=] { - // TODO(b/185167543): do this dynamically, instead of from a static number - // of threads - unique_fd clientFd(TEMP_FAILURE_RETRY( - accept4(mServer.get(), nullptr, 0 /*length*/, SOCK_CLOEXEC))); - if (clientFd < 0) { - // If this log becomes confusing, should save more state from - // setupUnixDomainServer in order to output here. - ALOGE("Could not accept4 socket: %s", strerror(errno)); - return; - } - - LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get()); - - mConnection->join(std::move(clientFd)); - })); - } + std::thread thread = + std::thread(&RpcServer::establishConnection, this, + std::move(sp<RpcServer>::fromExisting(this)), std::move(clientFd)); + mConnectingThreads[thread.get_id()] = std::move(thread); } - // TODO(b/185167543): don't waste extra thread for join, and combine threads - // between clients - for (auto& t : pool) t.join(); + return true; } -std::vector<sp<RpcConnection>> RpcServer::listConnections() { +std::vector<sp<RpcSession>> RpcServer::listSessions() { std::lock_guard<std::mutex> _l(mLock); - if (mConnection == nullptr) return {}; - return {mConnection}; + std::vector<sp<RpcSession>> sessions; + for (auto& [id, session] : mSessions) { + (void)id; + sessions.push_back(session); + } + return sessions; } -bool RpcServer::setupSocketServer(const RpcSocketAddress& addr) { +size_t RpcServer::numUninitializedSessions() { + std::lock_guard<std::mutex> _l(mLock); + return mConnectingThreads.size(); +} + +void RpcServer::establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd) { + LOG_ALWAYS_FATAL_IF(this != server.get(), "Must pass same ownership object"); + + // TODO(b/183988761): cannot trust this simple ID + LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!"); + bool idValid = true; + int32_t id; + if (sizeof(id) != read(clientFd.get(), &id, sizeof(id))) { + ALOGE("Could not read ID from fd %d", clientFd.get()); + idValid = false; + } + + std::thread thisThread; + sp<RpcSession> session; { std::lock_guard<std::mutex> _l(mLock); - LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Each RpcServer can only have one server."); + + auto threadId = mConnectingThreads.find(std::this_thread::get_id()); + LOG_ALWAYS_FATAL_IF(threadId == mConnectingThreads.end(), + "Must establish connection on owned thread"); + thisThread = std::move(threadId->second); + ScopeGuard detachGuard = [&]() { thisThread.detach(); }; + mConnectingThreads.erase(threadId); + + if (!idValid) { + return; + } + + if (id == RPC_SESSION_ID_NEW) { + LOG_ALWAYS_FATAL_IF(mSessionIdCounter >= INT32_MAX, "Out of session IDs"); + mSessionIdCounter++; + + session = RpcSession::make(); + session->setForServer(wp<RpcServer>::fromExisting(this), mSessionIdCounter); + + mSessions[mSessionIdCounter] = session; + } else { + auto it = mSessions.find(id); + if (it == mSessions.end()) { + ALOGE("Cannot add thread, no record of session with ID %d", id); + return; + } + session = it->second; + } + + detachGuard.Disable(); + session->preJoin(std::move(thisThread)); } + // avoid strong cycle + server = nullptr; + // + // + // DO NOT ACCESS MEMBER VARIABLES BELOW + // + + session->join(std::move(clientFd)); +} + +bool RpcServer::setupSocketServer(const RpcSocketAddress& addr) { + LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str()); + LOG_ALWAYS_FATAL_IF(hasServer(), "Each RpcServer can only have one server."); + unique_fd serverFd( TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0))); if (serverFd == -1) { @@ -187,6 +253,43 @@ bool RpcServer::setupSocketServer(const RpcSocketAddress& addr) { return false; } + LOG_RPC_DETAIL("Successfully setup socket server %s", addr.toString().c_str()); + + mServer = std::move(serverFd); + return true; +} + +void RpcServer::onSessionTerminating(const sp<RpcSession>& session) { + auto id = session->mId; + LOG_ALWAYS_FATAL_IF(id == std::nullopt, "Server sessions must be initialized with ID"); + LOG_RPC_DETAIL("Dropping session %d", *id); + + std::lock_guard<std::mutex> _l(mLock); + auto it = mSessions.find(*id); + LOG_ALWAYS_FATAL_IF(it == mSessions.end(), "Bad state, unknown session id %d", *id); + LOG_ALWAYS_FATAL_IF(it->second != session, "Bad state, session has id mismatch %d", *id); + (void)mSessions.erase(it); +} + +bool RpcServer::hasServer() { + LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!"); + std::lock_guard<std::mutex> _l(mLock); + return mServer.ok(); +} + +unique_fd RpcServer::releaseServer() { + LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!"); + std::lock_guard<std::mutex> _l(mLock); + return std::move(mServer); +} + +bool RpcServer::setupExternalServer(base::unique_fd serverFd) { + LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!"); + std::lock_guard<std::mutex> _l(mLock); + if (mServer.ok()) { + ALOGE("Each RpcServer can only have one server."); + return false; + } mServer = std::move(serverFd); return true; } diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp new file mode 100644 index 0000000000..05fa49ec76 --- /dev/null +++ b/libs/binder/RpcSession.cpp @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2020 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 "RpcSession" + +#include <binder/RpcSession.h> + +#include <inttypes.h> +#include <unistd.h> + +#include <string_view> + +#include <binder/Parcel.h> +#include <binder/RpcServer.h> +#include <binder/Stability.h> +#include <utils/String8.h> + +#include "RpcSocketAddress.h" +#include "RpcState.h" +#include "RpcWireFormat.h" + +#ifdef __GLIBC__ +extern "C" pid_t gettid(); +#endif + +namespace android { + +using base::unique_fd; + +RpcSession::RpcSession() { + LOG_RPC_DETAIL("RpcSession created %p", this); + + mState = std::make_unique<RpcState>(); +} +RpcSession::~RpcSession() { + LOG_RPC_DETAIL("RpcSession destroyed %p", this); + + std::lock_guard<std::mutex> _l(mMutex); + LOG_ALWAYS_FATAL_IF(mServerConnections.size() != 0, + "Should not be able to destroy a session with servers in use."); +} + +sp<RpcSession> RpcSession::make() { + return sp<RpcSession>::make(); +} + +bool RpcSession::setupUnixDomainClient(const char* path) { + return setupSocketClient(UnixSocketAddress(path)); +} + +bool RpcSession::setupVsockClient(unsigned int cid, unsigned int port) { + return setupSocketClient(VsockSocketAddress(cid, port)); +} + +bool RpcSession::setupInetClient(const char* addr, unsigned int port) { + auto aiStart = InetSocketAddress::getAddrInfo(addr, port); + if (aiStart == nullptr) return false; + for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) { + InetSocketAddress socketAddress(ai->ai_addr, ai->ai_addrlen, addr, port); + if (setupSocketClient(socketAddress)) return true; + } + ALOGE("None of the socket address resolved for %s:%u can be added as inet client.", addr, port); + return false; +} + +bool RpcSession::addNullDebuggingClient() { + unique_fd serverFd(TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC))); + + if (serverFd == -1) { + ALOGE("Could not connect to /dev/null: %s", strerror(errno)); + return false; + } + + addClientConnection(std::move(serverFd)); + return true; +} + +sp<IBinder> RpcSession::getRootObject() { + ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT); + return state()->getRootObject(connection.fd(), sp<RpcSession>::fromExisting(this)); +} + +status_t RpcSession::getRemoteMaxThreads(size_t* maxThreads) { + ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT); + return state()->getMaxThreads(connection.fd(), sp<RpcSession>::fromExisting(this), maxThreads); +} + +status_t RpcSession::transact(const RpcAddress& address, uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags) { + ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), + (flags & IBinder::FLAG_ONEWAY) ? ConnectionUse::CLIENT_ASYNC + : ConnectionUse::CLIENT); + return state()->transact(connection.fd(), address, code, data, + sp<RpcSession>::fromExisting(this), reply, flags); +} + +status_t RpcSession::sendDecStrong(const RpcAddress& address) { + ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), + ConnectionUse::CLIENT_REFCOUNT); + return state()->sendDecStrong(connection.fd(), address); +} + +status_t RpcSession::readId() { + { + std::lock_guard<std::mutex> _l(mMutex); + LOG_ALWAYS_FATAL_IF(mForServer != nullptr, "Can only update ID for client."); + } + + int32_t id; + + ExclusiveConnection connection(sp<RpcSession>::fromExisting(this), ConnectionUse::CLIENT); + status_t status = + state()->getSessionId(connection.fd(), sp<RpcSession>::fromExisting(this), &id); + if (status != OK) return status; + + LOG_RPC_DETAIL("RpcSession %p has id %d", this, id); + mId = id; + return OK; +} + +void RpcSession::preJoin(std::thread thread) { + LOG_ALWAYS_FATAL_IF(thread.get_id() != std::this_thread::get_id(), "Must own this thread"); + + { + std::lock_guard<std::mutex> _l(mMutex); + mThreads[thread.get_id()] = std::move(thread); + } +} + +void RpcSession::join(unique_fd client) { + // must be registered to allow arbitrary client code executing commands to + // be able to do nested calls (we can't only read from it) + sp<RpcConnection> connection = assignServerToThisThread(std::move(client)); + + while (true) { + status_t error = + state()->getAndExecuteCommand(connection->fd, sp<RpcSession>::fromExisting(this)); + + if (error != OK) { + ALOGI("Binder connection thread closing w/ status %s", statusToString(error).c_str()); + break; + } + } + + LOG_ALWAYS_FATAL_IF(!removeServerConnection(connection), + "bad state: connection object guaranteed to be in list"); + + { + std::lock_guard<std::mutex> _l(mMutex); + auto it = mThreads.find(std::this_thread::get_id()); + LOG_ALWAYS_FATAL_IF(it == mThreads.end()); + it->second.detach(); + mThreads.erase(it); + } +} + +void RpcSession::terminateLocked() { + // TODO(b/185167543): + // - kindly notify other side of the connection of termination (can't be + // locked) + // - prevent new client/servers from being added + // - stop all threads which are currently reading/writing + // - terminate RpcState? + + if (mTerminated) return; + + sp<RpcServer> server = mForServer.promote(); + if (server) { + server->onSessionTerminating(sp<RpcSession>::fromExisting(this)); + } +} + +wp<RpcServer> RpcSession::server() { + return mForServer; +} + +bool RpcSession::setupSocketClient(const RpcSocketAddress& addr) { + { + std::lock_guard<std::mutex> _l(mMutex); + LOG_ALWAYS_FATAL_IF(mClientConnections.size() != 0, + "Must only setup session once, but already has %zu clients", + mClientConnections.size()); + } + + if (!setupOneSocketClient(addr, RPC_SESSION_ID_NEW)) return false; + + // TODO(b/185167543): we should add additional sessions dynamically + // instead of all at once. + // TODO(b/186470974): first risk of blocking + size_t numThreadsAvailable; + if (status_t status = getRemoteMaxThreads(&numThreadsAvailable); status != OK) { + ALOGE("Could not get max threads after initial session to %s: %s", addr.toString().c_str(), + statusToString(status).c_str()); + return false; + } + + if (status_t status = readId(); status != OK) { + ALOGE("Could not get session id after initial session to %s; %s", addr.toString().c_str(), + statusToString(status).c_str()); + return false; + } + + // we've already setup one client + for (size_t i = 0; i + 1 < numThreadsAvailable; i++) { + // TODO(b/185167543): shutdown existing connections? + if (!setupOneSocketClient(addr, mId.value())) return false; + } + + return true; +} + +bool RpcSession::setupOneSocketClient(const RpcSocketAddress& addr, int32_t id) { + for (size_t tries = 0; tries < 5; tries++) { + if (tries > 0) usleep(10000); + + unique_fd serverFd( + TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0))); + if (serverFd == -1) { + int savedErrno = errno; + ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), + strerror(savedErrno)); + return false; + } + + if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) { + if (errno == ECONNRESET) { + ALOGW("Connection reset on %s", addr.toString().c_str()); + continue; + } + int savedErrno = errno; + ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(), + strerror(savedErrno)); + return false; + } + + if (sizeof(id) != TEMP_FAILURE_RETRY(write(serverFd.get(), &id, sizeof(id)))) { + int savedErrno = errno; + ALOGE("Could not write id to socket at %s: %s", addr.toString().c_str(), + strerror(savedErrno)); + return false; + } + + LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get()); + + addClientConnection(std::move(serverFd)); + return true; + } + + ALOGE("Ran out of retries to connect to %s", addr.toString().c_str()); + return false; +} + +void RpcSession::addClientConnection(unique_fd fd) { + std::lock_guard<std::mutex> _l(mMutex); + sp<RpcConnection> session = sp<RpcConnection>::make(); + session->fd = std::move(fd); + mClientConnections.push_back(session); +} + +void RpcSession::setForServer(const wp<RpcServer>& server, int32_t sessionId) { + mId = sessionId; + mForServer = server; +} + +sp<RpcSession::RpcConnection> RpcSession::assignServerToThisThread(unique_fd fd) { + std::lock_guard<std::mutex> _l(mMutex); + sp<RpcConnection> session = sp<RpcConnection>::make(); + session->fd = std::move(fd); + session->exclusiveTid = gettid(); + mServerConnections.push_back(session); + + return session; +} + +bool RpcSession::removeServerConnection(const sp<RpcConnection>& connection) { + std::lock_guard<std::mutex> _l(mMutex); + if (auto it = std::find(mServerConnections.begin(), mServerConnections.end(), connection); + it != mServerConnections.end()) { + mServerConnections.erase(it); + if (mServerConnections.size() == 0) { + terminateLocked(); + } + return true; + } + return false; +} + +RpcSession::ExclusiveConnection::ExclusiveConnection(const sp<RpcSession>& session, + ConnectionUse use) + : mSession(session) { + pid_t tid = gettid(); + std::unique_lock<std::mutex> _l(mSession->mMutex); + + mSession->mWaitingThreads++; + while (true) { + sp<RpcConnection> exclusive; + sp<RpcConnection> available; + + // CHECK FOR DEDICATED CLIENT SOCKET + // + // A server/looper should always use a dedicated session if available + findConnection(tid, &exclusive, &available, mSession->mClientConnections, + mSession->mClientConnectionsOffset); + + // WARNING: this assumes a server cannot request its client to send + // a transaction, as mServerConnections is excluded below. + // + // Imagine we have more than one thread in play, and a single thread + // sends a synchronous, then an asynchronous command. Imagine the + // asynchronous command is sent on the first client connection. Then, if + // we naively send a synchronous command to that same connection, the + // thread on the far side might be busy processing the asynchronous + // command. So, we move to considering the second available thread + // for subsequent calls. + if (use == ConnectionUse::CLIENT_ASYNC && (exclusive != nullptr || available != nullptr)) { + mSession->mClientConnectionsOffset = + (mSession->mClientConnectionsOffset + 1) % mSession->mClientConnections.size(); + } + + // USE SERVING SOCKET (for nested transaction) + // + // asynchronous calls cannot be nested + if (use != ConnectionUse::CLIENT_ASYNC) { + // server connections are always assigned to a thread + findConnection(tid, &exclusive, nullptr /*available*/, mSession->mServerConnections, + 0 /* index hint */); + } + + // if our thread is already using a session, prioritize using that + if (exclusive != nullptr) { + mConnection = exclusive; + mReentrant = true; + break; + } else if (available != nullptr) { + mConnection = available; + mConnection->exclusiveTid = tid; + break; + } + + // in regular binder, this would usually be a deadlock :) + LOG_ALWAYS_FATAL_IF(mSession->mClientConnections.size() == 0, + "Not a client of any session. You must create a session to an " + "RPC server to make any non-nested (e.g. oneway or on another thread) " + "calls."); + + LOG_RPC_DETAIL("No available session (have %zu clients and %zu servers). Waiting...", + mSession->mClientConnections.size(), mSession->mServerConnections.size()); + mSession->mAvailableConnectionCv.wait(_l); + } + mSession->mWaitingThreads--; +} + +void RpcSession::ExclusiveConnection::findConnection(pid_t tid, sp<RpcConnection>* exclusive, + sp<RpcConnection>* available, + std::vector<sp<RpcConnection>>& sockets, + size_t socketsIndexHint) { + LOG_ALWAYS_FATAL_IF(sockets.size() > 0 && socketsIndexHint >= sockets.size(), + "Bad index %zu >= %zu", socketsIndexHint, sockets.size()); + + if (*exclusive != nullptr) return; // consistent with break below + + for (size_t i = 0; i < sockets.size(); i++) { + sp<RpcConnection>& socket = sockets[(i + socketsIndexHint) % sockets.size()]; + + // take first available session (intuition = caching) + if (available && *available == nullptr && socket->exclusiveTid == std::nullopt) { + *available = socket; + continue; + } + + // though, prefer to take session which is already inuse by this thread + // (nested transactions) + if (exclusive && socket->exclusiveTid == tid) { + *exclusive = socket; + break; // consistent with return above + } + } +} + +RpcSession::ExclusiveConnection::~ExclusiveConnection() { + // reentrant use of a session means something less deep in the call stack + // is using this fd, and it retains the right to it. So, we don't give up + // exclusive ownership, and no thread is freed. + if (!mReentrant) { + std::unique_lock<std::mutex> _l(mSession->mMutex); + mConnection->exclusiveTid = std::nullopt; + if (mSession->mWaitingThreads > 0) { + _l.unlock(); + mSession->mAvailableConnectionCv.notify_one(); + } + } +} + +} // namespace android diff --git a/libs/binder/RpcSocketAddress.h b/libs/binder/RpcSocketAddress.h index c6a06cf817..c7ba5d96a7 100644 --- a/libs/binder/RpcSocketAddress.h +++ b/libs/binder/RpcSocketAddress.h @@ -24,9 +24,7 @@ #include <sys/types.h> #include <sys/un.h> -#ifdef __BIONIC__ -#include <linux/vm_sockets.h> -#endif +#include "vm_sockets.h" namespace android { @@ -59,8 +57,6 @@ private: sockaddr_un mAddr; }; -#ifdef __BIONIC__ - class VsockSocketAddress : public RpcSocketAddress { public: VsockSocketAddress(unsigned int cid, unsigned int port) @@ -80,8 +76,6 @@ private: sockaddr_vm mAddr; }; -#endif // __BIONIC__ - class InetSocketAddress : public RpcSocketAddress { public: InetSocketAddress(const sockaddr* sockAddr, size_t size, const char* addr, unsigned int port) diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index 19dea7e607..2ba9fa2bd5 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -31,16 +31,16 @@ namespace android { RpcState::RpcState() {} RpcState::~RpcState() {} -status_t RpcState::onBinderLeaving(const sp<RpcConnection>& connection, const sp<IBinder>& binder, +status_t RpcState::onBinderLeaving(const sp<RpcSession>& session, const sp<IBinder>& binder, RpcAddress* outAddress) { bool isRemote = binder->remoteBinder(); bool isRpc = isRemote && binder->remoteBinder()->isRpcBinder(); - if (isRpc && binder->remoteBinder()->getPrivateAccessorForId().rpcConnection() != connection) { + if (isRpc && binder->remoteBinder()->getPrivateAccessorForId().rpcSession() != session) { // We need to be able to send instructions over the socket for how to // connect to a different server, and we also need to let the host // process know that this is happening. - ALOGE("Cannot send binder from unrelated binder RPC connection."); + ALOGE("Cannot send binder from unrelated binder RPC session."); return INVALID_OPERATION; } @@ -91,8 +91,7 @@ status_t RpcState::onBinderLeaving(const sp<RpcConnection>& connection, const sp return OK; } -sp<IBinder> RpcState::onBinderEntering(const sp<RpcConnection>& connection, - const RpcAddress& address) { +sp<IBinder> RpcState::onBinderEntering(const sp<RpcSession>& session, const RpcAddress& address) { std::unique_lock<std::mutex> _l(mNodeMutex); if (auto it = mNodeForAddress.find(address); it != mNodeForAddress.end()) { @@ -106,7 +105,7 @@ sp<IBinder> RpcState::onBinderEntering(const sp<RpcConnection>& connection, // We have timesRecd RPC refcounts, but we only need to hold on to one // when we keep the object. All additional dec strongs are sent // immediately, we wait to send the last one in BpBinder::onLastDecStrong. - (void)connection->sendDecStrong(address); + (void)session->sendDecStrong(address); return binder; } @@ -114,9 +113,9 @@ sp<IBinder> RpcState::onBinderEntering(const sp<RpcConnection>& connection, auto&& [it, inserted] = mNodeForAddress.insert({address, BinderNode{}}); LOG_ALWAYS_FATAL_IF(!inserted, "Failed to insert binder when creating proxy"); - // Currently, all binders are assumed to be part of the same connection (no + // Currently, all binders are assumed to be part of the same session (no // device global binders in the RPC world). - sp<IBinder> binder = BpBinder::create(connection, it->first); + sp<IBinder> binder = BpBinder::create(session, it->first); it->second.binder = binder; it->second.timesRecd = 1; return binder; @@ -183,6 +182,27 @@ void RpcState::terminate() { } } +RpcState::CommandData::CommandData(size_t size) : mSize(size) { + // The maximum size for regular binder is 1MB for all concurrent + // transactions. A very small proportion of transactions are even + // larger than a page, but we need to avoid allocating too much + // data on behalf of an arbitrary client, or we could risk being in + // a position where a single additional allocation could run out of + // memory. + // + // Note, this limit may not reflect the total amount of data allocated for a + // transaction (in some cases, additional fixed size amounts are added), + // though for rough consistency, we should avoid cases where this data type + // is used for multiple dynamic allocations for a single transaction. + constexpr size_t kMaxTransactionAllocation = 100 * 1000; + if (size == 0) return; + if (size > kMaxTransactionAllocation) { + ALOGW("Transaction requested too much data allocation %zu", size); + return; + } + mData.reset(new (std::nothrow) uint8_t[size]); +} + bool RpcState::rpcSend(const base::unique_fd& fd, const char* what, const void* data, size_t size) { LOG_RPC_DETAIL("Sending %s on fd %d: %s", what, fd.get(), hexString(data, size).c_str()); @@ -232,14 +252,13 @@ bool RpcState::rpcRec(const base::unique_fd& fd, const char* what, void* data, s return true; } -sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd, - const sp<RpcConnection>& connection) { +sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd, const sp<RpcSession>& session) { Parcel data; - data.markForRpc(connection); + data.markForRpc(session); Parcel reply; - status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, data, - connection, &reply, 0); + status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_ROOT, data, session, + &reply, 0); if (status != OK) { ALOGE("Error getting root object: %s", statusToString(status).c_str()); return nullptr; @@ -248,14 +267,14 @@ sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd, return reply.readStrongBinder(); } -status_t RpcState::getMaxThreads(const base::unique_fd& fd, const sp<RpcConnection>& connection, +status_t RpcState::getMaxThreads(const base::unique_fd& fd, const sp<RpcSession>& session, size_t* maxThreadsOut) { Parcel data; - data.markForRpc(connection); + data.markForRpc(session); Parcel reply; status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_MAX_THREADS, data, - connection, &reply, 0); + session, &reply, 0); if (status != OK) { ALOGE("Error getting max threads: %s", statusToString(status).c_str()); return status; @@ -273,29 +292,29 @@ status_t RpcState::getMaxThreads(const base::unique_fd& fd, const sp<RpcConnecti return OK; } -status_t RpcState::getConnectionId(const base::unique_fd& fd, const sp<RpcConnection>& connection, - int32_t* connectionIdOut) { +status_t RpcState::getSessionId(const base::unique_fd& fd, const sp<RpcSession>& session, + int32_t* sessionIdOut) { Parcel data; - data.markForRpc(connection); + data.markForRpc(session); Parcel reply; - status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_CONNECTION_ID, data, - connection, &reply, 0); + status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_SESSION_ID, data, + session, &reply, 0); if (status != OK) { - ALOGE("Error getting connection ID: %s", statusToString(status).c_str()); + ALOGE("Error getting session ID: %s", statusToString(status).c_str()); return status; } - int32_t connectionId; - status = reply.readInt32(&connectionId); + int32_t sessionId; + status = reply.readInt32(&sessionId); if (status != OK) return status; - *connectionIdOut = connectionId; + *sessionIdOut = sessionId; return OK; } status_t RpcState::transact(const base::unique_fd& fd, const RpcAddress& address, uint32_t code, - const Parcel& data, const sp<RpcConnection>& connection, Parcel* reply, + const Parcel& data, const sp<RpcSession>& session, Parcel* reply, uint32_t flags) { uint64_t asyncNumber = 0; @@ -328,7 +347,11 @@ status_t RpcState::transact(const base::unique_fd& fd, const RpcAddress& address .asyncNumber = asyncNumber, }; - std::vector<uint8_t> transactionData(sizeof(RpcWireTransaction) + data.dataSize()); + CommandData transactionData(sizeof(RpcWireTransaction) + data.dataSize()); + if (!transactionData.valid()) { + return NO_MEMORY; + } + memcpy(transactionData.data() + 0, &transaction, sizeof(RpcWireTransaction)); memcpy(transactionData.data() + sizeof(RpcWireTransaction), data.data(), data.dataSize()); @@ -355,7 +378,7 @@ status_t RpcState::transact(const base::unique_fd& fd, const RpcAddress& address LOG_ALWAYS_FATAL_IF(reply == nullptr, "Reply parcel must be used for synchronous transaction."); - return waitForReply(fd, connection, reply); + return waitForReply(fd, session, reply); } static void cleanup_reply_data(Parcel* p, const uint8_t* data, size_t dataSize, @@ -367,7 +390,7 @@ static void cleanup_reply_data(Parcel* p, const uint8_t* data, size_t dataSize, LOG_ALWAYS_FATAL_IF(objectsCount, 0); } -status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcConnection>& connection, +status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcSession>& session, Parcel* reply) { RpcWireHeader command; while (true) { @@ -377,13 +400,16 @@ status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcConnectio if (command.command == RPC_COMMAND_REPLY) break; - status_t status = processServerCommand(fd, connection, command); + status_t status = processServerCommand(fd, session, command); if (status != OK) return status; } - uint8_t* data = new uint8_t[command.bodySize]; + CommandData data(command.bodySize); + if (!data.valid()) { + return NO_MEMORY; + } - if (!rpcRec(fd, "reply body", data, command.bodySize)) { + if (!rpcRec(fd, "reply body", data.data(), command.bodySize)) { return DEAD_OBJECT; } @@ -393,13 +419,14 @@ status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcConnectio terminate(); return BAD_VALUE; } - RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data); + RpcWireReply* rpcReply = reinterpret_cast<RpcWireReply*>(data.data()); if (rpcReply->status != OK) return rpcReply->status; + data.release(); reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data), nullptr, 0, cleanup_reply_data); - reply->markForRpc(connection); + reply->markForRpc(session); return OK; } @@ -430,8 +457,7 @@ status_t RpcState::sendDecStrong(const base::unique_fd& fd, const RpcAddress& ad return OK; } -status_t RpcState::getAndExecuteCommand(const base::unique_fd& fd, - const sp<RpcConnection>& connection) { +status_t RpcState::getAndExecuteCommand(const base::unique_fd& fd, const sp<RpcSession>& session) { LOG_RPC_DETAIL("getAndExecuteCommand on fd %d", fd.get()); RpcWireHeader command; @@ -439,15 +465,14 @@ status_t RpcState::getAndExecuteCommand(const base::unique_fd& fd, return DEAD_OBJECT; } - return processServerCommand(fd, connection, command); + return processServerCommand(fd, session, command); } -status_t RpcState::processServerCommand(const base::unique_fd& fd, - const sp<RpcConnection>& connection, +status_t RpcState::processServerCommand(const base::unique_fd& fd, const sp<RpcSession>& session, const RpcWireHeader& command) { switch (command.command) { case RPC_COMMAND_TRANSACT: - return processTransact(fd, connection, command); + return processTransact(fd, session, command); case RPC_COMMAND_DEC_STRONG: return processDecStrong(fd, command); } @@ -456,21 +481,24 @@ status_t RpcState::processServerCommand(const base::unique_fd& fd, // RPC-binder-level wire protocol is not self synchronizing, we have no way // to understand where the current command ends and the next one begins. We // also can't consider it a fatal error because this would allow any client - // to kill us, so ending the connection for misbehaving client. - ALOGE("Unknown RPC command %d - terminating connection", command.command); + // to kill us, so ending the session for misbehaving client. + ALOGE("Unknown RPC command %d - terminating session", command.command); terminate(); return DEAD_OBJECT; } -status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcConnection>& connection, +status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcSession>& session, const RpcWireHeader& command) { LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_TRANSACT, "command: %d", command.command); - std::vector<uint8_t> transactionData(command.bodySize); + CommandData transactionData(command.bodySize); + if (!transactionData.valid()) { + return NO_MEMORY; + } if (!rpcRec(fd, "transaction body", transactionData.data(), transactionData.size())) { return DEAD_OBJECT; } - return processTransactInternal(fd, connection, std::move(transactionData)); + return processTransactInternal(fd, session, std::move(transactionData)); } static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t dataSize, @@ -482,9 +510,8 @@ static void do_nothing_to_transact_data(Parcel* p, const uint8_t* data, size_t d (void)objectsCount; } -status_t RpcState::processTransactInternal(const base::unique_fd& fd, - const sp<RpcConnection>& connection, - std::vector<uint8_t>&& transactionData) { +status_t RpcState::processTransactInternal(const base::unique_fd& fd, const sp<RpcSession>& session, + CommandData transactionData) { if (transactionData.size() < sizeof(RpcWireTransaction)) { ALOGE("Expecting %zu but got %zu bytes for RpcWireTransaction. Terminating!", sizeof(RpcWireTransaction), transactionData.size()); @@ -505,7 +532,6 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd, auto it = mNodeForAddress.find(addr); if (it == mNodeForAddress.end()) { ALOGE("Unknown binder address %s.", addr.toString().c_str()); - dump(); replyStatus = BAD_VALUE; } else { target = it->second.binder.promote(); @@ -515,7 +541,7 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd, // However, for local binders, it indicates a misbehaving client // (any binder which is being transacted on should be holding a // strong ref count), so in either case, terminating the - // connection. + // session. ALOGE("While transacting, binder has been deleted at address %s. Terminating!", addr.toString().c_str()); terminate(); @@ -545,7 +571,7 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd, } Parcel reply; - reply.markForRpc(connection); + reply.markForRpc(session); if (replyStatus == OK) { Parcel data; @@ -556,14 +582,14 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd, transactionData.size() - offsetof(RpcWireTransaction, data), nullptr /*object*/, 0 /*objectCount*/, do_nothing_to_transact_data); - data.markForRpc(connection); + data.markForRpc(session); if (target) { replyStatus = target->transact(transaction->code, data, &reply, transaction->flags); } else { LOG_RPC_DETAIL("Got special transaction %u", transaction->code); - sp<RpcServer> server = connection->server().promote(); + sp<RpcServer> server = session->server().promote(); if (server) { // special case for 'zero' address (special server commands) switch (transaction->code) { @@ -575,13 +601,13 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd, replyStatus = reply.writeInt32(server->getMaxThreads()); break; } - case RPC_SPECIAL_TRANSACT_GET_CONNECTION_ID: { - // only connections w/ services can be the source of a - // connection ID (so still guarded by non-null server) + case RPC_SPECIAL_TRANSACT_GET_SESSION_ID: { + // only sessions w/ services can be the source of a + // session ID (so still guarded by non-null server) // - // connections associated with servers must have an ID + // sessions associated with servers must have an ID // (hence abort) - int32_t id = connection->getPrivateAccessorForId().get().value(); + int32_t id = session->getPrivateAccessorForId().get().value(); replyStatus = reply.writeInt32(id); break; } @@ -635,11 +661,11 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd, // justification for const_cast (consider avoiding priority_queue): // - AsyncTodo operator< doesn't depend on 'data' object // - gotta go fast - std::vector<uint8_t> data = std::move( + CommandData data = std::move( const_cast<BinderNode::AsyncTodo&>(it->second.asyncTodo.top()).data); it->second.asyncTodo.pop(); _l.unlock(); - return processTransactInternal(fd, connection, std::move(data)); + return processTransactInternal(fd, session, std::move(data)); } } return OK; @@ -649,7 +675,10 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd, .status = replyStatus, }; - std::vector<uint8_t> replyData(sizeof(RpcWireReply) + reply.dataSize()); + CommandData replyData(sizeof(RpcWireReply) + reply.dataSize()); + if (!replyData.valid()) { + return NO_MEMORY; + } memcpy(replyData.data() + 0, &rpcReply, sizeof(RpcWireReply)); memcpy(replyData.data() + sizeof(RpcWireReply), reply.data(), reply.dataSize()); @@ -676,7 +705,10 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd, status_t RpcState::processDecStrong(const base::unique_fd& fd, const RpcWireHeader& command) { LOG_ALWAYS_FATAL_IF(command.command != RPC_COMMAND_DEC_STRONG, "command: %d", command.command); - std::vector<uint8_t> commandData(command.bodySize); + CommandData commandData(command.bodySize); + if (!commandData.valid()) { + return NO_MEMORY; + } if (!rpcRec(fd, "dec ref body", commandData.data(), commandData.size())) { return DEAD_OBJECT; } @@ -695,7 +727,6 @@ status_t RpcState::processDecStrong(const base::unique_fd& fd, const RpcWireHead auto it = mNodeForAddress.find(addr); if (it == mNodeForAddress.end()) { ALOGE("Unknown binder address %s for dec strong.", addr.toString().c_str()); - dump(); return OK; } @@ -728,7 +759,7 @@ status_t RpcState::processDecStrong(const base::unique_fd& fd, const RpcWireHead } _l.unlock(); - tempHold = nullptr; // destructor may make binder calls on this connection + tempHold = nullptr; // destructor may make binder calls on this session return OK; } diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h index 825fd7c4e5..31f8a22065 100644 --- a/libs/binder/RpcState.h +++ b/libs/binder/RpcState.h @@ -18,9 +18,10 @@ #include <android-base/unique_fd.h> #include <binder/IBinder.h> #include <binder/Parcel.h> -#include <binder/RpcConnection.h> +#include <binder/RpcSession.h> #include <map> +#include <optional> #include <queue> namespace android { @@ -43,7 +44,7 @@ struct RpcWireHeader; /** * Abstracts away management of ref counts and the wire format from - * RpcConnection + * RpcSession */ class RpcState { public: @@ -51,71 +52,83 @@ public: ~RpcState(); // TODO(b/182940634): combine some special transactions into one "getServerInfo" call? - sp<IBinder> getRootObject(const base::unique_fd& fd, const sp<RpcConnection>& connection); - status_t getMaxThreads(const base::unique_fd& fd, const sp<RpcConnection>& connection, + sp<IBinder> getRootObject(const base::unique_fd& fd, const sp<RpcSession>& session); + status_t getMaxThreads(const base::unique_fd& fd, const sp<RpcSession>& session, size_t* maxThreadsOut); - status_t getConnectionId(const base::unique_fd& fd, const sp<RpcConnection>& connection, - int32_t* connectionIdOut); + status_t getSessionId(const base::unique_fd& fd, const sp<RpcSession>& session, + int32_t* sessionIdOut); [[nodiscard]] status_t transact(const base::unique_fd& fd, const RpcAddress& address, uint32_t code, const Parcel& data, - const sp<RpcConnection>& connection, Parcel* reply, - uint32_t flags); + const sp<RpcSession>& session, Parcel* reply, uint32_t flags); [[nodiscard]] status_t sendDecStrong(const base::unique_fd& fd, const RpcAddress& address); [[nodiscard]] status_t getAndExecuteCommand(const base::unique_fd& fd, - const sp<RpcConnection>& connection); + const sp<RpcSession>& session); /** * Called by Parcel for outgoing binders. This implies one refcount of * ownership to the outgoing binder. */ - [[nodiscard]] status_t onBinderLeaving(const sp<RpcConnection>& connection, - const sp<IBinder>& binder, RpcAddress* outAddress); + [[nodiscard]] status_t onBinderLeaving(const sp<RpcSession>& session, const sp<IBinder>& binder, + RpcAddress* outAddress); /** * Called by Parcel for incoming binders. This either returns the refcount * to the process, if this process already has one, or it takes ownership of * that refcount */ - sp<IBinder> onBinderEntering(const sp<RpcConnection>& connection, const RpcAddress& address); + sp<IBinder> onBinderEntering(const sp<RpcSession>& session, const RpcAddress& address); size_t countBinders(); void dump(); private: /** - * Called when reading or writing data to a connection fails to clean up - * data associated with the connection in order to cleanup binders. + * Called when reading or writing data to a session fails to clean up + * data associated with the session in order to cleanup binders. * Specifically, we have a strong dependency cycle, since BpBinder is * OBJECT_LIFETIME_WEAK (so that onAttemptIncStrong may return true). * - * BpBinder -> RpcConnection -> RpcState + * BpBinder -> RpcSession -> RpcState * ^-----------------------------/ * * In the success case, eventually all refcounts should be propagated over - * the connection, though this could also be called to eagerly cleanup - * the connection. + * the session, though this could also be called to eagerly cleanup + * the session. * - * WARNING: RpcState is responsible for calling this when the connection is + * WARNING: RpcState is responsible for calling this when the session is * no longer recoverable. */ void terminate(); + // Alternative to std::vector<uint8_t> that doesn't abort on allocation failure and caps + // large allocations to avoid being requested from allocating too much data. + struct CommandData { + explicit CommandData(size_t size); + bool valid() { return mSize == 0 || mData != nullptr; } + size_t size() { return mSize; } + uint8_t* data() { return mData.get(); } + uint8_t* release() { return mData.release(); } + + private: + std::unique_ptr<uint8_t[]> mData; + size_t mSize; + }; + [[nodiscard]] bool rpcSend(const base::unique_fd& fd, const char* what, const void* data, size_t size); [[nodiscard]] bool rpcRec(const base::unique_fd& fd, const char* what, void* data, size_t size); - [[nodiscard]] status_t waitForReply(const base::unique_fd& fd, - const sp<RpcConnection>& connection, Parcel* reply); + [[nodiscard]] status_t waitForReply(const base::unique_fd& fd, const sp<RpcSession>& session, + Parcel* reply); [[nodiscard]] status_t processServerCommand(const base::unique_fd& fd, - const sp<RpcConnection>& connection, + const sp<RpcSession>& session, const RpcWireHeader& command); - [[nodiscard]] status_t processTransact(const base::unique_fd& fd, - const sp<RpcConnection>& connection, + [[nodiscard]] status_t processTransact(const base::unique_fd& fd, const sp<RpcSession>& session, const RpcWireHeader& command); [[nodiscard]] status_t processTransactInternal(const base::unique_fd& fd, - const sp<RpcConnection>& connection, - std::vector<uint8_t>&& transactionData); + const sp<RpcSession>& session, + CommandData transactionData); [[nodiscard]] status_t processDecStrong(const base::unique_fd& fd, const RpcWireHeader& command); @@ -150,7 +163,7 @@ private: // async transaction queue, _only_ for local binder struct AsyncTodo { - std::vector<uint8_t> data; // most convenient format, to move it here + CommandData data; uint64_t asyncNumber = 0; bool operator<(const AsyncTodo& o) const { @@ -168,7 +181,7 @@ private: std::mutex mNodeMutex; bool mTerminated = false; - // binders known by both sides of a connection + // binders known by both sides of a session std::map<RpcAddress, BinderNode> mNodeForAddress; }; diff --git a/libs/binder/RpcWireFormat.h b/libs/binder/RpcWireFormat.h index 56af0d3ab5..c5fa008308 100644 --- a/libs/binder/RpcWireFormat.h +++ b/libs/binder/RpcWireFormat.h @@ -48,9 +48,11 @@ enum : uint32_t { enum : uint32_t { RPC_SPECIAL_TRANSACT_GET_ROOT = 0, RPC_SPECIAL_TRANSACT_GET_MAX_THREADS = 1, - RPC_SPECIAL_TRANSACT_GET_CONNECTION_ID = 2, + RPC_SPECIAL_TRANSACT_GET_SESSION_ID = 2, }; +constexpr int32_t RPC_SESSION_ID_NEW = -1; + // serialization is like: // |RpcWireHeader|struct desginated by 'command'| (over and over again) diff --git a/libs/binder/aidl/android/content/pm/OWNERS b/libs/binder/aidl/android/content/pm/OWNERS index b99ca09674..31005184bf 100644 --- a/libs/binder/aidl/android/content/pm/OWNERS +++ b/libs/binder/aidl/android/content/pm/OWNERS @@ -1,4 +1,5 @@ narayan@google.com patb@google.com svetoslavganov@google.com -toddke@google.com
\ No newline at end of file +toddke@google.com +patb@google.com
\ No newline at end of file diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/binder_module.h index 151235c16b..9dea3b448b 100644 --- a/libs/binder/include/private/binder/binder_module.h +++ b/libs/binder/binder_module.h @@ -29,14 +29,14 @@ #undef B_PACK_CHARS #endif -#include <sys/ioctl.h> #include <linux/android/binder.h> +#include <sys/ioctl.h> #ifndef BR_FROZEN_REPLY // Temporary definition of BR_FROZEN_REPLY. For production // this will come from UAPI binder.h #define BR_FROZEN_REPLY _IO('r', 18) -#endif //BR_FROZEN_REPLY +#endif // BR_FROZEN_REPLY #ifndef BINDER_FREEZE /* @@ -49,46 +49,46 @@ struct binder_freeze_info { // // Group-leader PID of process to be frozen // - uint32_t pid; + uint32_t pid; // // Enable(1) / Disable(0) freeze for given PID // - uint32_t enable; + uint32_t enable; // // Timeout to wait for transactions to drain. // 0: don't wait (ioctl will return EAGAIN if not drained) // N: number of ms to wait - uint32_t timeout_ms; + uint32_t timeout_ms; }; -#endif //BINDER_FREEZE +#endif // BINDER_FREEZE #ifndef BINDER_GET_FROZEN_INFO -#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info) +#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info) struct binder_frozen_status_info { // // Group-leader PID of process to be queried // - __u32 pid; + __u32 pid; // // Indicates whether the process has received any sync calls since last // freeze (cleared at freeze/unfreeze) // - __u32 sync_recv; + __u32 sync_recv; // // Indicates whether the process has received any async calls since last // freeze (cleared at freeze/unfreeze) // - __u32 async_recv; + __u32 async_recv; }; -#endif //BINDER_GET_FROZEN_INFO +#endif // BINDER_GET_FROZEN_INFO #ifndef BR_ONEWAY_SPAM_SUSPECT // Temporary definition of BR_ONEWAY_SPAM_SUSPECT. For production // this will come from UAPI binder.h #define BR_ONEWAY_SPAM_SUSPECT _IO('r', 19) -#endif //BR_ONEWAY_SPAM_SUSPECT +#endif // BR_ONEWAY_SPAM_SUSPECT #ifndef BINDER_ENABLE_ONEWAY_SPAM_DETECTION /* @@ -96,6 +96,6 @@ struct binder_frozen_status_info { * these will be defined in the UAPI binder.h file from upstream kernel. */ #define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32) -#endif //BINDER_ENABLE_ONEWAY_SPAM_DETECTION +#endif // BINDER_ENABLE_ONEWAY_SPAM_DETECTION #endif // _BINDER_MODULE_H_ diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index ad618f9de4..61bf018e43 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -28,7 +28,7 @@ // --------------------------------------------------------------------------- namespace android { -class RpcConnection; +class RpcSession; class RpcState; namespace internal { class Stability; @@ -41,11 +41,11 @@ class BpBinder : public IBinder { public: static sp<BpBinder> create(int32_t handle); - static sp<BpBinder> create(const sp<RpcConnection>& connection, const RpcAddress& address); + static sp<BpBinder> create(const sp<RpcSession>& session, const RpcAddress& address); /** * Return value: - * true - this is associated with a socket RpcConnection + * true - this is associated with a socket RpcSession * false - (usual) binder over e.g. /dev/binder */ bool isRpcBinder() const; @@ -133,7 +133,7 @@ public: // valid if isRpcBinder const RpcAddress& rpcAddress() const { return mBinder->rpcAddress(); } - const sp<RpcConnection>& rpcConnection() const { return mBinder->rpcConnection(); } + const sp<RpcSession>& rpcSession() const { return mBinder->rpcSession(); } const BpBinder* mBinder; }; @@ -148,19 +148,19 @@ private: struct BinderHandle { int32_t handle; }; - struct SocketHandle { - sp<RpcConnection> connection; + struct RpcHandle { + sp<RpcSession> session; RpcAddress address; }; - using Handle = std::variant<BinderHandle, SocketHandle>; + using Handle = std::variant<BinderHandle, RpcHandle>; int32_t binderHandle() const; const RpcAddress& rpcAddress() const; - const sp<RpcConnection>& rpcConnection() const; + const sp<RpcSession>& rpcSession() const; explicit BpBinder(Handle&& handle); BpBinder(BinderHandle&& handle, int32_t trackedUid); - explicit BpBinder(SocketHandle&& handle); + explicit BpBinder(RpcHandle&& handle); virtual ~BpBinder(); virtual void onFirstRef(); diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index 23a0cb0148..5d04ebedae 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -212,6 +212,7 @@ private: // Whether the work source should be propagated. bool mPropagateWorkSource; bool mIsLooper; + bool mIsFlushing; int32_t mStrictModePolicy; int32_t mLastTransactionBinderFlags; CallRestriction mCallRestriction; diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h index 96597320f6..f3ba830923 100644 --- a/libs/binder/include/binder/LazyServiceRegistrar.h +++ b/libs/binder/include/binder/LazyServiceRegistrar.h @@ -50,8 +50,12 @@ class LazyServiceRegistrar { int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT); /** * Force the service to persist, even when it has 0 clients. - * If setting this flag from the server side, make sure to do so before calling registerService, - * or there may be a race with the default dynamic shutdown. + * If setting this flag from the server side, make sure to do so before calling + * registerService, or there may be a race with the default dynamic shutdown. + * + * This should only be used if it is every eventually set to false. If a + * service needs to persist but doesn't need to dynamically shut down, + * prefer to control it with another mechanism such as ctl.start. */ void forcePersist(bool persist); diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 957837233b..5aaaa0c3d2 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -50,7 +50,7 @@ template <typename T> class LightFlattenable; class IBinder; class IPCThreadState; class ProcessState; -class RpcConnection; +class RpcSession; class String8; class TextOutput; @@ -103,7 +103,7 @@ public: // Whenever possible, markForBinder should be preferred. This method is // called automatically on reply Parcels for RPC transactions. - void markForRpc(const sp<RpcConnection>& connection); + void markForRpc(const sp<RpcSession>& session); // Whether this Parcel is written for RPC transactions (after calls to // markForBinder or markForRpc). @@ -1136,7 +1136,7 @@ private: release_func mOwner; - sp<RpcConnection> mConnection; + sp<RpcSession> mSession; class Blob { public: diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h index 5535d8a87c..8f0c6fd5e1 100644 --- a/libs/binder/include/binder/RpcServer.h +++ b/libs/binder/include/binder/RpcServer.h @@ -17,11 +17,12 @@ #include <android-base/unique_fd.h> #include <binder/IBinder.h> -#include <binder/RpcConnection.h> +#include <binder/RpcSession.h> #include <utils/Errors.h> #include <utils/RefBase.h> #include <mutex> +#include <thread> // WARNING: This is a feature which is still in development, and it is subject // to radical change. Any production use of this may subject your code to any @@ -48,21 +49,19 @@ public: static sp<RpcServer> make(); /** - * This represents a connection for responses, e.g.: + * This represents a session for responses, e.g.: * * process A serves binder a - * process B opens a connection to process A + * process B opens a session to process A * process B makes binder b and sends it to A - * A uses this 'back connection' to send things back to B + * A uses this 'back session' to send things back to B */ [[nodiscard]] bool setupUnixDomainServer(const char* path); -#ifdef __BIONIC__ /** * Creates an RPC server at the current port. */ [[nodiscard]] bool setupVsockServer(unsigned int port); -#endif // __BIONIC__ /** * Creates an RPC server at the current port using IPv4. @@ -75,10 +74,26 @@ public: */ [[nodiscard]] bool setupInetServer(unsigned int port, unsigned int* assignedPort); + /** + * If setup*Server has been successful, return true. Otherwise return false. + */ + [[nodiscard]] bool hasServer(); + + /** + * If hasServer(), return the server FD. Otherwise return invalid FD. + */ + [[nodiscard]] base::unique_fd releaseServer(); + + /** + * Set up server using an external FD previously set up by releaseServer(). + * Return false if there's already a server. + */ + bool setupExternalServer(base::unique_fd serverFd); + void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction(); /** - * This must be called before adding a client connection. + * This must be called before adding a client session. * * If this is not specified, this will be a single-threaded server. * @@ -91,36 +106,59 @@ public: /** * The root object can be retrieved by any client, without any * authentication. TODO(b/183988761) + * + * Holds a strong reference to the root object. */ void setRootObject(const sp<IBinder>& binder); + /** + * Holds a weak reference to the root object. + */ + void setRootObjectWeak(const wp<IBinder>& binder); sp<IBinder> getRootObject(); /** - * You must have at least one client connection before calling this. + * You must have at least one client session before calling this. + * + * TODO(b/185167543): way to shut down? */ void join(); /** + * Accept one connection on this server. You must have at least one client + * session before calling this. + */ + [[nodiscard]] bool acceptOne(); + + /** * For debugging! */ - std::vector<sp<RpcConnection>> listConnections(); + std::vector<sp<RpcSession>> listSessions(); + size_t numUninitializedSessions(); ~RpcServer(); + // internal use only + + void onSessionTerminating(const sp<RpcSession>& session); + private: friend sp<RpcServer>; RpcServer(); + void establishConnection(sp<RpcServer>&& session, base::unique_fd clientFd); bool setupSocketServer(const RpcSocketAddress& address); bool mAgreedExperimental = false; bool mStarted = false; // TODO(b/185167543): support dynamically added clients size_t mMaxThreads = 1; - base::unique_fd mServer; // socket we are accepting connections on + base::unique_fd mServer; // socket we are accepting sessions on std::mutex mLock; // for below + std::map<std::thread::id, std::thread> mConnectingThreads; sp<IBinder> mRootObject; - sp<RpcConnection> mConnection; + wp<IBinder> mRootObjectWeak; + std::map<int32_t, sp<RpcSession>> mSessions; + int32_t mSessionIdCounter = 0; }; } // namespace android diff --git a/libs/binder/include/binder/RpcConnection.h b/libs/binder/include/binder/RpcSession.h index 7e31e8a4fc..bcc213c8bd 100644 --- a/libs/binder/include/binder/RpcConnection.h +++ b/libs/binder/include/binder/RpcSession.h @@ -21,7 +21,9 @@ #include <utils/Errors.h> #include <utils/RefBase.h> +#include <map> #include <optional> +#include <thread> #include <vector> // WARNING: This is a feature which is still in development, and it is subject @@ -36,12 +38,13 @@ class RpcSocketAddress; class RpcState; /** - * This represents a multi-threaded/multi-socket connection between a client - * and a server. + * This represents a session (group of connections) between a client + * and a server. Multiple connections are needed for multiple parallel "binder" + * calls which may also have nested calls. */ -class RpcConnection final : public virtual RefBase { +class RpcSession final : public virtual RefBase { public: - static sp<RpcConnection> make(); + static sp<RpcSession> make(); /** * This should be called once per thread, matching 'join' in the remote @@ -49,12 +52,10 @@ public: */ [[nodiscard]] bool setupUnixDomainClient(const char* path); -#ifdef __BIONIC__ /** * Connects to an RPC server at the CVD & port. */ [[nodiscard]] bool setupVsockClient(unsigned int cvd, unsigned int port); -#endif // __BIONIC__ /** * Connects to an RPC server at the given address and port. @@ -64,29 +65,29 @@ public: /** * For debugging! * - * Sets up an empty socket. All queries to this socket which require a + * Sets up an empty connection. All queries to this connection which require a * response will never be satisfied. All data sent here will be * unceremoniously cast down the bottomless pit, /dev/null. */ [[nodiscard]] bool addNullDebuggingClient(); /** - * Query the other side of the connection for the root object hosted by that + * Query the other side of the session for the root object hosted by that * process's RpcServer (if one exists) */ sp<IBinder> getRootObject(); /** - * Query the other side of the connection for the maximum number of threads + * Query the other side of the session for the maximum number of threads * it supports (maximum number of concurrent non-nested synchronous transactions) */ - status_t getMaxThreads(size_t* maxThreads); + status_t getRemoteMaxThreads(size_t* maxThreads); [[nodiscard]] status_t transact(const RpcAddress& address, uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); [[nodiscard]] status_t sendDecStrong(const RpcAddress& address); - ~RpcConnection(); + ~RpcSession(); wp<RpcServer> server(); @@ -95,27 +96,31 @@ public: class PrivateAccessorForId { private: - friend class RpcConnection; + friend class RpcSession; friend class RpcState; - explicit PrivateAccessorForId(const RpcConnection* connection) : mConnection(connection) {} + explicit PrivateAccessorForId(const RpcSession* session) : mSession(session) {} - const std::optional<int32_t> get() { return mConnection->mId; } + const std::optional<int32_t> get() { return mSession->mId; } - const RpcConnection* mConnection; + const RpcSession* mSession; }; PrivateAccessorForId getPrivateAccessorForId() const { return PrivateAccessorForId(this); } private: friend PrivateAccessorForId; - friend sp<RpcConnection>; + friend sp<RpcSession>; friend RpcServer; - RpcConnection(); + RpcSession(); status_t readId(); + // transfer ownership of thread + void preJoin(std::thread thread); + // join on thread passed to preJoin void join(base::unique_fd client); + void terminateLocked(); - struct ConnectionSocket : public RefBase { + struct RpcConnection : public RefBase { base::unique_fd fd; // whether this or another thread is currently using this fd to make @@ -124,32 +129,33 @@ private: }; bool setupSocketClient(const RpcSocketAddress& address); - bool setupOneSocketClient(const RpcSocketAddress& address); - void addClient(base::unique_fd fd); - void setForServer(const wp<RpcServer>& server, int32_t connectionId); - sp<ConnectionSocket> assignServerToThisThread(base::unique_fd fd); - bool removeServerSocket(const sp<ConnectionSocket>& socket); + bool setupOneSocketClient(const RpcSocketAddress& address, int32_t sessionId); + void addClientConnection(base::unique_fd fd); + void setForServer(const wp<RpcServer>& server, int32_t sessionId); + sp<RpcConnection> assignServerToThisThread(base::unique_fd fd); + bool removeServerConnection(const sp<RpcConnection>& connection); - enum class SocketUse { + enum class ConnectionUse { CLIENT, CLIENT_ASYNC, CLIENT_REFCOUNT, }; - // RAII object for connection socket - class ExclusiveSocket { + // RAII object for session connection + class ExclusiveConnection { public: - explicit ExclusiveSocket(const sp<RpcConnection>& connection, SocketUse use); - ~ExclusiveSocket(); - const base::unique_fd& fd() { return mSocket->fd; } + explicit ExclusiveConnection(const sp<RpcSession>& session, ConnectionUse use); + ~ExclusiveConnection(); + const base::unique_fd& fd() { return mConnection->fd; } private: - static void findSocket(pid_t tid, sp<ConnectionSocket>* exclusive, - sp<ConnectionSocket>* available, - std::vector<sp<ConnectionSocket>>& sockets, size_t socketsIndexHint); + static void findConnection(pid_t tid, sp<RpcConnection>* exclusive, + sp<RpcConnection>* available, + std::vector<sp<RpcConnection>>& sockets, + size_t socketsIndexHint); - sp<RpcConnection> mConnection; // avoid deallocation - sp<ConnectionSocket> mSocket; + sp<RpcSession> mSession; // avoid deallocation + sp<RpcConnection> mConnection; // whether this is being used for a nested transaction (being on the same // thread guarantees we won't write in the middle of a message, the way @@ -157,13 +163,13 @@ private: bool mReentrant = false; }; - // On the other side of a connection, for each of mClients here, there should - // be one of mServers on the other side (and vice versa). + // On the other side of a session, for each of mClientConnections here, there should + // be one of mServerConnections on the other side (and vice versa). // - // For the simplest connection, a single server with one client, you would + // For the simplest session, a single server with one client, you would // have: - // - the server has a single 'mServers' and a thread listening on this - // - the client has a single 'mClients' and makes calls to this + // - the server has a single 'mServerConnections' and a thread listening on this + // - the client has a single 'mClientConnections' and makes calls to this // - here, when the client makes a call, the server can call back into it // (nested calls), but outside of this, the client will only ever read // calls from the server when it makes a call itself. @@ -171,19 +177,28 @@ private: // For a more complicated case, the client might itself open up a thread to // serve calls to the server at all times (e.g. if it hosts a callback) - wp<RpcServer> mForServer; // maybe null, for client connections + wp<RpcServer> mForServer; // maybe null, for client sessions // TODO(b/183988761): this shouldn't be guessable std::optional<int32_t> mId; std::unique_ptr<RpcState> mState; - std::mutex mSocketMutex; // for all below - std::condition_variable mSocketCv; // for mWaitingThreads + std::mutex mMutex; // for all below + + std::condition_variable mAvailableConnectionCv; // for mWaitingThreads size_t mWaitingThreads = 0; - size_t mClientsOffset = 0; // hint index into clients, ++ when sending an async transaction - std::vector<sp<ConnectionSocket>> mClients; - std::vector<sp<ConnectionSocket>> mServers; + // hint index into clients, ++ when sending an async transaction + size_t mClientConnectionsOffset = 0; + std::vector<sp<RpcConnection>> mClientConnections; + std::vector<sp<RpcConnection>> mServerConnections; + + // TODO(b/185167543): use for reverse sessions (allow client to also + // serve calls on a session). + // TODO(b/185167543): allow sharing between different sessions in a + // process? (or combine with mServerConnections) + std::map<std::thread::id, std::thread> mThreads; + bool mTerminated = false; }; } // namespace android diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index 9e2050b411..78f2d3af14 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -150,6 +150,11 @@ typedef void (*AIBinder_Class_onDestroy)(void* userData); /** * This is called whenever a transaction needs to be processed by a local implementation. * + * This method will be called after the equivalent of + * android.os.Parcel#enforceInterface is called. That is, the interface + * descriptor associated with the AIBinder_Class descriptor will already be + * checked. + * * \param binder the object being transacted on. * \param code implementation-specific code representing which transaction should be taken. * \param in the implementation-specific input data to this transaction. @@ -452,12 +457,14 @@ void* AIBinder_getUserData(AIBinder* binder) __INTRODUCED_IN(29); */ /** - * Creates a parcel to start filling out for a transaction. This may add data to the parcel for - * security, debugging, or other purposes. This parcel is to be sent via AIBinder_transact and it - * represents the input data to the transaction. It is recommended to check if the object is local - * and call directly into its user data before calling this as the parceling and unparceling cost - * can be avoided. This AIBinder must be either built with a class or associated with a class before - * using this API. + * Creates a parcel to start filling out for a transaction. This will add a header to the + * transaction that corresponds to android.os.Parcel#writeInterfaceToken. This may add debugging + * or other information to the transaction for platform use or to enable other features to work. The + * contents of this header is a platform implementation detail, and it is required to use + * libbinder_ndk. This parcel is to be sent via AIBinder_transact and it represents the input data + * to the transaction. It is recommended to check if the object is local and call directly into its + * user data before calling this as the parceling and unparceling cost can be avoided. This AIBinder + * must be either built with a class or associated with a class before using this API. * * This does not affect the ownership of binder. When this function succeeds, the in parcel's * ownership is passed to the caller. At this point, the parcel can be filled out and passed to diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index a90b4aabca..2a66941cef 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -35,7 +35,7 @@ __BEGIN_DECLS * \return EX_NONE on success. */ __attribute__((warn_unused_result)) binder_exception_t AServiceManager_addService( - AIBinder* binder, const char* instance); + AIBinder* binder, const char* instance) __INTRODUCED_IN(29); /** * Gets a binder object with this specific instance name. Will return nullptr immediately if the @@ -47,7 +47,8 @@ __attribute__((warn_unused_result)) binder_exception_t AServiceManager_addServic * * \param instance identifier of the service used to lookup the service. */ -__attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance); +__attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance) + __INTRODUCED_IN(29); /** * Gets a binder object with this specific instance name. Blocks for a couple of seconds waiting on @@ -59,7 +60,8 @@ __attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const * * \param instance identifier of the service used to lookup the service. */ -__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance); +__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance) + __INTRODUCED_IN(29); /** * Registers a lazy service with the default service manager under the 'instance' name. @@ -135,6 +137,10 @@ bool AServiceManager_isUpdatableViaApex(const char* instance) __INTRODUCED_IN(31 /** * Prevent lazy services without client from shutting down their process * + * This should only be used if it is every eventually set to false. If a + * service needs to persist but doesn't need to dynamically shut down, + * prefer to control it with another mechanism. + * * \param persist 'true' if the process should not exit. */ void AServiceManager_forceLazyServicesPersist(bool persist) __INTRODUCED_IN(31); diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index 57c9013f60..49d3401a4f 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -50,6 +50,8 @@ rust_library { "//apex_available:platform", "com.android.virt", ], + lints: "none", + clippy_lints: "none", } rust_bindgen { diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs index 9095af29e0..1d1a295221 100644 --- a/libs/binder/rust/sys/lib.rs +++ b/libs/binder/rust/sys/lib.rs @@ -16,14 +16,6 @@ //! Generated Rust bindings to libbinder_ndk -#![allow( - non_camel_case_types, - non_snake_case, - non_upper_case_globals, - unused, - improper_ctypes, - missing_docs -)] use std::error::Error; use std::fmt; diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index c0f7c99564..ec231b2345 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -63,6 +63,9 @@ cc_test { "libbinder", "libutils", ], + static_libs: [ + "libgmock", + ], compile_multilib: "32", multilib: { lib32: { suffix: "" } }, cflags: ["-DBINDER_IPC_32BIT=1"], @@ -101,6 +104,9 @@ cc_test { "libbinder", "libutils", ], + static_libs: [ + "libgmock", + ], test_suites: ["device-tests", "vts"], require_root: true, } diff --git a/libs/binder/tests/IBinderRpcTest.aidl b/libs/binder/tests/IBinderRpcTest.aidl index 2bdb26483f..ef4198d8f2 100644 --- a/libs/binder/tests/IBinderRpcTest.aidl +++ b/libs/binder/tests/IBinderRpcTest.aidl @@ -18,8 +18,8 @@ interface IBinderRpcTest { oneway void sendString(@utf8InCpp String str); @utf8InCpp String doubleString(@utf8InCpp String str); - // number of known RPC binders to process, RpcState::countBinders - int countBinders(); + // number of known RPC binders to process, RpcState::countBinders by session + int[] countBinders(); // Caller sends server, callee pings caller's server and returns error code. int pingMe(IBinder binder); @@ -36,7 +36,7 @@ interface IBinderRpcTest { // should always return the same binder IBinder alwaysGiveMeTheSameBinder(); - // Idea is that the server will not hold onto the session, the remote connection + // Idea is that the server will not hold onto the session, the remote session // object must. This is to test lifetimes of binder objects, and consequently, also // identity (since by assigning sessions names, we can make sure a section always // references the session it was originally opened with). diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index dc8c0f157e..0c3fbcd2da 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -23,6 +23,7 @@ #include <stdlib.h> #include <thread> +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <binder/Binder.h> @@ -31,16 +32,23 @@ #include <binder/IServiceManager.h> #include <binder/ParcelRef.h> -#include <private/binder/binder_module.h> #include <linux/sched.h> #include <sys/epoll.h> #include <sys/prctl.h> +#include "../binder_module.h" #include "binderAbiHelper.h" #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) using namespace android; +using testing::Not; + +// e.g. EXPECT_THAT(expr, StatusEq(OK)) << "additional message"; +MATCHER_P(StatusEq, expected, (negation ? "not " : "") + statusToString(expected)) { + *result_listener << statusToString(arg); + return expected == arg; +} static ::testing::AssertionResult IsPageAligned(void *buf) { if (((unsigned long)buf & ((unsigned long)PAGE_SIZE - 1)) == 0) @@ -205,19 +213,16 @@ class BinderLibTest : public ::testing::Test { protected: sp<IBinder> addServerEtc(int32_t *idPtr, int code) { - int ret; int32_t id; Parcel data, reply; sp<IBinder> binder; - ret = m_server->transact(code, data, &reply); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(m_server->transact(code, data, &reply), StatusEq(NO_ERROR)); EXPECT_FALSE(binder != nullptr); binder = reply.readStrongBinder(); EXPECT_TRUE(binder != nullptr); - ret = reply.readInt32(&id); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR)); if (idPtr) *idPtr = id; return binder; @@ -401,29 +406,25 @@ class TestDeathRecipient : public IBinder::DeathRecipient, public BinderLibTestE }; TEST_F(BinderLibTest, NopTransaction) { - status_t ret; Parcel data, reply; - ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply), + StatusEq(NO_ERROR)); } TEST_F(BinderLibTest, NopTransactionOneway) { - status_t ret; Parcel data, reply; - ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_ONE_WAY); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_ONE_WAY), + StatusEq(NO_ERROR)); } TEST_F(BinderLibTest, NopTransactionClear) { - status_t ret; Parcel data, reply; // make sure it accepts the transaction flag - ret = m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_CLEAR_BUF); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply, TF_CLEAR_BUF), + StatusEq(NO_ERROR)); } TEST_F(BinderLibTest, Freeze) { - status_t ret; Parcel data, reply, replypid; std::ifstream freezer_file("/sys/fs/cgroup/freezer/cgroup.freeze"); @@ -442,9 +443,8 @@ TEST_F(BinderLibTest, Freeze) { return; } - ret = m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid); + EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GETPID, data, &replypid), StatusEq(NO_ERROR)); int32_t pid = replypid.readInt32(); - EXPECT_EQ(NO_ERROR, ret); 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)); } @@ -468,42 +468,36 @@ TEST_F(BinderLibTest, Freeze) { TEST_F(BinderLibTest, SetError) { int32_t testValue[] = { 0, -123, 123 }; for (size_t i = 0; i < ARRAY_SIZE(testValue); i++) { - status_t ret; Parcel data, reply; data.writeInt32(testValue[i]); - ret = m_server->transact(BINDER_LIB_TEST_SET_ERROR_TRANSACTION, data, &reply); - EXPECT_EQ(testValue[i], ret); + EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_SET_ERROR_TRANSACTION, data, &reply), + StatusEq(testValue[i])); } } TEST_F(BinderLibTest, GetId) { - status_t ret; int32_t id; Parcel data, reply; - ret = m_server->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply); - EXPECT_EQ(NO_ERROR, ret); - ret = reply.readInt32(&id); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GET_ID_TRANSACTION, data, &reply), + StatusEq(NO_ERROR)); + EXPECT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR)); EXPECT_EQ(0, id); } TEST_F(BinderLibTest, PtrSize) { - status_t ret; int32_t ptrsize; Parcel data, reply; sp<IBinder> server = addServer(); ASSERT_TRUE(server != nullptr); - ret = server->transact(BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, data, &reply); - EXPECT_EQ(NO_ERROR, ret); - ret = reply.readInt32(&ptrsize); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, data, &reply), + StatusEq(NO_ERROR)); + EXPECT_THAT(reply.readInt32(&ptrsize), StatusEq(NO_ERROR)); RecordProperty("TestPtrSize", sizeof(void *)); RecordProperty("ServerPtrSize", sizeof(void *)); } TEST_F(BinderLibTest, IndirectGetId2) { - status_t ret; int32_t id; int32_t count; Parcel data, reply; @@ -521,22 +515,19 @@ TEST_F(BinderLibTest, IndirectGetId2) datai.appendTo(&data); } - ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply); - ASSERT_EQ(NO_ERROR, ret); + ASSERT_THAT(m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply), + StatusEq(NO_ERROR)); - ret = reply.readInt32(&id); - ASSERT_EQ(NO_ERROR, ret); + ASSERT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR)); EXPECT_EQ(0, id); - ret = reply.readInt32(&count); - ASSERT_EQ(NO_ERROR, ret); + ASSERT_THAT(reply.readInt32(&count), StatusEq(NO_ERROR)); EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count); for (size_t i = 0; i < (size_t)count; i++) { BinderLibTestBundle replyi(&reply); EXPECT_TRUE(replyi.isValid()); - ret = replyi.readInt32(&id); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(replyi.readInt32(&id), StatusEq(NO_ERROR)); EXPECT_EQ(serverId[i], id); EXPECT_EQ(replyi.dataSize(), replyi.dataPosition()); } @@ -546,7 +537,6 @@ TEST_F(BinderLibTest, IndirectGetId2) TEST_F(BinderLibTest, IndirectGetId3) { - status_t ret; int32_t id; int32_t count; Parcel data, reply; @@ -571,15 +561,13 @@ TEST_F(BinderLibTest, IndirectGetId3) datai.appendTo(&data); } - ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply); - ASSERT_EQ(NO_ERROR, ret); + ASSERT_THAT(m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply), + StatusEq(NO_ERROR)); - ret = reply.readInt32(&id); - ASSERT_EQ(NO_ERROR, ret); + ASSERT_THAT(reply.readInt32(&id), StatusEq(NO_ERROR)); EXPECT_EQ(0, id); - ret = reply.readInt32(&count); - ASSERT_EQ(NO_ERROR, ret); + ASSERT_THAT(reply.readInt32(&count), StatusEq(NO_ERROR)); EXPECT_EQ(ARRAY_SIZE(serverId), (size_t)count); for (size_t i = 0; i < (size_t)count; i++) { @@ -587,18 +575,15 @@ TEST_F(BinderLibTest, IndirectGetId3) BinderLibTestBundle replyi(&reply); EXPECT_TRUE(replyi.isValid()); - ret = replyi.readInt32(&id); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(replyi.readInt32(&id), StatusEq(NO_ERROR)); EXPECT_EQ(serverId[i], id); - ret = replyi.readInt32(&counti); - ASSERT_EQ(NO_ERROR, ret); + ASSERT_THAT(replyi.readInt32(&counti), StatusEq(NO_ERROR)); EXPECT_EQ(1, counti); BinderLibTestBundle replyi2(&replyi); EXPECT_TRUE(replyi2.isValid()); - ret = replyi2.readInt32(&id); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(replyi2.readInt32(&id), StatusEq(NO_ERROR)); EXPECT_EQ(0, id); EXPECT_EQ(replyi2.dataSize(), replyi2.dataPosition()); @@ -610,16 +595,13 @@ TEST_F(BinderLibTest, IndirectGetId3) TEST_F(BinderLibTest, CallBack) { - status_t ret; Parcel data, reply; sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack(); data.writeStrongBinder(callBack); - ret = m_server->transact(BINDER_LIB_TEST_NOP_CALL_BACK, data, &reply, TF_ONE_WAY); - EXPECT_EQ(NO_ERROR, ret); - ret = callBack->waitEvent(5); - EXPECT_EQ(NO_ERROR, ret); - ret = callBack->getResult(); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_NOP_CALL_BACK, data, &reply, TF_ONE_WAY), + StatusEq(NO_ERROR)); + EXPECT_THAT(callBack->waitEvent(5), StatusEq(NO_ERROR)); + EXPECT_THAT(callBack->getResult(), StatusEq(NO_ERROR)); } TEST_F(BinderLibTest, AddServer) @@ -630,7 +612,6 @@ TEST_F(BinderLibTest, AddServer) TEST_F(BinderLibTest, DeathNotificationStrongRef) { - status_t ret; sp<IBinder> sbinder; sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient(); @@ -638,20 +619,17 @@ TEST_F(BinderLibTest, DeathNotificationStrongRef) { sp<IBinder> binder = addServer(); ASSERT_TRUE(binder != nullptr); - ret = binder->linkToDeath(testDeathRecipient); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(binder->linkToDeath(testDeathRecipient), StatusEq(NO_ERROR)); sbinder = binder; } { Parcel data, reply; - ret = sbinder->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY); - EXPECT_EQ(0, ret); + EXPECT_THAT(sbinder->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY), + StatusEq(OK)); } IPCThreadState::self()->flushCommands(); - ret = testDeathRecipient->waitEvent(5); - EXPECT_EQ(NO_ERROR, ret); - ret = sbinder->unlinkToDeath(testDeathRecipient); - EXPECT_EQ(DEAD_OBJECT, ret); + EXPECT_THAT(testDeathRecipient->waitEvent(5), StatusEq(NO_ERROR)); + EXPECT_THAT(sbinder->unlinkToDeath(testDeathRecipient), StatusEq(DEAD_OBJECT)); } TEST_F(BinderLibTest, DeathNotificationMultiple) @@ -674,8 +652,9 @@ TEST_F(BinderLibTest, DeathNotificationMultiple) callBack[i] = new BinderLibTestCallBack(); data.writeStrongBinder(target); data.writeStrongBinder(callBack[i]); - ret = linkedclient[i]->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(linkedclient[i]->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, + &reply, TF_ONE_WAY), + StatusEq(NO_ERROR)); } { Parcel data, reply; @@ -683,8 +662,9 @@ TEST_F(BinderLibTest, DeathNotificationMultiple) passiveclient[i] = addServer(); ASSERT_TRUE(passiveclient[i] != nullptr); data.writeStrongBinder(target); - ret = passiveclient[i]->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data, &reply, TF_ONE_WAY); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(passiveclient[i]->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data, + &reply, TF_ONE_WAY), + StatusEq(NO_ERROR)); } } { @@ -694,10 +674,8 @@ TEST_F(BinderLibTest, DeathNotificationMultiple) } for (int i = 0; i < clientcount; i++) { - ret = callBack[i]->waitEvent(5); - EXPECT_EQ(NO_ERROR, ret); - ret = callBack[i]->getResult(); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(callBack[i]->waitEvent(5), StatusEq(NO_ERROR)); + EXPECT_THAT(callBack[i]->getResult(), StatusEq(NO_ERROR)); } } @@ -712,8 +690,7 @@ TEST_F(BinderLibTest, DeathNotificationThread) sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient(); - ret = target->linkToDeath(testDeathRecipient); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(target->linkToDeath(testDeathRecipient), StatusEq(NO_ERROR)); { Parcel data, reply; @@ -750,14 +727,13 @@ TEST_F(BinderLibTest, DeathNotificationThread) callback = new BinderLibTestCallBack(); data.writeStrongBinder(target); data.writeStrongBinder(callback); - ret = client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, TF_ONE_WAY); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(client->transact(BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, data, &reply, + TF_ONE_WAY), + StatusEq(NO_ERROR)); } - ret = callback->waitEvent(5); - EXPECT_EQ(NO_ERROR, ret); - ret = callback->getResult(); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(callback->waitEvent(5), StatusEq(NO_ERROR)); + EXPECT_THAT(callback->getResult(), StatusEq(NO_ERROR)); } TEST_F(BinderLibTest, PassFile) { @@ -773,17 +749,14 @@ TEST_F(BinderLibTest, PassFile) { Parcel data, reply; uint8_t writebuf[1] = { write_value }; - ret = data.writeFileDescriptor(pipefd[1], true); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(data.writeFileDescriptor(pipefd[1], true), StatusEq(NO_ERROR)); - ret = data.writeInt32(sizeof(writebuf)); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(data.writeInt32(sizeof(writebuf)), StatusEq(NO_ERROR)); - ret = data.write(writebuf, sizeof(writebuf)); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(data.write(writebuf, sizeof(writebuf)), StatusEq(NO_ERROR)); - ret = m_server->transact(BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, data, &reply); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, data, &reply), + StatusEq(NO_ERROR)); } ret = read(pipefd[0], buf, sizeof(buf)); @@ -864,11 +837,10 @@ TEST_F(BinderLibTest, RemoteGetExtension) { } TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) { - status_t ret; Parcel data, reply; - ret = m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GET_SELF_TRANSACTION, data, &reply), + StatusEq(NO_ERROR)); const flat_binder_object *fb = reply.readObject(false); ASSERT_TRUE(fb != nullptr); @@ -888,8 +860,8 @@ TEST_F(BinderLibTest, FreedBinder) { wp<IBinder> keepFreedBinder; { Parcel data, reply; - ret = server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply); - ASSERT_EQ(NO_ERROR, ret); + ASSERT_THAT(server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply), + StatusEq(NO_ERROR)); struct flat_binder_object *freed = (struct flat_binder_object *)(reply.data()); freedHandle = freed->handle; /* Add a weak ref to the freed binder so the driver does not @@ -950,7 +922,6 @@ TEST_F(BinderLibTest, ParcelAllocatedOnAnotherThread) { } TEST_F(BinderLibTest, CheckNoHeaderMappedInUser) { - status_t ret; Parcel data, reply; sp<BinderLibTestCallBack> callBack = new BinderLibTestCallBack(); for (int i = 0; i < 2; i++) { @@ -964,13 +935,12 @@ TEST_F(BinderLibTest, CheckNoHeaderMappedInUser) { datai.appendTo(&data); } - ret = m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_INDIRECT_TRANSACTION, data, &reply), + StatusEq(NO_ERROR)); } TEST_F(BinderLibTest, OnewayQueueing) { - status_t ret; Parcel data, data2; sp<IBinder> pollServer = addPollServer(); @@ -983,25 +953,21 @@ TEST_F(BinderLibTest, OnewayQueueing) data2.writeStrongBinder(callBack2); data2.writeInt32(0); // delay in us - ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, nullptr, TF_ONE_WAY); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, nullptr, TF_ONE_WAY), + StatusEq(NO_ERROR)); // The delay ensures that this second transaction will end up on the async_todo list // (for a single-threaded server) - ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, nullptr, TF_ONE_WAY); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, nullptr, TF_ONE_WAY), + StatusEq(NO_ERROR)); // The server will ensure that the two transactions are handled in the expected order; // If the ordering is not as expected, an error will be returned through the callbacks. - ret = callBack->waitEvent(2); - EXPECT_EQ(NO_ERROR, ret); - ret = callBack->getResult(); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(callBack->waitEvent(2), StatusEq(NO_ERROR)); + EXPECT_THAT(callBack->getResult(), StatusEq(NO_ERROR)); - ret = callBack2->waitEvent(2); - EXPECT_EQ(NO_ERROR, ret); - ret = callBack2->getResult(); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(callBack2->waitEvent(2), StatusEq(NO_ERROR)); + EXPECT_THAT(callBack2->getResult(), StatusEq(NO_ERROR)); } TEST_F(BinderLibTest, WorkSourceUnsetByDefault) @@ -1120,8 +1086,8 @@ TEST_F(BinderLibTest, SchedPolicySet) { ASSERT_TRUE(server != nullptr); Parcel data, reply; - status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply), + StatusEq(NO_ERROR)); int policy = reply.readInt32(); int priority = reply.readInt32(); @@ -1140,8 +1106,8 @@ TEST_F(BinderLibTest, InheritRt) { EXPECT_EQ(0, sched_setscheduler(getpid(), SCHED_RR, ¶m)); Parcel data, reply; - status_t ret = server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(server->transact(BINDER_LIB_TEST_GET_SCHEDULING_POLICY, data, &reply), + StatusEq(NO_ERROR)); int policy = reply.readInt32(); int priority = reply.readInt32(); @@ -1158,10 +1124,9 @@ TEST_F(BinderLibTest, VectorSent) { std::vector<uint64_t> const testValue = { std::numeric_limits<uint64_t>::max(), 0, 200 }; data.writeUint64Vector(testValue); - status_t ret = server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply); - EXPECT_EQ(NO_ERROR, ret); + EXPECT_THAT(server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply), StatusEq(NO_ERROR)); std::vector<uint64_t> readValue; - ret = reply.readUint64Vector(&readValue); + EXPECT_THAT(reply.readUint64Vector(&readValue), StatusEq(OK)); EXPECT_EQ(readValue, testValue); } @@ -1186,19 +1151,18 @@ TEST_F(BinderLibTest, BufRejected) { memcpy(parcelData, &obj, sizeof(obj)); data.setDataSize(sizeof(obj)); - status_t ret = server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply); // Either the kernel should reject this transaction (if it's correct), but // if it's not, the server implementation should return an error if it // finds an object in the received Parcel. - EXPECT_NE(NO_ERROR, ret); + EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply), + Not(StatusEq(NO_ERROR))); } TEST_F(BinderLibTest, GotSid) { sp<IBinder> server = addServer(); Parcel data; - status_t ret = server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr); - EXPECT_EQ(OK, ret); + EXPECT_THAT(server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr), StatusEq(OK)); } class BinderLibTestService : public BBinder diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp index ce47c0d042..a457e67735 100644 --- a/libs/binder/tests/binderRpcBenchmark.cpp +++ b/libs/binder/tests/binderRpcBenchmark.cpp @@ -18,8 +18,8 @@ #include <android-base/logging.h> #include <benchmark/benchmark.h> #include <binder/Binder.h> -#include <binder/RpcConnection.h> #include <binder/RpcServer.h> +#include <binder/RpcSession.h> #include <thread> @@ -30,8 +30,8 @@ using android::BBinder; using android::IBinder; using android::interface_cast; using android::OK; -using android::RpcConnection; using android::RpcServer; +using android::RpcSession; using android::sp; using android::binder::Status; @@ -46,17 +46,17 @@ class MyBinderRpcBenchmark : public BnBinderRpcBenchmark { } }; -static sp<RpcConnection> gConnection = RpcConnection::make(); +static sp<RpcSession> gSession = RpcSession::make(); void BM_getRootObject(benchmark::State& state) { while (state.KeepRunning()) { - CHECK(gConnection->getRootObject() != nullptr); + CHECK(gSession->getRootObject() != nullptr); } } BENCHMARK(BM_getRootObject); void BM_pingTransaction(benchmark::State& state) { - sp<IBinder> binder = gConnection->getRootObject(); + sp<IBinder> binder = gSession->getRootObject(); CHECK(binder != nullptr); while (state.KeepRunning()) { @@ -66,7 +66,7 @@ void BM_pingTransaction(benchmark::State& state) { BENCHMARK(BM_pingTransaction); void BM_repeatString(benchmark::State& state) { - sp<IBinder> binder = gConnection->getRootObject(); + sp<IBinder> binder = gSession->getRootObject(); CHECK(binder != nullptr); sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder); CHECK(iface != nullptr); @@ -95,7 +95,7 @@ void BM_repeatString(benchmark::State& state) { BENCHMARK(BM_repeatString); void BM_repeatBinder(benchmark::State& state) { - sp<IBinder> binder = gConnection->getRootObject(); + sp<IBinder> binder = gSession->getRootObject(); CHECK(binder != nullptr); sp<IBinderRpcBenchmark> iface = interface_cast<IBinderRpcBenchmark>(binder); CHECK(iface != nullptr); @@ -128,10 +128,11 @@ int main(int argc, char** argv) { for (size_t tries = 0; tries < 5; tries++) { usleep(10000); - if (gConnection->setupUnixDomainClient(addr.c_str())) goto success; + if (gSession->setupUnixDomainClient(addr.c_str())) goto success; } LOG(FATAL) << "Could not connect."; success: ::benchmark::RunSpecifiedBenchmarks(); + return 0; } diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index d23df8eaec..a96deb5d27 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -25,8 +25,8 @@ #include <binder/BpBinder.h> #include <binder/IServiceManager.h> #include <binder/ProcessState.h> -#include <binder/RpcConnection.h> #include <binder/RpcServer.h> +#include <binder/RpcSession.h> #include <gtest/gtest.h> #include <chrono> @@ -34,14 +34,11 @@ #include <iostream> #include <thread> -#ifdef __BIONIC__ -#include <linux/vm_sockets.h> -#endif //__BIONIC__ - #include <sys/prctl.h> #include <unistd.h> -#include "../RpcState.h" // for debugging +#include "../RpcState.h" // for debugging +#include "../vm_sockets.h" // for VMADDR_* namespace android { @@ -52,6 +49,19 @@ TEST(BinderRpcParcel, EntireParcelFormatted) { EXPECT_DEATH(p.markForBinder(sp<BBinder>::make()), ""); } +TEST(BinderRpc, SetExternalServer) { + base::unique_fd sink(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR))); + int sinkFd = sink.get(); + auto server = RpcServer::make(); + server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction(); + ASSERT_FALSE(server->hasServer()); + ASSERT_TRUE(server->setupExternalServer(std::move(sink))); + ASSERT_TRUE(server->hasServer()); + base::unique_fd retrieved = server->releaseServer(); + ASSERT_FALSE(server->hasServer()); + ASSERT_EQ(sinkFd, retrieved.get()); +} + using android::binder::Status; #define EXPECT_OK(status) \ @@ -88,24 +98,21 @@ public: *strstr = str + str; return Status::ok(); } - Status countBinders(int32_t* out) override { + Status countBinders(std::vector<int32_t>* out) override { sp<RpcServer> spServer = server.promote(); if (spServer == nullptr) { return Status::fromExceptionCode(Status::EX_NULL_POINTER); } - size_t count = 0; - for (auto connection : spServer->listConnections()) { - count += connection->state()->countBinders(); - } - // help debugging if we don't have one binder (this call is always made - // in this test when exactly one binder is held, which is held only to - // call this method - all other binders should be cleaned up) - if (count != 1) { - for (auto connection : spServer->listConnections()) { - connection->state()->dump(); + out->clear(); + for (auto session : spServer->listSessions()) { + size_t count = session->state()->countBinders(); + if (count != 1) { + // this is called when there is only one binder held remaining, + // so to aid debugging + session->state()->dump(); } + out->push_back(count); } - *out = count; return Status::ok(); } Status pingMe(const sp<IBinder>& binder, int32_t* out) override { @@ -228,50 +235,63 @@ static std::string allocateSocketAddress() { return temp + "/binderRpcTest_" + std::to_string(id++); }; -struct ProcessConnection { +struct ProcessSession { // reference to process hosting a socket server Process host; - // client connection object associated with other process - sp<RpcConnection> connection; + struct SessionInfo { + sp<RpcSession> session; + sp<IBinder> root; + }; - // pre-fetched root object - sp<IBinder> rootBinder; + // client session objects associated with other process + // each one represents a separate session + std::vector<SessionInfo> sessions; - // whether connection should be invalidated by end of run - bool expectInvalid = false; + ProcessSession(ProcessSession&&) = default; + ~ProcessSession() { + for (auto& session : sessions) { + session.root = nullptr; + } - ProcessConnection(ProcessConnection&&) = default; - ~ProcessConnection() { - rootBinder = nullptr; - EXPECT_NE(nullptr, connection); - EXPECT_NE(nullptr, connection->state()); - EXPECT_EQ(0, connection->state()->countBinders()) << (connection->state()->dump(), "dump:"); + for (auto& info : sessions) { + sp<RpcSession>& session = info.session; + + EXPECT_NE(nullptr, session); + EXPECT_NE(nullptr, session->state()); + EXPECT_EQ(0, session->state()->countBinders()) << (session->state()->dump(), "dump:"); - wp<RpcConnection> weakConnection = connection; - connection = nullptr; - EXPECT_EQ(nullptr, weakConnection.promote()) << "Leaked connection"; + wp<RpcSession> weakSession = session; + session = nullptr; + EXPECT_EQ(nullptr, weakSession.promote()) << "Leaked session"; + } } }; -// Process connection where the process hosts IBinderRpcTest, the server used +// Process session where the process hosts IBinderRpcTest, the server used // for most testing here -struct BinderRpcTestProcessConnection { - ProcessConnection proc; +struct BinderRpcTestProcessSession { + ProcessSession proc; - // pre-fetched root object + // pre-fetched root object (for first session) sp<IBinder> rootBinder; - // pre-casted root object + // pre-casted root object (for first session) sp<IBinderRpcTest> rootIface; - BinderRpcTestProcessConnection(BinderRpcTestProcessConnection&&) = default; - ~BinderRpcTestProcessConnection() { - if (!proc.expectInvalid) { - int32_t remoteBinders = 0; - EXPECT_OK(rootIface->countBinders(&remoteBinders)); - // should only be the root binder object, iface - EXPECT_EQ(remoteBinders, 1); + // whether session should be invalidated by end of run + bool expectInvalid = false; + + BinderRpcTestProcessSession(BinderRpcTestProcessSession&&) = default; + ~BinderRpcTestProcessSession() { + if (!expectInvalid) { + std::vector<int32_t> remoteCounts; + // calling over any sessions counts across all sessions + EXPECT_OK(rootIface->countBinders(&remoteCounts)); + EXPECT_EQ(remoteCounts.size(), proc.sessions.size()); + for (auto remoteCount : remoteCounts) { + EXPECT_EQ(remoteCount, 1); + } } rootIface = nullptr; @@ -281,19 +301,15 @@ struct BinderRpcTestProcessConnection { enum class SocketType { UNIX, -#ifdef __BIONIC__ VSOCK, -#endif // __BIONIC__ INET, }; static inline std::string PrintSocketType(const testing::TestParamInfo<SocketType>& info) { switch (info.param) { case SocketType::UNIX: return "unix_domain_socket"; -#ifdef __BIONIC__ case SocketType::VSOCK: return "vm_socket"; -#endif // __BIONIC__ case SocketType::INET: return "inet_socket"; default: @@ -305,8 +321,11 @@ class BinderRpc : public ::testing::TestWithParam<SocketType> { public: // This creates a new process serving an interface on a certain number of // threads. - ProcessConnection createRpcTestSocketServerProcess( - size_t numThreads, const std::function<void(const sp<RpcServer>&)>& configure) { + ProcessSession createRpcTestSocketServerProcess( + size_t numThreads, size_t numSessions, + const std::function<void(const sp<RpcServer>&)>& configure) { + CHECK_GE(numSessions, 1) << "Must have at least one session to a server"; + SocketType socketType = GetParam(); std::string addr = allocateSocketAddress(); @@ -314,77 +333,72 @@ public: static unsigned int vsockPort = 3456; vsockPort++; - auto ret = ProcessConnection{ + auto ret = ProcessSession{ .host = Process([&](Pipe* pipe) { sp<RpcServer> server = RpcServer::make(); server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction(); server->setMaxThreads(numThreads); + unsigned int outPort = 0; + switch (socketType) { case SocketType::UNIX: CHECK(server->setupUnixDomainServer(addr.c_str())) << addr; break; -#ifdef __BIONIC__ case SocketType::VSOCK: CHECK(server->setupVsockServer(vsockPort)); break; -#endif // __BIONIC__ case SocketType::INET: { - unsigned int outPort = 0; CHECK(server->setupInetServer(0, &outPort)); CHECK_NE(0, outPort); - CHECK(android::base::WriteFully(pipe->writeEnd(), &outPort, - sizeof(outPort))); break; } default: LOG_ALWAYS_FATAL("Unknown socket type"); } + CHECK(android::base::WriteFully(pipe->writeEnd(), &outPort, sizeof(outPort))); + configure(server); server->join(); }), - .connection = RpcConnection::make(), }; - unsigned int inetPort = 0; + // always read socket, so that we have waited for the server to start + unsigned int outPort = 0; + CHECK(android::base::ReadFully(ret.host.getPipe()->readEnd(), &outPort, sizeof(outPort))); if (socketType == SocketType::INET) { - CHECK(android::base::ReadFully(ret.host.getPipe()->readEnd(), &inetPort, - sizeof(inetPort))); - CHECK_NE(0, inetPort); + CHECK_NE(0, outPort); } - // create remainder of connections - for (size_t tries = 0; tries < 10; tries++) { - usleep(10000); + for (size_t i = 0; i < numSessions; i++) { + sp<RpcSession> session = RpcSession::make(); switch (socketType) { case SocketType::UNIX: - if (ret.connection->setupUnixDomainClient(addr.c_str())) goto success; + if (session->setupUnixDomainClient(addr.c_str())) goto success; break; -#ifdef __BIONIC__ case SocketType::VSOCK: - if (ret.connection->setupVsockClient(VMADDR_CID_LOCAL, vsockPort)) goto success; + if (session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort)) goto success; break; -#endif // __BIONIC__ case SocketType::INET: - if (ret.connection->setupInetClient("127.0.0.1", inetPort)) goto success; + if (session->setupInetClient("127.0.0.1", outPort)) goto success; break; default: LOG_ALWAYS_FATAL("Unknown socket type"); } + LOG_ALWAYS_FATAL("Could not connect"); + success: + ret.sessions.push_back({session, session->getRootObject()}); } - LOG_ALWAYS_FATAL("Could not connect"); - success: - - ret.rootBinder = ret.connection->getRootObject(); return ret; } - BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads) { - BinderRpcTestProcessConnection ret{ - .proc = createRpcTestSocketServerProcess(numThreads, + BinderRpcTestProcessSession createRpcTestSocketServerProcess(size_t numThreads, + size_t numSessions = 1) { + BinderRpcTestProcessSession ret{ + .proc = createRpcTestSocketServerProcess(numThreads, numSessions, [&](const sp<RpcServer>& server) { sp<MyBinderRpcTest> service = new MyBinderRpcTest; @@ -393,7 +407,7 @@ public: }), }; - ret.rootBinder = ret.proc.rootBinder; + ret.rootBinder = ret.proc.sessions.at(0).root; ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder); return ret; @@ -401,16 +415,12 @@ public: }; TEST_P(BinderRpc, RootObjectIsNull) { - auto proc = createRpcTestSocketServerProcess(1, [](const sp<RpcServer>& server) { + auto proc = createRpcTestSocketServerProcess(1, 1, [](const sp<RpcServer>& server) { // this is the default, but to be explicit server->setRootObject(nullptr); }); - // retrieved by getRootObject when process is created above - EXPECT_EQ(nullptr, proc.rootBinder); - - // make sure we can retrieve it again (process doesn't crash) - EXPECT_EQ(nullptr, proc.connection->getRootObject()); + EXPECT_EQ(nullptr, proc.sessions.at(0).root); } TEST_P(BinderRpc, Ping) { @@ -425,6 +435,14 @@ TEST_P(BinderRpc, GetInterfaceDescriptor) { EXPECT_EQ(IBinderRpcTest::descriptor, proc.rootBinder->getInterfaceDescriptor()); } +TEST_P(BinderRpc, MultipleSessions) { + auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 5 /*sessions*/); + for (auto session : proc.proc.sessions) { + ASSERT_NE(nullptr, session.root); + EXPECT_EQ(OK, session.root->pingBinder()); + } +} + TEST_P(BinderRpc, TransactionsMustBeMarkedRpc) { auto proc = createRpcTestSocketServerProcess(1); Parcel data; @@ -563,7 +581,7 @@ TEST_P(BinderRpc, HoldBinder) { // These are behavioral differences form regular binder, where certain usecases // aren't supported. -TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) { +TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketSessions) { auto proc1 = createRpcTestSocketServerProcess(1); auto proc2 = createRpcTestSocketServerProcess(1); @@ -572,6 +590,15 @@ TEST_P(BinderRpc, CannotMixBindersBetweenUnrelatedSocketConnections) { proc1.rootIface->repeatBinder(proc2.rootBinder, &outBinder).transactionError()); } +TEST_P(BinderRpc, CannotMixBindersBetweenTwoSessionsToTheSameServer) { + auto proc = createRpcTestSocketServerProcess(1 /*threads*/, 2 /*sessions*/); + + sp<IBinder> outBinder; + EXPECT_EQ(INVALID_OPERATION, + proc.rootIface->repeatBinder(proc.proc.sessions.at(1).root, &outBinder) + .transactionError()); +} + TEST_P(BinderRpc, CannotSendRegularBinderOverSocketBinder) { auto proc = createRpcTestSocketServerProcess(1); @@ -856,7 +883,7 @@ TEST_P(BinderRpc, Die) { EXPECT_EQ(DEAD_OBJECT, proc.rootIface->die(doDeathCleanup).transactionError()) << "Do death cleanup: " << doDeathCleanup; - proc.proc.expectInvalid = true; + proc.expectInvalid = true; } } @@ -907,13 +934,42 @@ TEST_P(BinderRpc, Fds) { INSTANTIATE_TEST_CASE_P(PerSocket, BinderRpc, ::testing::ValuesIn({ SocketType::UNIX, +// TODO(b/185269356): working on host #ifdef __BIONIC__ SocketType::VSOCK, -#endif // __BIONIC__ +#endif SocketType::INET, }), PrintSocketType); +class BinderRpcServerRootObject : public ::testing::TestWithParam<std::tuple<bool, bool>> {}; + +TEST_P(BinderRpcServerRootObject, WeakRootObject) { + using SetFn = std::function<void(RpcServer*, sp<IBinder>)>; + auto setRootObject = [](bool isStrong) -> SetFn { + return isStrong ? SetFn(&RpcServer::setRootObject) : SetFn(&RpcServer::setRootObjectWeak); + }; + + auto server = RpcServer::make(); + auto [isStrong1, isStrong2] = GetParam(); + auto binder1 = sp<BBinder>::make(); + IBinder* binderRaw1 = binder1.get(); + setRootObject(isStrong1)(server.get(), binder1); + EXPECT_EQ(binderRaw1, server->getRootObject()); + binder1.clear(); + EXPECT_EQ((isStrong1 ? binderRaw1 : nullptr), server->getRootObject()); + + auto binder2 = sp<BBinder>::make(); + IBinder* binderRaw2 = binder2.get(); + setRootObject(isStrong2)(server.get(), binder2); + EXPECT_EQ(binderRaw2, server->getRootObject()); + binder2.clear(); + EXPECT_EQ((isStrong2 ? binderRaw2 : nullptr), server->getRootObject()); +} + +INSTANTIATE_TEST_CASE_P(BinderRpc, BinderRpcServerRootObject, + ::testing::Combine(::testing::Bool(), ::testing::Bool())); + } // namespace android int main(int argc, char** argv) { diff --git a/libs/binder/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index 74b8eb8d93..74b8eb8d93 100644 --- a/libs/binder/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp diff --git a/libs/binder/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 624def1a7a..624def1a7a 100644 --- a/libs/binder/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp diff --git a/libs/binder/parcel_fuzzer/binder.h b/libs/binder/tests/parcel_fuzzer/binder.h index 0c51d68a37..0c51d68a37 100644 --- a/libs/binder/parcel_fuzzer/binder.h +++ b/libs/binder/tests/parcel_fuzzer/binder.h diff --git a/libs/binder/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp index 008780cb2e..008780cb2e 100644 --- a/libs/binder/parcel_fuzzer/binder_ndk.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp diff --git a/libs/binder/parcel_fuzzer/binder_ndk.h b/libs/binder/tests/parcel_fuzzer/binder_ndk.h index e69d9c1d8f..cf24ab932b 100644 --- a/libs/binder/parcel_fuzzer/binder_ndk.h +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.h @@ -23,7 +23,7 @@ // libbinder_ndk doesn't export this header which breaks down its API for NDK // and APEX users, but we need access to it to fuzz. -#include "../ndk/parcel_internal.h" +#include "../../ndk/parcel_internal.h" class NdkParcelAdapter { public: diff --git a/libs/binder/parcel_fuzzer/hwbinder.cpp b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp index 0fec393e55..0fec393e55 100644 --- a/libs/binder/parcel_fuzzer/hwbinder.cpp +++ b/libs/binder/tests/parcel_fuzzer/hwbinder.cpp diff --git a/libs/binder/parcel_fuzzer/hwbinder.h b/libs/binder/tests/parcel_fuzzer/hwbinder.h index 1fa56d433d..1fa56d433d 100644 --- a/libs/binder/parcel_fuzzer/hwbinder.h +++ b/libs/binder/tests/parcel_fuzzer/hwbinder.h diff --git a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h index 0a083d7665..0a083d7665 100644 --- a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h diff --git a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h index 749bf212e6..749bf212e6 100644 --- a/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h diff --git a/libs/binder/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp index 332e2ada52..a47b753497 100644 --- a/libs/binder/parcel_fuzzer/main.cpp +++ b/libs/binder/tests/parcel_fuzzer/main.cpp @@ -23,7 +23,7 @@ #include <iostream> #include <android-base/logging.h> -#include <binder/RpcConnection.h> +#include <binder/RpcSession.h> #include <fuzzbinder/random_parcel.h> #include <fuzzer/FuzzedDataProvider.h> @@ -33,7 +33,7 @@ #include <sys/time.h> using android::fillRandomParcel; -using android::RpcConnection; +using android::RpcSession; using android::sp; void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) { @@ -61,9 +61,9 @@ void doFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads, P p; if constexpr (std::is_same_v<P, android::Parcel>) { if (provider.ConsumeBool()) { - auto connection = sp<RpcConnection>::make(); - CHECK(connection->addNullDebuggingClient()); - p.markForRpc(connection); + auto session = sp<RpcSession>::make(); + CHECK(session->addNullDebuggingClient()); + p.markForRpc(session); fillRandomParcelData(&p, std::move(provider)); } else { fillRandomParcel(&p, std::move(provider)); diff --git a/libs/binder/parcel_fuzzer/parcel_fuzzer.h b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h index b68a8a91e6..b68a8a91e6 100644 --- a/libs/binder/parcel_fuzzer/parcel_fuzzer.h +++ b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h diff --git a/libs/binder/parcel_fuzzer/random_fd.cpp b/libs/binder/tests/parcel_fuzzer/random_fd.cpp index cef6adb82d..cef6adb82d 100644 --- a/libs/binder/parcel_fuzzer/random_fd.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_fd.cpp diff --git a/libs/binder/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp index b045a22eff..b045a22eff 100644 --- a/libs/binder/parcel_fuzzer/random_parcel.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp diff --git a/libs/binder/parcel_fuzzer/util.cpp b/libs/binder/tests/parcel_fuzzer/util.cpp index 479f406d8c..479f406d8c 100644 --- a/libs/binder/parcel_fuzzer/util.cpp +++ b/libs/binder/tests/parcel_fuzzer/util.cpp diff --git a/libs/binder/parcel_fuzzer/util.h b/libs/binder/tests/parcel_fuzzer/util.h index 45e8c571a8..45e8c571a8 100644 --- a/libs/binder/parcel_fuzzer/util.h +++ b/libs/binder/tests/parcel_fuzzer/util.h diff --git a/libs/binder/tests/rpc_fuzzer/Android.bp b/libs/binder/tests/rpc_fuzzer/Android.bp new file mode 100644 index 0000000000..1c75306398 --- /dev/null +++ b/libs/binder/tests/rpc_fuzzer/Android.bp @@ -0,0 +1,40 @@ +package { + // 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_fuzz { + name: "binder_rpc_fuzzer", + host_supported: true, + + fuzz_config: { + cc: ["smoreland@google.com"], + }, + + srcs: [ + "main.cpp", + ], + static_libs: [ + "libbase", + "libcutils", + "liblog", + "libutils", + ], + + target: { + android: { + shared_libs: [ + "libbinder", + ], + }, + host: { + static_libs: [ + "libbinder", + ], + }, + }, +} diff --git a/libs/binder/tests/rpc_fuzzer/main.cpp b/libs/binder/tests/rpc_fuzzer/main.cpp new file mode 100644 index 0000000000..3603ebe41b --- /dev/null +++ b/libs/binder/tests/rpc_fuzzer/main.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/unique_fd.h> +#include <binder/Binder.h> +#include <binder/Parcel.h> +#include <binder/RpcServer.h> +#include <binder/RpcSession.h> + +#include <sys/resource.h> +#include <sys/un.h> + +namespace android { + +static const std::string kSock = std::string(getenv("TMPDIR") ?: "/tmp") + + "/binderRpcFuzzerSocket_" + std::to_string(getpid()); + +size_t getHardMemoryLimit() { + struct rlimit limit; + CHECK(0 == getrlimit(RLIMIT_AS, &limit)) << errno; + return limit.rlim_max; +} + +void setMemoryLimit(size_t cur, size_t max) { + const struct rlimit kLimit = { + .rlim_cur = cur, + .rlim_max = max, + }; + CHECK(0 == setrlimit(RLIMIT_AS, &kLimit)) << errno; +} + +class SomeBinder : public BBinder { + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) { + (void)flags; + + if ((code & 1) == 0) { + sp<IBinder> binder; + (void)data.readStrongBinder(&binder); + if (binder != nullptr) { + (void)binder->pingBinder(); + } + } + if ((code & 2) == 0) { + (void)data.readInt32(); + } + if ((code & 4) == 0) { + (void)reply->writeStrongBinder(sp<BBinder>::make()); + } + + return OK; + } +}; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size > 50000) return 0; + + unlink(kSock.c_str()); + + sp<RpcServer> server = RpcServer::make(); + server->setRootObject(sp<SomeBinder>::make()); + server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction(); + CHECK(server->setupUnixDomainServer(kSock.c_str())); + + static constexpr size_t kMemLimit = 1llu * 1024 * 1024 * 1024; + size_t hardLimit = getHardMemoryLimit(); + setMemoryLimit(std::min(kMemLimit, hardLimit), hardLimit); + + std::thread serverThread([=] { (void)server->acceptOne(); }); + + sockaddr_un addr{ + .sun_family = AF_UNIX, + }; + CHECK_LT(kSock.size(), sizeof(addr.sun_path)); + memcpy(&addr.sun_path, kSock.c_str(), kSock.size()); + + base::unique_fd clientFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0))); + CHECK_NE(clientFd.get(), -1); + CHECK_EQ(0, + TEMP_FAILURE_RETRY( + connect(clientFd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)))) + << strerror(errno); + + serverThread.join(); + + // TODO(b/182938024): fuzz multiple sessions, instead of just one + +#if 0 + // make fuzzer more productive locally by forcing it to create a new session + int32_t id = -1; + CHECK(base::WriteFully(clientFd, &id, sizeof(id))); +#endif + + CHECK(base::WriteFully(clientFd, data, size)); + + clientFd.reset(); + + // TODO(b/185167543): better way to force a server to shutdown + while (!server->listSessions().empty() && server->numUninitializedSessions()) { + usleep(1); + } + + setMemoryLimit(hardLimit, hardLimit); + + return 0; +} + +} // namespace android diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/unit_fuzzers/Android.bp index b1263e8d8e..b1263e8d8e 100644 --- a/libs/binder/tests/fuzzers/Android.bp +++ b/libs/binder/tests/unit_fuzzers/Android.bp diff --git a/libs/binder/tests/fuzzers/BinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BinderFuzz.cpp index 1e5d80a205..1e5d80a205 100644 --- a/libs/binder/tests/fuzzers/BinderFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/BinderFuzz.cpp diff --git a/libs/binder/tests/fuzzers/BinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h index 69f1b9dfb2..69f1b9dfb2 100644 --- a/libs/binder/tests/fuzzers/BinderFuzzFunctions.h +++ b/libs/binder/tests/unit_fuzzers/BinderFuzzFunctions.h diff --git a/libs/binder/tests/fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp index c50279b13d..c50279b13d 100644 --- a/libs/binder/tests/fuzzers/BpBinderFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzz.cpp diff --git a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h index 6ca0e2f075..6ca0e2f075 100644 --- a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h +++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h diff --git a/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp index 09cb2162f7..09cb2162f7 100644 --- a/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/BufferedTextOutputFuzz.cpp diff --git a/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h index 626b7585f7..626b7585f7 100644 --- a/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h +++ b/libs/binder/tests/unit_fuzzers/IBinderFuzzFunctions.h diff --git a/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp b/libs/binder/tests/unit_fuzzers/MemoryDealerFuzz.cpp index f9dda8c558..f9dda8c558 100644 --- a/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/MemoryDealerFuzz.cpp diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp b/libs/binder/tests/unit_fuzzers/PersistableBundleFuzz.cpp index 4843c46545..4843c46545 100644 --- a/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/PersistableBundleFuzz.cpp diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/PersistableBundleFuzzFunctions.h index 820e9e8c31..820e9e8c31 100644 --- a/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h +++ b/libs/binder/tests/unit_fuzzers/PersistableBundleFuzzFunctions.h diff --git a/libs/binder/tests/fuzzers/StabilityFuzz.cpp b/libs/binder/tests/unit_fuzzers/StabilityFuzz.cpp index 8ad9b44f65..8ad9b44f65 100644 --- a/libs/binder/tests/fuzzers/StabilityFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/StabilityFuzz.cpp diff --git a/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h index 8b4ed702af..8b4ed702af 100644 --- a/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h +++ b/libs/binder/tests/unit_fuzzers/StabilityFuzzFunctions.h diff --git a/libs/binder/tests/fuzzers/StatusFuzz.cpp b/libs/binder/tests/unit_fuzzers/StatusFuzz.cpp index 4f6ad6f707..4f6ad6f707 100644 --- a/libs/binder/tests/fuzzers/StatusFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/StatusFuzz.cpp diff --git a/libs/binder/tests/fuzzers/StatusFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/StatusFuzzFunctions.h index bc8d17a34f..bc8d17a34f 100644 --- a/libs/binder/tests/fuzzers/StatusFuzzFunctions.h +++ b/libs/binder/tests/unit_fuzzers/StatusFuzzFunctions.h diff --git a/libs/binder/tests/fuzzers/TextOutputFuzz.cpp b/libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp index 5e3502aace..5e3502aace 100644 --- a/libs/binder/tests/fuzzers/TextOutputFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/TextOutputFuzz.cpp diff --git a/libs/binder/tests/fuzzers/commonFuzzHelpers.h b/libs/binder/tests/unit_fuzzers/commonFuzzHelpers.h index d58d9b6d84..d58d9b6d84 100644 --- a/libs/binder/tests/fuzzers/commonFuzzHelpers.h +++ b/libs/binder/tests/unit_fuzzers/commonFuzzHelpers.h diff --git a/libs/binder/vm_sockets.h b/libs/binder/vm_sockets.h new file mode 100644 index 0000000000..7d9732b0fa --- /dev/null +++ b/libs/binder/vm_sockets.h @@ -0,0 +1,54 @@ +/**************************************************************************** + **************************************************************************** + *** + *** This header was automatically generated from a Linux kernel header + *** of the same name, to make information necessary for userspace to + *** call into the kernel available to libc. It contains only constants, + *** structures, and macros generated from the original header, and thus, + *** contains no copyrightable information. + *** + *** Copied and modified from bionic/libc/kernel/uapi/linux/vm_sockets.h + *** + **************************************************************************** + ****************************************************************************/ +#pragma once + +#ifdef __BIONIC__ +#include <linux/vm_sockets.h> +#else + +#ifndef _UAPI_VM_SOCKETS_H +#define _UAPI_VM_SOCKETS_H +#define SO_VM_SOCKETS_BUFFER_SIZE 0 +#define SO_VM_SOCKETS_BUFFER_MIN_SIZE 1 +#define SO_VM_SOCKETS_BUFFER_MAX_SIZE 2 +#define SO_VM_SOCKETS_PEER_HOST_VM_ID 3 +#define SO_VM_SOCKETS_TRUSTED 5 +#define SO_VM_SOCKETS_CONNECT_TIMEOUT 6 +#define SO_VM_SOCKETS_NONBLOCK_TXRX 7 +#define VMADDR_CID_ANY (-1U) +#define VMADDR_PORT_ANY (-1U) +#define VMADDR_CID_HYPERVISOR 0 +#define VMADDR_CID_LOCAL 1 +#define VMADDR_CID_HOST 2 +#define VM_SOCKETS_INVALID_VERSION (-1U) +#define VM_SOCKETS_VERSION_EPOCH(_v) (((_v)&0xFF000000) >> 24) +#define VM_SOCKETS_VERSION_MAJOR(_v) (((_v)&0x00FF0000) >> 16) +#define VM_SOCKETS_VERSION_MINOR(_v) (((_v)&0x0000FFFF)) +struct sockaddr_vm { + sa_family_t svm_family; + // NOLINTNEXTLINE(google-runtime-int) + unsigned short svm_reserved1; + unsigned int svm_port; + unsigned int svm_cid; + // NOLINTNEXTLINE(google-runtime-int) + unsigned char svm_zero[sizeof(struct sockaddr) - sizeof(sa_family_t) - sizeof(unsigned short) - + sizeof(unsigned int) - sizeof(unsigned int)]; +}; +#define IOCTL_VM_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9) +#ifndef AF_VSOCK +#define AF_VSOCK 40 +#endif +#endif + +#endif diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 8a82c2abdf..29c788bca3 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -47,6 +47,7 @@ static const char* native_processes_to_dump[] = { // Native processes to dump on debuggable builds. static const char* debuggable_native_processes_to_dump[] = { + "/system/bin/keystore2", "/system/bin/vold", NULL, }; diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 3d854c2b92..a2868c6143 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -213,7 +213,8 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, // for this is the scale is calculated based on the requested size and buffer size. // If there's no buffer, the scale will always be 1. if (mLastBufferInfo.hasBuffer) { - setMatrix(&t, mLastBufferInfo); + t.setDestinationFrame(mSurfaceControl, + Rect(0, 0, newSize.getWidth(), newSize.getHeight())); } applyTransaction = true; } @@ -316,6 +317,11 @@ void BLASTBufferQueue::releaseBufferCallback(uint64_t graphicBufferId, std::unique_lock _lock{mMutex}; BQA_LOGV("releaseBufferCallback graphicBufferId=%" PRIu64, graphicBufferId); + if (mSurfaceControl != nullptr) { + mTransformHint = mSurfaceControl->getTransformHint(); + mBufferItemConsumer->setTransformHint(mTransformHint); + } + auto it = mSubmitted.find(graphicBufferId); if (it == mSubmitted.end()) { BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %" PRIu64, @@ -416,8 +422,8 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this)); mSurfaceControlsWithPendingCallback.push(mSurfaceControl); - setMatrix(t, mLastBufferInfo); - t->setCrop(mSurfaceControl, crop); + t->setDestinationFrame(mSurfaceControl, Rect(0, 0, mSize.getWidth(), mSize.getHeight())); + t->setBufferCrop(mSurfaceControl, crop); t->setTransform(mSurfaceControl, bufferItem.mTransform); t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse); if (!bufferItem.mIsAutoTimestamp) { @@ -543,19 +549,6 @@ bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) { return mSize != bufferSize; } -void BLASTBufferQueue::setMatrix(SurfaceComposerClient::Transaction* t, - const BufferInfo& bufferInfo) { - uint32_t bufWidth = bufferInfo.crop.getWidth(); - uint32_t bufHeight = bufferInfo.crop.getHeight(); - - float sx = mSize.width / static_cast<float>(bufWidth); - float sy = mSize.height / static_cast<float>(bufHeight); - - t->setMatrix(mSurfaceControl, sx, 0, 0, sy); - // Update position based on crop. - t->setPosition(mSurfaceControl, bufferInfo.crop.left * sx * -1, bufferInfo.crop.top * sy * -1); -} - void BLASTBufferQueue::setTransactionCompleteCallback( uint64_t frameNumber, std::function<void(int64_t)>&& transactionCompleteCallback) { std::lock_guard _lock{mMutex}; @@ -608,6 +601,14 @@ public: status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override { return mBbq->setFrameTimelineInfo(frameTimelineInfo); } + protected: + uint32_t getTransformHint() const override { + if (mStickyTransform == 0 && !transformToDisplayInverse()) { + return mBbq->getLastTransformHint(); + } else { + return 0; + } + } }; // TODO: Can we coalesce this with frame updates? Need to confirm @@ -666,12 +667,12 @@ private: void run() { std::unique_lock<std::mutex> lock(mMutex); while (!mDone) { - mCv.wait(lock); while (!mRunnables.empty()) { std::function<void()> runnable = mRunnables.front(); mRunnables.pop_front(); runnable(); } + mCv.wait(lock); } } @@ -777,4 +778,12 @@ PixelFormat BLASTBufferQueue::convertBufferFormat(PixelFormat& format) { return convertedFormat; } +uint32_t BLASTBufferQueue::getLastTransformHint() const { + if (mSurfaceControl != nullptr) { + return mSurfaceControl->getTransformHint(); + } else { + return 0; + } +} + } // namespace android diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index a7cf39add9..df308d897b 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -1621,6 +1621,26 @@ status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, return NO_ERROR; } +status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, + Rect* outRect, uint32_t* outTransform) { + ATRACE_CALL(); + BQ_LOGV("getLastQueuedBuffer"); + + std::lock_guard<std::mutex> lock(mCore->mMutex); + if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) { + *outBuffer = nullptr; + *outFence = Fence::NO_FENCE; + return NO_ERROR; + } + + *outBuffer = mSlots[mCore->mLastQueuedSlot].mGraphicBuffer; + *outFence = mLastQueueBufferFence; + *outRect = mLastQueuedCrop; + *outTransform = mLastQueuedTransform; + + return NO_ERROR; +} + void BufferQueueProducer::getFrameTimestamps(FrameEventHistoryDelta* outDelta) { addAndGetFrameTimestamps(nullptr, outDelta); } diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index c1f9b85229..797069c798 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -81,6 +81,7 @@ enum { QUEUE_BUFFERS, CANCEL_BUFFERS, QUERY_MULTIPLE, + GET_LAST_QUEUED_BUFFER2, }; class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> @@ -646,6 +647,56 @@ public: return result; } + virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, + Rect* outRect, uint32_t* outTransform) override { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + status_t result = remote()->transact(GET_LAST_QUEUED_BUFFER2, data, &reply); + if (result != NO_ERROR) { + ALOGE("getLastQueuedBuffer failed to transact: %d", result); + return result; + } + status_t remoteError = NO_ERROR; + result = reply.readInt32(&remoteError); + if (result != NO_ERROR) { + ALOGE("getLastQueuedBuffer failed to read status: %d", result); + return result; + } + if (remoteError != NO_ERROR) { + return remoteError; + } + bool hasBuffer = false; + result = reply.readBool(&hasBuffer); + if (result != NO_ERROR) { + ALOGE("getLastQueuedBuffer failed to read buffer: %d", result); + return result; + } + sp<GraphicBuffer> buffer; + if (hasBuffer) { + buffer = new GraphicBuffer(); + result = reply.read(*buffer); + if (result == NO_ERROR) { + result = reply.read(*outRect); + } + if (result == NO_ERROR) { + result = reply.readUint32(outTransform); + } + } + if (result != NO_ERROR) { + ALOGE("getLastQueuedBuffer failed to read buffer: %d", result); + return result; + } + sp<Fence> fence(new Fence); + result = reply.read(*fence); + if (result != NO_ERROR) { + ALOGE("getLastQueuedBuffer failed to read fence: %d", result); + return result; + } + *outBuffer = buffer; + *outFence = fence; + return result; + } + virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) { Parcel data, reply; status_t result = data.writeInterfaceToken( @@ -870,6 +921,11 @@ public: outBuffer, outFence, outTransformMatrix); } + status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, Rect* outRect, + uint32_t* outTransform) override { + return mBase->getLastQueuedBuffer(outBuffer, outFence, outRect, outTransform); + } + void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override { return mBase->getFrameTimestamps(outDelta); } @@ -1362,6 +1418,45 @@ status_t BnGraphicBufferProducer::onTransact( } return NO_ERROR; } + case GET_LAST_QUEUED_BUFFER2: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + sp<GraphicBuffer> buffer(nullptr); + sp<Fence> fence(Fence::NO_FENCE); + Rect crop; + uint32_t transform; + status_t result = getLastQueuedBuffer(&buffer, &fence, &crop, &transform); + reply->writeInt32(result); + if (result != NO_ERROR) { + return result; + } + if (!buffer.get()) { + reply->writeBool(false); + } else { + reply->writeBool(true); + result = reply->write(*buffer); + if (result == NO_ERROR) { + result = reply->write(crop); + } + if (result == NO_ERROR) { + result = reply->writeUint32(transform); + } + } + if (result != NO_ERROR) { + ALOGE("getLastQueuedBuffer failed to write buffer: %d", result); + return result; + } + if (fence == nullptr) { + ALOGE("getLastQueuedBuffer returned a NULL fence, setting to Fence::NO_FENCE"); + fence = Fence::NO_FENCE; + } + result = reply->write(*fence); + if (result != NO_ERROR) { + ALOGE("getLastQueuedBuffer failed to write fence: %d", result); + return result; + } + return NO_ERROR; + } + case GET_FRAME_TIMESTAMPS: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); FrameEventHistoryDelta frameTimestamps; diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 53721cf6f7..71e18a97c9 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -824,6 +824,36 @@ public: return error; } + virtual status_t addTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener) { + Parcel data, reply; + SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); + SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener)); + + const status_t error = + remote()->transact(BnSurfaceComposer::ADD_TUNNEL_MODE_ENABLED_LISTENER, data, + &reply); + if (error != NO_ERROR) { + ALOGE("addTunnelModeEnabledListener: Failed to transact"); + } + return error; + } + + virtual status_t removeTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener) { + Parcel data, reply; + SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); + SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener)); + + const status_t error = + remote()->transact(BnSurfaceComposer::REMOVE_TUNNEL_MODE_ENABLED_LISTENER, data, + &reply); + if (error != NO_ERROR) { + ALOGE("removeTunnelModeEnabledListener: Failed to transact"); + } + return error; + } + status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, bool allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax, @@ -1740,6 +1770,26 @@ status_t BnSurfaceComposer::onTransact( } return removeFpsListener(listener); } + case ADD_TUNNEL_MODE_ENABLED_LISTENER: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<gui::ITunnelModeEnabledListener> listener; + status_t result = data.readNullableStrongBinder(&listener); + if (result != NO_ERROR) { + ALOGE("addTunnelModeEnabledListener: Failed to read listener"); + return result; + } + return addTunnelModeEnabledListener(listener); + } + case REMOVE_TUNNEL_MODE_ENABLED_LISTENER: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<gui::ITunnelModeEnabledListener> listener; + status_t result = data.readNullableStrongBinder(&listener); + if (result != NO_ERROR) { + ALOGE("removeTunnelModeEnabledListener: Failed to read listener"); + return result; + } + return removeTunnelModeEnabledListener(listener); + } case SET_DESIRED_DISPLAY_MODE_SPECS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> displayToken = data.readStrongBinder(); diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp index e707684618..0827bbe3ee 100644 --- a/libs/gui/LayerDebugInfo.cpp +++ b/libs/gui/LayerDebugInfo.cpp @@ -119,9 +119,11 @@ std::string to_string(const LayerDebugInfo& info) { info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion"); if (info.mStretchEffect.hasEffect()) { const auto& se = info.mStretchEffect; - StringAppendF(&result, " StretchEffect area=[%f, %f, %f, %f] vec=(%f, %f) maxAmount=%f\n", - se.area.left, se.area.top, se.area.right, se.area.bottom, se.vectorX, - se.vectorY, se.maxAmount); + StringAppendF(&result, + " StretchEffect width = %f, height = %f vec=(%f, %f) " + "maxAmount=(%f, %f)\n", + se.width, se.height, + se.vectorX, se.vectorY, se.maxAmountX, se.maxAmountY); } StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ", diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 267db7686a..e65c721ae1 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -64,6 +64,8 @@ layer_state_t::layer_state_t() fixedTransformHint(ui::Transform::ROT_INVALID), frameNumber(0), autoRefresh(false), + bufferCrop(Rect::INVALID_RECT), + destinationFrame(Rect::INVALID_RECT), releaseBufferListener(nullptr) { matrix.dsdx = matrix.dtdy = 1.0f; matrix.dsdy = matrix.dtdx = 0.0f; @@ -167,6 +169,7 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.write, stretchEffect); SAFE_PARCEL(output.write, bufferCrop); + SAFE_PARCEL(output.write, destinationFrame); return NO_ERROR; } @@ -296,6 +299,7 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.read, stretchEffect); SAFE_PARCEL(input.read, bufferCrop); + SAFE_PARCEL(input.read, destinationFrame); return NO_ERROR; } @@ -543,6 +547,10 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eBufferCropChanged; bufferCrop = other.bufferCrop; } + if (other.what & eDestinationFrameChanged) { + what |= eDestinationFrameChanged; + destinationFrame = other.destinationFrame; + } if ((other.what & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " "other.what=0x%" PRIu64 " what=0x%" PRIu64, diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index f4aa7b3d8b..e117d1189c 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -1291,7 +1291,7 @@ int Surface::query(int what, int* value) const { mUserHeight ? mUserHeight : mDefaultHeight); return NO_ERROR; case NATIVE_WINDOW_TRANSFORM_HINT: - *value = static_cast<int>(mTransformHint); + *value = static_cast<int>(getTransformHint()); return NO_ERROR; case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: { status_t err = NO_ERROR; @@ -1495,6 +1495,9 @@ int Surface::perform(int operation, va_list args) case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER: res = dispatchGetLastQueuedBuffer(args); break; + case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2: + res = dispatchGetLastQueuedBuffer2(args); + break; case NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO: res = dispatchSetFrameTimelineInfo(args); break; @@ -1808,6 +1811,39 @@ int Surface::dispatchGetLastQueuedBuffer(va_list args) { return result; } +int Surface::dispatchGetLastQueuedBuffer2(va_list args) { + AHardwareBuffer** buffer = va_arg(args, AHardwareBuffer**); + int* fence = va_arg(args, int*); + ARect* crop = va_arg(args, ARect*); + uint32_t* transform = va_arg(args, uint32_t*); + sp<GraphicBuffer> graphicBuffer; + sp<Fence> spFence; + + Rect r; + int result = + mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, &r, transform); + + if (graphicBuffer != nullptr) { + *buffer = graphicBuffer->toAHardwareBuffer(); + AHardwareBuffer_acquire(*buffer); + + // Avoid setting crop* unless buffer is valid (matches IGBP behavior) + crop->left = r.left; + crop->top = r.top; + crop->right = r.right; + crop->bottom = r.bottom; + } else { + *buffer = nullptr; + } + + if (spFence != nullptr) { + *fence = spFence->dup(); + } else { + *fence = -1; + } + return result; +} + int Surface::dispatchSetFrameTimelineInfo(va_list args) { ATRACE_CALL(); auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t)); @@ -1825,7 +1861,7 @@ int Surface::dispatchGetExtraBufferCount(va_list args) { return getExtraBufferCount(extraBuffers); } -bool Surface::transformToDisplayInverse() { +bool Surface::transformToDisplayInverse() const { return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) == NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 0656dae6fb..371454a3de 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1649,8 +1649,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApply } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setStretchEffect( - const sp<SurfaceControl>& sc, float left, float top, float right, float bottom, float vecX, - float vecY, float maxAmount) { + const sp<SurfaceControl>& sc, const StretchEffect& stretchEffect) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; @@ -1658,10 +1657,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setStret } s->what |= layer_state_t::eStretchChanged; - s->stretchEffect = StretchEffect{.area = {left, top, right, bottom}, - .vectorX = vecX, - .vectorY = vecY, - .maxAmount = maxAmount}; + s->stretchEffect = stretchEffect; return *this; } @@ -1680,6 +1676,21 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDestinationFrame( + const sp<SurfaceControl>& sc, const Rect& destinationFrame) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eDestinationFrameChanged; + s->destinationFrame = destinationFrame; + + registerSurfaceControlForCallback(sc); + return *this; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { @@ -2067,6 +2078,16 @@ status_t SurfaceComposerClient::removeFpsListener(const sp<gui::IFpsListener>& l return ComposerService::getComposerService()->removeFpsListener(listener); } +status_t SurfaceComposerClient::addTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener) { + return ComposerService::getComposerService()->addTunnelModeEnabledListener(listener); +} + +status_t SurfaceComposerClient::removeTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener) { + return ComposerService::getComposerService()->removeTunnelModeEnabledListener(listener); +} + bool SurfaceComposerClient::getDisplayBrightnessSupport(const sp<IBinder>& displayToken) { bool support = false; ComposerService::getComposerService()->getDisplayBrightnessSupport(displayToken, &support); diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index 37750faa8f..d7c07b9755 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -166,7 +166,7 @@ void SurfaceControl::updateDefaultBufferSize(uint32_t width, uint32_t height) { Mutex::Autolock _l(mLock); mWidth = width; mHeight = height; if (mBbq) { - mBbq->update(this, width, height, mFormat); + mBbq->update(mBbqChild, width, height, mFormat); } } diff --git a/libs/gui/aidl/android/gui/ITunnelModeEnabledListener.aidl b/libs/gui/aidl/android/gui/ITunnelModeEnabledListener.aidl new file mode 100644 index 0000000000..2a89ccacec --- /dev/null +++ b/libs/gui/aidl/android/gui/ITunnelModeEnabledListener.aidl @@ -0,0 +1,29 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +/** @hide */ +oneway interface ITunnelModeEnabledListener { + + /** + * Called when tunnel mode status has changed. Tunnel mode is: + * - enabled when there is a sideband stream attached to one of the layers in + * surface flinger + * - disabled when there is no layer with a sideband stream + */ + void onTunnelModeEnabledChanged(boolean enabled); +} diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 139dbb7f6b..c4ca399e37 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -103,6 +103,8 @@ public: void setSidebandStream(const sp<NativeHandle>& stream); + uint32_t getLastTransformHint() const; + virtual ~BLASTBufferQueue(); private: diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h index a7f7d1defa..0ad3075a4d 100644 --- a/libs/gui/include/gui/BufferQueueProducer.h +++ b/libs/gui/include/gui/BufferQueueProducer.h @@ -186,6 +186,10 @@ public: virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]) override; + // See IGraphicBufferProducer::getLastQueuedBuffer + virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, + Rect* outRect, uint32_t* outTransform) override; + // See IGraphicBufferProducer::getFrameTimestamps virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override; diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index c3b92622b6..98df83453d 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -640,6 +640,22 @@ public: virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]) = 0; + // Returns the last queued buffer along with a fence which must signal + // before the contents of the buffer are read. If there are no buffers in + // the queue, outBuffer will be populated with nullptr and outFence will be + // populated with Fence::NO_FENCE + // + // outRect & outTransform are not modified if outBuffer is null. + // + // Returns NO_ERROR or the status of the Binder transaction + virtual status_t getLastQueuedBuffer([[maybe_unused]] sp<GraphicBuffer>* outBuffer, + [[maybe_unused]] sp<Fence>* outFence, + [[maybe_unused]] Rect* outRect, + [[maybe_unused]] uint32_t* outTransform) { + // Too many things implement IGraphicBufferProducer... + return UNKNOWN_TRANSACTION; + } + // Gets the frame events that haven't already been retrieved. virtual void getFrameTimestamps(FrameEventHistoryDelta* /*outDelta*/) {} diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index cb0468901a..439d90ad10 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -21,6 +21,7 @@ #include <android/gui/IHdrLayerInfoListener.h> #include <android/gui/IScreenCaptureListener.h> #include <android/gui/ITransactionTraceListener.h> +#include <android/gui/ITunnelModeEnabledListener.h> #include <binder/IBinder.h> #include <binder/IInterface.h> #include <gui/FrameTimelineInfo.h> @@ -377,6 +378,21 @@ public: */ virtual status_t removeFpsListener(const sp<gui::IFpsListener>& listener) = 0; + /* Registers a listener to receive tunnel mode enabled updates from SurfaceFlinger. + * + * Requires ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t addTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener) = 0; + + /* + * Removes a listener that was receiving tunnel mode enabled updates from SurfaceFlinger. + * + * Requires ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t removeTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener) = 0; + /* Sets the refresh rate boundaries for the display. * * The primary refresh rate range represents display manager's general guidance on the display @@ -607,6 +623,8 @@ public: ADD_HDR_LAYER_INFO_LISTENER, REMOVE_HDR_LAYER_INFO_LISTENER, ON_PULL_ATOM, + ADD_TUNNEL_MODE_ENABLED_LISTENER, + REMOVE_TUNNEL_MODE_ENABLED_LISTENER, // Always append new enum to the end. }; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 3947f22462..16430b324d 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -104,7 +104,7 @@ struct layer_state_t { eHasListenerCallbacksChanged = 0x20000000, eInputInfoChanged = 0x40000000, eCornerRadiusChanged = 0x80000000, - /* was eFrameChanged, now available 0x1'00000000, */ + eDestinationFrameChanged = 0x1'00000000, eCachedBufferChanged = 0x2'00000000, eBackgroundColorChanged = 0x4'00000000, eMetadataChanged = 0x8'00000000, @@ -228,6 +228,7 @@ struct layer_state_t { StretchEffect stretchEffect; Rect bufferCrop; + Rect destinationFrame; // Listens to when the buffer is safe to be released. This is used for blast // layers only. The callback includes a release fence as well as the graphic diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index d22bdaaa98..48885ebdbb 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -276,9 +276,9 @@ private: int dispatchAddQueueInterceptor(va_list args); int dispatchAddQueryInterceptor(va_list args); int dispatchGetLastQueuedBuffer(va_list args); + int dispatchGetLastQueuedBuffer2(va_list args); int dispatchSetFrameTimelineInfo(va_list args); int dispatchGetExtraBufferCount(va_list args); - bool transformToDisplayInverse(); protected: virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); @@ -490,6 +490,8 @@ protected: // mTransformHint is the transform probably applied to buffers of this // window. this is only a hint, actual transform may differ. uint32_t mTransformHint; + virtual uint32_t getTransformHint() const { return mTransformHint; } + bool transformToDisplayInverse() const; // mProducerControlledByApp whether this buffer producer is controlled // by the application diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index f3439c4c84..2582882c85 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -50,6 +50,7 @@ class HdrCapabilities; class ISurfaceComposerClient; class IGraphicBufferProducer; class IRegionSamplingListener; +class ITunnelModeEnabledListener; class Region; struct SurfaceControlStats { @@ -538,11 +539,24 @@ public: // transactions from blocking each other. Transaction& setApplyToken(const sp<IBinder>& token); - Transaction& setStretchEffect(const sp<SurfaceControl>& sc, float left, float top, - float right, float bottom, float vecX, float vecY, - float maxAmount); + /** + * Provides the stretch effect configured on a container that the + * surface is rendered within. + * @param sc target surface the stretch should be applied to + * @param stretchEffect the corresponding stretch effect to be applied + * to the surface. This can be directly on the surface itself or + * configured from a parent of the surface in which case the + * StretchEffect provided has parameters mapping the position of + * the surface within the container that has the stretch configured + * on it + * @return The transaction being constructed + */ + Transaction& setStretchEffect(const sp<SurfaceControl>& sc, + const StretchEffect& stretchEffect); Transaction& setBufferCrop(const sp<SurfaceControl>& sc, const Rect& bufferCrop); + Transaction& setDestinationFrame(const sp<SurfaceControl>& sc, + const Rect& destinationFrame); status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); @@ -597,6 +611,10 @@ public: static status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener); static status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener); static status_t removeFpsListener(const sp<gui::IFpsListener>& listener); + static status_t addTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener); + static status_t removeTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener); private: virtual void onFirstRef(); diff --git a/services/surfaceflinger/TraceUtils.h b/libs/gui/include/gui/TraceUtils.h index 90a34a5ed8..b9ec14aaf0 100644 --- a/services/surfaceflinger/TraceUtils.h +++ b/libs/gui/include/gui/TraceUtils.h @@ -14,9 +14,6 @@ * limitations under the License. */ -// TODO(b/183120308): This file is a copy of f/b/libs/hwui/utils/TraceUtils.h -// It should be migrated to a common place where both SF and hwui could use it. - #pragma once #include <cutils/trace.h> diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index ea8c2957a1..b8d34c3160 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -845,6 +845,16 @@ public: return NO_ERROR; } status_t removeFpsListener(const sp<gui::IFpsListener>& /*listener*/) { return NO_ERROR; } + + status_t addTunnelModeEnabledListener(const sp<gui::ITunnelModeEnabledListener>& /*listener*/) { + return NO_ERROR; + } + + status_t removeTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& /*listener*/) { + return NO_ERROR; + } + status_t setDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/, ui::DisplayModeId /*defaultMode*/, bool /*allowGroupSwitching*/, diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 6f79f4b179..a63ec8fb2a 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -49,7 +49,6 @@ cc_library { "Keyboard.cpp", "KeyCharacterMap.cpp", "KeyLayoutMap.cpp", - "LatencyStatistics.cpp", "PropertyMap.cpp", "TouchVideoFrame.cpp", "VelocityControl.cpp", diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index deb4679558..70ed438112 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -39,6 +39,60 @@ using android::base::StringPrintf; namespace android { +namespace { + +float transformAngle(const ui::Transform& transform, float angleRadians) { + // Construct and transform a vector oriented at the specified clockwise angle from vertical. + // Coordinate system: down is increasing Y, right is increasing X. + float x = sinf(angleRadians); + float y = -cosf(angleRadians); + vec2 transformedPoint = transform.transform(x, y); + + // Determine how the origin is transformed by the matrix so that we + // can transform orientation vectors. + const vec2 origin = transform.transform(0, 0); + + transformedPoint.x -= origin.x; + transformedPoint.y -= origin.y; + + // Derive the transformed vector's clockwise angle from vertical. + float result = atan2f(transformedPoint.x, -transformedPoint.y); + if (result < -M_PI_2) { + result += M_PI; + } else if (result > M_PI_2) { + result -= M_PI; + } + return result; +} + +// Rotates the given point to the transform's orientation. If the display width and height are +// provided, the point is rotated in the screen space. Otherwise, the point is rotated about the +// origin. This helper is used to avoid the extra overhead of creating new Transforms. +vec2 rotatePoint(const ui::Transform& transform, float x, float y, int32_t displayWidth = 0, + int32_t displayHeight = 0) { + // 0x7 encapsulates all 3 rotations (see ui::Transform::RotationFlags) + static const int ALL_ROTATIONS_MASK = 0x7; + const uint32_t orientation = (transform.getOrientation() & ALL_ROTATIONS_MASK); + if (orientation == ui::Transform::ROT_0) { + return {x, y}; + } + + vec2 xy(x, y); + if (orientation == ui::Transform::ROT_90) { + xy.x = displayHeight - y; + xy.y = x; + } else if (orientation == ui::Transform::ROT_180) { + xy.x = displayWidth - x; + xy.y = displayHeight - y; + } else if (orientation == ui::Transform::ROT_270) { + xy.x = y; + xy.y = displayWidth - x; + } + return xy; +} + +} // namespace + const char* motionClassificationToString(MotionClassification classification) { switch (classification) { case MotionClassification::NONE: @@ -315,9 +369,14 @@ void PointerCoords::copyFrom(const PointerCoords& other) { } void PointerCoords::transform(const ui::Transform& transform) { - vec2 newCoords = transform.transform(getX(), getY()); - setAxisValue(AMOTION_EVENT_AXIS_X, newCoords.x); - setAxisValue(AMOTION_EVENT_AXIS_Y, newCoords.y); + const vec2 xy = transform.transform(getXYValue()); + setAxisValue(AMOTION_EVENT_AXIS_X, xy.x); + setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y); + + if (BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_ORIENTATION)) { + const float val = getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); + setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, transformAngle(transform, val)); + } } // --- PointerProperties --- @@ -444,45 +503,32 @@ const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( } float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, - size_t historicalIndex) const { - if (axis != AMOTION_EVENT_AXIS_X && axis != AMOTION_EVENT_AXIS_Y) { - return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); - } - // 0x7 encapsulates all 3 rotations (see ui::Transform::RotationFlags) - static const int ALL_ROTATIONS_MASK = 0x7; - uint32_t orientation = (mTransform.getOrientation() & ALL_ROTATIONS_MASK); - if (orientation == ui::Transform::ROT_0) { - return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); - } + size_t historicalIndex) const { + const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex); - // For compatibility, convert raw coordinates into "oriented screen space". Once app developers - // are educated about getRaw, we can consider removing this. - vec2 xy = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getXYValue(); - const float unrotatedX = xy.x; - if (orientation == ui::Transform::ROT_90) { - xy.x = mDisplayHeight - xy.y; - xy.y = unrotatedX; - } else if (orientation == ui::Transform::ROT_180) { - xy.x = mDisplayWidth - xy.x; - xy.y = mDisplayHeight - xy.y; - } else if (orientation == ui::Transform::ROT_270) { - xy.x = xy.y; - xy.y = mDisplayWidth - unrotatedX; + if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) { + // For compatibility, convert raw coordinates into "oriented screen space". Once app + // developers are educated about getRaw, we can consider removing this. + const vec2 xy = rotatePoint(mTransform, coords->getX(), coords->getY(), mDisplayWidth, + mDisplayHeight); + static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); + return xy[axis]; } - static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); - return xy[axis]; + + return coords->getAxisValue(axis); } float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const { - if (axis != AMOTION_EVENT_AXIS_X && axis != AMOTION_EVENT_AXIS_Y) { - return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); + const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex); + + if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) { + const vec2 xy = mTransform.transform(coords->getXYValue()); + static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); + return xy[axis]; } - vec2 vals = mTransform.transform( - getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getXYValue()); - static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); - return vals[axis]; + return coords->getAxisValue(axis); } ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const { @@ -513,60 +559,30 @@ void MotionEvent::scale(float globalScaleFactor) { } } -static vec2 transformPoint(const std::array<float, 9>& matrix, float x, float y) { - // Apply perspective transform like Skia. - float newX = matrix[0] * x + matrix[1] * y + matrix[2]; - float newY = matrix[3] * x + matrix[4] * y + matrix[5]; - float newZ = matrix[6] * x + matrix[7] * y + matrix[8]; - if (newZ) { - newZ = 1.0f / newZ; - } - vec2 transformedPoint; - transformedPoint.x = newX * newZ; - transformedPoint.y = newY * newZ; - return transformedPoint; -} - -static float transformAngle(const std::array<float, 9>& matrix, float angleRadians, float originX, - float originY) { - // Construct and transform a vector oriented at the specified clockwise angle from vertical. - // Coordinate system: down is increasing Y, right is increasing X. - float x = sinf(angleRadians); - float y = -cosf(angleRadians); - vec2 transformedPoint = transformPoint(matrix, x, y); - - transformedPoint.x -= originX; - transformedPoint.y -= originY; - - // Derive the transformed vector's clockwise angle from vertical. - float result = atan2f(transformedPoint.x, -transformedPoint.y); - if (result < - M_PI_2) { - result += M_PI; - } else if (result > M_PI_2) { - result -= M_PI; - } - return result; -} - void MotionEvent::transform(const std::array<float, 9>& matrix) { - // We want to preserve the rawX and rawY so we just update the transform - // using the values of the transform passed in + // We want to preserve the raw axes values stored in the PointerCoords, so we just update the + // transform using the values passed in. ui::Transform newTransform; newTransform.set(matrix); mTransform = newTransform * mTransform; - // Determine how the origin is transformed by the matrix so that we - // can transform orientation vectors. - vec2 origin = transformPoint(matrix, 0, 0); + // We need to update the AXIS_ORIENTATION value here to maintain the old behavior where the + // orientation angle is not affected by the initial transformation set in the MotionEvent. + std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(), + [&newTransform](PointerCoords& c) { + float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); + c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, + transformAngle(newTransform, orientation)); + }); +} + +void MotionEvent::applyTransform(const std::array<float, 9>& matrix) { + ui::Transform transform; + transform.set(matrix); // Apply the transformation to all samples. - size_t numSamples = mSamplePointerCoords.size(); - for (size_t i = 0; i < numSamples; i++) { - PointerCoords& c = mSamplePointerCoords.editItemAt(i); - float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); - c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, - transformAngle(matrix, orientation, origin.x, origin.y)); - } + std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(), + [&transform](PointerCoords& c) { c.transform(transform); }); } #ifdef __linux__ diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 31027b6476..61d72adb33 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -87,8 +87,10 @@ std::string getInputDeviceConfigurationFilePathByName( // Search system repository. std::string path; - // Treblized input device config files will be located /odm/usr or /vendor/usr. - const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")}; + // Treblized input device config files will be located /product/usr, /system_ext/usr, + // /odm/usr or /vendor/usr. + const char* rootsForPartition[]{"/product", "/system_ext", "/odm", "/vendor", + getenv("ANDROID_ROOT")}; for (size_t i = 0; i < size(rootsForPartition); i++) { if (rootsForPartition[i] == nullptr) { continue; diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index de75691cc7..dd1c46265c 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -96,28 +96,42 @@ inline static const char* toString(bool value) { // --- InputMessage --- bool InputMessage::isValid(size_t actualSize) const { - if (size() == actualSize) { - switch (header.type) { - case Type::KEY: - return true; - case Type::MOTION: - return body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS; - case Type::FINISHED: - return true; - case Type::FOCUS: - return true; - case Type::CAPTURE: - return true; - case Type::DRAG: - return true; - case Type::TIMELINE: - const nsecs_t gpuCompletedTime = - body.timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; - const nsecs_t presentTime = - body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; - return presentTime > gpuCompletedTime; + if (size() != actualSize) { + ALOGE("Received message of incorrect size %zu (expected %zu)", actualSize, size()); + return false; + } + + switch (header.type) { + case Type::KEY: + return true; + case Type::MOTION: { + const bool valid = + body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS; + if (!valid) { + ALOGE("Received invalid MOTION: pointerCount = %" PRIu32, body.motion.pointerCount); + } + return valid; + } + case Type::FINISHED: + case Type::FOCUS: + case Type::CAPTURE: + case Type::DRAG: + return true; + case Type::TIMELINE: { + const nsecs_t gpuCompletedTime = + body.timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; + const nsecs_t presentTime = + body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; + const bool valid = presentTime > gpuCompletedTime; + if (!valid) { + ALOGE("Received invalid TIMELINE: gpuCompletedTime = %" PRId64 + " presentTime = %" PRId64, + gpuCompletedTime, presentTime); + } + return valid; } } + ALOGE("Invalid message type: %" PRIu32, header.type); return false; } @@ -404,9 +418,7 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { } if (!msg->isValid(nRead)) { -#if DEBUG_CHANNEL_MESSAGES - ALOGD("channel '%s' ~ received invalid message", mName.c_str()); -#endif + ALOGE("channel '%s' ~ received invalid message of size %zd", mName.c_str(), nRead); return BAD_VALUE; } diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index 8546bbbb43..99477200db 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -57,11 +57,12 @@ bool InputWindowInfo::operator==(const InputWindowInfo& info) const { info.frameLeft == frameLeft && info.frameTop == frameTop && info.frameRight == frameRight && info.frameBottom == frameBottom && info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor && - info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) && - info.visible == visible && info.trustedOverlay == trustedOverlay && - info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode && - info.hasWallpaper == hasWallpaper && info.paused == paused && - info.ownerPid == ownerPid && info.ownerUid == ownerUid && + info.transform == transform && info.displayWidth == displayWidth && + info.displayHeight == displayHeight && + info.touchableRegion.hasSameRects(touchableRegion) && info.visible == visible && + info.trustedOverlay == trustedOverlay && info.focusable == focusable && + info.touchOcclusionMode == touchOcclusionMode && info.hasWallpaper == hasWallpaper && + info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid && info.packageName == packageName && info.inputFeatures == inputFeatures && info.displayId == displayId && info.portalToDisplayId == portalToDisplayId && info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop && @@ -99,6 +100,8 @@ status_t InputWindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->writeFloat(transform.dtdy()) ?: parcel->writeFloat(transform.dsdy()) ?: parcel->writeFloat(transform.ty()) ?: + parcel->writeInt32(displayWidth) ?: + parcel->writeInt32(displayHeight) ?: parcel->writeBool(visible) ?: parcel->writeBool(focusable) ?: parcel->writeBool(hasWallpaper) ?: @@ -153,6 +156,8 @@ status_t InputWindowInfo::readFromParcel(const android::Parcel* parcel) { parcel->readFloat(&dtdy) ?: parcel->readFloat(&dsdy) ?: parcel->readFloat(&ty) ?: + parcel->readInt32(&displayWidth) ?: + parcel->readInt32(&displayHeight) ?: parcel->readBool(&visible) ?: parcel->readBool(&focusable) ?: parcel->readBool(&hasWallpaper) ?: diff --git a/libs/input/LatencyStatistics.cpp b/libs/input/LatencyStatistics.cpp deleted file mode 100644 index 394da22a62..0000000000 --- a/libs/input/LatencyStatistics.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2019 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/LatencyStatistics.h> - -#include <android-base/chrono_utils.h> - -#include <cmath> -#include <limits> - -namespace android { - -LatencyStatistics::LatencyStatistics(std::chrono::seconds period) : mReportPeriod(period) { - reset(); -} - -/** - * Add a raw value to the statistics - */ -void LatencyStatistics::addValue(float value) { - if (value < mMin) { - mMin = value; - } - if (value > mMax) { - mMax = value; - } - mSum += value; - mSum2 += value * value; - mCount++; -} - -/** - * Get the mean. Should not be called if no samples have been added. - */ -float LatencyStatistics::getMean() { - return mSum / mCount; -} - -/** - * Get the standard deviation. Should not be called if no samples have been added. - */ -float LatencyStatistics::getStDev() { - float mean = getMean(); - return sqrt(mSum2 / mCount - mean * mean); -} - -float LatencyStatistics::getMin() { - return mMin; -} - -float LatencyStatistics::getMax() { - return mMax; -} - -size_t LatencyStatistics::getCount() { - return mCount; -} - -/** - * Reset internal state. The variable 'when' is the time when the data collection started. - * Call this to start a new data collection window. - */ -void LatencyStatistics::reset() { - mMax = std::numeric_limits<float>::lowest(); - mMin = std::numeric_limits<float>::max(); - mSum = 0; - mSum2 = 0; - mCount = 0; - mLastReportTime = std::chrono::steady_clock::now(); -} - -bool LatencyStatistics::shouldReport() { - std::chrono::duration timeSinceReport = std::chrono::steady_clock::now() - mLastReportTime; - return mCount != 0 && timeSinceReport >= mReportPeriod; -} - -} // namespace android diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index fe8a5677d9..6ffc6a8926 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -19,7 +19,6 @@ cc_test { "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", "InputWindow_test.cpp", - "LatencyStatistics_test.cpp", "TouchVideoFrame_test.cpp", "VelocityTracker_test.cpp", "VerifiedInputEvent_test.cpp", @@ -49,8 +48,8 @@ cc_test { cc_library_static { name: "StructLayout_test", srcs: ["StructLayout_test.cpp"], + compile_multilib: "both", cflags: [ - "-O0", "-Wall", "-Werror", "-Wextra", diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 15ab428979..32b72ba616 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -636,28 +636,54 @@ TEST_F(MotionEventTest, Transform) { ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); } -TEST_F(MotionEventTest, RawCompatTransform) { - auto createTouchDownEvent = [](int x, int y, ui::Transform transform) { - std::vector<PointerProperties> pointerProperties; - pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER}); - std::vector<PointerCoords> pointerCoords; - pointerCoords.emplace_back().clear(); - pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, x); - pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, y); - nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC); - MotionEvent event; - event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN, - /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, - /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, - /* buttonState */ 0, MotionClassification::NONE, transform, - /* xPrecision */ 0, /* yPrecision */ 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, /* displayWidth */ 400, - /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(), - pointerProperties.data(), pointerCoords.data()); - return event; - }; +MotionEvent createTouchDownEvent(int x, int y, ui::Transform transform) { + std::vector<PointerProperties> pointerProperties; + pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER}); + std::vector<PointerCoords> pointerCoords; + pointerCoords.emplace_back().clear(); + pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, x); + pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, y); + nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC); + MotionEvent event; + event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN, + /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, + /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, + /* buttonState */ 0, MotionClassification::NONE, transform, + /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, /* displayWidth */ 400, + /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(), + pointerProperties.data(), pointerCoords.data()); + return event; +} +TEST_F(MotionEventTest, ApplyTransform) { + // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). + ui::Transform identity; + ui::Transform xform(ui::Transform::ROT_90, 800, 400); + xform.set(xform.tx() + 20, xform.ty() + 40); + MotionEvent event = createTouchDownEvent(60, 100, xform); + ASSERT_EQ(700, event.getRawX(0)); + ASSERT_EQ(60, event.getRawY(0)); + ASSERT_NE(event.getRawX(0), event.getX(0)); + ASSERT_NE(event.getRawY(0), event.getY(0)); + + MotionEvent changedEvent = createTouchDownEvent(60, 100, identity); + const std::array<float, 9> rowMajor{xform[0][0], xform[1][0], xform[2][0], + xform[0][1], xform[1][1], xform[2][1], + xform[0][2], xform[1][2], xform[2][2]}; + changedEvent.applyTransform(rowMajor); + + // transformContent effectively rotates the raw coordinates, so those should now include + // both rotation AND offset + ASSERT_EQ(720, changedEvent.getRawX(0)); + ASSERT_EQ(100, changedEvent.getRawY(0)); + + // The transformed output should be the same then + ASSERT_NEAR(event.getX(0), changedEvent.getX(0), 0.001); + ASSERT_NEAR(event.getY(0), changedEvent.getY(0), 0.001); +} + +TEST_F(MotionEventTest, RawCompatTransform) { { // Make sure raw is raw regardless of transform translation. ui::Transform xform; diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp index c18a17f1ae..493f2f4495 100644 --- a/libs/input/tests/InputWindow_test.cpp +++ b/libs/input/tests/InputWindow_test.cpp @@ -55,6 +55,8 @@ TEST(InputWindowInfo, Parcelling) { i.globalScaleFactor = 0.3; i.alpha = 0.7; i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1}); + i.displayWidth = 1000; + i.displayHeight = 2000; i.visible = false; i.focusable = false; i.hasWallpaper = false; @@ -91,6 +93,8 @@ TEST(InputWindowInfo, Parcelling) { ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor); ASSERT_EQ(i.alpha, i2.alpha); ASSERT_EQ(i.transform, i2.transform); + ASSERT_EQ(i.displayWidth, i2.displayWidth); + ASSERT_EQ(i.displayHeight, i2.displayHeight); ASSERT_EQ(i.visible, i2.visible); ASSERT_EQ(i.focusable, i2.focusable); ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper); diff --git a/libs/input/tests/LatencyStatistics_test.cpp b/libs/input/tests/LatencyStatistics_test.cpp deleted file mode 100644 index eb12d4ef6f..0000000000 --- a/libs/input/tests/LatencyStatistics_test.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2019 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 <gtest/gtest.h> -#include <input/LatencyStatistics.h> -#include <cmath> -#include <limits> -#include <thread> - -namespace android { -namespace test { - -TEST(LatencyStatisticsTest, ResetStats) { - LatencyStatistics stats{5min}; - stats.addValue(5.0); - stats.addValue(19.3); - stats.addValue(20); - stats.reset(); - - ASSERT_EQ(stats.getCount(), 0u); - ASSERT_EQ(std::isnan(stats.getStDev()), true); - ASSERT_EQ(std::isnan(stats.getMean()), true); -} - -TEST(LatencyStatisticsTest, AddStatsValue) { - LatencyStatistics stats{5min}; - stats.addValue(5.0); - - ASSERT_EQ(stats.getMin(), 5.0); - ASSERT_EQ(stats.getMax(), 5.0); - ASSERT_EQ(stats.getCount(), 1u); - ASSERT_EQ(stats.getMean(), 5.0); - ASSERT_EQ(stats.getStDev(), 0.0); -} - -TEST(LatencyStatisticsTest, AddMultipleStatsValue) { - LatencyStatistics stats{5min}; - stats.addValue(4.0); - stats.addValue(6.0); - stats.addValue(8.0); - stats.addValue(10.0); - - float stdev = stats.getStDev(); - - ASSERT_EQ(stats.getMin(), 4.0); - ASSERT_EQ(stats.getMax(), 10.0); - ASSERT_EQ(stats.getCount(), 4u); - ASSERT_EQ(stats.getMean(), 7.0); - ASSERT_EQ(stdev * stdev, 5.0); -} - -TEST(LatencyStatisticsTest, ShouldReportStats) { - LatencyStatistics stats{0min}; - stats.addValue(5.0); - - std::this_thread::sleep_for(1us); - - ASSERT_EQ(stats.shouldReport(), true); -} - -} // namespace test -} // namespace android
\ No newline at end of file diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index 3865ba5777..f0e1c4d749 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -300,6 +300,9 @@ enum ANativeWindow_ChangeFrameRateStrategy { * this ANativeWindow is consumed by something other than the system compositor, * e.g. a media codec, this call has no effect. * + * You can register for changes in the refresh rate using + * \a AChoreographer_registerRefreshRateCallback. + * * Available since API level 31. * * \param window pointer to an ANativeWindow object. diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index cc82bb4699..935eded983 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -257,6 +257,7 @@ enum { NATIVE_WINDOW_SET_QUERY_INTERCEPTOR = 47, /* private */ NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO = 48, /* private */ NATIVE_WINDOW_GET_EXTRA_BUFFER_COUNT = 49, /* private */ + NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2 = 50, /* private */ // clang-format on }; @@ -1067,6 +1068,31 @@ static inline int ANativeWindow_getLastQueuedBuffer(ANativeWindow* window, } /** + * Retrieves the last queued buffer for this window, along with the fence that + * fires when the buffer is ready to be read. The cropRect & transform should be applied to the + * buffer's content. + * + * If there was no buffer previously queued, then outBuffer will be NULL and + * the value of outFence will be -1. + * + * Note that if outBuffer is not NULL, then the caller will hold a reference + * onto the buffer. Accordingly, the caller must call AHardwareBuffer_release + * when the buffer is no longer needed so that the system may reclaim the + * buffer. + * + * \return NO_ERROR on success. + * \return NO_MEMORY if there was insufficient memory. + * \return STATUS_UNKNOWN_TRANSACTION if this ANativeWindow doesn't support this method, callers + * should fall back to ANativeWindow_getLastQueuedBuffer instead. + */ +static inline int ANativeWindow_getLastQueuedBuffer2(ANativeWindow* window, + AHardwareBuffer** outBuffer, int* outFence, + ARect* outCropRect, uint32_t* outTransform) { + return window->perform(window, NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2, outBuffer, outFence, + outCropRect, outTransform); +} + +/** * Retrieves an identifier for the next frame to be queued by this window. * * \return the next frame id. diff --git a/libs/permission/Android.bp b/libs/permission/Android.bp index a5712b319f..3243a6b5f4 100644 --- a/libs/permission/Android.bp +++ b/libs/permission/Android.bp @@ -7,17 +7,43 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aidl_interface { + name: "framework-permission-aidl", + unstable: true, + local_include_dir: "aidl", + backend: { + ndk: { + enabled: false + } + }, + srcs: [ + "aidl/android/content/AttributionSourceState.aidl", + "aidl/android/permission/IPermissionChecker.aidl", + ], +} + cc_library_shared { name: "libpermission", + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], srcs: [ "AppOpsManager.cpp", "IAppOpsCallback.cpp", "IAppOpsService.cpp", + "android/permission/PermissionChecker.cpp", ], export_include_dirs: ["include"], shared_libs: [ + "libutils", "libbinder", + "libcutils", "liblog", - "libutils", ], + static_libs: [ + "framework-permission-aidl-cpp", + ], + export_static_lib_headers: ["framework-permission-aidl-cpp"], } diff --git a/libs/permission/aidl/android/content/AttributionSourceState.aidl b/libs/permission/aidl/android/content/AttributionSourceState.aidl new file mode 100644 index 0000000000..b6e54bf153 --- /dev/null +++ b/libs/permission/aidl/android/content/AttributionSourceState.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content; + +/** + * Payload for the {@link AttributionSource} class needed to interoperate + * with different languages. + * + * {@hide} + */ +parcelable AttributionSourceState { + /** The UID that is accessing the permission protected data. */ + int uid; + /** The package that is accessing the permission protected data. */ + @nullable @utf8InCpp String packageName; + /** The attribution tag of the app accessing the permission protected data. */ + @nullable @utf8InCpp String attributionTag; + /** Unique token for that source. */ + @nullable IBinder token; + /** Permissions that should be considered revoked regardless if granted. */ + @nullable @utf8InCpp String[] renouncedPermissions; + /** The next app to receive the permission protected data. */ + // TODO: We use an array as a workaround - the C++ backend doesn't + // support referring to the parcelable as it expects ctor/dtor + @nullable AttributionSourceState[] next; +} diff --git a/libs/permission/aidl/android/permission/IPermissionChecker.aidl b/libs/permission/aidl/android/permission/IPermissionChecker.aidl new file mode 100644 index 0000000000..1f0e32d248 --- /dev/null +++ b/libs/permission/aidl/android/permission/IPermissionChecker.aidl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.permission; + +import android.content.AttributionSourceState; + +/** + * Interface to communicate directly with the permission checker service. + */ +interface IPermissionChecker { + const int PERMISSION_GRANTED = 0; + const int PERMISSION_SOFT_DENIED = 1; + const int PERMISSION_HARD_DENIED = 2; + + int checkPermission(String permission, in AttributionSourceState attributionSource, + @nullable String message, boolean forDataDelivery, boolean startDataDelivery, + boolean fromDatasource); + + void finishDataDelivery(String op, in AttributionSourceState attributionSource); + + int checkOp(int op, in AttributionSourceState attributionSource, + String message, boolean forDataDelivery, boolean startDataDelivery); +} diff --git a/libs/permission/android/permission/PermissionChecker.cpp b/libs/permission/android/permission/PermissionChecker.cpp new file mode 100644 index 0000000000..a8083ee410 --- /dev/null +++ b/libs/permission/android/permission/PermissionChecker.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <mutex> +#include <include/android/permission/PermissionChecker.h> +#include <binder/Binder.h> +#include <binder/IServiceManager.h> + +#include <utils/SystemClock.h> + +#include <sys/types.h> +#include <private/android_filesystem_config.h> + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "PermissionChecker" + +namespace android { + +using android::content::AttributionSourceState; + +PermissionChecker::PermissionChecker() +{ +} + +sp<IPermissionChecker> PermissionChecker::getService() +{ + static String16 permission_checker("permission_checker"); + + std::lock_guard<Mutex> scoped_lock(mLock); + int64_t startTime = 0; + sp<IPermissionChecker> service = mService; + while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { + sp<IBinder> binder = defaultServiceManager()->checkService(permission_checker); + if (binder == nullptr) { + // Wait for the permission checker service to come back... + if (startTime == 0) { + startTime = uptimeMillis(); + ALOGW("Waiting for permission checker service"); + } else if ((uptimeMillis() - startTime) > 10000) { + ALOGE("Waiting too long for permission checker service, giving up"); + service = nullptr; + break; + } + sleep(1); + } else { + mService = interface_cast<IPermissionChecker>(binder); + } + } + return mService; +} + +PermissionChecker::PermissionResult + PermissionChecker::checkPermissionForDataDeliveryFromDatasource( + const String16& permission, AttributionSourceState& attributionSource, + const String16& message) +{ + return static_cast<PermissionResult>(checkPermission(permission, attributionSource, message, + /*forDataDelivery*/ true, /*startDataDelivery*/ false,/*fromDatasource*/ true)); +} + +PermissionChecker::PermissionResult + PermissionChecker::checkPermissionForStartDataDeliveryFromDatasource( + const String16& permission, AttributionSourceState& attributionSource, + const String16& message) +{ + return static_cast<PermissionResult>(checkPermission(permission, attributionSource, message, + /*forDataDelivery*/ true, /*startDataDelivery*/ true, /*fromDatasource*/ true)); +} + +void PermissionChecker::finishDataDelivery(const String16& op, + AttributionSourceState& attributionSource) +{ + sp<IPermissionChecker> service = getService(); + if (service != nullptr) { + binder::Status status = service->finishDataDelivery(op, attributionSource); + if (!status.isOk()) { + ALOGE("finishDataDelivery failed: %s", status.exceptionMessage().c_str()); + } + } +} + +int32_t PermissionChecker::checkPermission(const String16& permission, + AttributionSourceState& attributionSource, const String16& message, + bool forDataDelivery, bool startDataDelivery, bool fromDatasource) +{ + sp<IPermissionChecker> service = getService(); + if (service != nullptr) { + int32_t result; + binder::Status status = service->checkPermission(permission, attributionSource, message, + forDataDelivery, startDataDelivery, fromDatasource, &result); + if (status.isOk()) { + return result; + } + ALOGE("checkPermission failed: %s", status.exceptionMessage().c_str()); + } + return PERMISSION_DENIED; +} + +} // namespace android diff --git a/libs/permission/include/android/permission/PermissionChecker.h b/libs/permission/include/android/permission/PermissionChecker.h new file mode 100644 index 0000000000..20ab51fc8a --- /dev/null +++ b/libs/permission/include/android/permission/PermissionChecker.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/content/AttributionSourceState.h> +#include <android/permission/IPermissionChecker.h> + +#include <utils/threads.h> + +#include <optional> + +#ifdef __ANDROID_VNDK__ +#error "This header is not visible to vendors" +#endif + +// --------------------------------------------------------------------------- +namespace android { + +using android::content::AttributionSourceState; +using android::permission::IPermissionChecker; + +class PermissionChecker +{ +public: + + enum PermissionResult { + + /** + * The permission is granted. + */ + PERMISSION_GRANTED = IPermissionChecker::PERMISSION_GRANTED, + + /** + * The permission is denied. Applicable only to runtime and app op permissions. + * + * Returned when: + * - the runtime permission is granted, but the corresponding app op is denied + * for runtime permissions. + * - the app ops is ignored for app op permissions. + * + */ + PERMISSION_SOFT_DENIED = IPermissionChecker::PERMISSION_SOFT_DENIED, + + /** + * The permission is denied. + * + * Returned when: + * - the permission is denied for non app op permissions. + * - the app op is denied or app op is AppOpsManager#MODE_DEFAULT and permission is denied. + */ + PERMISSION_HARD_DENIED = IPermissionChecker::PERMISSION_HARD_DENIED + }; + + PermissionChecker(); + + /** + * Checks whether a given data access chain described by the given attribution source + * has a given permission and whether the app op that corresponds to this permission + * is allowed. Call this method if you are the datasource which would not blame you for + * access to the data since you are the data. Note that the attribution source chain + * + * NOTE: The attribution source should be for yourself with its next attribution + * source being the app that would receive the data from you. + * + * NOTE: Use this method only for permission checks at the point where you will deliver + * the permission protected data to clients. + * + * @param permission The permission to check. + * @param attributionSource The attribution chain to check. + * @param message A message describing the reason the permission was checked. + * @return The permission check result which is either PERMISSION_GRANTED, + * or PERMISSION_SOFT_DENIED or PERMISSION_HARD_DENIED. + */ + PermissionChecker::PermissionResult checkPermissionForDataDeliveryFromDatasource( + const String16& permission, AttributionSourceState& attributionSource, + const String16& message); + + /** + * Checks whether a given data access chain described by the given attribution source + * has a given permission and whether the app op that corresponds to this permission + * is allowed. The app ops are also marked as started. This is useful for long running + * permissions like camera and microphone. + * + * NOTE: The attribution source should be for yourself with its next attribution + * source being the app that would receive the data from you. + * + * NOTE: Use this method only for permission checks at the point where you will deliver + * the permission protected data to clients. + * + * @param permission The permission to check. + * @param attributionSource The attribution chain to check. + * @param message A message describing the reason the permission was checked. + * @return The permission check result which is either PERMISSION_GRANTED, + * or PERMISSION_SOFT_DENIED or PERMISSION_HARD_DENIED. + */ + PermissionResult checkPermissionForStartDataDeliveryFromDatasource( + const String16& permission, AttributionSourceState& attributionSource, + const String16& message); + + /** + * Finishes an ongoing op for data access chain described by the given + * attribution source. + * + * @param op The op to finish. + * @param attributionSource The attribution chain for which to finish data delivery. + */ + void finishDataDelivery(const String16& op, AttributionSourceState& attributionSource); + +private: + Mutex mLock; + sp<IPermissionChecker> mService; + sp<IPermissionChecker> getService(); + + int32_t checkPermission(const String16& permission, AttributionSourceState& attributionSource, + const String16& message, bool forDataDelivery, bool startDataDelivery, + bool fromDatasource); +}; + + +} // namespace android + +// --------------------------------------------------------------------------- diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index f395ab43d8..570c7bc08d 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -95,6 +95,7 @@ filegroup { "skia/debug/SkiaMemoryReporter.cpp", "skia/filters/BlurFilter.cpp", "skia/filters/LinearEffect.cpp", + "skia/filters/StretchShaderFactory.cpp" ], } @@ -117,7 +118,7 @@ cc_library_static { include_dirs: [ "external/skia/src/gpu", ], - whole_static_libs: ["libskia"], + whole_static_libs: ["libskia_renderengine"], lto: { thin: true, }, diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index d87315fdc2..3c582383ca 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -249,7 +249,7 @@ std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCre // initialize EGL for the default display EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (!eglInitialize(display, nullptr, nullptr)) { - LOG_ALWAYS_FATAL("failed to initialize EGL"); + LOG_ALWAYS_FATAL("failed to initialize EGL. EGL error=0x%x", eglGetError()); } const auto eglVersion = eglQueryString(display, EGL_VERSION); @@ -1233,8 +1233,10 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, } } - mState.maxMasteringLuminance = layer->source.buffer.maxMasteringLuminance; - mState.maxContentLuminance = layer->source.buffer.maxContentLuminance; + // Ensure luminance is at least 100 nits to avoid div-by-zero + const float maxLuminance = std::max(100.f, layer->source.buffer.maxLuminanceNits); + mState.maxMasteringLuminance = maxLuminance; + mState.maxContentLuminance = maxLuminance; mState.projectionMatrix = projectionMatrix * layer->geometry.positionTransform; const FloatRect bounds = layer->geometry.boundaries; diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h index a637796267..53fa622ad8 100644 --- a/libs/renderengine/include/renderengine/DisplaySettings.h +++ b/libs/renderengine/include/renderengine/DisplaySettings.h @@ -60,6 +60,9 @@ struct DisplaySettings { // capture of a device in landscape while the buffer is in portrait // orientation. uint32_t orientation = ui::Transform::ROT_0; + + // SDR white point, -1f if unknown + float sdrWhitePointNits = -1.f; }; static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) { diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h index e976a5a7fc..715f3f84ae 100644 --- a/libs/renderengine/include/renderengine/LayerSettings.h +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -64,8 +64,8 @@ struct Buffer { // HDR color-space setting for Y410. bool isY410BT2020 = false; - float maxMasteringLuminance = 0.0; - float maxContentLuminance = 0.0; + + float maxLuminanceNits = 0.0; }; // Metadata describing the layer geometry. @@ -107,6 +107,9 @@ struct PixelSource { * material design guidelines. */ struct ShadowSettings { + // Boundaries of the shadow. + FloatRect boundaries = FloatRect(); + // Color to the ambient shadow. The alpha is premultiplied. vec4 ambientColor = vec4(); @@ -150,6 +153,10 @@ struct LayerSettings { // True if blending will be forced to be disabled. bool disableBlending = false; + // If true, then this layer casts a shadow and/or blurs behind it, but it does + // not otherwise draw any of the layer's other contents. + bool skipContentDraw = false; + ShadowSettings shadow; int backgroundBlurRadius = 0; @@ -175,8 +182,7 @@ static inline bool operator==(const Buffer& lhs, const Buffer& rhs) { lhs.textureTransform == rhs.textureTransform && lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha && lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 && - lhs.maxMasteringLuminance == rhs.maxMasteringLuminance && - lhs.maxContentLuminance == rhs.maxContentLuminance; + lhs.maxLuminanceNits == rhs.maxLuminanceNits; } static inline bool operator==(const Geometry& lhs, const Geometry& rhs) { @@ -190,21 +196,10 @@ static inline bool operator==(const PixelSource& lhs, const PixelSource& rhs) { } static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& rhs) { - return lhs.ambientColor == rhs.ambientColor && lhs.spotColor == rhs.spotColor && - lhs.lightPos == rhs.lightPos && lhs.lightRadius == rhs.lightRadius && - lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent; -} - -static inline bool operator==(const BlurRegion& lhs, const BlurRegion& rhs) { - return lhs.alpha == rhs.alpha && lhs.cornerRadiusTL == rhs.cornerRadiusTL && - lhs.cornerRadiusTR == rhs.cornerRadiusTR && lhs.cornerRadiusBL == rhs.cornerRadiusBL && - lhs.cornerRadiusBR == rhs.cornerRadiusBR && lhs.blurRadius == rhs.blurRadius && - lhs.left == rhs.left && lhs.top == rhs.top && lhs.right == rhs.right && - lhs.bottom == rhs.bottom; -} - -static inline bool operator!=(const BlurRegion& lhs, const BlurRegion& rhs) { - return !(lhs == rhs); + return lhs.boundaries == rhs.boundaries && lhs.ambientColor == rhs.ambientColor && + lhs.spotColor == rhs.spotColor && lhs.lightPos == rhs.lightPos && + lhs.lightRadius == rhs.lightRadius && lhs.length == rhs.length && + lhs.casterIsTranslucent == rhs.casterIsTranslucent; } static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) { @@ -221,8 +216,11 @@ static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha && lhs.sourceDataspace == rhs.sourceDataspace && lhs.colorTransform == rhs.colorTransform && - lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow && - lhs.backgroundBlurRadius == rhs.backgroundBlurRadius; + lhs.disableBlending == rhs.disableBlending && + lhs.skipContentDraw == rhs.skipContentDraw && lhs.shadow == rhs.shadow && + lhs.backgroundBlurRadius == rhs.backgroundBlurRadius && + lhs.blurRegionTransform == rhs.blurRegionTransform && + lhs.stretchEffect == rhs.stretchEffect; } // Defining PrintTo helps with Google Tests. @@ -237,8 +235,7 @@ static inline void PrintTo(const Buffer& settings, ::std::ostream* os) { *os << "\n .usePremultipliedAlpha = " << settings.usePremultipliedAlpha; *os << "\n .isOpaque = " << settings.isOpaque; *os << "\n .isY410BT2020 = " << settings.isY410BT2020; - *os << "\n .maxMasteringLuminance = " << settings.maxMasteringLuminance; - *os << "\n .maxContentLuminance = " << settings.maxContentLuminance; + *os << "\n .maxLuminanceNits = " << settings.maxLuminanceNits; *os << "\n}"; } @@ -263,6 +260,8 @@ static inline void PrintTo(const PixelSource& settings, ::std::ostream* os) { static inline void PrintTo(const ShadowSettings& settings, ::std::ostream* os) { *os << "ShadowSettings {"; + *os << "\n .boundaries = "; + PrintTo(settings.boundaries, os); *os << "\n .ambientColor = " << settings.ambientColor; *os << "\n .spotColor = " << settings.spotColor; *os << "\n .lightPos = " << settings.lightPos; @@ -272,8 +271,23 @@ static inline void PrintTo(const ShadowSettings& settings, ::std::ostream* os) { *os << "\n}"; } +static inline void PrintTo(const StretchEffect& effect, ::std::ostream* os) { + *os << "StretchEffect {"; + *os << "\n .width = " << effect.width; + *os << "\n .height = " << effect.height; + *os << "\n .vectorX = " << effect.vectorX; + *os << "\n .vectorY = " << effect.vectorY; + *os << "\n .maxAmountX = " << effect.maxAmountX; + *os << "\n .maxAmountY = " << effect.maxAmountY; + *os << "\n .mappedLeft = " << effect.mappedChildBounds.left; + *os << "\n .mappedTop = " << effect.mappedChildBounds.top; + *os << "\n .mappedRight = " << effect.mappedChildBounds.right; + *os << "\n .mappedBottom = " << effect.mappedChildBounds.bottom; + *os << "\n}"; +} + static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) { - *os << "LayerSettings {"; + *os << "LayerSettings for '" << settings.name.c_str() << "' {"; *os << "\n .geometry = "; PrintTo(settings.geometry, os); *os << "\n .source = "; @@ -283,6 +297,7 @@ static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) { PrintTo(settings.sourceDataspace, os); *os << "\n .colorTransform = " << settings.colorTransform; *os << "\n .disableBlending = " << settings.disableBlending; + *os << "\n .skipContentDraw = " << settings.skipContentDraw; *os << "\n .backgroundBlurRadius = " << settings.backgroundBlurRadius; for (auto blurRegion : settings.blurRegions) { *os << "\n"; @@ -290,6 +305,8 @@ static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) { } *os << "\n .shadow = "; PrintTo(settings.shadow, os); + *os << "\n .stretchEffect = "; + PrintTo(settings.stretchEffect, os); *os << "\n}"; } diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp index 6ab93dfb4d..77e01f4967 100644 --- a/libs/renderengine/skia/Cache.cpp +++ b/libs/renderengine/skia/Cache.cpp @@ -37,10 +37,6 @@ const auto kScaleAndTranslate = mat4(0.7f, 0.f, 0.f, 0.f, 0.f, 0.7f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 67.3f, 52.2f, 0.f, 1.f); -const auto kScaleYOnly = mat4(1.f, 0.f, 0.f, 0.f, - 0.f, 0.7f, 0.f, 0.f, - 0.f, 0.f, 1.f, 0.f, - 0.f, 0.f, 0.f, 1.f); // clang-format on // When setting layer.sourceDataspace, whether it matches the destination or not determines whether // a color correction effect is added to the shader. @@ -108,8 +104,7 @@ static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySetting .source = PixelSource{.buffer = Buffer{ .buffer = srcTexture, - .maxMasteringLuminance = 1000.f, - .maxContentLuminance = 1000.f, + .maxLuminanceNits = 1000.f, }}, }; @@ -189,34 +184,51 @@ static void drawBlurLayers(SkiaRenderEngine* renderengine, const DisplaySettings } } -static void drawTextureScaleLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, - const std::shared_ptr<ExternalTexture>& dstTexture, - const std::shared_ptr<ExternalTexture>& srcTexture) { +// The unique feature of these layers is that the boundary is slightly smaller than the rounded +// rect crop, so the rounded edges intersect that boundary and require a different clipping method. +static void drawClippedLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, + const std::shared_ptr<ExternalTexture>& dstTexture, + const std::shared_ptr<ExternalTexture>& srcTexture) { const Rect& displayRect = display.physicalDisplay; - FloatRect rect(0, 0, displayRect.width(), displayRect.height()); + FloatRect rect(0, 0, displayRect.width(), displayRect.height() - 20); // boundary is smaller + + // clang-format off + const auto symmetric = mat4(0.9f, 0.f, 0.f, 0.f, + 0.f, 0.9f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 8.8f, 8.1f, 0.f, 1.f); + const auto asymmetric = mat4(0.9f, 0.f, 0.f, 0.f, + 0.f, 0.7f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 8.8f, 8.1f, 0.f, 1.f); + + // clang-format on LayerSettings layer{ .geometry = Geometry{ .boundaries = rect, - .roundedCornersCrop = rect, - .positionTransform = kScaleAndTranslate, - .roundedCornersRadius = 300, + .roundedCornersRadius = 27, // larger than the 20 above. + .roundedCornersCrop = + FloatRect(0, 0, displayRect.width(), displayRect.height()), }, .source = PixelSource{.buffer = Buffer{ .buffer = srcTexture, - .maxMasteringLuminance = 1000.f, - .maxContentLuminance = 1000.f, - .textureTransform = kScaleYOnly, + .isOpaque = 0, + .maxLuminanceNits = 1000.f, }}, .sourceDataspace = kOtherDataSpace, }; auto layers = std::vector<const LayerSettings*>{&layer}; - for (float alpha : {0.5f, 1.f}) { - layer.alpha = alpha, - renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd(), nullptr); + for (auto transform : {symmetric, asymmetric}) { + layer.geometry.positionTransform = transform; + // In real use, I saw alpha of 1.0 and 0.999, probably a mistake, but cache both shaders. + for (float alpha : {0.5f, 1.f}) { + layer.alpha = alpha, + renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, + base::unique_fd(), nullptr); + } } } @@ -238,63 +250,70 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine) { if (previousCount) { ALOGD("%d Shaders already compiled before Cache::primeShaderCache ran\n", previousCount); } - const nsecs_t timeBefore = systemTime(); - // The dimensions should not matter, so long as we draw inside them. - const Rect displayRect(0, 0, 1080, 2340); - DisplaySettings display{ - .physicalDisplay = displayRect, - .clip = displayRect, - .maxLuminance = 500, - .outputDataspace = kDestDataSpace, - }; - const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; + // The loop is beneficial for debugging and should otherwise be optimized out by the compiler. + // Adding additional bounds to the loop is useful for verifying that the size of the dst buffer + // does not impact the shader compilation counts by triggering different behaviors in RE/Skia. + for (SkSize bounds : {SkSize::Make(128, 128), /*SkSize::Make(1080, 2340)*/}) { + const nsecs_t timeBefore = systemTime(); + // The dimensions should not matter, so long as we draw inside them. + const Rect displayRect(0, 0, bounds.fWidth, bounds.fHeight); + DisplaySettings display{ + .physicalDisplay = displayRect, + .clip = displayRect, + .maxLuminance = 500, + .outputDataspace = kDestDataSpace, + }; + + const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; - sp<GraphicBuffer> dstBuffer = - new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1, - usage, "primeShaderCache_dst"); + sp<GraphicBuffer> dstBuffer = + new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, + 1, usage, "primeShaderCache_dst"); - const auto dstTexture = std::make_shared<ExternalTexture>(dstBuffer, *renderengine, - ExternalTexture::Usage::WRITEABLE); - // This buffer will be the source for the call to drawImageLayers. Draw - // something to it as a placeholder for what an app draws. We should draw - // something, but the details are not important. Make use of the shadow layer drawing step - // to populate it. - sp<GraphicBuffer> srcBuffer = - new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1, - usage, "drawImageLayer_src"); + const auto dstTexture = + std::make_shared<ExternalTexture>(dstBuffer, *renderengine, + ExternalTexture::Usage::WRITEABLE); + // This buffer will be the source for the call to drawImageLayers. Draw + // something to it as a placeholder for what an app draws. We should draw + // something, but the details are not important. Make use of the shadow layer drawing step + // to populate it. + sp<GraphicBuffer> srcBuffer = + new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, + 1, usage, "drawImageLayer_src"); - const auto srcTexture = - std::make_shared<ExternalTexture>(srcBuffer, *renderengine, - ExternalTexture::Usage::READABLE | - ExternalTexture::Usage::WRITEABLE); + const auto srcTexture = + std::make_shared<ExternalTexture>(srcBuffer, *renderengine, + ExternalTexture::Usage::READABLE | + ExternalTexture::Usage::WRITEABLE); - drawSolidLayers(renderengine, display, dstTexture); - drawShadowLayers(renderengine, display, srcTexture); - drawBlurLayers(renderengine, display, dstTexture); - // The majority of shaders are related to sampling images. - drawImageLayers(renderengine, display, dstTexture, srcTexture); + drawSolidLayers(renderengine, display, dstTexture); + drawShadowLayers(renderengine, display, srcTexture); + drawBlurLayers(renderengine, display, dstTexture); + // The majority of shaders are related to sampling images. + drawImageLayers(renderengine, display, dstTexture, srcTexture); - // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; - const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE; + // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; + const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE; - sp<GraphicBuffer> externalBuffer = - new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1, - usageExternal, "primeShaderCache_external"); - const auto externalTexture = - std::make_shared<ExternalTexture>(externalBuffer, *renderengine, - ExternalTexture::Usage::READABLE); - // TODO(b/184665179) doubles number of image shader compilations, but only somewhere - // between 6 and 8 will occur in real uses. - drawImageLayers(renderengine, display, dstTexture, externalTexture); + sp<GraphicBuffer> externalBuffer = + new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, + 1, usageExternal, "primeShaderCache_external"); + const auto externalTexture = + std::make_shared<ExternalTexture>(externalBuffer, *renderengine, + ExternalTexture::Usage::READABLE); + // TODO(b/184665179) doubles number of image shader compilations, but only somewhere + // between 6 and 8 will occur in real uses. + drawImageLayers(renderengine, display, dstTexture, externalTexture); - // Draw layers for b/185569240. - drawTextureScaleLayers(renderengine, display, dstTexture, externalTexture); + // Draw layers for b/185569240. + drawClippedLayers(renderengine, display, dstTexture, externalTexture); - const nsecs_t timeAfter = systemTime(); - const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6; - const int shadersCompiled = renderengine->reportShadersCompiled(); - ALOGD("Shader cache generated %d shaders in %f ms\n", shadersCompiled, compileTimeMs); + const nsecs_t timeAfter = systemTime(); + const float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6; + const int shadersCompiled = renderengine->reportShadersCompiled(); + ALOGD("Shader cache generated %d shaders in %f ms\n", shadersCompiled, compileTimeMs); + } } } // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index acdb78a212..9f6ecaa1a0 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -56,6 +56,7 @@ #include "log/log_main.h" #include "skia/debug/SkiaCapture.h" #include "skia/debug/SkiaMemoryReporter.h" +#include "skia/filters/StretchShaderFactory.h" #include "system/graphics-base-v1.0.h" namespace { @@ -365,6 +366,10 @@ bool SkiaGLRenderEngine::supportsProtectedContent() const { return mProtectedEGLContext != EGL_NO_CONTEXT; } +GrDirectContext* SkiaGLRenderEngine::getActiveGrContext() const { + return mInProtectedContext ? mProtectedGrContext.get() : mGrContext.get(); +} + bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) { if (useProtectedContext == mInProtectedContext) { return true; @@ -372,6 +377,12 @@ bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) { if (useProtectedContext && !supportsProtectedContent()) { return false; } + + // release any scratch resources before switching into a new mode + if (getActiveGrContext()) { + getActiveGrContext()->purgeUnlockedResources(true); + } + const EGLSurface surface = useProtectedContext ? mProtectedPlaceholderSurface : mPlaceholderSurface; const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext; @@ -379,6 +390,11 @@ bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) { if (success) { mInProtectedContext = useProtectedContext; + // given that we are sharing the same thread between two GrContexts we need to + // make sure that the thread state is reset when switching between the two. + if (getActiveGrContext()) { + getActiveGrContext()->resetContext(); + } } return success; } @@ -489,32 +505,31 @@ void SkiaGLRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffe if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) { return; } - ATRACE_CALL(); - - // We need to switch the currently bound context if the buffer is protected but the current - // context is not. The current state must then be restored after the buffer is cached. - const bool protectedContextState = mInProtectedContext; - if (!useProtectedContext(protectedContextState || - (buffer->getUsage() & GRALLOC_USAGE_PROTECTED))) { - ALOGE("Attempting to cache a buffer into a different context than what is currently bound"); + // We currently don't attempt to map a buffer if the buffer contains protected content + // or we are using a protected context because GPU resources for protected buffers is + // much more limited. + const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED; + if (isProtectedBuffer || mInProtectedContext) { return; } + ATRACE_CALL(); - auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext; - auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache; + // If we were to support caching protected buffers then we will need to switch the currently + // bound context if we are not already using the protected context (and subsequently switch + // back after the buffer is cached). + auto grContext = getActiveGrContext(); + auto& cache = mTextureCache; std::lock_guard<std::mutex> lock(mRenderingMutex); mGraphicBufferExternalRefs[buffer->getId()]++; if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) { std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = - std::make_shared<AutoBackendTexture::LocalRef>(grContext.get(), + std::make_shared<AutoBackendTexture::LocalRef>(grContext, buffer->toAHardwareBuffer(), isRenderable); cache.insert({buffer->getId(), imageTextureRef}); } - // restore the original state of the protected context if necessary - useProtectedContext(protectedContextState); } void SkiaGLRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { @@ -541,14 +556,23 @@ void SkiaGLRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buf } } -sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader, - const LayerSettings* layer, - const DisplaySettings& display, - bool undoPremultipliedAlpha, - bool requiresLinearEffect) { - if (layer->stretchEffect.hasEffect()) { - // TODO: Implement +sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader( + sk_sp<SkShader> shader, + const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha, + bool requiresLinearEffect) { + const auto stretchEffect = layer->stretchEffect; + // The given surface will be stretched by HWUI via matrix transformation + // which gets similar results for most surfaces + // Determine later on if we need to leverage the stertch shader within + // surface flinger + if (stretchEffect.hasEffect()) { + const auto targetBuffer = layer->source.buffer.buffer; + const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr; + if (graphicBuffer && shader) { + shader = mStretchShaderFactory.createSkShader(shader, stretchEffect); + } } + if (requiresLinearEffect) { const ui::Dataspace inputDataspace = mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::UNKNOWN; @@ -567,10 +591,14 @@ sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> sh } else { runtimeEffect = effectIter->second; } + float maxLuminance = layer->source.buffer.maxLuminanceNits; + // If the buffer doesn't have a max luminance, treat it as SDR & use the display's SDR + // white point + if (maxLuminance <= 0.f) { + maxLuminance = display.sdrWhitePointNits; + } return createLinearEffectShader(shader, effect, runtimeEffect, layer->colorTransform, - display.maxLuminance, - layer->source.buffer.maxMasteringLuminance, - layer->source.buffer.maxContentLuminance); + display.maxLuminance, maxLuminance); } return shader; } @@ -634,6 +662,17 @@ private: int mSaveCount; }; +static SkRRect getBlurRRect(const BlurRegion& region) { + const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom); + const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL), + SkVector::Make(region.cornerRadiusTR, region.cornerRadiusTR), + SkVector::Make(region.cornerRadiusBR, region.cornerRadiusBR), + SkVector::Make(region.cornerRadiusBL, region.cornerRadiusBL)}; + SkRRect roundedRect; + roundedRect.setRectRadii(rect, radii); + return roundedRect; +} + status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, const std::shared_ptr<ExternalTexture>& buffer, @@ -662,7 +701,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, validateOutputBufferUsage(buffer->getBuffer()); - auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext; + auto grContext = getActiveGrContext(); auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache; std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef; @@ -670,7 +709,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, surfaceTextureRef = it->second; } else { surfaceTextureRef = - std::make_shared<AutoBackendTexture::LocalRef>(grContext.get(), + std::make_shared<AutoBackendTexture::LocalRef>(grContext, buffer->getBuffer() ->toAHardwareBuffer(), true); @@ -678,8 +717,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, const ui::Dataspace dstDataspace = mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN; - sk_sp<SkSurface> dstSurface = - surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext.get()); + sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext); SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get()); if (dstCanvas == nullptr) { @@ -809,7 +847,9 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, // Layers have a local transform that should be applied to them canvas->concat(getSkM44(layer->geometry.positionTransform).asM33()); - const auto bounds = getSkRect(layer->geometry.boundaries); + const auto [bounds, roundRectClip] = + getBoundsAndClip(layer->geometry.boundaries, layer->geometry.roundedCornersCrop, + layer->geometry.roundedCornersRadius); if (mBlurFilter && layerHasBlur(layer)) { std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs; @@ -819,58 +859,81 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, blurInput = activeSurface->makeImageSnapshot(); } // rect to be blurred in the coordinate space of blurInput - const auto blurRect = canvas->getTotalMatrix().mapRect(bounds); + const auto blurRect = canvas->getTotalMatrix().mapRect(bounds.rect()); + + // if the clip needs to be applied then apply it now and make sure + // it is restored before we attempt to draw any shadows. + SkAutoCanvasRestore acr(canvas, true); + if (!roundRectClip.isEmpty()) { + canvas->clipRRect(roundRectClip, true); + } // TODO(b/182216890): Filter out empty layers earlier if (blurRect.width() > 0 && blurRect.height() > 0) { if (layer->backgroundBlurRadius > 0) { ATRACE_NAME("BackgroundBlur"); auto blurredImage = - mBlurFilter->generate(grContext.get(), layer->backgroundBlurRadius, - blurInput, blurRect); + mBlurFilter->generate(grContext, layer->backgroundBlurRadius, blurInput, + blurRect); cachedBlurs[layer->backgroundBlurRadius] = blurredImage; - mBlurFilter->drawBlurRegion(canvas, getBlurRegion(layer), blurRect, - blurredImage, blurInput); + mBlurFilter->drawBlurRegion(canvas, bounds, layer->backgroundBlurRadius, 1.0f, + blurRect, blurredImage, blurInput); } - SkAutoCanvasRestore acr(canvas, true); + canvas->concat(getSkM44(layer->blurRegionTransform).asM33()); for (auto region : layer->blurRegions) { if (cachedBlurs[region.blurRadius] == nullptr) { ATRACE_NAME("BlurRegion"); cachedBlurs[region.blurRadius] = - mBlurFilter->generate(grContext.get(), region.blurRadius, blurInput, + mBlurFilter->generate(grContext, region.blurRadius, blurInput, blurRect); } - mBlurFilter->drawBlurRegion(canvas, region, blurRect, + mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius, + region.alpha, blurRect, cachedBlurs[region.blurRadius], blurInput); } } } - // Shadows are assumed to live only on their own layer - it's not valid - // to draw the boundary rectangles when there is already a caster shadow - // TODO(b/175915334): consider relaxing this restriction to enable more flexible - // composition - using a well-defined invalid color is long-term less error-prone. if (layer->shadow.length > 0) { - const auto rect = layer->geometry.roundedCornersRadius > 0 - ? getSkRect(layer->geometry.roundedCornersCrop) - : bounds; // This would require a new parameter/flag to SkShadowUtils::DrawShadow LOG_ALWAYS_FATAL_IF(layer->disableBlending, "Cannot disableBlending with a shadow"); - drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow); - continue; + + SkRRect shadowBounds, shadowClip; + if (layer->geometry.boundaries == layer->shadow.boundaries) { + shadowBounds = bounds; + shadowClip = roundRectClip; + } else { + std::tie(shadowBounds, shadowClip) = + getBoundsAndClip(layer->shadow.boundaries, + layer->geometry.roundedCornersCrop, + layer->geometry.roundedCornersRadius); + } + + // Technically, if bounds is a rect and roundRectClip is not empty, + // it means that the bounds and roundedCornersCrop were different + // enough that we should intersect them to find the proper shadow. + // In practice, this often happens when the two rectangles appear to + // not match due to rounding errors. Draw the rounded version, which + // looks more like the intent. + const auto& rrect = + shadowBounds.isRect() && !shadowClip.isEmpty() ? shadowClip : shadowBounds; + drawShadow(canvas, rrect, layer->shadow); } const bool requiresLinearEffect = layer->colorTransform != mat4() || (mUseColorManagement && - needsToneMapping(layer->sourceDataspace, display.outputDataspace)); + needsToneMapping(layer->sourceDataspace, display.outputDataspace)) || + (display.sdrWhitePointNits > 0.f && + display.sdrWhitePointNits != display.maxLuminance); // quick abort from drawing the remaining portion of the layer - if (layer->alpha == 0 && !requiresLinearEffect && !layer->disableBlending && - (!displayColorTransform || displayColorTransform->isAlphaUnchanged())) { + if (layer->skipContentDraw || + (layer->alpha == 0 && !requiresLinearEffect && !layer->disableBlending && + (!displayColorTransform || displayColorTransform->isAlphaUnchanged()))) { continue; } @@ -897,7 +960,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, // we didn't find anything in the cache then we intentionally did not cache this // buffer's resources. imageTextureRef = std::make_shared< - AutoBackendTexture::LocalRef>(grContext.get(), + AutoBackendTexture::LocalRef>(grContext, item.buffer->getBuffer()->toAHardwareBuffer(), false); } @@ -906,7 +969,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, imageTextureRef->makeImage(layerDataspace, item.usePremultipliedAlpha ? kPremul_SkAlphaType : kUnpremul_SkAlphaType, - grContext.get()); + grContext); auto texMatrix = getSkM44(item.textureTransform).asM33(); // textureTansform was intended to be passed directly into a shader, so when @@ -922,7 +985,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, // The shader does not respect the translation, so we add it to the texture // transform for the SkImage. This will make sure that the correct layer contents // are drawn in the correct part of the screen. - matrix.postTranslate(layer->geometry.boundaries.left, layer->geometry.boundaries.top); + matrix.postTranslate(bounds.rect().fLeft, bounds.rect().fTop); sk_sp<SkShader> shader; @@ -984,11 +1047,15 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, paint.setColorFilter(displayColorTransform); - if (layer->geometry.roundedCornersRadius > 0) { + if (!roundRectClip.isEmpty()) { + canvas->clipRRect(roundRectClip, true); + } + + if (!bounds.isRect()) { paint.setAntiAlias(true); - canvas->drawRRect(getRoundedRect(layer), paint); + canvas->drawRRect(bounds, paint); } else { - canvas->drawRect(bounds, paint); + canvas->drawRect(bounds.rect(), paint); } if (kFlushAfterEveryLayer) { ATRACE_NAME("flush surface"); @@ -1036,25 +1103,89 @@ inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) { return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); } -inline SkRRect SkiaGLRenderEngine::getRoundedRect(const LayerSettings* layer) { - const auto rect = getSkRect(layer->geometry.roundedCornersCrop); - const auto cornerRadius = layer->geometry.roundedCornersRadius; - return SkRRect::MakeRectXY(rect, cornerRadius, cornerRadius); -} +inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip(const FloatRect& boundsRect, + const FloatRect& cropRect, + const float cornerRadius) { + const SkRect bounds = getSkRect(boundsRect); + const SkRect crop = getSkRect(cropRect); + + SkRRect clip; + if (cornerRadius > 0) { + // it the crop and the bounds are equivalent or there is no crop then we don't need a clip + if (bounds == crop || crop.isEmpty()) { + return {SkRRect::MakeRectXY(bounds, cornerRadius, cornerRadius), clip}; + } + + // This makes an effort to speed up common, simple bounds + clip combinations by + // converting them to a single RRect draw. It is possible there are other cases + // that can be converted. + if (crop.contains(bounds)) { + bool intersectionIsRoundRect = true; + // check each cropped corner to ensure that it exactly matches the crop or is full + SkVector radii[4]; + + const auto insetCrop = crop.makeInset(cornerRadius, cornerRadius); + + const bool leftEqual = bounds.fLeft == crop.fLeft; + const bool topEqual = bounds.fTop == crop.fTop; + const bool rightEqual = bounds.fRight == crop.fRight; + const bool bottomEqual = bounds.fBottom == crop.fBottom; + + // compute the UpperLeft corner radius + if (leftEqual && topEqual) { + radii[0].set(cornerRadius, cornerRadius); + } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) || + (topEqual && bounds.fLeft >= insetCrop.fLeft) || + insetCrop.contains(bounds.fLeft, bounds.fTop)) { + radii[0].set(0, 0); + } else { + intersectionIsRoundRect = false; + } + // compute the UpperRight corner radius + if (rightEqual && topEqual) { + radii[1].set(cornerRadius, cornerRadius); + } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) || + (topEqual && bounds.fRight <= insetCrop.fRight) || + insetCrop.contains(bounds.fRight, bounds.fTop)) { + radii[1].set(0, 0); + } else { + intersectionIsRoundRect = false; + } + // compute the BottomRight corner radius + if (rightEqual && bottomEqual) { + radii[2].set(cornerRadius, cornerRadius); + } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) || + (bottomEqual && bounds.fRight <= insetCrop.fRight) || + insetCrop.contains(bounds.fRight, bounds.fBottom)) { + radii[2].set(0, 0); + } else { + intersectionIsRoundRect = false; + } + // compute the BottomLeft corner radius + if (leftEqual && bottomEqual) { + radii[3].set(cornerRadius, cornerRadius); + } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) || + (bottomEqual && bounds.fLeft >= insetCrop.fLeft) || + insetCrop.contains(bounds.fLeft, bounds.fBottom)) { + radii[3].set(0, 0); + } else { + intersectionIsRoundRect = false; + } + + if (intersectionIsRoundRect) { + SkRRect intersectionBounds; + intersectionBounds.setRectRadii(bounds, radii); + return {intersectionBounds, clip}; + } + } + + // we didn't it any of our fast paths so set the clip to the cropRect + clip.setRectXY(crop, cornerRadius, cornerRadius); + } -inline BlurRegion SkiaGLRenderEngine::getBlurRegion(const LayerSettings* layer) { - const auto rect = getSkRect(layer->geometry.boundaries); - const auto cornersRadius = layer->geometry.roundedCornersRadius; - return BlurRegion{.blurRadius = static_cast<uint32_t>(layer->backgroundBlurRadius), - .cornerRadiusTL = cornersRadius, - .cornerRadiusTR = cornersRadius, - .cornerRadiusBL = cornersRadius, - .cornerRadiusBR = cornersRadius, - .alpha = 1, - .left = static_cast<int>(rect.fLeft), - .top = static_cast<int>(rect.fTop), - .right = static_cast<int>(rect.fRight), - .bottom = static_cast<int>(rect.fBottom)}; + // if we hit this point then we either don't have rounded corners or we are going to rely + // on the clip to round the corners for us + return {SkRRect::MakeRect(bounds), clip}; } inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer) { @@ -1084,17 +1215,14 @@ size_t SkiaGLRenderEngine::getMaxViewportDims() const { return mGrContext->maxRenderTargetSize(); } -void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRect& casterRect, float cornerRadius, +void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRRect& casterRRect, const ShadowSettings& settings) { ATRACE_CALL(); const float casterZ = settings.length / 2.0f; - const auto shadowShape = cornerRadius > 0 - ? SkPath::RRect(SkRRect::MakeRectXY(casterRect, cornerRadius, cornerRadius)) - : SkPath::Rect(casterRect); const auto flags = settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag; - SkShadowUtils::DrawShadow(canvas, shadowShape, SkPoint3::Make(0, 0, casterZ), + SkShadowUtils::DrawShadow(canvas, SkPath::RRect(casterRRect), SkPoint3::Make(0, 0, casterZ), getSkPoint3(settings.lightPos), settings.lightRadius, getSkColor(settings.ambientColor), getSkColor(settings.spotColor), flags); @@ -1226,13 +1354,11 @@ void SkiaGLRenderEngine::onPrimaryDisplaySizeChanged(ui::Size size) { const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER; // start by resizing the current context - auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext; - grContext->setResourceCacheLimit(maxResourceBytes); + getActiveGrContext()->setResourceCacheLimit(maxResourceBytes); // if it is possible to switch contexts then we will resize the other context if (useProtectedContext(!mInProtectedContext)) { - grContext = mInProtectedContext ? mProtectedGrContext : mGrContext; - grContext->setResourceCacheLimit(maxResourceBytes); + getActiveGrContext()->setResourceCacheLimit(maxResourceBytes); // reset back to the initial context that was active when this method was called useProtectedContext(!mInProtectedContext); } diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h index e71c560a1f..4265c08a3f 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -39,6 +39,7 @@ #include "debug/SkiaCapture.h" #include "filters/BlurFilter.h" #include "filters/LinearEffect.h" +#include "filters/StretchShaderFactory.h" namespace android { namespace renderengine { @@ -87,21 +88,23 @@ private: int hwcFormat, Protection protection); inline SkRect getSkRect(const FloatRect& layer); inline SkRect getSkRect(const Rect& layer); - inline SkRRect getRoundedRect(const LayerSettings* layer); - inline BlurRegion getBlurRegion(const LayerSettings* layer); + inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const FloatRect& bounds, + const FloatRect& crop, float cornerRadius); inline bool layerHasBlur(const LayerSettings* layer); inline SkColor getSkColor(const vec4& color); inline SkM44 getSkM44(const mat4& matrix); inline SkPoint3 getSkPoint3(const vec3& vector); + inline GrDirectContext* getActiveGrContext() const; base::unique_fd flush(); bool waitFence(base::unique_fd fenceFd); void initCanvas(SkCanvas* canvas, const DisplaySettings& display); - void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius, + void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect, const ShadowSettings& shadowSettings); // If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned. // Otherwise it returns the input shader. - sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings* layer, + sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, + const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha, bool requiresLinearEffect); @@ -116,14 +119,21 @@ private: const PixelFormat mDefaultPixelFormat; const bool mUseColorManagement; + // Identifier used or various mappings of layers to various + // textures or shaders + using GraphicBufferId = uint64_t; + // Number of external holders of ExternalTexture references, per GraphicBuffer ID. - std::unordered_map<uint64_t, int32_t> mGraphicBufferExternalRefs GUARDED_BY(mRenderingMutex); + std::unordered_map<GraphicBufferId, int32_t> mGraphicBufferExternalRefs + GUARDED_BY(mRenderingMutex); // Cache of GL textures that we'll store per GraphicBuffer ID, sliced by GPU context. - std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache + std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache GUARDED_BY(mRenderingMutex); - std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>> + std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mProtectedTextureCache GUARDED_BY(mRenderingMutex); std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects; + + StretchShaderFactory mStretchShaderFactory; // Mutex guarding rendering operations, so that: // 1. GL operations aren't interleaved, and // 2. Internal state related to rendering that is potentially modified by diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp index 5cba8fb011..4ad6e94edf 100644 --- a/libs/renderengine/skia/filters/BlurFilter.cpp +++ b/libs/renderengine/skia/filters/BlurFilter.cpp @@ -139,23 +139,21 @@ static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRec return matrix; } -void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion, +void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, + const uint32_t blurRadius, const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage, sk_sp<SkImage> input) { ATRACE_CALL(); SkPaint paint; - paint.setAlphaf(effectRegion.alpha); - if (effectRegion.alpha == 1.0f) { - paint.setBlendMode(SkBlendMode::kSrc); - } + paint.setAlphaf(blurAlpha); const auto blurMatrix = getShaderTransform(canvas, blurRect, kInverseInputScale); SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone); const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling, &blurMatrix); - if (effectRegion.blurRadius < kMaxCrossFadeRadius) { + if (blurRadius < kMaxCrossFadeRadius) { // For sampling Skia's API expects the inverse of what logically seems appropriate. In this // case you might expect the matrix to simply be the canvas matrix. SkMatrix inputMatrix; @@ -168,30 +166,21 @@ void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion blurBuilder.child("originalInput") = input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling, inputMatrix); - blurBuilder.uniform("mixFactor") = effectRegion.blurRadius / kMaxCrossFadeRadius; + blurBuilder.uniform("mixFactor") = blurRadius / kMaxCrossFadeRadius; paint.setShader(blurBuilder.makeShader(nullptr, true)); } else { paint.setShader(blurShader); } - // TODO we should AA at least the drawRoundRect which would mean no SRC blending - // TODO this round rect calculation doesn't match the one used to draw in RenderEngine - auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right, - effectRegion.bottom); - - if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 || - effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) { - const SkVector radii[4] = - {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL), - SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR), - SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL), - SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)}; - SkRRect roundedRect; - roundedRect.setRectRadii(rect, radii); - canvas->drawRRect(roundedRect, paint); + if (effectRegion.isRect()) { + if (blurAlpha == 1.0f) { + paint.setBlendMode(SkBlendMode::kSrc); + } + canvas->drawRect(effectRegion.rect(), paint); } else { - canvas->drawRect(rect, paint); + paint.setAntiAlias(true); + canvas->drawRRect(effectRegion, paint); } } diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h index 731ba11483..a8e6dd7579 100644 --- a/libs/renderengine/skia/filters/BlurFilter.h +++ b/libs/renderengine/skia/filters/BlurFilter.h @@ -20,7 +20,6 @@ #include <SkImage.h> #include <SkRuntimeEffect.h> #include <SkSurface.h> -#include <ui/BlurRegion.h> using namespace std; @@ -52,8 +51,19 @@ public: sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius, const sk_sp<SkImage> blurInput, const SkRect& blurRect) const; - void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& blurRect, - sk_sp<SkImage> blurredImage, sk_sp<SkImage> input); + /** + * Draw the blurred content (from the generate method) into the canvas. + * @param canvas is the destination/output for the blur + * @param effectRegion the RoundRect in canvas coordinates that determines the blur coverage + * @param blurRadius radius of the blur used to determine the intensity of the crossfade effect + * @param blurAlpha alpha value applied to the effectRegion when the blur is drawn + * @param blurRect bounds of the blurredImage translated into canvas coordinates + * @param blurredImage down-sampled blurred content that was produced by the generate() method + * @param input original unblurred input that is used to crossfade with the blurredImage + */ + void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, const uint32_t blurRadius, + const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage, + sk_sp<SkImage> input); private: sk_sp<SkRuntimeEffect> mBlurEffect; diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp index 0fbd6698d8..9b044e1685 100644 --- a/libs/renderengine/skia/filters/LinearEffect.cpp +++ b/libs/renderengine/skia/filters/LinearEffect.cpp @@ -127,7 +127,7 @@ static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, SkStrin default: shader.append(R"( float3 ScaleLuminance(float3 xyz) { - return xyz * in_displayMaxLuminance; + return xyz * in_inputMaxLuminance; } )"); break; @@ -448,7 +448,7 @@ sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) { sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEffect& linearEffect, sk_sp<SkRuntimeEffect> runtimeEffect, const mat4& colorTransform, float maxDisplayLuminance, - float maxMasteringLuminance, float maxContentLuminance) { + float maxLuminance) { ATRACE_CALL(); SkRuntimeShaderBuilder effectBuilder(runtimeEffect); @@ -467,8 +467,10 @@ sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEff } effectBuilder.uniform("in_displayMaxLuminance") = maxDisplayLuminance; + // If the input luminance is unknown, use display luminance (aka, no-op any luminance changes) + // This will be the case for eg screenshots in addition to uncalibrated displays effectBuilder.uniform("in_inputMaxLuminance") = - std::min(maxMasteringLuminance, maxContentLuminance); + maxLuminance > 0 ? maxLuminance : maxDisplayLuminance; return effectBuilder.makeShader(nullptr, false); } diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h index 20b8338931..14a3b61ede 100644 --- a/libs/renderengine/skia/filters/LinearEffect.h +++ b/libs/renderengine/skia/filters/LinearEffect.h @@ -90,15 +90,13 @@ sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect); // matrix transforming from linear XYZ to linear RGB immediately before OETF. // We also provide additional HDR metadata upon creating the shader: // * The max display luminance is the max luminance of the physical display in nits -// * The max mastering luminance is provided as the max luminance from the SMPTE 2086 -// standard. -// * The max content luminance is provided as the max light level from the CTA 861.3 -// standard. +// * The max luminance is provided as the max luminance for the buffer, either from the SMPTE 2086 +// or as the max light level from the CTA 861.3 standard. sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader, const LinearEffect& linearEffect, sk_sp<SkRuntimeEffect> runtimeEffect, const mat4& colorTransform, float maxDisplayLuminance, - float maxMasteringLuminance, float maxContentLuminance); + float maxLuminance); } // namespace skia } // namespace renderengine } // namespace android diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.cpp b/libs/renderengine/skia/filters/StretchShaderFactory.cpp new file mode 100644 index 0000000000..4ac5c4028b --- /dev/null +++ b/libs/renderengine/skia/filters/StretchShaderFactory.cpp @@ -0,0 +1,246 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "StretchShaderFactory.h" +#include <SkImageFilter.h> +#include <SkRefCnt.h> +#include <SkRuntimeEffect.h> +#include <SkString.h> +#include <SkSurface.h> +#include "log/log.h" +#include <memory> + +namespace android { +namespace renderengine { +namespace skia { + +static const SkString stretchShader = SkString(R"( + uniform shader uContentTexture; + + // multiplier to apply to scale effect + uniform float uMaxStretchIntensity; + + // Maximum percentage to stretch beyond bounds of target + uniform float uStretchAffectedDistX; + uniform float uStretchAffectedDistY; + + // Distance stretched as a function of the normalized overscroll times + // scale intensity + uniform float uDistanceStretchedX; + uniform float uDistanceStretchedY; + uniform float uInverseDistanceStretchedX; + uniform float uInverseDistanceStretchedY; + uniform float uDistDiffX; + + // Difference between the peak stretch amount and overscroll amount normalized + uniform float uDistDiffY; + + // Horizontal offset represented as a ratio of pixels divided by the target width + uniform float uScrollX; + // Vertical offset represented as a ratio of pixels divided by the target height + uniform float uScrollY; + + // Normalized overscroll amount in the horizontal direction + uniform float uOverscrollX; + + // Normalized overscroll amount in the vertical direction + uniform float uOverscrollY; + uniform float viewportWidth; // target height in pixels + uniform float viewportHeight; // target width in pixels + + // uInterpolationStrength is the intensity of the interpolation. + // if uInterpolationStrength is 0, then the stretch is constant for all the + // uStretchAffectedDist. if uInterpolationStrength is 1, then stretch intensity + // is interpolated based on the pixel position in the uStretchAffectedDist area; + // The closer we are from the scroll anchor point, the more it stretches, + // and the other way around. + uniform float uInterpolationStrength; + + float easeIn(float t, float d) { + return t * d; + } + + float computeOverscrollStart( + float inPos, + float overscroll, + float uStretchAffectedDist, + float uInverseStretchAffectedDist, + float distanceStretched, + float interpolationStrength + ) { + float offsetPos = uStretchAffectedDist - inPos; + float posBasedVariation = mix( + 1. ,easeIn(offsetPos, uInverseStretchAffectedDist), interpolationStrength); + float stretchIntensity = overscroll * posBasedVariation; + return distanceStretched - (offsetPos / (1. + stretchIntensity)); + } + + float computeOverscrollEnd( + float inPos, + float overscroll, + float reverseStretchDist, + float uStretchAffectedDist, + float uInverseStretchAffectedDist, + float distanceStretched, + float interpolationStrength + ) { + float offsetPos = inPos - reverseStretchDist; + float posBasedVariation = mix( + 1. ,easeIn(offsetPos, uInverseStretchAffectedDist), interpolationStrength); + float stretchIntensity = (-overscroll) * posBasedVariation; + return 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity))); + } + + // Prefer usage of return values over out parameters as it enables + // SKSL to properly inline method calls and works around potential GPU + // driver issues on Wembly. See b/182566543 for details + float computeOverscroll( + float inPos, + float overscroll, + float uStretchAffectedDist, + float uInverseStretchAffectedDist, + float distanceStretched, + float distanceDiff, + float interpolationStrength + ) { + float outPos = inPos; + // overscroll is provided via uniform so there is no concern + // for potential incoherent branches + if (overscroll > 0) { + if (inPos <= uStretchAffectedDist) { + outPos = computeOverscrollStart( + inPos, + overscroll, + uStretchAffectedDist, + uInverseStretchAffectedDist, + distanceStretched, + interpolationStrength + ); + } else if (inPos >= distanceStretched) { + outPos = distanceDiff + inPos; + } + } + if (overscroll < 0) { + float stretchAffectedDist = 1. - uStretchAffectedDist; + if (inPos >= stretchAffectedDist) { + outPos = computeOverscrollEnd( + inPos, + overscroll, + stretchAffectedDist, + uStretchAffectedDist, + uInverseStretchAffectedDist, + distanceStretched, + interpolationStrength + ); + } else if (inPos < stretchAffectedDist) { + outPos = -distanceDiff + inPos; + } + } + return outPos; + } + + vec4 main(vec2 coord) { + // Normalize SKSL pixel coordinate into a unit vector + float inU = coord.x / viewportWidth; + float inV = coord.y / viewportHeight; + float outU; + float outV; + float stretchIntensity; + // Add the normalized scroll position within scrolling list + inU += uScrollX; + inV += uScrollY; + outU = inU; + outV = inV; + outU = computeOverscroll( + inU, + uOverscrollX, + uStretchAffectedDistX, + uInverseDistanceStretchedX, + uDistanceStretchedX, + uDistDiffX, + uInterpolationStrength + ); + outV = computeOverscroll( + inV, + uOverscrollY, + uStretchAffectedDistY, + uInverseDistanceStretchedY, + uDistanceStretchedY, + uDistDiffY, + uInterpolationStrength + ); + coord.x = (outU - uScrollX) * viewportWidth; + coord.y = (outV - uScrollY) * viewportHeight; + return sample(uContentTexture, coord); + })"); + +const float INTERPOLATION_STRENGTH_VALUE = 0.7f; + +sk_sp<SkShader> StretchShaderFactory::createSkShader(const sk_sp<SkShader>& inputShader, + const StretchEffect& stretchEffect) { + if (!stretchEffect.hasEffect()) { + return nullptr; + } + + float viewportWidth = stretchEffect.width; + float viewportHeight = stretchEffect.height; + float normOverScrollDistX = stretchEffect.vectorX; + float normOverScrollDistY = stretchEffect.vectorY; + float distanceStretchedX = + StretchEffect::CONTENT_DISTANCE_STRETCHED / (1 + abs(normOverScrollDistX)); + float distanceStretchedY = + StretchEffect::CONTENT_DISTANCE_STRETCHED / (1 + abs(normOverScrollDistY)); + float inverseDistanceStretchedX = + 1.f / StretchEffect::CONTENT_DISTANCE_STRETCHED; + float inverseDistanceStretchedY = + 1.f / StretchEffect::CONTENT_DISTANCE_STRETCHED; + float diffX = + distanceStretchedX - StretchEffect::CONTENT_DISTANCE_STRETCHED; + float diffY = + distanceStretchedY - StretchEffect::CONTENT_DISTANCE_STRETCHED; + auto& srcBounds = stretchEffect.mappedChildBounds; + float normalizedScrollX = srcBounds.left / viewportWidth; + float normalizedScrollY = srcBounds.top / viewportHeight; + + if (mBuilder == nullptr) { + const static SkRuntimeEffect::Result instance = + SkRuntimeEffect::MakeForShader(stretchShader); + mBuilder = std::make_unique<SkRuntimeShaderBuilder>(instance.effect); + } + + mBuilder->child("uContentTexture") = inputShader; + mBuilder->uniform("uInterpolationStrength").set(&INTERPOLATION_STRENGTH_VALUE, 1); + mBuilder->uniform("uStretchAffectedDistX").set(&StretchEffect::CONTENT_DISTANCE_STRETCHED, 1); + mBuilder->uniform("uStretchAffectedDistY").set(&StretchEffect::CONTENT_DISTANCE_STRETCHED, 1); + mBuilder->uniform("uDistanceStretchedX").set(&distanceStretchedX, 1); + mBuilder->uniform("uDistanceStretchedY").set(&distanceStretchedY, 1); + mBuilder->uniform("uInverseDistanceStretchedX").set(&inverseDistanceStretchedX, 1); + mBuilder->uniform("uInverseDistanceStretchedY").set(&inverseDistanceStretchedY, 1); + mBuilder->uniform("uDistDiffX").set(&diffX, 1); + mBuilder->uniform("uDistDiffY").set(&diffY, 1); + mBuilder->uniform("uOverscrollX").set(&normOverScrollDistX, 1); + mBuilder->uniform("uOverscrollY").set(&normOverScrollDistY, 1); + mBuilder->uniform("uScrollX").set(&normalizedScrollX, 1); + mBuilder->uniform("uScrollY").set(&normalizedScrollY, 1); + mBuilder->uniform("viewportWidth").set(&viewportWidth, 1); + mBuilder->uniform("viewportHeight").set(&viewportHeight, 1); + + return mBuilder->makeShader(nullptr, false); +} + +} // namespace skia +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.h b/libs/renderengine/skia/filters/StretchShaderFactory.h new file mode 100644 index 0000000000..9c3ab7cb4f --- /dev/null +++ b/libs/renderengine/skia/filters/StretchShaderFactory.h @@ -0,0 +1,36 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <SkImage.h> +#include <SkRuntimeEffect.h> +#include <SkShader.h> +#include <ui/StretchEffect.h> + +namespace android { +namespace renderengine { +namespace skia { +class StretchShaderFactory { +public: + sk_sp<SkShader> createSkShader(const sk_sp<SkShader>& inputShader, + const StretchEffect& stretchEffect); + +private: + std::unique_ptr<SkRuntimeShaderBuilder> mBuilder; +}; +} // namespace skia +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 85322a9f71..1ca7a16fe0 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -263,6 +263,11 @@ public: } } + void expectBufferColor(const Point& point, uint8_t r, uint8_t g, uint8_t b, uint8_t a, + uint8_t tolerance = 0) { + expectBufferColor(Rect(point.x, point.y, point.x + 1, point.y + 1), r, g, b, a, tolerance); + } + void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t tolerance = 0) { auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) { @@ -1260,6 +1265,7 @@ void RenderEngineTest::drawShadowWithoutCaster(const FloatRect& castingBounds, renderengine::LayerSettings shadowLayer; shadowLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; shadowLayer.geometry.boundaries = castingBounds; + shadowLayer.skipContentDraw = true; shadowLayer.alpha = 1.0f; ColorSourceVariant::fillColor(shadowLayer, 0, 0, 0, this); shadowLayer.shadow = shadow; @@ -1887,6 +1893,51 @@ TEST_P(RenderEngineTest, testRoundedCornersCrop) { 0, 255, 0, 255); } +TEST_P(RenderEngineTest, testRoundedCornersParentCrop) { + initializeRenderEngine(); + + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; + + std::vector<const renderengine::LayerSettings*> layers; + + renderengine::LayerSettings redLayer; + redLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; + redLayer.geometry.boundaries = fullscreenRect().toFloatRect(); + redLayer.geometry.roundedCornersRadius = 5.0f; + redLayer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect(); + // Red background. + redLayer.source.solidColor = half3(1.0f, 0.0f, 0.0f); + redLayer.alpha = 1.0f; + + layers.push_back(&redLayer); + + // Green layer with 1/2 size with parent crop rect. + renderengine::LayerSettings greenLayer = redLayer; + greenLayer.geometry.boundaries = + FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2); + greenLayer.source.solidColor = half3(0.0f, 1.0f, 0.0f); + + layers.push_back(&greenLayer); + + invokeDraw(settings, layers); + + // Due to roundedCornersRadius, the corners are untouched. + expectBufferColor(Point(0, 0), 0, 0, 0, 0); + expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, 0), 0, 0, 0, 0); + expectBufferColor(Point(0, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0); + expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1), 0, 0, 0, 0); + + // top middle should be green and the bottom middle red + expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, 0), 0, 255, 0, 255); + expectBufferColor(Point(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, 255); + + // the bottom edge of the green layer should not be rounded + expectBufferColor(Point(0, (DEFAULT_DISPLAY_HEIGHT / 2) - 1), 0, 255, 0, 255); +} + TEST_P(RenderEngineTest, testClear) { initializeRenderEngine(); diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index c87a836de1..9009ce46d3 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -101,6 +101,7 @@ void RenderEngineThreaded::waitUntilInitialized() const { } void RenderEngineThreaded::primeCache() { + ATRACE_CALL(); // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { @@ -131,6 +132,7 @@ void RenderEngineThreaded::dump(std::string& result) { } void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) { + ATRACE_CALL(); std::promise<void> resultPromise; std::future<void> resultFuture = resultPromise.get_future(); { @@ -146,6 +148,7 @@ void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) { } void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) { + ATRACE_CALL(); std::promise<void> resultPromise; std::future<void> resultFuture = resultPromise.get_future(); { @@ -162,6 +165,7 @@ void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) { void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) { + ATRACE_CALL(); // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { @@ -175,6 +179,7 @@ void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buf } void RenderEngineThreaded::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { + ATRACE_CALL(); // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { @@ -247,6 +252,7 @@ status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) { + ATRACE_CALL(); std::promise<status_t> resultPromise; std::future<status_t> resultFuture = resultPromise.get_future(); { @@ -264,6 +270,7 @@ status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display, } void RenderEngineThreaded::cleanFramebufferCache() { + ATRACE_CALL(); // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index 240738d42d..0a49008584 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -231,6 +231,10 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi mFlags |= SENSOR_FLAG_WAKE_UP; } break; + case SENSOR_TYPE_DEVICE_ORIENTATION: + mStringType = SENSOR_STRING_TYPE_DEVICE_ORIENTATION; + mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; + break; case SENSOR_TYPE_DYNAMIC_SENSOR_META: mStringType = SENSOR_STRING_TYPE_DYNAMIC_SENSOR_META; mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; // special trigger diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp index b3537ce444..ef3ceda07c 100644 --- a/libs/sensorprivacy/SensorPrivacyManager.cpp +++ b/libs/sensorprivacy/SensorPrivacyManager.cpp @@ -100,6 +100,15 @@ void SensorPrivacyManager::removeSensorPrivacyListener( } } +void SensorPrivacyManager::removeIndividualSensorPrivacyListener(int sensor, + const sp<hardware::ISensorPrivacyListener>& listener) +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + service->removeIndividualSensorPrivacyListener(sensor, listener); + } +} + bool SensorPrivacyManager::isSensorPrivacyEnabled() { sp<hardware::ISensorPrivacyManager> service = getService(); diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl index c8ceeb89af..f91f5b9017 100644 --- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl +++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl @@ -28,6 +28,8 @@ interface ISensorPrivacyManager { void removeSensorPrivacyListener(in ISensorPrivacyListener listener); + void removeIndividualSensorPrivacyListener(int sensor, in ISensorPrivacyListener listener); + boolean isSensorPrivacyEnabled(); boolean isIndividualSensorPrivacyEnabled(int userId, int sensor); diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h index fb4cbe9d55..af699d0b31 100644 --- a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h +++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h @@ -42,6 +42,8 @@ public: status_t addIndividualSensorPrivacyListener(int userId, int sensor, const sp<hardware::ISensorPrivacyListener>& listener); void removeSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener); + void removeIndividualSensorPrivacyListener(int sensor, + const sp<hardware::ISensorPrivacyListener>& listener); bool isSensorPrivacyEnabled(); bool isIndividualSensorPrivacyEnabled(int userId, int sensor); status_t isIndividualSensorPrivacyEnabled(int userId, int sensor, bool &result); diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp index 636fbde8d7..9dc9beb8e7 100644 --- a/libs/ui/Gralloc4.cpp +++ b/libs/ui/Gralloc4.cpp @@ -36,6 +36,7 @@ using android::hardware::graphics::common::V1_2::BufferUsage; using android::hardware::graphics::mapper::V4_0::BufferDescriptor; using android::hardware::graphics::mapper::V4_0::Error; using android::hardware::graphics::mapper::V4_0::IMapper; +using AidlDataspace = ::aidl::android::hardware::graphics::common::Dataspace; using BufferDump = android::hardware::graphics::mapper::V4_0::IMapper::BufferDump; using MetadataDump = android::hardware::graphics::mapper::V4_0::IMapper::MetadataDump; using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType; @@ -597,7 +598,7 @@ status_t Gralloc4Mapper::getDataspace(buffer_handle_t bufferHandle, if (!outDataspace) { return BAD_VALUE; } - aidl::android::hardware::graphics::common::Dataspace dataspace; + AidlDataspace dataspace; status_t error = get(bufferHandle, gralloc4::MetadataType_Dataspace, gralloc4::decodeDataspace, &dataspace); if (error) { @@ -841,6 +842,7 @@ status_t Gralloc4Mapper::bufferDumpHelper(const BufferDump& bufferDump, std::ost uint32_t pixelFormatFourCC; uint64_t pixelFormatModifier; uint64_t usage; + AidlDataspace dataspace; uint64_t allocationSize; uint64_t protectedContent; ExtendableType compression; @@ -892,6 +894,11 @@ status_t Gralloc4Mapper::bufferDumpHelper(const BufferDump& bufferDump, std::ost if (error != NO_ERROR) { return error; } + error = metadataDumpHelper(bufferDump, StandardMetadataType::DATASPACE, + gralloc4::decodeDataspace, &dataspace); + if (error != NO_ERROR) { + return error; + } error = metadataDumpHelper(bufferDump, StandardMetadataType::ALLOCATION_SIZE, gralloc4::decodeAllocationSize, &allocationSize); if (error != NO_ERROR) { @@ -932,6 +939,7 @@ status_t Gralloc4Mapper::bufferDumpHelper(const BufferDump& bufferDump, std::ost << "KiB, w/h:" << width << "x" << height << ", usage: 0x" << std::hex << usage << std::dec << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested) << ", fourcc/mod:" << pixelFormatFourCC << "/" << pixelFormatModifier + << ", dataspace: 0x" << std::hex << static_cast<uint32_t>(dataspace) << ", compressed: "; if (less) { diff --git a/libs/ui/include/ui/BlurRegion.h b/libs/ui/include/ui/BlurRegion.h index 69a586ecbb..a9ca369758 100644 --- a/libs/ui/include/ui/BlurRegion.h +++ b/libs/ui/include/ui/BlurRegion.h @@ -20,6 +20,8 @@ #include <iosfwd> #include <iostream> +#include <math/HashCombine.h> + namespace android { struct BlurRegion { @@ -33,6 +35,16 @@ struct BlurRegion { int top; int right; int bottom; + + inline bool operator==(const BlurRegion& other) const { + return blurRadius == other.blurRadius && cornerRadiusTL == other.cornerRadiusTL && + cornerRadiusTR == other.cornerRadiusTR && cornerRadiusBL == other.cornerRadiusBL && + cornerRadiusBR == other.cornerRadiusBR && alpha == other.alpha && + left == other.left && top == other.top && right == other.right && + bottom == other.bottom; + } + + inline bool operator!=(const BlurRegion& other) const { return !(*this == other); } }; static inline void PrintTo(const BlurRegion& blurRegion, ::std::ostream* os) { @@ -50,4 +62,15 @@ static inline void PrintTo(const BlurRegion& blurRegion, ::std::ostream* os) { *os << "\n}"; } -} // namespace android
\ No newline at end of file +} // namespace android + +namespace std { +template <> +struct hash<android::BlurRegion> { + size_t operator()(const android::BlurRegion& region) const { + return android::hashCombine(region.blurRadius, region.cornerRadiusTL, region.cornerRadiusTR, + region.cornerRadiusBL, region.cornerRadiusBR, region.alpha, + region.left, region.top, region.right, region.bottom); + } +}; +} // namespace std
\ No newline at end of file diff --git a/libs/ui/include/ui/StretchEffect.h b/libs/ui/include/ui/StretchEffect.h index 0803df3828..cf08acb359 100644 --- a/libs/ui/include/ui/StretchEffect.h +++ b/libs/ui/include/ui/StretchEffect.h @@ -25,31 +25,49 @@ namespace android { struct StretchEffect : public LightFlattenablePod<StretchEffect> { - FloatRect area = {0, 0, 0, 0}; - float vectorX = 0; - float vectorY = 0; - float maxAmount = 0; - - bool operator==(const StretchEffect& other) const { - return area == other.area && vectorX == other.vectorX && vectorY == other.vectorY && - maxAmount == other.maxAmount; - } + constexpr static const float CONTENT_DISTANCE_STRETCHED = 1.f; - static bool isZero(float value) { - constexpr float NON_ZERO_EPSILON = 0.001f; - return fabsf(value) <= NON_ZERO_EPSILON; - } + float width = 0; + float height = 0; + float vectorX = 0; + float vectorY = 0; + float maxAmountX = 0; + float maxAmountY = 0; + FloatRect mappedChildBounds = {0, 0, 0, 0}; + + bool operator==(const StretchEffect& other) const { + return width == other.width && height == other.height && + vectorX == other.vectorX && + vectorY == other.vectorY && + maxAmountX == other.maxAmountX && + maxAmountY == other.maxAmountY && + mappedChildBounds == other.mappedChildBounds; + } - bool isNoOp() const { return isZero(vectorX) && isZero(vectorY); } + static bool isZero(float value) { + constexpr float NON_ZERO_EPSILON = 0.001f; + return fabsf(value) <= NON_ZERO_EPSILON; + } - bool hasEffect() const { return !isNoOp(); } + bool isNoOp() const { return isZero(vectorX) && isZero(vectorY); } - void sanitize() { - // If the area is empty, or the max amount is zero, then reset back to defaults - if (area.isEmpty() || isZero(maxAmount)) { - *this = StretchEffect{}; - } + bool hasEffect() const { return !isNoOp(); } + + void sanitize() { + // If the area is empty, or the max amount is zero, then reset back to defaults + if (width == 0.f || height == 0.f || isZero(maxAmountX) || + isZero(maxAmountY)) { + *this = StretchEffect{}; } + } + + float getStretchWidthMultiplier() const { + return CONTENT_DISTANCE_STRETCHED / (1.f + abs(vectorX)); + } + + float getStretchHeightMultiplier() const { + return CONTENT_DISTANCE_STRETCHED / (1.f + abs(vectorY)); + } }; static_assert(std::is_trivially_copyable<StretchEffect>::value, diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 9b98a17b4d..6612a932a0 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -63,11 +63,16 @@ cc_defaults { "libcutils", "libhidlbase", "libinput", + "libkll", "liblog", + "libprotobuf-cpp-lite", "libstatslog", + "libstatspull", + "libstatssocket", "libutils", "libui", "lib-platform-compat-native-api", + "server_configurable_flags", ], static_libs: [ "libattestation", diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING index 8073a9322c..dc0e60c858 100644 --- a/services/inputflinger/TEST_MAPPING +++ b/services/inputflinger/TEST_MAPPING @@ -38,6 +38,14 @@ "include-filter": "android.security.cts.MotionEventTest" } ] + }, + { + "name": "CtsSecurityBulletinHostTestCases", + "options": [ + { + "include-filter": "android.security.cts.Poc19_03#testPocBug_115739809" + } + ] } ] } diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp index ea37f4d424..902bd0d7bb 100644 --- a/services/inputflinger/benchmarks/Android.bp +++ b/services/inputflinger/benchmarks/Android.bp @@ -12,7 +12,10 @@ cc_benchmark { srcs: [ "InputDispatcher_benchmarks.cpp", ], - defaults: ["inputflinger_defaults"], + defaults: [ + "inputflinger_defaults", + "libinputdispatcher_defaults", + ], shared_libs: [ "libbase", "libbinder", diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 1b5f1ab76e..bc77b8aef4 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -16,9 +16,11 @@ #include <benchmark/benchmark.h> +#include <android/os/IInputConstants.h> #include <binder/Binder.h> #include "../dispatcher/InputDispatcher.h" +using android::os::IInputConstants; using android::os::InputEventInjectionResult; using android::os::InputEventInjectionSync; @@ -226,7 +228,7 @@ static MotionEvent generateMotionEvent() { ui::Transform identityTransform; MotionEvent event; - event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + event.initialize(IInputConstants::INVALID_INPUT_EVENT_ID, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, @@ -252,9 +254,9 @@ static NotifyMotionArgs generateMotionArgs() { const nsecs_t currentTime = now(); // Define a valid motion event. - NotifyMotionArgs args(/* id */ 0, currentTime, currentTime, DEVICE_ID, - AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER, - AMOTION_EVENT_ACTION_DOWN, + NotifyMotionArgs args(IInputConstants::INVALID_INPUT_EVENT_ID, currentTime, currentTime, + DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + POLICY_FLAG_PASS_TO_USER, AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, pointerProperties, pointerCoords, @@ -283,14 +285,12 @@ static void benchmarkNotifyMotion(benchmark::State& state) { for (auto _ : state) { // Send ACTION_DOWN motionArgs.action = AMOTION_EVENT_ACTION_DOWN; - motionArgs.id = 0; motionArgs.downTime = now(); motionArgs.eventTime = motionArgs.downTime; dispatcher->notifyMotion(&motionArgs); // Send ACTION_UP motionArgs.action = AMOTION_EVENT_ACTION_UP; - motionArgs.id = 1; motionArgs.eventTime = now(); dispatcher->notifyMotion(&motionArgs); diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index 9750ef9833..1b3888b340 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -38,8 +38,11 @@ filegroup { "InjectionState.cpp", "InputDispatcher.cpp", "InputDispatcherFactory.cpp", + "InputEventTimeline.cpp", "InputState.cpp", "InputTarget.cpp", + "LatencyAggregator.cpp", + "LatencyTracker.cpp", "Monitor.cpp", "TouchState.cpp", "DragState.cpp", @@ -54,11 +57,16 @@ cc_defaults { "libcrypto", "libcutils", "libinput", + "libkll", "liblog", + "libprotobuf-cpp-lite", "libstatslog", + "libstatspull", + "libstatssocket", "libui", "libutils", "lib-platform-compat-native-api", + "server_configurable_flags", ], static_libs: [ "libattestation", diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 6ed959306b..881024fc6e 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -296,7 +296,7 @@ std::string SensorEntry::getDescription() const { volatile int32_t DispatchEntry::sNextSeqAtomic; DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags, - ui::Transform transform, float globalScaleFactor, vec2 displaySize) + ui::Transform transform, float globalScaleFactor, int2 displaySize) : seq(nextSeq()), eventEntry(std::move(eventEntry)), targetFlags(targetFlags), diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 45c5e24395..ebbd8e93bf 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -79,7 +79,7 @@ struct ConfigurationChangedEntry : EventEntry { explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime); std::string getDescription() const override; - virtual ~ConfigurationChangedEntry(); + ~ConfigurationChangedEntry() override; }; struct DeviceResetEntry : EventEntry { @@ -88,7 +88,7 @@ struct DeviceResetEntry : EventEntry { DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId); std::string getDescription() const override; - virtual ~DeviceResetEntry(); + ~DeviceResetEntry() override; }; struct FocusEntry : EventEntry { @@ -100,7 +100,7 @@ struct FocusEntry : EventEntry { const std::string& reason); std::string getDescription() const override; - virtual ~FocusEntry(); + ~FocusEntry() override; }; struct PointerCaptureChangedEntry : EventEntry { @@ -109,7 +109,7 @@ struct PointerCaptureChangedEntry : EventEntry { PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, bool hasPointerCapture); std::string getDescription() const override; - virtual ~PointerCaptureChangedEntry(); + ~PointerCaptureChangedEntry() override; }; struct DragEntry : EventEntry { @@ -153,7 +153,7 @@ struct KeyEntry : EventEntry { std::string getDescription() const override; void recycle(); - virtual ~KeyEntry(); + ~KeyEntry() override; }; struct MotionEntry : EventEntry { @@ -204,7 +204,7 @@ struct SensorEntry : EventEntry { std::vector<float> values); std::string getDescription() const override; - virtual ~SensorEntry(); + ~SensorEntry() override; }; // Tracks the progress of dispatching a particular event to a particular connection. @@ -215,7 +215,7 @@ struct DispatchEntry { int32_t targetFlags; ui::Transform transform; float globalScaleFactor; - vec2 displaySize; + int2 displaySize; // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app, // and will be undefined before that. nsecs_t deliveryTime; // time when the event was actually delivered @@ -228,7 +228,7 @@ struct DispatchEntry { int32_t resolvedFlags; DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags, - ui::Transform transform, float globalScaleFactor, vec2 displaySize); + ui::Transform transform, float globalScaleFactor, int2 displaySize); inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; } diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 16cb7d7a51..7e069c80d0 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -59,7 +59,6 @@ static constexpr bool DEBUG_TOUCH_OCCLUSION = true; #include <log/log.h> #include <log/log_event_list.h> #include <powermanager/PowerManager.h> -#include <statslog.h> #include <unistd.h> #include <utils/Trace.h> @@ -90,6 +89,14 @@ using com::android::internal::compat::IPlatformCompatNative; namespace android::inputdispatcher { +// When per-window-input-rotation is enabled, InputFlinger works in the un-rotated display +// coordinates and SurfaceFlinger includes the display rotation in the input window transforms. +static bool isPerWindowInputRotationEnabled() { + static const bool PER_WINDOW_INPUT_ROTATION = + base::GetBoolProperty("persist.debug.per_window_input_rotation", false); + return PER_WINDOW_INPUT_ROTATION; +} + // Default input dispatching timeout if there is no focused application or paused window // from which to determine an appropriate dispatching timeout. const std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT = std::chrono::milliseconds( @@ -283,27 +290,6 @@ static V getValueByKey(const std::unordered_map<K, V>& map, K key) { return it != map.end() ? it->second : V{}; } -/** - * Find the entry in std::unordered_map by value, and remove it. - * If more than one entry has the same value, then all matching - * key-value pairs will be removed. - * - * Return true if at least one value has been removed. - */ -template <typename K, typename V> -static bool removeByValue(std::unordered_map<K, V>& map, const V& value) { - bool removed = false; - for (auto it = map.begin(); it != map.end();) { - if (it->second == value) { - it = map.erase(it); - removed = true; - } else { - it++; - } - } - return removed; -} - static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) { if (first == second) { return true; @@ -468,6 +454,56 @@ static std::optional<int32_t> findMonitorPidByToken( return std::nullopt; } +static bool shouldReportMetricsForConnection(const Connection& connection) { + // Do not keep track of gesture monitors. They receive every event and would disproportionately + // affect the statistics. + if (connection.monitor) { + return false; + } + // If the connection is experiencing ANR, let's skip it. We have separate ANR metrics + if (!connection.responsive) { + return false; + } + return true; +} + +static bool shouldReportFinishedEvent(const DispatchEntry& dispatchEntry, + const Connection& connection) { + const EventEntry& eventEntry = *dispatchEntry.eventEntry; + const int32_t& inputEventId = eventEntry.id; + if (inputEventId != dispatchEntry.resolvedEventId) { + // Event was transmuted + return false; + } + if (inputEventId == android::os::IInputConstants::INVALID_INPUT_EVENT_ID) { + return false; + } + // Only track latency for events that originated from hardware + if (eventEntry.isSynthesized()) { + return false; + } + const EventEntry::Type& inputEventEntryType = eventEntry.type; + if (inputEventEntryType == EventEntry::Type::KEY) { + const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry); + if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) { + return false; + } + } else if (inputEventEntryType == EventEntry::Type::MOTION) { + const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); + if (motionEntry.action == AMOTION_EVENT_ACTION_CANCEL || + motionEntry.action == AMOTION_EVENT_ACTION_HOVER_EXIT) { + return false; + } + } else { + // Not a key or a motion + return false; + } + if (!shouldReportMetricsForConnection(connection)) { + return false; + } + return true; +} + // --- InputDispatcher --- InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) @@ -489,6 +525,8 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mFocusedDisplayId(ADISPLAY_ID_DEFAULT), mFocusedWindowRequestedPointerCapture(false), mWindowTokenWithPointerCapture(nullptr), + mLatencyAggregator(), + mLatencyTracker(&mLatencyAggregator), mCompatService(getCompatService()) { mLooper = new Looper(false); mReporter = createInputReporter(); @@ -507,8 +545,8 @@ InputDispatcher::~InputDispatcher() { drainInboundQueueLocked(); } - while (!mConnectionsByFd.empty()) { - sp<Connection> connection = mConnectionsByFd.begin()->second; + while (!mConnectionsByToken.empty()) { + sp<Connection> connection = mConnectionsByToken.begin()->second; removeInputChannel(connection->inputChannel->getConnectionToken()); } } @@ -2406,7 +2444,7 @@ void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowH inputTarget.flags = targetFlags; inputTarget.globalScaleFactor = windowInfo->globalScaleFactor; inputTarget.displaySize = - vec2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight); + int2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight); inputTargets.push_back(inputTarget); it = inputTargets.end() - 1; } @@ -2875,6 +2913,8 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio "event", connection->getInputChannelName().c_str()); #endif + // We keep the 'resolvedEventId' here equal to the original 'motionEntry.id' because + // this is a one-to-one event conversion. dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; } @@ -2907,6 +2947,12 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio ATRACE_NAME(message.c_str()); } + if ((motionEntry.flags & AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE) && + (motionEntry.policyFlags & POLICY_FLAG_TRUSTED)) { + // Skip reporting pointer down outside focus to the policy. + break; + } + dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction, inputTarget.inputChannel->getConnectionToken()); @@ -2936,7 +2982,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio // Enqueue the dispatch entry. connection->outboundQueue.push_back(dispatchEntry.release()); - traceOutboundQueueLength(connection); + traceOutboundQueueLength(*connection); } /** @@ -3117,7 +3163,6 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, motionEntry.downTime, motionEntry.eventTime, motionEntry.pointerCount, motionEntry.pointerProperties, usingCoords); - reportTouchEventForStatistics(motionEntry); break; } @@ -3191,13 +3236,13 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(), connection->outboundQueue.end(), dispatchEntry)); - traceOutboundQueueLength(connection); + traceOutboundQueueLength(*connection); connection->waitQueue.push_back(dispatchEntry); if (connection->responsive) { mAnrTracker.insert(dispatchEntry->timeoutTime, connection->inputChannel->getConnectionToken()); } - traceWaitQueueLength(connection); + traceWaitQueueLength(*connection); } } @@ -3266,9 +3311,9 @@ void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, // Clear the dispatch queues. drainDispatchQueue(connection->outboundQueue); - traceOutboundQueueLength(connection); + traceOutboundQueueLength(*connection); drainDispatchQueue(connection->waitQueue); - traceWaitQueueLength(connection); + traceWaitQueueLength(*connection); // The connection appears to be unrecoverably broken. // Ignore already broken or zombie connections. @@ -3297,86 +3342,85 @@ void InputDispatcher::releaseDispatchEntry(DispatchEntry* dispatchEntry) { delete dispatchEntry; } -int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { - InputDispatcher* d = static_cast<InputDispatcher*>(data); +int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionToken) { + std::scoped_lock _l(mLock); + sp<Connection> connection = getConnectionLocked(connectionToken); + if (connection == nullptr) { + ALOGW("Received looper callback for unknown input channel token %p. events=0x%x", + connectionToken.get(), events); + return 0; // remove the callback + } - { // acquire lock - std::scoped_lock _l(d->mLock); - - if (d->mConnectionsByFd.find(fd) == d->mConnectionsByFd.end()) { - ALOGE("Received spurious receive callback for unknown input channel. " - "fd=%d, events=0x%x", - fd, events); - return 0; // remove the callback - } - - bool notify; - sp<Connection> connection = d->mConnectionsByFd[fd]; - if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) { - if (!(events & ALOOPER_EVENT_INPUT)) { - ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " - "events=0x%x", - connection->getInputChannelName().c_str(), events); - return 1; - } + bool notify; + if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) { + if (!(events & ALOOPER_EVENT_INPUT)) { + ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " + "events=0x%x", + connection->getInputChannelName().c_str(), events); + return 1; + } - nsecs_t currentTime = now(); - bool gotOne = false; - status_t status = OK; - for (;;) { - Result<InputPublisher::ConsumerResponse> result = - connection->inputPublisher.receiveConsumerResponse(); - if (!result.ok()) { - status = result.error().code(); - break; - } + nsecs_t currentTime = now(); + bool gotOne = false; + status_t status = OK; + for (;;) { + Result<InputPublisher::ConsumerResponse> result = + connection->inputPublisher.receiveConsumerResponse(); + if (!result.ok()) { + status = result.error().code(); + break; + } - if (std::holds_alternative<InputPublisher::Finished>(*result)) { - const InputPublisher::Finished& finish = - std::get<InputPublisher::Finished>(*result); - d->finishDispatchCycleLocked(currentTime, connection, finish.seq, - finish.handled, finish.consumeTime); - } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) { - // TODO(b/167947340): Report this data to LatencyTracker + if (std::holds_alternative<InputPublisher::Finished>(*result)) { + const InputPublisher::Finished& finish = + std::get<InputPublisher::Finished>(*result); + finishDispatchCycleLocked(currentTime, connection, finish.seq, finish.handled, + finish.consumeTime); + } else if (std::holds_alternative<InputPublisher::Timeline>(*result)) { + if (shouldReportMetricsForConnection(*connection)) { + const InputPublisher::Timeline& timeline = + std::get<InputPublisher::Timeline>(*result); + mLatencyTracker + .trackGraphicsLatency(timeline.inputEventId, + connection->inputChannel->getConnectionToken(), + std::move(timeline.graphicsTimeline)); } - gotOne = true; } - if (gotOne) { - d->runCommandsLockedInterruptible(); - if (status == WOULD_BLOCK) { - return 1; - } + gotOne = true; + } + if (gotOne) { + runCommandsLockedInterruptible(); + if (status == WOULD_BLOCK) { + return 1; } + } - notify = status != DEAD_OBJECT || !connection->monitor; - if (notify) { - ALOGE("channel '%s' ~ Failed to receive finished signal. status=%s(%d)", - connection->getInputChannelName().c_str(), statusToString(status).c_str(), - status); - } - } else { - // Monitor channels are never explicitly unregistered. - // We do it automatically when the remote endpoint is closed so don't warn about them. - const bool stillHaveWindowHandle = - d->getWindowHandleLocked(connection->inputChannel->getConnectionToken()) != - nullptr; - notify = !connection->monitor && stillHaveWindowHandle; - if (notify) { - ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. " - "events=0x%x", - connection->getInputChannelName().c_str(), events); - } + notify = status != DEAD_OBJECT || !connection->monitor; + if (notify) { + ALOGE("channel '%s' ~ Failed to receive finished signal. status=%s(%d)", + connection->getInputChannelName().c_str(), statusToString(status).c_str(), + status); + } + } else { + // Monitor channels are never explicitly unregistered. + // We do it automatically when the remote endpoint is closed so don't warn about them. + const bool stillHaveWindowHandle = + getWindowHandleLocked(connection->inputChannel->getConnectionToken()) != nullptr; + notify = !connection->monitor && stillHaveWindowHandle; + if (notify) { + ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. events=0x%x", + connection->getInputChannelName().c_str(), events); } + } - // Remove the channel. - d->removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify); - return 0; // remove the callback - } // release lock + // Remove the channel. + removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify); + return 0; // remove the callback } void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( const CancelationOptions& options) { - for (const auto& [fd, connection] : mConnectionsByFd) { + for (const auto& [token, connection] : mConnectionsByToken) { synthesizeCancelationEventsForConnectionLocked(connection, options); } } @@ -3849,6 +3893,12 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { args->xCursorPosition, args->yCursorPosition, args->downTime, args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0); + if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID && + IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER && + !mInputFilterEnabled) { + const bool isDown = args->action == AMOTION_EVENT_ACTION_DOWN; + mLatencyTracker.trackListener(args->id, isDown, args->eventTime, args->readTime); + } needWake = enqueueInboundEventLocked(std::move(newEntry)); mLock.unlock(); @@ -4342,11 +4392,11 @@ bool InputDispatcher::hasResponsiveConnectionLocked(InputWindowHandle& windowHan std::shared_ptr<InputChannel> InputDispatcher::getInputChannelLocked( const sp<IBinder>& token) const { - size_t count = mInputChannelsByToken.count(token); - if (count == 0) { + auto connectionIt = mConnectionsByToken.find(token); + if (connectionIt == mConnectionsByToken.end()) { return nullptr; } - return mInputChannelsByToken.at(token); + return connectionIt->second->inputChannel; } void InputDispatcher::updateWindowHandlesForDisplayLocked( @@ -4449,6 +4499,13 @@ void InputDispatcher::setInputWindowsLocked( // Copy old handles for release if they are no longer present. const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId); + // Save the old windows' orientation by ID before it gets updated. + std::unordered_map<int32_t, uint32_t> oldWindowOrientations; + for (const sp<InputWindowHandle>& handle : oldWindowHandles) { + oldWindowOrientations.emplace(handle->getId(), + handle->getInfo()->transform.getOrientation()); + } + updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId); const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId); @@ -4497,6 +4554,25 @@ void InputDispatcher::setInputWindowsLocked( } } + if (isPerWindowInputRotationEnabled()) { + // Determine if the orientation of any of the input windows have changed, and cancel all + // pointer events if necessary. + for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) { + const sp<InputWindowHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle); + if (newWindowHandle != nullptr && + newWindowHandle->getInfo()->transform.getOrientation() != + oldWindowOrientations[oldWindowHandle->getId()]) { + std::shared_ptr<InputChannel> inputChannel = + getInputChannelLocked(newWindowHandle->getToken()); + if (inputChannel != nullptr) { + CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + "touched window's orientation changed"); + synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); + } + } + } + } + // Release information for windows that are no longer present. // This ensures that unused input channels are released promptly. // Otherwise, they might stick around until the window handle is destroyed @@ -4770,6 +4846,40 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< return true; } +// Binder call +bool InputDispatcher::transferTouch(const sp<IBinder>& destChannelToken) { + sp<IBinder> fromToken; + { // acquire lock + std::scoped_lock _l(mLock); + + sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(destChannelToken); + if (toWindowHandle == nullptr) { + ALOGW("Could not find window associated with token=%p", destChannelToken.get()); + return false; + } + + const int32_t displayId = toWindowHandle->getInfo()->displayId; + + auto touchStateIt = mTouchStatesByDisplay.find(displayId); + if (touchStateIt == mTouchStatesByDisplay.end()) { + ALOGD("Could not transfer touch because the display %" PRId32 " is not being touched", + displayId); + return false; + } + + TouchState& state = touchStateIt->second; + if (state.windows.size() != 1) { + ALOGW("Cannot transfer touch state because there are %zu windows being touched", + state.windows.size()); + return false; + } + const TouchedWindow& touchedWindow = state.windows[0]; + fromToken = touchedWindow.windowHandle->getToken(); + } // release lock + + return transferTouchFocus(fromToken, destChannelToken); +} + void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { if (DEBUG_FOCUS) { ALOGD("Resetting and dropping all events (%s).", reason); @@ -4996,13 +5106,13 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT "ReplacedKeys: <empty>\n"; } - if (!mConnectionsByFd.empty()) { + if (!mConnectionsByToken.empty()) { dump += INDENT "Connections:\n"; - for (const auto& pair : mConnectionsByFd) { - const sp<Connection>& connection = pair.second; + for (const auto& [token, connection] : mConnectionsByToken) { dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', " "status=%s, monitor=%s, responsive=%s\n", - pair.first, connection->getInputChannelName().c_str(), + connection->inputChannel->getFd().get(), + connection->getInputChannelName().c_str(), connection->getWindowName().c_str(), connection->getStatusLabel(), toString(connection->monitor), toString(connection->responsive)); @@ -5038,6 +5148,8 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += StringPrintf(INDENT2 "KeyRepeatDelay: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatDelay)); dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatTimeout)); + dump += mLatencyTracker.dump(INDENT2); + dump += mLatencyAggregator.dump(INDENT2); } void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) { @@ -5050,14 +5162,23 @@ void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor> } } +class LooperEventCallback : public LooperCallback { +public: + LooperEventCallback(std::function<int(int events)> callback) : mCallback(callback) {} + int handleEvent(int /*fd*/, int events, void* /*data*/) override { return mCallback(events); } + +private: + std::function<int(int events)> mCallback; +}; + Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) { #if DEBUG_CHANNEL_CREATION ALOGD("channel '%s' ~ createInputChannel", name.c_str()); #endif - std::shared_ptr<InputChannel> serverChannel; + std::unique_ptr<InputChannel> serverChannel; std::unique_ptr<InputChannel> clientChannel; - status_t result = openInputChannelPair(name, serverChannel, clientChannel); + status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel); if (result) { return base::Error(result) << "Failed to open input channel pair with name " << name; @@ -5065,13 +5186,20 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const { // acquire lock std::scoped_lock _l(mLock); - sp<Connection> connection = new Connection(serverChannel, false /*monitor*/, mIdGenerator); - + const sp<IBinder>& token = serverChannel->getConnectionToken(); int fd = serverChannel->getFd(); - mConnectionsByFd[fd] = connection; - mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel; + sp<Connection> connection = + new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator); + + if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) { + ALOGE("Created a new connection, but the token %p is already known", token.get()); + } + mConnectionsByToken.emplace(token, connection); + + std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback, + this, std::placeholders::_1, token); - mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr); } // release lock // Wake the looper because some connections have changed. @@ -5099,16 +5227,21 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_ } sp<Connection> connection = new Connection(serverChannel, true /*monitor*/, mIdGenerator); - + const sp<IBinder>& token = serverChannel->getConnectionToken(); const int fd = serverChannel->getFd(); - mConnectionsByFd[fd] = connection; - mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel; + + if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) { + ALOGE("Created a new connection, but the token %p is already known", token.get()); + } + mConnectionsByToken.emplace(token, connection); + std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback, + this, std::placeholders::_1, token); auto& monitorsByDisplay = isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay; monitorsByDisplay[displayId].emplace_back(serverChannel, pid); - mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr); ALOGI("Created monitor %s for display %" PRId32 ", gesture=%s, pid=%" PRId32, name.c_str(), displayId, toString(isGestureMonitor), pid); } @@ -5143,7 +5276,6 @@ status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connection } removeConnectionLocked(connection); - mInputChannelsByToken.erase(connectionToken); if (connection->monitor) { removeMonitorChannelLocked(connectionToken); @@ -5301,9 +5433,8 @@ sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConn return nullptr; } - for (const auto& pair : mConnectionsByFd) { - const sp<Connection>& connection = pair.second; - if (connection->inputChannel->getConnectionToken() == inputConnectionToken) { + for (const auto& [token, connection] : mConnectionsByToken) { + if (token == inputConnectionToken) { return connection; } } @@ -5321,7 +5452,7 @@ std::string InputDispatcher::getConnectionNameLocked(const sp<IBinder>& connecti void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) { mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); - removeByValue(mConnectionsByFd, connection); + mConnectionsByToken.erase(connection->inputChannel->getConnectionToken()); } void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime, @@ -5594,7 +5725,12 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(), ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str()); } - reportDispatchStatistics(std::chrono::nanoseconds(eventDuration), *connection, handled); + if (shouldReportFinishedEvent(*dispatchEntry, *connection)) { + mLatencyTracker.trackFinishedEvent(dispatchEntry->eventEntry->id, + connection->inputChannel->getConnectionToken(), + dispatchEntry->deliveryTime, commandEntry->consumeTime, + finishTime); + } bool restartEvent; if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) { @@ -5626,10 +5762,10 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c processConnectionResponsiveLocked(*connection); } } - traceWaitQueueLength(connection); + traceWaitQueueLength(*connection); if (restartEvent && connection->status == Connection::STATUS_NORMAL) { connection->outboundQueue.push_front(dispatchEntry); - traceOutboundQueueLength(connection); + traceOutboundQueueLength(*connection); } else { releaseDispatchEntry(dispatchEntry); } @@ -5906,58 +6042,25 @@ void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* comman mLock.lock(); } -void InputDispatcher::reportDispatchStatistics(std::chrono::nanoseconds eventDuration, - const Connection& connection, bool handled) { - // TODO Write some statistics about how long we spend waiting. -} - -/** - * Report the touch event latency to the statsd server. - * Input events are reported for statistics if: - * - This is a touchscreen event - * - InputFilter is not enabled - * - Event is not injected or synthesized - * - * Statistics should be reported before calling addValue, to prevent a fresh new sample - * from getting aggregated with the "old" data. - */ -void InputDispatcher::reportTouchEventForStatistics(const MotionEntry& motionEntry) - REQUIRES(mLock) { - const bool reportForStatistics = (motionEntry.source == AINPUT_SOURCE_TOUCHSCREEN) && - !(motionEntry.isSynthesized()) && !mInputFilterEnabled; - if (!reportForStatistics) { - return; - } - - if (mTouchStatistics.shouldReport()) { - android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mTouchStatistics.getMin(), - mTouchStatistics.getMax(), mTouchStatistics.getMean(), - mTouchStatistics.getStDev(), mTouchStatistics.getCount()); - mTouchStatistics.reset(); - } - const float latencyMicros = nanoseconds_to_microseconds(now() - motionEntry.eventTime); - mTouchStatistics.addValue(latencyMicros); -} - void InputDispatcher::traceInboundQueueLengthLocked() { if (ATRACE_ENABLED()) { ATRACE_INT("iq", mInboundQueue.size()); } } -void InputDispatcher::traceOutboundQueueLength(const sp<Connection>& connection) { +void InputDispatcher::traceOutboundQueueLength(const Connection& connection) { if (ATRACE_ENABLED()) { char counterName[40]; - snprintf(counterName, sizeof(counterName), "oq:%s", connection->getWindowName().c_str()); - ATRACE_INT(counterName, connection->outboundQueue.size()); + snprintf(counterName, sizeof(counterName), "oq:%s", connection.getWindowName().c_str()); + ATRACE_INT(counterName, connection.outboundQueue.size()); } } -void InputDispatcher::traceWaitQueueLength(const sp<Connection>& connection) { +void InputDispatcher::traceWaitQueueLength(const Connection& connection) { if (ATRACE_ENABLED()) { char counterName[40]; - snprintf(counterName, sizeof(counterName), "wq:%s", connection->getWindowName().c_str()); - ATRACE_INT(counterName, connection->waitQueue.size()); + snprintf(counterName, sizeof(counterName), "wq:%s", connection.getWindowName().c_str()); + ATRACE_INT(counterName, connection.waitQueue.size()); } } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 7ab4fd76dc..bb3f3e6dfc 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -29,6 +29,8 @@ #include "InputState.h" #include "InputTarget.h" #include "InputThread.h" +#include "LatencyAggregator.h" +#include "LatencyTracker.h" #include "Monitor.h" #include "TouchState.h" #include "TouchedWindow.h" @@ -39,7 +41,6 @@ #include <input/InputApplication.h> #include <input/InputTransport.h> #include <input/InputWindow.h> -#include <input/LatencyStatistics.h> #include <limits.h> #include <stddef.h> #include <ui/Region.h> @@ -81,60 +82,60 @@ class Connection; */ class InputDispatcher : public android::InputDispatcherInterface { protected: - virtual ~InputDispatcher(); + ~InputDispatcher() override; public: explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy); - virtual void dump(std::string& dump) override; - virtual void monitor() override; - virtual bool waitForIdle() override; - virtual status_t start() override; - virtual status_t stop() override; - - virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; - virtual void notifyKey(const NotifyKeyArgs* args) override; - virtual void notifyMotion(const NotifyMotionArgs* args) override; - virtual void notifySwitch(const NotifySwitchArgs* args) override; - virtual void notifySensor(const NotifySensorArgs* args) override; - virtual void notifyVibratorState(const NotifyVibratorStateArgs* args) override; - virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; - virtual void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override; - - virtual android::os::InputEventInjectionResult injectInputEvent( + void dump(std::string& dump) override; + void monitor() override; + bool waitForIdle() override; + status_t start() override; + status_t stop() override; + + void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; + void notifyKey(const NotifyKeyArgs* args) override; + void notifyMotion(const NotifyMotionArgs* args) override; + void notifySwitch(const NotifySwitchArgs* args) override; + void notifySensor(const NotifySensorArgs* args) override; + void notifyVibratorState(const NotifyVibratorStateArgs* args) override; + void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; + void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override; + + android::os::InputEventInjectionResult injectInputEvent( const InputEvent* event, int32_t injectorPid, int32_t injectorUid, android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) override; - virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override; + std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override; - virtual void setInputWindows( - const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>& - handlesPerDisplay) override; - virtual void setFocusedApplication( + void setInputWindows(const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>& + handlesPerDisplay) override; + void setFocusedApplication( int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override; - virtual void setFocusedDisplay(int32_t displayId) override; - virtual void setInputDispatchMode(bool enabled, bool frozen) override; - virtual void setInputFilterEnabled(bool enabled) override; - virtual void setInTouchMode(bool inTouchMode) override; - virtual void setMaximumObscuringOpacityForTouch(float opacity) override; - virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override; - - virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, - bool isDragDrop = false) override; - - virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel( + void setFocusedDisplay(int32_t displayId) override; + void setInputDispatchMode(bool enabled, bool frozen) override; + void setInputFilterEnabled(bool enabled) override; + void setInTouchMode(bool inTouchMode) override; + void setMaximumObscuringOpacityForTouch(float opacity) override; + void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override; + + bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, + bool isDragDrop = false) override; + bool transferTouch(const sp<IBinder>& destChannelToken) override; + + base::Result<std::unique_ptr<InputChannel>> createInputChannel( const std::string& name) override; - virtual void setFocusedWindow(const FocusRequest&) override; - virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId, - bool isGestureMonitor, - const std::string& name, - int32_t pid) override; - virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) override; - virtual status_t pilferPointers(const sp<IBinder>& token) override; - virtual void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override; - virtual bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override; + void setFocusedWindow(const FocusRequest&) override; + base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId, + bool isGestureMonitor, + const std::string& name, + int32_t pid) override; + status_t removeInputChannel(const sp<IBinder>& connectionToken) override; + status_t pilferPointers(const sp<IBinder>& token) override; + void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override; + bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override; std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const; @@ -211,9 +212,6 @@ private: bool addPortalWindows = false, bool ignoreDragWindow = false) REQUIRES(mLock); - // All registered connections mapped by channel file descriptor. - std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock); - sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const REQUIRES(mLock); @@ -225,8 +223,10 @@ private: struct StrongPointerHash { std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); } }; - std::unordered_map<sp<IBinder>, std::shared_ptr<InputChannel>, StrongPointerHash<IBinder>> - mInputChannelsByToken GUARDED_BY(mLock); + + // All registered connections mapped by input channel token. + std::unordered_map<sp<IBinder>, sp<Connection>, StrongPointerHash<IBinder>> mConnectionsByToken + GUARDED_BY(mLock); // Finds the display ID of the gesture monitor identified by the provided token. std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token) @@ -544,7 +544,7 @@ private: bool notify) REQUIRES(mLock); void drainDispatchQueue(std::deque<DispatchEntry*>& queue); void releaseDispatchEntry(DispatchEntry* dispatchEntry); - static int handleReceiveCallback(int fd, int events, void* data); + int handleReceiveCallback(int events, sp<IBinder> connectionToken); // The action sent should only be of type AMOTION_EVENT_* void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action, const sp<IBinder>& newToken) REQUIRES(mLock); @@ -637,15 +637,11 @@ private: void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); // Statistics gathering. - static constexpr std::chrono::duration TOUCH_STATS_REPORT_PERIOD = 5min; - LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD}; - - void reportTouchEventForStatistics(const MotionEntry& entry); - void reportDispatchStatistics(std::chrono::nanoseconds eventDuration, - const Connection& connection, bool handled); + LatencyAggregator mLatencyAggregator GUARDED_BY(mLock); + LatencyTracker mLatencyTracker GUARDED_BY(mLock); void traceInboundQueueLengthLocked() REQUIRES(mLock); - void traceOutboundQueueLength(const sp<Connection>& connection); - void traceWaitQueueLength(const sp<Connection>& connection); + void traceOutboundQueueLength(const Connection& connection); + void traceWaitQueueLength(const Connection& connection); sp<InputReporterInterface> mReporter; sp<com::android::internal::compat::IPlatformCompatNative> mCompatService; diff --git a/services/inputflinger/dispatcher/InputEventTimeline.cpp b/services/inputflinger/dispatcher/InputEventTimeline.cpp new file mode 100644 index 0000000000..3edb6381d4 --- /dev/null +++ b/services/inputflinger/dispatcher/InputEventTimeline.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InputEventTimeline.h" + +namespace android::inputdispatcher { + +ConnectionTimeline::ConnectionTimeline(nsecs_t deliveryTime, nsecs_t consumeTime, + nsecs_t finishTime) + : deliveryTime(deliveryTime), + consumeTime(consumeTime), + finishTime(finishTime), + mHasDispatchTimeline(true) {} + +ConnectionTimeline::ConnectionTimeline(std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) + : graphicsTimeline(std::move(graphicsTimeline)), mHasGraphicsTimeline(true) {} + +bool ConnectionTimeline::isComplete() const { + return mHasDispatchTimeline && mHasGraphicsTimeline; +} + +bool ConnectionTimeline::setDispatchTimeline(nsecs_t inDeliveryTime, nsecs_t inConsumeTime, + nsecs_t inFinishTime) { + if (mHasDispatchTimeline) { + return false; + } + deliveryTime = inDeliveryTime; + consumeTime = inConsumeTime; + finishTime = inFinishTime; + mHasDispatchTimeline = true; + return true; +} + +bool ConnectionTimeline::setGraphicsTimeline(std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) { + if (mHasGraphicsTimeline) { + return false; + } + graphicsTimeline = std::move(timeline); + mHasGraphicsTimeline = true; + return true; +} + +bool ConnectionTimeline::operator==(const ConnectionTimeline& rhs) const { + return deliveryTime == rhs.deliveryTime && consumeTime == rhs.consumeTime && + finishTime == rhs.finishTime && graphicsTimeline == rhs.graphicsTimeline && + mHasDispatchTimeline == rhs.mHasDispatchTimeline && + mHasGraphicsTimeline == rhs.mHasGraphicsTimeline; +} + +bool ConnectionTimeline::operator!=(const ConnectionTimeline& rhs) const { + return !operator==(rhs); +} + +InputEventTimeline::InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime) + : isDown(isDown), eventTime(eventTime), readTime(readTime) {} + +bool InputEventTimeline::operator==(const InputEventTimeline& rhs) const { + if (connectionTimelines.size() != rhs.connectionTimelines.size()) { + return false; + } + for (const auto& [connectionToken, connectionTimeline] : connectionTimelines) { + auto it = rhs.connectionTimelines.find(connectionToken); + if (it == rhs.connectionTimelines.end()) { + return false; + } + if (connectionTimeline != it->second) { + return false; + } + } + return isDown == rhs.isDown && eventTime == rhs.eventTime && readTime == rhs.readTime; +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputEventTimeline.h b/services/inputflinger/dispatcher/InputEventTimeline.h new file mode 100644 index 0000000000..77b8472f89 --- /dev/null +++ b/services/inputflinger/dispatcher/InputEventTimeline.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTEVENTTIMELINE_H +#define _UI_INPUT_INPUTDISPATCHER_INPUTEVENTTIMELINE_H + +#include <binder/IBinder.h> +#include <input/Input.h> +#include <unordered_map> + +namespace android { + +namespace inputdispatcher { + +/** + * Describes the input event timeline for each connection. + * An event with the same inputEventId can go to more than 1 connection simultaneously. + * For each connection that the input event goes to, there will be a separate ConnectionTimeline + * created. + * To create a complete ConnectionTimeline, we must receive two calls: + * 1) setDispatchTimeline + * 2) setGraphicsTimeline + * + * In a typical scenario, the dispatch timeline is known first. Later, if a frame is produced, the + * graphics timeline is available. + */ +struct ConnectionTimeline { + // DispatchTimeline + nsecs_t deliveryTime; // time at which the event was sent to the receiver + nsecs_t consumeTime; // time at which the receiver read the event + nsecs_t finishTime; // time at which the finish event was received + // GraphicsTimeline + std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; + + ConnectionTimeline(nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime); + ConnectionTimeline(std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline); + + /** + * True if all contained timestamps are valid, false otherwise. + */ + bool isComplete() const; + /** + * Set the dispatching-related times. Return true if the operation succeeded, false if the + * dispatching times have already been set. If this function returns false, it likely indicates + * an error from the app side. + */ + bool setDispatchTimeline(nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime); + /** + * Set the graphics-related times. Return true if the operation succeeded, false if the + * graphics times have already been set. If this function returns false, it likely indicates + * an error from the app side. + */ + bool setGraphicsTimeline(std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline); + + inline bool operator==(const ConnectionTimeline& rhs) const; + inline bool operator!=(const ConnectionTimeline& rhs) const; + +private: + bool mHasDispatchTimeline = false; + bool mHasGraphicsTimeline = false; +}; + +struct InputEventTimeline { + InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime); + const bool isDown; // True if this is an ACTION_DOWN event + const nsecs_t eventTime; + const nsecs_t readTime; + + struct IBinderHash { + std::size_t operator()(const sp<IBinder>& b) const { + return std::hash<IBinder*>{}(b.get()); + } + }; + + std::unordered_map<sp<IBinder>, ConnectionTimeline, IBinderHash> connectionTimelines; + + bool operator==(const InputEventTimeline& rhs) const; +}; + +class InputEventTimelineProcessor { +protected: + InputEventTimelineProcessor() {} + virtual ~InputEventTimelineProcessor() {} + +public: + /** + * Process the provided timeline + */ + virtual void processTimeline(const InputEventTimeline& timeline) = 0; +}; + +} // namespace inputdispatcher +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_INPUTEVENTTIMELINE_H diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index 2543852788..1c4980b302 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -101,7 +101,7 @@ struct InputTarget { float globalScaleFactor = 1.0f; // Display-size in its natural rotation. Used for compatibility transform of raw coordinates. - vec2 displaySize = {AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE}; + int2 displaySize = {AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE}; // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. diff --git a/services/inputflinger/dispatcher/LatencyAggregator.cpp b/services/inputflinger/dispatcher/LatencyAggregator.cpp new file mode 100644 index 0000000000..a5bfc25e78 --- /dev/null +++ b/services/inputflinger/dispatcher/LatencyAggregator.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "LatencyAggregator" +#include "LatencyAggregator.h" + +#include <inttypes.h> + +#include <android-base/stringprintf.h> +#include <input/Input.h> +#include <log/log.h> +#include <server_configurable_flags/get_flags.h> + +using android::base::StringPrintf; +using dist_proc::aggregation::KllQuantile; +using std::chrono_literals::operator""ms; + +// Convert the provided nanoseconds into hundreds of microseconds. +// Use hundreds of microseconds (as opposed to microseconds) to preserve space. +static inline int64_t ns2hus(nsecs_t nanos) { + return ns2us(nanos) / 100; +} + +// The maximum number of events that we will store in the statistics. Any events that we will +// receive after we have reached this number will be ignored. We could also implement this by +// checking the actual size of the current data and making sure that we do not go over. However, +// the serialization process of sketches is too heavy (1 ms for all 14 sketches), and would be too +// much to do (even if infrequently). +// The value here has been determined empirically. +static constexpr size_t MAX_EVENTS_FOR_STATISTICS = 20000; + +// Category (=namespace) name for the input settings that are applied at boot time +static const char* INPUT_NATIVE_BOOT = "input_native_boot"; +// Feature flag name for the threshold of end-to-end touch latency that would trigger +// SlowEventReported atom to be pushed +static const char* SLOW_EVENT_MIN_REPORTING_LATENCY_MILLIS = + "slow_event_min_reporting_latency_millis"; +// Feature flag name for the minimum delay before reporting a slow event after having just reported +// a slow event. This helps limit the amount of data sent to the server +static const char* SLOW_EVENT_MIN_REPORTING_INTERVAL_MILLIS = + "slow_event_min_reporting_interval_millis"; + +// If an event has end-to-end latency > 200 ms, it will get reported as a slow event. +std::chrono::milliseconds DEFAULT_SLOW_EVENT_MIN_REPORTING_LATENCY = 200ms; +// If we receive two slow events less than 1 min apart, we will only report 1 of them. +std::chrono::milliseconds DEFAULT_SLOW_EVENT_MIN_REPORTING_INTERVAL = 60000ms; + +static std::chrono::milliseconds getSlowEventMinReportingLatency() { + std::string millis = server_configurable_flags:: + GetServerConfigurableFlag(INPUT_NATIVE_BOOT, SLOW_EVENT_MIN_REPORTING_LATENCY_MILLIS, + std::to_string( + DEFAULT_SLOW_EVENT_MIN_REPORTING_LATENCY.count())); + return std::chrono::milliseconds(std::stoi(millis)); +} + +static std::chrono::milliseconds getSlowEventMinReportingInterval() { + std::string millis = server_configurable_flags:: + GetServerConfigurableFlag(INPUT_NATIVE_BOOT, SLOW_EVENT_MIN_REPORTING_INTERVAL_MILLIS, + std::to_string( + DEFAULT_SLOW_EVENT_MIN_REPORTING_INTERVAL.count())); + return std::chrono::milliseconds(std::stoi(millis)); +} + +namespace android::inputdispatcher { + +/** + * Same as android::util::BytesField, but doesn't store raw pointers, and therefore deletes its + * resources automatically. + */ +class SafeBytesField { +public: + explicit SafeBytesField(dist_proc::aggregation::KllQuantile& quantile) { + const zetasketch::android::AggregatorStateProto aggProto = quantile.SerializeToProto(); + mBuffer.resize(aggProto.ByteSizeLong()); + aggProto.SerializeToArray(mBuffer.data(), mBuffer.size()); + } + android::util::BytesField getBytesField() { + return android::util::BytesField(mBuffer.data(), mBuffer.size()); + } + +private: + std::vector<char> mBuffer; +}; + +LatencyAggregator::LatencyAggregator() { + AStatsManager_setPullAtomCallback(android::util::INPUT_EVENT_LATENCY_SKETCH, nullptr, + LatencyAggregator::pullAtomCallback, this); + dist_proc::aggregation::KllQuantileOptions options; + options.set_inv_eps(100); // Request precision of 1.0%, instead of default 0.1% + for (size_t i = 0; i < SketchIndex::SIZE; i++) { + mDownSketches[i] = KllQuantile::Create(options); + mMoveSketches[i] = KllQuantile::Create(options); + } +} + +LatencyAggregator::~LatencyAggregator() { + AStatsManager_clearPullAtomCallback(android::util::INPUT_EVENT_LATENCY_SKETCH); +} + +AStatsManager_PullAtomCallbackReturn LatencyAggregator::pullAtomCallback(int32_t atomTag, + AStatsEventList* data, + void* cookie) { + LatencyAggregator* pAggregator = reinterpret_cast<LatencyAggregator*>(cookie); + if (pAggregator == nullptr) { + LOG_ALWAYS_FATAL("pAggregator is null!"); + } + return pAggregator->pullData(data); +} + +void LatencyAggregator::processTimeline(const InputEventTimeline& timeline) { + processStatistics(timeline); + processSlowEvent(timeline); +} + +void LatencyAggregator::processStatistics(const InputEventTimeline& timeline) { + // Before we do any processing, check that we have not yet exceeded MAX_SIZE + if (mNumSketchEventsProcessed >= MAX_EVENTS_FOR_STATISTICS) { + return; + } + mNumSketchEventsProcessed++; + + std::array<std::unique_ptr<KllQuantile>, SketchIndex::SIZE>& sketches = + timeline.isDown ? mDownSketches : mMoveSketches; + + // Process common ones first + const nsecs_t eventToRead = timeline.readTime - timeline.eventTime; + sketches[SketchIndex::EVENT_TO_READ]->Add(ns2hus(eventToRead)); + + // Now process per-connection ones + for (const auto& [connectionToken, connectionTimeline] : timeline.connectionTimelines) { + if (!connectionTimeline.isComplete()) { + continue; + } + const nsecs_t readToDeliver = connectionTimeline.deliveryTime - timeline.readTime; + const nsecs_t deliverToConsume = + connectionTimeline.consumeTime - connectionTimeline.deliveryTime; + const nsecs_t consumeToFinish = + connectionTimeline.finishTime - connectionTimeline.consumeTime; + const nsecs_t gpuCompletedTime = + connectionTimeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; + const nsecs_t presentTime = + connectionTimeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; + const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime; + const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime; + const nsecs_t endToEnd = presentTime - timeline.eventTime; + + sketches[SketchIndex::READ_TO_DELIVER]->Add(ns2hus(readToDeliver)); + sketches[SketchIndex::DELIVER_TO_CONSUME]->Add(ns2hus(deliverToConsume)); + sketches[SketchIndex::CONSUME_TO_FINISH]->Add(ns2hus(consumeToFinish)); + sketches[SketchIndex::CONSUME_TO_GPU_COMPLETE]->Add(ns2hus(consumeToGpuComplete)); + sketches[SketchIndex::GPU_COMPLETE_TO_PRESENT]->Add(ns2hus(gpuCompleteToPresent)); + sketches[SketchIndex::END_TO_END]->Add(ns2hus(endToEnd)); + } +} + +AStatsManager_PullAtomCallbackReturn LatencyAggregator::pullData(AStatsEventList* data) { + std::array<std::unique_ptr<SafeBytesField>, SketchIndex::SIZE> serializedDownData; + std::array<std::unique_ptr<SafeBytesField>, SketchIndex::SIZE> serializedMoveData; + for (size_t i = 0; i < SketchIndex::SIZE; i++) { + serializedDownData[i] = std::make_unique<SafeBytesField>(*mDownSketches[i]); + serializedMoveData[i] = std::make_unique<SafeBytesField>(*mMoveSketches[i]); + } + android::util:: + addAStatsEvent(data, android::util::INPUT_EVENT_LATENCY_SKETCH, + // DOWN sketches + serializedDownData[SketchIndex::EVENT_TO_READ]->getBytesField(), + serializedDownData[SketchIndex::READ_TO_DELIVER]->getBytesField(), + serializedDownData[SketchIndex::DELIVER_TO_CONSUME]->getBytesField(), + serializedDownData[SketchIndex::CONSUME_TO_FINISH]->getBytesField(), + serializedDownData[SketchIndex::CONSUME_TO_GPU_COMPLETE] + ->getBytesField(), + serializedDownData[SketchIndex::GPU_COMPLETE_TO_PRESENT] + ->getBytesField(), + serializedDownData[SketchIndex::END_TO_END]->getBytesField(), + // MOVE sketches + serializedMoveData[SketchIndex::EVENT_TO_READ]->getBytesField(), + serializedMoveData[SketchIndex::READ_TO_DELIVER]->getBytesField(), + serializedMoveData[SketchIndex::DELIVER_TO_CONSUME]->getBytesField(), + serializedMoveData[SketchIndex::CONSUME_TO_FINISH]->getBytesField(), + serializedMoveData[SketchIndex::CONSUME_TO_GPU_COMPLETE] + ->getBytesField(), + serializedMoveData[SketchIndex::GPU_COMPLETE_TO_PRESENT] + ->getBytesField(), + serializedMoveData[SketchIndex::END_TO_END]->getBytesField()); + + for (size_t i = 0; i < SketchIndex::SIZE; i++) { + mDownSketches[i]->Reset(); + mMoveSketches[i]->Reset(); + } + // Start new aggregations + mNumSketchEventsProcessed = 0; + return AStatsManager_PULL_SUCCESS; +} + +void LatencyAggregator::processSlowEvent(const InputEventTimeline& timeline) { + static const std::chrono::duration sSlowEventThreshold = getSlowEventMinReportingLatency(); + static const std::chrono::duration sSlowEventReportingInterval = + getSlowEventMinReportingInterval(); + for (const auto& [token, connectionTimeline] : timeline.connectionTimelines) { + if (!connectionTimeline.isComplete()) { + continue; + } + mNumEventsSinceLastSlowEventReport++; + const nsecs_t presentTime = + connectionTimeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; + const std::chrono::nanoseconds endToEndLatency = + std::chrono::nanoseconds(presentTime - timeline.eventTime); + if (endToEndLatency < sSlowEventThreshold) { + continue; + } + // This is a slow event. Before we report it, check if we are reporting too often + const std::chrono::duration elapsedSinceLastReport = + std::chrono::nanoseconds(timeline.eventTime - mLastSlowEventTime); + if (elapsedSinceLastReport < sSlowEventReportingInterval) { + mNumSkippedSlowEvents++; + continue; + } + + const nsecs_t eventToRead = timeline.readTime - timeline.eventTime; + const nsecs_t readToDeliver = connectionTimeline.deliveryTime - timeline.readTime; + const nsecs_t deliverToConsume = + connectionTimeline.consumeTime - connectionTimeline.deliveryTime; + const nsecs_t consumeToFinish = + connectionTimeline.finishTime - connectionTimeline.consumeTime; + const nsecs_t gpuCompletedTime = + connectionTimeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; + const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime; + const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime; + + android::util::stats_write(android::util::SLOW_INPUT_EVENT_REPORTED, timeline.isDown, + static_cast<int32_t>(ns2us(eventToRead)), + static_cast<int32_t>(ns2us(readToDeliver)), + static_cast<int32_t>(ns2us(deliverToConsume)), + static_cast<int32_t>(ns2us(consumeToFinish)), + static_cast<int32_t>(ns2us(consumeToGpuComplete)), + static_cast<int32_t>(ns2us(gpuCompleteToPresent)), + static_cast<int32_t>(ns2us(endToEndLatency.count())), + static_cast<int32_t>(mNumEventsSinceLastSlowEventReport), + static_cast<int32_t>(mNumSkippedSlowEvents)); + mNumEventsSinceLastSlowEventReport = 0; + mNumSkippedSlowEvents = 0; + mLastSlowEventTime = timeline.readTime; + } +} + +std::string LatencyAggregator::dump(const char* prefix) { + std::string sketchDump = StringPrintf("%s Sketches:\n", prefix); + for (size_t i = 0; i < SketchIndex::SIZE; i++) { + const int64_t numDown = mDownSketches[i]->num_values(); + SafeBytesField downBytesField(*mDownSketches[i]); + const float downBytesKb = downBytesField.getBytesField().arg_length * 1E-3; + const int64_t numMove = mMoveSketches[i]->num_values(); + SafeBytesField moveBytesField(*mMoveSketches[i]); + const float moveBytesKb = moveBytesField.getBytesField().arg_length * 1E-3; + sketchDump += + StringPrintf("%s mDownSketches[%zu]->num_values = %" PRId64 " size = %.1fKB" + " mMoveSketches[%zu]->num_values = %" PRId64 " size = %.1fKB\n", + prefix, i, numDown, downBytesKb, i, numMove, moveBytesKb); + } + + return StringPrintf("%sLatencyAggregator:\n", prefix) + sketchDump + + StringPrintf("%s mNumSketchEventsProcessed=%zu\n", prefix, mNumSketchEventsProcessed) + + StringPrintf("%s mLastSlowEventTime=%" PRId64 "\n", prefix, mLastSlowEventTime) + + StringPrintf("%s mNumEventsSinceLastSlowEventReport = %zu\n", prefix, + mNumEventsSinceLastSlowEventReport) + + StringPrintf("%s mNumSkippedSlowEvents = %zu\n", prefix, mNumSkippedSlowEvents); +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/LatencyAggregator.h b/services/inputflinger/dispatcher/LatencyAggregator.h new file mode 100644 index 0000000000..ed5731f8d9 --- /dev/null +++ b/services/inputflinger/dispatcher/LatencyAggregator.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_LATENCYAGGREGATOR_H +#define _UI_INPUT_INPUTDISPATCHER_LATENCYAGGREGATOR_H + +#include <kll.h> +#include <statslog.h> +#include <utils/Timers.h> + +#include "InputEventTimeline.h" + +namespace android::inputdispatcher { + +enum SketchIndex : size_t { + EVENT_TO_READ = 0, + READ_TO_DELIVER = 1, + DELIVER_TO_CONSUME = 2, + CONSUME_TO_FINISH = 3, + CONSUME_TO_GPU_COMPLETE = 4, + GPU_COMPLETE_TO_PRESENT = 5, + END_TO_END = 6, // EVENT_TO_PRESENT + SIZE = 7, // Must be last +}; + +// Let's create a full timeline here: +// eventTime +// readTime +// <---- after this point, the data becomes per-connection +// deliveryTime // time at which the event was sent to the receiver +// consumeTime // time at which the receiver read the event +// finishTime // time at which the finish event was received +// GraphicsTimeline::GPU_COMPLETED_TIME +// GraphicsTimeline::PRESENT_TIME + +/** + * Keep sketches of the provided events and report slow events + */ +class LatencyAggregator final : public InputEventTimelineProcessor { +public: + LatencyAggregator(); + /** + * Record a complete event timeline + */ + void processTimeline(const InputEventTimeline& timeline) override; + + std::string dump(const char* prefix); + + ~LatencyAggregator(); + +private: + static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atom_tag, + AStatsEventList* data, + void* cookie); + AStatsManager_PullAtomCallbackReturn pullData(AStatsEventList* data); + // ---------- Slow event handling ---------- + void processSlowEvent(const InputEventTimeline& timeline); + nsecs_t mLastSlowEventTime = 0; + // How many slow events have been skipped due to rate limiting + size_t mNumSkippedSlowEvents = 0; + // How many events have been received since the last time we reported a slow event + size_t mNumEventsSinceLastSlowEventReport = 0; + + // ---------- Statistics handling ---------- + void processStatistics(const InputEventTimeline& timeline); + // Sketches + std::array<std::unique_ptr<dist_proc::aggregation::KllQuantile>, SketchIndex::SIZE> + mDownSketches; + std::array<std::unique_ptr<dist_proc::aggregation::KllQuantile>, SketchIndex::SIZE> + mMoveSketches; + // How many events have been processed so far + size_t mNumSketchEventsProcessed = 0; +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_LATENCYAGGREGATOR_H diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp new file mode 100644 index 0000000000..d634dcda1d --- /dev/null +++ b/services/inputflinger/dispatcher/LatencyTracker.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "LatencyTracker" +#include "LatencyTracker.h" + +#include <inttypes.h> + +#include <android-base/properties.h> +#include <android-base/stringprintf.h> +#include <android/os/IInputConstants.h> +#include <input/Input.h> +#include <log/log.h> + +using android::base::HwTimeoutMultiplier; +using android::base::StringPrintf; + +namespace android::inputdispatcher { + +/** + * Events that are older than this time will be considered mature, at which point we will stop + * waiting for the apps to provide further information about them. + * It's likely that the apps will ANR if the events are not received by this deadline, and we + * already track ANR metrics separately. + */ +const std::chrono::duration ANR_TIMEOUT = std::chrono::milliseconds( + android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS * + HwTimeoutMultiplier()); + +static bool isMatureEvent(nsecs_t eventTime, nsecs_t now) { + std::chrono::duration age = std::chrono::nanoseconds(now) - std::chrono::nanoseconds(eventTime); + return age > ANR_TIMEOUT; +} + +/** + * A multimap allows to have several entries with the same key. This function just erases a specific + * key-value pair. Equivalent to the imaginary std api std::multimap::erase(key, value). + */ +template <typename K, typename V> +static void eraseByKeyAndValue(std::multimap<K, V>& map, K key, V value) { + auto iterpair = map.equal_range(key); + + for (auto it = iterpair.first; it != iterpair.second; ++it) { + if (it->second == value) { + map.erase(it); + break; + } + } +} + +LatencyTracker::LatencyTracker(InputEventTimelineProcessor* processor) + : mTimelineProcessor(processor) { + LOG_ALWAYS_FATAL_IF(processor == nullptr); +} + +void LatencyTracker::trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, + nsecs_t readTime) { + reportAndPruneMatureRecords(eventTime); + const auto it = mTimelines.find(inputEventId); + if (it != mTimelines.end()) { + // Input event ids are randomly generated, so it's possible that two events have the same + // event id. Drop this event, and also drop the existing event because the apps would + // confuse us by reporting the rest of the timeline for one of them. This should happen + // rarely, so we won't lose much data + mTimelines.erase(it); + // In case we have another input event with a different id and at the same eventTime, + // only erase this specific inputEventId. + eraseByKeyAndValue(mEventTimes, eventTime, inputEventId); + return; + } + mTimelines.emplace(inputEventId, InputEventTimeline(isDown, eventTime, readTime)); + mEventTimes.emplace(eventTime, inputEventId); +} + +void LatencyTracker::trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken, + nsecs_t deliveryTime, nsecs_t consumeTime, + nsecs_t finishTime) { + const auto it = mTimelines.find(inputEventId); + if (it == mTimelines.end()) { + // It's possible that an app sends a bad (or late)'Finish' signal, since it's free to do + // anything in its process. Just drop the report and move on. + return; + } + + InputEventTimeline& timeline = it->second; + const auto connectionIt = timeline.connectionTimelines.find(connectionToken); + if (connectionIt == timeline.connectionTimelines.end()) { + // Most likely case: app calls 'finishInputEvent' before it reports the graphics timeline + timeline.connectionTimelines.emplace(connectionToken, + ConnectionTimeline{deliveryTime, consumeTime, + finishTime}); + } else { + // Already have a record for this connectionToken + ConnectionTimeline& connectionTimeline = connectionIt->second; + const bool success = + connectionTimeline.setDispatchTimeline(deliveryTime, consumeTime, finishTime); + if (!success) { + // We are receiving unreliable data from the app. Just delete the entire connection + // timeline for this event + timeline.connectionTimelines.erase(connectionIt); + } + } +} + +void LatencyTracker::trackGraphicsLatency( + int32_t inputEventId, const sp<IBinder>& connectionToken, + std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) { + const auto it = mTimelines.find(inputEventId); + if (it == mTimelines.end()) { + // It's possible that an app sends a bad (or late) 'Timeline' signal, since it's free to do + // anything in its process. Just drop the report and move on. + return; + } + + InputEventTimeline& timeline = it->second; + const auto connectionIt = timeline.connectionTimelines.find(connectionToken); + if (connectionIt == timeline.connectionTimelines.end()) { + timeline.connectionTimelines.emplace(connectionToken, std::move(graphicsTimeline)); + } else { + // Most likely case + ConnectionTimeline& connectionTimeline = connectionIt->second; + const bool success = connectionTimeline.setGraphicsTimeline(std::move(graphicsTimeline)); + if (!success) { + // We are receiving unreliable data from the app. Just delete the entire connection + // timeline for this event + timeline.connectionTimelines.erase(connectionIt); + } + } +} + +/** + * We should use the current time 'now()' here to determine the age of the event, but instead we + * are using the latest 'eventTime' for efficiency since this time is already acquired, and + * 'trackListener' should happen soon after the event occurs. + */ +void LatencyTracker::reportAndPruneMatureRecords(nsecs_t newEventTime) { + while (!mEventTimes.empty()) { + const auto& [oldestEventTime, oldestInputEventId] = *mEventTimes.begin(); + if (isMatureEvent(oldestEventTime, newEventTime /*now*/)) { + // Report and drop this event + const auto it = mTimelines.find(oldestInputEventId); + LOG_ALWAYS_FATAL_IF(it == mTimelines.end(), + "Event %" PRId32 " is in mEventTimes, but not in mTimelines", + oldestInputEventId); + const InputEventTimeline& timeline = it->second; + mTimelineProcessor->processTimeline(timeline); + mTimelines.erase(it); + mEventTimes.erase(mEventTimes.begin()); + } else { + // If the oldest event does not need to be pruned, no events should be pruned. + return; + } + } +} + +void LatencyTracker::reportNow() { + for (const auto& [inputEventId, timeline] : mTimelines) { + mTimelineProcessor->processTimeline(timeline); + } + mTimelines.clear(); + mEventTimes.clear(); +} + +std::string LatencyTracker::dump(const char* prefix) { + return StringPrintf("%sLatencyTracker:\n", prefix) + + StringPrintf("%s mTimelines.size() = %zu\n", prefix, mTimelines.size()) + + StringPrintf("%s mEventTimes.size() = %zu\n", prefix, mEventTimes.size()); +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h new file mode 100644 index 0000000000..289b8ed6c4 --- /dev/null +++ b/services/inputflinger/dispatcher/LatencyTracker.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_LATENCYTRACKER_H +#define _UI_INPUT_INPUTDISPATCHER_LATENCYTRACKER_H + +#include <map> +#include <unordered_map> + +#include <binder/IBinder.h> +#include <input/Input.h> + +#include "InputEventTimeline.h" + +namespace android::inputdispatcher { + +/** + * Maintain a record for input events that are received by InputDispatcher, sent out to the apps, + * and processed by the apps. Once an event becomes "mature" (older than the ANR timeout), report + * the entire input event latency history to the reporting function. + * + * All calls to LatencyTracker should come from the same thread. It is not thread-safe. + */ +class LatencyTracker { +public: + /** + * Create a LatencyTracker. + * param reportingFunction: the function that will be called in order to report full latency. + */ + LatencyTracker(InputEventTimelineProcessor* processor); + /** + * Start keeping track of an event identified by inputEventId. This must be called first. + */ + void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime); + void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken, + nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime); + void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken, + std::array<nsecs_t, GraphicsTimeline::SIZE> timeline); + + /** + * Report all collected events immediately, even if some of them are currently incomplete + * and may receive 'trackFinishedEvent' or 'trackGraphicsLatency' calls in the future. + * This is useful for tests. Otherwise, tests would have to inject additional "future" events, + * which is not convenient. + */ + void reportNow(); + + std::string dump(const char* prefix); + +private: + /** + * A collection of InputEventTimelines keyed by inputEventId. An InputEventTimeline is first + * created when 'trackListener' is called. + * When either 'trackFinishedEvent' or 'trackGraphicsLatency' is called for this input event, + * the corresponding InputEventTimeline will be updated for that token. + */ + std::unordered_map<int32_t /*inputEventId*/, InputEventTimeline> mTimelines; + /** + * The collection of eventTimes will help us quickly find the events that we should prune + * from the 'mTimelines'. Since 'mTimelines' is keyed by inputEventId, it would be inefficient + * to walk through it directly to find the oldest input events to get rid of. + * There is a 1:1 mapping between 'mTimelines' and 'mEventTimes'. + * We are using 'multimap' instead of 'map' because there could be more than 1 event with the + * same eventTime. + */ + std::multimap<nsecs_t /*eventTime*/, int32_t /*inputEventId*/> mEventTimes; + + InputEventTimelineProcessor* mTimelineProcessor; + void reportAndPruneMatureRecords(nsecs_t newEventTime); +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_LATENCYTRACKER_H diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index b601dfc8a4..7f85e5393b 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -151,6 +151,14 @@ public: */ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, bool isDragDrop) = 0; + + /** + * Transfer touch focus to the provided channel, no matter where the current touch is. + * + * Return true on success, false if there was no on-going touch. + */ + virtual bool transferTouch(const sp<IBinder>& destChannelToken) = 0; + /** * Sets focus on the specified window. */ diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 2d0fdf7958..e91f84e876 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -413,8 +413,7 @@ void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& o } } -void InputReader::dispatchExternalStylusState(const StylusState& state) { - std::scoped_lock _l(mLock); +void InputReader::dispatchExternalStylusStateLocked(const StylusState& state) { for (auto& devicePair : mDevices) { std::shared_ptr<InputDevice>& device = devicePair.second; device->updateExternalStylusState(state); @@ -945,7 +944,7 @@ void InputReader::ContextImpl::getExternalStylusDevices(std::vector<InputDeviceI } void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) { - mReader->dispatchExternalStylusState(state); + mReader->dispatchExternalStylusStateLocked(state); } InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() { diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp index 1a40d06f45..16251ee0ca 100644 --- a/services/inputflinger/reader/controller/PeripheralController.cpp +++ b/services/inputflinger/reader/controller/PeripheralController.cpp @@ -104,7 +104,7 @@ void PeripheralController::Light::setRawLightBrightness(int32_t rawLightId, int3 context.setLightBrightness(rawLightId, brightness); } -bool PeripheralController::SingleLight::setLightColor(int32_t color) { +bool PeripheralController::MonoLight::setLightColor(int32_t color) { int32_t brightness = getAlpha(color); setRawLightBrightness(rawId, brightness); @@ -148,7 +148,7 @@ bool PeripheralController::MultiColorLight::setLightColor(int32_t color) { return true; } -std::optional<int32_t> PeripheralController::SingleLight::getLightColor() { +std::optional<int32_t> PeripheralController::MonoLight::getLightColor() { std::optional<int32_t> brightness = getRawLightBrightness(rawId); if (!brightness.has_value()) { return std::nullopt; @@ -234,7 +234,7 @@ std::optional<int32_t> PeripheralController::PlayerIdLight::getLightPlayerId() { return std::nullopt; } -void PeripheralController::SingleLight::dump(std::string& dump) { +void PeripheralController::MonoLight::dump(std::string& dump) { dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0)); } @@ -423,7 +423,7 @@ void PeripheralController::configureLights() { playerIdLightIds); mLights.insert_or_assign(light->id, std::move(light)); // Remove these raw lights from raw light info as they've been used to compose a - // Player ID light, so we do not expose these raw lights as single lights. + // Player ID light, so we do not expose these raw lights as mono lights. for (const auto& [playerId, rawId] : playerIdLightIds) { rawInfos.erase(rawId); } @@ -460,13 +460,12 @@ void PeripheralController::configureLights() { mLights.insert_or_assign(light->id, std::move(light)); continue; } - // Construct a single LED light + // Construct a Mono LED light if (DEBUG_LIGHT_DETAILS) { - ALOGD("Single light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str()); + ALOGD("Mono light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str()); } - std::unique_ptr<Light> light = - std::make_unique<SingleLight>(getDeviceContext(), rawInfo.name, ++mNextId, - rawInfo.id); + std::unique_ptr<Light> light = std::make_unique<MonoLight>(getDeviceContext(), rawInfo.name, + ++mNextId, rawInfo.id); mLights.insert_or_assign(light->id, std::move(light)); } diff --git a/services/inputflinger/reader/controller/PeripheralController.h b/services/inputflinger/reader/controller/PeripheralController.h index ff3607f6c1..b1bc8c732c 100644 --- a/services/inputflinger/reader/controller/PeripheralController.h +++ b/services/inputflinger/reader/controller/PeripheralController.h @@ -78,10 +78,10 @@ private: void setRawLightBrightness(int32_t rawLightId, int32_t brightness); }; - struct SingleLight : public Light { - explicit SingleLight(InputDeviceContext& context, const std::string& name, int32_t id, - int32_t rawId) - : Light(context, name, id, InputDeviceLightType::SINGLE), rawId(rawId) {} + struct MonoLight : public Light { + explicit MonoLight(InputDeviceContext& context, const std::string& name, int32_t id, + int32_t rawId) + : Light(context, name, id, InputDeviceLightType::MONO), rawId(rawId) {} int32_t rawId; bool setLightColor(int32_t color) override; diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 1405671a50..bc79ccf652 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -203,7 +203,7 @@ private: void notifyExternalStylusPresenceChangedLocked() REQUIRES(mLock); void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) REQUIRES(mLock); - void dispatchExternalStylusState(const StylusState& state); + void dispatchExternalStylusStateLocked(const StylusState& state) REQUIRES(mLock); // The PointerController that is shared among all the input devices that need it. std::weak_ptr<PointerControllerInterface> mPointerController; diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index d6bd8237e7..437902a79b 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -188,6 +188,8 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { mOrientation = DISPLAY_ORIENTATION_0; + mDisplayWidth = 0; + mDisplayHeight = 0; const bool isOrientedDevice = (mParameters.orientationAware && mParameters.hasAssociatedDisplay); @@ -202,6 +204,8 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config->getDisplayViewportByType(ViewportType::INTERNAL); if (internalViewport) { mOrientation = getInverseRotation(internalViewport->orientation); + mDisplayWidth = internalViewport->deviceWidth; + mDisplayHeight = internalViewport->deviceHeight; } } } else { @@ -360,13 +364,19 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { } mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + if (isPerWindowInputRotationEnabled()) { + // Rotate the cursor position that is in PointerController's rotated coordinate space + // to InputReader's un-rotated coordinate space. + rotatePoint(mOrientation, xCursorPosition /*byRef*/, yCursorPosition /*byRef*/, + mDisplayWidth, mDisplayHeight); + } pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); displayId = mPointerController->getDisplayId(); - } else if (mSource == AINPUT_SOURCE_MOUSE_RELATIVE) { - // Pointer capture mode + } else { + // Pointer capture and navigation modes pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 9a8ca01294..88e947f7d5 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -105,6 +105,8 @@ private: VelocityControl mWheelYVelocityControl; int32_t mOrientation; + int32_t mDisplayWidth; + int32_t mDisplayHeight; std::shared_ptr<PointerControllerInterface> mPointerController; diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index 1843b03c59..da0fea4616 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -68,6 +68,29 @@ static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) { *deltaX = -*deltaY; *deltaY = temp; break; + + default: + break; + } +} + +// Rotates the given point (x, y) by the supplied orientation. The width and height are the +// dimensions of the surface prior to this rotation being applied. +static void rotatePoint(int32_t orientation, float& x, float& y, int32_t width, int32_t height) { + rotateDelta(orientation, &x, &y); + switch (orientation) { + case DISPLAY_ORIENTATION_90: + y += width; + break; + case DISPLAY_ORIENTATION_180: + x += width; + y += height; + break; + case DISPLAY_ORIENTATION_270: + x += height; + break; + default: + break; } } diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 13712eee53..60502384a3 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -28,30 +28,6 @@ namespace android { -namespace { - -// Rotates the given point (x, y) by the supplied orientation. The width and height are the -// dimensions of the surface prior to this rotation being applied. -void rotatePoint(int32_t orientation, float& x, float& y, int32_t width, int32_t height) { - rotateDelta(orientation, &x, &y); - switch (orientation) { - case DISPLAY_ORIENTATION_90: - y += width; - break; - case DISPLAY_ORIENTATION_180: - x += width; - y += height; - break; - case DISPLAY_ORIENTATION_270: - x += height; - break; - default: - break; - } -} - -} // namespace - // --- Constants --- // Maximum amount of latency to add to touch events while waiting for data from an @@ -682,7 +658,9 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { int32_t rawHeight = mRawPointerAxes.getRawHeight(); bool viewportChanged = mViewport != *newViewport; + bool skipViewportUpdate = false; if (viewportChanged) { + bool viewportOrientationChanged = mViewport.orientation != newViewport->orientation; mViewport = *newViewport; if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) { @@ -746,6 +724,8 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mPhysicalLeft = naturalPhysicalLeft; mPhysicalTop = naturalPhysicalTop; + const int32_t oldSurfaceWidth = mRawSurfaceWidth; + const int32_t oldSurfaceHeight = mRawSurfaceHeight; mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; mRawSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth; @@ -763,6 +743,11 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mSurfaceOrientation = mParameters.orientationAware ? DISPLAY_ORIENTATION_0 : getInverseRotation(mViewport.orientation); + // For orientation-aware devices that work in the un-rotated coordinate space, the + // viewport update should be skipped if it is only a change in the orientation. + skipViewportUpdate = mParameters.orientationAware && + mRawSurfaceWidth == oldSurfaceWidth && + mRawSurfaceHeight == oldSurfaceHeight && viewportOrientationChanged; } else { mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0; @@ -802,7 +787,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mPointerController.reset(); } - if (viewportChanged || deviceModeChanged) { + if ((viewportChanged && !skipViewportUpdate) || deviceModeChanged) { ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, " "display id %d", getDeviceId(), getDeviceName().c_str(), mRawSurfaceWidth, mRawSurfaceHeight, @@ -2456,6 +2441,12 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u int32_t metaState = getContext()->getGlobalMetaState(); int32_t buttonState = mCurrentCookedState.buttonState; + uint32_t flags = 0; + + if (!PointerGesture::canGestureAffectWindowFocus(mPointerGesture.currentGestureMode)) { + flags |= AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE; + } + // Update last coordinates of pointers that have moved so that we observe the new // pointer positions at the same time as other pointers that have just gone up. bool down = mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP || @@ -2485,8 +2476,8 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits); if (!dispatchedGestureIdBits.isEmpty()) { if (cancelPreviousGesture) { - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, - metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, + flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, mPointerGesture.downTime); @@ -2504,7 +2495,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u uint32_t id = upGestureIdBits.clearFirstMarkedBit(); dispatchMotion(when, readTime, policyFlags, mSource, - AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState, + AMOTION_EVENT_ACTION_POINTER_UP, 0, flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0, @@ -2517,7 +2508,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u // Send motion events for all pointers that moved. if (moveNeeded) { - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, + dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.currentGestureProperties, mPointerGesture.currentGestureCoords, @@ -2538,7 +2529,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u } dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, - 0, 0, metaState, buttonState, 0, + 0, flags, metaState, buttonState, 0, mPointerGesture.currentGestureProperties, mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0, @@ -2548,8 +2539,8 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u // Send motion events for hover. if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) { - dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, + flags, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.currentGestureProperties, mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, @@ -2573,7 +2564,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u const int32_t displayId = mPointerController->getDisplayId(); NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags, metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {}); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 5146299bd5..920f8428f3 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -590,6 +590,27 @@ private: QUIET, }; + // When a gesture is sent to an unfocused window, return true if it can bring that window + // into focus, false otherwise. + static bool canGestureAffectWindowFocus(Mode mode) { + switch (mode) { + case Mode::TAP: + case Mode::TAP_DRAG: + case Mode::BUTTON_CLICK_OR_DRAG: + // Taps can affect window focus. + return true; + case Mode::FREEFORM: + case Mode::HOVER: + case Mode::NEUTRAL: + case Mode::PRESS: + case Mode::QUIET: + case Mode::SWIPE: + // Most gestures can be performed on an unfocused window, so they should not + // not affect window focus. + return false; + } + } + // Time the first finger went down. nsecs_t firstTouchTime; diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 42b54c75b6..918e1bef7a 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -46,6 +46,7 @@ cc_test { "InputDispatcher_test.cpp", "InputReader_test.cpp", "InputFlingerService_test.cpp", + "LatencyTracker_test.cpp", "TestInputListener.cpp", "UinputDevice.cpp", ], diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 9687c83ca2..93aa6aca6d 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -441,7 +441,7 @@ protected: sp<FakeInputDispatcherPolicy> mFakePolicy; sp<InputDispatcher> mDispatcher; - virtual void SetUp() override { + void SetUp() override { mFakePolicy = new FakeInputDispatcherPolicy(); mDispatcher = new InputDispatcher(mFakePolicy); mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false); @@ -449,7 +449,7 @@ protected: ASSERT_EQ(OK, mDispatcher->start()); } - virtual void TearDown() override { + void TearDown() override { ASSERT_EQ(OK, mDispatcher->stop()); mFakePolicy.clear(); mDispatcher.clear(); @@ -1225,6 +1225,11 @@ public: return *this; } + MotionEventBuilder& addFlag(uint32_t flags) { + mFlags |= flags; + return *this; + } + MotionEvent build() { std::vector<PointerProperties> pointerProperties; std::vector<PointerCoords> pointerCoords; @@ -1244,7 +1249,7 @@ public: MotionEvent event; ui::Transform identityTransform; event.initialize(InputEvent::nextId(), DEVICE_ID, mSource, mDisplayId, INVALID_HMAC, - mAction, mActionButton, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, + mAction, mActionButton, mFlags, /* edgeFlags */ 0, AMETA_NONE, mButtonState, MotionClassification::NONE, identityTransform, /* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition, mRawYCursorPosition, mDisplayWidth, mDisplayHeight, mEventTime, mEventTime, @@ -1260,6 +1265,7 @@ private: int32_t mDisplayId{ADISPLAY_ID_DEFAULT}; int32_t mActionButton{0}; int32_t mButtonState{0}; + int32_t mFlags{0}; float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION}; float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION}; int32_t mDisplayWidth{AMOTION_EVENT_INVALID_DISPLAY_SIZE}; @@ -1708,7 +1714,13 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { 0 /*expectedFlags*/); } -TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) { +using TransferFunction = + std::function<bool(sp<InputDispatcher> dispatcher, sp<IBinder>, sp<IBinder>)>; + +class TransferTouchFixture : public InputDispatcherTest, + public ::testing::WithParamInterface<TransferFunction> {}; + +TEST_P(TransferTouchFixture, TransferTouch_OnePointer) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); // Create a couple of windows @@ -1729,8 +1741,10 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) { firstWindow->consumeMotionDown(); secondWindow->assertNoEvents(); - // Transfer touch focus to the second window - mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken()); + // Transfer touch to the second window + TransferFunction f = GetParam(); + const bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken()); + ASSERT_TRUE(success); // The first window gets cancel and the second gets down firstWindow->consumeMotionCancel(); secondWindow->consumeMotionDown(); @@ -1745,7 +1759,7 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) { secondWindow->consumeMotionUp(); } -TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) { +TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); PointF touchPoint = {10, 10}; @@ -1780,7 +1794,9 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) { secondWindow->assertNoEvents(); // Transfer touch focus to the second window - mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken()); + TransferFunction f = GetParam(); + bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken()); + ASSERT_TRUE(success); // The first window gets cancel and the second gets down and pointer down firstWindow->consumeMotionCancel(); secondWindow->consumeMotionDown(); @@ -1807,6 +1823,21 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) { secondWindow->consumeMotionUp(); } +// For the cases of single pointer touch and two pointers non-split touch, the api's +// 'transferTouch' and 'transferTouchFocus' are equivalent in behaviour. They only differ +// for the case where there are multiple pointers split across several windows. +INSTANTIATE_TEST_SUITE_P(TransferFunctionTests, TransferTouchFixture, + ::testing::Values( + [&](sp<InputDispatcher> dispatcher, sp<IBinder> /*ignored*/, + sp<IBinder> destChannelToken) { + return dispatcher->transferTouch(destChannelToken); + }, + [&](sp<InputDispatcher> dispatcher, sp<IBinder> from, + sp<IBinder> to) { + return dispatcher->transferTouchFocus(from, to, + false /*isDragAndDrop*/); + })); + TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); @@ -1877,6 +1908,82 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { secondWindow->consumeMotionUp(); } +// Same as TransferTouchFocus_TwoPointersSplitTouch, but using 'transferTouch' api. +// Unlike 'transferTouchFocus', calling 'transferTouch' when there are two windows receiving +// touch is not supported, so the touch should continue on those windows and the transferred-to +// window should get nothing. +TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + + // Create a non touch modal window that supports split touch + sp<FakeWindowHandle> firstWindow = + new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); + firstWindow->setFrame(Rect(0, 0, 600, 400)); + firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | + InputWindowInfo::Flag::SPLIT_TOUCH); + + // Create a non touch modal window that supports split touch + sp<FakeWindowHandle> secondWindow = + new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT); + secondWindow->setFrame(Rect(0, 400, 600, 800)); + secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | + InputWindowInfo::Flag::SPLIT_TOUCH); + + // Add the windows to the dispatcher + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); + + PointF pointInFirst = {300, 200}; + PointF pointInSecond = {300, 600}; + + // Send down to the first window + NotifyMotionArgs firstDownMotionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {pointInFirst}); + mDispatcher->notifyMotion(&firstDownMotionArgs); + // Only the first window should get the down event + firstWindow->consumeMotionDown(); + secondWindow->assertNoEvents(); + + // Send down to the second window + NotifyMotionArgs secondDownMotionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInFirst, pointInSecond}); + mDispatcher->notifyMotion(&secondDownMotionArgs); + // The first window gets a move and the second a down + firstWindow->consumeMotionMove(); + secondWindow->consumeMotionDown(); + + // Transfer touch focus to the second window + const bool transferred = mDispatcher->transferTouch(secondWindow->getToken()); + // The 'transferTouch' call should not succeed, because there are 2 touched windows + ASSERT_FALSE(transferred); + firstWindow->assertNoEvents(); + secondWindow->assertNoEvents(); + + // The rest of the dispatch should proceed as normal + // Send pointer up to the second window + NotifyMotionArgs pointerUpMotionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {pointInFirst, pointInSecond}); + mDispatcher->notifyMotion(&pointerUpMotionArgs); + // The first window gets MOVE and the second gets pointer up + firstWindow->consumeMotionMove(); + secondWindow->consumeMotionUp(); + + // Send up event to the first window + NotifyMotionArgs upMotionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&upMotionArgs); + // The first window gets nothing and the second gets up + firstWindow->consumeMotionUp(); + secondWindow->assertNoEvents(); +} + TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = @@ -3105,6 +3212,25 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_OnAlr mFakePolicy->assertOnPointerDownWasNotCalled(); } +// Have two windows, one with focus. Injecting a trusted DOWN MotionEvent with the flag +// NO_FOCUS_CHANGE on the unfocused window should not call the onPointerDownOutsideFocus callback. +TEST_F(InputDispatcherOnPointerDownOutsideFocus, NoFocusChangeFlag) { + const MotionEvent event = + MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(20).y(20)) + .addFlag(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, event)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mUnfocusedWindow->consumeAnyMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); + + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertOnPointerDownWasNotCalled(); + // Ensure that the unfocused window did not receive any FOCUS events. + mUnfocusedWindow->assertNoEvents(); +} + // These tests ensures we can send touch events to a single client when there are multiple input // windows that point to the same client token. class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest { diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 5eaca71c6a..7a11ca7396 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -2768,18 +2768,22 @@ protected: ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source; } - static void assertPointerCoords(const PointerCoords& coords, - float x, float y, float pressure, float size, - float touchMajor, float touchMinor, float toolMajor, float toolMinor, - float orientation, float distance) { - ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), 1); - ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), 1); + static void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure, + float size, float touchMajor, float touchMinor, float toolMajor, + float toolMinor, float orientation, float distance, + float scaledAxisEpsilon = 1.f) { + ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), scaledAxisEpsilon); + ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), scaledAxisEpsilon); ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON); ASSERT_NEAR(size, coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), EPSILON); - ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), 1); - ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), 1); - ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), 1); - ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), 1); + ASSERT_NEAR(touchMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + scaledAxisEpsilon); + ASSERT_NEAR(touchMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + scaledAxisEpsilon); + ASSERT_NEAR(toolMajor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + scaledAxisEpsilon); + ASSERT_NEAR(toolMinor, coords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + scaledAxisEpsilon); ASSERT_NEAR(orientation, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), EPSILON); ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON); } @@ -3822,6 +3826,12 @@ protected: setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, uniqueId, NO_PORT, viewportType); } + + static void assertCursorPointerCoords(const PointerCoords& coords, float x, float y, + float pressure) { + ASSERT_NO_FATAL_FAILURE(assertPointerCoords(coords, x, y, pressure, 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, EPSILON)); + } }; const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6; @@ -3836,10 +3846,10 @@ void CursorInputMapperTest::testMotionRotation(CursorInputMapper& mapper, int32_ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - float(rotatedX) / TRACKBALL_MOVEMENT_THRESHOLD, - float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD, - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(args.pointerCoords[0], + float(rotatedX) / TRACKBALL_MOVEMENT_THRESHOLD, + float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f)); } TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) { @@ -3929,8 +3939,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaStat ASSERT_EQ(uint32_t(1), args.pointerCount); ASSERT_EQ(0, args.pointerProperties[0].id); ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, args.pointerProperties[0].toolType); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f)); ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); ASSERT_EQ(ARBITRARY_TIME, args.downTime); @@ -3948,8 +3957,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaStat ASSERT_EQ(uint32_t(1), args.pointerCount); ASSERT_EQ(0, args.pointerProperties[0].id); ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, args.pointerProperties[0].toolType); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f)); ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); ASSERT_EQ(ARBITRARY_TIME, args.downTime); @@ -3970,8 +3978,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaStat ASSERT_EQ(uint32_t(1), args.pointerCount); ASSERT_EQ(0, args.pointerProperties[0].id); ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, args.pointerProperties[0].toolType); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); ASSERT_EQ(ARBITRARY_TIME, args.downTime); @@ -3989,8 +3996,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaStat ASSERT_EQ(uint32_t(1), args.pointerCount); ASSERT_EQ(0, args.pointerProperties[0].id); ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, args.pointerProperties[0].toolType); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); ASSERT_EQ(ARBITRARY_TIME, args.downTime); @@ -4007,16 +4013,17 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) { process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], + 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, + 0.0f)); // Motion in Y but not X. process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, -2); process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, + -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f)); } TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) { @@ -4030,26 +4037,22 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) { process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f)); // Button release. process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0); process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); } TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { @@ -4065,15 +4068,15 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, - 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], + 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, + -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, - 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], + 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, + -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f)); // Move X, Y a bit while pressed. process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 2); @@ -4081,22 +4084,20 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, - 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], + 2.0f / TRACKBALL_MOVEMENT_THRESHOLD, + 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f)); // Release Button. process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0); process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f)); } TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMotions) { @@ -4178,15 +4179,15 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState); ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState); ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 0); process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); @@ -4194,22 +4195,22 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); ASSERT_EQ(0, motionArgs.buttonState); ASSERT_EQ(0, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); ASSERT_EQ(0, motionArgs.buttonState); ASSERT_EQ(0, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); ASSERT_EQ(0, motionArgs.buttonState); ASSERT_EQ(0, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 1); @@ -4221,16 +4222,16 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { motionArgs.buttonState); ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); @@ -4238,8 +4239,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { motionArgs.buttonState); ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 0); process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); @@ -4247,15 +4248,15 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f)); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0); process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); @@ -4263,8 +4264,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); ASSERT_EQ(0, motionArgs.buttonState); ASSERT_EQ(0, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0); process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); @@ -4272,15 +4273,15 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { ASSERT_EQ(0, motionArgs.buttonState); ASSERT_EQ(0, mFakePointerController->getButtonState()); ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(0, motionArgs.buttonState); ASSERT_EQ(0, mFakePointerController->getButtonState()); ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); // press BTN_BACK, release BTN_BACK process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 1); @@ -4293,15 +4294,15 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 0); process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); @@ -4309,16 +4310,16 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); ASSERT_EQ(0, motionArgs.buttonState); ASSERT_EQ(0, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); ASSERT_EQ(0, motionArgs.buttonState); ASSERT_EQ(0, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); @@ -4334,15 +4335,15 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 0); process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); @@ -4350,15 +4351,15 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); ASSERT_EQ(0, motionArgs.buttonState); ASSERT_EQ(0, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); ASSERT_EQ(0, motionArgs.buttonState); ASSERT_EQ(0, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); @@ -4375,15 +4376,15 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 0); process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); @@ -4391,15 +4392,15 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); ASSERT_EQ(0, motionArgs.buttonState); ASSERT_EQ(0, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); ASSERT_EQ(0, motionArgs.buttonState); ASSERT_EQ(0, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); @@ -4416,15 +4417,15 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 0); process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); @@ -4432,15 +4433,15 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); ASSERT_EQ(0, motionArgs.buttonState); ASSERT_EQ(0, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); ASSERT_EQ(0, motionArgs.buttonState); ASSERT_EQ(0, mFakePointerController->getButtonState()); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - 100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); @@ -8743,20 +8744,20 @@ protected: } }; -TEST_F(LightControllerTest, SingleLight) { - RawLightInfo infoSingle = {.id = 1, - .name = "Mono", - .maxBrightness = 255, - .flags = InputLightClass::BRIGHTNESS, - .path = ""}; - mFakeEventHub->addRawLightInfo(infoSingle.id, std::move(infoSingle)); +TEST_F(LightControllerTest, MonoLight) { + RawLightInfo infoMono = {.id = 1, + .name = "Mono", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + mFakeEventHub->addRawLightInfo(infoMono.id, std::move(infoMono)); PeripheralController& controller = addControllerAndConfigure<PeripheralController>(); InputDeviceInfo info; controller.populateDeviceInfo(&info); const auto& ids = info.getLightIds(); ASSERT_EQ(1UL, ids.size()); - ASSERT_EQ(InputDeviceLightType::SINGLE, info.getLightInfo(ids[0])->type); + ASSERT_EQ(InputDeviceLightType::MONO, info.getLightInfo(ids[0])->type); ASSERT_TRUE(controller.setLightColor(ids[0], LIGHT_BRIGHTNESS)); ASSERT_EQ(controller.getLightColor(ids[0]).value_or(-1), LIGHT_BRIGHTNESS); diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp new file mode 100644 index 0000000000..e7e1937235 --- /dev/null +++ b/services/inputflinger/tests/LatencyTracker_test.cpp @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../dispatcher/LatencyTracker.h" + +#include <binder/Binder.h> +#include <gtest/gtest.h> +#include <inttypes.h> +#include <log/log.h> + +#define TAG "LatencyTracker_test" + +using android::inputdispatcher::InputEventTimeline; +using android::inputdispatcher::LatencyTracker; + +namespace android::inputdispatcher { + +InputEventTimeline getTestTimeline() { + InputEventTimeline t( + /*isDown*/ true, + /*eventTime*/ 2, + /*readTime*/ 3); + ConnectionTimeline expectedCT(/*deliveryTime*/ 6, /* consumeTime*/ 7, /*finishTime*/ 8); + std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; + graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 9; + graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 10; + expectedCT.setGraphicsTimeline(std::move(graphicsTimeline)); + t.connectionTimelines.emplace(new BBinder(), std::move(expectedCT)); + return t; +} + +// --- LatencyTrackerTest --- +class LatencyTrackerTest : public testing::Test, public InputEventTimelineProcessor { +protected: + std::unique_ptr<LatencyTracker> mTracker; + sp<IBinder> connection1; + sp<IBinder> connection2; + + void SetUp() override { + connection1 = new BBinder(); + connection2 = new BBinder(); + + mTracker = std::make_unique<LatencyTracker>(this); + } + void TearDown() override {} + + void assertReceivedTimeline(const InputEventTimeline& timeline); + /** + * Timelines can be received in any order (order is not guaranteed). So if we are expecting more + * than 1 timeline, use this function to check that the set of received timelines matches + * what we expected. + */ + void assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines); + +private: + void processTimeline(const InputEventTimeline& timeline) override { + mReceivedTimelines.push_back(timeline); + } + std::deque<InputEventTimeline> mReceivedTimelines; +}; + +void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) { + mTracker->reportNow(); + ASSERT_FALSE(mReceivedTimelines.empty()); + const InputEventTimeline& t = mReceivedTimelines.front(); + ASSERT_EQ(timeline, t); + mReceivedTimelines.pop_front(); +} + +/** + * We are essentially comparing two multisets, but without constructing them. + * This comparison is inefficient, but it avoids having to construct a set, and also avoids the + * declaration of copy constructor for ConnectionTimeline. + * We ensure that collections A and B have the same size, that for every element in A, there is an + * equal element in B, and for every element in B there is an equal element in A. + */ +void LatencyTrackerTest::assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines) { + mTracker->reportNow(); + ASSERT_EQ(timelines.size(), mReceivedTimelines.size()); + for (const InputEventTimeline& expectedTimeline : timelines) { + bool found = false; + for (const InputEventTimeline& receivedTimeline : mReceivedTimelines) { + if (receivedTimeline == expectedTimeline) { + found = true; + break; + } + } + ASSERT_TRUE(found) << "Could not find expected timeline with eventTime=" + << expectedTimeline.eventTime; + } + for (const InputEventTimeline& receivedTimeline : mReceivedTimelines) { + bool found = false; + for (const InputEventTimeline& expectedTimeline : timelines) { + if (receivedTimeline == expectedTimeline) { + found = true; + break; + } + } + ASSERT_TRUE(found) << "Could not find received timeline with eventTime=" + << receivedTimeline.eventTime; + } + mReceivedTimelines.clear(); +} + +/** + * Ensure that calling 'trackListener' in isolation only creates an inputflinger timeline, without + * any additional ConnectionTimeline's. + */ +TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) { + mTracker->trackListener(1 /*inputEventId*/, false /*isDown*/, 2 /*eventTime*/, 3 /*readTime*/); + assertReceivedTimeline(InputEventTimeline{false, 2, 3}); +} + +/** + * A single call to trackFinishedEvent should not cause a timeline to be reported. + */ +TEST_F(LatencyTrackerTest, TrackFinishedEvent_DoesNotTriggerReporting) { + mTracker->trackFinishedEvent(1 /*inputEventId*/, connection1, 2 /*deliveryTime*/, + 3 /*consumeTime*/, 4 /*finishTime*/); + assertReceivedTimelines({}); +} + +/** + * A single call to trackGraphicsLatency should not cause a timeline to be reported. + */ +TEST_F(LatencyTrackerTest, TrackGraphicsLatency_DoesNotTriggerReporting) { + std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; + graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2; + graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3; + mTracker->trackGraphicsLatency(1 /*inputEventId*/, connection2, graphicsTimeline); + assertReceivedTimelines({}); +} + +TEST_F(LatencyTrackerTest, TrackAllParameters_ReportsFullTimeline) { + constexpr int32_t inputEventId = 1; + InputEventTimeline expected = getTestTimeline(); + + const auto& [connectionToken, expectedCT] = *expected.connectionTimelines.begin(); + + mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime); + mTracker->trackFinishedEvent(inputEventId, connectionToken, expectedCT.deliveryTime, + expectedCT.consumeTime, expectedCT.finishTime); + mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline); + + assertReceivedTimeline(expected); +} + +TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) { + constexpr int32_t inputEventId1 = 1; + InputEventTimeline timeline1( + /*isDown*/ true, + /*eventTime*/ 2, + /*readTime*/ 3); + timeline1.connectionTimelines.emplace(connection1, + ConnectionTimeline(/*deliveryTime*/ 6, /*consumeTime*/ 7, + /*finishTime*/ 8)); + ConnectionTimeline& connectionTimeline1 = timeline1.connectionTimelines.begin()->second; + std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline1; + graphicsTimeline1[GraphicsTimeline::GPU_COMPLETED_TIME] = 9; + graphicsTimeline1[GraphicsTimeline::PRESENT_TIME] = 10; + connectionTimeline1.setGraphicsTimeline(std::move(graphicsTimeline1)); + + constexpr int32_t inputEventId2 = 10; + InputEventTimeline timeline2( + /*isDown*/ false, + /*eventTime*/ 20, + /*readTime*/ 30); + timeline2.connectionTimelines.emplace(connection2, + ConnectionTimeline(/*deliveryTime*/ 60, + /*consumeTime*/ 70, + /*finishTime*/ 80)); + ConnectionTimeline& connectionTimeline2 = timeline2.connectionTimelines.begin()->second; + std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline2; + graphicsTimeline2[GraphicsTimeline::GPU_COMPLETED_TIME] = 90; + graphicsTimeline2[GraphicsTimeline::PRESENT_TIME] = 100; + connectionTimeline2.setGraphicsTimeline(std::move(graphicsTimeline2)); + + // Start processing first event + mTracker->trackListener(inputEventId1, timeline1.isDown, timeline1.eventTime, + timeline1.readTime); + // Start processing second event + mTracker->trackListener(inputEventId2, timeline2.isDown, timeline2.eventTime, + timeline2.readTime); + mTracker->trackFinishedEvent(inputEventId1, connection1, connectionTimeline1.deliveryTime, + connectionTimeline1.consumeTime, connectionTimeline1.finishTime); + + mTracker->trackFinishedEvent(inputEventId2, connection2, connectionTimeline2.deliveryTime, + connectionTimeline2.consumeTime, connectionTimeline2.finishTime); + mTracker->trackGraphicsLatency(inputEventId1, connection1, + connectionTimeline1.graphicsTimeline); + mTracker->trackGraphicsLatency(inputEventId2, connection2, + connectionTimeline2.graphicsTimeline); + // Now both events should be completed + assertReceivedTimelines({timeline1, timeline2}); +} + +/** + * Check that LatencyTracker consistently tracks events even if there are many incomplete events. + */ +TEST_F(LatencyTrackerTest, IncompleteEvents_AreHandledConsistently) { + InputEventTimeline timeline = getTestTimeline(); + std::vector<InputEventTimeline> expectedTimelines; + const ConnectionTimeline& expectedCT = timeline.connectionTimelines.begin()->second; + const sp<IBinder>& token = timeline.connectionTimelines.begin()->first; + + for (size_t i = 1; i <= 100; i++) { + mTracker->trackListener(i /*inputEventId*/, timeline.isDown, timeline.eventTime, + timeline.readTime); + expectedTimelines.push_back( + InputEventTimeline{timeline.isDown, timeline.eventTime, timeline.readTime}); + } + // Now, complete the first event that was sent. + mTracker->trackFinishedEvent(1 /*inputEventId*/, token, expectedCT.deliveryTime, + expectedCT.consumeTime, expectedCT.finishTime); + mTracker->trackGraphicsLatency(1 /*inputEventId*/, token, expectedCT.graphicsTimeline); + + expectedTimelines[0].connectionTimelines.emplace(token, std::move(expectedCT)); + assertReceivedTimelines(expectedTimelines); +} + +/** + * For simplicity of the implementation, LatencyTracker only starts tracking an event when + * 'trackListener' is invoked. + * Both 'trackFinishedEvent' and 'trackGraphicsLatency' should not start a new event. + * If they are received before 'trackListener' (which should not be possible), they are ignored. + */ +TEST_F(LatencyTrackerTest, EventsAreTracked_WhenTrackListenerIsCalledFirst) { + constexpr int32_t inputEventId = 1; + InputEventTimeline expected = getTestTimeline(); + const ConnectionTimeline& expectedCT = expected.connectionTimelines.begin()->second; + mTracker->trackFinishedEvent(inputEventId, connection1, expectedCT.deliveryTime, + expectedCT.consumeTime, expectedCT.finishTime); + mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline); + + mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime); + assertReceivedTimeline( + InputEventTimeline{expected.isDown, expected.eventTime, expected.readTime}); +} + +} // namespace android::inputdispatcher diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 9955cdb080..f9491969e2 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -2138,7 +2138,12 @@ void SensorService::SensorPrivacyPolicy::registerSelf() { void SensorService::SensorPrivacyPolicy::unregisterSelf() { AutoCallerClear acc; SensorPrivacyManager spm; - spm.removeSensorPrivacyListener(this); + if (mIsIndividualMic) { + spm.removeIndividualSensorPrivacyListener( + SensorPrivacyManager::INDIVIDUAL_SENSOR_MICROPHONE, this); + } else { + spm.removeSensorPrivacyListener(this); + } } bool SensorService::SensorPrivacyPolicy::isSensorPrivacyEnabled() { diff --git a/services/stats/android.frameworks.stats@1.0-service.xml b/services/stats/android.frameworks.stats@1.0-service.xml index 5fd361c57d..c564b7be53 100644 --- a/services/stats/android.frameworks.stats@1.0-service.xml +++ b/services/stats/android.frameworks.stats@1.0-service.xml @@ -1,5 +1,5 @@ <manifest version="1.0" type="framework"> - <hal> + <hal max-level="5"> <name>android.frameworks.stats</name> <transport>hwbinder</transport> <version>1.0</version> diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 98853523f3..e669e4532b 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -25,7 +25,7 @@ cc_defaults { name: "libsurfaceflinger_defaults", defaults: [ "surfaceflinger_defaults", - "skia_deps", + "skia_renderengine_deps", ], cflags: [ "-DLOG_TAG=\"SurfaceFlinger\"", @@ -188,6 +188,7 @@ filegroup { "SurfaceInterceptor.cpp", "SurfaceTracing.cpp", "TransactionCallbackInvoker.cpp", + "TunnelModeEnabledReporter.cpp", ], } diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index 4c73b6edda..cacad52410 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -58,8 +58,7 @@ namespace android { -static constexpr float defaultMaxMasteringLuminance = 1000.0; -static constexpr float defaultMaxContentLuminance = 1000.0; +static constexpr float defaultMaxLuminance = 1000.0; BufferLayer::BufferLayer(const LayerCreationArgs& args) : Layer(args), @@ -206,12 +205,24 @@ std::optional<compositionengine::LayerFE::LayerSettings> BufferLayer::prepareCli layer.source.buffer.isY410BT2020 = isHdrY410(); bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086; bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3; - layer.source.buffer.maxMasteringLuminance = hasSmpte2086 - ? mBufferInfo.mHdrMetadata.smpte2086.maxLuminance - : defaultMaxMasteringLuminance; - layer.source.buffer.maxContentLuminance = hasCta861_3 - ? mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel - : defaultMaxContentLuminance; + float maxLuminance = 0.f; + if (hasSmpte2086 && hasCta861_3) { + maxLuminance = std::min(mBufferInfo.mHdrMetadata.smpte2086.maxLuminance, + mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel); + } else if (hasSmpte2086) { + maxLuminance = mBufferInfo.mHdrMetadata.smpte2086.maxLuminance; + } else if (hasCta861_3) { + maxLuminance = mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel; + } else { + switch (layer.sourceDataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_ST2084: + case HAL_DATASPACE_TRANSFER_HLG: + // Behavior-match previous releases for HDR content + maxLuminance = defaultMaxLuminance; + break; + } + } + layer.source.buffer.maxLuminanceNits = maxLuminance; layer.frameNumber = mCurrentFrameNumber; layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer()->getId() : 0; diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 24b35997b9..54daa102ba 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -295,12 +295,72 @@ bool BufferStateLayer::setBufferCrop(const Rect& bufferCrop) { return true; } +bool BufferStateLayer::setDestinationFrame(const Rect& destinationFrame) { + if (mCurrentState.destinationFrame == destinationFrame) return false; + + mCurrentState.sequence++; + mCurrentState.destinationFrame = destinationFrame; + + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +// Translate destination frame into scale and position. If a destination frame is not set, use the +// provided scale and position +void BufferStateLayer::updateGeometry() { + if (mCurrentState.destinationFrame.isEmpty()) { + // If destination frame is not set, use the requested transform set via + // BufferStateLayer::setPosition and BufferStateLayer::setMatrix. + mCurrentState.transform = mRequestedTransform; + return; + } + + Rect destRect = mCurrentState.destinationFrame; + int32_t destW = destRect.width(); + int32_t destH = destRect.height(); + if (destRect.left < 0) { + destRect.left = 0; + destRect.right = destW; + } + if (destRect.top < 0) { + destRect.top = 0; + destRect.bottom = destH; + } + + if (!mCurrentState.buffer) { + ui::Transform t; + t.set(destRect.left, destRect.top); + mCurrentState.transform = t; + return; + } + + uint32_t bufferWidth = mCurrentState.buffer->getBuffer()->getWidth(); + uint32_t bufferHeight = mCurrentState.buffer->getBuffer()->getHeight(); + // Undo any transformations on the buffer. + if (mCurrentState.bufferTransform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); + if (mCurrentState.transformToDisplayInverse) { + if (invTransform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + } + + float sx = destW / static_cast<float>(bufferWidth); + float sy = destH / static_cast<float>(bufferHeight); + ui::Transform t; + t.set(sx, 0, 0, sy); + t.set(destRect.left, destRect.top); + mCurrentState.transform = t; + return; +} + bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix, bool allowNonRectPreservingTransforms) { - if (mCurrentState.transform.dsdx() == matrix.dsdx && - mCurrentState.transform.dtdy() == matrix.dtdy && - mCurrentState.transform.dtdx() == matrix.dtdx && - mCurrentState.transform.dsdy() == matrix.dsdy) { + if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy && + mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) { return false; } @@ -313,7 +373,7 @@ bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix, return false; } - mCurrentState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); + mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); mCurrentState.sequence++; mCurrentState.modified = true; @@ -323,11 +383,11 @@ bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix, } bool BufferStateLayer::setPosition(float x, float y) { - if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) { + if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) { return false; } - mCurrentState.transform.set(x, y); + mRequestedTransform.set(x, y); mCurrentState.sequence++; mCurrentState.modified = true; @@ -523,35 +583,35 @@ bool BufferStateLayer::setTransparentRegionHint(const Region& transparent) { Rect BufferStateLayer::getBufferSize(const State& s) const { // for buffer state layers we use the display frame size as the buffer size. - if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) { - return Rect(getActiveWidth(s), getActiveHeight(s)); - } if (mBufferInfo.mBuffer == nullptr) { return Rect::INVALID_RECT; } - // if the display frame is not defined, use the parent bounds as the buffer size. - const auto& p = mDrawingParent.promote(); - if (p != nullptr) { - Rect parentBounds = Rect(p->getBounds(Region())); - if (!parentBounds.isEmpty()) { - return parentBounds; + uint32_t bufWidth = mBufferInfo.mBuffer->getBuffer()->getWidth(); + uint32_t bufHeight = mBufferInfo.mBuffer->getBuffer()->getHeight(); + + // Undo any transformations on the buffer and return the result. + if (mBufferInfo.mTransform & ui::Transform::ROT_90) { + std::swap(bufWidth, bufHeight); + } + + if (getTransformToDisplayInverse()) { + uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); + if (invTransform & ui::Transform::ROT_90) { + std::swap(bufWidth, bufHeight); } } - return Rect::INVALID_RECT; + return Rect(0, 0, bufWidth, bufHeight); } FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) const { - const State& s(getDrawingState()); - // for buffer state layers we use the display frame size as the buffer size. - if (getActiveWidth(s) < UINT32_MAX && getActiveHeight(s) < UINT32_MAX) { - return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s)); + if (mBufferInfo.mBuffer == nullptr) { + return parentBounds; } - // if the display frame is not defined, use the parent bounds as the buffer size. - return parentBounds; + return getBufferSize(getDrawingState()).toFloatRect(); } // ----------------------------------------------------------------------- @@ -622,7 +682,10 @@ void BufferStateLayer::setAutoRefresh(bool autoRefresh) { } bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) { - if (mSidebandStreamChanged.exchange(false)) { + // We need to update the sideband stream if the layer has both a buffer and a sideband stream. + const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get(); + + if (mSidebandStreamChanged.exchange(false) || updateSidebandStream) { const State& s(getDrawingState()); // mSidebandStreamChanged was true mSidebandStream = s.sidebandStream; diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index 570a41afc3..2e484524a1 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -51,9 +51,6 @@ public: return flags; } - uint32_t getActiveWidth(const Layer::State& s) const override { return s.width; } - uint32_t getActiveHeight(const Layer::State& s) const override { return s.height; } - ui::Transform getActiveTransform(const Layer::State& s) const override { return s.transform; } Region getActiveTransparentRegion(const Layer::State& s) const override { return s.transparentRegionHint; } @@ -89,6 +86,8 @@ public: void setAutoRefresh(bool autoRefresh) override; bool setBufferCrop(const Rect& bufferCrop) override; + bool setDestinationFrame(const Rect& destinationFrame) override; + void updateGeometry() override; // ----------------------------------------------------------------------- @@ -181,6 +180,10 @@ private: // - If the integer decreases in setBuffer or doTransaction, a buffer was dropped std::atomic<int32_t> mPendingBufferTransactions{0}; + // Contains requested position and matrix updates. This will be applied if the client does + // not specify a destination frame. + ui::Transform mRequestedTransform; + // TODO(marissaw): support sticky transform for LEGACY camera mode class HwcSlotGenerator : public ClientCache::ErasedRecipient { diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index 497621397c..257974f4e9 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -163,6 +163,9 @@ public: // Enables (or disables) composition on this output virtual void setCompositionEnabled(bool) = 0; + // Enables (or disables) layer caching on this output + virtual void setLayerCachingEnabled(bool) = 0; + // Sets the projection state to use virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect, const Rect& orientedDisplaySpaceRect) = 0; @@ -181,6 +184,9 @@ public: // Sets the output color mode virtual void setColorProfile(const ColorProfile&) = 0; + // Sets current calibrated display brightness information + virtual void setDisplayBrightness(float sdrWhitePointNits, float displayBrightnessNits) = 0; + // Outputs a string with a state dump virtual void dump(std::string&) const = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index eeb20fc1d1..f10ff2598d 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -20,6 +20,7 @@ #include <compositionengine/Output.h> #include <compositionengine/impl/ClientCompositionRequestCache.h> #include <compositionengine/impl/OutputCompositionState.h> +#include <compositionengine/impl/planner/Planner.h> #include <renderengine/DisplaySettings.h> #include <renderengine/LayerSettings.h> #include <memory> @@ -28,21 +29,18 @@ namespace android::compositionengine::impl { -namespace planner { -class Planner; -} // namespace planner - // The implementation class contains the common implementation, but does not // actually contain the final output state. class Output : public virtual compositionengine::Output { public: - Output(); + Output() = default; ~Output() override; // compositionengine::Output overrides bool isValid() const override; std::optional<DisplayId> getDisplayId() const override; void setCompositionEnabled(bool) override; + void setLayerCachingEnabled(bool) override; void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect, const Rect& orientedDisplaySpaceRect) override; void setDisplaySize(const ui::Size&) override; @@ -51,6 +49,7 @@ public: void setColorTransform(const compositionengine::CompositionRefreshArgs&) override; void setColorProfile(const ColorProfile&) override; + void setDisplayBrightness(float sdrWhitePointNits, float displayBrightnessNits) override; void dump(std::string&) const override; void dumpPlannerInfo(const Vector<String16>& args, std::string&) const override; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h index f0ef6d6e7d..d41c2dd527 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h @@ -118,6 +118,12 @@ struct OutputCompositionState { // The earliest time to send the present command to the HAL std::chrono::steady_clock::time_point earliestPresentTime; + // Current display brightness + float displayBrightnessNits{-1.f}; + + // SDR white point + float sdrWhitePointNits{-1.f}; + // Debugging void dump(std::string& result) const; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h index 2488c66b6d..2ffd47293c 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h @@ -78,9 +78,10 @@ private: void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&); void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&); void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition, - bool isPeekingThrough); + bool isPeekingThrough, bool skipLayer); void detectDisallowedCompositionTypeChange(Hwc2::IComposerClient::Composition from, Hwc2::IComposerClient::Composition to) const; + bool isClientCompositionForced(bool isPeekingThrough) const; }; // This template factory function standardizes the implementation details of the diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h index b98043b92e..3f670a1b32 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h @@ -125,6 +125,9 @@ struct OutputLayerCompositionState { // Set to true when overridden info has been sent to HW composer bool stateOverridden = false; + + // True when this layer was skipped as part of SF-side layer caching. + bool layerSkipped = false; }; // The HWC state is optional, and is only set up if there is any potential diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h index 06f26eb8d1..fdcd6abe65 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h @@ -78,6 +78,7 @@ public: size_t getDisplayCost() const; bool hasBufferUpdate() const; + bool hasRenderedBuffer() const { return mTexture != nullptr; } bool hasReadyBuffer() const; // Decomposes this CachedSet into a vector of its layers as individual CachedSets @@ -110,6 +111,9 @@ public: // CachedSet and punching a hole. bool requiresHolePunch() const; + // True if any constituent layer is configured to blur any layers behind. + bool hasBlurBehind() const; + // Add a layer that will be drawn behind this one. ::render() will render a // hole in this CachedSet's buffer, allowing the supplied layer to peek // through. Must be called before ::render(). diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h index b09f1d1a09..213c55e0ce 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h @@ -20,6 +20,7 @@ #include <compositionengine/impl/planner/CachedSet.h> #include <compositionengine/impl/planner/LayerState.h> +#include <numeric> #include <vector> namespace android { @@ -36,8 +37,7 @@ class Predictor; class Flattener { public: - Flattener(Predictor& predictor, bool enableHolePunch = false) - : mEnableHolePunch(enableHolePunch), mPredictor(predictor) {} + Flattener(bool enableHolePunch = false) : mEnableHolePunch(enableHolePunch) {} void setDisplaySize(ui::Size size) { mDisplaySize = size; } @@ -61,10 +61,76 @@ private: bool mergeWithCachedSets(const std::vector<const LayerState*>& layers, std::chrono::steady_clock::time_point now); + // A Run is a sequence of CachedSets, which is a candidate for flattening into a single + // CachedSet. Because it is wasteful to flatten 1 CachedSet, a Run must contain more than 1 + // CachedSet + class Run { + public: + // A builder for a Run, to aid in construction + class Builder { + private: + std::vector<CachedSet>::const_iterator mStart; + std::vector<size_t> mLengths; + const CachedSet* mHolePunchCandidate = nullptr; + + public: + // Initializes a Builder a CachedSet to start from. + // This start iterator must be an iterator for mLayers + void init(const std::vector<CachedSet>::const_iterator& start) { + mStart = start; + mLengths.push_back(start->getLayerCount()); + } + + // Appends a new CachedSet to the end of the run + // The provided length must be the size of the next sequential CachedSet in layers + void append(size_t length) { mLengths.push_back(length); } + + // Sets the hole punch candidate for the Run. + void setHolePunchCandidate(const CachedSet* holePunchCandidate) { + mHolePunchCandidate = holePunchCandidate; + } + + // Builds a Run instance, if a valid Run may be built. + std::optional<Run> validateAndBuild() { + if (mLengths.size() <= 1) { + return std::nullopt; + } + + return Run(mStart, + std::reduce(mLengths.cbegin(), mLengths.cend(), 0u, + [](size_t left, size_t right) { return left + right; }), + mHolePunchCandidate); + } + + void reset() { *this = {}; } + }; + + // Gets the starting CachedSet of this run. + // This is an iterator into mLayers + const std::vector<CachedSet>::const_iterator& getStart() const { return mStart; } + // Gets the total number of layers encompassing this Run. + size_t getLayerLength() const { return mLength; } + // Gets the hole punch candidate for this Run. + const CachedSet* getHolePunchCandidate() const { return mHolePunchCandidate; } + + private: + Run(std::vector<CachedSet>::const_iterator start, size_t length, + const CachedSet* holePunchCandidate) + : mStart(start), mLength(length), mHolePunchCandidate(holePunchCandidate) {} + const std::vector<CachedSet>::const_iterator mStart; + const size_t mLength; + const CachedSet* const mHolePunchCandidate; + + friend class Builder; + }; + + std::vector<Run> findCandidateRuns(std::chrono::steady_clock::time_point now) const; + + std::optional<Run> findBestRun(std::vector<Run>& runs) const; + void buildCachedSets(std::chrono::steady_clock::time_point now); const bool mEnableHolePunch; - Predictor& mPredictor; ui::Size mDisplaySize; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h index 3391273679..fef0dfb700 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h @@ -26,6 +26,7 @@ #include <string> #include "DisplayHardware/Hal.h" +#include "math/HashCombine.h" namespace std { template <typename T> @@ -48,24 +49,26 @@ using LayerId = int32_t; // clang-format off enum class LayerStateField : uint32_t { - None = 0u, - Id = 1u << 0, - Name = 1u << 1, - DisplayFrame = 1u << 2, - SourceCrop = 1u << 3, - BufferTransform = 1u << 4, - BlendMode = 1u << 5, - Alpha = 1u << 6, - LayerMetadata = 1u << 7, - VisibleRegion = 1u << 8, - Dataspace = 1u << 9, - PixelFormat = 1u << 10, - ColorTransform = 1u << 11, - SurfaceDamage = 1u << 12, - CompositionType = 1u << 13, - SidebandStream = 1u << 14, - Buffer = 1u << 15, - SolidColor = 1u << 16, + None = 0u, + Id = 1u << 0, + Name = 1u << 1, + DisplayFrame = 1u << 2, + SourceCrop = 1u << 3, + BufferTransform = 1u << 4, + BlendMode = 1u << 5, + Alpha = 1u << 6, + LayerMetadata = 1u << 7, + VisibleRegion = 1u << 8, + Dataspace = 1u << 9, + PixelFormat = 1u << 10, + ColorTransform = 1u << 11, + SurfaceDamage = 1u << 12, + CompositionType = 1u << 13, + SidebandStream = 1u << 14, + Buffer = 1u << 15, + SolidColor = 1u << 16, + BackgroundBlurRadius = 1u << 17, + BlurRegions = 1u << 18, }; // clang-format on @@ -225,6 +228,9 @@ public: const std::string& getName() const { return mName.get(); } Rect getDisplayFrame() const { return mDisplayFrame.get(); } const Region& getVisibleRegion() const { return mVisibleRegion.get(); } + bool hasBlurBehind() const { + return mBackgroundBlurRadius.get() > 0 || !mBlurRegions.get().empty(); + } hardware::graphics::composer::hal::Composition getCompositionType() const { return mCompositionType.get(); } @@ -398,7 +404,45 @@ private: return std::vector<std::string>{stream.str()}; }}; - static const constexpr size_t kNumNonUniqueFields = 14; + OutputLayerState<int32_t, LayerStateField::BackgroundBlurRadius> mBackgroundBlurRadius{ + [](auto layer) { + return layer->getLayerFE().getCompositionState()->backgroundBlurRadius; + }}; + + using BlurRegionsState = + OutputLayerState<std::vector<BlurRegion>, LayerStateField::BlurRegions>; + BlurRegionsState mBlurRegions{[](auto layer) { + return layer->getLayerFE().getCompositionState()->blurRegions; + }, + [](const std::vector<BlurRegion>& regions) { + std::vector<std::string> result; + for (const auto region : regions) { + std::string str; + base::StringAppendF(&str, + "{radius=%du, cornerRadii=[%f, %f, " + "%f, %f], alpha=%f, rect=[%d, " + "%d, %d, %d]", + region.blurRadius, + region.cornerRadiusTL, + region.cornerRadiusTR, + region.cornerRadiusBL, + region.cornerRadiusBR, region.alpha, + region.left, region.top, region.right, + region.bottom); + result.push_back(str); + } + return result; + }, + BlurRegionsState::getDefaultEquals(), + [](const std::vector<BlurRegion>& regions) { + size_t hash = 0; + for (const auto& region : regions) { + android::hashCombineSingle(hash, region); + } + return hash; + }}; + + static const constexpr size_t kNumNonUniqueFields = 16; std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() { std::array<const StateInterface*, kNumNonUniqueFields> constFields = @@ -413,10 +457,10 @@ private: std::array<const StateInterface*, kNumNonUniqueFields> getNonUniqueFields() const { return { - &mDisplayFrame, &mSourceCrop, &mBufferTransform, &mBlendMode, - &mAlpha, &mLayerMetadata, &mVisibleRegion, &mOutputDataspace, - &mPixelFormat, &mColorTransform, &mCompositionType, &mSidebandStream, - &mBuffer, &mSolidColor, + &mDisplayFrame, &mSourceCrop, &mBufferTransform, &mBlendMode, + &mAlpha, &mLayerMetadata, &mVisibleRegion, &mOutputDataspace, + &mPixelFormat, &mColorTransform, &mCompositionType, &mSidebandStream, + &mBuffer, &mSolidColor, &mBackgroundBlurRadius, &mBlurRegions, }; } }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h index c2037a899e..4365b93bb9 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h @@ -76,6 +76,8 @@ private: std::optional<Predictor::PredictedPlan> mPredictedPlan; NonBufferHash mFlattenedHash = 0; + + bool mPredictorEnabled = false; }; } // namespace compositionengine::impl::planner diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h index 5aa53e54dd..4b4d37539e 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -36,6 +36,7 @@ public: MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>()); MOCK_METHOD1(setCompositionEnabled, void(bool)); + MOCK_METHOD1(setLayerCachingEnabled, void(bool)); MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&)); MOCK_METHOD1(setDisplaySize, void(const ui::Size&)); MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool)); @@ -43,6 +44,7 @@ public: MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&)); MOCK_METHOD1(setColorProfile, void(const ColorProfile&)); + MOCK_METHOD2(setDisplayBrightness, void(float, float)); MOCK_CONST_METHOD1(dump, void(std::string&)); MOCK_CONST_METHOD2(dumpPlannerInfo, void(const Vector<String16>&, std::string&)); diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp index 2d9f01b9fd..7e020ee1c1 100644 --- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp +++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp @@ -36,7 +36,8 @@ inline bool equalIgnoringSource(const renderengine::LayerSettings& lhs, lhs.sourceDataspace == rhs.sourceDataspace && lhs.colorTransform == rhs.colorTransform && lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow && - lhs.backgroundBlurRadius == rhs.backgroundBlurRadius; + lhs.backgroundBlurRadius == rhs.backgroundBlurRadius && + lhs.stretchEffect == rhs.stretchEffect; } inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) { @@ -45,8 +46,7 @@ inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const rendereng lhs.textureTransform == rhs.textureTransform && lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha && lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 && - lhs.maxMasteringLuminance == rhs.maxMasteringLuminance && - lhs.maxContentLuminance == rhs.maxContentLuminance; + lhs.maxLuminanceNits == rhs.maxLuminanceNits; } inline bool equalIgnoringBuffer(const renderengine::LayerSettings& lhs, diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index 1ffb1c8e2d..953eb76b63 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -192,16 +192,7 @@ std::unique_ptr<compositionengine::OutputLayer> Display::createOutputLayer( if (const auto halDisplayId = HalDisplayId::tryCast(mId); outputLayer && !mIsDisconnected && halDisplayId) { auto& hwc = getCompositionEngine().getHwComposer(); - // Note: For the moment we ensure it is safe to take a reference to the - // HWComposer implementation by destroying all the OutputLayers (and - // hence the HWC2::Layers they own) before setting a new HWComposer. See - // for example SurfaceFlinger::updateVrFlinger(). - // TODO(b/121291683): Make this safer. - auto hwcLayer = - std::shared_ptr<HWC2::Layer>(hwc.createLayer(*halDisplayId), - [&hwc, id = *halDisplayId](HWC2::Layer* layer) { - hwc.destroyLayer(id, layer); - }); + auto hwcLayer = hwc.createLayer(*halDisplayId); ALOGE_IF(!hwcLayer, "Failed to create a HWC layer for a HWC supported display %s", getName().c_str()); outputLayer->setHwcLayer(std::move(hwcLayer)); @@ -267,7 +258,7 @@ void Display::chooseCompositionStrategy() { auto& hwc = getCompositionEngine().getHwComposer(); if (status_t result = hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(), - &changes); + getState().earliestPresentTime, &changes); result != NO_ERROR) { ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result, strerror(-result)); @@ -367,13 +358,8 @@ compositionengine::Output::FrameFences Display::presentAndGetFrameFences() { return fences; } - { - ATRACE_NAME("wait for earliest present time"); - std::this_thread::sleep_until(getState().earliestPresentTime); - } - auto& hwc = getCompositionEngine().getHwComposer(); - hwc.presentAndGetReleaseFences(*halDisplayIdOpt); + hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime); fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt); diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp index 0cc2c6e637..5565396922 100644 --- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp +++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp @@ -101,9 +101,9 @@ void dumpVal(std::string& out, const char* name, const mat4& tr) { } void dumpVal(std::string& out, const char* name, const StretchEffect& effect) { - StringAppendF(&out, "%s={ area=[%f, %f, %f, %f], vec=(%f, %f), max=%f } ", name, - effect.area.left, effect.area.top, effect.area.right, effect.area.bottom, - effect.vectorX, effect.vectorY, effect.maxAmount); + StringAppendF(&out, "%s={ width =%f, height = %f, vec=(%f, %f), max=(%f, %f) } ", name, + effect.width, effect.height, + effect.vectorX, effect.vectorY, effect.maxAmountX, effect.maxAmountY); } } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 297e687fcb..088a400877 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -55,18 +55,6 @@ Output::~Output() = default; namespace impl { -Output::Output() { - const bool enableLayerCaching = [] { - const bool enable = - android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false); - return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable); - }(); - - if (enableLayerCaching) { - mPlanner = std::make_unique<planner::Planner>(); - } -} - namespace { template <typename T> @@ -135,6 +123,29 @@ void Output::setCompositionEnabled(bool enabled) { dirtyEntireOutput(); } +void Output::setLayerCachingEnabled(bool enabled) { + if (enabled == (mPlanner != nullptr)) { + return; + } + + if (enabled) { + mPlanner = std::make_unique<planner::Planner>(); + if (mRenderSurface) { + mPlanner->setDisplaySize(mRenderSurface->getSize()); + } + } else { + mPlanner.reset(); + } + + for (auto* outputLayer : getOutputLayersOrderedByZ()) { + if (!outputLayer) { + continue; + } + + outputLayer->editState().overrideInfo = {}; + } +} + void Output::setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect, const Rect& orientedDisplaySpaceRect) { auto& outputState = editState(); @@ -256,6 +267,18 @@ void Output::setColorProfile(const ColorProfile& colorProfile) { dirtyEntireOutput(); } +void Output::setDisplayBrightness(float sdrWhitePointNits, float displayBrightnessNits) { + auto& outputState = editState(); + if (outputState.sdrWhitePointNits == sdrWhitePointNits && + outputState.displayBrightnessNits == displayBrightnessNits) { + // Nothing changed + return; + } + outputState.sdrWhitePointNits = sdrWhitePointNits; + outputState.displayBrightnessNits = displayBrightnessNits; + dirtyEntireOutput(); +} + void Output::dump(std::string& out) const { using android::base::StringAppendF; @@ -1032,8 +1055,13 @@ std::optional<base::unique_fd> Output::composeSurfaces( clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut() ? outputState.dataspace : ui::Dataspace::UNKNOWN; - clientCompositionDisplay.maxLuminance = - mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance(); + + // If we have a valid current display brightness use that, otherwise fall back to the + // display's max desired + clientCompositionDisplay.maxLuminance = outputState.displayBrightnessNits > 0.f + ? outputState.displayBrightnessNits + : mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance(); + clientCompositionDisplay.sdrWhitePointNits = outputState.sdrWhitePointNits; // Compute the global color transform matrix. if (!outputState.usesDeviceComposition && !getSkipColorTransform()) { diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 01da070347..cd143272e9 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <DisplayHardware/Hal.h> #include <android-base/stringprintf.h> #include <compositionengine/DisplayColorProfile.h> #include <compositionengine/LayerFECompositionState.h> @@ -350,12 +351,14 @@ void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t writeOutputDependentPerFrameStateToHWC(hwcLayer.get()); writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState); - writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough); + writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough, + skipLayer); // Always set the layer color after setting the composition type. writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState); editState().hwc->stateOverridden = isOverridden; + editState().hwc->layerSkipped = skipLayer; } void OutputLayer::writeOutputDependentGeometryStateToHWC(HWC2::Layer* hwcLayer, @@ -482,7 +485,8 @@ void OutputLayer::writeOutputIndependentPerFrameStateToHWC( const Region& surfaceDamage = getState().overrideInfo.buffer ? getState().overrideInfo.damageRegion - : outputIndependentState.surfaceDamage; + : (getState().hwc->stateOverridden ? Region::INVALID_REGION + : outputIndependentState.surfaceDamage); if (auto error = hwcLayer->setSurfaceDamage(surfaceDamage); error != hal::Error::NONE) { ALOGE("[%s] Failed to set surface damage: %s (%d)", getLayerFE().getDebugName(), @@ -573,17 +577,19 @@ void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer, void OutputLayer::writeCompositionTypeToHWC(HWC2::Layer* hwcLayer, hal::Composition requestedCompositionType, - bool isPeekingThrough) { + bool isPeekingThrough, bool skipLayer) { auto& outputDependentState = editState(); - // If we are forcing client composition, we need to tell the HWC - if (outputDependentState.forceClientComposition || - (!isPeekingThrough && getLayerFE().hasRoundedCorners())) { + if (isClientCompositionForced(isPeekingThrough)) { + // If we are forcing client composition, we need to tell the HWC requestedCompositionType = hal::Composition::CLIENT; } // Set the requested composition type with the HWC whenever it changes - if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType) { + // We also resend the composition type when this layer was previously skipped, to ensure that + // the composition type is up-to-date. + if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType || + (outputDependentState.hwc->layerSkipped && !skipLayer)) { outputDependentState.hwc->hwcCompositionType = requestedCompositionType; if (auto error = hwcLayer->setCompositionType(requestedCompositionType); @@ -663,12 +669,22 @@ void OutputLayer::detectDisallowedCompositionTypeChange(hal::Composition from, } } +bool OutputLayer::isClientCompositionForced(bool isPeekingThrough) const { + return getState().forceClientComposition || + (!isPeekingThrough && getLayerFE().hasRoundedCorners()); +} + void OutputLayer::applyDeviceCompositionTypeChange(hal::Composition compositionType) { auto& state = editState(); LOG_FATAL_IF(!state.hwc); auto& hwcState = *state.hwc; - detectDisallowedCompositionTypeChange(hwcState.hwcCompositionType, compositionType); + // Only detected disallowed changes if this was not a skip layer, because the + // validated composition type may be arbitrary (usually DEVICE, to reflect that there were + // fewer GPU layers) + if (!hwcState.layerSkipped) { + detectDisallowedCompositionTypeChange(hwcState.hwcCompositionType, compositionType); + } hwcState.hwcCompositionType = compositionType; } diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp index 67854cf31f..b61daeb7cd 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp @@ -17,6 +17,7 @@ #undef LOG_TAG #define LOG_TAG "Planner" // #define LOG_NDEBUG 0 +#define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <android-base/properties.h> #include <compositionengine/impl/OutputCompositionState.h> @@ -25,6 +26,8 @@ #include <renderengine/DisplaySettings.h> #include <renderengine/RenderEngine.h> +#include <utils/Trace.h> + namespace android::compositionengine::impl::planner { const bool CachedSet::sDebugHighlighLayers = @@ -154,6 +157,7 @@ void CachedSet::updateAge(std::chrono::steady_clock::time_point now) { void CachedSet::render(renderengine::RenderEngine& renderEngine, const OutputCompositionState& outputState) { + ATRACE_CALL(); const Rect& viewport = outputState.layerStackSpace.content; const ui::Dataspace& outputDataspace = outputState.dataspace; const ui::Transform::RotationFlags orientation = @@ -280,6 +284,11 @@ bool CachedSet::requiresHolePunch() const { return layerFE.hasRoundedCorners(); } +bool CachedSet::hasBlurBehind() const { + return std::any_of(mLayers.cbegin(), mLayers.cend(), + [](const Layer& layer) { return layer.getState()->hasBlurBehind(); }); +} + namespace { bool contains(const Rect& outer, const Rect& inner) { return outer.left <= inner.left && outer.right >= inner.right && outer.top <= inner.top && diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index a63f21f41a..2def99d649 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -17,10 +17,12 @@ #undef LOG_TAG #define LOG_TAG "Planner" // #define LOG_NDEBUG 0 +#define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <compositionengine/impl/planner/Flattener.h> #include <compositionengine/impl/planner/LayerState.h> -#include <compositionengine/impl/planner/Predictor.h> + +#include <utils/Trace.h> using time_point = std::chrono::steady_clock::time_point; using namespace std::chrono_literals; @@ -59,6 +61,7 @@ bool isSameStack(const std::vector<const LayerState*>& incomingLayers, NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& layers, NonBufferHash hash, time_point now) { + ATRACE_CALL(); const size_t unflattenedDisplayCost = calculateDisplayCost(layers); mUnflattenedDisplayCost += unflattenedDisplayCost; @@ -92,7 +95,8 @@ NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& lay void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine, const OutputCompositionState& outputState) { - if (!mNewCachedSet) { + ATRACE_CALL(); + if (!mNewCachedSet || mNewCachedSet->hasRenderedBuffer()) { return; } @@ -126,9 +130,11 @@ void Flattener::dump(std::string& result) const { return left.first < right.first; }; - const size_t maxLayerCount = std::max_element(mInitialLayerCounts.cbegin(), - mInitialLayerCounts.cend(), compareLayerCounts) - ->first; + const size_t maxLayerCount = mInitialLayerCounts.empty() + ? 0u + : std::max_element(mInitialLayerCounts.cbegin(), mInitialLayerCounts.cend(), + compareLayerCounts) + ->first; result.append("\n Initial counts:\n"); for (size_t count = 1; count < maxLayerCount; ++count) { @@ -212,6 +218,7 @@ NonBufferHash Flattener::computeLayersHash() const{ // was already populated with these layers, i.e. on the second and following // calls with the same geometry. bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers, time_point now) { + ATRACE_CALL(); std::vector<CachedSet> merged; if (mLayers.empty()) { @@ -326,89 +333,105 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers return true; } -void Flattener::buildCachedSets(time_point now) { - struct Run { - Run(std::vector<CachedSet>::const_iterator start, size_t length) - : start(start), length(length) {} - - std::vector<CachedSet>::const_iterator start; - size_t length; - }; - - if (mLayers.empty()) { - ALOGV("[%s] No layers found, returning", __func__); - return; - } - +std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const { + ATRACE_CALL(); std::vector<Run> runs; bool isPartOfRun = false; - - // Keep track of the layer that follows a run. It's possible that we will - // render it with a hole-punch. - const CachedSet* holePunchLayer = nullptr; + Run::Builder builder; + bool firstLayer = true; + bool runHasFirstLayer = false; for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) { - if (now - currentSet->getLastUpdate() > kActiveLayerTimeout) { - // Layer is inactive + const bool layerIsInactive = now - currentSet->getLastUpdate() > kActiveLayerTimeout; + const bool layerHasBlur = currentSet->hasBlurBehind(); + if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur)) { if (isPartOfRun) { - runs.back().length += currentSet->getLayerCount(); + builder.append(currentSet->getLayerCount()); } else { // Runs can't start with a non-buffer layer if (currentSet->getFirstLayer().getBuffer() == nullptr) { ALOGV("[%s] Skipping initial non-buffer layer", __func__); } else { - runs.emplace_back(currentSet, currentSet->getLayerCount()); + builder.init(currentSet); + if (firstLayer) { + runHasFirstLayer = true; + } isPartOfRun = true; } } } else if (isPartOfRun) { - // Runs must be at least 2 sets long or there's nothing to combine - if (runs.back().start->getLayerCount() == runs.back().length) { - runs.pop_back(); - } else { - // The prior run contained at least two sets. Currently, we'll - // only possibly merge a single run, so only keep track of a - // holePunchLayer if this is the first run. - if (runs.size() == 1) { - holePunchLayer = &(*currentSet); - } - - // TODO(b/185114532: Break out of the loop? We may find more runs, but we - // won't do anything with them. + builder.setHolePunchCandidate(&(*currentSet)); + if (auto run = builder.validateAndBuild(); run) { + runs.push_back(*run); } + runHasFirstLayer = false; + builder.reset(); isPartOfRun = false; } + + firstLayer = false; } - // Check for at least 2 sets one more time in case the set includes the last layer - if (isPartOfRun && runs.back().start->getLayerCount() == runs.back().length) { - runs.pop_back(); + // If we're in the middle of a run at the end, we still need to validate and build it. + if (isPartOfRun) { + if (auto run = builder.validateAndBuild(); run) { + runs.push_back(*run); + } } ALOGV("[%s] Found %zu candidate runs", __func__, runs.size()); + return runs; +} + +std::optional<Flattener::Run> Flattener::findBestRun(std::vector<Flattener::Run>& runs) const { if (runs.empty()) { + return std::nullopt; + } + + // TODO (b/181192467): Choose the best run, instead of just the first. + return runs[0]; +} + +void Flattener::buildCachedSets(time_point now) { + ATRACE_CALL(); + if (mLayers.empty()) { + ALOGV("[%s] No layers found, returning", __func__); + return; + } + + // Don't try to build a new cached set if we already have a new one in progress + if (mNewCachedSet) { + return; + } + + std::vector<Run> runs = findCandidateRuns(now); + + std::optional<Run> bestRun = findBestRun(runs); + + if (!bestRun) { return; } - mNewCachedSet.emplace(*runs[0].start); + mNewCachedSet.emplace(*bestRun->getStart()); mNewCachedSet->setLastUpdate(now); - auto currentSet = runs[0].start; - while (mNewCachedSet->getLayerCount() < runs[0].length) { + auto currentSet = bestRun->getStart(); + while (mNewCachedSet->getLayerCount() < bestRun->getLayerLength()) { ++currentSet; mNewCachedSet->append(*currentSet); } - if (mEnableHolePunch && holePunchLayer && holePunchLayer->requiresHolePunch()) { + if (mEnableHolePunch && bestRun->getHolePunchCandidate() && + bestRun->getHolePunchCandidate()->requiresHolePunch()) { // Add the pip layer to mNewCachedSet, but in a special way - it should // replace the buffer with a clear round rect. - mNewCachedSet->addHolePunchLayerIfFeasible(*holePunchLayer, - runs[0].start == mLayers.cbegin()); + mNewCachedSet->addHolePunchLayerIfFeasible(*bestRun->getHolePunchCandidate(), + bestRun->getStart() == mLayers.cbegin()); } // TODO(b/181192467): Actually compute new LayerState vector and corresponding hash for each run - mPredictor.getPredictedPlan({}, 0); + // and feedback into the predictor ++mCachedSetCreationCount; mCachedSetCreationCost += mNewCachedSet->getCreationCost(); diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp index 7e85dcaee0..2f2cc06c9b 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp @@ -18,17 +18,25 @@ #undef LOG_TAG #define LOG_TAG "Planner" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <android-base/properties.h> #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/impl/OutputLayerCompositionState.h> #include <compositionengine/impl/planner/Planner.h> +#include <utils/Trace.h> + namespace android::compositionengine::impl::planner { Planner::Planner() - : mFlattener(mPredictor, - base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), false)) {} + : mFlattener(base::GetBoolProperty(std::string("debug.sf.enable_hole_punch_pip"), false)) { + // Implicitly, layer caching must also be enabled. + // E.g., setprop debug.sf.enable_layer_caching 1, or + // adb shell service call SurfaceFlinger 1040 i32 1 [i64 <display ID>] + mPredictorEnabled = + base::GetBoolProperty(std::string("debug.sf.enable_planner_prediction"), false); +} void Planner::setDisplaySize(ui::Size size) { mFlattener.setDisplaySize(size); @@ -36,6 +44,7 @@ void Planner::setDisplaySize(ui::Size size) { void Planner::plan( compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) { + ATRACE_CALL(); std::unordered_set<LayerId> removedLayers; removedLayers.reserve(mPreviousLayers.size()); @@ -99,19 +108,26 @@ void Planner::plan( const bool layersWereFlattened = hash != mFlattenedHash; ALOGV("[%s] Initial hash %zx flattened hash %zx", __func__, hash, mFlattenedHash); - mPredictedPlan = - mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>() - : mCurrentLayers, - mFlattenedHash); - if (mPredictedPlan) { - ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str()); - } else { - ALOGV("[%s] No prediction found\n", __func__); + if (mPredictorEnabled) { + mPredictedPlan = + mPredictor.getPredictedPlan(layersWereFlattened ? std::vector<const LayerState*>() + : mCurrentLayers, + mFlattenedHash); + if (mPredictedPlan) { + ALOGV("[%s] Predicting plan %s", __func__, to_string(mPredictedPlan->plan).c_str()); + } else { + ALOGV("[%s] No prediction found\n", __func__); + } } } void Planner::reportFinalPlan( compositionengine::Output::OutputLayersEnumerator<compositionengine::Output>&& layers) { + ATRACE_CALL(); + if (!mPredictorEnabled) { + return; + } + Plan finalPlan; const GraphicBuffer* currentOverrideBuffer = nullptr; bool hasSkippedLayers = false; @@ -145,6 +161,7 @@ void Planner::reportFinalPlan( void Planner::renderCachedSets(renderengine::RenderEngine& renderEngine, const OutputCompositionState& outputState) { + ATRACE_CALL(); mFlattener.renderCachedSets(renderEngine, outputState); } @@ -185,7 +202,9 @@ void Planner::dump(const Vector<String16>& args, std::string& result) { return; } - mPredictor.compareLayerStacks(leftHash, rightHash, result); + if (mPredictorEnabled) { + mPredictor.compareLayerStacks(leftHash, rightHash, result); + } } else if (command == "--describe" || command == "-d") { if (args.size() < 3) { base::StringAppendF(&result, @@ -209,7 +228,9 @@ void Planner::dump(const Vector<String16>& args, std::string& result) { return; } - mPredictor.describeLayerStack(hash, result); + if (mPredictorEnabled) { + mPredictor.describeLayerStack(hash, result); + } } else if (command == "--help" || command == "-h") { dumpUsage(result); } else if (command == "--similar" || command == "-s") { @@ -232,7 +253,9 @@ void Planner::dump(const Vector<String16>& args, std::string& result) { return; } - mPredictor.listSimilarStacks(*plan, result); + if (mPredictorEnabled) { + mPredictor.listSimilarStacks(*plan, result); + } } else if (command == "--layers" || command == "-l") { mFlattener.dumpLayers(result); } else { @@ -247,7 +270,9 @@ void Planner::dump(const Vector<String16>& args, std::string& result) { mFlattener.dump(result); result.append("\n"); - mPredictor.dump(result); + if (mPredictorEnabled) { + mPredictor.dump(result); + } } void Planner::dumpUsage(std::string& result) const { diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index 8a83639ce5..e12cb57feb 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -533,16 +533,15 @@ using DisplayCreateOutputLayerTest = FullDisplayImplTestCommon; TEST_F(DisplayCreateOutputLayerTest, setsHwcLayer) { sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>(); - StrictMock<HWC2::mock::Layer> hwcLayer; + auto hwcLayer = std::make_shared<StrictMock<HWC2::mock::Layer>>(); EXPECT_CALL(mHwComposer, createLayer(HalDisplayId(DEFAULT_DISPLAY_ID))) - .WillOnce(Return(&hwcLayer)); + .WillOnce(Return(hwcLayer)); auto outputLayer = mDisplay->createOutputLayer(layerFE); - EXPECT_EQ(&hwcLayer, outputLayer->getHwcLayer()); + EXPECT_EQ(hwcLayer.get(), outputLayer->getHwcLayer()); - EXPECT_CALL(mHwComposer, destroyLayer(HalDisplayId(DEFAULT_DISPLAY_ID), &hwcLayer)); outputLayer.reset(); } @@ -627,7 +626,7 @@ TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutIfNotAHwcDisplay) { TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) { EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false)); EXPECT_CALL(mHwComposer, - getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _)) + getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _)) .WillOnce(Return(INVALID_OPERATION)); mDisplay->chooseCompositionStrategy(); @@ -649,7 +648,8 @@ TEST_F(DisplayChooseCompositionStrategyTest, normalOperation) { .InSequence(s) .WillOnce(Return(false)); - EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _)) + EXPECT_CALL(mHwComposer, + getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _)) .WillOnce(Return(NO_ERROR)); EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false)); @@ -679,8 +679,9 @@ TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) { .InSequence(s) .WillOnce(Return(false)); - EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _)) - .WillOnce(DoAll(SetArgPointee<2>(changes), Return(NO_ERROR))); + EXPECT_CALL(mHwComposer, + getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _)) + .WillOnce(DoAll(SetArgPointee<3>(changes), Return(NO_ERROR))); EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1); EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1); EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1); @@ -867,7 +868,8 @@ TEST_F(DisplayPresentAndGetFrameFencesTest, returnsPresentAndLayerFences) { sp<Fence> layer1Fence = new Fence(); sp<Fence> layer2Fence = new Fence(); - EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1); + EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID), _)) + .Times(1); EXPECT_CALL(mHwComposer, getPresentFence(HalDisplayId(DEFAULT_DISPLAY_ID))) .WillOnce(Return(presentFence)); EXPECT_CALL(mHwComposer, @@ -1039,7 +1041,7 @@ TEST_F(DisplayFunctionalTest, postFramebufferCriticalCallsAreOrdered) { mDisplay->editState().isEnabled = true; - EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_)); + EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_, _)); EXPECT_CALL(*mDisplaySurface, onFrameCommitted()); mDisplay->postFramebuffer(); diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index bac894a6dc..cd2d09e3db 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -48,15 +48,15 @@ public: MOCK_METHOD3(allocateVirtualDisplay, std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*)); MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId)); - MOCK_METHOD1(createLayer, HWC2::Layer*(HalDisplayId)); - MOCK_METHOD2(destroyLayer, void(HalDisplayId, HWC2::Layer*)); - MOCK_METHOD3(getDeviceCompositionChanges, - status_t(HalDisplayId, bool, + MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId)); + MOCK_METHOD4(getDeviceCompositionChanges, + status_t(HalDisplayId, bool, std::chrono::steady_clock::time_point, std::optional<android::HWComposer::DeviceRequestedChanges>*)); MOCK_METHOD5(setClientTarget, status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&, ui::Dataspace)); - MOCK_METHOD1(presentAndGetReleaseFences, status_t(HalDisplayId)); + MOCK_METHOD2(presentAndGetReleaseFences, + status_t(HalDisplayId, std::chrono::steady_clock::time_point)); MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode)); MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t)); MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&)); diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index 3adfe405ff..5bd12160cc 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -1065,12 +1065,62 @@ TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) { kOverrideSurfaceDamage); expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence); expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE); + EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false)); + + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false); +} + +TEST_F(OutputLayerWriteStateToHWCTest, previousOverriddenLayerSendsSurfaceDamage) { + mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE; + mOutputLayer.editState().hwc->stateOverridden = true; + + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion, + Region::INVALID_REGION); + expectSetHdrMetadataAndBufferCalls(); + expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE); + EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false)); + + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false); +} + +TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedDeviceCompositionInfo) { + mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE; + mOutputLayer.editState().hwc->stateOverridden = true; + mOutputLayer.editState().hwc->layerSkipped = true; + mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE; + + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion, + Region::INVALID_REGION); + expectSetHdrMetadataAndBufferCalls(); + expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE); EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false)); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, /*zIsOverridden*/ false, /*isPeekingThrough*/ false); } +TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedClientCompositionInfo) { + mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE; + mOutputLayer.editState().forceClientComposition = true; + mOutputLayer.editState().hwc->stateOverridden = true; + mOutputLayer.editState().hwc->layerSkipped = true; + mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT; + + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion, + Region::INVALID_REGION); + expectSetHdrMetadataAndBufferCalls(); + expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT); + EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false)); + + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false); +} + TEST_F(OutputLayerWriteStateToHWCTest, peekThroughChangesBlendMode) { auto peekThroughLayerFE = sp<compositionengine::mock::LayerFE>::make(); OutputLayer peekThroughLayer{mOutput, peekThroughLayerFE}; diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 27980a01ac..6677f408ad 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -238,6 +238,49 @@ TEST_F(OutputTest, setCompositionEnabledSetsDisabledAndDirtiesEntireOutput) { } /* + * Output::setLayerCachingEnabled() + */ + +TEST_F(OutputTest, setLayerCachingEnabled_enablesCaching) { + const auto kSize = ui::Size(1, 1); + EXPECT_CALL(*mRenderSurface, getSize()).WillRepeatedly(ReturnRef(kSize)); + mOutput->setLayerCachingEnabled(false); + mOutput->setLayerCachingEnabled(true); + + EXPECT_TRUE(mOutput->plannerEnabled()); +} + +TEST_F(OutputTest, setLayerCachingEnabled_disablesCaching) { + const auto kSize = ui::Size(1, 1); + EXPECT_CALL(*mRenderSurface, getSize()).WillRepeatedly(ReturnRef(kSize)); + mOutput->setLayerCachingEnabled(true); + mOutput->setLayerCachingEnabled(false); + + EXPECT_FALSE(mOutput->plannerEnabled()); +} + +TEST_F(OutputTest, setLayerCachingEnabled_disablesCachingAndResetsOverrideInfo) { + renderengine::mock::RenderEngine renderEngine; + const auto kSize = ui::Size(1, 1); + EXPECT_CALL(*mRenderSurface, getSize()).WillRepeatedly(ReturnRef(kSize)); + mOutput->setLayerCachingEnabled(true); + + // Inject some layers + InjectedLayer layer; + layer.outputLayerState.overrideInfo.buffer = std::make_shared< + renderengine::ExternalTexture>(new GraphicBuffer(), renderEngine, + renderengine::ExternalTexture::Usage::READABLE | + renderengine::ExternalTexture::Usage::WRITEABLE); + injectOutputLayer(layer); + // inject a null layer to check for null exceptions + injectNullOutputLayer(); + + EXPECT_NE(nullptr, layer.outputLayerState.overrideInfo.buffer); + mOutput->setLayerCachingEnabled(false); + EXPECT_EQ(nullptr, layer.outputLayerState.overrideInfo.buffer); +} + +/* * Output::setProjection() */ @@ -972,9 +1015,7 @@ TEST_F(OutputPrepareFrameTest, delegatesToChooseCompositionStrategyAndRenderSurf mOutput.editState().usesDeviceComposition = true; EXPECT_CALL(mOutput, chooseCompositionStrategy()).Times(1); - if (mOutput.plannerEnabled()) { - EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); - } + EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u)); EXPECT_CALL(*mRenderSurface, prepareFrame(false, true)); mOutput.prepareFrame(); diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp index a39331c02d..8f44677676 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp @@ -134,6 +134,7 @@ void expectReadyBuffer(const CachedSet& cachedSet) { EXPECT_NE(nullptr, cachedSet.getBuffer()); EXPECT_NE(nullptr, cachedSet.getDrawFence()); EXPECT_TRUE(cachedSet.hasReadyBuffer()); + EXPECT_TRUE(cachedSet.hasRenderedBuffer()); } TEST_F(CachedSetTest, createFromLayer) { @@ -567,5 +568,30 @@ TEST_F(CachedSetTest, decompose_removesHolePunch) { } } +TEST_F(CachedSetTest, hasBlurBehind) { + mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1; + mTestLayers[1]->layerState->update(&mTestLayers[1]->outputLayer); + mTestLayers[2]->layerFECompositionState.blurRegions.push_back( + BlurRegion{1, 0, 0, 0, 0, 0, 0, 0, 0, 0}); + mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer); + + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); + CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); + + CachedSet cachedSet1(layer1); + CachedSet cachedSet2(layer2); + CachedSet cachedSet3(layer3); + + // Cached set 4 will consist of layers 1 and 2, which will contain a blur behind + CachedSet cachedSet4(layer1); + cachedSet4.addLayer(layer2.getState(), kStartTime); + + EXPECT_FALSE(cachedSet1.hasBlurBehind()); + EXPECT_TRUE(cachedSet2.hasBlurBehind()); + EXPECT_TRUE(cachedSet3.hasBlurBehind()); + EXPECT_TRUE(cachedSet4.hasBlurBehind()); +} + } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index 71757f69ea..7ec2c98bc1 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -18,7 +18,6 @@ #include <compositionengine/impl/planner/CachedSet.h> #include <compositionengine/impl/planner/Flattener.h> #include <compositionengine/impl/planner/LayerState.h> -#include <compositionengine/impl/planner/Predictor.h> #include <compositionengine/mock/LayerFE.h> #include <compositionengine/mock/OutputLayer.h> #include <gtest/gtest.h> @@ -31,7 +30,6 @@ using namespace std::chrono_literals; using impl::planner::Flattener; using impl::planner::LayerState; using impl::planner::NonBufferHash; -using impl::planner::Predictor; using testing::_; using testing::ByMove; @@ -47,7 +45,7 @@ namespace { class FlattenerTest : public testing::Test { public: - FlattenerTest() : mFlattener(std::make_unique<Flattener>(mPredictor, true)) {} + FlattenerTest() : mFlattener(std::make_unique<Flattener>(true)) {} void SetUp() override; protected: @@ -55,10 +53,6 @@ protected: void initializeFlattener(const std::vector<const LayerState*>& layers); void expectAllLayersFlattened(const std::vector<const LayerState*>& layers); - // TODO(b/181192467): Once Flattener starts to do something useful with Predictor, - // mPredictor should be mocked and checked for expectations. - Predictor mPredictor; - // mRenderEngine may be held as a pointer to mFlattener, so mFlattener must be destroyed first. renderengine::mock::RenderEngine mRenderEngine; std::unique_ptr<Flattener> mFlattener; @@ -648,5 +642,189 @@ TEST_F(FlattenerTest, flattenLayers_pip) { EXPECT_EQ(&mTestLayers[2]->outputLayer, peekThroughLayer1); EXPECT_EQ(peekThroughLayer1, peekThroughLayer2); } + +TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) { + auto& layerState1 = mTestLayers[0]->layerState; + + auto& layerState2 = mTestLayers[1]->layerState; + mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1; + layerState2->update(&mTestLayers[1]->outputLayer); + + auto& layerState3 = mTestLayers[2]->layerState; + const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer; + const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer; + const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer; + + const std::vector<const LayerState*> layers = { + layerState1.get(), + layerState2.get(), + layerState3.get(), + }; + + initializeFlattener(layers); + + // Mark the first two layers inactive, which contain the blur behind + mTime += 200ms; + layerState3->resetFramesSinceBufferUpdate(); + + // layers would be flattened but the buffer would not be overridden + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + + initializeOverrideBuffer(layers); + EXPECT_EQ(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + mFlattener->renderCachedSets(mRenderEngine, mOutputState); + + for (const auto layer : layers) { + EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer); + } + + // the new flattened layer is replaced + initializeOverrideBuffer(layers); + EXPECT_NE(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + mFlattener->renderCachedSets(mRenderEngine, mOutputState); + EXPECT_NE(nullptr, overrideBuffer1); + EXPECT_EQ(overrideBuffer1, overrideBuffer2); + EXPECT_EQ(nullptr, overrideBuffer3); +} + +TEST_F(FlattenerTest, flattenLayers_doesNotFlattenBlurBehindRun) { + auto& layerState1 = mTestLayers[0]->layerState; + + auto& layerState2 = mTestLayers[1]->layerState; + mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1; + layerState2->update(&mTestLayers[1]->outputLayer); + + auto& layerState3 = mTestLayers[2]->layerState; + + const std::vector<const LayerState*> layers = { + layerState1.get(), + layerState2.get(), + layerState3.get(), + }; + + initializeFlattener(layers); + + // Mark the last two layers inactive, which contains the blur layer, but does not contain the + // first layer + mTime += 200ms; + layerState1->resetFramesSinceBufferUpdate(); + + // layers would be flattened but the buffer would not be overridden + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillRepeatedly(Return(NO_ERROR)); + + initializeOverrideBuffer(layers); + EXPECT_EQ(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + mFlattener->renderCachedSets(mRenderEngine, mOutputState); + + for (const auto layer : layers) { + EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer); + } + + // nothing is flattened because the last two frames cannot be cached due to containing a blur + // layer + initializeOverrideBuffer(layers); + EXPECT_EQ(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + mFlattener->renderCachedSets(mRenderEngine, mOutputState); + for (const auto layer : layers) { + EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer); + } +} + +TEST_F(FlattenerTest, flattenLayers_flattenSkipsLayerWithBlurBehind) { + auto& layerState1 = mTestLayers[0]->layerState; + + auto& layerStateWithBlurBehind = mTestLayers[1]->layerState; + mTestLayers[1]->layerFECompositionState.backgroundBlurRadius = 1; + layerStateWithBlurBehind->update(&mTestLayers[1]->outputLayer); + + auto& layerState3 = mTestLayers[2]->layerState; + auto& layerState4 = mTestLayers[3]->layerState; + const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer; + const auto& blurOverrideBuffer = + layerStateWithBlurBehind->getOutputLayer()->getState().overrideInfo.buffer; + const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer; + const auto& overrideBuffer4 = layerState4->getOutputLayer()->getState().overrideInfo.buffer; + + const std::vector<const LayerState*> layers = { + layerState1.get(), + layerStateWithBlurBehind.get(), + layerState3.get(), + layerState4.get(), + }; + + initializeFlattener(layers); + + // Mark the last three layers inactive, which contains the blur layer, but does not contain the + // first layer + mTime += 200ms; + layerState1->resetFramesSinceBufferUpdate(); + + // layers would be flattened but the buffer would not be overridden + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + + initializeOverrideBuffer(layers); + EXPECT_EQ(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + mFlattener->renderCachedSets(mRenderEngine, mOutputState); + + for (const auto layer : layers) { + EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer); + } + + // the new flattened layer is replaced + initializeOverrideBuffer(layers); + EXPECT_NE(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + mFlattener->renderCachedSets(mRenderEngine, mOutputState); + EXPECT_EQ(nullptr, overrideBuffer1); + EXPECT_EQ(nullptr, blurOverrideBuffer); + EXPECT_NE(nullptr, overrideBuffer3); + EXPECT_EQ(overrideBuffer3, overrideBuffer4); +} + +TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) { + auto& layerState1 = mTestLayers[0]->layerState; + auto& layerState2 = mTestLayers[1]->layerState; + const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer; + const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer; + + const std::vector<const LayerState*> layers = { + layerState1.get(), + layerState2.get(), + }; + + initializeFlattener(layers); + + // Mark the layers inactive + mTime += 200ms; + // layers would be flattened but the buffer would not be overridden + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + + initializeOverrideBuffer(layers); + EXPECT_EQ(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + mFlattener->renderCachedSets(mRenderEngine, mOutputState); + + EXPECT_EQ(nullptr, overrideBuffer1); + EXPECT_EQ(nullptr, overrideBuffer2); + + // Simulate attempting to render prior to merging the new cached set with the layer stack. + // Here we should not try to re-render. + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0); + mFlattener->renderCachedSets(mRenderEngine, mOutputState); + + // We provide the override buffer now that it's rendered + EXPECT_NE(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + mFlattener->renderCachedSets(mRenderEngine, mOutputState); + + EXPECT_NE(nullptr, overrideBuffer1); + EXPECT_EQ(overrideBuffer2, overrideBuffer1); +} + } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp index 948c850735..a09ce1479e 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp @@ -58,6 +58,10 @@ const GenericLayerMetadataEntry sMetadataValueOne = GenericLayerMetadataEntry{ const GenericLayerMetadataEntry sMetadataValueTwo = GenericLayerMetadataEntry{ .value = std::vector<uint8_t>({1, 3}), }; +const constexpr int32_t sBgBlurRadiusOne = 3; +const constexpr int32_t sBgBlurRadiusTwo = 4; +const BlurRegion sBlurRegionOne = BlurRegion{1, 2.f, 3.f, 4.f, 5.f, 6.f, 7, 8, 9, 10}; +const BlurRegion sBlurRegionTwo = BlurRegion{2, 3.f, 4.f, 5.f, 6.f, 7.f, 8, 9, 10, 11}; struct LayerStateTest : public testing::Test { LayerStateTest() { @@ -830,6 +834,114 @@ TEST_F(LayerStateTest, compareSolidColor) { EXPECT_TRUE(otherLayerState->compare(*mLayerState)); } +TEST_F(LayerStateTest, updateBackgroundBlur) { + OutputLayerCompositionState outputLayerCompositionState; + LayerFECompositionState layerFECompositionState; + layerFECompositionState.backgroundBlurRadius = sBgBlurRadiusOne; + setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + layerFECompositionState); + mLayerState = std::make_unique<LayerState>(&mOutputLayer); + + mock::OutputLayer newOutputLayer; + mock::LayerFE newLayerFE; + LayerFECompositionState layerFECompositionStateTwo; + layerFECompositionStateTwo.backgroundBlurRadius = sBgBlurRadiusTwo; + setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + layerFECompositionStateTwo); + Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); + EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BackgroundBlurRadius), updates); +} + +TEST_F(LayerStateTest, compareBackgroundBlur) { + OutputLayerCompositionState outputLayerCompositionState; + LayerFECompositionState layerFECompositionState; + layerFECompositionState.backgroundBlurRadius = sBgBlurRadiusOne; + setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + layerFECompositionState); + mLayerState = std::make_unique<LayerState>(&mOutputLayer); + mock::OutputLayer newOutputLayer; + mock::LayerFE newLayerFE; + LayerFECompositionState layerFECompositionStateTwo; + layerFECompositionStateTwo.backgroundBlurRadius = sBgBlurRadiusTwo; + setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + layerFECompositionStateTwo); + auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); + + verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, + LayerStateField::BackgroundBlurRadius); + + EXPECT_TRUE(mLayerState->compare(*otherLayerState)); + EXPECT_TRUE(otherLayerState->compare(*mLayerState)); +} + +TEST_F(LayerStateTest, updateBlurRegions) { + OutputLayerCompositionState outputLayerCompositionState; + LayerFECompositionState layerFECompositionState; + layerFECompositionState.blurRegions.push_back(sBlurRegionOne); + setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + layerFECompositionState); + mLayerState = std::make_unique<LayerState>(&mOutputLayer); + + mock::OutputLayer newOutputLayer; + mock::LayerFE newLayerFE; + LayerFECompositionState layerFECompositionStateTwo; + layerFECompositionStateTwo.blurRegions.push_back(sBlurRegionTwo); + setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + layerFECompositionStateTwo); + Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); + EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlurRegions), updates); +} + +TEST_F(LayerStateTest, compareBlurRegions) { + OutputLayerCompositionState outputLayerCompositionState; + LayerFECompositionState layerFECompositionState; + layerFECompositionState.blurRegions.push_back(sBlurRegionOne); + setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + layerFECompositionState); + mLayerState = std::make_unique<LayerState>(&mOutputLayer); + mock::OutputLayer newOutputLayer; + mock::LayerFE newLayerFE; + LayerFECompositionState layerFECompositionStateTwo; + layerFECompositionStateTwo.blurRegions.push_back(sBlurRegionTwo); + setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + layerFECompositionStateTwo); + auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); + + verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::BlurRegions); + + EXPECT_TRUE(mLayerState->compare(*otherLayerState)); + EXPECT_TRUE(otherLayerState->compare(*mLayerState)); +} + +TEST_F(LayerStateTest, hasBlurBehind_noBlur_returnsFalse) { + OutputLayerCompositionState outputLayerCompositionState; + LayerFECompositionState layerFECompositionState; + setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + layerFECompositionState); + mLayerState = std::make_unique<LayerState>(&mOutputLayer); + EXPECT_FALSE(mLayerState->hasBlurBehind()); +} + +TEST_F(LayerStateTest, hasBlurBehind_withBackgroundBlur_returnsTrue) { + OutputLayerCompositionState outputLayerCompositionState; + LayerFECompositionState layerFECompositionState; + layerFECompositionState.backgroundBlurRadius = sBgBlurRadiusOne; + setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + layerFECompositionState); + mLayerState = std::make_unique<LayerState>(&mOutputLayer); + EXPECT_TRUE(mLayerState->hasBlurBehind()); +} + +TEST_F(LayerStateTest, hasBlurBehind_withBlurRegion_returnsTrue) { + OutputLayerCompositionState outputLayerCompositionState; + LayerFECompositionState layerFECompositionState; + layerFECompositionState.blurRegions.push_back(sBlurRegionOne); + setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + layerFECompositionState); + mLayerState = std::make_unique<LayerState>(&mOutputLayer); + EXPECT_TRUE(mLayerState->hasBlurBehind()); +} + TEST_F(LayerStateTest, dumpDoesNotCrash) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 8692ee60dc..0f18235751 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -138,6 +138,10 @@ void DisplayDevice::setPowerMode(hal::PowerMode mode) { getCompositionDisplay()->setCompositionEnabled(mPowerMode != hal::PowerMode::OFF); } +void DisplayDevice::enableLayerCaching(bool enable) { + getCompositionDisplay()->setLayerCachingEnabled(enable); +} + hal::PowerMode DisplayDevice::getPowerMode() const { return mPowerMode; } @@ -268,8 +272,9 @@ void DisplayDevice::dump(std::string& result) const { StringAppendF(&result, "+ %s\n", getDebugName().c_str()); StringAppendF(&result, " powerMode=%s (%d)\n", to_string(mPowerMode).c_str(), static_cast<int32_t>(mPowerMode)); + const auto activeMode = getActiveMode(); StringAppendF(&result, " activeMode=%s\n", - mSupportedModes.size() ? to_string(*getActiveMode()).c_str() : "none"); + activeMode ? to_string(*activeMode).c_str() : "none"); result.append(" supportedModes=\n"); diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 68846d35bd..bf249cdb25 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -157,6 +157,9 @@ public: void setPowerMode(hardware::graphics::composer::hal::PowerMode mode); bool isPoweredOn() const; + // Enables layer caching on this DisplayDevice + void enableLayerCaching(bool enable); + ui::Dataspace getCompositionDataSpace() const; /* ------------------------------------------------------------------------ diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index d04b5f7316..27146ab79c 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -78,7 +78,19 @@ Display::Display(android::Hwc2::Composer& composer, } Display::~Display() { - mLayers.clear(); + // Note: The calls to onOwningDisplayDestroyed() are allowed (and expected) + // to call Display::onLayerDestroyed(). As that call removes entries from + // mLayers, we do not want to have a for loop directly over it here. Since + // the end goal is an empty mLayers anyway, we just go ahead and swap an + // initially empty local container with mLayers, and then enumerate + // the contents of the local container. + Layers destroyingLayers; + std::swap(mLayers, destroyingLayers); + for (const auto& [_, weakLayer] : destroyingLayers) { + if (std::shared_ptr layer = weakLayer.lock()) { + layer->onOwningDisplayDestroyed(); + } + } Error error = Error::NONE; const char* msg; @@ -110,29 +122,21 @@ Error Display::acceptChanges() return static_cast<Error>(intError); } -Error Display::createLayer(HWC2::Layer** outLayer) { - if (!outLayer) { - return Error::BAD_PARAMETER; - } +base::expected<std::shared_ptr<HWC2::Layer>, hal::Error> Display::createLayer() { HWLayerId layerId = 0; auto intError = mComposer.createLayer(mId, &layerId); auto error = static_cast<Error>(intError); if (error != Error::NONE) { - return error; + return base::unexpected(error); } - auto layer = std::make_unique<impl::Layer>(mComposer, mCapabilities, mId, layerId); - *outLayer = layer.get(); - mLayers.emplace(layerId, std::move(layer)); - return Error::NONE; + auto layer = std::make_shared<impl::Layer>(mComposer, mCapabilities, *this, layerId); + mLayers.emplace(layerId, layer); + return layer; } -Error Display::destroyLayer(HWC2::Layer* layer) { - if (!layer) { - return Error::BAD_PARAMETER; - } - mLayers.erase(layer->getId()); - return Error::NONE; +void Display::onLayerDestroyed(hal::HWLayerId layerId) { + mLayers.erase(layerId); } bool Display::isVsyncPeriodSwitchSupported() const { @@ -161,7 +165,7 @@ Error Display::getChangedCompositionTypes(std::unordered_map<HWC2::Layer*, Compo auto type = types[element]; ALOGV("getChangedCompositionTypes: adding %" PRIu64 " %s", layer->getId(), to_string(type).c_str()); - outTypes->emplace(layer, type); + outTypes->emplace(layer.get(), type); } else { ALOGE("getChangedCompositionTypes: invalid layer %" PRIu64 " found" " on display %" PRIu64, layerIds[element], mId); @@ -254,7 +258,7 @@ Error Display::getRequests(HWC2::DisplayRequest* outDisplayRequests, if (layer) { auto layerRequest = static_cast<LayerRequest>(layerRequests[element]); - outLayerRequests->emplace(layer, layerRequest); + outLayerRequests->emplace(layer.get(), layerRequest); } else { ALOGE("getRequests: invalid layer %" PRIu64 " found on display %" PRIu64, layerIds[element], mId); @@ -340,7 +344,7 @@ Error Display::getReleaseFences(std::unordered_map<HWC2::Layer*, sp<Fence>>* out auto layer = getLayerById(layerIds[element]); if (layer) { sp<Fence> fence(new Fence(fenceFds[element])); - releaseFences.emplace(layer, fence); + releaseFences.emplace(layer.get(), fence); } else { ALOGE("getReleaseFences: invalid layer %" PRIu64 " found on display %" PRIu64, layerIds[element], mId); @@ -550,12 +554,9 @@ void Display::setConnected(bool connected) { // Other Display methods -HWC2::Layer* Display::getLayerById(HWLayerId id) const { - if (mLayers.count(id) == 0) { - return nullptr; - } - - return mLayers.at(id).get(); +std::shared_ptr<HWC2::Layer> Display::getLayerById(HWLayerId id) const { + auto it = mLayers.find(id); + return it != mLayers.end() ? it->second.lock() : nullptr; } } // namespace impl @@ -566,47 +567,78 @@ Layer::~Layer() = default; namespace impl { Layer::Layer(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities, - HWDisplayId displayId, HWLayerId layerId) + HWC2::Display& display, HWLayerId layerId) : mComposer(composer), mCapabilities(capabilities), - mDisplayId(displayId), + mDisplay(&display), mId(layerId), mColorMatrix(android::mat4()) { - ALOGV("Created layer %" PRIu64 " on display %" PRIu64, layerId, displayId); + ALOGV("Created layer %" PRIu64 " on display %" PRIu64, layerId, display.getId()); } Layer::~Layer() { - auto intError = mComposer.destroyLayer(mDisplayId, mId); + onOwningDisplayDestroyed(); +} + +void Layer::onOwningDisplayDestroyed() { + // Note: onOwningDisplayDestroyed() may be called to perform cleanup by + // either the Layer dtor or by the Display dtor and must be safe to call + // from either path. In particular, the call to Display::onLayerDestroyed() + // is expected to be safe to do, + + if (CC_UNLIKELY(!mDisplay)) { + return; + } + + mDisplay->onLayerDestroyed(mId); + + // Note: If the HWC display was actually disconnected, these calls are will + // return an error. We always make them as there may be other reasons for + // the HWC2::Display to be destroyed. + auto intError = mComposer.destroyLayer(mDisplay->getId(), mId); auto error = static_cast<Error>(intError); ALOGE_IF(error != Error::NONE, "destroyLayer(%" PRIu64 ", %" PRIu64 ")" " failed: %s (%d)", - mDisplayId, mId, to_string(error).c_str(), intError); + mDisplay->getId(), mId, to_string(error).c_str(), intError); + + mDisplay = nullptr; } Error Layer::setCursorPosition(int32_t x, int32_t y) { - auto intError = mComposer.setCursorPosition(mDisplayId, mId, x, y); + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + + auto intError = mComposer.setCursorPosition(mDisplay->getId(), mId, x, y); return static_cast<Error>(intError); } Error Layer::setBuffer(uint32_t slot, const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence) { + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + if (buffer == nullptr && mBufferSlot == slot) { return Error::NONE; } mBufferSlot = slot; int32_t fenceFd = acquireFence->dup(); - auto intError = mComposer.setLayerBuffer(mDisplayId, mId, slot, buffer, - fenceFd); + auto intError = mComposer.setLayerBuffer(mDisplay->getId(), mId, slot, buffer, fenceFd); return static_cast<Error>(intError); } Error Layer::setSurfaceDamage(const Region& damage) { + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + if (damage.isRect() && mDamageRegion.isRect() && (damage.getBounds() == mDamageRegion.getBounds())) { return Error::NONE; @@ -617,8 +649,8 @@ Error Layer::setSurfaceDamage(const Region& damage) // rects for HWC Hwc2::Error intError = Hwc2::Error::NONE; if (damage.isRect() && damage.getBounds() == Rect::INVALID_RECT) { - intError = mComposer.setLayerSurfaceDamage(mDisplayId, - mId, std::vector<Hwc2::IComposerClient::Rect>()); + intError = mComposer.setLayerSurfaceDamage(mDisplay->getId(), mId, + std::vector<Hwc2::IComposerClient::Rect>()); } else { size_t rectCount = 0; auto rectArray = damage.getArray(&rectCount); @@ -629,7 +661,7 @@ Error Layer::setSurfaceDamage(const Region& damage) rectArray[rect].right, rectArray[rect].bottom}); } - intError = mComposer.setLayerSurfaceDamage(mDisplayId, mId, hwcRects); + intError = mComposer.setLayerSurfaceDamage(mDisplay->getId(), mId, hwcRects); } return static_cast<Error>(intError); @@ -637,34 +669,54 @@ Error Layer::setSurfaceDamage(const Region& damage) Error Layer::setBlendMode(BlendMode mode) { - auto intError = mComposer.setLayerBlendMode(mDisplayId, mId, mode); + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + + auto intError = mComposer.setLayerBlendMode(mDisplay->getId(), mId, mode); return static_cast<Error>(intError); } Error Layer::setColor(Color color) { - auto intError = mComposer.setLayerColor(mDisplayId, mId, color); + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + + auto intError = mComposer.setLayerColor(mDisplay->getId(), mId, color); return static_cast<Error>(intError); } Error Layer::setCompositionType(Composition type) { - auto intError = mComposer.setLayerCompositionType(mDisplayId, mId, type); + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + + auto intError = mComposer.setLayerCompositionType(mDisplay->getId(), mId, type); return static_cast<Error>(intError); } Error Layer::setDataspace(Dataspace dataspace) { + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + if (dataspace == mDataSpace) { return Error::NONE; } mDataSpace = dataspace; - auto intError = mComposer.setLayerDataspace(mDisplayId, mId, mDataSpace); + auto intError = mComposer.setLayerDataspace(mDisplay->getId(), mId, mDataSpace); return static_cast<Error>(intError); } Error Layer::setPerFrameMetadata(const int32_t supportedPerFrameMetadata, const android::HdrMetadata& metadata) { + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + if (metadata == mHdrMetadata) { return Error::NONE; } @@ -705,7 +757,7 @@ Error Layer::setPerFrameMetadata(const int32_t supportedPerFrameMetadata, } Error error = static_cast<Error>( - mComposer.setLayerPerFrameMetadata(mDisplayId, mId, perFrameMetadatas)); + mComposer.setLayerPerFrameMetadata(mDisplay->getId(), mId, perFrameMetadatas)); if (validTypes & HdrMetadata::HDR10PLUS) { if (CC_UNLIKELY(mHdrMetadata.hdr10plus.size() == 0)) { @@ -715,8 +767,9 @@ Error Layer::setPerFrameMetadata(const int32_t supportedPerFrameMetadata, std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs; perFrameMetadataBlobs.push_back( {Hwc2::PerFrameMetadataKey::HDR10_PLUS_SEI, mHdrMetadata.hdr10plus}); - Error setMetadataBlobsError = static_cast<Error>( - mComposer.setLayerPerFrameMetadataBlobs(mDisplayId, mId, perFrameMetadataBlobs)); + Error setMetadataBlobsError = + static_cast<Error>(mComposer.setLayerPerFrameMetadataBlobs(mDisplay->getId(), mId, + perFrameMetadataBlobs)); if (error == Error::NONE) { return setMetadataBlobsError; } @@ -726,46 +779,70 @@ Error Layer::setPerFrameMetadata(const int32_t supportedPerFrameMetadata, Error Layer::setDisplayFrame(const Rect& frame) { + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + Hwc2::IComposerClient::Rect hwcRect{frame.left, frame.top, frame.right, frame.bottom}; - auto intError = mComposer.setLayerDisplayFrame(mDisplayId, mId, hwcRect); + auto intError = mComposer.setLayerDisplayFrame(mDisplay->getId(), mId, hwcRect); return static_cast<Error>(intError); } Error Layer::setPlaneAlpha(float alpha) { - auto intError = mComposer.setLayerPlaneAlpha(mDisplayId, mId, alpha); + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + + auto intError = mComposer.setLayerPlaneAlpha(mDisplay->getId(), mId, alpha); return static_cast<Error>(intError); } Error Layer::setSidebandStream(const native_handle_t* stream) { + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + if (mCapabilities.count(Capability::SIDEBAND_STREAM) == 0) { ALOGE("Attempted to call setSidebandStream without checking that the " "device supports sideband streams"); return Error::UNSUPPORTED; } - auto intError = mComposer.setLayerSidebandStream(mDisplayId, mId, stream); + auto intError = mComposer.setLayerSidebandStream(mDisplay->getId(), mId, stream); return static_cast<Error>(intError); } Error Layer::setSourceCrop(const FloatRect& crop) { + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + Hwc2::IComposerClient::FRect hwcRect{ crop.left, crop.top, crop.right, crop.bottom}; - auto intError = mComposer.setLayerSourceCrop(mDisplayId, mId, hwcRect); + auto intError = mComposer.setLayerSourceCrop(mDisplay->getId(), mId, hwcRect); return static_cast<Error>(intError); } Error Layer::setTransform(Transform transform) { + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + auto intTransform = static_cast<Hwc2::Transform>(transform); - auto intError = mComposer.setLayerTransform(mDisplayId, mId, intTransform); + auto intError = mComposer.setLayerTransform(mDisplay->getId(), mId, intTransform); return static_cast<Error>(intError); } Error Layer::setVisibleRegion(const Region& region) { + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + if (region.isRect() && mVisibleRegion.isRect() && (region.getBounds() == mVisibleRegion.getBounds())) { return Error::NONE; @@ -781,22 +858,30 @@ Error Layer::setVisibleRegion(const Region& region) rectArray[rect].right, rectArray[rect].bottom}); } - auto intError = mComposer.setLayerVisibleRegion(mDisplayId, mId, hwcRects); + auto intError = mComposer.setLayerVisibleRegion(mDisplay->getId(), mId, hwcRects); return static_cast<Error>(intError); } Error Layer::setZOrder(uint32_t z) { - auto intError = mComposer.setLayerZOrder(mDisplayId, mId, z); + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + + auto intError = mComposer.setLayerZOrder(mDisplay->getId(), mId, z); return static_cast<Error>(intError); } // Composer HAL 2.3 Error Layer::setColorTransform(const android::mat4& matrix) { + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + if (matrix == mColorMatrix) { return Error::NONE; } - auto intError = mComposer.setLayerColorTransform(mDisplayId, mId, matrix.asArray()); + auto intError = mComposer.setLayerColorTransform(mDisplay->getId(), mId, matrix.asArray()); Error error = static_cast<Error>(intError); if (error != Error::NONE) { return error; @@ -808,7 +893,12 @@ Error Layer::setColorTransform(const android::mat4& matrix) { // Composer HAL 2.4 Error Layer::setLayerGenericMetadata(const std::string& name, bool mandatory, const std::vector<uint8_t>& value) { - auto intError = mComposer.setLayerGenericMetadata(mDisplayId, mId, name, mandatory, value); + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + + auto intError = + mComposer.setLayerGenericMetadata(mDisplay->getId(), mId, name, mandatory, value); return static_cast<Error>(intError); } diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index e7bf286d08..fae95e79c3 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -16,6 +16,7 @@ #pragma once +#include <android-base/expected.h> #include <gui/HdrMetadata.h> #include <math/mat4.h> #include <ui/HdrCapabilities.h> @@ -84,10 +85,11 @@ public: virtual void setConnected(bool connected) = 0; // For use by Device only virtual const std::unordered_set<hal::DisplayCapability>& getCapabilities() const = 0; virtual bool isVsyncPeriodSwitchSupported() const = 0; + virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0; [[clang::warn_unused_result]] virtual hal::Error acceptChanges() = 0; - [[clang::warn_unused_result]] virtual hal::Error createLayer(Layer** outLayer) = 0; - [[clang::warn_unused_result]] virtual hal::Error destroyLayer(Layer* layer) = 0; + [[clang::warn_unused_result]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error> + createLayer() = 0; [[clang::warn_unused_result]] virtual hal::Error getChangedCompositionTypes( std::unordered_map<Layer*, hal::Composition>* outTypes) = 0; [[clang::warn_unused_result]] virtual hal::Error getColorModes( @@ -152,6 +154,8 @@ public: namespace impl { +class Layer; + class Display : public HWC2::Display { public: Display(android::Hwc2::Composer&, const std::unordered_set<hal::Capability>&, hal::HWDisplayId, @@ -160,10 +164,9 @@ public: // Required by HWC2 hal::Error acceptChanges() override; - hal::Error createLayer(Layer** outLayer) override; - hal::Error destroyLayer(Layer*) override; + base::expected<std::shared_ptr<HWC2::Layer>, hal::Error> createLayer() override; hal::Error getChangedCompositionTypes( - std::unordered_map<Layer*, hal::Composition>* outTypes) override; + std::unordered_map<HWC2::Layer*, hal::Composition>* outTypes) override; hal::Error getColorModes(std::vector<hal::ColorMode>* outModes) const override; // Returns a bitmask which contains HdrMetadata::Type::*. int32_t getSupportedPerFrameMetadata() const override; @@ -174,7 +177,7 @@ public: hal::Error getName(std::string* outName) const override; hal::Error getRequests( hal::DisplayRequest* outDisplayRequests, - std::unordered_map<Layer*, hal::LayerRequest>* outLayerRequests) override; + std::unordered_map<HWC2::Layer*, hal::LayerRequest>* outLayerRequests) override; hal::Error getConnectionType(ui::DisplayConnectionType*) const override; hal::Error supportsDoze(bool* outSupport) const override; hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override; @@ -185,8 +188,8 @@ public: uint64_t maxFrames) const override; hal::Error getDisplayedContentSample(uint64_t maxFrames, uint64_t timestamp, android::DisplayedFrameStats* outStats) const override; - hal::Error getReleaseFences( - std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const override; + hal::Error getReleaseFences(std::unordered_map<HWC2::Layer*, android::sp<android::Fence>>* + outFences) const override; hal::Error present(android::sp<android::Fence>* outPresentFence) override; hal::Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target, const android::sp<android::Fence>& acquireFence, @@ -218,13 +221,14 @@ public: const std::unordered_set<hal::DisplayCapability>& getCapabilities() const override { return mDisplayCapabilities; }; - virtual bool isVsyncPeriodSwitchSupported() const override; + bool isVsyncPeriodSwitchSupported() const override; + void onLayerDestroyed(hal::HWLayerId layerId) override; private: // This may fail (and return a null pointer) if no layer with this ID exists // on this display - Layer* getLayerById(hal::HWLayerId) const; + std::shared_ptr<HWC2::Layer> getLayerById(hal::HWLayerId id) const; friend android::TestableSurfaceFlinger; @@ -240,7 +244,8 @@ private: hal::DisplayType mType; bool mIsConnected = false; - std::unordered_map<hal::HWLayerId, std::unique_ptr<Layer>> mLayers; + using Layers = std::unordered_map<hal::HWLayerId, std::weak_ptr<HWC2::impl::Layer>>; + Layers mLayers; std::once_flag mDisplayCapabilityQueryFlag; std::unordered_set<hal::DisplayCapability> mDisplayCapabilities; @@ -295,10 +300,12 @@ namespace impl { class Layer : public HWC2::Layer { public: Layer(android::Hwc2::Composer& composer, - const std::unordered_set<hal::Capability>& capabilities, hal::HWDisplayId displayId, + const std::unordered_set<hal::Capability>& capabilities, HWC2::Display& display, hal::HWLayerId layerId); ~Layer() override; + void onOwningDisplayDestroyed(); + hal::HWLayerId getId() const override { return mId; } hal::Error setCursorPosition(int32_t x, int32_t y) override; @@ -334,7 +341,7 @@ private: android::Hwc2::Composer& mComposer; const std::unordered_set<hal::Capability>& mCapabilities; - hal::HWDisplayId mDisplayId; + HWC2::Display* mDisplay; hal::HWLayerId mId; // Cached HWC2 data, to ensure the same commands aren't sent to the HWC diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index b73d0320c3..36876dc7f1 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -305,20 +305,15 @@ int32_t HWComposer::getAttribute(hal::HWDisplayId hwcDisplayId, hal::HWConfigId return value; } -HWC2::Layer* HWComposer::createLayer(HalDisplayId displayId) { +std::shared_ptr<HWC2::Layer> HWComposer::createLayer(HalDisplayId displayId) { RETURN_IF_INVALID_DISPLAY(displayId, nullptr); - HWC2::Layer* layer; - auto error = mDisplayData[displayId].hwcDisplay->createLayer(&layer); - RETURN_IF_HWC_ERROR(error, displayId, nullptr); - return layer; -} - -void HWComposer::destroyLayer(HalDisplayId displayId, HWC2::Layer* layer) { - RETURN_IF_INVALID_DISPLAY(displayId); - - auto error = mDisplayData[displayId].hwcDisplay->destroyLayer(layer); - RETURN_IF_HWC_ERROR(error, displayId); + auto expected = mDisplayData[displayId].hwcDisplay->createLayer(); + if (!expected.has_value()) { + auto error = std::move(expected).error(); + RETURN_IF_HWC_ERROR(error, displayId, nullptr); + } + return std::move(expected).value(); } bool HWComposer::isConnected(PhysicalDisplayId displayId) const { @@ -471,6 +466,7 @@ status_t HWComposer::setClientTarget(HalDisplayId displayId, uint32_t slot, status_t HWComposer::getDeviceCompositionChanges( HalDisplayId displayId, bool frameUsesClientComposition, + std::chrono::steady_clock::time_point earliestPresentTime, std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) { ATRACE_CALL(); @@ -487,12 +483,14 @@ status_t HWComposer::getDeviceCompositionChanges( hal::Error error = hal::Error::NONE; - // First try to skip validate altogether when there is no client - // composition. When there is client composition, since we haven't - // rendered to the client target yet, we should not attempt to skip - // validate. + // First try to skip validate altogether when we passed the earliest time + // to present and there is no client. Otherwise, we may present a frame too + // early or in case of client composition we first need to render the + // client target buffer. + const bool canSkipValidate = + std::chrono::steady_clock::now() >= earliestPresentTime && !frameUsesClientComposition; displayData.validateWasSkipped = false; - if (!frameUsesClientComposition) { + if (canSkipValidate) { sp<Fence> outPresentFence; uint32_t state = UINT32_MAX; error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state); @@ -556,7 +554,8 @@ sp<Fence> HWComposer::getLayerReleaseFence(HalDisplayId displayId, HWC2::Layer* return fence->second; } -status_t HWComposer::presentAndGetReleaseFences(HalDisplayId displayId) { +status_t HWComposer::presentAndGetReleaseFences( + HalDisplayId displayId, std::chrono::steady_clock::time_point earliestPresentTime) { ATRACE_CALL(); RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); @@ -572,6 +571,11 @@ status_t HWComposer::presentAndGetReleaseFences(HalDisplayId displayId) { return NO_ERROR; } + { + ATRACE_NAME("wait for earliest present time"); + std::this_thread::sleep_until(earliestPresentTime); + } + auto error = hwcDisplay->present(&displayData.lastPresentFence); RETURN_IF_HWC_ERROR_FOR("present", error, displayId, UNKNOWN_ERROR); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index f532e50df0..d0c0c1105a 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -116,9 +116,7 @@ public: virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0; // Attempts to create a new layer on this display - virtual HWC2::Layer* createLayer(HalDisplayId) = 0; - // Destroy a previously created layer - virtual void destroyLayer(HalDisplayId, HWC2::Layer*) = 0; + virtual std::shared_ptr<HWC2::Layer> createLayer(HalDisplayId) = 0; // Gets any required composition change requests from the HWC device. // @@ -129,13 +127,15 @@ public: // expected. virtual status_t getDeviceCompositionChanges( HalDisplayId, bool frameUsesClientComposition, + std::chrono::steady_clock::time_point earliestPresentTime, std::optional<DeviceRequestedChanges>* outChanges) = 0; virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target, ui::Dataspace) = 0; // Present layers to the display and read releaseFences. - virtual status_t presentAndGetReleaseFences(HalDisplayId) = 0; + virtual status_t presentAndGetReleaseFences( + HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime) = 0; // set power mode virtual status_t setPowerMode(PhysicalDisplayId, hal::PowerMode) = 0; @@ -262,19 +262,19 @@ public: void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override; // Attempts to create a new layer on this display - HWC2::Layer* createLayer(HalDisplayId) override; - // Destroy a previously created layer - void destroyLayer(HalDisplayId, HWC2::Layer*) override; + std::shared_ptr<HWC2::Layer> createLayer(HalDisplayId) override; status_t getDeviceCompositionChanges( HalDisplayId, bool frameUsesClientComposition, + std::chrono::steady_clock::time_point earliestPresentTime, std::optional<DeviceRequestedChanges>* outChanges) override; status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target, ui::Dataspace) override; // Present layers to the display and read releaseFences. - status_t presentAndGetReleaseFences(HalDisplayId) override; + status_t presentAndGetReleaseFences( + HalDisplayId, std::chrono::steady_clock::time_point earliestPresentTime) override; // set power mode status_t setPowerMode(PhysicalDisplayId, hal::PowerMode mode) override; diff --git a/services/surfaceflinger/EffectLayer.cpp b/services/surfaceflinger/EffectLayer.cpp index caef33848e..fd18c3bf31 100644 --- a/services/surfaceflinger/EffectLayer.cpp +++ b/services/surfaceflinger/EffectLayer.cpp @@ -57,19 +57,15 @@ std::vector<compositionengine::LayerFE::LayerSettings> EffectLayer::prepareClien return {}; } - std::optional<compositionengine::LayerFE::LayerSettings> shadowSettings = - prepareShadowClientComposition(*layerSettings, targetSettings.viewport, - targetSettings.dataspace); - if (shadowSettings) { - results.push_back(*shadowSettings); - } + // set the shadow for the layer if needed + prepareShadowClientComposition(*layerSettings, targetSettings.viewport); // If fill bounds are occluded or the fill color is invalid skip the fill settings. if (targetSettings.realContentIsVisible && fillsColor()) { // Set color for color fill settings. layerSettings->source.solidColor = getColor().rgb; results.push_back(*layerSettings); - } else if (hasBlur()) { + } else if (hasBlur() || drawShadows()) { results.push_back(*layerSettings); } diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 0033dbeed6..f19e2a7863 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -730,9 +730,11 @@ namespace impl { int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) { ATRACE_CALL(); std::scoped_lock lock(mMutex); + while (mPredictions.size() >= kMaxTokens) { + mPredictions.erase(mPredictions.begin()); + } const int64_t assignedToken = mCurrentToken++; - mPredictions[assignedToken] = {systemTime(), predictions}; - flushTokens(systemTime()); + mPredictions[assignedToken] = predictions; return assignedToken; } @@ -740,23 +742,11 @@ std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) std::scoped_lock lock(mMutex); auto predictionsIterator = mPredictions.find(token); if (predictionsIterator != mPredictions.end()) { - return predictionsIterator->second.predictions; + return predictionsIterator->second; } return {}; } -void TokenManager::flushTokens(nsecs_t flushTime) { - for (auto it = mPredictions.begin(); it != mPredictions.end();) { - if (flushTime - it->second.timestamp >= kMaxRetentionTime) { - it = mPredictions.erase(it); - } else { - // Tokens are ordered by time. If i'th token is within the retention time, then the - // i+1'th token will also be within retention time. - break; - } - } -} - FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid, JankClassificationThresholds thresholds) : mMaxDisplayFrames(kDefaultMaxDisplayFrames), diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index 0563a53cb1..42be55ae2c 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -92,11 +92,6 @@ struct TimelineItem { bool operator!=(const TimelineItem& other) const { return !(*this == other); } }; -struct TokenManagerPrediction { - nsecs_t timestamp = 0; - TimelineItem predictions; -}; - struct JankClassificationThresholds { // The various thresholds for App and SF. If the actual timestamp falls within the threshold // compared to prediction, we treat it as on time. @@ -334,11 +329,10 @@ private: void flushTokens(nsecs_t flushTime) REQUIRES(mMutex); - std::map<int64_t, TokenManagerPrediction> mPredictions GUARDED_BY(mMutex); + std::map<int64_t, TimelineItem> mPredictions GUARDED_BY(mMutex); int64_t mCurrentToken GUARDED_BY(mMutex); mutable std::mutex mMutex; - static constexpr nsecs_t kMaxRetentionTime = - std::chrono::duration_cast<std::chrono::nanoseconds>(120ms).count(); + static constexpr size_t kMaxTokens = 500; }; class FrameTimeline : public android::frametimeline::FrameTimeline { diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 0d673eab78..a7c870483b 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -129,10 +129,10 @@ Layer::Layer(const LayerCreationArgs& args) mCurrentState.frameRateSelectionPriority = PRIORITY_UNSET; mCurrentState.metadata = args.metadata; mCurrentState.shadowRadius = 0.f; - mCurrentState.treeHasFrameRateVote = false; mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID; mCurrentState.frameTimelineInfo = {}; mCurrentState.postTime = -1; + mCurrentState.destinationFrame.makeInvalid(); if (args.flags & ISurfaceComposerClient::eNoColorFill) { // Set an invalid color so there is no color fill. @@ -221,7 +221,10 @@ void Layer::removeRelativeZ(const std::vector<Layer*>& layersInTree) { } void Layer::removeFromCurrentState() { - mRemovedFromCurrentState = true; + if (!mRemovedFromCurrentState) { + mRemovedFromCurrentState = true; + mFlinger->mScheduler->deregisterLayer(this); + } mFlinger->markLayerPendingRemovalLocked(this); } @@ -246,7 +249,10 @@ void Layer::onRemovedFromCurrentState() { } void Layer::addToCurrentState() { - mRemovedFromCurrentState = false; + if (mRemovedFromCurrentState) { + mRemovedFromCurrentState = false; + mFlinger->mScheduler->registerLayer(this); + } for (const auto& child : mCurrentChildren) { child->addToCurrentState(); @@ -315,55 +321,6 @@ FloatRect Layer::getBounds(const Region& activeTransparentRegion) const { return reduce(mBounds, activeTransparentRegion); } -ui::Transform Layer::getBufferScaleTransform() const { - // If the layer is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g. - // it isFixedSize) then there may be additional scaling not accounted - // for in the layer transform. - if (!isFixedSize() || getBuffer() == nullptr) { - return {}; - } - - // If the layer is a buffer state layer, the active width and height - // could be infinite. In that case, return the effective transform. - const uint32_t activeWidth = getActiveWidth(getDrawingState()); - const uint32_t activeHeight = getActiveHeight(getDrawingState()); - if (activeWidth >= UINT32_MAX && activeHeight >= UINT32_MAX) { - return {}; - } - - int bufferWidth = getBuffer()->getWidth(); - int bufferHeight = getBuffer()->getHeight(); - - if (getBufferTransform() & NATIVE_WINDOW_TRANSFORM_ROT_90) { - std::swap(bufferWidth, bufferHeight); - } - - float sx = activeWidth / static_cast<float>(bufferWidth); - float sy = activeHeight / static_cast<float>(bufferHeight); - - ui::Transform extraParentScaling; - extraParentScaling.set(sx, 0, 0, sy); - return extraParentScaling; -} - -ui::Transform Layer::getTransformWithScale(const ui::Transform& bufferScaleTransform) const { - // We need to mirror this scaling to child surfaces or we will break the contract where WM can - // treat child surfaces as pixels in the parent surface. - if (!isFixedSize() || getBuffer() == nullptr) { - return mEffectiveTransform; - } - return mEffectiveTransform * bufferScaleTransform; -} - -FloatRect Layer::getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const { - // We need the pre scaled layer bounds when computing child bounds to make sure the child is - // cropped to its parent layer after any buffer transform scaling is applied. - if (!isFixedSize() || getBuffer() == nullptr) { - return mBounds; - } - return bufferScaleTransform.inverse().transform(mBounds); -} - void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float parentShadowRadius) { const State& s(getDrawingState()); @@ -400,11 +357,8 @@ void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform, // don't pass it to its children. const float childShadowRadius = canDrawShadows() ? 0.f : mEffectiveShadowRadius; - // Add any buffer scaling to the layer's children. - ui::Transform bufferScaleTransform = getBufferScaleTransform(); for (const sp<Layer>& child : mDrawingChildren) { - child->computeBounds(getBoundsPreScaling(bufferScaleTransform), - getTransformWithScale(bufferScaleTransform), childShadowRadius); + child->computeBounds(mBounds, mEffectiveTransform, childShadowRadius); } } @@ -619,6 +573,9 @@ std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCom layerSettings.geometry.boundaries = bounds; layerSettings.geometry.positionTransform = getTransform().asMatrix4(); + // skip drawing content if the targetSettings indicate the content will be occluded + layerSettings.skipContentDraw = !targetSettings.realContentIsVisible; + if (hasColorTransform()) { layerSettings.colorTransform = getColorTransform(); } @@ -635,65 +592,12 @@ std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCom layerSettings.blurRegionTransform = getActiveTransform(getDrawingState()).inverse().asMatrix4(); } - layerSettings.stretchEffect = getDrawingState().stretchEffect; + layerSettings.stretchEffect = getStretchEffect(); // Record the name of the layer for debugging further down the stack. layerSettings.name = getName(); return layerSettings; } -std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareShadowClientComposition( - const LayerFE::LayerSettings& casterLayerSettings, const Rect& layerStackRect, - ui::Dataspace outputDataspace) { - renderengine::ShadowSettings shadow = getShadowSettings(layerStackRect); - if (shadow.length <= 0.f) { - return {}; - } - - const float casterAlpha = casterLayerSettings.alpha; - const bool casterIsOpaque = ((casterLayerSettings.source.buffer.buffer != nullptr) && - casterLayerSettings.source.buffer.isOpaque); - - compositionengine::LayerFE::LayerSettings shadowLayer = casterLayerSettings; - - shadowLayer.shadow = shadow; - shadowLayer.geometry.boundaries = mBounds; // ignore transparent region - - // If the casting layer is translucent, we need to fill in the shadow underneath the layer. - // Otherwise the generated shadow will only be shown around the casting layer. - shadowLayer.shadow.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f); - shadowLayer.shadow.ambientColor *= casterAlpha; - shadowLayer.shadow.spotColor *= casterAlpha; - shadowLayer.sourceDataspace = outputDataspace; - shadowLayer.source.buffer.buffer = nullptr; - shadowLayer.source.buffer.fence = nullptr; - shadowLayer.frameNumber = 0; - shadowLayer.bufferId = 0; - shadowLayer.name = getName(); - - if (shadowLayer.shadow.ambientColor.a <= 0.f && shadowLayer.shadow.spotColor.a <= 0.f) { - return {}; - } - - float casterCornerRadius = shadowLayer.geometry.roundedCornersRadius; - const FloatRect& cornerRadiusCropRect = shadowLayer.geometry.roundedCornersCrop; - const FloatRect& casterRect = shadowLayer.geometry.boundaries; - - // crop used to set the corner radius may be larger than the content rect. Adjust the corner - // radius accordingly. - if (casterCornerRadius > 0.f) { - float cropRectOffset = std::max(std::abs(cornerRadiusCropRect.top - casterRect.top), - std::abs(cornerRadiusCropRect.left - casterRect.left)); - if (cropRectOffset > casterCornerRadius) { - casterCornerRadius = 0; - } else { - casterCornerRadius -= cropRectOffset; - } - shadowLayer.geometry.roundedCornersRadius = casterCornerRadius; - } - - return shadowLayer; -} - void Layer::prepareClearClientComposition(LayerFE::LayerSettings& layerSettings, bool blackout) const { layerSettings.source.buffer.buffer = nullptr; @@ -707,6 +611,9 @@ void Layer::prepareClearClientComposition(LayerFE::LayerSettings& layerSettings, layerSettings.name = getName(); } +// TODO(b/188891810): This method now only ever returns 0 or 1 layers so we should return +// std::optional instead of a vector. Additionally, we should consider removing +// this method entirely in favor of calling prepareClientComposition directly. std::vector<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompositionList( compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) { std::optional<compositionengine::LayerFE::LayerSettings> layerSettings = @@ -722,21 +629,10 @@ std::vector<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompo return {*layerSettings}; } - std::optional<compositionengine::LayerFE::LayerSettings> shadowSettings = - prepareShadowClientComposition(*layerSettings, targetSettings.viewport, - targetSettings.dataspace); - // There are no shadows to render. - if (!shadowSettings) { - return {*layerSettings}; - } - - // If the layer casts a shadow but the content casting the shadow is occluded, skip - // composing the non-shadow content and only draw the shadows. - if (targetSettings.realContentIsVisible) { - return {*shadowSettings, *layerSettings}; - } + // set the shadow for the layer if needed + prepareShadowClientComposition(*layerSettings, targetSettings.viewport); - return {*shadowSettings}; + return {*layerSettings}; } Hwc2::IComposerClient::Composition Layer::getCompositionType(const DisplayDevice& display) const { @@ -858,7 +754,12 @@ uint32_t Layer::doTransaction(uint32_t flags) { const State& s(getDrawingState()); State& c(getCurrentState()); - if (getActiveGeometry(c) != getActiveGeometry(s)) { + // Translates dest frame into scale and position updates. This helps align geometry calculations + // for BufferStateLayer with other layers. This should ideally happen in the client once client + // has the display orientation details from WM. + updateGeometry(); + + if (c.width != s.width || c.height != s.height || !(c.transform == s.transform)) { // invalidate and recompute the visible regions if needed flags |= Layer::eVisibleRegion; } @@ -907,8 +808,15 @@ void Layer::commitTransaction(State& stateToCommit) { // list. addSurfaceFrameDroppedForBuffer(bufferSurfaceFrame); } + const bool frameRateVoteChanged = + mDrawingState.frameRateForLayerTree != stateToCommit.frameRateForLayerTree; mDrawingState = stateToCommit; + if (frameRateVoteChanged) { + mFlinger->mScheduler->recordLayerHistory(this, systemTime(), + LayerHistory::LayerUpdateType::SetFrameRate); + } + // Set the present state for all bufferlessSurfaceFramesTX to Presented. The // bufferSurfaceFrameTX will be presented in latchBuffer. for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) { @@ -933,20 +841,18 @@ uint32_t Layer::setTransactionFlags(uint32_t flags) { } bool Layer::setPosition(float x, float y) { - if (mCurrentState.requested_legacy.transform.tx() == x && - mCurrentState.requested_legacy.transform.ty() == y) - return false; + if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) return false; mCurrentState.sequence++; // We update the requested and active position simultaneously because // we want to apply the position portion of the transform matrix immediately, // but still delay scaling when resizing a SCALING_MODE_FREEZE layer. - mCurrentState.requested_legacy.transform.set(x, y); + mCurrentState.transform.set(x, y); // Here we directly update the active state // unlike other setters, because we store it within // the transform, but use different latching rules. // b/38182305 - mCurrentState.active_legacy.transform.set(x, y); + mCurrentState.transform.set(x, y); mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); @@ -1065,6 +971,7 @@ bool Layer::setSize(uint32_t w, uint32_t h) { setDefaultBufferSize(mCurrentState.requested_legacy.w, mCurrentState.requested_legacy.h); return true; } + bool Layer::setAlpha(float alpha) { if (mCurrentState.color.a == alpha) return false; mCurrentState.sequence++; @@ -1143,8 +1050,7 @@ bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix, return false; } mCurrentState.sequence++; - mCurrentState.requested_legacy.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, - matrix.dsdy); + mCurrentState.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); return true; @@ -1313,8 +1219,7 @@ void Layer::updateTreeHasFrameRateVote() { }; // update parents and children about the vote - // First traverse the tree and count how many layers has votes. In addition - // activate the layers in Scheduler's LayerHistory for it to check for changes + // First traverse the tree and count how many layers has votes. int layersWithVote = 0; traverseTree([&layersWithVote](Layer* layer) { const auto layerVotedWithDefaultCompatibility = @@ -1334,20 +1239,11 @@ void Layer::updateTreeHasFrameRateVote() { } }); - // Now update the other layers + // Now we can update the tree frame rate vote for each layer in the tree + const bool treeHasFrameRateVote = layersWithVote > 0; bool transactionNeeded = false; - traverseTree([layersWithVote, &transactionNeeded, this](Layer* layer) { - const bool treeHasFrameRateVote = layersWithVote > 0; - if (layer->mCurrentState.treeHasFrameRateVote != treeHasFrameRateVote) { - layer->mCurrentState.sequence++; - layer->mCurrentState.treeHasFrameRateVote = treeHasFrameRateVote; - layer->mCurrentState.modified = true; - layer->setTransactionFlags(eTransactionNeeded); - transactionNeeded = true; - - mFlinger->mScheduler->recordLayerHistory(layer, systemTime(), - LayerHistory::LayerUpdateType::SetFrameRate); - } + traverseTree([treeHasFrameRateVote, &transactionNeeded](Layer* layer) { + transactionNeeded = layer->updateFrameRateForLayerTree(treeHasFrameRateVote); }); if (transactionNeeded) { @@ -1476,32 +1372,42 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer( return surfaceFrame; } -Layer::FrameRate Layer::getFrameRateForLayerTree() const { - const auto frameRate = getDrawingState().frameRate; +bool Layer::updateFrameRateForLayerTree(bool treeHasFrameRateVote) { + const auto updateCurrentState = [&](FrameRate frameRate) { + if (mCurrentState.frameRateForLayerTree == frameRate) { + return false; + } + mCurrentState.frameRateForLayerTree = frameRate; + mCurrentState.sequence++; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; + }; + + const auto frameRate = mCurrentState.frameRate; if (frameRate.rate.isValid() || frameRate.type == FrameRateCompatibility::NoVote) { - return frameRate; + return updateCurrentState(frameRate); } // This layer doesn't have a frame rate. Check if its ancestors have a vote - if (sp<Layer> parent = getParent(); parent) { - if (const auto parentFrameRate = parent->getFrameRateForLayerTree(); - parentFrameRate.rate.isValid()) { - return parentFrameRate; + for (sp<Layer> parent = getParent(); parent; parent = parent->getParent()) { + if (parent->mCurrentState.frameRate.rate.isValid()) { + return updateCurrentState(parent->mCurrentState.frameRate); } } // This layer and its ancestors don't have a frame rate. If one of successors // has a vote, return a NoVote for successors to set the vote - if (getDrawingState().treeHasFrameRateVote) { - return {Fps(0.0f), FrameRateCompatibility::NoVote}; + if (treeHasFrameRateVote) { + return updateCurrentState(FrameRate(Fps(0.0f), FrameRateCompatibility::NoVote)); } - return frameRate; + return updateCurrentState(frameRate); } -// ---------------------------------------------------------------------------- -// pageflip handling... -// ---------------------------------------------------------------------------- +Layer::FrameRate Layer::getFrameRateForLayerTree() const { + return getDrawingState().frameRateForLayerTree; +} bool Layer::isHiddenByPolicy() const { const State& s(mDrawingState); @@ -1560,20 +1466,20 @@ LayerDebugInfo Layer::getLayerDebugInfo(const DisplayDevice* display) const { info.mVisibleRegion = getVisibleRegion(display); info.mSurfaceDamageRegion = surfaceDamageRegion; info.mLayerStack = getLayerStack(); - info.mX = ds.active_legacy.transform.tx(); - info.mY = ds.active_legacy.transform.ty(); + info.mX = ds.transform.tx(); + info.mY = ds.transform.ty(); info.mZ = ds.z; - info.mWidth = ds.active_legacy.w; - info.mHeight = ds.active_legacy.h; + info.mWidth = ds.width; + info.mHeight = ds.height; info.mCrop = ds.crop; info.mColor = ds.color; info.mFlags = ds.flags; info.mPixelFormat = getPixelFormat(); info.mDataSpace = static_cast<android_dataspace>(getDataSpace()); - info.mMatrix[0][0] = ds.active_legacy.transform[0][0]; - info.mMatrix[0][1] = ds.active_legacy.transform[0][1]; - info.mMatrix[1][0] = ds.active_legacy.transform[1][0]; - info.mMatrix[1][1] = ds.active_legacy.transform[1][1]; + info.mMatrix[0][0] = ds.transform[0][0]; + info.mMatrix[0][1] = ds.transform[0][1]; + info.mMatrix[1][0] = ds.transform[1][0]; + info.mMatrix[1][1] = ds.transform[1][1]; { sp<const GraphicBuffer> buffer = getBuffer(); if (buffer != 0) { @@ -1770,8 +1676,7 @@ ssize_t Layer::removeChild(const sp<Layer>& layer) { void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) { for (const sp<Layer>& child : mDrawingChildren) { child->mDrawingParent = newParent; - child->computeBounds(newParent->mBounds, - newParent->getTransformWithScale(newParent->getBufferScaleTransform()), + child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform, newParent->mEffectiveShadowRadius); } } @@ -2115,16 +2020,37 @@ Layer::RoundedCornerState Layer::getRoundedCornerState() const { : RoundedCornerState(); } -renderengine::ShadowSettings Layer::getShadowSettings(const Rect& layerStackRect) const { +void Layer::prepareShadowClientComposition(LayerFE::LayerSettings& caster, + const Rect& layerStackRect) { renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings; + // Note: this preserves existing behavior of shadowing the entire layer and not cropping it if + // transparent regions are present. This may not be necessary since shadows are only cast by + // SurfaceFlinger's EffectLayers, which do not typically use transparent regions. + state.boundaries = mBounds; + // Shift the spot light x-position to the middle of the display and then // offset it by casting layer's screen pos. state.lightPos.x = (layerStackRect.width() / 2.f) - mScreenBounds.left; state.lightPos.y -= mScreenBounds.top; state.length = mEffectiveShadowRadius; - return state; + + if (state.length > 0.f) { + const float casterAlpha = caster.alpha; + const bool casterIsOpaque = + ((caster.source.buffer.buffer != nullptr) && caster.source.buffer.isOpaque); + + // If the casting layer is translucent, we need to fill in the shadow underneath the layer. + // Otherwise the generated shadow will only be shown around the casting layer. + state.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f); + state.ambientColor *= casterAlpha; + state.spotColor *= casterAlpha; + + if (state.ambientColor.a > 0.f && state.spotColor.a > 0.f) { + caster.shadow = state; + } + } } void Layer::commitChildList() { diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index a83408b7f0..66d70185a7 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -239,8 +239,8 @@ public: FrameRate frameRate; - // Indicates whether parents / children of this layer had set FrameRate - bool treeHasFrameRateVote; + // The combined frame rate of parents / children of this layer + FrameRate frameRateForLayerTree; // Set by window manager indicating the layer and all its children are // in a different orientation than the display. The hint suggests that @@ -276,6 +276,7 @@ public: StretchEffect stretchEffect; Rect bufferCrop; + Rect destinationFrame; }; /* @@ -484,12 +485,9 @@ public: // to avoid grabbing the lock again to avoid deadlock virtual bool isCreatedFromMainThread() const { return false; } - virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; } - virtual uint32_t getActiveWidth(const Layer::State& s) const { return s.active_legacy.w; } - virtual uint32_t getActiveHeight(const Layer::State& s) const { return s.active_legacy.h; } - virtual ui::Transform getActiveTransform(const Layer::State& s) const { - return s.active_legacy.transform; - } + uint32_t getActiveWidth(const Layer::State& s) const { return s.width; } + uint32_t getActiveHeight(const Layer::State& s) const { return s.height; } + ui::Transform getActiveTransform(const Layer::State& s) const { return s.transform; } virtual Region getActiveTransparentRegion(const Layer::State& s) const { return s.activeTransparentRegion_legacy; } @@ -649,16 +647,6 @@ public: // Compute bounds for the layer and cache the results. void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius); - // Returns the buffer scale transform if a scaling mode is set. - ui::Transform getBufferScaleTransform() const; - - // Get effective layer transform, taking into account all its parent transform with any - // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE. - ui::Transform getTransformWithScale(const ui::Transform& bufferScaleTransform) const; - - // Returns the bounds of the layer without any buffer scaling. - FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const; - int32_t getSequence() const override { return sequence; } // For tracing. @@ -776,8 +764,6 @@ public: // is ready to acquire a buffer. ui::Transform::RotationFlags getFixedTransformHint() const; - renderengine::ShadowSettings getShadowSettings(const Rect& layerStackRect) const; - /** * Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder * which will not emit children who have relativeZOrder to another layer, this method @@ -888,8 +874,10 @@ public: StretchEffect getStretchEffect() const; virtual bool setBufferCrop(const Rect& /* bufferCrop */) { return false; } + virtual bool setDestinationFrame(const Rect& /* destinationFrame */) { return false; } virtual std::atomic<int32_t>* getPendingBufferCounter() { return nullptr; } virtual std::string getPendingBufferCounterName() { return ""; } + virtual void updateGeometry() {} protected: friend class impl::SurfaceInterceptor; @@ -905,9 +893,6 @@ protected: virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom); virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition( compositionengine::LayerFE::ClientCompositionTargetSettings&); - virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareShadowClientComposition( - const LayerFE::LayerSettings&, const Rect& layerStackRect, - ui::Dataspace outputDataspace); virtual void preparePerFrameCompositionState(); virtual void commitTransaction(State& stateToCommit); virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit); @@ -933,6 +918,7 @@ protected: // Modifies the passed in layer settings to clear the contents. If the blackout flag is set, // the settings clears the content with a solid black fill. void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const; + void prepareShadowClientComposition(LayerFE::LayerSettings& caster, const Rect& layerStackRect); void prepareBasicGeometryCompositionState(); void prepareGeometryCompositionState(); @@ -1067,6 +1053,8 @@ private: // Fills in the frame and transform info for the InputWindowInfo void fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay); + bool updateFrameRateForLayerTree(bool treeHasFrameRateVote); + // Cached properties computed from drawing state // Effective transform taking into account parent transforms and any parent scaling, which is // a transform from the current layer coordinate space to display(screen) coordinate space. diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 00090d948a..653aca6cde 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -56,16 +56,14 @@ enum class samplingStep { noWorkNeeded, idleTimerWaiting, waitForQuietFrame, - waitForZeroPhase, waitForSamplePhase, sample }; -constexpr auto timeForRegionSampling = 5000000ns; -constexpr auto maxRegionSamplingSkips = 10; constexpr auto defaultRegionSamplingWorkDuration = 3ms; constexpr auto defaultRegionSamplingPeriod = 100ms; constexpr auto defaultRegionSamplingTimerTimeout = 100ms; +constexpr auto maxRegionSamplingDelay = 100ms; // TODO: (b/127403193) duration to string conversion could probably be constexpr template <typename Rep, typename Per> inline std::string toNsString(std::chrono::duration<Rep, Per> t) { @@ -99,97 +97,22 @@ RegionSamplingThread::EnvironmentTimingTunables::EnvironmentTimingTunables() { } } -struct SamplingOffsetCallback : VSyncSource::Callback { - SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler, - std::chrono::nanoseconds targetSamplingWorkDuration) - : mRegionSamplingThread(samplingThread), - mTargetSamplingWorkDuration(targetSamplingWorkDuration), - mVSyncSource(scheduler.makePrimaryDispSyncSource("SamplingThreadDispSyncListener", 0ns, - 0ns, - /*traceVsync=*/false)) { - mVSyncSource->setCallback(this); - } - - ~SamplingOffsetCallback() { stopVsyncListener(); } - - SamplingOffsetCallback(const SamplingOffsetCallback&) = delete; - SamplingOffsetCallback& operator=(const SamplingOffsetCallback&) = delete; - - void startVsyncListener() { - std::lock_guard lock(mMutex); - if (mVsyncListening) return; - - mPhaseIntervalSetting = Phase::ZERO; - mVSyncSource->setVSyncEnabled(true); - mVsyncListening = true; - } - - void stopVsyncListener() { - std::lock_guard lock(mMutex); - stopVsyncListenerLocked(); - } - -private: - void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ { - if (!mVsyncListening) return; - - mVSyncSource->setVSyncEnabled(false); - mVsyncListening = false; - } - - void onVSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/, - nsecs_t /*deadlineTimestamp*/) final { - std::unique_lock<decltype(mMutex)> lock(mMutex); - - if (mPhaseIntervalSetting == Phase::ZERO) { - ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase)); - mPhaseIntervalSetting = Phase::SAMPLING; - mVSyncSource->setDuration(mTargetSamplingWorkDuration, 0ns); - return; - } - - if (mPhaseIntervalSetting == Phase::SAMPLING) { - mPhaseIntervalSetting = Phase::ZERO; - mVSyncSource->setDuration(0ns, 0ns); - stopVsyncListenerLocked(); - lock.unlock(); - mRegionSamplingThread.notifySamplingOffset(); - return; - } - } - - RegionSamplingThread& mRegionSamplingThread; - const std::chrono::nanoseconds mTargetSamplingWorkDuration; - mutable std::mutex mMutex; - enum class Phase { - ZERO, - SAMPLING - } mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/ - = Phase::ZERO; - bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false; - std::unique_ptr<VSyncSource> mVSyncSource; -}; - -RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler, - const TimingTunables& tunables) +RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, const TimingTunables& tunables) : mFlinger(flinger), - mScheduler(scheduler), mTunables(tunables), mIdleTimer( "RegSampIdle", std::chrono::duration_cast<std::chrono::milliseconds>( mTunables.mSamplingTimerTimeout), [] {}, [this] { checkForStaleLuma(); }), - mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler, - tunables.mSamplingDuration)), - lastSampleTime(0ns) { + mLastSampleTime(0ns) { mThread = std::thread([this]() { threadMain(); }); pthread_setname_np(mThread.native_handle(), "RegionSampling"); mIdleTimer.start(); } -RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler) - : RegionSamplingThread(flinger, scheduler, +RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger) + : RegionSamplingThread(flinger, TimingTunables{defaultRegionSamplingWorkDuration, defaultRegionSamplingPeriod, defaultRegionSamplingTimerTimeout}) {} @@ -224,48 +147,46 @@ void RegionSamplingThread::removeListener(const sp<IRegionSamplingListener>& lis void RegionSamplingThread::checkForStaleLuma() { std::lock_guard lock(mThreadControlMutex); - if (mDiscardedFrames > 0) { - ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForZeroPhase)); - mDiscardedFrames = 0; - mPhaseCallback->startVsyncListener(); + if (mSampleRequestTime.has_value()) { + ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase)); + mSampleRequestTime.reset(); + mFlinger.scheduleRegionSamplingThread(); } } -void RegionSamplingThread::notifyNewContent() { - doSample(); -} - -void RegionSamplingThread::notifySamplingOffset() { - doSample(); +void RegionSamplingThread::onCompositionComplete( + std::optional<std::chrono::steady_clock::time_point> samplingDeadline) { + doSample(samplingDeadline); } -void RegionSamplingThread::doSample() { +void RegionSamplingThread::doSample( + std::optional<std::chrono::steady_clock::time_point> samplingDeadline) { std::lock_guard lock(mThreadControlMutex); - auto now = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC)); - if (lastSampleTime + mTunables.mSamplingPeriod > now) { + const auto now = std::chrono::steady_clock::now(); + if (mLastSampleTime + mTunables.mSamplingPeriod > now) { + // content changed, but we sampled not too long ago, so we need to sample some time in the + // future. ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting)); - if (mDiscardedFrames == 0) mDiscardedFrames++; + mSampleRequestTime = now; return; } - if (mDiscardedFrames < maxRegionSamplingSkips) { + if (!mSampleRequestTime.has_value() || now - *mSampleRequestTime < maxRegionSamplingDelay) { // If there is relatively little time left for surfaceflinger // until the next vsync deadline, defer this sampling work // to a later frame, when hopefully there will be more time. - const DisplayStatInfo stats = mScheduler.getDisplayStatInfo(systemTime()); - if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) { + if (samplingDeadline.has_value() && now + mTunables.mSamplingDuration > *samplingDeadline) { ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame)); - mDiscardedFrames++; + mSampleRequestTime = mSampleRequestTime.value_or(now); return; } } ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample)); - mDiscardedFrames = 0; - lastSampleTime = now; + mSampleRequestTime.reset(); + mLastSampleTime = now; mIdleTimer.reset(); - mPhaseCallback->stopVsyncListener(); mSampleRequested = true; mCondition.notify_one(); diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h index 86632db490..2231853fcb 100644 --- a/services/surfaceflinger/RegionSamplingThread.h +++ b/services/surfaceflinger/RegionSamplingThread.h @@ -63,9 +63,8 @@ public: struct EnvironmentTimingTunables : TimingTunables { EnvironmentTimingTunables(); }; - explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler, - const TimingTunables& tunables); - explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler); + explicit RegionSamplingThread(SurfaceFlinger& flinger, const TimingTunables& tunables); + explicit RegionSamplingThread(SurfaceFlinger& flinger); ~RegionSamplingThread(); @@ -76,12 +75,11 @@ public: // Remove the listener to stop receiving median luma notifications. void removeListener(const sp<IRegionSamplingListener>& listener); - // Notifies sampling engine that new content is available. This will trigger a sampling - // pass at some point in the future. - void notifyNewContent(); - - // Notifies the sampling engine that it has a good timing window in which to sample. - void notifySamplingOffset(); + // Notifies sampling engine that composition is done and new content is + // available, and the deadline for the sampling work on the main thread to + // be completed without eating the budget of another frame. + void onCompositionComplete( + std::optional<std::chrono::steady_clock::time_point> samplingDeadline); private: struct Descriptor { @@ -99,7 +97,7 @@ private: const sp<GraphicBuffer>& buffer, const Point& leftTop, const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation); - void doSample(); + void doSample(std::optional<std::chrono::steady_clock::time_point> samplingDeadline); void binderDied(const wp<IBinder>& who) override; void checkForStaleLuma(); @@ -107,20 +105,18 @@ private: void threadMain(); SurfaceFlinger& mFlinger; - Scheduler& mScheduler; const TimingTunables mTunables; scheduler::OneShotTimer mIdleTimer; - std::unique_ptr<SamplingOffsetCallback> const mPhaseCallback; - std::thread mThread; std::mutex mThreadControlMutex; std::condition_variable_any mCondition; bool mRunning GUARDED_BY(mThreadControlMutex) = true; bool mSampleRequested GUARDED_BY(mThreadControlMutex) = false; - uint32_t mDiscardedFrames GUARDED_BY(mThreadControlMutex) = 0; - std::chrono::nanoseconds lastSampleTime GUARDED_BY(mThreadControlMutex); + std::optional<std::chrono::steady_clock::time_point> mSampleRequestTime + GUARDED_BY(mThreadControlMutex); + std::chrono::steady_clock::time_point mLastSampleTime GUARDED_BY(mThreadControlMutex); std::mutex mSamplingMutex; std::unordered_map<wp<IBinder>, Descriptor, WpHash> mDescriptors GUARDED_BY(mSamplingMutex); diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp index ce5c31ac19..50b38c9cf6 100644 --- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp +++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp @@ -60,8 +60,7 @@ public: mRegistration.schedule({.workDuration = mWorkDuration.count(), .readyDuration = mReadyDuration.count(), .earliestVsync = mLastCallTime.count()}); - LOG_ALWAYS_FATAL_IF((scheduleResult != scheduler::ScheduleResult::Scheduled), - "Error scheduling callback: rc %X", scheduleResult); + LOG_ALWAYS_FATAL_IF((!scheduleResult.has_value()), "Error scheduling callback"); } void stop() { @@ -100,8 +99,7 @@ private: mRegistration.schedule({.workDuration = mWorkDuration.count(), .readyDuration = mReadyDuration.count(), .earliestVsync = vsyncTime}); - LOG_ALWAYS_FATAL_IF((scheduleResult != ScheduleResult::Scheduled), - "Error rescheduling callback: rc %X", scheduleResult); + LOG_ALWAYS_FATAL_IF(!scheduleResult.has_value(), "Error rescheduling callback"); } } diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index f4bc2a10cb..0563795c79 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -84,8 +84,11 @@ LayerHistory::LayerHistory(const RefreshRateConfigs& refreshRateConfigs) LayerHistory::~LayerHistory() = default; void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) { - auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type); std::lock_guard lock(mLock); + for (const auto& info : mLayerInfos) { + LOG_ALWAYS_FATAL_IF(info.first == layer, "%s already registered", layer->getName().c_str()); + } + auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type); mLayerInfos.emplace_back(layer, std::move(info)); } @@ -94,7 +97,7 @@ void LayerHistory::deregisterLayer(Layer* layer) { const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(), [layer](const auto& pair) { return pair.first == layer; }); - LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer); + LOG_ALWAYS_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer); const size_t i = static_cast<size_t>(it - mLayerInfos.begin()); if (i < mActiveLayersEnd) { @@ -111,7 +114,11 @@ void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now, const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(), [layer](const auto& pair) { return pair.first == layer; }); - LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer); + if (it == mLayerInfos.end()) { + // Offscreen layer + ALOGV("LayerHistory::record: %s not registered", layer->getName().c_str()); + return; + } const auto& info = it->second; const auto layerProps = LayerInfo::LayerProps{ diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h index ba03c89a1d..34cc3890d3 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.h +++ b/services/surfaceflinger/Scheduler/LayerInfo.h @@ -272,7 +272,7 @@ private: // Used for sanitizing the heuristic data. If two frames are less than // this period apart from each other they'll be considered as duplicates. - static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(120.f).getPeriodNsecs(); + static constexpr nsecs_t kMinPeriodBetweenFrames = Fps(240.f).getPeriodNsecs(); // Used for sanitizing the heuristic data. If two frames are more than // this period apart from each other, the interval between them won't be // taken into account when calculating average frame rate. diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp index 7ff0ddfab4..4d51125156 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.cpp +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -23,7 +23,6 @@ #include <utils/threads.h> #include <gui/DisplayEventReceiver.h> -#include <gui/IDisplayEventConnection.h> #include "EventThread.h" #include "FrameTimeline.h" @@ -33,27 +32,32 @@ namespace android::impl { void MessageQueue::Handler::dispatchRefresh() { - if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) { + if ((mEventMask.fetch_or(eventMaskRefresh) & eventMaskRefresh) == 0) { mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH)); } } void MessageQueue::Handler::dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp) { - if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) { + if ((mEventMask.fetch_or(eventMaskInvalidate) & eventMaskInvalidate) == 0) { mVsyncId = vsyncId; mExpectedVSyncTime = expectedVSyncTimestamp; mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE)); } } +bool MessageQueue::Handler::invalidatePending() { + constexpr auto pendingMask = eventMaskInvalidate | eventMaskRefresh; + return (mEventMask.load() & pendingMask) != 0; +} + void MessageQueue::Handler::handleMessage(const Message& message) { switch (message.what) { case INVALIDATE: - android_atomic_and(~eventMaskInvalidate, &mEventMask); + mEventMask.fetch_and(~eventMaskInvalidate); mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime); break; case REFRESH: - android_atomic_and(~eventMaskRefresh, &mEventMask); + mEventMask.fetch_and(~eventMaskRefresh); mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime); break; } @@ -106,7 +110,7 @@ void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, ns { std::lock_guard lock(mVsync.mutex); mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime); - mVsync.mScheduled = false; + mVsync.scheduled = false; } mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions( {targetWakeupTime, readyTime, vsyncTime}), @@ -131,9 +135,10 @@ void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) { ATRACE_CALL(); std::lock_guard lock(mVsync.mutex); mVsync.workDuration = workDuration; - if (mVsync.mScheduled) { - mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0, - mVsync.lastCallbackTime.count()}); + if (mVsync.scheduled) { + mVsync.expectedWakeupTime = mVsync.registration->schedule( + {mVsync.workDuration.get().count(), + /*readyDuration=*/0, mVsync.lastCallbackTime.count()}); } } @@ -176,10 +181,11 @@ void MessageQueue::invalidate() { } std::lock_guard lock(mVsync.mutex); - mVsync.mScheduled = true; - mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(), - .readyDuration = 0, - .earliestVsync = mVsync.lastCallbackTime.count()}); + mVsync.scheduled = true; + mVsync.expectedWakeupTime = + mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(), + .readyDuration = 0, + .earliestVsync = mVsync.lastCallbackTime.count()}); } void MessageQueue::refresh() { @@ -200,4 +206,19 @@ void MessageQueue::injectorCallback() { } } +std::optional<std::chrono::steady_clock::time_point> MessageQueue::nextExpectedInvalidate() { + if (mHandler->invalidatePending()) { + return std::chrono::steady_clock::now(); + } + + std::lock_guard lock(mVsync.mutex); + if (mVsync.scheduled) { + LOG_ALWAYS_FATAL_IF(!mVsync.expectedWakeupTime.has_value(), "callback was never scheduled"); + const auto expectedWakeupTime = std::chrono::nanoseconds(*mVsync.expectedWakeupTime); + return std::optional<std::chrono::steady_clock::time_point>(expectedWakeupTime); + } + + return std::nullopt; +} + } // namespace android::impl diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h index 2934af047c..58ce9b9a2f 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.h +++ b/services/surfaceflinger/Scheduler/MessageQueue.h @@ -72,6 +72,7 @@ public: virtual void postMessage(sp<MessageHandler>&&) = 0; virtual void invalidate() = 0; virtual void refresh() = 0; + virtual std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() = 0; }; // --------------------------------------------------------------------------- @@ -81,9 +82,13 @@ namespace impl { class MessageQueue : public android::MessageQueue { protected: class Handler : public MessageHandler { - enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 }; + enum : uint32_t { + eventMaskInvalidate = 0x1, + eventMaskRefresh = 0x2, + eventMaskTransaction = 0x4 + }; MessageQueue& mQueue; - int32_t mEventMask; + std::atomic<uint32_t> mEventMask; std::atomic<int64_t> mVsyncId; std::atomic<nsecs_t> mExpectedVSyncTime; @@ -92,6 +97,7 @@ protected: void handleMessage(const Message& message) override; virtual void dispatchRefresh(); virtual void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp); + virtual bool invalidatePending(); }; friend class Handler; @@ -107,7 +113,8 @@ protected: TracedOrdinal<std::chrono::nanoseconds> workDuration GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)}; std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0}; - bool mScheduled GUARDED_BY(mutex) = false; + bool scheduled GUARDED_BY(mutex) = false; + std::optional<nsecs_t> expectedWakeupTime GUARDED_BY(mutex); TracedOrdinal<int> value = {"VSYNC-sf", 0}; }; @@ -141,6 +148,8 @@ public: // sends REFRESH message at next VSYNC void refresh() override; + + std::optional<std::chrono::steady_clock::time_point> nextExpectedInvalidate() override; }; } // namespace impl diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h index 9d7110327b..b52706f727 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatch.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h @@ -16,8 +16,10 @@ #pragma once +#include <utils/Log.h> #include <utils/Timers.h> #include <functional> +#include <optional> #include <string> #include "StrongTyping.h" @@ -26,7 +28,8 @@ namespace android::scheduler { class TimeKeeper; class VSyncTracker; -enum class ScheduleResult { Scheduled, CannotSchedule, Error }; +using ScheduleResult = std::optional<nsecs_t>; + enum class CancelResult { Cancelled, TooLate, Error }; /* @@ -121,11 +124,8 @@ public: * * \param [in] token The callback to schedule. * \param [in] scheduleTiming The timing information for this schedule call - * \return A ScheduleResult::Scheduled if callback was scheduled. - * A ScheduleResult::CannotSchedule - * if (workDuration + readyDuration - earliestVsync) is in the past, - * or if a callback was dispatched for the predictedVsync already. A ScheduleResult::Error if - * there was another error. + * \return The expected callback time if a callback was scheduled. + * std::nullopt if the callback is not registered. */ virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0; diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index ca6ea27d14..28be962f2c 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp @@ -26,6 +26,20 @@ namespace android::scheduler { using base::StringAppendF; +namespace { +nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime, + const VSyncDispatch::ScheduleTiming& timing) { + return nextVsyncTime - timing.readyDuration - timing.workDuration; +} + +nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now, + const VSyncDispatch::ScheduleTiming& timing) { + const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom( + std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration)); + return getExpectedCallbackTime(nextVsyncTime, timing); +} +} // namespace + VSyncDispatch::~VSyncDispatch() = default; VSyncTracker::~VSyncTracker() = default; TimeKeeper::~TimeKeeper() = default; @@ -74,7 +88,7 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTim bool const wouldSkipAVsyncTarget = mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance)); if (wouldSkipAVsyncTarget) { - return ScheduleResult::Scheduled; + return getExpectedCallbackTime(nextVsyncTime, timing); } bool const alreadyDispatchedForVsync = mLastDispatchTime && @@ -89,7 +103,7 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTim auto const nextReadyTime = nextVsyncTime - timing.readyDuration; mScheduleTiming = timing; mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime}; - return ScheduleResult::Scheduled; + return getExpectedCallbackTime(nextVsyncTime, timing); } void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) { @@ -317,7 +331,7 @@ void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) { ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, ScheduleTiming scheduleTiming) { - auto result = ScheduleResult::Error; + ScheduleResult result; { std::lock_guard lock(mMutex); @@ -333,11 +347,11 @@ ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, auto const rearmImminent = now > mIntendedWakeupTime; if (CC_UNLIKELY(rearmImminent)) { callback->addPendingWorkloadUpdate(scheduleTiming); - return ScheduleResult::Scheduled; + return getExpectedCallbackTime(mTracker, now, scheduleTiming); } result = callback->schedule(scheduleTiming, mTracker, now); - if (result == ScheduleResult::CannotSchedule) { + if (!result.has_value()) { return result; } @@ -416,7 +430,7 @@ VSyncCallbackRegistration::~VSyncCallbackRegistration() { ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) { if (!mValidToken) { - return ScheduleResult::Error; + return std::nullopt; } return mDispatch.get().schedule(mToken, scheduleTiming); } diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index df98eb6b74..881ee5b8f4 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -23,7 +23,6 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "SurfaceFlinger.h" -#include "TraceUtils.h" #include <android-base/properties.h> #include <android/configuration.h> @@ -58,6 +57,7 @@ #include <gui/LayerMetadata.h> #include <gui/LayerState.h> #include <gui/Surface.h> +#include <gui/TraceUtils.h> #include <hidl/ServiceManagement.h> #include <layerproto/LayerProtoParser.h> #include <log/log.h> @@ -130,6 +130,7 @@ #include "SurfaceFlingerProperties.h" #include "SurfaceInterceptor.h" #include "TimeStats/TimeStats.h" +#include "TunnelModeEnabledReporter.h" #include "android-base/parseint.h" #include "android-base/stringprintf.h" #include "android-base/strings.h" @@ -287,6 +288,8 @@ const String16 sRotateSurfaceFlinger("android.permission.ROTATE_SURFACE_FLINGER" const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER"); const String16 sControlDisplayBrightness("android.permission.CONTROL_DISPLAY_BRIGHTNESS"); const String16 sDump("android.permission.DUMP"); +const String16 sCaptureBlackoutContent("android.permission.CAPTURE_BLACKOUT_CONTENT"); + const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled"; // --------------------------------------------------------------------------- @@ -306,6 +309,7 @@ ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat: Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB; ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888; bool SurfaceFlinger::useFrameRateApi; +bool SurfaceFlinger::enableSdrDimming; std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) { switch(displayColorSetting) { @@ -381,6 +385,12 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI mColorSpaceAgnosticDataspace = static_cast<ui::Dataspace>(color_space_agnostic_dataspace(Dataspace::UNKNOWN)); + mLayerCachingEnabled = [] { + const bool enable = + android::sysprop::SurfaceFlingerProperties::enable_layer_caching().value_or(false); + return base::GetBoolProperty(std::string("debug.sf.enable_layer_caching"), enable); + }(); + useContextPriority = use_context_priority(true); using Values = SurfaceFlingerProperties::primary_display_orientation_values; @@ -471,6 +481,9 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI base::SetProperty(KERNEL_IDLE_TIMER_PROP, mKernelIdleTimerEnabled ? "true" : "false"); mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0); + + // Debug property overrides ro. property + enableSdrDimming = property_get_bool("debug.sf.enable_sdr_dimming", enable_sdr_dimming(false)); } SurfaceFlinger::~SurfaceFlinger() = default; @@ -1456,6 +1469,26 @@ status_t SurfaceFlinger::removeFpsListener(const sp<gui::IFpsListener>& listener return NO_ERROR; } +status_t SurfaceFlinger::addTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener) { + if (!listener) { + return BAD_VALUE; + } + + mTunnelModeEnabledReporter->addListener(listener); + return NO_ERROR; +} + +status_t SurfaceFlinger::removeTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener) { + if (!listener) { + return BAD_VALUE; + } + + mTunnelModeEnabledReporter->removeListener(listener); + return NO_ERROR; +} + status_t SurfaceFlinger::getDisplayBrightnessSupport(const sp<IBinder>& displayToken, bool* outSupport) const { if (!displayToken || !outSupport) { @@ -1480,8 +1513,13 @@ status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, } return ftl::chain(schedule([=]() MAIN_THREAD { - if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { - return getHwComposer().setDisplayBrightness(*displayId, + if (const auto display = getDisplayDeviceLocked(displayToken)) { + if (enableSdrDimming) { + display->getCompositionDisplay() + ->setDisplayBrightness(brightness.sdrWhitePointNits, + brightness.displayBrightnessNits); + } + return getHwComposer().setDisplayBrightness(display->getPhysicalId(), brightness.displayBrightness); } else { ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get()); @@ -1703,11 +1741,11 @@ void SurfaceFlinger::setVsyncEnabled(bool enabled) { } SurfaceFlinger::FenceWithFenceTime SurfaceFlinger::previousFrameFence() { - // We are storing the last 2 present fences. If sf's phase offset is to be - // woken up before the actual vsync but targeting the next vsync, we need to check - // fence N-2 - return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? mPreviousPresentFences[0] - : mPreviousPresentFences[1]; + const auto now = systemTime(); + const auto vsyncPeriod = mScheduler->getDisplayStatInfo(now).vsyncPeriod; + const bool expectedPresentTimeIsTheNextVsync = mExpectedPresentTime - now <= vsyncPeriod; + return expectedPresentTimeIsTheNextVsync ? mPreviousPresentFences[0] + : mPreviousPresentFences[1]; } bool SurfaceFlinger::previousFramePending(int graceTimeMs) { @@ -1741,12 +1779,6 @@ nsecs_t SurfaceFlinger::calculateExpectedPresentTime(DisplayStatInfo stats) cons } void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime) { - const auto vsyncIn = [&] { - if (!ATRACE_ENABLED()) return 0.f; - return (expectedVSyncTime - systemTime()) / 1e6f; - }(); - - ATRACE_FORMAT("onMessageReceived %" PRId64 " vsyncIn %.2fms", vsyncId, vsyncIn); switch (what) { case MessageQueue::INVALIDATE: { onMessageInvalidate(vsyncId, expectedVSyncTime); @@ -1760,8 +1792,6 @@ void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t ex } void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime) { - ATRACE_CALL(); - const nsecs_t frameStart = systemTime(); // calculate the expected present time once and use the cached // value throughout this frame to make sure all layers are @@ -1776,6 +1806,13 @@ void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncT const nsecs_t lastScheduledPresentTime = mScheduledPresentTime; mScheduledPresentTime = expectedVSyncTime; + const auto vsyncIn = [&] { + if (!ATRACE_ENABLED()) return 0.f; + return (mExpectedPresentTime - systemTime()) / 1e6f; + }(); + ATRACE_FORMAT("onMessageInvalidate %" PRId64 " vsyncIn %.2fms%s", vsyncId, vsyncIn, + mExpectedPresentTime == expectedVSyncTime ? "" : " (adjusted)"); + // When Backpressure propagation is enabled we want to give a small grace period // for the present fence to fire instead of just giving up on this frame to handle cases // where present fence is just about to get signaled. @@ -1904,6 +1941,7 @@ void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncT mRefreshPending = true; onMessageRefresh(); } + notifyRegionSamplingThread(); } bool SurfaceFlinger::handleMessageTransaction() { @@ -2174,6 +2212,10 @@ void SurfaceFlinger::postComposition() { if (mFpsReporter) { mFpsReporter->dispatchLayerFps(); } + + if (mTunnelModeEnabledReporter) { + mTunnelModeEnabledReporter->updateTunnelModeStatus(); + } hdrInfoListeners.reserve(mHdrLayerInfoListeners.size()); for (auto& [key, value] : mHdrLayerInfoListeners) { if (value && value->hasListeners()) { @@ -2191,20 +2233,10 @@ void SurfaceFlinger::postComposition() { mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) { if (layer->isVisible() && compositionDisplay->belongsInOutput(layer->getCompositionEngineLayerFE())) { - bool isHdr = false; - switch (layer->getDataSpace()) { - case ui::Dataspace::BT2020: - case ui::Dataspace::BT2020_HLG: - case ui::Dataspace::BT2020_PQ: - case ui::Dataspace::BT2020_ITU: - case ui::Dataspace::BT2020_ITU_HLG: - case ui::Dataspace::BT2020_ITU_PQ: - isHdr = true; - break; - default: - isHdr = false; - break; - } + const Dataspace transfer = + static_cast<Dataspace>(layer->getDataSpace() & Dataspace::TRANSFER_MASK); + const bool isHdr = (transfer == Dataspace::TRANSFER_ST2084 || + transfer == Dataspace::TRANSFER_HLG); if (isHdr) { info.numberOfHdrLayers++; @@ -2306,10 +2338,6 @@ void SurfaceFlinger::postComposition() { } } - if (mLumaSampling && mRegionSamplingThread) { - mRegionSamplingThread->notifyNewContent(); - } - // Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the // side-effect of getTotalSize(), so we check that again here if (ATRACE_ENABLED()) { @@ -2643,6 +2671,7 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, builder.setGpuVirtualDisplayIdGenerator(mGpuVirtualDisplayIdGenerator); builder.setName(state.displayName); const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build()); + compositionDisplay->setLayerCachingEnabled(mLayerCachingEnabled); sp<compositionengine::DisplaySurface> displaySurface; sp<IGraphicBufferProducer> producer; @@ -2817,16 +2846,19 @@ void SurfaceFlinger::processDisplayChangesLocked() { } void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) { - /* - * Traversal of the children - * (perform the transaction for each of them if needed) - */ + // Commit display transactions + const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded; + if (displayTransactionNeeded) { + processDisplayChangesLocked(); + processDisplayHotplugEventsLocked(); + } - if ((transactionFlags & eTraversalNeeded) || mForceTraversal) { - mForceTraversal = false; + // Commit layer transactions. This needs to happen after display transactions are + // committed because some geometry logic relies on display orientation. + if ((transactionFlags & eTraversalNeeded) || mForceTraversal || displayTransactionNeeded) { mCurrentState.traverse([&](Layer* layer) { uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded); - if (!trFlags) return; + if (!trFlags && !displayTransactionNeeded) return; const uint32_t flags = layer->doTransaction(0); if (flags & Layer::eVisibleRegion) @@ -2838,15 +2870,7 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) { }); } - /* - * Perform display own transactions if needed - */ - - if (transactionFlags & eDisplayTransactionNeeded) { - processDisplayChangesLocked(); - processDisplayHotplugEventsLocked(); - } - + // Update transform hint if (transactionFlags & (eTransformHintUpdateNeeded | eDisplayTransactionNeeded)) { // The transform hint might have changed for some layers // (either because a display has changed, or because a layer @@ -3066,9 +3090,9 @@ void SurfaceFlinger::initScheduler(const DisplayDeviceState& displayState) { configs.late.sfWorkDuration); mRegionSamplingThread = - new RegionSamplingThread(*this, *mScheduler, - RegionSamplingThread::EnvironmentTimingTunables()); + new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables()); mFpsReporter = new FpsReporter(*mFrameTimeline, *this); + mTunnelModeEnabledReporter = new TunnelModeEnabledReporter(*this); // Dispatch a mode change request for the primary display on scheduler // initialization, so that the EventThreads always contain a reference to a // prior configuration. @@ -4040,6 +4064,11 @@ uint32_t SurfaceFlinger::setClientStateLocked( flags |= eTraversalNeeded; } } + if (what & layer_state_t::eDestinationFrameChanged) { + if (layer->setDestinationFrame(s.destinationFrame)) { + flags |= eTraversalNeeded; + } + } // This has to happen after we reparent children because when we reparent to null we remove // child layers from current state and remove its relative z. If the children are reparented in // the same transaction, then we have to make sure we reparent the children first so we do not @@ -5069,6 +5098,8 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: case SET_DISPLAY_CONTENT_SAMPLING_ENABLED: case GET_DISPLAYED_CONTENT_SAMPLE: + case ADD_TUNNEL_MODE_ENABLED_LISTENER: + case REMOVE_TUNNEL_MODE_ENABLED_LISTENER: case NOTIFY_POWER_BOOST: case SET_GLOBAL_SHADOW_SETTINGS: case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: { @@ -5188,9 +5219,9 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { code == IBinder::SYSPROPS_TRANSACTION) { return OK; } - // Numbers from 1000 to 1038 are currently used for backdoors. The code + // Numbers from 1000 to 1040 are currently used for backdoors. The code // in onTransact verifies that the user is root, and has access to use SF. - if (code >= 1000 && code <= 1039) { + if (code >= 1000 && code <= 1040) { ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code); return OK; } @@ -5587,6 +5618,45 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r mScheduler->onFrameRateOverridesChanged(mAppConnectionHandle, displayId); return NO_ERROR; } + // Toggle caching feature + // First argument is an int32 - nonzero enables caching and zero disables caching + // Second argument is an optional uint64 - if present, then limits enabling/disabling + // caching to a particular physical display + case 1040: { + status_t error = + schedule([&] { + n = data.readInt32(); + std::optional<PhysicalDisplayId> inputId = std::nullopt; + if (uint64_t inputDisplayId; + data.readUint64(&inputDisplayId) == NO_ERROR) { + const auto token = getPhysicalDisplayToken( + static_cast<PhysicalDisplayId>(inputDisplayId)); + if (!token) { + ALOGE("No display with id: %" PRIu64, inputDisplayId); + return NAME_NOT_FOUND; + } + + inputId = std::make_optional<PhysicalDisplayId>(inputDisplayId); + } + { + Mutex::Autolock lock(mStateLock); + mLayerCachingEnabled = n != 0; + for (const auto& [_, display] : mDisplays) { + if (!inputId || *inputId == display->getPhysicalId()) { + display->enableLayerCaching(mLayerCachingEnabled); + } + } + } + return OK; + }).get(); + + if (error != OK) { + return error; + } + invalidateHwcGeometry(); + repaintEverything(); + return NO_ERROR; + } } } return err; @@ -5682,6 +5752,14 @@ static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) { } } +static bool hasCaptureBlackoutContentPermission() { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + return uid == AID_GRAPHICS || uid == AID_SYSTEM || + PermissionCache::checkPermission(sCaptureBlackoutContent, pid, uid); +} + static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) { IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); @@ -5852,6 +5930,10 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, Rect layerStackSpaceRect; ui::Dataspace dataspace; bool captureSecureLayers; + + // Call this before holding mStateLock to avoid any deadlocking. + bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission(); + { Mutex::Autolock lock(mStateLock); @@ -5861,9 +5943,8 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, return NAME_NOT_FOUND; } - const int uid = IPCThreadState::self()->getCallingUid(); - const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM; - if (!forSystem && parent->getCurrentState().flags & layer_state_t::eLayerSecure) { + if (!canCaptureBlackoutContent && + parent->getCurrentState().flags & layer_state_t::eLayerSecure) { ALOGW("Attempting to capture secure layer: PERMISSION_DENIED"); return PERMISSION_DENIED; } @@ -6013,8 +6094,7 @@ status_t SurfaceFlinger::captureScreenCommon( return BAD_VALUE; } - const int uid = IPCThreadState::self()->getCallingUid(); - const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM; + bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission(); static_cast<void>(schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable { if (mRefreshPending) { @@ -6034,8 +6114,9 @@ status_t SurfaceFlinger::captureScreenCommon( status_t result = NO_ERROR; renderArea->render([&] { - result = renderScreenImplLocked(*renderArea, traverseLayers, buffer, forSystem, - regionSampling, grayscale, captureResults); + result = renderScreenImplLocked(*renderArea, traverseLayers, buffer, + canCaptureBlackoutContent, regionSampling, grayscale, + captureResults); }); captureResults.result = result; @@ -6047,8 +6128,9 @@ status_t SurfaceFlinger::captureScreenCommon( status_t SurfaceFlinger::renderScreenImplLocked( const RenderArea& renderArea, TraverseLayersFunction traverseLayers, - const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool forSystem, - bool regionSampling, bool grayscale, ScreenCaptureResults& captureResults) { + const std::shared_ptr<renderengine::ExternalTexture>& buffer, + bool canCaptureBlackoutContent, bool regionSampling, bool grayscale, + ScreenCaptureResults& captureResults) { ATRACE_CALL(); traverseLayers([&](Layer* layer) { @@ -6061,7 +6143,7 @@ status_t SurfaceFlinger::renderScreenImplLocked( // We allow the system server to take screenshots of secure layers for // use in situations like the Screen-rotation animation and place // the impetus on WindowManager to not persist them. - if (captureResults.capturedSecureLayers && !forSystem) { + if (captureResults.capturedSecureLayers && !canCaptureBlackoutContent) { ALOGW("FB is protected: PERMISSION_DENIED"); return PERMISSION_DENIED; } @@ -6401,13 +6483,17 @@ wp<Layer> SurfaceFlinger::fromHandleLocked(const sp<IBinder>& handle) const { void SurfaceFlinger::onLayerFirstRef(Layer* layer) { mNumLayers++; - mScheduler->registerLayer(layer); + if (!layer->isRemovedFromCurrentState()) { + mScheduler->registerLayer(layer); + } } void SurfaceFlinger::onLayerDestroyed(Layer* layer) { - mScheduler->deregisterLayer(layer); mNumLayers--; removeFromOffscreenLayers(layer); + if (!layer->isRemovedFromCurrentState()) { + mScheduler->deregisterLayer(layer); + } } // WARNING: ONLY CALL THIS FROM LAYER DTOR @@ -6716,6 +6802,19 @@ sp<Layer> SurfaceFlinger::handleLayerCreatedLocked(const sp<IBinder>& handle, bo return layer; } + +void SurfaceFlinger::scheduleRegionSamplingThread() { + static_cast<void>(schedule([&] { notifyRegionSamplingThread(); })); +} + +void SurfaceFlinger::notifyRegionSamplingThread() { + if (!mLumaSampling || !mRegionSamplingThread) { + return; + } + + mRegionSamplingThread->onCompositionComplete(mEventQueue->nextExpectedInvalidate()); +} + } // namespace android #if defined(__gl_h_) diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 4bbdd48035..c3820053c1 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -90,6 +90,7 @@ namespace android { class Client; class EventThread; class FpsReporter; +class TunnelModeEnabledReporter; class HdrLayerInfoReporter; class HWComposer; struct SetInputWindowsListener; @@ -271,6 +272,10 @@ public: static constexpr SkipInitializationTag SkipInitialization; + // Whether or not SDR layers should be dimmed to the desired SDR white point instead of + // being treated as native display brightness + static bool enableSdrDimming; + // must be called before clients can connect void init() ANDROID_API; @@ -355,6 +360,7 @@ private: friend class BufferStateLayer; friend class Client; friend class FpsReporter; + friend class TunnelModeEnabledReporter; friend class Layer; friend class MonitoredProducer; friend class RefreshRateOverlay; @@ -669,6 +675,10 @@ private: status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override; status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) override; status_t removeFpsListener(const sp<gui::IFpsListener>& listener) override; + status_t addTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener) override; + status_t removeTunnelModeEnabledListener( + const sp<gui::ITunnelModeEnabledListener>& listener) override; status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, ui::DisplayModeId displayModeId, bool allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax, @@ -911,8 +921,8 @@ private: const sp<IScreenCaptureListener>&); status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction, const std::shared_ptr<renderengine::ExternalTexture>&, - bool forSystem, bool regionSampling, bool grayscale, - ScreenCaptureResults&); + bool canCaptureBlackoutContent, bool regionSampling, + bool grayscale, ScreenCaptureResults&); sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock); sp<DisplayDevice> getDisplayById(DisplayId displayId) const REQUIRES(mStateLock); @@ -1236,6 +1246,7 @@ private: int mDebugRegion = 0; bool mDebugDisableHWC = false; bool mDebugDisableTransformHint = false; + bool mLayerCachingEnabled = false; volatile nsecs_t mDebugInTransaction = 0; bool mForceFullDamage = false; bool mPropagateBackpressureClientComposition = false; @@ -1363,6 +1374,7 @@ private: bool mLumaSampling = true; sp<RegionSamplingThread> mRegionSamplingThread; sp<FpsReporter> mFpsReporter; + sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter; ui::DisplayPrimaries mInternalDisplayPrimaries; const float mInternalDisplayDensity; @@ -1428,6 +1440,9 @@ private: REQUIRES(mStateLock); std::atomic<ui::Transform::RotationFlags> mDefaultDisplayTransformHint; + + void scheduleRegionSamplingThread(); + void notifyRegionSamplingThread(); }; } // namespace android diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp index b3dca789e2..4a69c8f0c3 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.cpp +++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp @@ -321,6 +321,10 @@ bool use_frame_rate_api(bool defaultValue) { return defaultValue; } +bool enable_sdr_dimming(bool defaultValue) { + return SurfaceFlingerProperties::enable_sdr_dimming().value_or(defaultValue); +} + int32_t display_update_imminent_timeout_ms(int32_t defaultValue) { auto temp = SurfaceFlingerProperties::display_update_imminent_timeout_ms(); if (temp.has_value()) { diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h index b19d216268..039d316a95 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.h +++ b/services/surfaceflinger/SurfaceFlingerProperties.h @@ -102,6 +102,8 @@ bool enable_frame_rate_override(bool defaultValue); bool enable_layer_caching(bool defaultValue); +bool enable_sdr_dimming(bool defaultValue); + } // namespace sysprop } // namespace android #endif // SURFACEFLINGERPROPERTIES_H_ diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp index 113f463c39..c5f1598d15 100644 --- a/services/surfaceflinger/SurfaceInterceptor.cpp +++ b/services/surfaceflinger/SurfaceInterceptor.cpp @@ -130,8 +130,8 @@ void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment, transaction->set_animation(layerFlags & BnSurfaceComposer::eAnimation); const int32_t layerId(getLayerId(layer)); - addPositionLocked(transaction, layerId, layer->mCurrentState.active_legacy.transform.tx(), - layer->mCurrentState.active_legacy.transform.ty()); + addPositionLocked(transaction, layerId, layer->mCurrentState.transform.tx(), + layer->mCurrentState.transform.ty()); addDepthLocked(transaction, layerId, layer->mCurrentState.z); addAlphaLocked(transaction, layerId, layer->mCurrentState.color.a); addTransparentRegionLocked(transaction, layerId, diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index 10d58a6087..d6a0787ddc 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -351,12 +351,16 @@ void TimeStats::recordDisplayEventConnectionCount(int32_t count) { std::max(mTimeStats.displayEventConnectionsCountLegacy, count); } +static int32_t toMs(nsecs_t nanos) { + int64_t millis = + std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::nanoseconds(nanos)) + .count(); + millis = std::clamp(millis, int64_t(INT32_MIN), int64_t(INT32_MAX)); + return static_cast<int32_t>(millis); +} + static int32_t msBetween(nsecs_t start, nsecs_t end) { - int64_t delta = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::nanoseconds(end - start)) - .count(); - delta = std::clamp(delta, int64_t(INT32_MIN), int64_t(INT32_MAX)); - return static_cast<int32_t>(delta); + return toMs(end - start); } void TimeStats::recordFrameDuration(nsecs_t startTime, nsecs_t endTime) { @@ -829,10 +833,9 @@ void TimeStats::incrementJankyFrames(const JankyFramesInfo& info) { // TimeStats Histograms only retain positive values, so we don't need to check if these // deadlines were really missed if we know that the frame had jank, since deadlines // that were met will be dropped. - timelineStats.displayDeadlineDeltas.insert(static_cast<int32_t>(info.displayDeadlineDelta)); - timelineStats.displayPresentDeltas.insert(static_cast<int32_t>(info.displayPresentJitter)); - timeStatsLayer.deltas["appDeadlineDeltas"].insert( - static_cast<int32_t>(info.appDeadlineDelta)); + timelineStats.displayDeadlineDeltas.insert(toMs(info.displayDeadlineDelta)); + timelineStats.displayPresentDeltas.insert(toMs(info.displayPresentJitter)); + timeStatsLayer.deltas["appDeadlineDeltas"].insert(toMs(info.appDeadlineDelta)); } } diff --git a/services/surfaceflinger/TunnelModeEnabledReporter.cpp b/services/surfaceflinger/TunnelModeEnabledReporter.cpp new file mode 100644 index 0000000000..1b3ddf7a00 --- /dev/null +++ b/services/surfaceflinger/TunnelModeEnabledReporter.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "TunnelModeEnabledReporter" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <algorithm> + +#include "Layer.h" +#include "SurfaceFlinger.h" +#include "TunnelModeEnabledReporter.h" + +namespace android { + +TunnelModeEnabledReporter::TunnelModeEnabledReporter(SurfaceFlinger& flinger) : mFlinger(flinger) {} + +void TunnelModeEnabledReporter::updateTunnelModeStatus() { + bool tunnelModeEnabled = false; + mFlinger.mCurrentState.traverse([&](Layer* layer) { + auto& currentState = layer->getCurrentState(); + if (currentState.sidebandStream != nullptr) { + tunnelModeEnabled = true; + return; + } + }); + dispatchTunnelModeEnabled(tunnelModeEnabled); +} + +void TunnelModeEnabledReporter::dispatchTunnelModeEnabled(bool tunnelModeEnabled) { + std::vector<sp<gui::ITunnelModeEnabledListener>> localListeners; + { + std::scoped_lock lock(mMutex); + if (mTunnelModeEnabled == tunnelModeEnabled) { + return; + } + mTunnelModeEnabled = tunnelModeEnabled; + + std::transform(mListeners.begin(), mListeners.end(), std::back_inserter(localListeners), + [](const std::pair<wp<IBinder>, sp<gui::ITunnelModeEnabledListener>>& + entry) { return entry.second; }); + } + + for (sp<gui::ITunnelModeEnabledListener>& listener : localListeners) { + listener->onTunnelModeEnabledChanged(tunnelModeEnabled); + } +} + +void TunnelModeEnabledReporter::binderDied(const wp<IBinder>& who) { + std::scoped_lock lock(mMutex); + mListeners.erase(who); +} + +void TunnelModeEnabledReporter::addListener(const sp<gui::ITunnelModeEnabledListener>& listener) { + sp<IBinder> asBinder = IInterface::asBinder(listener); + asBinder->linkToDeath(this); + bool tunnelModeEnabled = false; + { + std::scoped_lock lock(mMutex); + mListeners.emplace(wp<IBinder>(asBinder), listener); + tunnelModeEnabled = mTunnelModeEnabled; + } + listener->onTunnelModeEnabledChanged(tunnelModeEnabled); +} + +void TunnelModeEnabledReporter::removeListener( + const sp<gui::ITunnelModeEnabledListener>& listener) { + std::lock_guard lock(mMutex); + mListeners.erase(wp<IBinder>(IInterface::asBinder(listener))); +} + +} // namespace android diff --git a/services/surfaceflinger/TunnelModeEnabledReporter.h b/services/surfaceflinger/TunnelModeEnabledReporter.h new file mode 100644 index 0000000000..d55507a8e9 --- /dev/null +++ b/services/surfaceflinger/TunnelModeEnabledReporter.h @@ -0,0 +1,66 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/thread_annotations.h> +#include <android/gui/ITunnelModeEnabledListener.h> +#include <binder/IBinder.h> + +#include <unordered_map> + +namespace android { + +class Layer; +class SurfaceFlinger; + +class TunnelModeEnabledReporter : public IBinder::DeathRecipient { +public: + TunnelModeEnabledReporter(SurfaceFlinger& flinger); + + // Checks if there is a tunnel mode enabled state change and if so, dispatches the updated + // tunnel mode enabled/disabled state to the registered listeners + // This method performs layer stack traversals, so mStateLock must be held when calling this + // method. + void updateTunnelModeStatus(); + + // Dispatches tunnelModeEnabled to all registered listeners + void dispatchTunnelModeEnabled(bool tunnelModeEnabled); + + // Override for IBinder::DeathRecipient + void binderDied(const wp<IBinder>&) override; + + // Registers a TunnelModeEnabled listener + void addListener(const sp<gui::ITunnelModeEnabledListener>& listener); + + // Deregisters a TunnelModeEnabled listener + void removeListener(const sp<gui::ITunnelModeEnabledListener>& listener); + +private: + mutable std::mutex mMutex; + struct WpHash { + size_t operator()(const wp<IBinder>& p) const { + return std::hash<IBinder*>()(p.unsafe_get()); + } + }; + + SurfaceFlinger& mFlinger; + std::unordered_map<wp<IBinder>, sp<gui::ITunnelModeEnabledListener>, WpHash> mListeners + GUARDED_BY(mMutex); + bool mTunnelModeEnabled GUARDED_BY(mMutex) = false; +}; + +} // namespace android diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index ee5542d628..78f8a2f0ae 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -463,3 +463,12 @@ prop { access: Readonly prop_name: "ro.surface_flinger.enable_layer_caching" } + +# Enables SDR layer dimming +prop { + api_name: "enable_sdr_dimming" + type: Boolean + scope: Public + access: Readonly + prop_name: "ro.surface_flinger.enable_sdr_dimming" +}
\ No newline at end of file diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt index 47e14f656b..9c567d6afa 100644 --- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt +++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt @@ -53,6 +53,10 @@ props { prop_name: "ro.surface_flinger.protected_contents" } prop { + api_name: "enable_sdr_dimming" + prop_name: "ro.surface_flinger.enable_sdr_dimming" + } + prop { api_name: "force_hwc_copy_for_virtual_displays" prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays" } diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp index 2828d61a76..43d957cf7a 100644 --- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp @@ -273,6 +273,198 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildCrop) { } } +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusBufferRotationTransform) { + sp<SurfaceControl> layer; + sp<SurfaceControl> parent; + ASSERT_NO_FATAL_FAILURE( + parent = LayerTransactionTest::createLayer("parent", 0, 0, + ISurfaceComposerClient::eFXSurfaceEffect)); + + const uint32_t bufferWidth = 1500; + const uint32_t bufferHeight = 300; + + const uint32_t layerWidth = 300; + const uint32_t layerHeight = 1500; + + const uint32_t testArea = 4; + const float cornerRadius = 120.0f; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", bufferWidth, bufferHeight)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, bufferWidth, bufferHeight)); + + Transaction() + .reparent(layer, parent) + .setColor(parent, half3(0, 1, 0)) + .setCrop(parent, Rect(0, 0, layerWidth, layerHeight)) + .setCornerRadius(parent, cornerRadius) + + .setTransform(layer, ui::Transform::ROT_90) + .setDestinationFrame(layer, Rect(0, 0, layerWidth, layerHeight)) + .apply(); + { + auto shot = getScreenCapture(); + // Corners are transparent + // top-left + shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK); + // top-right + shot->expectColor(Rect(layerWidth - testArea, 0, layerWidth, testArea), Color::BLACK); + // bottom-left + shot->expectColor(Rect(0, layerHeight - testArea, testArea, layerHeight), Color::BLACK); + // bottom-right + shot->expectColor(Rect(layerWidth - testArea, layerHeight - testArea, layerWidth, + layerHeight), + Color::BLACK); + + // Area after corner radius is solid + // top-left to top-right under the corner + shot->expectColor(Rect(0, cornerRadius, layerWidth, cornerRadius + testArea), Color::RED); + // bottom-left to bottom-right above the corner + shot->expectColor(Rect(0, layerHeight - cornerRadius - testArea, layerWidth, + layerHeight - cornerRadius), + Color::RED); + // left side after the corner + shot->expectColor(Rect(cornerRadius, 0, cornerRadius + testArea, layerHeight), Color::RED); + // right side before the corner + shot->expectColor(Rect(layerWidth - cornerRadius - testArea, 0, layerWidth - cornerRadius, + layerHeight), + Color::RED); + } +} + +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusBufferCropTransform) { + sp<SurfaceControl> layer; + sp<SurfaceControl> parent; + ASSERT_NO_FATAL_FAILURE( + parent = LayerTransactionTest::createLayer("parent", 0, 0, + ISurfaceComposerClient::eFXSurfaceEffect)); + + const uint32_t bufferWidth = 150 * 2; + const uint32_t bufferHeight = 750 * 2; + + const Rect bufferCrop(0, 0, 150, 750); + + const uint32_t layerWidth = 300; + const uint32_t layerHeight = 1500; + + const uint32_t testArea = 4; + const float cornerRadius = 120.0f; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", bufferWidth, bufferHeight)); + ASSERT_NO_FATAL_FAILURE(fillLayerQuadrant(layer, bufferWidth, bufferHeight, Color::RED, + Color::BLACK, Color::GREEN, Color::BLUE)); + + Transaction() + .reparent(layer, parent) + .setColor(parent, half3(0, 1, 0)) + .setCrop(parent, Rect(0, 0, layerWidth, layerHeight)) + .setCornerRadius(parent, cornerRadius) + + .setBufferCrop(layer, bufferCrop) + .setDestinationFrame(layer, Rect(0, 0, layerWidth, layerHeight)) + .apply(); + { + auto shot = getScreenCapture(); + // Corners are transparent + // top-left + shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK); + // top-right + shot->expectColor(Rect(layerWidth - testArea, 0, layerWidth, testArea), Color::BLACK); + // bottom-left + shot->expectColor(Rect(0, layerHeight - testArea, testArea, layerHeight), Color::BLACK); + // bottom-right + shot->expectColor(Rect(layerWidth - testArea, layerHeight - testArea, layerWidth, + layerHeight), + Color::BLACK); + + // Area after corner radius is solid + // since the buffer is scaled, there will blending so adjust some of the bounds when + // checking. + float adjustedCornerRadius = cornerRadius + 15; + float adjustedLayerHeight = layerHeight - 15; + float adjustedLayerWidth = layerWidth - 15; + + // top-left to top-right under the corner + shot->expectColor(Rect(15, adjustedCornerRadius, adjustedLayerWidth, + adjustedCornerRadius + testArea), + Color::RED); + // bottom-left to bottom-right above the corner + shot->expectColor(Rect(15, adjustedLayerHeight - adjustedCornerRadius - testArea, + adjustedLayerWidth, adjustedLayerHeight - adjustedCornerRadius), + Color::RED); + // left side after the corner + shot->expectColor(Rect(adjustedCornerRadius, 15, adjustedCornerRadius + testArea, + adjustedLayerHeight), + Color::RED); + // right side before the corner + shot->expectColor(Rect(adjustedLayerWidth - adjustedCornerRadius - testArea, 15, + adjustedLayerWidth - adjustedCornerRadius, adjustedLayerHeight), + Color::RED); + } +} + +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildBufferRotationTransform) { + sp<SurfaceControl> layer; + sp<SurfaceControl> parent; + ASSERT_NO_FATAL_FAILURE( + parent = LayerTransactionTest::createLayer("parent", 0, 0, + ISurfaceComposerClient::eFXSurfaceEffect)); + + const uint32_t bufferWidth = 1500; + const uint32_t bufferHeight = 300; + + const uint32_t layerWidth = 300; + const uint32_t layerHeight = 1500; + + const uint32_t testArea = 4; + const float cornerRadius = 120.0f; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", bufferWidth, bufferHeight)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::BLUE, bufferWidth, bufferHeight)); + + sp<SurfaceControl> child; + ASSERT_NO_FATAL_FAILURE(child = createLayer("child", bufferWidth, bufferHeight)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::RED, bufferWidth, bufferHeight)); + + Transaction() + .reparent(layer, parent) + .reparent(child, layer) + .setColor(parent, half3(0, 1, 0)) + .setCrop(parent, Rect(0, 0, layerWidth, layerHeight)) + .setCornerRadius(parent, cornerRadius) /* */ + + .setTransform(layer, ui::Transform::ROT_90) + .setDestinationFrame(layer, Rect(0, 0, layerWidth, layerHeight)) + + .setTransform(child, ui::Transform::ROT_90) + .setDestinationFrame(child, Rect(0, 0, layerWidth, layerHeight)) + .apply(); + { + auto shot = getScreenCapture(); + // Corners are transparent + // top-left + shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK); + // top-right + shot->expectColor(Rect(layerWidth - testArea, 0, layerWidth, testArea), Color::BLACK); + // bottom-left + shot->expectColor(Rect(0, layerHeight - testArea, testArea, layerHeight), Color::BLACK); + // bottom-right + shot->expectColor(Rect(layerWidth - testArea, layerHeight - testArea, layerWidth, + layerHeight), + Color::BLACK); + + // Area after corner radius is solid + // top-left to top-right under the corner + shot->expectColor(Rect(0, cornerRadius, layerWidth, cornerRadius + testArea), Color::RED); + // bottom-left to bottom-right above the corner + shot->expectColor(Rect(0, layerHeight - cornerRadius - testArea, layerWidth, + layerHeight - cornerRadius), + Color::RED); + // left side after the corner + shot->expectColor(Rect(cornerRadius, 0, cornerRadius + testArea, layerHeight), Color::RED); + // right side before the corner + shot->expectColor(Rect(layerWidth - cornerRadius - testArea, 0, layerWidth - cornerRadius, + layerHeight), + Color::RED); + } +} + TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusSimple) { if (!deviceSupportsBlurs()) GTEST_SKIP(); if (!deviceUsesSkiaRenderEngine()) GTEST_SKIP(); diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp index 4753362292..34c918261a 100644 --- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp @@ -19,6 +19,7 @@ #pragma clang diagnostic ignored "-Wconversion" #include <gui/BufferItemConsumer.h> +#include <private/android_filesystem_config.h> #include "TransactionTestHarnesses.h" namespace android { @@ -170,7 +171,11 @@ TEST_P(LayerTypeTransactionTest, SetFlagsSecure) { args.displayToken = mDisplay; ScreenCaptureResults captureResults; - ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults)); + { + // Ensure the UID is not root because root has all permissions + UIDFaker f(AID_APP_START); + ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults)); + } Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true); ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, captureResults)); diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp index 2e9c10ce5c..6912fcf219 100644 --- a/services/surfaceflinger/tests/ScreenCapture_test.cpp +++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp @@ -84,7 +84,11 @@ TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) { Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true); - ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults)); + { + // Ensure the UID is not root because root has all permissions + UIDFaker f(AID_APP_START); + ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults)); + } UIDFaker f(AID_SYSTEM); @@ -528,7 +532,7 @@ TEST_F(ScreenCaptureTest, CaptureInvalidLayer) { ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults)); } -TEST_F(ScreenCaptureTest, CaputureSecureLayer) { +TEST_F(ScreenCaptureTest, CaptureSecureLayer) { sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, ISurfaceComposerClient::eFXSurfaceBufferState); sp<SurfaceControl> secureLayer = @@ -552,8 +556,12 @@ TEST_F(ScreenCaptureTest, CaputureSecureLayer) { args.childrenOnly = false; ScreenCaptureResults captureResults; - // Call from outside system with secure layers will result in permission denied - ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults)); + { + // Ensure the UID is not root because root has all permissions + UIDFaker f(AID_APP_START); + // Call from outside system with secure layers will result in permission denied + ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults)); + } UIDFaker f(AID_SYSTEM); diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 88fb811b52..1b25a3684e 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -86,6 +86,7 @@ cc_test { "TransactionApplicationTest.cpp", "TransactionFrameTracerTest.cpp", "TransactionSurfaceFrameTest.cpp", + "TunnelModeEnabledReporterTest.cpp", "StrongTypingTest.cpp", "VSyncDispatchTimerQueueTest.cpp", "VSyncDispatchRealtimeTest.cpp", @@ -94,6 +95,7 @@ cc_test { "VSyncReactorTest.cpp", "VsyncConfigurationTest.cpp", "mock/DisplayHardware/MockComposer.cpp", + "mock/DisplayHardware/MockHWC2.cpp", "mock/DisplayHardware/MockPowerAdvisor.cpp", "mock/MockEventThread.cpp", "mock/MockFrameTimeline.cpp", diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp index 54f4c7c018..a9ad249383 100644 --- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp +++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp @@ -66,7 +66,7 @@ public: ALOGD("schedule: %zu", token.value()); if (mCallbacks.count(token) == 0) { ALOGD("schedule: callback %zu not registered", token.value()); - return scheduler::ScheduleResult::Error; + return scheduler::ScheduleResult{}; } auto& callback = mCallbacks.at(token); @@ -75,7 +75,7 @@ public: callback.targetWakeupTime = timing.earliestVsync - timing.workDuration - timing.readyDuration; ALOGD("schedule: callback %zu scheduled", token.value()); - return scheduler::ScheduleResult::Scheduled; + return scheduler::ScheduleResult{callback.targetWakeupTime}; }); ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) { diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index 6ed614828e..c6a41159c1 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -73,7 +73,7 @@ public: mTokenManager = &mFrameTimeline->mTokenManager; mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter; maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames; - maxTokenRetentionTime = mTokenManager->kMaxRetentionTime; + maxTokens = mTokenManager->kMaxTokens; } // Each tracing session can be used for a single block of Start -> Stop. @@ -111,9 +111,11 @@ public: mFrameTimeline->setSfPresent(2500, presentFence1); } - void flushTokens(nsecs_t flushTime) { - std::lock_guard<std::mutex> lock(mTokenManager->mMutex); - mTokenManager->flushTokens(flushTime); + void flushTokens() { + for (size_t i = 0; i < maxTokens; i++) { + mTokenManager->generateTokenForPredictions({}); + } + EXPECT_EQ(getPredictions().size(), maxTokens); } SurfaceFrame& getSurfaceFrame(size_t displayFrameIdx, size_t surfaceFrameIdx) { @@ -132,7 +134,7 @@ public: a.presentTime == b.presentTime; } - const std::map<int64_t, TokenManagerPrediction>& getPredictions() const { + const std::map<int64_t, TimelineItem>& getPredictions() const { return mTokenManager->mPredictions; } @@ -155,7 +157,7 @@ public: TraceCookieCounter* mTraceCookieCounter; FenceToFenceTimeMap fenceFactory; uint32_t* maxDisplayFrames; - nsecs_t maxTokenRetentionTime; + size_t maxTokens; static constexpr pid_t kSurfaceFlingerPid = 666; static constexpr nsecs_t kPresentThreshold = std::chrono::nanoseconds(2ns).count(); static constexpr nsecs_t kDeadlineThreshold = std::chrono::nanoseconds(2ns).count(); @@ -177,12 +179,11 @@ static constexpr int32_t sLayerIdTwo = 2; TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) { int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0}); EXPECT_EQ(getPredictions().size(), 1u); - flushTokens(systemTime() + maxTokenRetentionTime); + flushTokens(); int64_t token2 = mTokenManager->generateTokenForPredictions({10, 20, 30}); std::optional<TimelineItem> predictions = mTokenManager->getPredictionsForToken(token1); // token1 should have expired - EXPECT_EQ(getPredictions().size(), 1u); EXPECT_EQ(predictions.has_value(), false); predictions = mTokenManager->getPredictionsForToken(token2); @@ -212,7 +213,7 @@ TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) { TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) { int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0}); - flushTokens(systemTime() + maxTokenRetentionTime); + flushTokens(); auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne, sLayerIdOne, sLayerNameOne, sLayerNameOne, @@ -707,7 +708,7 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_displayFramePredictionExpiredPres sLayerNameOne, /*isBuffer*/ true); surfaceFrame1->setAcquireFenceTime(45); // Trigger a prediction expiry - flushTokens(systemTime() + maxTokenRetentionTime); + flushTokens(); mFrameTimeline->setSfWakeUp(sfToken1, 52, refreshRate); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); @@ -1065,7 +1066,7 @@ TEST_F(FrameTimelineTest, traceDisplayFrame_predictionExpiredDoesNotTraceExpecte tracingSession->StartBlocking(); int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 25, 30}); // Flush the token so that it would expire - flushTokens(systemTime() + maxTokenRetentionTime); + flushTokens(); // Set up the display frame mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11)); @@ -1283,7 +1284,7 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDoesNotTraceExpecte mTokenManager->generateTokenForPredictions({appStartTime, appEndTime, appPresentTime}); // Flush the token so that it would expire - flushTokens(systemTime() + maxTokenRetentionTime); + flushTokens(); auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0}, sPidOne, sUidOne, sLayerIdOne, sLayerNameOne, @@ -1359,7 +1360,7 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDroppedFramesTraced mTokenManager->generateTokenForPredictions({appStartTime, appEndTime, appPresentTime}); // Flush the token so that it would expire - flushTokens(systemTime() + maxTokenRetentionTime); + flushTokens(); auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0}, sPidOne, sUidOne, sLayerIdOne, sLayerNameOne, diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index 6b8217087a..cbf8cc21bd 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -37,6 +37,7 @@ #include "DisplayHardware/HWComposer.h" #include "DisplayHardware/Hal.h" #include "mock/DisplayHardware/MockComposer.h" +#include "mock/DisplayHardware/MockHWC2.h" // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion" @@ -120,13 +121,19 @@ struct HWComposerLayerTest : public testing::Test { static constexpr hal::HWLayerId kLayerId = static_cast<hal::HWLayerId>(1002); HWComposerLayerTest(const std::unordered_set<hal::Capability>& capabilities) - : mCapabilies(capabilities) {} + : mCapabilies(capabilities) { + EXPECT_CALL(mDisplay, getId()).WillRepeatedly(Return(kDisplayId)); + } - ~HWComposerLayerTest() override { EXPECT_CALL(*mHal, destroyLayer(kDisplayId, kLayerId)); } + ~HWComposerLayerTest() override { + EXPECT_CALL(mDisplay, onLayerDestroyed(kLayerId)); + EXPECT_CALL(*mHal, destroyLayer(kDisplayId, kLayerId)); + } std::unique_ptr<Hwc2::mock::Composer> mHal{new StrictMock<Hwc2::mock::Composer>()}; const std::unordered_set<hal::Capability> mCapabilies; - HWC2::impl::Layer mLayer{*mHal, mCapabilies, kDisplayId, kLayerId}; + StrictMock<HWC2::mock::Display> mDisplay; + HWC2::impl::Layer mLayer{*mHal, mCapabilies, mDisplay, kLayerId}; }; struct HWComposerLayerGenericMetadataTest : public HWComposerLayerTest { @@ -176,4 +183,4 @@ TEST_F(HWComposerLayerGenericMetadataTest, forwardsSupportedMetadata) { } } // namespace -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp index 325fb8f680..d6ce5e2ce3 100644 --- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp @@ -126,7 +126,7 @@ TEST_F(LayerInfoTest, ignoresSmallPeriods) { std::deque<FrameTimeData> frameTimes; constexpr auto kExpectedFps = Fps(50.0f); constexpr auto kExpectedPeriod = kExpectedFps.getPeriodNsecs(); - constexpr auto kSmallPeriod = Fps(150.0f).getPeriodNsecs(); + constexpr auto kSmallPeriod = Fps(250.0f).getPeriodNsecs(); constexpr int kNumIterations = 10; for (int i = 1; i <= kNumIterations; i++) { frameTimes.push_back(FrameTimeData{.presentTime = kExpectedPeriod * i, diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp index 8208b3fa73..dbd51fed78 100644 --- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp @@ -104,8 +104,12 @@ TEST_F(MessageQueueTest, invalidate) { const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(), .readyDuration = 0, .earliestVsync = 0}; - EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1); + EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value()); + + EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate()); + EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value()); + EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count()); } TEST_F(MessageQueueTest, invalidateTwice) { @@ -114,11 +118,15 @@ TEST_F(MessageQueueTest, invalidateTwice) { .readyDuration = 0, .earliestVsync = 0}; - EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1); + EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate()); + EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value()); + EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count()); - EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1); + EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567)); EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate()); + EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value()); + EXPECT_EQ(4567, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count()); } TEST_F(MessageQueueTest, invalidateTwiceWithCallback) { @@ -127,8 +135,10 @@ TEST_F(MessageQueueTest, invalidateTwiceWithCallback) { .readyDuration = 0, .earliestVsync = 0}; - EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1); + EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate()); + EXPECT_TRUE(mEventQueue.nextExpectedInvalidate().has_value()); + EXPECT_EQ(1234, mEventQueue.nextExpectedInvalidate().value().time_since_epoch().count()); const auto startTime = 100; const auto endTime = startTime + mDuration.count(); @@ -141,12 +151,14 @@ TEST_F(MessageQueueTest, invalidateTwiceWithCallback) { EXPECT_CALL(*mHandler, dispatchInvalidate(vsyncId, presentTime)).Times(1); EXPECT_NO_FATAL_FAILURE(mEventQueue.triggerVsyncCallback(presentTime, startTime, endTime)); + EXPECT_FALSE(mEventQueue.nextExpectedInvalidate().has_value()); + const auto timingAfterCallback = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(), .readyDuration = 0, .earliestVsync = presentTime}; - EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).Times(1); + EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0)); EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate()); } @@ -158,7 +170,7 @@ TEST_F(MessageQueueTest, invalidateWithDurationChange) { .readyDuration = 0, .earliestVsync = 0}; - EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1); + EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0)); EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate()); } diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index 0b70f27781..7ace70aeef 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -1450,12 +1450,12 @@ TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) { .seamlessness = Seamlessness::OnlySeamless, .weight = 1.0f, .focused = true}}; - auto& seamedLayer = layers[0]; ASSERT_EQ(HWC_CONFIG_ID_50, refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) .getModeId()); + auto& seamedLayer = layers[0]; seamedLayer.name = "30Hz ExplicitDefault", seamedLayer.desiredRefreshRate = Fps(30.0f); refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_30); @@ -1464,6 +1464,27 @@ TEST_F(RefreshRateConfigsTest, nonSeamlessExactAndSeamlessMultipleLayers) { .getModeId()); } +TEST_F(RefreshRateConfigsTest, minLayersDontTrigerSeamedSwitch) { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, + /*currentConfigId=*/HWC_CONFIG_ID_90); + + // Allow group switching. + RefreshRateConfigs::Policy policy; + policy.defaultMode = refreshRateConfigs->getCurrentPolicy().defaultMode; + policy.allowGroupSwitching = true; + ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0); + + auto layers = std::vector<LayerRequirement>{LayerRequirement{.name = "Min", + .vote = LayerVoteType::Min, + .weight = 1.f, + .focused = true}}; + + ASSERT_EQ(HWC_CONFIG_ID_90, + refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}) + .getModeId()); +} + TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) { auto refreshRateConfigs = std::make_unique<RefreshRateConfigs>(m30_60_90Device, diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index d004b9d9eb..a551248dd6 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -276,6 +276,7 @@ public: static void setLayerSidebandStream(const sp<Layer>& layer, const sp<NativeHandle>& sidebandStream) { layer->mDrawingState.sidebandStream = sidebandStream; + layer->mCurrentState.sidebandStream = sidebandStream; layer->mSidebandStream = sidebandStream; layer->editCompositionState()->sidebandStream = sidebandStream; } diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp index ff53a7b09c..3e4e130a14 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -1011,6 +1011,9 @@ TEST_F(TimeStatsTest, globalStatsCallback) { constexpr size_t MISSED_FRAMES = 4; constexpr size_t CLIENT_COMPOSITION_FRAMES = 3; constexpr size_t DISPLAY_EVENT_CONNECTIONS = 14; + constexpr nsecs_t DISPLAY_DEADLINE_DELTA = 1'000'000; + constexpr nsecs_t DISPLAY_PRESENT_JITTER = 2'000'000; + constexpr nsecs_t APP_DEADLINE_DELTA = 3'000'000; EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); @@ -1036,24 +1039,35 @@ TEST_F(TimeStatsTest, globalStatsCallback) { mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000)); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3}); + JankType::SurfaceFlingerCpuDeadlineMissed, + DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, + APP_DEADLINE_DELTA}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3}); + JankType::SurfaceFlingerGpuDeadlineMissed, + DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, + APP_DEADLINE_DELTA}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::DisplayHAL, 1, 2, 3}); + JankType::DisplayHAL, DISPLAY_DEADLINE_DELTA, + DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::AppDeadlineMissed, 1, 2, 3}); + JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA, + DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::SurfaceFlingerScheduling, 1, 2, 3}); + JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA, + DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::PredictionError, 1, 2, 3}); + JankType::PredictionError, DISPLAY_DEADLINE_DELTA, + DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2, - 3}); + JankType::AppDeadlineMissed | JankType::BufferStuffing, + DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, + APP_DEADLINE_DELTA}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::BufferStuffing, 1, 2, 3}); + JankType::BufferStuffing, DISPLAY_DEADLINE_DELTA, + DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::None, 1, 2, 3}); + JankType::None, DISPLAY_DEADLINE_DELTA, + DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA}); std::string pulledData; EXPECT_TRUE(mTimeStats->onPullAtom(10062 /*SURFACEFLINGER_STATS_GLOBAL_INFO*/, &pulledData)); @@ -1137,6 +1151,10 @@ TEST_F(TimeStatsTest, globalStatsCallback) { TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { constexpr size_t LATE_ACQUIRE_FRAMES = 2; constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 3; + constexpr nsecs_t DISPLAY_DEADLINE_DELTA = 1'000'000; + constexpr nsecs_t DISPLAY_PRESENT_JITTER = 2'000'000; + constexpr nsecs_t APP_DEADLINE_DELTA_2MS = 2'000'000; + constexpr nsecs_t APP_DEADLINE_DELTA_3MS = 3'000'000; EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000); @@ -1155,22 +1173,32 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3}); + JankType::SurfaceFlingerCpuDeadlineMissed, + DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, + APP_DEADLINE_DELTA_3MS}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3}); + JankType::SurfaceFlingerGpuDeadlineMissed, + DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, + APP_DEADLINE_DELTA_3MS}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::DisplayHAL, 1, 2, 3}); + JankType::DisplayHAL, DISPLAY_DEADLINE_DELTA, + DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::AppDeadlineMissed, 1, 2, 3}); + JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA, + DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::SurfaceFlingerScheduling, 1, 2, 2}); + JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA, + DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::PredictionError, 1, 2, 2}); + JankType::PredictionError, DISPLAY_DEADLINE_DELTA, + DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2, - 2}); + JankType::AppDeadlineMissed | JankType::BufferStuffing, + DISPLAY_DEADLINE_DELTA, APP_DEADLINE_DELTA_2MS, + APP_DEADLINE_DELTA_2MS}); mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), - JankType::None, 1, 2, 3}); + JankType::None, DISPLAY_DEADLINE_DELTA, + DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS}); std::string pulledData; EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData)); diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp new file mode 100644 index 0000000000..d7d7ea77d6 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp @@ -0,0 +1,194 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "TunnelModeEnabledReporterTest" + +#include <android/gui/BnTunnelModeEnabledListener.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <gui/LayerMetadata.h> + +#include "BufferStateLayer.h" +#include "TestableSurfaceFlinger.h" +#include "TunnelModeEnabledReporter.h" +#include "mock/DisplayHardware/MockComposer.h" +#include "mock/MockEventThread.h" + +namespace android { + +using testing::_; +using testing::Mock; +using testing::Return; + +using android::Hwc2::IComposer; +using android::Hwc2::IComposerClient; + +using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; + +constexpr int DEFAULT_SIDEBAND_STREAM = 51; + +struct TestableTunnelModeEnabledListener : public gui::BnTunnelModeEnabledListener { + TestableTunnelModeEnabledListener() {} + + bool mTunnelModeEnabled = false; + + binder::Status onTunnelModeEnabledChanged(bool tunnelModeEnabled) override { + mTunnelModeEnabled = tunnelModeEnabled; + return binder::Status::ok(); + } +}; + +class TunnelModeEnabledReporterTest : public testing::Test { +public: + TunnelModeEnabledReporterTest(); + ~TunnelModeEnabledReporterTest() override; + +protected: + static constexpr uint32_t WIDTH = 100; + static constexpr uint32_t HEIGHT = 100; + static constexpr uint32_t LAYER_FLAGS = 0; + + void setupScheduler(); + void setupComposer(uint32_t virtualDisplayCount); + sp<BufferStateLayer> createBufferStateLayer(LayerMetadata metadata); + + TestableSurfaceFlinger mFlinger; + Hwc2::mock::Composer* mComposer = nullptr; + sp<TestableTunnelModeEnabledListener> mTunnelModeEnabledListener = + new TestableTunnelModeEnabledListener(); + sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter = + new TunnelModeEnabledReporter(*(mFlinger.flinger())); +}; + +TunnelModeEnabledReporterTest::TunnelModeEnabledReporterTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + setupScheduler(); + mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); + mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(false); +} + +TunnelModeEnabledReporterTest::~TunnelModeEnabledReporterTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(false); + mTunnelModeEnabledReporter->removeListener(mTunnelModeEnabledListener); +} + +sp<BufferStateLayer> TunnelModeEnabledReporterTest::createBufferStateLayer( + LayerMetadata metadata = {}) { + sp<Client> client; + LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT, + LAYER_FLAGS, metadata); + return new BufferStateLayer(args); +} + +void TunnelModeEnabledReporterTest::setupScheduler() { + auto eventThread = std::make_unique<mock::EventThread>(); + auto sfEventThread = std::make_unique<mock::EventThread>(); + + EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*eventThread, createEventConnection(_, _)) + .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, + ResyncCallback()))); + + EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) + .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, + ResyncCallback()))); + + auto vsyncController = std::make_unique<mock::VsyncController>(); + auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); + + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, currentPeriod()) + .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), + std::move(eventThread), std::move(sfEventThread)); +} + +namespace { + +TEST_F(TunnelModeEnabledReporterTest, callsAddedListeners) { + mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener); + + bool expectedTunnelModeEnabled = false; + mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(expectedTunnelModeEnabled); + EXPECT_EQ(expectedTunnelModeEnabled, mTunnelModeEnabledListener->mTunnelModeEnabled); + + expectedTunnelModeEnabled = true; + mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(expectedTunnelModeEnabled); + EXPECT_EQ(expectedTunnelModeEnabled, mTunnelModeEnabledListener->mTunnelModeEnabled); + + mTunnelModeEnabledReporter->removeListener(mTunnelModeEnabledListener); + + mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(false); + EXPECT_EQ(expectedTunnelModeEnabled, mTunnelModeEnabledListener->mTunnelModeEnabled); +} + +TEST_F(TunnelModeEnabledReporterTest, callsNewListenerImmediately) { + bool expectedTunnelModeEnabled = false; + mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(expectedTunnelModeEnabled); + + mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener); + EXPECT_EQ(expectedTunnelModeEnabled, mTunnelModeEnabledListener->mTunnelModeEnabled); +} + +TEST_F(TunnelModeEnabledReporterTest, callsNewListenerWithFreshInformation) { + sp<Layer> layer = createBufferStateLayer(); + sp<NativeHandle> stream = + NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM), + false); + mFlinger.setLayerSidebandStream(layer, stream); + mFlinger.mutableCurrentState().layersSortedByZ.add(layer); + mTunnelModeEnabledReporter->updateTunnelModeStatus(); + mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener); + EXPECT_EQ(true, mTunnelModeEnabledListener->mTunnelModeEnabled); + mTunnelModeEnabledReporter->removeListener(mTunnelModeEnabledListener); + + mFlinger.mutableCurrentState().layersSortedByZ.remove(layer); + mTunnelModeEnabledReporter->updateTunnelModeStatus(); + mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener); + EXPECT_EQ(false, mTunnelModeEnabledListener->mTunnelModeEnabled); +} + +TEST_F(TunnelModeEnabledReporterTest, layerWithSidebandStreamTriggersUpdate) { + mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener); + EXPECT_EQ(false, mTunnelModeEnabledListener->mTunnelModeEnabled); + + sp<Layer> simpleLayer = createBufferStateLayer(); + sp<Layer> layerWithSidebandStream = createBufferStateLayer(); + sp<NativeHandle> stream = + NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM), + false); + mFlinger.setLayerSidebandStream(layerWithSidebandStream, stream); + + mFlinger.mutableCurrentState().layersSortedByZ.add(simpleLayer); + mFlinger.mutableCurrentState().layersSortedByZ.add(layerWithSidebandStream); + mTunnelModeEnabledReporter->updateTunnelModeStatus(); + EXPECT_EQ(true, mTunnelModeEnabledListener->mTunnelModeEnabled); + + mFlinger.mutableCurrentState().layersSortedByZ.remove(layerWithSidebandStream); + mTunnelModeEnabledReporter->updateTunnelModeStatus(); + EXPECT_EQ(false, mTunnelModeEnabledListener->mTunnelModeEnabled); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index b64cce9e43..d59d64bc02 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -232,11 +232,12 @@ TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) { VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold, mVsyncMoveThreshold}; CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = 1000}), - ScheduleResult::Scheduled); + const auto result = mDispatch.schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(900, *result); } } @@ -245,11 +246,13 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFuture) { EXPECT_CALL(mMockClock, alarmAt(_, 900)); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = intended}), - ScheduleResult::Scheduled); + const auto result = mDispatch.schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = intended}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(900, *result); + advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); @@ -277,11 +280,12 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingAdjustmentPast) { EXPECT_CALL(mMockClock, alarmAt(_, mPeriod)); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = workDuration, - .readyDuration = 0, - .earliestVsync = mPeriod}), - ScheduleResult::Scheduled); + const auto result = mDispatch.schedule(cb, + {.workDuration = workDuration, + .readyDuration = 0, + .earliestVsync = mPeriod}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(mPeriod, *result); } TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) { @@ -289,11 +293,11 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) { EXPECT_CALL(mMockClock, alarmCancel()); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = mPeriod}), - ScheduleResult::Scheduled); + const auto result = + mDispatch.schedule(cb, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(mPeriod - 100, *result); EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled); } @@ -302,11 +306,11 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) { EXPECT_CALL(mMockClock, alarmCancel()); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = mPeriod}), - ScheduleResult::Scheduled); + const auto result = + mDispatch.schedule(cb, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(mPeriod - 100, *result); mMockClock.advanceBy(950); EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate); } @@ -316,11 +320,11 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) { EXPECT_CALL(mMockClock, alarmCancel()); PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s)); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = mPeriod}), - ScheduleResult::Scheduled); + const auto result = + mDispatch.schedule(cb, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(mPeriod - 100, *result); std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); }); EXPECT_TRUE(cb.waitForPause()); @@ -337,11 +341,11 @@ TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) { PausingCallback cb(mDispatch, 50ms); cb.stashResource(resource); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = mPeriod}), - ScheduleResult::Scheduled); + const auto result = + mDispatch.schedule(cb, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(mPeriod - 100, *result); std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); }); EXPECT_TRUE(cb.waitForPause()); @@ -535,21 +539,25 @@ TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) { std::optional<nsecs_t> lastTarget; tmp = mDispatch.registerCallback( [&](auto timestamp, auto, auto) { - EXPECT_EQ(mDispatch.schedule(tmp, - {.workDuration = 400, - .readyDuration = 0, - .earliestVsync = timestamp - mVsyncMoveThreshold}), - ScheduleResult::Scheduled); - EXPECT_EQ(mDispatch.schedule(tmp, - {.workDuration = 400, - .readyDuration = 0, - .earliestVsync = timestamp}), - ScheduleResult::Scheduled); - EXPECT_EQ(mDispatch.schedule(tmp, - {.workDuration = 400, - .readyDuration = 0, - .earliestVsync = timestamp + mVsyncMoveThreshold}), - ScheduleResult::Scheduled); + auto result = + mDispatch.schedule(tmp, + {.workDuration = 400, + .readyDuration = 0, + .earliestVsync = timestamp - mVsyncMoveThreshold}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(mPeriod + timestamp - 400, *result); + result = mDispatch.schedule(tmp, + {.workDuration = 400, + .readyDuration = 0, + .earliestVsync = timestamp}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(mPeriod + timestamp - 400, *result); + result = mDispatch.schedule(tmp, + {.workDuration = 400, + .readyDuration = 0, + .earliestVsync = timestamp + mVsyncMoveThreshold}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(mPeriod + timestamp - 400, *result); lastTarget = timestamp; }, "oo"); @@ -627,36 +635,41 @@ TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) { TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) { VSyncDispatch::CallbackToken token(100); - EXPECT_THAT(mDispatch.schedule(token, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = 1000}), - Eq(ScheduleResult::Error)); + EXPECT_FALSE(mDispatch + .schedule(token, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}) + .has_value()); EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error)); } TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) { CountingCallback cb0(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb0, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); - EXPECT_EQ(mDispatch.schedule(cb0, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); + auto result = + mDispatch.schedule(cb0, + {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(500, *result); + result = mDispatch.schedule(cb0, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(900, *result); } // b/1450138150 TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) { EXPECT_CALL(mMockClock, alarmAt(_, 500)); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); + auto result = + mDispatch.schedule(cb, + {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(500, *result); mMockClock.advanceBy(400); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); + result = mDispatch.schedule(cb, + {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(1200, *result); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); } @@ -667,24 +680,30 @@ TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedul .WillOnce(Return(1000)) .WillOnce(Return(1002)); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); + auto result = + mDispatch.schedule(cb, + {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(500, *result); mMockClock.advanceBy(400); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); + result = mDispatch.schedule(cb, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(602, *result); } TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) { CountingCallback cb0(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb0, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); + auto result = + mDispatch.schedule(cb0, + {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(500, *result); advanceToNextCallback(); - EXPECT_EQ(mDispatch.schedule(cb0, - {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}), - ScheduleResult::Scheduled); + result = mDispatch.schedule(cb0, + {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(900, *result); } TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) { @@ -692,26 +711,32 @@ TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) { EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq); EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq); CountingCallback cb0(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb0, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); + auto result = + mDispatch.schedule(cb0, + {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(500, *result); advanceToNextCallback(); - EXPECT_EQ(mDispatch.schedule(cb0, - {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}), - ScheduleResult::Scheduled); + result = mDispatch.schedule(cb0, + {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(1100, *result); } TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) { EXPECT_CALL(mMockClock, alarmAt(_, 600)); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); + auto result = + mDispatch.schedule(cb, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(600, *result); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); + result = mDispatch.schedule(cb, + {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(600, *result); advanceToNextCallback(); } @@ -754,16 +779,19 @@ TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent CountingCallback cb1(mDispatch); CountingCallback cb2(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); + auto result = + mDispatch.schedule(cb1, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(600, *result); mMockClock.setLag(100); mMockClock.advanceBy(620); - EXPECT_EQ(mDispatch.schedule(cb2, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}), - ScheduleResult::Scheduled); + result = mDispatch.schedule(cb2, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(1900, *result); mMockClock.advanceBy(80); EXPECT_THAT(cb1.mCalls.size(), Eq(1)); @@ -779,16 +807,19 @@ TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent EXPECT_CALL(mMockClock, alarmAt(_, 1630)).InSequence(seq); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); + auto result = + mDispatch.schedule(cb, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(600, *result); mMockClock.setLag(100); mMockClock.advanceBy(620); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}), - ScheduleResult::Scheduled); + result = mDispatch.schedule(cb, + {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(1630, *result); mMockClock.advanceBy(80); EXPECT_THAT(cb.mCalls.size(), Eq(1)); @@ -802,12 +833,15 @@ TEST_F(VSyncDispatchTimerQueueTest, skipsRearmingWhenNotNextScheduled) { CountingCallback cb1(mDispatch); CountingCallback cb2(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); - EXPECT_EQ(mDispatch.schedule(cb2, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}), - ScheduleResult::Scheduled); + auto result = + mDispatch.schedule(cb1, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(600, *result); + result = mDispatch.schedule(cb2, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(1900, *result); mMockClock.setLag(100); mMockClock.advanceBy(620); @@ -828,12 +862,15 @@ TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenCancelledAndIsNextScheduled) { CountingCallback cb1(mDispatch); CountingCallback cb2(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); - EXPECT_EQ(mDispatch.schedule(cb2, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}), - ScheduleResult::Scheduled); + auto result = + mDispatch.schedule(cb1, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(600, *result); + result = mDispatch.schedule(cb2, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(1900, *result); mMockClock.setLag(100); mMockClock.advanceBy(620); @@ -861,12 +898,15 @@ TEST_F(VSyncDispatchTimerQueueTest, laggedTimerGroupsCallbacksWithinLag) { .InSequence(seq) .WillOnce(Return(1000)); - EXPECT_EQ(mDispatch.schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); - EXPECT_EQ(mDispatch.schedule(cb2, - {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}), - ScheduleResult::Scheduled); + auto result = + mDispatch.schedule(cb1, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(600, *result); + result = mDispatch.schedule(cb2, + {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(610, *result); mMockClock.setLag(100); mMockClock.advanceBy(700); @@ -886,11 +926,12 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithReadyDuration) { EXPECT_CALL(mMockClock, alarmAt(_, 900)); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, - {.workDuration = 70, - .readyDuration = 30, - .earliestVsync = intended}), - ScheduleResult::Scheduled); + const auto result = mDispatch.schedule(cb, + {.workDuration = 70, + .readyDuration = 30, + .earliestVsync = intended}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(900, *result); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); @@ -922,9 +963,9 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) { "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.wakeupTime()); - EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0), - Eq(ScheduleResult::Scheduled)); + EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0) + .has_value()); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(*wakeup, Eq(900)); @@ -944,9 +985,9 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, stateSchedulingReallyLongWakeupLatency) "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.wakeupTime()); - EXPECT_THAT(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994}, - mStubTracker, now), - Eq(ScheduleResult::Scheduled)); + EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994}, + mStubTracker, now) + .has_value()); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(*wakeup, Eq(9500)); @@ -967,9 +1008,9 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) { }, mVsyncMoveThreshold); - EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0), - Eq(ScheduleResult::Scheduled)); + EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0) + .has_value()); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(*wakeup, Eq(900)); @@ -1002,9 +1043,9 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) { entry.update(mStubTracker, 0); EXPECT_FALSE(entry.wakeupTime()); - EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0), - Eq(ScheduleResult::Scheduled)); + EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0) + .has_value()); auto wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(wakeup, Eq(900)); @@ -1018,9 +1059,9 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) { TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) { VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); - EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0), - Eq(ScheduleResult::Scheduled)); + EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0) + .has_value()); entry.update(mStubTracker, 0); auto const wakeup = entry.wakeupTime(); @@ -1031,26 +1072,26 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) { TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) { VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); - EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0), - Eq(ScheduleResult::Scheduled)); + EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0) + .has_value()); entry.executing(); // 1000 is executing // had 1000 not been executing, this could have been scheduled for time 800. - EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0), - Eq(ScheduleResult::Scheduled)); + EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0) + .has_value()); EXPECT_THAT(*entry.wakeupTime(), Eq(1800)); EXPECT_THAT(*entry.readyTime(), Eq(2000)); - EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0), - Eq(ScheduleResult::Scheduled)); + EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0) + .has_value()); EXPECT_THAT(*entry.wakeupTime(), Eq(1950)); EXPECT_THAT(*entry.readyTime(), Eq(2000)); - EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001}, - mStubTracker, 0), - Eq(ScheduleResult::Scheduled)); + EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001}, + mStubTracker, 0) + .has_value()); EXPECT_THAT(*entry.wakeupTime(), Eq(1800)); EXPECT_THAT(*entry.readyTime(), Eq(2000)); } @@ -1071,32 +1112,32 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, .InSequence(seq) .WillOnce(Return(2000)); - EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0), - Eq(ScheduleResult::Scheduled)); + EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0) + .has_value()); entry.executing(); // 1000 is executing - EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0), - Eq(ScheduleResult::Scheduled)); + EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0) + .has_value()); } TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) { VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); - EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0), - Eq(ScheduleResult::Scheduled)); - EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0), - Eq(ScheduleResult::Scheduled)); - EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0), - Eq(ScheduleResult::Scheduled)); - EXPECT_THAT(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0), - Eq(ScheduleResult::Scheduled)); + EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0) + .has_value()); + EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0) + .has_value()); + EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0) + .has_value()); + EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0) + .has_value()); } TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) { @@ -1128,9 +1169,9 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, runCallbackWithReadyDuration) { }, mVsyncMoveThreshold); - EXPECT_THAT(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500}, - mStubTracker, 0), - Eq(ScheduleResult::Scheduled)); + EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500}, + mStubTracker, 0) + .has_value()); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(*wakeup, Eq(900)); diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.cpp new file mode 100644 index 0000000000..2647bf4f9d --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.cpp @@ -0,0 +1,31 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "MockHWC2" + +#include "mock/DisplayHardware/MockHWC2.h" + +namespace android::HWC2::mock { + +// Explicit default instantiation is recommended. +Display::Display() = default; +Display::~Display() = default; + +Layer::Layer() = default; +Layer::~Layer() = default; + +} // namespace android::HWC2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h new file mode 100644 index 0000000000..c3919d9310 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h @@ -0,0 +1,122 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gmock/gmock.h> + +#include "DisplayHardware/HWC2.h" + +namespace android::HWC2::mock { + +class Display : public HWC2::Display { +public: + Display(); + ~Display() override; + + MOCK_METHOD(hal::HWDisplayId, getId, (), (const, override)); + MOCK_METHOD(bool, isConnected, (), (const, override)); + MOCK_METHOD(void, setConnected, (bool), (override)); + MOCK_METHOD(const std::unordered_set<hal::DisplayCapability> &, getCapabilities, (), + (const, override)); + MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (), (const, override)); + MOCK_METHOD(void, onLayerDestroyed, (hal::HWLayerId), (override)); + + MOCK_METHOD(hal::Error, acceptChanges, (), (override)); + MOCK_METHOD((base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>), createLayer, (), + (override)); + MOCK_METHOD(hal::Error, getChangedCompositionTypes, + ((std::unordered_map<Layer *, hal::Composition> *)), (override)); + MOCK_METHOD(hal::Error, getColorModes, (std::vector<hal::ColorMode> *), (const, override)); + MOCK_METHOD(int32_t, getSupportedPerFrameMetadata, (), (const, override)); + MOCK_METHOD(hal::Error, getRenderIntents, (hal::ColorMode, std::vector<hal::RenderIntent> *), + (const, override)); + MOCK_METHOD(hal::Error, getDataspaceSaturationMatrix, (hal::Dataspace, android::mat4 *), + (override)); + MOCK_METHOD(hal::Error, getName, (std::string *), (const, override)); + MOCK_METHOD(hal::Error, getRequests, + (hal::DisplayRequest *, (std::unordered_map<Layer *, hal::LayerRequest> *)), + (override)); + MOCK_METHOD(hal::Error, getConnectionType, (ui::DisplayConnectionType *), (const, override)); + MOCK_METHOD(hal::Error, supportsDoze, (bool *), (const, override)); + MOCK_METHOD(hal::Error, getHdrCapabilities, (android::HdrCapabilities *), (const, override)); + MOCK_METHOD(hal::Error, getDisplayedContentSamplingAttributes, + (hal::PixelFormat *, hal::Dataspace *, uint8_t *), (const, override)); + MOCK_METHOD(hal::Error, setDisplayContentSamplingEnabled, (bool, uint8_t, uint64_t), + (const, override)); + MOCK_METHOD(hal::Error, getDisplayedContentSample, + (uint64_t, uint64_t, android::DisplayedFrameStats *), (const, override)); + MOCK_METHOD(hal::Error, getReleaseFences, + ((std::unordered_map<Layer *, android::sp<android::Fence>> *)), (const, override)); + MOCK_METHOD(hal::Error, present, (android::sp<android::Fence> *), (override)); + MOCK_METHOD(hal::Error, setClientTarget, + (uint32_t, const android::sp<android::GraphicBuffer> &, + const android::sp<android::Fence> &, hal::Dataspace), + (override)); + MOCK_METHOD(hal::Error, setColorMode, (hal::ColorMode, hal::RenderIntent), (override)); + MOCK_METHOD(hal::Error, setColorTransform, (const android::mat4 &, hal::ColorTransform), + (override)); + MOCK_METHOD(hal::Error, setOutputBuffer, + (const android::sp<android::GraphicBuffer> &, const android::sp<android::Fence> &), + (override)); + MOCK_METHOD(hal::Error, setPowerMode, (hal::PowerMode), (override)); + MOCK_METHOD(hal::Error, setVsyncEnabled, (hal::Vsync), (override)); + MOCK_METHOD(hal::Error, validate, (uint32_t *, uint32_t *), (override)); + MOCK_METHOD(hal::Error, presentOrValidate, + (uint32_t *, uint32_t *, android::sp<android::Fence> *, uint32_t *), (override)); + MOCK_METHOD(std::future<hal::Error>, setDisplayBrightness, (float), (override)); + MOCK_METHOD(hal::Error, setActiveConfigWithConstraints, + (hal::HWConfigId, const hal::VsyncPeriodChangeConstraints &, + hal::VsyncPeriodChangeTimeline *), + (override)); + MOCK_METHOD(hal::Error, setAutoLowLatencyMode, (bool), (override)); + MOCK_METHOD(hal::Error, getSupportedContentTypes, (std::vector<hal::ContentType> *), + (const, override)); + MOCK_METHOD(hal::Error, setContentType, (hal::ContentType), (override)); + MOCK_METHOD(hal::Error, getClientTargetProperty, (hal::ClientTargetProperty *), (override)); +}; + +class Layer : public HWC2::Layer { +public: + Layer(); + ~Layer() override; + + MOCK_METHOD(hal::HWLayerId, getId, (), (const, override)); + MOCK_METHOD(hal::Error, setCursorPosition, (int32_t, int32_t), (override)); + MOCK_METHOD(hal::Error, setBuffer, + (uint32_t, const android::sp<android::GraphicBuffer> &, + const android::sp<android::Fence> &), + (override)); + MOCK_METHOD(hal::Error, setSurfaceDamage, (const android::Region &), (override)); + MOCK_METHOD(hal::Error, setBlendMode, (hal::BlendMode), (override)); + MOCK_METHOD(hal::Error, setColor, (hal::Color), (override)); + MOCK_METHOD(hal::Error, setCompositionType, (hal::Composition), (override)); + MOCK_METHOD(hal::Error, setDataspace, (android::ui::Dataspace), (override)); + MOCK_METHOD(hal::Error, setPerFrameMetadata, (const int32_t, const android::HdrMetadata &), + (override)); + MOCK_METHOD(hal::Error, setDisplayFrame, (const android::Rect &), (override)); + MOCK_METHOD(hal::Error, setPlaneAlpha, (float), (override)); + MOCK_METHOD(hal::Error, setSidebandStream, (const native_handle_t *), (override)); + MOCK_METHOD(hal::Error, setSourceCrop, (const android::FloatRect &), (override)); + MOCK_METHOD(hal::Error, setTransform, (hal::Transform), (override)); + MOCK_METHOD(hal::Error, setVisibleRegion, (const android::Region &), (override)); + MOCK_METHOD(hal::Error, setZOrder, (uint32_t), (override)); + MOCK_METHOD(hal::Error, setColorTransform, (const android::mat4 &), (override)); + MOCK_METHOD(hal::Error, setLayerGenericMetadata, + (const std::string &, bool, const std::vector<uint8_t> &), (override)); +}; + +} // namespace android::HWC2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h index 453c93a083..0e7b320787 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h +++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h @@ -39,6 +39,7 @@ public: void(scheduler::VSyncDispatch&, frametimeline::TokenManager&, std::chrono::nanoseconds)); MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration)); + MOCK_METHOD0(nextExpectedInvalidate, std::optional<std::chrono::steady_clock::time_point>()); }; } // namespace android::mock diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp index eb24a22719..80e9a3c3b1 100644 --- a/services/vr/hardware_composer/Android.bp +++ b/services/vr/hardware_composer/Android.bp @@ -106,42 +106,6 @@ cc_library_static { ], } -cc_binary { - name: "vr_hwc", - enabled: false, - system_ext_specific: true, - vintf_fragments: ["manifest_vr_hwc.xml"], - srcs: [ - "vr_hardware_composer_service.cpp", - ], - static_libs: [ - "libvr_hwc-impl", - // NOTE: This needs to be included after the *-impl lib otherwise the - // symbols in the *-binder library get optimized out. - "libvr_hwc-binder", - ], - shared_libs: [ - "android.frameworks.vr.composer@2.0", - "android.hardware.graphics.composer@2.3", - "libbase", - "libbinder", - "liblog", - "libhardware", - "libhidlbase", - "libui", - "libutils", - "libvr_hwc-hal", - ], - cflags: [ - "-DLOG_TAG=\"vr_hwc\"", - "-Wall", - "-Werror", - ], - init_rc: [ - "vr_hwc.rc", - ], -} - cc_test { name: "vr_hwc_test", gtest: true, diff --git a/services/vr/hardware_composer/manifest_vr_hwc.xml b/services/vr/hardware_composer/manifest_vr_hwc.xml deleted file mode 100644 index 1068cac33a..0000000000 --- a/services/vr/hardware_composer/manifest_vr_hwc.xml +++ /dev/null @@ -1,11 +0,0 @@ -<manifest version="1.0" type="framework"> - <hal> - <name>android.hardware.graphics.composer</name> - <transport>hwbinder</transport> - <version>2.1</version> - <interface> - <name>IComposer</name> - <instance>vr</instance> - </interface> - </hal> -</manifest> diff --git a/services/vr/hardware_composer/vr_hardware_composer_service.cpp b/services/vr/hardware_composer/vr_hardware_composer_service.cpp deleted file mode 100644 index 7701847120..0000000000 --- a/services/vr/hardware_composer/vr_hardware_composer_service.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2017 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/ProcessState.h> -#include <binder/IServiceManager.h> -#include <hwbinder/IPCThreadState.h> -#include <impl/vr_hwc.h> -#include <inttypes.h> - -#include "vr_composer.h" - -int main() { - android::ProcessState::self()->startThreadPool(); - - // Register the hwbinder HWC HAL service used by SurfaceFlinger while in VR - // mode. - android::sp<android::dvr::VrHwc> service = new android::dvr::VrHwc(); - - LOG_ALWAYS_FATAL_IF(!service.get(), "Failed to get service"); - LOG_ALWAYS_FATAL_IF(service->isRemote(), "Service is remote"); - - const char instance[] = "vr"; - LOG_ALWAYS_FATAL_IF(service->registerAsService(instance) != android::OK, - "Failed to register service"); - - android::sp<android::dvr::VrComposer> composer = - new android::dvr::VrComposer(service.get()); - - android::sp<android::IServiceManager> sm(android::defaultServiceManager()); - - // Register the binder service used by VR Window Manager service to receive - // frame information from VR HWC HAL. - android::status_t status = sm->addService( - android::dvr::VrComposer::SERVICE_NAME(), composer.get(), - false /* allowIsolated */); - LOG_ALWAYS_FATAL_IF(status != android::OK, - "VrDisplay service failed to start: %" PRId32, status); - - android::hardware::ProcessState::self()->startThreadPool(); - android::hardware::IPCThreadState::self()->joinThreadPool(); - - return 0; -} diff --git a/services/vr/hardware_composer/vr_hwc.rc b/services/vr/hardware_composer/vr_hwc.rc deleted file mode 100644 index 645ab807da..0000000000 --- a/services/vr/hardware_composer/vr_hwc.rc +++ /dev/null @@ -1,6 +0,0 @@ -service vr_hwc /system/bin/vr_hwc - class hal animation - user system - group system graphics - onrestart restart surfaceflinger - writepid /dev/cpuset/system-background/tasks |