diff options
143 files changed, 4107 insertions, 1766 deletions
diff --git a/TEST_MAPPING b/TEST_MAPPING index 307e21cc44..4b64203387 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -60,9 +60,6 @@ ] }, { - "name": "libsurfaceflinger_unittest" - }, - { "name": "CtsGraphicsTestCases", "options": [ { diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index a5462367f8..3f180d94a7 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -47,6 +47,9 @@ cc_defaults { "libutils", "server_configurable_flags", ], + static_libs: [ + "libasync_safe", + ], export_shared_lib_headers: [ "libbinder", ], @@ -250,6 +253,7 @@ cc_binary { ], static_libs: [ + "libasync_safe", "libdiskusage", "libotapreoptparameters", ], diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index 0cf50a3e32..204953cd07 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -36,6 +36,7 @@ #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> +#include <async_safe/log.h> #include <cutils/fs.h> #include <cutils/properties.h> #include <cutils/sched_policy.h> @@ -727,7 +728,8 @@ bool copy_system_profile(const std::string& system_profile, if (flock(out_fd.get(), LOCK_EX | LOCK_NB) != 0) { if (errno != EWOULDBLOCK) { - PLOG(WARNING) << "Error locking profile " << package_name; + async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Error locking profile %s: %d", + package_name.c_str(), errno); } // This implies that the app owning this profile is running // (and has acquired the lock). @@ -735,13 +737,15 @@ bool copy_system_profile(const std::string& system_profile, // The app never acquires the lock for the reference profiles of primary apks. // Only dex2oat from installd will do that. Since installd is single threaded // we should not see this case. Nevertheless be prepared for it. - PLOG(WARNING) << "Failed to flock " << package_name; + async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Failed to flock %s: %d", + package_name.c_str(), errno); return false; } bool truncated = ftruncate(out_fd.get(), 0) == 0; if (!truncated) { - PLOG(WARNING) << "Could not truncate " << package_name; + async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Could not truncate %s: %d", + package_name.c_str(), errno); } // Copy over data. @@ -755,7 +759,8 @@ bool copy_system_profile(const std::string& system_profile, write(out_fd.get(), buffer, bytes); } if (flock(out_fd.get(), LOCK_UN) != 0) { - PLOG(WARNING) << "Error unlocking profile " << package_name; + async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG, "Error unlocking profile %s: %d", + package_name.c_str(), errno); } // Use _exit since we don't want to run the global destructors in the child. // b/62597429 @@ -1513,7 +1518,8 @@ static bool process_secondary_dex_dexopt(const std::string& dex_path, const char // Validate the path structure. if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) { - LOG(ERROR) << "Could not validate secondary dex path " << dex_path; + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, + "Could not validate secondary dex path %s", dex_path.c_str()); _exit(kSecondaryDexDexoptAnalyzerSkippedValidatePath); } @@ -1809,7 +1815,8 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins drop_capabilities(uid); if (flock(out_oat.fd(), LOCK_EX | LOCK_NB) != 0) { - PLOG(ERROR) << "flock(" << out_oat.path() << ") failed"; + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "flock(%s) failed", + out_oat.path().c_str()); _exit(DexoptReturnCodes::kFlock); } @@ -1904,7 +1911,8 @@ bool reconcile_secondary_dex_file(const std::string& dex_path, const char* volume_uuid_cstr = volume_uuid ? volume_uuid->c_str() : nullptr; if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr, uid, storage_flag)) { - LOG(ERROR) << "Could not validate secondary dex path " << dex_path; + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, + "Could not validate secondary dex path %s", dex_path.c_str()); _exit(kReconcileSecondaryDexValidationError); } @@ -1917,7 +1925,8 @@ bool reconcile_secondary_dex_file(const std::string& dex_path, case kSecondaryDexAccessIOError: _exit(kReconcileSecondaryDexAccessIOError); case kSecondaryDexAccessPermissionError: _exit(kReconcileSecondaryDexValidationError); default: - LOG(ERROR) << "Unexpected result from check_secondary_dex_access: " << access_check; + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, + "Unexpected result from check_secondary_dex_access: %d", access_check); _exit(kReconcileSecondaryDexValidationError); } @@ -1930,7 +1939,7 @@ bool reconcile_secondary_dex_file(const std::string& dex_path, std::string error_msg; if (!create_secondary_dex_oat_layout( dex_path,isas[i], oat_dir, oat_isa_dir, oat_path, &error_msg)) { - LOG(ERROR) << error_msg; + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "%s", error_msg.c_str()); _exit(kReconcileSecondaryDexValidationError); } @@ -1957,7 +1966,8 @@ bool reconcile_secondary_dex_file(const std::string& dex_path, result = rmdir_if_empty(oat_dir) && result; } if (!result) { - PLOG(ERROR) << "Failed to clean secondary dex artifacts for location " << dex_path; + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, + "Could not validate secondary dex path %s", dex_path.c_str()); } _exit(result ? kReconcileSecondaryDexCleanedUp : kReconcileSecondaryDexAccessIOError); } @@ -2030,7 +2040,8 @@ bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkg pipe_read.reset(); if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr, uid, storage_flag)) { - LOG(ERROR) << "Could not validate secondary dex path " << dex_path; + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, + "Could not validate secondary dex path %s", dex_path.c_str()); _exit(DexoptReturnCodes::kHashValidatePath); } @@ -2041,6 +2052,8 @@ bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkg _exit(0); } PLOG(ERROR) << "Failed to open secondary dex " << dex_path; + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, + "Failed to open secondary dex %s: %d", dex_path.c_str(), errno); _exit(DexoptReturnCodes::kHashOpenPath); } @@ -2053,7 +2066,8 @@ bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkg if (bytes_read == 0) { break; } else if (bytes_read == -1) { - PLOG(ERROR) << "Failed to read secondary dex " << dex_path; + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, + "Failed to read secondary dex %s: %d", dex_path.c_str(), errno); _exit(DexoptReturnCodes::kHashReadDex); } diff --git a/cmds/installd/file_parsing.h b/cmds/installd/file_parsing.h index 3e2f815f32..88801ca671 100644 --- a/cmds/installd/file_parsing.h +++ b/cmds/installd/file_parsing.h @@ -19,18 +19,14 @@ #include <fstream> #include <functional> -#include <string> +#include <string_view> +#include "android-base/unique_fd.h" namespace android { namespace installd { -bool ParseFile(const std::string& strFile, std::function<bool (const std::string&)> parse) { - std::ifstream input_stream(strFile); - - if (!input_stream.is_open()) { - return false; - } - +template<typename Func> +bool ParseFile(std::istream& input_stream, Func parse) { while (!input_stream.eof()) { // Read the next line. std::string line; @@ -54,6 +50,15 @@ bool ParseFile(const std::string& strFile, std::function<bool (const std::string return true; } +template<typename Func> +bool ParseFile(std::string_view str_file, Func parse) { + std::ifstream ifs(str_file); + if (!ifs.is_open()) { + return false; + } + return ParseFile(ifs, parse); +} + } // namespace installd } // namespace android diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index ed31ad9cfa..6aa32b8d8e 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -26,6 +26,7 @@ #include <sys/capability.h> #include <sys/prctl.h> #include <sys/stat.h> +#include <sys/mman.h> #include <android-base/logging.h> #include <android-base/macros.h> @@ -36,6 +37,7 @@ #include <log/log.h> #include <private/android_filesystem_config.h> +#include "android-base/file.h" #include "dexopt.h" #include "file_parsing.h" #include "globals.h" @@ -195,38 +197,63 @@ private: // export NAME VALUE // For simplicity, don't respect string quotation. The values we are interested in can be // encoded without them. - // init.environ.rc and etc/classpath have the same format for - // environment variable exports and can be matched by the same regex. + // + // init.environ.rc and derive_classpath all have the same format for + // environment variable exports (since they are all meant to be read by + // init) and can be matched by the same regex. + + std::regex export_regex("\\s*export\\s+(\\S+)\\s+(\\S+)"); + auto parse_results = [&](auto& input) { + ParseFile(input, [&](const std::string& line) { + std::smatch export_match; + if (!std::regex_match(line, export_match, export_regex)) { + return true; + } + + if (export_match.size() != 3) { + return true; + } + + std::string name = export_match[1].str(); + std::string value = export_match[2].str(); + + system_properties_.SetProperty(name, value); + + return true; + }); + }; + // TODO Just like with the system-properties above we really should have // common code between init and otapreopt to deal with reading these // things. See b/181182967 + // There have been a variety of places the various env-vars have been + // over the years. Expand or reduce this list as needed. static constexpr const char* kEnvironmentVariableSources[] = { - "/init.environ.rc", "/etc/classpath" + "/init.environ.rc", }; - - std::regex export_regex("\\s*export\\s+(\\S+)\\s+(\\S+)"); + // First get everything from the static files. for (const char* env_vars_file : kEnvironmentVariableSources) { - bool parse_result = ParseFile(env_vars_file, [&](const std::string& line) { - std::smatch export_match; - if (!std::regex_match(line, export_match, export_regex)) { - return true; - } - - if (export_match.size() != 3) { - return true; - } - - std::string name = export_match[1].str(); - std::string value = export_match[2].str(); - - system_properties_.SetProperty(name, value); + parse_results(env_vars_file); + } - return true; - }); - if (!parse_result) { - return false; - } + // Next get everything from derive_classpath, since we're already in the + // chroot it will get the new versions of any dependencies. + { + android::base::unique_fd fd(memfd_create("derive_classpath_temp", MFD_CLOEXEC)); + if (!fd.ok()) { + LOG(ERROR) << "Unable to create fd for derive_classpath"; + return false; + } + std::string memfd_file = StringPrintf("/proc/%d/fd/%d", getpid(), fd.get()); + std::string error_msg; + if (!Exec({"/apex/com.android.sdkext/bin/derive_classpath", memfd_file}, &error_msg)) { + PLOG(ERROR) << "Running derive_classpath failed: " << error_msg; + return false; + } + std::ifstream ifs(memfd_file); + parse_results(ifs); } + if (system_properties_.GetProperty(kAndroidDataPathPropertyName) == nullptr) { return false; } diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index 83f01de01e..c62734a925 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -275,6 +275,7 @@ static int otapreopt_chroot(const int argc, char **arg) { static constexpr const std::string_view kRequiredApexs[] = { "com.android.art", "com.android.runtime", + "com.android.sdkext", // For derive_classpath }; std::array<bool, arraysize(kRequiredApexs)> found_apexs{ false, false }; DIR* apex_dir = opendir("/apex"); diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index f67ab812fa..70820172b7 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -20,6 +20,7 @@ cc_test { "libcutils", ], static_libs: [ + "libasync_safe", "libdiskusage", "libinstalld", "liblog", @@ -44,6 +45,7 @@ cc_test { "server_configurable_flags", ], static_libs: [ + "libasync_safe", "libdiskusage", "libinstalld", "liblog", @@ -84,6 +86,7 @@ cc_test { "server_configurable_flags", ], static_libs: [ + "libasync_safe", "libdiskusage", "libinstalld", "liblog", @@ -124,6 +127,7 @@ cc_test { "server_configurable_flags", ], static_libs: [ + "libasync_safe", "libdiskusage", "libinstalld", "liblog", diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index b429fb3440..90db5091e1 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -481,7 +481,12 @@ void ServiceManager::tryStartService(const std::string& name) { name.c_str()); std::thread([=] { - (void)base::SetProperty("ctl.interface_start", "aidl/" + name); + if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) { + LOG(INFO) << "Tried to start aidl service " << name + << " as a lazy service, but was unable to. Usually this happens when a " + "service is not installed, but if the service is intended to be used as a " + "lazy service, then it may be configured incorrectly."; + } }).detach(); } diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h index 8d1bf99fe7..fb7d09c04d 100644 --- a/include/android/imagedecoder.h +++ b/include/android/imagedecoder.h @@ -739,6 +739,9 @@ int32_t AImageDecoder_getRepeatCount(AImageDecoder* _Nonnull decoder) * skipping frames in an image with such frames may not produce the correct * results. * + * Only supported by {@link ANDROID_BITMAP_FORMAT_RGBA_8888} and + * {@link ANDROID_BITMAP_FORMAT_RGBA_F16}. + * * @param decoder an {@link AImageDecoder} object. * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value * indicating the reason for the failure. @@ -747,6 +750,8 @@ int32_t AImageDecoder_getRepeatCount(AImageDecoder* _Nonnull decoder) * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder * represents an image that is not animated (see * {@link AImageDecoder_isAnimated}) or the AImageDecoder is null. + * - {@link ANDROID_IMAGE_DECODER_INVALID_STATE): The requested + * {@link AndroidBitmapFormat} does not support animation. * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The input appears * to be truncated. The client must call {@link AImageDecoder_rewind} * before calling {@link AImageDecoder_decodeImage} again. @@ -832,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. @@ -856,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); @@ -891,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 b7eafcd6cd..988137112b 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -526,6 +526,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 @@ -534,11 +537,12 @@ void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction, * * \param compatibility The frame rate compatibility of this surface. The compatibility value may * influence the system's choice of display frame rate. To specify a compatibility use the - * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum. + * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum. This parameter is ignored when frameRate is 0. * - * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless. - * A seamless transition is one that doesn't have any visual interruptions, such as a black - * screen for a second or two. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values. + * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this + * surface should be seamless. A seamless transition is one that doesn't have any visual + * interruptions, such as a black screen for a second or two. See the + * ANATIVEWINDOW_CHANGE_FRAME_RATE_* values. This parameter is ignored when frameRate is 0. * * Available since API level 31. */ diff --git a/include/input/Input.h b/include/input/Input.h index bb5ca0ef04..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, }; @@ -318,6 +326,12 @@ private: */ constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN(); +/** + * Invalid value for display size. Used when display size isn't available for an event or doesn't + * matter. This is just a constant 0 so that it has no effect if unused. + */ +constexpr int32_t AMOTION_EVENT_INVALID_DISPLAY_SIZE = 0; + /* * Pointer coordinate data. */ @@ -360,6 +374,8 @@ struct PointerCoords { return getAxisValue(AMOTION_EVENT_AXIS_Y); } + vec2 getXYValue() const { return vec2(getX(), getY()); } + #ifdef __linux__ status_t readFromParcel(Parcel* parcel); status_t writeToParcel(Parcel* parcel) const; @@ -548,6 +564,8 @@ public: void setCursorPosition(float x, float y); + int2 getDisplaySize() const { return {mDisplayWidth, mDisplayHeight}; } + static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); } inline nsecs_t getDownTime() const { return mDownTime; } @@ -570,8 +588,17 @@ public: inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; } + /** + * The actual raw pointer coords: whatever comes from the input device without any external + * transforms applied. + */ const PointerCoords* getRawPointerCoords(size_t pointerIndex) const; + /** + * This is the raw axis value. However, for X/Y axes, this currently applies a "compat-raw" + * transform because many apps (incorrectly) assumed that raw == oriented-screen-space. + * "compat raw" is raw coordinates with screen rotation applied. + */ float getRawAxisValue(int32_t axis, size_t pointerIndex) const; inline float getRawX(size_t pointerIndex) const { @@ -634,9 +661,18 @@ public: return mSampleEventTimes[historicalIndex]; } + /** + * The actual raw pointer coords: whatever comes from the input device without any external + * transforms applied. + */ const PointerCoords* getHistoricalRawPointerCoords( size_t pointerIndex, size_t historicalIndex) const; + /** + * This is the raw axis value. However, for X/Y axes, this currently applies a "compat-raw" + * transform because many apps (incorrectly) assumed that raw == oriented-screen-space. + * "compat raw" is raw coordinates with screen rotation applied. + */ float getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const; @@ -704,9 +740,9 @@ public: int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, float yPrecision, float rawXCursorPosition, - float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime, - size_t pointerCount, const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords); + float rawYCursorPosition, int32_t displayWidth, int32_t displayHeight, + nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); void copyFrom(const MotionEvent* other, bool keepHistory); @@ -718,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; @@ -759,6 +799,8 @@ protected: float mYPrecision; float mRawXCursorPosition; float mRawYCursorPosition; + int32_t mDisplayWidth; + int32_t mDisplayHeight; nsecs_t mDownTime; Vector<PointerProperties> mPointerProperties; std::vector<nsecs_t> mSampleEventTimes; diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index eef057e773..e8ffc64535 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -136,6 +136,8 @@ struct InputMessage { float yPrecision; float xCursorPosition; float yCursorPosition; + int32_t displayWidth; + int32_t displayHeight; uint32_t pointerCount; uint32_t empty3; /** @@ -227,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" @@ -353,8 +355,9 @@ public: int32_t metaState, int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, - uint32_t pointerCount, const PointerProperties* pointerProperties, + float yCursorPosition, int32_t displayWidth, int32_t displayHeight, + nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); /* Publishes a focus event to the input channel. diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index 36097d6d65..121be6d963 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -168,6 +168,10 @@ struct InputWindowInfo : public Parcelable { // Transform applied to individual windows. ui::Transform transform; + // Display size in its natural rotation. Used to rotate raw coordinates for compatibility. + int32_t displayWidth = AMOTION_EVENT_INVALID_DISPLAY_SIZE; + int32_t displayHeight = AMOTION_EVENT_INVALID_DISPLAY_SIZE; + /* * This is filled in by the WM relative to the frame and then translated * to absolute coordinates by SurfaceFlinger once the frame is computed. 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/Binder.cpp b/libs/binder/Binder.cpp index d964d255fe..d5bdd1c803 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -29,6 +29,16 @@ namespace android { +// Service implementations inherit from BBinder and IBinder, and this is frozen +// in prebuilts. +#ifdef __LP64__ +static_assert(sizeof(IBinder) == 24); +static_assert(sizeof(BBinder) == 40); +#else +static_assert(sizeof(IBinder) == 12); +static_assert(sizeof(BBinder) == 20); +#endif + // --------------------------------------------------------------------------- IBinder::IBinder() 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..ef7fd44419 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 diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index a73530938c..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__) @@ -78,7 +78,11 @@ static size_t pad_size(size_t s) { namespace android { // many things compile this into prebuilts on the stack -static_assert(sizeof(Parcel) == 60 || sizeof(Parcel) == 120); +#ifdef __LP64__ +static_assert(sizeof(Parcel) == 120); +#else +static_assert(sizeof(Parcel) == 60); +#endif static std::atomic<size_t> gParcelGlobalAllocCount; static std::atomic<size_t> gParcelGlobalAllocSize; @@ -198,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; @@ -269,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); @@ -282,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); @@ -564,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 { @@ -2495,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/PermissionCache.cpp b/libs/binder/PermissionCache.cpp index 6eae5efc29..670fd55da3 100644 --- a/libs/binder/PermissionCache.cpp +++ b/libs/binder/PermissionCache.cpp @@ -109,5 +109,10 @@ bool PermissionCache::checkPermission( return granted; } +void PermissionCache::purgeCache() { + PermissionCache& pc(PermissionCache::getInstance()); + pc.purge(); +} + // --------------------------------------------------------------------------- } // namespace android 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 1388a801e4..0000000000 --- a/libs/binder/RpcConnection.cpp +++ /dev/null @@ -1,445 +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 <arpa/inet.h> -#include <netdb.h> -#include <netinet/in.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> -#include <unistd.h> - -#include <string_view> - -#include <binder/Parcel.h> -#include <binder/Stability.h> -#include <utils/String8.h> - -#include "RpcState.h" -#include "RpcWireFormat.h" - -#ifdef __GLIBC__ -extern "C" pid_t gettid(); -#endif - -#ifdef __BIONIC__ -#include <linux/vm_sockets.h> -#endif - -namespace android { - -using base::unique_fd; -using AddrInfo = std::unique_ptr<addrinfo, decltype(&freeaddrinfo)>; - -RpcConnection::SocketAddress::~SocketAddress() {} - -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(); -} - -class UnixSocketAddress : public RpcConnection::SocketAddress { -public: - explicit UnixSocketAddress(const char* path) : mAddr({.sun_family = AF_UNIX}) { - unsigned int pathLen = strlen(path) + 1; - LOG_ALWAYS_FATAL_IF(pathLen > sizeof(mAddr.sun_path), "Socket path is too long: %u %s", - pathLen, path); - memcpy(mAddr.sun_path, path, pathLen); - } - virtual ~UnixSocketAddress() {} - std::string toString() const override { - return String8::format("path '%.*s'", static_cast<int>(sizeof(mAddr.sun_path)), - mAddr.sun_path) - .c_str(); - } - const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); } - size_t addrSize() const override { return sizeof(mAddr); } - -private: - sockaddr_un mAddr; -}; - -bool RpcConnection::setupUnixDomainServer(const char* path) { - return setupSocketServer(UnixSocketAddress(path)); -} - -bool RpcConnection::addUnixDomainClient(const char* path) { - return addSocketClient(UnixSocketAddress(path)); -} - -#ifdef __BIONIC__ - -class VsockSocketAddress : public RpcConnection::SocketAddress { -public: - VsockSocketAddress(unsigned int cid, unsigned int port) - : mAddr({ - .svm_family = AF_VSOCK, - .svm_port = port, - .svm_cid = cid, - }) {} - virtual ~VsockSocketAddress() {} - std::string toString() const override { - return String8::format("cid %u port %u", mAddr.svm_cid, mAddr.svm_port).c_str(); - } - const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); } - size_t addrSize() const override { return sizeof(mAddr); } - -private: - sockaddr_vm mAddr; -}; - -bool RpcConnection::setupVsockServer(unsigned int port) { - // realizing value w/ this type at compile time to avoid ubsan abort - constexpr unsigned int kAnyCid = VMADDR_CID_ANY; - - return setupSocketServer(VsockSocketAddress(kAnyCid, port)); -} - -bool RpcConnection::addVsockClient(unsigned int cid, unsigned int port) { - return addSocketClient(VsockSocketAddress(cid, port)); -} - -#endif // __BIONIC__ - -class SocketAddressImpl : public RpcConnection::SocketAddress { -public: - SocketAddressImpl(const sockaddr* addr, size_t size, const String8& desc) - : mAddr(addr), mSize(size), mDesc(desc) {} - [[nodiscard]] std::string toString() const override { - return std::string(mDesc.c_str(), mDesc.size()); - } - [[nodiscard]] const sockaddr* addr() const override { return mAddr; } - [[nodiscard]] size_t addrSize() const override { return mSize; } - void set(const sockaddr* addr, size_t size) { - mAddr = addr; - mSize = size; - } - -private: - const sockaddr* mAddr = nullptr; - size_t mSize = 0; - String8 mDesc; -}; - -AddrInfo GetAddrInfo(const char* addr, unsigned int port) { - addrinfo hint{ - .ai_flags = 0, - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_STREAM, - .ai_protocol = 0, - }; - addrinfo* aiStart = nullptr; - if (int rc = getaddrinfo(addr, std::to_string(port).data(), &hint, &aiStart); 0 != rc) { - ALOGE("Unable to resolve %s:%u: %s", addr, port, gai_strerror(rc)); - return AddrInfo(nullptr, nullptr); - } - if (aiStart == nullptr) { - ALOGE("Unable to resolve %s:%u: getaddrinfo returns null", addr, port); - return AddrInfo(nullptr, nullptr); - } - return AddrInfo(aiStart, &freeaddrinfo); -} - -bool RpcConnection::setupInetServer(unsigned int port) { - auto aiStart = GetAddrInfo("127.0.0.1", port); - if (aiStart == nullptr) return false; - SocketAddressImpl socketAddress(nullptr, 0, String8::format("127.0.0.1:%u", port)); - for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) { - socketAddress.set(ai->ai_addr, ai->ai_addrlen); - if (setupSocketServer(socketAddress)) return true; - } - ALOGE("None of the socket address resolved for 127.0.0.1:%u can be set up as inet server.", - port); - return false; -} - -bool RpcConnection::addInetClient(const char* addr, unsigned int port) { - auto aiStart = GetAddrInfo(addr, port); - if (aiStart == nullptr) return false; - SocketAddressImpl socketAddress(nullptr, 0, String8::format("%s:%u", addr, port)); - for (auto ai = aiStart.get(); ai != nullptr; ai = ai->ai_next) { - socketAddress.set(ai->ai_addr, ai->ai_addrlen); - if (addSocketClient(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::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); -} - -void RpcConnection::join() { - // 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()); - - // 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(clientFd)); - - 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"); -} - -void RpcConnection::setForServer(const wp<RpcServer>& server) { - mForServer = server; -} - -wp<RpcServer> RpcConnection::server() { - return mForServer; -} - -bool RpcConnection::setupSocketServer(const SocketAddress& addr) { - LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Each RpcConnection can only have one server."); - - unique_fd serverFd( - TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0))); - if (serverFd == -1) { - ALOGE("Could not create socket: %s", strerror(errno)); - return false; - } - - if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), addr.addr(), addr.addrSize()))) { - int savedErrno = errno; - ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); - return false; - } - - if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) { - int savedErrno = errno; - ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); - return false; - } - - mServer = std::move(serverFd); - return true; -} - -bool RpcConnection::addSocketClient(const SocketAddress& 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); -} - -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 1fa37bacb3..786e2db3b3 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -19,6 +19,7 @@ #include <sys/socket.h> #include <sys/un.h> +#include <thread> #include <vector> #include <binder/Parcel.h> @@ -26,6 +27,7 @@ #include <log/log.h> #include "RpcState.h" +#include "RpcSocketAddress.h" #include "RpcWireFormat.h" namespace android { @@ -43,22 +45,175 @@ void RpcServer::iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction() mAgreedExperimental = true; } -sp<RpcConnection> RpcServer::addClientConnection() { - LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!"); +bool RpcServer::setupUnixDomainServer(const char* path) { + return setupSocketServer(UnixSocketAddress(path)); +} + +#ifdef __BIONIC__ - auto connection = RpcConnection::make(); - connection->setForServer(sp<RpcServer>::fromExisting(this)); - mConnections.push_back(connection); - return connection; +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; + + return setupSocketServer(VsockSocketAddress(kAnyCid, port)); +} + +#endif // __BIONIC__ + +bool RpcServer::setupInetServer(unsigned int port, unsigned int* assignedPort) { + const char* kAddr = "127.0.0.1"; + + if (assignedPort != nullptr) *assignedPort = 0; + auto aiStart = InetSocketAddress::getAddrInfo(kAddr, 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, kAddr, port); + if (!setupSocketServer(socketAddress)) { + continue; + } + + LOG_ALWAYS_FATAL_IF(socketAddress.addr()->sa_family != AF_INET, "expecting inet"); + sockaddr_in addr{}; + socklen_t len = sizeof(addr); + if (0 != getsockname(mServer.get(), reinterpret_cast<sockaddr*>(&addr), &len)) { + int savedErrno = errno; + ALOGE("Could not getsockname at %s: %s", socketAddress.toString().c_str(), + strerror(savedErrno)); + return false; + } + LOG_ALWAYS_FATAL_IF(len != sizeof(addr), "Wrong socket type: len %zu vs len %zu", + static_cast<size_t>(len), sizeof(addr)); + unsigned int realPort = ntohs(addr.sin_port); + LOG_ALWAYS_FATAL_IF(port != 0 && realPort != port, + "Requesting inet server on %s but it is set up on %u.", + socketAddress.toString().c_str(), realPort); + + if (assignedPort != nullptr) { + *assignedPort = realPort; + } + + return true; + } + ALOGE("None of the socket address resolved for %s:%u can be set up as inet server.", kAddr, + port); + return false; +} + +void RpcServer::setMaxThreads(size_t threads) { + LOG_ALWAYS_FATAL_IF(threads <= 0, "RpcServer is useless without threads"); + LOG_ALWAYS_FATAL_IF(mStarted, "must be called before started"); + mMaxThreads = threads; +} + +size_t RpcServer::getMaxThreads() { + return mMaxThreads; } void RpcServer::setRootObject(const sp<IBinder>& binder) { - LOG_ALWAYS_FATAL_IF(mRootObject != nullptr, "There can only be one root object"); + std::lock_guard<std::mutex> _l(mLock); mRootObject = binder; } sp<IBinder> RpcServer::getRootObject() { + std::lock_guard<std::mutex> _l(mLock); return mRootObject; } +void RpcServer::join() { + LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!"); + + 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."); + } + + while (true) { + unique_fd clientFd( + TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, 0 /*length*/, SOCK_CLOEXEC))); + + if (clientFd < 0) { + ALOGE("Could not accept4 socket: %s", strerror(errno)); + continue; + } + LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get()); + + // TODO(b/183988761): cannot trust this simple ID + LOG_ALWAYS_FATAL_IF(!mAgreedExperimental, "no!"); + int32_t id; + if (sizeof(id) != read(clientFd.get(), &id, sizeof(id))) { + ALOGE("Could not read ID from fd %d", clientFd.get()); + continue; + } + + { + std::lock_guard<std::mutex> _l(mLock); + + sp<RpcSession> session; + if (id == RPC_SESSION_ID_NEW) { + // new client! + 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); + continue; + } + session = it->second; + } + + session->startThread(std::move(clientFd)); + } + } +} + +std::vector<sp<RpcSession>> RpcServer::listSessions() { + std::lock_guard<std::mutex> _l(mLock); + std::vector<sp<RpcSession>> sessions; + for (auto& [id, session] : mSessions) { + (void)id; + sessions.push_back(session); + } + return sessions; +} + +bool RpcServer::setupSocketServer(const RpcSocketAddress& addr) { + LOG_RPC_DETAIL("Setting up socket server %s", addr.toString().c_str()); + + { + std::lock_guard<std::mutex> _l(mLock); + LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "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) { + ALOGE("Could not create socket: %s", strerror(errno)); + return false; + } + + if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), addr.addr(), addr.addrSize()))) { + int savedErrno = errno; + ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); + return false; + } + + if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) { + int savedErrno = errno; + ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno)); + return false; + } + + LOG_RPC_DETAIL("Successfully setup socket server %s", addr.toString().c_str()); + + mServer = std::move(serverFd); + return true; +} + } // namespace android diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp new file mode 100644 index 0000000000..09ec20dbf0 --- /dev/null +++ b/libs/binder/RpcSession.cpp @@ -0,0 +1,376 @@ +/* + * 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/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(mServers.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)); +} + +#ifdef __BIONIC__ + +bool RpcSession::setupVsockClient(unsigned int cid, unsigned int port) { + return setupSocketClient(VsockSocketAddress(cid, port)); +} + +#endif // __BIONIC__ + +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; + } + + addClient(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::getMaxThreads(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::startThread(unique_fd client) { + std::lock_guard<std::mutex> _l(mMutex); + sp<RpcSession> holdThis = sp<RpcSession>::fromExisting(this); + int fd = client.release(); + auto thread = std::thread([=] { + holdThis->join(unique_fd(fd)); + { + std::lock_guard<std::mutex> _l(holdThis->mMutex); + size_t erased = mThreads.erase(std::this_thread::get_id()); + LOG_ALWAYS_FATAL_IF(erased != 0, "Could not erase thread."); + } + }); + 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"); +} + +wp<RpcServer> RpcSession::server() { + return mForServer; +} + +bool RpcSession::setupSocketClient(const RpcSocketAddress& addr) { + { + std::lock_guard<std::mutex> _l(mMutex); + LOG_ALWAYS_FATAL_IF(mClients.size() != 0, + "Must only setup session once, but already has %zu clients", + mClients.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 = getMaxThreads(&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): avoid race w/ accept4 not being called on server + for (size_t tries = 0; tries < 5; tries++) { + if (setupOneSocketClient(addr, mId.value())) break; + usleep(10000); + } + } + + return true; +} + +bool RpcSession::setupOneSocketClient(const RpcSocketAddress& addr, int32_t id) { + 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; + } + + 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()); + + addClient(std::move(serverFd)); + return true; +} + +void RpcSession::addClient(unique_fd fd) { + std::lock_guard<std::mutex> _l(mMutex); + sp<RpcConnection> session = sp<RpcConnection>::make(); + session->fd = std::move(fd); + mClients.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(); + mServers.push_back(session); + + return session; +} + +bool RpcSession::removeServerConnection(const sp<RpcConnection>& connection) { + std::lock_guard<std::mutex> _l(mMutex); + if (auto it = std::find(mServers.begin(), mServers.end(), connection); it != mServers.end()) { + mServers.erase(it); + 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->mClients, mSession->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 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->mClientsOffset = (mSession->mClientsOffset + 1) % mSession->mClients.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->mServers, + 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->mClients.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->mClients.size(), mSession->mServers.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 new file mode 100644 index 0000000000..c6a06cf817 --- /dev/null +++ b/libs/binder/RpcSocketAddress.h @@ -0,0 +1,122 @@ +/* + * 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 <string> + +#include <arpa/inet.h> +#include <netdb.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> + +#ifdef __BIONIC__ +#include <linux/vm_sockets.h> +#endif + +namespace android { + +class RpcSocketAddress { +public: + virtual ~RpcSocketAddress() {} + virtual std::string toString() const = 0; + virtual const sockaddr* addr() const = 0; + virtual size_t addrSize() const = 0; +}; + +class UnixSocketAddress : public RpcSocketAddress { +public: + explicit UnixSocketAddress(const char* path) : mAddr({.sun_family = AF_UNIX}) { + unsigned int pathLen = strlen(path) + 1; + LOG_ALWAYS_FATAL_IF(pathLen > sizeof(mAddr.sun_path), "Socket path is too long: %u %s", + pathLen, path); + memcpy(mAddr.sun_path, path, pathLen); + } + virtual ~UnixSocketAddress() {} + std::string toString() const override { + return String8::format("path '%.*s'", static_cast<int>(sizeof(mAddr.sun_path)), + mAddr.sun_path) + .c_str(); + } + const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); } + size_t addrSize() const override { return sizeof(mAddr); } + +private: + sockaddr_un mAddr; +}; + +#ifdef __BIONIC__ + +class VsockSocketAddress : public RpcSocketAddress { +public: + VsockSocketAddress(unsigned int cid, unsigned int port) + : mAddr({ + .svm_family = AF_VSOCK, + .svm_port = port, + .svm_cid = cid, + }) {} + virtual ~VsockSocketAddress() {} + std::string toString() const override { + return String8::format("cid %u port %u", mAddr.svm_cid, mAddr.svm_port).c_str(); + } + const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); } + size_t addrSize() const override { return sizeof(mAddr); } + +private: + sockaddr_vm mAddr; +}; + +#endif // __BIONIC__ + +class InetSocketAddress : public RpcSocketAddress { +public: + InetSocketAddress(const sockaddr* sockAddr, size_t size, const char* addr, unsigned int port) + : mSockAddr(sockAddr), mSize(size), mAddr(addr), mPort(port) {} + [[nodiscard]] std::string toString() const override { + return String8::format("%s:%u", mAddr, mPort).c_str(); + } + [[nodiscard]] const sockaddr* addr() const override { return mSockAddr; } + [[nodiscard]] size_t addrSize() const override { return mSize; } + + using AddrInfo = std::unique_ptr<addrinfo, decltype(&freeaddrinfo)>; + static AddrInfo getAddrInfo(const char* addr, unsigned int port) { + addrinfo hint{ + .ai_flags = 0, + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_protocol = 0, + }; + addrinfo* aiStart = nullptr; + if (int rc = getaddrinfo(addr, std::to_string(port).data(), &hint, &aiStart); 0 != rc) { + ALOGE("Unable to resolve %s:%u: %s", addr, port, gai_strerror(rc)); + return AddrInfo(nullptr, nullptr); + } + if (aiStart == nullptr) { + ALOGE("Unable to resolve %s:%u: getaddrinfo returns null", addr, port); + return AddrInfo(nullptr, nullptr); + } + return AddrInfo(aiStart, &freeaddrinfo); + } + +private: + const sockaddr* mSockAddr; + size_t mSize; + const char* mAddr; + unsigned int mPort; +}; + +} // namespace android diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index d9341369fa..96190dc03c 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; @@ -232,14 +231,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,8 +246,54 @@ sp<IBinder> RpcState::getRootObject(const base::unique_fd& fd, return reply.readStrongBinder(); } +status_t RpcState::getMaxThreads(const base::unique_fd& fd, const sp<RpcSession>& session, + size_t* maxThreadsOut) { + Parcel data; + data.markForRpc(session); + Parcel reply; + + status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_MAX_THREADS, data, + session, &reply, 0); + if (status != OK) { + ALOGE("Error getting max threads: %s", statusToString(status).c_str()); + return status; + } + + int32_t maxThreads; + status = reply.readInt32(&maxThreads); + if (status != OK) return status; + if (maxThreads <= 0) { + ALOGE("Error invalid max maxThreads: %d", maxThreads); + return BAD_VALUE; + } + + *maxThreadsOut = maxThreads; + return OK; +} + +status_t RpcState::getSessionId(const base::unique_fd& fd, const sp<RpcSession>& session, + int32_t* sessionIdOut) { + Parcel data; + data.markForRpc(session); + Parcel reply; + + status_t status = transact(fd, RpcAddress::zero(), RPC_SPECIAL_TRANSACT_GET_SESSION_ID, data, + session, &reply, 0); + if (status != OK) { + ALOGE("Error getting session ID: %s", statusToString(status).c_str()); + return status; + } + + int32_t sessionId; + status = reply.readInt32(&sessionId); + if (status != OK) return status; + + *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; @@ -309,7 +353,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, @@ -321,7 +365,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) { @@ -331,7 +375,7 @@ 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; } @@ -353,7 +397,7 @@ status_t RpcState::waitForReply(const base::unique_fd& fd, const sp<RpcConnectio reply->ipcSetDataReference(rpcReply->data, command.bodySize - offsetof(RpcWireReply, data), nullptr, 0, cleanup_reply_data); - reply->markForRpc(connection); + reply->markForRpc(session); return OK; } @@ -384,8 +428,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; @@ -393,15 +436,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); } @@ -410,12 +452,12 @@ 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); @@ -424,7 +466,7 @@ status_t RpcState::processTransact(const base::unique_fd& fd, const sp<RpcConnec 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, @@ -436,8 +478,7 @@ 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, +status_t RpcState::processTransactInternal(const base::unique_fd& fd, const sp<RpcSession>& session, std::vector<uint8_t>&& transactionData) { if (transactionData.size() < sizeof(RpcWireTransaction)) { ALOGE("Expecting %zu but got %zu bytes for RpcWireTransaction. Terminating!", @@ -469,7 +510,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(); @@ -499,7 +540,7 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd, } Parcel reply; - reply.markForRpc(connection); + reply.markForRpc(session); if (replyStatus == OK) { Parcel data; @@ -510,29 +551,41 @@ 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); - // special case for 'zero' address (special server commands) - switch (transaction->code) { - case RPC_SPECIAL_TRANSACT_GET_ROOT: { - sp<IBinder> root; - sp<RpcServer> server = connection->server().promote(); - if (server) { - root = server->getRootObject(); - } else { - ALOGE("Root object requested, but no server attached."); - } - replyStatus = reply.writeStrongBinder(root); - break; - } - default: { - replyStatus = UNKNOWN_TRANSACTION; + sp<RpcServer> server = session->server().promote(); + if (server) { + // special case for 'zero' address (special server commands) + switch (transaction->code) { + case RPC_SPECIAL_TRANSACT_GET_ROOT: { + replyStatus = reply.writeStrongBinder(server->getRootObject()); + break; + } + case RPC_SPECIAL_TRANSACT_GET_MAX_THREADS: { + replyStatus = reply.writeInt32(server->getMaxThreads()); + break; + } + 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) + // + // sessions associated with servers must have an ID + // (hence abort) + int32_t id = session->getPrivateAccessorForId().get().value(); + replyStatus = reply.writeInt32(id); + break; + } + default: { + replyStatus = UNKNOWN_TRANSACTION; + } } + } else { + ALOGE("Special command sent, but no server object attached."); } } } @@ -581,7 +634,7 @@ status_t RpcState::processTransactInternal(const base::unique_fd& fd, 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; @@ -670,7 +723,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 f4f5151c73..3f3eb1c2ed 100644 --- a/libs/binder/RpcState.h +++ b/libs/binder/RpcState.h @@ -18,7 +18,7 @@ #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 <queue> @@ -43,55 +43,59 @@ struct RpcWireHeader; /** * Abstracts away management of ref counts and the wire format from - * RpcConnection + * RpcSession */ class RpcState { public: RpcState(); ~RpcState(); - sp<IBinder> getRootObject(const base::unique_fd& fd, const sp<RpcConnection>& connection); + // TODO(b/182940634): combine some special transactions into one "getServerInfo" call? + 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 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(); @@ -100,16 +104,15 @@ private: 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, + const sp<RpcSession>& session, std::vector<uint8_t>&& transactionData); [[nodiscard]] status_t processDecStrong(const base::unique_fd& fd, const RpcWireHeader& command); @@ -163,7 +166,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 60ec6c91bf..c5fa008308 100644 --- a/libs/binder/RpcWireFormat.h +++ b/libs/binder/RpcWireFormat.h @@ -47,8 +47,12 @@ enum : uint32_t { */ enum : uint32_t { RPC_SPECIAL_TRANSACT_GET_ROOT = 0, + RPC_SPECIAL_TRANSACT_GET_MAX_THREADS = 1, + 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/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/IBinder.h b/libs/binder/include/binder/IBinder.h index 31f63c86b8..97c826ce76 100644 --- a/libs/binder/include/binder/IBinder.h +++ b/libs/binder/include/binder/IBinder.h @@ -16,6 +16,7 @@ #pragma once +#include <android-base/unique_fd.h> #include <utils/Errors.h> #include <utils/RefBase.h> #include <utils/String16.h> @@ -49,39 +50,39 @@ class [[clang::lto_visibility_public]] IBinder : public virtual RefBase { public: enum { - FIRST_CALL_TRANSACTION = 0x00000001, - LAST_CALL_TRANSACTION = 0x00ffffff, + FIRST_CALL_TRANSACTION = 0x00000001, + LAST_CALL_TRANSACTION = 0x00ffffff, - PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'), - DUMP_TRANSACTION = B_PACK_CHARS('_','D','M','P'), - SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_','C','M','D'), - INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'), - SYSPROPS_TRANSACTION = B_PACK_CHARS('_', 'S', 'P', 'R'), - EXTENSION_TRANSACTION = B_PACK_CHARS('_', 'E', 'X', 'T'), - DEBUG_PID_TRANSACTION = B_PACK_CHARS('_', 'P', 'I', 'D'), + PING_TRANSACTION = B_PACK_CHARS('_', 'P', 'N', 'G'), + DUMP_TRANSACTION = B_PACK_CHARS('_', 'D', 'M', 'P'), + SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_', 'C', 'M', 'D'), + INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'), + SYSPROPS_TRANSACTION = B_PACK_CHARS('_', 'S', 'P', 'R'), + EXTENSION_TRANSACTION = B_PACK_CHARS('_', 'E', 'X', 'T'), + DEBUG_PID_TRANSACTION = B_PACK_CHARS('_', 'P', 'I', 'D'), // See android.os.IBinder.TWEET_TRANSACTION // Most importantly, messages can be anything not exceeding 130 UTF-8 // characters, and callees should exclaim "jolly good message old boy!" - TWEET_TRANSACTION = B_PACK_CHARS('_', 'T', 'W', 'T'), + TWEET_TRANSACTION = B_PACK_CHARS('_', 'T', 'W', 'T'), // See android.os.IBinder.LIKE_TRANSACTION // Improve binder self-esteem. - LIKE_TRANSACTION = B_PACK_CHARS('_', 'L', 'I', 'K'), + LIKE_TRANSACTION = B_PACK_CHARS('_', 'L', 'I', 'K'), // Corresponds to TF_ONE_WAY -- an asynchronous call. - FLAG_ONEWAY = 0x00000001, + FLAG_ONEWAY = 0x00000001, // Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call // is made - FLAG_CLEAR_BUF = 0x00000020, + FLAG_CLEAR_BUF = 0x00000020, // Private userspace flag for transaction which is being requested from // a vendor context. - FLAG_PRIVATE_VENDOR = 0x10000000, + FLAG_PRIVATE_VENDOR = 0x10000000, }; - IBinder(); + IBinder(); /** * Check if this IBinder implements the interface named by 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/PermissionCache.h b/libs/binder/include/binder/PermissionCache.h index 835a3a8eb8..21aa705ff7 100644 --- a/libs/binder/include/binder/PermissionCache.h +++ b/libs/binder/include/binder/PermissionCache.h @@ -73,6 +73,8 @@ public: static bool checkPermission(const String16& permission, pid_t pid, uid_t uid); + + static void purgeCache(); }; // --------------------------------------------------------------------------- diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h index d29b651f0b..c98151d293 100644 --- a/libs/binder/include/binder/RpcServer.h +++ b/libs/binder/include/binder/RpcServer.h @@ -17,57 +17,95 @@ #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> + // 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 // number of problems. namespace android { +class RpcSocketAddress; + /** * This represents a server of an interface, which may be connected to by any * number of clients over sockets. * - * This object is not (currently) thread safe. All calls to it are expected to - * happen at process startup. + * Usage: + * auto server = RpcServer::make(); + * // only supports one now + * if (!server->setup*Server(...)) { + * :( + * } + * server->join(); */ class RpcServer final : public virtual RefBase { public: static sp<RpcServer> make(); - void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction(); + /** + * This represents a session for responses, e.g.: + * + * process A serves binder a + * process B opens a session to process A + * process B makes binder b and sends it to A + * A uses this 'back session' to send things back to B + */ + [[nodiscard]] bool setupUnixDomainServer(const char* path); +#ifdef __BIONIC__ /** - * Setup a static connection, when the number of clients are known. + * 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. * - * Each call to this function corresponds to a different client, and clients - * each have their own threadpools. + * TODO(b/182914638): IPv6 support * - * TODO(b/167966510): support dynamic creation of connections/threads + * Set |port| to 0 to pick an ephemeral port; see discussion of + * /proc/sys/net/ipv4/ip_local_port_range in ip(7). In this case, |assignedPort| + * will be set to the picked port number, if it is not null. */ - sp<RpcConnection> addClientConnection(); + [[nodiscard]] bool setupInetServer(unsigned int port, unsigned int* assignedPort); + + void iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction(); /** - * Allowing a server to explicitly drop clients would be easy to add here, - * but it is not currently implemented, since users of this functionality - * could not use similar functionality if they are running under real - * binder. + * This must be called before adding a client session. + * + * If this is not specified, this will be a single-threaded server. + * + * TODO(b/185167543): these are currently created per client, but these + * should be shared. */ - // void drop(const sp<RpcConnection>& connection); + void setMaxThreads(size_t threads); + size_t getMaxThreads(); /** * The root object can be retrieved by any client, without any - * authentication. + * authentication. TODO(b/183988761) */ void setRootObject(const sp<IBinder>& binder); + sp<IBinder> getRootObject(); /** - * Root object set with setRootObject + * You must have at least one client session before calling this. + * + * TODO(b/185167543): way to shut down? */ - sp<IBinder> getRootObject(); + void join(); + + /** + * For debugging! + */ + std::vector<sp<RpcSession>> listSessions(); ~RpcServer(); @@ -75,11 +113,17 @@ private: friend sp<RpcServer>; RpcServer(); + 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 sessions on + std::mutex mLock; // for below sp<IBinder> mRootObject; - - std::vector<sp<RpcConnection>> mConnections; // per-client + 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 2395e78de2..3f58b2ce8e 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 @@ -32,103 +34,92 @@ namespace android { class Parcel; class RpcServer; +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(); - - /** - * This represents a connection for responses, e.g.: - * - * process A serves binder a - * process B opens a connection to process A - * process B makes binder b and sends it to A - * A uses this 'back connection' to send things back to B - * - * This should be called once, and then a call should be made to join per - * connection thread. - */ - [[nodiscard]] bool setupUnixDomainServer(const char* path); + static sp<RpcSession> make(); /** * This should be called once per thread, matching 'join' in the remote * process. */ - [[nodiscard]] bool addUnixDomainClient(const char* path); + [[nodiscard]] bool setupUnixDomainClient(const char* path); #ifdef __BIONIC__ /** - * Creates an RPC server at the current port. - */ - [[nodiscard]] bool setupVsockServer(unsigned int port); - - /** * Connects to an RPC server at the CVD & port. */ - [[nodiscard]] bool addVsockClient(unsigned int cvd, unsigned int port); + [[nodiscard]] bool setupVsockClient(unsigned int cvd, unsigned int port); #endif // __BIONIC__ /** - * Creates an RPC server at the current port. - */ - [[nodiscard]] bool setupInetServer(unsigned int port); - - /** * Connects to an RPC server at the given address and port. */ - [[nodiscard]] bool addInetClient(const char* addr, unsigned int port); + [[nodiscard]] bool setupInetClient(const char* addr, unsigned int port); /** * 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 session for the maximum number of threads + * it supports (maximum number of concurrent non-nested synchronous transactions) + */ + status_t getMaxThreads(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); - /** - * Adds a server thread accepting connections. Must be called after - * setup*Server. - */ - void join(); - - ~RpcConnection(); + ~RpcSession(); - void setForServer(const wp<RpcServer>& server); wp<RpcServer> server(); // internal only const std::unique_ptr<RpcState>& state() { return mState; } - class SocketAddress { - public: - virtual ~SocketAddress(); - virtual std::string toString() const = 0; - virtual const sockaddr* addr() const = 0; - virtual size_t addrSize() const = 0; + class PrivateAccessorForId { + private: + friend class RpcSession; + friend class RpcState; + explicit PrivateAccessorForId(const RpcSession* session) : mSession(session) {} + + const std::optional<int32_t> get() { return mSession->mId; } + + const RpcSession* mSession; }; + PrivateAccessorForId getPrivateAccessorForId() const { return PrivateAccessorForId(this); } private: - friend sp<RpcConnection>; - RpcConnection(); + friend PrivateAccessorForId; + friend sp<RpcSession>; + friend RpcServer; + RpcSession(); - struct ConnectionSocket : public RefBase { + status_t readId(); + + void startThread(base::unique_fd client); + void join(base::unique_fd client); + + struct RpcConnection : public RefBase { base::unique_fd fd; // whether this or another thread is currently using this fd to make @@ -136,32 +127,34 @@ private: std::optional<pid_t> exclusiveTid; }; - bool setupSocketServer(const SocketAddress& address); - bool addSocketClient(const SocketAddress& address); - void addClient(base::unique_fd&& fd); - sp<ConnectionSocket> assignServerToThisThread(base::unique_fd&& fd); - bool removeServerSocket(const sp<ConnectionSocket>& socket); + bool setupSocketClient(const RpcSocketAddress& address); + bool setupOneSocketClient(const RpcSocketAddress& address, int32_t sessionId); + void addClient(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 @@ -169,10 +162,10 @@ private: bool mReentrant = false; }; - // On the other side of a connection, for each of mClients here, there should + // On the other side of a session, for each of mClients here, there should // be one of mServers 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 @@ -183,18 +176,26 @@ 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; - base::unique_fd mServer; // socket we are accepting connections on + std::mutex mMutex; // for all below - std::mutex mSocketMutex; // for all below - std::condition_variable mSocketCv; // for mWaitingThreads + 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; + std::vector<sp<RpcConnection>> mClients; + std::vector<sp<RpcConnection>> mServers; + + // 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 mServers) + std::map<std::thread::id, std::thread> mThreads; }; } // namespace android diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index eb103d3d77..b03e24cd1a 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -55,7 +55,9 @@ cc_library { defaults: ["libbinder_ndk_host_user"], host_supported: true, - llndk_stubs: "libbinder_ndk.llndk", + llndk: { + symbol_file: "libbinder_ndk.map.txt", + }, export_include_dirs: [ "include_cpp", @@ -192,13 +194,3 @@ ndk_library { symbol_file: "libbinder_ndk.map.txt", first_version: "29", } - -llndk_library { - name: "libbinder_ndk.llndk", - symbol_file: "libbinder_ndk.map.txt", - export_include_dirs: [ - "include_cpp", - "include_ndk", - "include_platform", - ], -} diff --git a/libs/binder/parcel_fuzzer/main.cpp b/libs/binder/parcel_fuzzer/main.cpp index 332e2ada52..a47b753497 100644 --- a/libs/binder/parcel_fuzzer/main.cpp +++ b/libs/binder/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/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 f303b7c1e3..c0f7c99564 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -35,6 +35,7 @@ cc_test { name: "binderDriverInterfaceTest_IPC_32", defaults: ["binder_test_defaults"], srcs: ["binderDriverInterfaceTest.cpp"], + header_libs: ["libbinder_headers"], compile_multilib: "32", multilib: { lib32: { suffix: "" } }, cflags: ["-DBINDER_IPC_32BIT=1"], @@ -49,7 +50,7 @@ cc_test { cflags: ["-DBINDER_IPC_32BIT=1"], }, }, - + header_libs: ["libbinder_headers"], srcs: ["binderDriverInterfaceTest.cpp"], test_suites: ["device-tests", "vts"], } 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..5676bd1939 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -31,11 +31,11 @@ #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]) diff --git a/libs/binder/tests/binderRpcBenchmark.cpp b/libs/binder/tests/binderRpcBenchmark.cpp index 7c82226aef..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); @@ -121,21 +121,18 @@ int main(int argc, char** argv) { std::thread([addr]() { sp<RpcServer> server = RpcServer::make(); server->setRootObject(sp<MyBinderRpcBenchmark>::make()); - server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction(); - - sp<RpcConnection> connection = server->addClientConnection(); - CHECK(connection->setupUnixDomainServer(addr.c_str())); - - connection->join(); + CHECK(server->setupUnixDomainServer(addr.c_str())); + server->join(); }).detach(); for (size_t tries = 0; tries < 5; tries++) { usleep(10000); - if (gConnection->addUnixDomainClient(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 dd68fdbc6d..8d10727c5b 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -17,6 +17,7 @@ #include <BnBinderRpcSession.h> #include <BnBinderRpcTest.h> #include <aidl/IBinderRpcTest.h> +#include <android-base/file.h> #include <android-base/logging.h> #include <android/binder_auto_utils.h> #include <android/binder_libbinder.h> @@ -24,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> @@ -77,7 +78,7 @@ std::atomic<int32_t> MyBinderRpcSession::gNum; class MyBinderRpcTest : public BnBinderRpcTest { public: - sp<RpcConnection> connection; + wp<RpcServer> server; Status sendString(const std::string& str) override { (void)str; @@ -87,13 +88,20 @@ public: *strstr = str + str; return Status::ok(); } - Status countBinders(int32_t* out) override { - if (connection == nullptr) { + Status countBinders(std::vector<int32_t>* out) override { + sp<RpcServer> spServer = server.promote(); + if (spServer == nullptr) { return Status::fromExceptionCode(Status::EX_NULL_POINTER); } - *out = connection->state()->countBinders(); - if (*out != 1) { - 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); } return Status::ok(); } @@ -176,14 +184,27 @@ public: }; sp<IBinder> MyBinderRpcTest::mHeldBinder; +class Pipe { +public: + Pipe() { CHECK(android::base::Pipe(&mRead, &mWrite)); } + Pipe(Pipe&&) = default; + android::base::borrowed_fd readEnd() { return mRead; } + android::base::borrowed_fd writeEnd() { return mWrite; } + +private: + android::base::unique_fd mRead; + android::base::unique_fd mWrite; +}; + class Process { public: - Process(const std::function<void()>& f) { + Process(Process&&) = default; + Process(const std::function<void(Pipe*)>& f) { if (0 == (mPid = fork())) { // racey: assume parent doesn't crash before this is set prctl(PR_SET_PDEATHSIG, SIGHUP); - f(); + f(&mPipe); } } ~Process() { @@ -191,9 +212,11 @@ public: kill(mPid, SIGKILL); } } + Pipe* getPipe() { return &mPipe; } private: pid_t mPid = 0; + Pipe mPipe; }; static std::string allocateSocketAddress() { @@ -202,48 +225,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() { - 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; - wp<RpcConnection> weakConnection = connection; - connection = nullptr; - EXPECT_EQ(nullptr, weakConnection.promote()) << "Leaked connection"; + EXPECT_NE(nullptr, session); + EXPECT_NE(nullptr, session->state()); + EXPECT_EQ(0, session->state()->countBinders()) << (session->state()->dump(), "dump:"); + + 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() { - 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; @@ -277,98 +315,99 @@ 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>&, const sp<RpcConnection>&)>& configure) { - CHECK_GT(numThreads, 0); + 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(); unlink(addr.c_str()); - static unsigned int port = 3456; - port++; + static unsigned int vsockPort = 3456; + vsockPort++; - auto ret = ProcessConnection{ - .host = Process([&] { + auto ret = ProcessSession{ + .host = Process([&](Pipe* pipe) { sp<RpcServer> server = RpcServer::make(); server->iUnderstandThisCodeIsExperimentalAndIWillNotUseItInProduction(); - - // server supporting one client on one socket - sp<RpcConnection> connection = server->addClientConnection(); + server->setMaxThreads(numThreads); switch (socketType) { case SocketType::UNIX: - CHECK(connection->setupUnixDomainServer(addr.c_str())) << addr; + CHECK(server->setupUnixDomainServer(addr.c_str())) << addr; break; #ifdef __BIONIC__ case SocketType::VSOCK: - CHECK(connection->setupVsockServer(port)); + CHECK(server->setupVsockServer(vsockPort)); break; #endif // __BIONIC__ - case SocketType::INET: - CHECK(connection->setupInetServer(port)); + 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"); } - configure(server, connection); + configure(server); - // accept 'numThreads' connections - std::vector<std::thread> pool; - for (size_t i = 0; i + 1 < numThreads; i++) { - pool.push_back(std::thread([=] { connection->join(); })); - } - connection->join(); - for (auto& t : pool) t.join(); + server->join(); }), - .connection = RpcConnection::make(), }; - // create remainder of connections - for (size_t i = 0; i < numThreads; i++) { - for (size_t tries = 0; tries < 5; tries++) { + unsigned int inetPort = 0; + if (socketType == SocketType::INET) { + CHECK(android::base::ReadFully(ret.host.getPipe()->readEnd(), &inetPort, + sizeof(inetPort))); + CHECK_NE(0, inetPort); + } + + for (size_t i = 0; i < numSessions; i++) { + sp<RpcSession> session = RpcSession::make(); + for (size_t tries = 0; tries < 10; tries++) { usleep(10000); switch (socketType) { case SocketType::UNIX: - if (ret.connection->addUnixDomainClient(addr.c_str())) goto success; + if (session->setupUnixDomainClient(addr.c_str())) goto success; break; #ifdef __BIONIC__ case SocketType::VSOCK: - if (ret.connection->addVsockClient(VMADDR_CID_LOCAL, port)) goto success; + if (session->setupVsockClient(VMADDR_CID_LOCAL, vsockPort)) goto success; break; #endif // __BIONIC__ case SocketType::INET: - if (ret.connection->addInetClient("127.0.0.1", port)) goto success; + if (session->setupInetClient("127.0.0.1", inetPort)) goto success; break; default: LOG_ALWAYS_FATAL("Unknown socket type"); } } LOG_ALWAYS_FATAL("Could not connect"); - success:; + success: + ret.sessions.push_back({session, session->getRootObject()}); } - - ret.rootBinder = ret.connection->getRootObject(); return ret; } - BinderRpcTestProcessConnection createRpcTestSocketServerProcess(size_t numThreads) { - BinderRpcTestProcessConnection ret{ - .proc = createRpcTestSocketServerProcess(numThreads, - [&](const sp<RpcServer>& server, - const sp<RpcConnection>& connection) { + BinderRpcTestProcessSession createRpcTestSocketServerProcess(size_t numThreads, + size_t numSessions = 1) { + BinderRpcTestProcessSession ret{ + .proc = createRpcTestSocketServerProcess(numThreads, numSessions, + [&](const sp<RpcServer>& server) { sp<MyBinderRpcTest> service = new MyBinderRpcTest; server->setRootObject(service); - service->connection = - connection; // for testing only + service->server = server; }), }; - ret.rootBinder = ret.proc.rootBinder; + ret.rootBinder = ret.proc.sessions.at(0).root; ret.rootIface = interface_cast<IBinderRpcTest>(ret.rootBinder); return ret; @@ -376,18 +415,12 @@ public: }; TEST_P(BinderRpc, RootObjectIsNull) { - auto proc = createRpcTestSocketServerProcess(1, - [](const sp<RpcServer>& server, - const sp<RpcConnection>&) { - // this is the default, but to be explicit - server->setRootObject(nullptr); - }); - - // retrieved by getRootObject when process is created above - EXPECT_EQ(nullptr, proc.rootBinder); + auto proc = createRpcTestSocketServerProcess(1, 1, [](const sp<RpcServer>& server) { + // this is the default, but to be explicit + server->setRootObject(nullptr); + }); - // 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) { @@ -402,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; @@ -540,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); @@ -549,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); @@ -833,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; } } 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..37fb8448ee 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -666,12 +666,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); } } 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 517b49e6b5..267db7686a 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -98,7 +98,6 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.write, transparentRegion); SAFE_PARCEL(output.writeUint32, transform); SAFE_PARCEL(output.writeBool, transformToDisplayInverse); - SAFE_PARCEL(output.write, crop); SAFE_PARCEL(output.write, orientedDisplaySpaceRect); if (buffer) { @@ -167,6 +166,7 @@ status_t layer_state_t::write(Parcel& output) const } SAFE_PARCEL(output.write, stretchEffect); + SAFE_PARCEL(output.write, bufferCrop); return NO_ERROR; } @@ -209,7 +209,6 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.read, transparentRegion); SAFE_PARCEL(input.readUint32, &transform); SAFE_PARCEL(input.readBool, &transformToDisplayInverse); - SAFE_PARCEL(input.read, crop); SAFE_PARCEL(input.read, orientedDisplaySpaceRect); bool tmpBool = false; @@ -296,6 +295,7 @@ status_t layer_state_t::read(const Parcel& input) } SAFE_PARCEL(input.read, stretchEffect); + SAFE_PARCEL(input.read, bufferCrop); return NO_ERROR; } @@ -539,6 +539,10 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eStretchChanged; stretchEffect = other.stretchEffect; } + if (other.what & eBufferCropChanged) { + what |= eBufferCropChanged; + bufferCrop = other.bufferCrop; + } 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/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 5db0eae416..11b8ebac81 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1648,8 +1648,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; @@ -1657,10 +1656,22 @@ 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; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBufferCrop( + const sp<SurfaceControl>& sc, const Rect& bufferCrop) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eBufferCropChanged; + s->bufferCrop = bufferCrop; + + registerSurfaceControlForCallback(sc); return *this; } diff --git a/libs/gui/include/gui/JankInfo.h b/libs/gui/include/gui/JankInfo.h index 85ae9cb2bc..ce9716f1fe 100644 --- a/libs/gui/include/gui/JankInfo.h +++ b/libs/gui/include/gui/JankInfo.h @@ -42,6 +42,10 @@ enum JankType { BufferStuffing = 0x40, // Jank due to unknown reasons. Unknown = 0x80, + // SF is said to be stuffed if the previous frame ran longer than expected resulting in the case + // where the previous frame was presented in the current frame's expected vsync. This pushes the + // current frame to the next vsync. The behavior is similar to BufferStuffing. + SurfaceFlingerStuffing = 0x100, }; } // namespace android diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index b4f62f2206..3947f22462 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -85,7 +85,7 @@ struct layer_state_t { eReleaseBufferListenerChanged = 0x00000400, eShadowRadiusChanged = 0x00000800, eLayerCreated = 0x00001000, - /* was eDetachChildren, now available 0x00002000, */ + eBufferCropChanged = 0x00002000, eRelativeLayerChanged = 0x00004000, eReparent = 0x00008000, eColorChanged = 0x00010000, @@ -227,6 +227,8 @@ struct layer_state_t { // Stretch effect to be applied to this layer StretchEffect stretchEffect; + Rect bufferCrop; + // 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 // buffer id to identify the buffer. diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 5bbd8e3d85..35757be988 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -538,9 +538,22 @@ 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); status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 5600eb3f5e..649a14087e 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -340,7 +340,8 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int3 int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, float yPrecision, - float rawXCursorPosition, float rawYCursorPosition, nsecs_t downTime, + float rawXCursorPosition, float rawYCursorPosition, + int32_t displayWidth, int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { @@ -357,6 +358,8 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int3 mYPrecision = yPrecision; mRawXCursorPosition = rawXCursorPosition; mRawYCursorPosition = rawYCursorPosition; + mDisplayWidth = displayWidth; + mDisplayHeight = displayHeight; mDownTime = downTime; mPointerProperties.clear(); mPointerProperties.appendArray(pointerProperties, pointerCount); @@ -380,6 +383,8 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mYPrecision = other->mYPrecision; mRawXCursorPosition = other->mRawXCursorPosition; mRawYCursorPosition = other->mRawYCursorPosition; + mDisplayWidth = other->mDisplayWidth; + mDisplayHeight = other->mDisplayHeight; mDownTime = other->mDownTime; mPointerProperties = other->mPointerProperties; @@ -426,7 +431,7 @@ const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const } float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const { - return getRawPointerCoords(pointerIndex)->getAxisValue(axis); + return getHistoricalRawAxisValue(axis, pointerIndex, getHistorySize()); } float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { @@ -440,7 +445,32 @@ const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const { - return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); + 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); + } + + // 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; + } + static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); + return xy[axis]; } float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, @@ -449,19 +479,10 @@ float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); } - float rawX = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getX(); - float rawY = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getY(); - vec2 vals = mTransform.transform(rawX, rawY); - - switch (axis) { - case AMOTION_EVENT_AXIS_X: - return vals.x; - case AMOTION_EVENT_AXIS_Y: - return vals.y; - } - - // This should never happen - return 0; + vec2 vals = mTransform.transform( + getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getXYValue()); + static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); + return vals[axis]; } ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const { @@ -548,6 +569,24 @@ void MotionEvent::transform(const std::array<float, 9>& matrix) { } } +void MotionEvent::applyTransform(const std::array<float, 9>& matrix) { + // Determine how the origin is transformed by the matrix so that we + // can transform orientation vectors. + vec2 origin = transformPoint(matrix, 0, 0); + + // 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)); + vec2 xy = transformPoint(matrix, c.getX(), c.getY()); + c.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x); + c.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y); + } +} + #ifdef __linux__ static status_t readFromParcel(ui::Transform& transform, const Parcel& parcel) { float dsdx, dtdx, tx, dtdy, dsdy, ty; @@ -606,6 +645,8 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mYPrecision = parcel->readFloat(); mRawXCursorPosition = parcel->readFloat(); mRawYCursorPosition = parcel->readFloat(); + mDisplayWidth = parcel->readInt32(); + mDisplayHeight = parcel->readInt32(); mDownTime = parcel->readInt64(); mPointerProperties.clear(); @@ -665,6 +706,8 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeFloat(mYPrecision); parcel->writeFloat(mRawXCursorPosition); parcel->writeFloat(mRawYCursorPosition); + parcel->writeInt32(mDisplayWidth); + parcel->writeInt32(mDisplayHeight); parcel->writeInt64(mDownTime); for (size_t i = 0; i < pointerCount; i++) { 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 3657fcfdfe..97f4e66e38 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; } @@ -228,6 +242,10 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.motion.xCursorPosition = body.motion.xCursorPosition; // float yCursorPosition msg->body.motion.yCursorPosition = body.motion.yCursorPosition; + // int32_t displayW + msg->body.motion.displayWidth = body.motion.displayWidth; + // int32_t displayH + msg->body.motion.displayHeight = body.motion.displayHeight; // uint32_t pointerCount msg->body.motion.pointerCount = body.motion.pointerCount; //struct Pointer pointers[MAX_POINTERS] @@ -400,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; } @@ -517,9 +533,9 @@ status_t InputPublisher::publishMotionEvent( std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, - float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, - nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords) { + float yPrecision, float xCursorPosition, float yCursorPosition, int32_t displayWidth, + int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { if (ATRACE_ENABLED()) { std::string message = StringPrintf( "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")", @@ -577,6 +593,8 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.yPrecision = yPrecision; msg.body.motion.xCursorPosition = xCursorPosition; msg.body.motion.yCursorPosition = yCursorPosition; + msg.body.motion.displayWidth = displayWidth; + msg.body.motion.displayHeight = displayHeight; msg.body.motion.downTime = downTime; msg.body.motion.eventTime = eventTime; msg.body.motion.pointerCount = pointerCount; @@ -1354,6 +1372,7 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage msg->body.motion.buttonState, msg->body.motion.classification, transform, msg->body.motion.xPrecision, msg->body.motion.yPrecision, msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, + msg->body.motion.displayWidth, msg->body.motion.displayHeight, msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); } diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index fe8a5677d9..767878b7c3 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -49,8 +49,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 601d8dabf7..32b72ba616 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -271,6 +271,7 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, mTransform, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); @@ -592,6 +593,7 @@ TEST_F(MotionEventTest, Transform) { AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, identityTransform, 0 /*xPrecision*/, 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); float originalRawX = 0 + 3; @@ -634,6 +636,97 @@ TEST_F(MotionEventTest, Transform) { ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); } +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; + xform.set(20, 40); + MotionEvent event = createTouchDownEvent(60, 100, xform); + ASSERT_EQ(60, event.getRawX(0)); + ASSERT_EQ(100, event.getRawY(0)); + ASSERT_NE(event.getRawX(0), event.getX(0)); + ASSERT_NE(event.getRawY(0), event.getY(0)); + } + + // Next check that getRaw contains rotation (for compatibility) but otherwise is still + // "Screen-space". The following tests check all 3 rotations. + { + // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). + 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)); + } + + { + // Same as above, but check rotate-180. + ui::Transform xform(ui::Transform::ROT_180, 400, 800); + xform.set(xform.tx() + 20, xform.ty() + 40); + MotionEvent event = createTouchDownEvent(60, 100, xform); + ASSERT_EQ(340, event.getRawX(0)); + ASSERT_EQ(700, event.getRawY(0)); + } + + { + // Same as above, but check rotate-270. + ui::Transform xform(ui::Transform::ROT_270, 800, 400); + xform.set(xform.tx() + 20, xform.ty() + 40); + MotionEvent event = createTouchDownEvent(60, 100, xform); + ASSERT_EQ(100, event.getRawX(0)); + ASSERT_EQ(340, event.getRawY(0)); + } +} + TEST_F(MotionEventTest, Initialize_SetsClassification) { std::array<MotionClassification, 3> classifications = { MotionClassification::NONE, @@ -657,7 +750,8 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); } @@ -678,8 +772,10 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0, - 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, - 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); + 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, + 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, + pointerCoords); event.offsetLocation(20, 60); ASSERT_EQ(280, event.getRawXCursorPosition()); ASSERT_EQ(540, event.getRawYCursorPosition()); diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index db36b353b1..2e85d24712 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -166,6 +166,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr float yPrecision = 0.5; constexpr float xCursorPosition = 1.3; constexpr float yCursorPosition = 50.6; + constexpr int32_t displayWidth = 1000; + constexpr int32_t displayHeight = 2000; constexpr nsecs_t downTime = 3; constexpr size_t pointerCount = 3; constexpr nsecs_t eventTime = 4; @@ -194,8 +196,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action, actionButton, flags, edgeFlags, metaState, buttonState, classification, transform, xPrecision, yPrecision, - xCursorPosition, yCursorPosition, downTime, eventTime, - pointerCount, pointerProperties, pointerCoords); + xCursorPosition, yCursorPosition, displayWidth, + displayHeight, downTime, eventTime, pointerCount, + pointerProperties, pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; @@ -236,6 +239,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition()); EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition()); EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition()); + EXPECT_EQ(displayWidth, motionEvent->getDisplaySize().x); + EXPECT_EQ(displayHeight, motionEvent->getDisplaySize().y); EXPECT_EQ(downTime, motionEvent->getDownTime()); EXPECT_EQ(eventTime, motionEvent->getEventTime()); EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); @@ -475,7 +480,7 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; @@ -491,7 +496,7 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; @@ -512,7 +517,7 @@ TEST_F(InputPublisherAndConsumerTest, status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 585779e472..5861d55156 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -74,9 +74,11 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124); CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128); CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132); - CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 136); - CHECK_OFFSET(InputMessage::Body::Motion, empty3, 140); - CHECK_OFFSET(InputMessage::Body::Motion, pointers, 144); + CHECK_OFFSET(InputMessage::Body::Motion, displayWidth, 136); + CHECK_OFFSET(InputMessage::Body::Motion, displayHeight, 140); + CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 144); + CHECK_OFFSET(InputMessage::Body::Motion, empty3, 148); + CHECK_OFFSET(InputMessage::Body::Motion, pointers, 152); CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0); CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index d049d05ac5..aefc2ec47d 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -183,7 +183,8 @@ static std::vector<MotionEvent> createMotionEventStream( AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, identityTransform, 0 /*xPrecision*/, 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); events.emplace_back(event); diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp index 36f87b8a6a..f79098c63c 100644 --- a/libs/input/tests/VerifiedInputEvent_test.cpp +++ b/libs/input/tests/VerifiedInputEvent_test.cpp @@ -46,8 +46,10 @@ static MotionEvent getMotionEventWithFlags(int32_t flags) { INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, - 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 100 /*downTime*/, - 200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); + 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, + 100 /*downTime*/, 200 /*eventTime*/, pointerCount, pointerProperties, + pointerCoords); return event; } diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index 8675439938..928600999c 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -60,7 +60,13 @@ ndk_library { cc_library { name: "libnativewindow", - llndk_stubs: "libnativewindow.llndk", + llndk: { + symbol_file: "libnativewindow.map.txt", + unversioned: true, + override_export_include_dirs: [ + "include" + ], + }, export_include_dirs: [ "include", "include-private", @@ -115,11 +121,4 @@ cc_library { }, } -llndk_library { - name: "libnativewindow.llndk", - symbol_file: "libnativewindow.map.txt", - unversioned: true, - export_include_dirs: ["include"], -} - subdirs = ["tests"] diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index 61b3f94aab..f0e1c4d749 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -300,8 +300,13 @@ 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. + * * \param frameRate The intended frame rate of this window, 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 @@ -309,15 +314,16 @@ enum ANativeWindow_ChangeFrameRateStrategy { * valid refresh rate for this device's display - e.g., it's fine to pass 30fps * to a device that can only run the display at 60fps. * - * \param window pointer to an ANativeWindow object. - * * \param compatibility The frame rate compatibility of this window. The * compatibility value may influence the system's choice of display refresh * rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info. + * This parameter is ignored when frameRate is 0. * - * \param changeFrameRateStrategy Whether display refresh rate transitions should be seamless. + * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this + * window should be seamless. * A seamless transition is one that doesn't have any visual interruptions, such as a black * screen for a second or two. See the ANATIVEWINDOW_CHANGE_FRAME_RATE_* values. + * This parameter is ignored when frameRate is 0. * * \return 0 for success, -EINVAL if the window, frame rate, or compatibility * value are invalid. 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/permission/include/binder/AppOpsManager.h b/libs/permission/include/binder/AppOpsManager.h index c048cbed37..e3d705fa35 100644 --- a/libs/permission/include/binder/AppOpsManager.h +++ b/libs/permission/include/binder/AppOpsManager.h @@ -146,7 +146,8 @@ public: OP_UWB_RANGING = 112, OP_ACTIVITY_RECOGNITION_SOURCE = 113, OP_BLUETOOTH_ADVERTISE = 114, - _NUM_OP = 115 + OP_RECORD_INCOMING_PHONE_AUDIO = 115, + _NUM_OP = 116 }; AppOpsManager(); 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..940003750e 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); diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h index c54c5ba047..6e983524ce 100644 --- a/libs/renderengine/include/renderengine/LayerSettings.h +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -156,6 +156,10 @@ struct LayerSettings { std::vector<BlurRegion> blurRegions; + // Transform matrix used to convert the blurRegions geometry into the same + // coordinate space as LayerSettings.geometry + mat4 blurRegionTransform = mat4(); + StretchEffect stretchEffect; // Name associated with the layer for debugging purposes. @@ -218,7 +222,9 @@ static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs 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.blurRegionTransform == rhs.blurRegionTransform && + lhs.stretchEffect == rhs.stretchEffect; } // Defining PrintTo helps with Google Tests. @@ -268,6 +274,21 @@ 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 << "\n .geometry = "; @@ -286,6 +307,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 1c2b2fc3ca..48d76cc475 100644 --- a/libs/renderengine/skia/Cache.cpp +++ b/libs/renderengine/skia/Cache.cpp @@ -37,12 +37,15 @@ 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 choosing dataspaces below, whether the match the destination or not determined whether -// a color correction effect is added to the shader. There may be other additional shader details -// for particular color spaces. -// TODO(b/184842383) figure out which color related shaders are necessary +// When setting layer.sourceDataspace, whether it matches the destination or not determines whether +// a color correction effect is added to the shader. constexpr auto kDestDataSpace = ui::Dataspace::SRGB; +constexpr auto kOtherDataSpace = ui::Dataspace::DISPLAY_P3; } // namespace static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, @@ -115,20 +118,24 @@ static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySetting // Test both drawRect and drawRRect auto layers = std::vector<const LayerSettings*>{&layer}; - for (bool identity : {true, false}) { - layer.geometry.positionTransform = identity ? mat4() : kScaleAndTranslate; - // Corner radii less than 0.5 creates a special shader. This likely occurs in real usage - // due to animating corner radius. - // For the non-idenity matrix, only the large corner radius will create a new shader. - for (float roundedCornersRadius : identity ? threeCornerRadii : oneCornerRadius) { - // roundedCornersCrop is always set, but it is this radius that triggers the behavior - layer.geometry.roundedCornersRadius = roundedCornersRadius; - for (bool isOpaque : {true, false}) { - layer.source.buffer.isOpaque = isOpaque; - for (auto alpha : {half(.23999f), half(1.0f)}) { - layer.alpha = alpha; - renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, - base::unique_fd(), nullptr); + for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) { + layer.sourceDataspace = dataspace; + for (bool identity : {true, false}) { + layer.geometry.positionTransform = identity ? mat4() : kScaleAndTranslate; + // Corner radii less than 0.5 creates a special shader. This likely occurs in real usage + // due to animating corner radius. + // For the non-idenity matrix, only the large corner radius will create a new shader. + for (float roundedCornersRadius : identity ? threeCornerRadii : oneCornerRadius) { + // roundedCornersCrop is always set, but it is this radius that triggers the + // behavior + layer.geometry.roundedCornersRadius = roundedCornersRadius; + for (bool isOpaque : {true, false}) { + layer.source.buffer.isOpaque = isOpaque; + for (auto alpha : {half(.23999f), half(1.0f)}) { + layer.alpha = alpha; + renderengine->drawLayers(display, layers, dstTexture, kUseFrameBufferCache, + base::unique_fd(), nullptr); + } } } } @@ -182,6 +189,37 @@ 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) { + const Rect& displayRect = display.physicalDisplay; + FloatRect rect(0, 0, displayRect.width(), displayRect.height()); + LayerSettings layer{ + .geometry = + Geometry{ + .boundaries = rect, + .roundedCornersCrop = rect, + .positionTransform = kScaleAndTranslate, + .roundedCornersRadius = 300, + }, + .source = PixelSource{.buffer = + Buffer{ + .buffer = srcTexture, + .maxMasteringLuminance = 1000.f, + .maxContentLuminance = 1000.f, + .textureTransform = kScaleYOnly, + }}, + .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); + } +} + // // The collection of shaders cached here were found by using perfetto to record shader compiles // during actions that involve RenderEngine, logging the layer settings, and the shader code @@ -200,60 +238,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, + }; - sp<GraphicBuffer> dstBuffer = - new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1, - usage, "primeShaderCache_dst"); + const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; - 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"); + sp<GraphicBuffer> dstBuffer = + new GraphicBuffer(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, + 1, usage, "primeShaderCache_dst"); - const auto srcTexture = - std::make_shared<ExternalTexture>(srcBuffer, *renderengine, - ExternalTexture::Usage::READABLE | - ExternalTexture::Usage::WRITEABLE); + 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"); - 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); + const auto srcTexture = + std::make_shared<ExternalTexture>(srcBuffer, *renderengine, + ExternalTexture::Usage::READABLE | + ExternalTexture::Usage::WRITEABLE); - // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; - const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE; + 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); - 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); + // should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; + const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE; - 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); + 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); + + 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 377b6f89ad..c5ad64ea22 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,18 +505,18 @@ 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 + // because GPU resources for protected buffers is much more limited. + const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED; + if (isProtectedBuffer) { return; } + ATRACE_CALL(); - auto grContext = mInProtectedContext ? mProtectedGrContext : mGrContext; + // 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 = mInProtectedContext ? mProtectedTextureCache : mTextureCache; std::lock_guard<std::mutex> lock(mRenderingMutex); @@ -508,13 +524,11 @@ void SkiaGLRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffe 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 +555,19 @@ 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; + if (stretchEffect.hasEffect()) { + const auto targetBuffer = layer->source.buffer.buffer; + const auto graphicsBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr; + if (graphicsBuffer && shader) { + shader = mStretchShaderFactory.createSkShader(shader, stretchEffect); + } } + if (requiresLinearEffect) { const ui::Dataspace inputDataspace = mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::UNKNOWN; @@ -634,6 +653,44 @@ private: int mSaveCount; }; +void drawStretch(const SkRect& bounds, const StretchEffect& stretchEffect, + SkCanvas* canvas, const SkPaint& paint) { + float top = bounds.top(); + float left = bounds.left(); + float bottom = bounds.bottom(); + float right = bounds.right(); + // Adjust the drawing bounds based on the stretch itself. + float stretchOffsetX = + round(bounds.width() * stretchEffect.getStretchWidthMultiplier()); + float stretchOffsetY = + round(bounds.height() * stretchEffect.getStretchHeightMultiplier()); + if (stretchEffect.vectorY < 0.f) { + top -= stretchOffsetY; + } else if (stretchEffect.vectorY > 0.f){ + bottom += stretchOffsetY; + } + + if (stretchEffect.vectorX < 0.f) { + left -= stretchOffsetX; + } else if (stretchEffect.vectorX > 0.f) { + right += stretchOffsetX; + } + + auto stretchBounds = SkRect::MakeLTRB(left, top, right, bottom); + canvas->drawRect(stretchBounds, paint); +} + +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 +719,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 +727,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 +735,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 +865,7 @@ 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); if (mBlurFilter && layerHasBlur(layer)) { std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs; @@ -819,30 +875,40 @@ 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); } + + 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); } } @@ -855,7 +921,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, if (layer->shadow.length > 0) { const auto rect = layer->geometry.roundedCornersRadius > 0 ? getSkRect(layer->geometry.roundedCornersCrop) - : bounds; + : bounds.rect(); // 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); @@ -895,7 +961,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); } @@ -904,7 +970,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 @@ -920,7 +986,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; @@ -982,11 +1048,25 @@ 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); + auto& stretchEffect = layer->stretchEffect; + // TODO (njawad) temporarily disable manipulation of geometry + // the layer bounds will be updated in HWUI instead of RenderEngine + // in a subsequent CL + // Keep the method call in a dead code path to make -Werror happy + // with unused methods + if (stretchEffect.hasEffect() && /* DISABLES CODE */ (false)) { + drawStretch(bounds.rect(), stretchEffect, canvas, paint); + } else { + canvas->drawRect(bounds.rect(), paint); + } } if (kFlushAfterEveryLayer) { ATRACE_NAME("flush surface"); @@ -1034,25 +1114,76 @@ 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); +inline std::pair<SkRRect, SkRRect> SkiaGLRenderEngine::getBoundsAndClip( + const LayerSettings* layer) { + const auto bounds = getSkRect(layer->geometry.boundaries); + const auto crop = getSkRect(layer->geometry.roundedCornersCrop); const auto cornerRadius = layer->geometry.roundedCornersRadius; - return SkRRect::MakeRectXY(rect, 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)}; + SkRRect clip; + if (cornerRadius > 0) { + // it the crop and the bounds are equivalent then we don't need a clip + if (bounds == crop) { + 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); + + // compute the UpperLeft corner radius + if (bounds.fLeft == crop.fLeft && bounds.fTop == crop.fTop) { + radii[0].set(cornerRadius, cornerRadius); + } else if (bounds.fLeft > insetCrop.fLeft && bounds.fTop > insetCrop.fTop) { + radii[0].set(0, 0); + } else { + intersectionIsRoundRect = false; + } + // compute the UpperRight corner radius + if (bounds.fRight == crop.fRight && bounds.fTop == crop.fTop) { + radii[1].set(cornerRadius, cornerRadius); + } else if (bounds.fRight < insetCrop.fRight && bounds.fTop > insetCrop.fTop) { + radii[1].set(0, 0); + } else { + intersectionIsRoundRect = false; + } + // compute the BottomRight corner radius + if (bounds.fRight == crop.fRight && bounds.fBottom == crop.fBottom) { + radii[2].set(cornerRadius, cornerRadius); + } else if (bounds.fRight < insetCrop.fRight && bounds.fBottom < insetCrop.fBottom) { + radii[2].set(0, 0); + } else { + intersectionIsRoundRect = false; + } + // compute the BottomLeft corner radius + if (bounds.fLeft == crop.fLeft && bounds.fBottom == crop.fBottom) { + radii[3].set(cornerRadius, cornerRadius); + } else if (bounds.fLeft > insetCrop.fLeft && bounds.fBottom < insetCrop.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); + } + + // 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) { @@ -1224,13 +1355,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..d9caf35ad3 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,12 +88,12 @@ 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 LayerSettings* layer); 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); @@ -101,7 +102,8 @@ private: 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 +118,20 @@ private: const PixelFormat mDefaultPixelFormat; const bool mUseColorManagement; + // Identifier used or various mappings of layers to various + // textures or shaders + using LayerId = 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<LayerId, 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<LayerId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache GUARDED_BY(mRenderingMutex); - std::unordered_map<uint64_t, std::shared_ptr<AutoBackendTexture::LocalRef>> + std::unordered_map<LayerId, 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/debug/record.sh b/libs/renderengine/skia/debug/record.sh index bc406d97a8..25c8cefd7d 100755 --- a/libs/renderengine/skia/debug/record.sh +++ b/libs/renderengine/skia/debug/record.sh @@ -31,7 +31,7 @@ adb shell setprop debug.renderengine.capture_skia_ms $1 # give the device time to both record, and starting writing the file. # Total time needed to write the file depends on how much data was recorded. # the loop at the end waits for this. -sleep $(($1 / 1000 + 2)); +sleep $(($1 / 1000 + 4)); # There is no guarantee that at least one frame passed through renderengine during that time # but as far as I know it always at least writes a 0-byte file with a new name, unless it crashes @@ -54,7 +54,7 @@ adb_filesize() { mskp_size=$(adb_filesize "/data/user/$name") if [[ $mskp_size = "0" ]]; then - echo "Empty file, probably no RenderEngine activity during recording period." + echo "File opened, but remains empty after recording period + wait. Either there was no RenderEngine activity during recording period, or recording process is still working. Check /data/user/$name manually later." exit 1 fi diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp index 6dd416101d..4ad6e94edf 100644 --- a/libs/renderengine/skia/filters/BlurFilter.cpp +++ b/libs/renderengine/skia/filters/BlurFilter.cpp @@ -53,7 +53,7 @@ BlurFilter::BlurFilter() { } )"); - auto [blurEffect, error] = SkRuntimeEffect::Make(blurString); + auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString); if (!blurEffect) { LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str()); } @@ -65,11 +65,11 @@ BlurFilter::BlurFilter() { uniform float mixFactor; half4 main(float2 xy) { - return half4(mix(sample(originalInput), sample(blurredInput), mixFactor)); + return half4(mix(sample(originalInput, xy), sample(blurredInput, xy), mixFactor)); } )"); - auto [mixEffect, mixError] = SkRuntimeEffect::Make(mixString); + auto [mixEffect, mixError] = SkRuntimeEffect::MakeForShader(mixString); if (!mixEffect) { LOG_ALWAYS_FATAL("RuntimeShader error: %s", mixError.c_str()); } @@ -86,8 +86,8 @@ sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t float radiusByPasses = tmpRadius / (float)numberOfPasses; // create blur surface with the bit depth and colorspace of the original surface - SkImageInfo scaledInfo = input->imageInfo().makeWH(blurRect.width() * kInputScale, - blurRect.height() * kInputScale); + SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale), + std::ceil(blurRect.height() * kInputScale)); const float stepX = radiusByPasses; const float stepY = radiusByPasses; @@ -105,7 +105,7 @@ sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix); blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInputScale, stepY * kInputScale}; blurBuilder.uniform("in_maxSizeXY") = - SkV2{blurRect.width() * kInputScale - 1, blurRect.height() * kInputScale - 1}; + SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale}; sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false)); @@ -116,7 +116,7 @@ sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear); blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale}; blurBuilder.uniform("in_maxSizeXY") = - SkV2{blurRect.width() * kInputScale - 1, blurRect.height() * kInputScale - 1}; + SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale}; tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false); } @@ -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 8e8e42e75a..0fbd6698d8 100644 --- a/libs/renderengine/skia/filters/LinearEffect.cpp +++ b/libs/renderengine/skia/filters/LinearEffect.cpp @@ -438,7 +438,7 @@ sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) { generateOETF(linearEffect.outputDataspace, shaderString); generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString); - auto [shader, error] = SkRuntimeEffect::Make(shaderString); + auto [shader, error] = SkRuntimeEffect::MakeForShader(shaderString); if (!shader) { LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str()); } diff --git a/libs/renderengine/skia/filters/StretchShaderFactory.cpp b/libs/renderengine/skia/filters/StretchShaderFactory.cpp new file mode 100644 index 0000000000..9b62789159 --- /dev/null +++ b/libs/renderengine/skia/filters/StretchShaderFactory.cpp @@ -0,0 +1,247 @@ +/* + * 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 easeInCubic(float t, float d) { + float tmp = t * d; + return tmp * tmp * tmp; + } + + float computeOverscrollStart( + float inPos, + float overscroll, + float uStretchAffectedDist, + float uInverseStretchAffectedDist, + float distanceStretched, + float interpolationStrength + ) { + float offsetPos = uStretchAffectedDist - inPos; + float posBasedVariation = mix( + 1. ,easeInCubic(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. ,easeInCubic(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 34ef0a4f08..72e32ed9fd 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) { @@ -487,6 +492,9 @@ public: void fillBufferAndBlurBackground(); template <typename SourceVariant> + void fillSmallLayerAndBlurBackground(); + + template <typename SourceVariant> void overlayCorners(); void fillRedBufferTextureTransform(); @@ -966,11 +974,44 @@ void RenderEngineTest::fillBufferAndBlurBackground() { if (mRE->supportsBackgroundBlur()) { // blurred color (downsampling should result in the center color being close to 128) expectBufferColor(Rect(center - 1, center - 5, center + 1, center + 5), 128, 128, 0, 255, - 10 /* tolerance */); + 50 /* tolerance */); } } template <typename SourceVariant> +void RenderEngineTest::fillSmallLayerAndBlurBackground() { + auto blurRadius = 50; + renderengine::DisplaySettings settings; + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + std::vector<const renderengine::LayerSettings*> layers; + + renderengine::LayerSettings backgroundLayer; + backgroundLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; + backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect(); + SourceVariant::fillColor(backgroundLayer, 1.0f, 0.0f, 0.0f, this); + backgroundLayer.alpha = 1.0f; + layers.push_back(&backgroundLayer); + + renderengine::LayerSettings blurLayer; + blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; + blurLayer.geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f); + blurLayer.backgroundBlurRadius = blurRadius; + SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this); + blurLayer.alpha = 0; + layers.push_back(&blurLayer); + + invokeDraw(settings, layers); + + // Give a generous tolerance - the blur rectangle is very small and this test is + // mainly concerned with ensuring that there's no device failure. + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), 255, 0, 0, 255, + 40 /* tolerance */); +} + +template <typename SourceVariant> void RenderEngineTest::overlayCorners() { renderengine::DisplaySettings settings; settings.physicalDisplay = fullscreenRect(); @@ -1402,12 +1443,16 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_color fillBufferColorTransformZeroLayerAlpha<ColorSourceVariant>(); } -// TODO(b/186010146): reenable once swiftshader is happy with this test -TEST_P(RenderEngineTest, DISABLED_drawLayers_fillBufferAndBlurBackground_colorSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) { initializeRenderEngine(); fillBufferAndBlurBackground<ColorSourceVariant>(); } +TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_colorSource) { + initializeRenderEngine(); + fillSmallLayerAndBlurBackground<ColorSourceVariant>(); +} + TEST_P(RenderEngineTest, drawLayers_overlayCorners_colorSource) { initializeRenderEngine(); overlayCorners<ColorSourceVariant>(); @@ -1478,12 +1523,16 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_opaqu fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } -// TODO(b/186010146): reenable once swiftshader is happy with this test -TEST_P(RenderEngineTest, DISABLED_drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) { initializeRenderEngine(); fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>(); } +TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_opaqueBufferSource) { + initializeRenderEngine(); + fillSmallLayerAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + TEST_P(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) { initializeRenderEngine(); overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>(); @@ -1554,12 +1603,16 @@ TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformZeroLayerAlpha_buffe fillBufferColorTransformZeroLayerAlpha<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } -// TODO(b/186010146): reenable once swiftshader is happy with this test -TEST_P(RenderEngineTest, DISABLED_drawLayers_fillBufferAndBlurBackground_bufferSource) { +TEST_P(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) { initializeRenderEngine(); fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); } +TEST_P(RenderEngineTest, drawLayers_fillSmallLayerAndBlurBackground_bufferSource) { + initializeRenderEngine(); + fillSmallLayerAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + TEST_P(RenderEngineTest, drawLayers_overlayCorners_bufferSource) { initializeRenderEngine(); overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); @@ -1839,6 +1892,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/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/opengl/Android.bp b/opengl/Android.bp index 3878cb1899..b15694bf50 100644 --- a/opengl/Android.bp +++ b/opengl/Android.bp @@ -69,11 +69,9 @@ cc_library_headers { host_supported: true, vendor_available: true, export_include_dirs: ["include"], -} - -llndk_headers { - name: "gl_llndk_headers", - export_include_dirs: ["include"], + llndk: { + llndk_headers: true, + }, } subdirs = [ diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index 7861d62311..c9fce8ad52 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -137,7 +137,12 @@ cc_library_static { cc_library_shared { name: "libEGL", defaults: ["egl_libs_defaults"], - llndk_stubs: "libEGL.llndk", + llndk: { + symbol_file: "libEGL.map.txt", + export_llndk_headers: ["gl_headers"], + // Don't export EGL/include from the LLNDK variant. + override_export_include_dirs: [], + }, srcs: [ "EGL/egl_tls.cpp", "EGL/egl_cache.cpp", @@ -203,7 +208,12 @@ cc_defaults { cc_library_shared { name: "libGLESv1_CM", defaults: ["gles_libs_defaults"], - llndk_stubs: "libGLESv1_CM.llndk", + llndk: { + symbol_file: "libGLESv1_CM.map.txt", + export_llndk_headers: ["gl_headers"], + // Don't export EGL/include from the LLNDK variant. + override_export_include_dirs: [], + }, srcs: ["GLES_CM/gl.cpp"], cflags: ["-DLOG_TAG=\"libGLESv1\""], version_script: "libGLESv1_CM.map.txt", @@ -215,7 +225,12 @@ cc_library_shared { cc_library_shared { name: "libGLESv2", defaults: ["gles_libs_defaults"], - llndk_stubs: "libGLESv2.llndk", + llndk: { + symbol_file: "libGLESv2.map.txt", + export_llndk_headers: ["gl_headers"], + // Don't export EGL/include from the LLNDK variant. + override_export_include_dirs: [], + }, srcs: ["GLES2/gl2.cpp"], cflags: ["-DLOG_TAG=\"libGLESv2\""], @@ -230,31 +245,12 @@ cc_library_shared { cc_library_shared { name: "libGLESv3", defaults: ["gles_libs_defaults"], - llndk_stubs: "libGLESv3.llndk", + llndk: { + symbol_file: "libGLESv3.map.txt", + export_llndk_headers: ["gl_headers"], + // Don't export EGL/include from the LLNDK variant. + override_export_include_dirs: [], + }, srcs: ["GLES2/gl2.cpp"], cflags: ["-DLOG_TAG=\"libGLESv3\""], } - -llndk_library { - name: "libEGL.llndk", - symbol_file: "libEGL.map.txt", - export_llndk_headers: ["gl_llndk_headers"], -} - -llndk_library { - name: "libGLESv1_CM.llndk", - symbol_file: "libGLESv1_CM.map.txt", - export_llndk_headers: ["gl_llndk_headers"], -} - -llndk_library { - name: "libGLESv2.llndk", - symbol_file: "libGLESv2.map.txt", - export_llndk_headers: ["gl_llndk_headers"], -} - -llndk_library { - name: "libGLESv3.llndk", - symbol_file: "libGLESv3.map.txt", - export_llndk_headers: ["gl_llndk_headers"], -} diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 7bd0c6b1b8..1b5f1ab76e 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -232,7 +232,8 @@ static MotionEvent generateMotionEvent() { /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, identityTransform, /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); return event; } diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 5270b8a107..6ed959306b 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -296,12 +296,13 @@ 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) + ui::Transform transform, float globalScaleFactor, vec2 displaySize) : seq(nextSeq()), eventEntry(std::move(eventEntry)), targetFlags(targetFlags), transform(transform), globalScaleFactor(globalScaleFactor), + displaySize(displaySize), deliveryTime(0), resolvedAction(0), resolvedFlags(0) {} diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index f3ef64ba8e..45c5e24395 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -215,6 +215,7 @@ struct DispatchEntry { int32_t targetFlags; ui::Transform transform; float globalScaleFactor; + vec2 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 @@ -227,7 +228,7 @@ struct DispatchEntry { int32_t resolvedFlags; DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags, - ui::Transform transform, float globalScaleFactor); + ui::Transform transform, float globalScaleFactor, vec2 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 edbf21aef4..446c913ab9 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -283,27 +283,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; @@ -339,14 +318,16 @@ static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inp // values do not represent on-screen coordinates, so they should not have any window // transformations applied to them. return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform, - 1.0f /*globalScaleFactor*/); + 1.0f /*globalScaleFactor*/, + inputTarget.displaySize); } } if (inputTarget.useDefaultPointerTransform()) { const ui::Transform& transform = inputTarget.getDefaultPointerTransform(); return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform, - inputTarget.globalScaleFactor); + inputTarget.globalScaleFactor, + inputTarget.displaySize); } ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); @@ -397,7 +378,8 @@ static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inp std::unique_ptr<DispatchEntry> dispatchEntry = std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags, - firstPointerTransform, inputTarget.globalScaleFactor); + firstPointerTransform, inputTarget.globalScaleFactor, + inputTarget.displaySize); return dispatchEntry; } @@ -504,8 +486,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()); } } @@ -2402,6 +2384,8 @@ void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowH inputTarget.inputChannel = inputChannel; inputTarget.flags = targetFlags; inputTarget.globalScaleFactor = windowInfo->globalScaleFactor; + inputTarget.displaySize = + vec2(windowHandle->getInfo()->displayWidth, windowHandle->getInfo()->displayHeight); inputTargets.push_back(inputTarget); it = inputTargets.end() - 1; } @@ -2902,6 +2886,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()); @@ -2967,7 +2957,7 @@ void InputDispatcher::updateInteractionTokensLocked(const EventEntry& entry, return; // Not a key or a motion } - std::unordered_set<sp<IBinder>, IBinderHash> newConnectionTokens; + std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> newConnectionTokens; std::vector<sp<Connection>> newConnections; for (const InputTarget& target : targets) { if ((target.flags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) == @@ -3107,6 +3097,8 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, motionEntry.xPrecision, motionEntry.yPrecision, motionEntry.xCursorPosition, motionEntry.yCursorPosition, + dispatchEntry->displaySize.x, + dispatchEntry->displaySize.y, motionEntry.downTime, motionEntry.eventTime, motionEntry.pointerCount, motionEntry.pointerProperties, usingCoords); @@ -3290,86 +3282,78 @@ void InputDispatcher::releaseDispatchEntry(DispatchEntry* dispatchEntry) { delete dispatchEntry; } -int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { - InputDispatcher* d = static_cast<InputDispatcher*>(data); - - { // 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; - } +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 + } - 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; - } + 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; + } - 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 - } - gotOne = true; - } - if (gotOne) { - d->runCommandsLockedInterruptible(); - if (status == WOULD_BLOCK) { - 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; } - 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); + 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)) { + // TODO(b/167947340): Report this data to LatencyTracker } - } 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); + gotOne = true; + } + if (gotOne) { + runCommandsLockedInterruptible(); + if (status == WOULD_BLOCK) { + return 1; } } - // Remove the channel. - d->removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify); - return 0; // remove the callback - } // release lock + 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. + 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); } } @@ -3819,7 +3803,8 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { args->action, args->actionButton, args->flags, args->edgeFlags, args->metaState, args->buttonState, args->classification, transform, args->xPrecision, args->yPrecision, args->xCursorPosition, - args->yCursorPosition, args->downTime, args->eventTime, + args->yCursorPosition, AMOTION_EVENT_INVALID_DISPLAY_SIZE, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, args->downTime, args->eventTime, args->pointerCount, args->pointerProperties, args->pointerCoords); policyFlags |= POLICY_FLAG_FILTERED; @@ -4284,12 +4269,8 @@ sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(const sp<IBinder>& return nullptr; } -sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const { - sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId); - return getWindowHandleLocked(focusedToken, displayId); -} - -bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const { +sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked( + const sp<InputWindowHandle>& windowHandle) const { for (auto& it : mWindowHandlesByDisplay) { const std::vector<sp<InputWindowHandle>>& windowHandles = it.second; for (const sp<InputWindowHandle>& handle : windowHandles) { @@ -4301,11 +4282,16 @@ bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowH windowHandle->getName().c_str(), it.first, windowHandle->getInfo()->displayId); } - return true; + return handle; } } } - return false; + return nullptr; +} + +sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const { + sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId); + return getWindowHandleLocked(focusedToken, displayId); } bool InputDispatcher::hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const { @@ -4333,11 +4319,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( @@ -4461,7 +4447,7 @@ void InputDispatcher::setInputWindowsLocked( TouchState& state = stateIt->second; for (size_t i = 0; i < state.windows.size();) { TouchedWindow& touchedWindow = state.windows[i]; - if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { + if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) { if (DEBUG_FOCUS) { ALOGD("Touched window was removed: %s in display %" PRId32, touchedWindow.windowHandle->getName().c_str(), displayId); @@ -4493,7 +4479,7 @@ void InputDispatcher::setInputWindowsLocked( // Otherwise, they might stick around until the window handle is destroyed // which might not happen until the next GC. for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) { - if (!hasWindowHandleLocked(oldWindowHandle)) { + if (getWindowHandleLocked(oldWindowHandle) == nullptr) { if (DEBUG_FOCUS) { ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); } @@ -4987,13 +4973,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)); @@ -5041,14 +5027,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; @@ -5056,13 +5051,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); - mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); + std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback, + this, std::placeholders::_1, token); + + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr); } // release lock // Wake the looper because some connections have changed. @@ -5090,18 +5092,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); - ALOGI("Created monitor %s for display %" PRId32 ", gesture=%s, pid=%" PRId32, name.c_str(), - displayId, toString(isGestureMonitor), pid); + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr); } // Wake the looper because some connections have changed. @@ -5134,7 +5139,6 @@ status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connection } removeConnectionLocked(connection); - mInputChannelsByToken.erase(connectionToken); if (connection->monitor) { removeMonitorChannelLocked(connectionToken); @@ -5292,9 +5296,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; } } @@ -5312,7 +5315,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, diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 5708face4b..7ba03e8063 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -211,9 +211,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); @@ -221,13 +218,14 @@ private: void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock); - struct IBinderHash { - std::size_t operator()(const sp<IBinder>& b) const { - return std::hash<IBinder*>{}(b.get()); - } + template <typename T> + 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>, IBinderHash> - 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) @@ -327,10 +325,11 @@ private: // to loop through all displays. sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken, int displayId) const REQUIRES(mLock); + sp<InputWindowHandle> getWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const + REQUIRES(mLock); std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock); sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock); - bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock); bool hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const REQUIRES(mLock); /* @@ -371,7 +370,8 @@ private: std::string mLastAnrState GUARDED_BY(mLock); // The connection tokens of the channels that the user last interacted, for debugging - std::unordered_set<sp<IBinder>, IBinderHash> mInteractionConnectionTokens GUARDED_BY(mLock); + std::unordered_set<sp<IBinder>, StrongPointerHash<IBinder>> mInteractionConnectionTokens + GUARDED_BY(mLock); void updateInteractionTokensLocked(const EventEntry& entry, const std::vector<InputTarget>& targets) REQUIRES(mLock); @@ -543,7 +543,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); diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index debf8058b3..2543852788 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -100,6 +100,9 @@ struct InputTarget { // (ignored for KeyEvents) 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}; + // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. BitSet32 pointerIds; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 13712eee53..fb654847ed 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -2456,6 +2456,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 +2491,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 +2510,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 +2523,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 +2544,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 +2554,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 +2579,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/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index ea7a162a1a..f021376e26 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -526,7 +526,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -539,6 +540,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, @@ -551,6 +553,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, @@ -564,6 +567,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, @@ -576,6 +580,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, @@ -587,7 +592,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -597,7 +603,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -609,7 +616,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -620,7 +628,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -633,7 +642,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_DISPLAY_SIZE, + AMOTION_EVENT_INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -1218,6 +1228,11 @@ public: return *this; } + MotionEventBuilder& addFlag(uint32_t flags) { + mFlags |= flags; + return *this; + } + MotionEvent build() { std::vector<PointerProperties> pointerProperties; std::vector<PointerCoords> pointerCoords; @@ -1237,11 +1252,11 @@ 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, mEventTime, mEventTime, mPointers.size(), - pointerProperties.data(), pointerCoords.data()); + mRawYCursorPosition, mDisplayWidth, mDisplayHeight, mEventTime, mEventTime, + mPointers.size(), pointerProperties.data(), pointerCoords.data()); return event; } @@ -1253,8 +1268,11 @@ 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}; + int32_t mDisplayHeight{AMOTION_EVENT_INVALID_DISPLAY_SIZE}; std::vector<PointerBuilder> mPointers; }; @@ -2032,6 +2050,19 @@ public: expectedDisplayId, expectedFlags); } + MotionEvent* consumeMotion() { + InputEvent* event = mInputReceiver->consume(); + if (!event) { + ADD_FAILURE() << "No event was produced"; + return nullptr; + } + if (event->getType() != AINPUT_EVENT_TYPE_MOTION) { + ADD_FAILURE() << "Received event of type " << event->getType() << " instead of motion"; + return nullptr; + } + return static_cast<MotionEvent*>(event); + } + void assertNoEvents() { mInputReceiver->assertNoEvents(); } private: @@ -2118,6 +2149,27 @@ TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) { mFakePolicy->assertNotifyMonitorResponsiveWasCalled(); } +// Tests for gesture monitors +TEST_F(InputDispatcherTest, GestureMonitor_NoWindowTransform) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + window->setWindowOffset(20, 40); + window->setWindowTransform(0, 1, -1, 0); + + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, + true /*isGestureMonitor*/); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + MotionEvent* event = monitor.consumeMotion(); + // Even though window has transform, gesture monitor must not. + ASSERT_EQ(ui::Transform(), event->getTransform()); +} + TEST_F(InputDispatcherTest, TestMoveEvent) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = @@ -3062,6 +3114,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/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index d8e8b52ded..5d6f8c773c 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -132,8 +132,6 @@ SensorDevice::SensorDevice() } void SensorDevice::initializeSensorList() { - float minPowerMa = 0.001; // 1 microAmp - checkReturn(mSensors->getSensorsList( [&](const auto &list) { const size_t count = list.size(); @@ -151,13 +149,18 @@ void SensorDevice::initializeSensorList() { // Don't crash in this case since CTS will verify that devices don't go to // production with a resolution of 0. if (sensor.resolution != 0) { - double promotedResolution = sensor.resolution; - double promotedMaxRange = sensor.maxRange; - if (fmod(promotedMaxRange, promotedResolution) != 0) { - ALOGW("%s's max range %f is not a multiple of the resolution %f", - sensor.name, sensor.maxRange, sensor.resolution); - SensorDeviceUtils::quantizeValue( - &sensor.maxRange, promotedResolution); + float quantizedRange = sensor.maxRange; + SensorDeviceUtils::quantizeValue( + &quantizedRange, sensor.resolution, /*factor=*/ 1); + // Only rewrite maxRange if the requantization produced a "significant" + // change, which is fairly arbitrarily defined as resolution / 8. + // Smaller deltas are permitted, as they may simply be due to floating + // point representation error, etc. + if (fabsf(sensor.maxRange - quantizedRange) > sensor.resolution / 8) { + ALOGW("%s's max range %.12f is not a multiple of the resolution " + "%.12f - updated to %.12f", sensor.name, sensor.maxRange, + sensor.resolution, quantizedRange); + sensor.maxRange = quantizedRange; } } else { // Don't crash here or the device will go into a crashloop. @@ -166,10 +169,11 @@ void SensorDevice::initializeSensorList() { } // Sanity check and clamp power if it is 0 (or close) - if (sensor.power < minPowerMa) { - ALOGI("Reported power %f not deemed sane, clamping to %f", - sensor.power, minPowerMa); - sensor.power = minPowerMa; + constexpr float MIN_POWER_MA = 0.001; // 1 microAmp + if (sensor.power < MIN_POWER_MA) { + ALOGI("%s's reported power %f invalid, clamped to %f", + sensor.name, sensor.power, MIN_POWER_MA); + sensor.power = MIN_POWER_MA; } mSensorList.push_back(sensor); diff --git a/services/sensorservice/SensorDeviceUtils.h b/services/sensorservice/SensorDeviceUtils.h index 1309971953..255f7e107c 100644 --- a/services/sensorservice/SensorDeviceUtils.h +++ b/services/sensorservice/SensorDeviceUtils.h @@ -32,16 +32,15 @@ using ::android::hidl::manager::V1_0::IServiceNotification; namespace android { namespace SensorDeviceUtils { -// Quantizes a single value using a sensor's resolution. -inline void quantizeValue(float *value, double resolution) { +// Quantizes a single value to (a fractional factor of) a sensor's resolution. Typically we +// increase the value of the sensor's nominal resolution to ensure that sensor accuracy +// improvements, like runtime calibration, are not masked during requantization. +inline void quantizeValue(float *value, double resolution, double factor = 0.125) { if (resolution == 0) { return; } - // Increase the value of the sensor's nominal resolution to ensure that - // sensor accuracy improvements, like runtime calibration, are not masked - // during requantization. - double incRes = 0.125 * resolution; + double incRes = factor * resolution; *value = round(static_cast<double>(*value) / incRes) * incRes; } diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 747e2f4c52..a6e5c770a7 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -81,7 +81,7 @@ cc_defaults { name: "libsurfaceflinger_defaults", defaults: [ "surfaceflinger_defaults", - "skia_deps", + "skia_renderengine_deps", "libdisplayconfig_defaults", "unfieddraw_defaults", ], diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 471a551432..f6a602c732 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -314,6 +314,17 @@ bool BufferStateLayer::setCrop(const Rect& crop) { return true; } +bool BufferStateLayer::setBufferCrop(const Rect& bufferCrop) { + if (mCurrentState.bufferCrop == bufferCrop) return false; + + mCurrentState.sequence++; + mCurrentState.bufferCrop = bufferCrop; + + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + bool BufferStateLayer::setMatrix(const layer_state_t::matrix22_t& matrix, bool allowNonRectPreservingTransforms) { if (mCurrentState.transform.dsdx() == matrix.dsdx && @@ -853,10 +864,15 @@ uint32_t BufferStateLayer::getEffectiveScalingMode() const { } Rect BufferStateLayer::computeBufferCrop(const State& s) { - if (s.buffer) { + if (s.buffer && !s.bufferCrop.isEmpty()) { + Rect bufferCrop; + s.buffer->getBuffer()->getBounds().intersect(s.bufferCrop, &bufferCrop); + return bufferCrop; + } else if (s.buffer) { return s.buffer->getBuffer()->getBounds(); + } else { + return s.bufferCrop; } - return Rect::INVALID_RECT; } sp<Layer> BufferStateLayer::createClone() { diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index 46dfd7f6f6..c4a1377a44 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; } @@ -88,6 +85,8 @@ public: FloatRect computeSourceBounds(const FloatRect& parentBounds) const override; void setAutoRefresh(bool autoRefresh) override; + bool setBufferCrop(const Rect& bufferCrop) override; + // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- diff --git a/services/surfaceflinger/Clock.h b/services/surfaceflinger/Clock.h new file mode 100644 index 0000000000..3f23c6d115 --- /dev/null +++ b/services/surfaceflinger/Clock.h @@ -0,0 +1,42 @@ +/* + * 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 <chrono> + +namespace android { + +// Abstract interface for timekeeping which can be injected for unit tests. +class Clock { +public: + Clock() = default; + virtual ~Clock() = default; + + // Returns the current time + virtual std::chrono::steady_clock::time_point now() const = 0; +}; + +class SteadyClock : public Clock { +public: + virtual ~SteadyClock() = default; + + std::chrono::steady_clock::time_point now() const override { + return std::chrono::steady_clock::now(); + } +}; + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h index 7ca91d86eb..58bb41adbb 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h @@ -96,6 +96,11 @@ struct ProjectionSpace { // physical translation and finally rotate to the physical orientation. return rotationTransform * destTranslation * scale * sourceTranslation; } + + bool operator==(const ProjectionSpace& other) const { + return bounds == other.bounds && content == other.content && + orientation == other.orientation; + } }; } // namespace compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h index db874875c4..f84ddd3225 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h @@ -57,11 +57,15 @@ public: void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot, sp<GraphicBuffer>* outBuffer); + // Special caching slot for the layer caching feature. + static const constexpr size_t FLATTENER_CACHING_SLOT = BufferQueue::NUM_BUFFER_SLOTS; + private: // an array where the index corresponds to a slot and the value corresponds to a (counter, // buffer) pair. "counter" is a unique value that indicates the last time this slot was updated // or used and allows us to keep track of the least-recently used buffer. - wp<GraphicBuffer> mBuffers[BufferQueue::NUM_BUFFER_SLOTS]; + static const constexpr size_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1; + wp<GraphicBuffer> mBuffers[kMaxLayerBufferCount]; uint32_t mNextSlot = 0; bool mReduceSlotsForWideVideo = false; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h index 356965cf48..b98043b92e 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h @@ -102,7 +102,7 @@ struct OutputLayerCompositionState { // behind the OutputLayer represented by this CompositionState and will // be visible through it. Unowned - the OutputLayer's lifetime will // outlast this.) - OutputLayer* peekThroughLayer = nullptr; + compositionengine::OutputLayer* peekThroughLayer = nullptr; } overrideInfo; /* diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h index a6c4eafbab..06f26eb8d1 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h @@ -60,7 +60,6 @@ public: void addLayer(const LayerState*, std::chrono::steady_clock::time_point lastUpdate); std::chrono::steady_clock::time_point getLastUpdate() const { return mLastUpdate; } - NonBufferHash getFingerprint() const { return mFingerprint; } size_t getLayerCount() const { return mLayers.size(); } const Layer& getFirstLayer() const { return mLayers[0]; } const Rect& getBounds() const { return mBounds; } @@ -121,7 +120,7 @@ public: void addHolePunchLayerIfFeasible(const CachedSet&, bool isFirstLayer); // Retrieve the layer that will be drawn behind this one. - OutputLayer* getHolePunchLayer() const; + compositionengine::OutputLayer* getHolePunchLayer() const; private: CachedSet() = default; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h index 942592a9eb..b09f1d1a09 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h @@ -49,6 +49,7 @@ public: const OutputCompositionState& outputState); void dump(std::string& result) const; + void dumpLayers(std::string& result) const; private: size_t calculateDisplayCost(const std::vector<const LayerState*>& layers) const; diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp index 2d9f01b9fd..b1ee3fbf5d 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) { 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/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp index 675c77b33c..ee442eed16 100644 --- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp +++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp @@ -92,7 +92,7 @@ void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uin format = buffer->getPixelFormat(); } bool widevideo = false; - uint32_t numSlots = BufferQueue::NUM_BUFFER_SLOTS; + uint32_t numSlots = kMaxLayerBufferCount; // Workaround to reduce slots for 8k buffers if ((width * height > MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT) && mReduceSlotsForWideVideo && diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 23b3ec4b49..9eea9fc029 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -725,7 +725,7 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr editState().earliestPresentTime = refreshArgs.earliestPresentTime; - OutputLayer* peekThroughLayer = nullptr; + compositionengine::OutputLayer* peekThroughLayer = nullptr; bool hasSecureCamera = false; bool hasSecureDisplay = false; bool needsProtected = false; diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 7b1376c0b7..6fc19e8dbc 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -18,6 +18,7 @@ #include <compositionengine/DisplayColorProfile.h> #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/Output.h> +#include <compositionengine/impl/HwcBufferCache.h> #include <compositionengine/impl/OutputCompositionState.h> #include <compositionengine/impl/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> @@ -559,9 +560,11 @@ void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer, sp<GraphicBuffer> buffer = outputIndependentState.buffer; sp<Fence> acquireFence = outputIndependentState.acquireFence; + int slot = outputIndependentState.bufferSlot; if (getState().overrideInfo.buffer != nullptr) { buffer = getState().overrideInfo.buffer->getBuffer(); acquireFence = getState().overrideInfo.acquireFence; + slot = HwcBufferCache::FLATTENER_CACHING_SLOT; } ALOGV("Writing buffer %p", buffer.get()); @@ -570,8 +573,7 @@ void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer, sp<GraphicBuffer> hwcBuffer; // We need access to the output-dependent state for the buffer cache there, // though otherwise the buffer is not output-dependent. - editState().hwc->hwcBufferCache.getHwcBuffer(outputIndependentState.bufferSlot, buffer, - &hwcSlot, &hwcBuffer); + editState().hwc->hwcBufferCache.getHwcBuffer(slot, buffer, &hwcSlot, &hwcBuffer); if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence); error != hal::Error::NONE) { diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp index b31e89e247..67854cf31f 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp @@ -158,8 +158,12 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, const ui::Dataspace& outputDataspace = outputState.dataspace; const ui::Transform::RotationFlags orientation = ui::Transform::toRotationFlags(outputState.framebufferSpace.orientation); + renderengine::DisplaySettings displaySettings{ - .physicalDisplay = Rect(0, 0, mBounds.getWidth(), mBounds.getHeight()), + .physicalDisplay = Rect(-mBounds.left + outputState.framebufferSpace.content.left, + -mBounds.top + outputState.framebufferSpace.content.top, + -mBounds.left + outputState.framebufferSpace.content.right, + -mBounds.top + outputState.framebufferSpace.content.bottom), .clip = viewport, .outputDataspace = outputDataspace, .orientation = orientation, @@ -246,9 +250,7 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, if (result == NO_ERROR) { mDrawFence = new Fence(drawFence.release()); - mOutputSpace = ProjectionSpace(ui::Size(outputState.framebufferSpace.bounds.getWidth(), - outputState.framebufferSpace.bounds.getHeight()), - mBounds); + mOutputSpace = outputState.framebufferSpace; mTexture = std::move(texture); mOutputSpace.orientation = outputState.framebufferSpace.orientation; mOutputDataspace = outputDataspace; @@ -304,7 +306,7 @@ void CachedSet::addHolePunchLayerIfFeasible(const CachedSet& holePunchLayer, boo } } -OutputLayer* CachedSet::getHolePunchLayer() const { +compositionengine::OutputLayer* CachedSet::getHolePunchLayer() const { return mHolePunchLayer ? mHolePunchLayer->getOutputLayer() : nullptr; } diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index b4e761004f..a63f21f41a 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -45,7 +45,10 @@ bool isSameStack(const std::vector<const LayerState*>& incomingLayers, } for (size_t i = 0; i < incomingLayers.size(); i++) { - if (incomingLayers[i]->getDifferingFields(*(existingLayers[i])) != LayerStateField::None) { + // Checking the IDs here is very strict, but we do this as otherwise we may mistakenly try + // to access destroyed OutputLayers later on. + if (incomingLayers[i]->getId() != existingLayers[i]->getId() || + incomingLayers[i]->getDifferingFields(*(existingLayers[i])) != LayerStateField::None) { return false; } } @@ -96,6 +99,14 @@ void Flattener::renderCachedSets(renderengine::RenderEngine& renderEngine, mNewCachedSet->render(renderEngine, outputState); } +void Flattener::dumpLayers(std::string& result) const { + result.append(" Current layers:"); + for (const CachedSet& layer : mLayers) { + result.append("\n"); + layer.dump(result); + } +} + void Flattener::dump(std::string& result) const { const auto now = std::chrono::steady_clock::now(); @@ -140,11 +151,7 @@ void Flattener::dump(std::string& result) const { base::StringAppendF(&result, "\n Current hash %016zx, last update %sago\n\n", mCurrentGeometry, durationString(lastUpdate).c_str()); - result.append(" Current layers:"); - for (const CachedSet& layer : mLayers) { - result.append("\n"); - layer.dump(result); - } + dumpLayers(result); } size_t Flattener::calculateDisplayCost(const std::vector<const LayerState*>& layers) const { @@ -232,7 +239,8 @@ bool Flattener::mergeWithCachedSets(const std::vector<const LayerState*>& layers auto currentLayerIter = mLayers.begin(); auto incomingLayerIter = layers.begin(); while (incomingLayerIter != layers.end()) { - if (mNewCachedSet && mNewCachedSet->getFingerprint() == (*incomingLayerIter)->getHash()) { + if (mNewCachedSet && + mNewCachedSet->getFirstLayer().getState()->getId() == (*incomingLayerIter)->getId()) { if (mNewCachedSet->hasBufferUpdate()) { ALOGV("[%s] Dropping new cached set", __func__); ++mInvalidatedCachedSetAges[0]; diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp index 7d2bf06133..7e85dcaee0 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp @@ -233,6 +233,8 @@ void Planner::dump(const Vector<String16>& args, std::string& result) { } mPredictor.listSimilarStacks(*plan, result); + } else if (command == "--layers" || command == "-l") { + mFlattener.dumpLayers(result); } else { base::StringAppendF(&result, "Unknown command '%s'\n\n", command.string()); dumpUsage(result); @@ -268,6 +270,9 @@ void Planner::dumpUsage(std::string& result) const { result.append("[--similar|-s] <plan>\n"); result.append(" Prints the example layer names for similar stacks matching <plan>\n"); + + result.append("[--layers|-l]\n"); + result.append(" Prints the current layers\n"); } } // namespace android::compositionengine::impl::planner diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index fb8ffcedf3..3adfe405ff 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <compositionengine/impl/HwcBufferCache.h> #include <compositionengine/impl/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> #include <compositionengine/mock/CompositionEngine.h> @@ -702,6 +703,7 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { static constexpr ui::Dataspace kOverrideDataspace = static_cast<ui::Dataspace>(72); static constexpr int kSupportedPerFrameMetadata = 101; static constexpr int kExpectedHwcSlot = 0; + static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::FLATTENER_CACHING_SLOT; static constexpr bool kLayerGenericMetadata1Mandatory = true; static constexpr bool kLayerGenericMetadata2Mandatory = true; @@ -824,10 +826,11 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { EXPECT_CALL(*mHwcLayer, setSidebandStream(kSidebandStreamHandle)); } - void expectSetHdrMetadataAndBufferCalls(sp<GraphicBuffer> buffer = kBuffer, + void expectSetHdrMetadataAndBufferCalls(uint32_t hwcSlot = kExpectedHwcSlot, + sp<GraphicBuffer> buffer = kBuffer, sp<Fence> fence = kFence) { EXPECT_CALL(*mHwcLayer, setPerFrameMetadata(kSupportedPerFrameMetadata, kHdrMetadata)); - EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, buffer, fence)); + EXPECT_CALL(*mHwcLayer, setBuffer(hwcSlot, buffer, fence)); } void expectGenericLayerMetadataCalls() { @@ -1060,15 +1063,75 @@ TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) { kOverrideBlendMode, kOverrideAlpha); expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion, kOverrideSurfaceDamage); - expectSetHdrMetadataAndBufferCalls(kOverrideBuffer, kOverrideFence); + expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence); 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, peekThroughChangesBlendMode) { + auto peekThroughLayerFE = sp<compositionengine::mock::LayerFE>::make(); + OutputLayer peekThroughLayer{mOutput, peekThroughLayerFE}; + + mOutputLayer.mState.overrideInfo.peekThroughLayer = &peekThroughLayer; + + expectGeometryCommonCalls(kDisplayFrame, kSourceCrop, kBufferTransform, + Hwc2::IComposerClient::BlendMode::PREMULTIPLIED); + expectPerFrameCommonCalls(); EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false)); mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, /*zIsOverridden*/ false, /*isPeekingThrough*/ false); } +TEST_F(OutputLayerWriteStateToHWCTest, isPeekingThroughSetsOverride) { + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(); + + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ true); + EXPECT_TRUE(mOutputLayer.getState().hwc->stateOverridden); +} + +TEST_F(OutputLayerWriteStateToHWCTest, zIsOverriddenSetsOverride) { + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(); + EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false)); + + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ true, /*isPeekingThrough*/ + false); + EXPECT_TRUE(mOutputLayer.getState().hwc->stateOverridden); +} + +TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersForceClientComposition) { + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(); + EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(true)); + expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT); + + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ + false); +} + +TEST_F(OutputLayerWriteStateToHWCTest, roundedCornersPeekingThroughAllowsDeviceComposition) { + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(); + expectSetHdrMetadataAndBufferCalls(); + EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); + expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE); + + mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE; + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ + true); + EXPECT_EQ(Hwc2::IComposerClient::Composition::DEVICE, + mOutputLayer.getState().hwc->hwcCompositionType); +} + /* * OutputLayer::writeCursorPositionToHWC() */ diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 7b71957021..27980a01ac 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -879,6 +879,64 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, forcesClientCompositionForAllLa mOutput->writeCompositionState(args); } +TEST_F(OutputUpdateAndWriteCompositionStateTest, peekThroughLayerChangesOrder) { + renderengine::mock::RenderEngine renderEngine; + InjectedLayer layer0; + InjectedLayer layer1; + InjectedLayer layer2; + InjectedLayer layer3; + + InSequence seq; + EXPECT_CALL(*layer0.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); + + uint32_t z = 0; + EXPECT_CALL(*layer0.outputLayer, + writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); + + // After calling planComposition (which clears overrideInfo), this test sets + // layer3 to be the peekThroughLayer for layer1 and layer2. As a result, it + // comes first, setting isPeekingThrough to true and zIsOverridden to true + // for it and the following layers. + EXPECT_CALL(*layer3.outputLayer, + writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, + /*zIsOverridden*/ true, /*isPeekingThrough*/ + true)); + EXPECT_CALL(*layer1.outputLayer, + writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, + /*zIsOverridden*/ true, /*isPeekingThrough*/ false)); + EXPECT_CALL(*layer2.outputLayer, + writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, z++, + /*zIsOverridden*/ true, /*isPeekingThrough*/ false)); + + injectOutputLayer(layer0); + injectOutputLayer(layer1); + injectOutputLayer(layer2); + injectOutputLayer(layer3); + + mOutput->editState().isEnabled = true; + + CompositionRefreshArgs args; + args.updatingGeometryThisFrame = true; + args.devOptForceClientComposition = false; + mOutput->updateCompositionState(args); + mOutput->planComposition(); + + std::shared_ptr<renderengine::ExternalTexture> buffer = std::make_shared< + renderengine::ExternalTexture>(new GraphicBuffer(), renderEngine, + renderengine::ExternalTexture::Usage::READABLE | + renderengine::ExternalTexture::Usage::WRITEABLE); + layer1.outputLayerState.overrideInfo.buffer = buffer; + layer2.outputLayerState.overrideInfo.buffer = buffer; + layer1.outputLayerState.overrideInfo.peekThroughLayer = layer3.outputLayer; + layer2.outputLayerState.overrideInfo.peekThroughLayer = layer3.outputLayer; + + mOutput->writeCompositionState(args); +} + /* * Output::prepareFrame() */ diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp index 283c69270f..a39331c02d 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp @@ -19,8 +19,12 @@ #include <compositionengine/impl/planner/LayerState.h> #include <compositionengine/mock/LayerFE.h> #include <compositionengine/mock/OutputLayer.h> +#include <gmock/gmock-actions.h> #include <gtest/gtest.h> +#include <renderengine/ExternalTexture.h> #include <renderengine/mock/RenderEngine.h> +#include <ui/GraphicTypes.h> +#include <memory> namespace android::compositionengine { using namespace std::chrono_literals; @@ -105,7 +109,6 @@ void CachedSetTest::TearDown() { } void expectEqual(const CachedSet& cachedSet, const CachedSet::Layer& layer) { - EXPECT_EQ(layer.getHash(), cachedSet.getFingerprint()); EXPECT_EQ(layer.getLastUpdate(), cachedSet.getLastUpdate()); EXPECT_EQ(layer.getDisplayFrame(), cachedSet.getBounds()); EXPECT_TRUE(layer.getVisibleRegion().hasSameRects(cachedSet.getVisibleRegion())); @@ -154,7 +157,6 @@ TEST_F(CachedSetTest, addLayer) { CachedSet cachedSet(layer1); cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); - EXPECT_EQ(layer1.getHash(), cachedSet.getFingerprint()); EXPECT_EQ(kStartTime, cachedSet.getLastUpdate()); EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getBounds()); Region expectedRegion; @@ -243,7 +245,6 @@ TEST_F(CachedSetTest, append) { cachedSet1.addLayer(layer3.getState(), kStartTime + 10ms); cachedSet1.append(cachedSet2); - EXPECT_EQ(layer1.getHash(), cachedSet1.getFingerprint()); EXPECT_EQ(kStartTime, cachedSet1.getLastUpdate()); EXPECT_EQ(Rect(0, 0, 3, 3), cachedSet1.getBounds()); Region expectedRegion; @@ -287,10 +288,11 @@ TEST_F(CachedSetTest, updateAge_BufferUpdate) { } TEST_F(CachedSetTest, render) { - CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); - sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE; - CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); - sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE; + // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0) + CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get(); + sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE; + CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get(); + sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE; CachedSet cachedSet(layer1); cachedSet.append(CachedSet(layer2)); @@ -307,7 +309,7 @@ TEST_F(CachedSetTest, render) { const std::vector<const renderengine::LayerSettings*>& layers, const std::shared_ptr<renderengine::ExternalTexture>&, const bool, base::unique_fd&&, base::unique_fd*) -> size_t { - EXPECT_EQ(Rect(0, 0, 2, 2), displaySettings.physicalDisplay); + EXPECT_EQ(Rect(-1, -1, 9, 4), displaySettings.physicalDisplay); EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip); EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation), displaySettings.orientation); @@ -324,15 +326,246 @@ TEST_F(CachedSetTest, render) { cachedSet.render(mRenderEngine, mOutputState); expectReadyBuffer(cachedSet); - EXPECT_EQ(Rect(0, 0, 2, 2), cachedSet.getOutputSpace().content); - EXPECT_EQ(Rect(mOutputState.framebufferSpace.bounds.getWidth(), - mOutputState.framebufferSpace.bounds.getHeight()), - cachedSet.getOutputSpace().bounds); + EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace()); // Now check that appending a new cached set properly cleans up RenderEngine resources. CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); cachedSet.append(CachedSet(layer3)); } +TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) { + // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0) + CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get(); + sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE; + CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get(); + sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE; + + CachedSet cachedSet(layer1); + cachedSet.append(CachedSet(layer2)); + + std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1; + clientCompList1.push_back({}); + clientCompList1[0].alpha = 0.5f; + + std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2; + clientCompList2.push_back({}); + clientCompList2[0].alpha = 0.75f; + + mOutputState.framebufferSpace = ProjectionSpace(ui::Size(10, 20), Rect(2, 3, 10, 5)); + + const auto drawLayers = [&](const renderengine::DisplaySettings& displaySettings, + const std::vector<const renderengine::LayerSettings*>& layers, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd&&, base::unique_fd*) -> size_t { + EXPECT_EQ(Rect(1, 2, 9, 4), displaySettings.physicalDisplay); + EXPECT_EQ(mOutputState.layerStackSpace.content, displaySettings.clip); + EXPECT_EQ(ui::Transform::toRotationFlags(mOutputState.framebufferSpace.orientation), + displaySettings.orientation); + EXPECT_EQ(0.5f, layers[0]->alpha); + EXPECT_EQ(0.75f, layers[1]->alpha); + EXPECT_EQ(ui::Dataspace::SRGB, displaySettings.outputDataspace); + + return NO_ERROR; + }; + + EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1)); + EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers)); + cachedSet.render(mRenderEngine, mOutputState); + expectReadyBuffer(cachedSet); + + EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace()); + + // Now check that appending a new cached set properly cleans up RenderEngine resources. + CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); + cachedSet.append(CachedSet(layer3)); +} + +TEST_F(CachedSetTest, holePunch_requiresBuffer) { + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE; + + CachedSet cachedSet(layer1); + EXPECT_CALL(*layerFE1, hasRoundedCorners()).WillRepeatedly(Return(true)); + + EXPECT_FALSE(cachedSet.requiresHolePunch()); +} + +TEST_F(CachedSetTest, holePunch_requiresRoundedCorners) { + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + + CachedSet cachedSet(layer1); + + EXPECT_FALSE(cachedSet.requiresHolePunch()); +} + +TEST_F(CachedSetTest, holePunch_requiresSingleLayer) { + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; + EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); + + CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); + + CachedSet cachedSet(layer1); + cachedSet.append(layer2); + + EXPECT_FALSE(cachedSet.requiresHolePunch()); +} + +TEST_F(CachedSetTest, requiresHolePunch) { + CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); + mTestLayers[0]->layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; + + CachedSet cachedSet(layer); + EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); + + EXPECT_TRUE(cachedSet.requiresHolePunch()); +} + +TEST_F(CachedSetTest, holePunch_requiresDeviceComposition) { + CachedSet::Layer& layer = *mTestLayers[0]->cachedSetLayer.get(); + sp<mock::LayerFE> layerFE = mTestLayers[0]->layerFE; + auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; + layerFECompositionState.buffer = sp<GraphicBuffer>::make(); + layerFECompositionState.forceClientComposition = true; + + CachedSet cachedSet(layer); + EXPECT_CALL(*layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); + + EXPECT_FALSE(cachedSet.requiresHolePunch()); +} + +TEST_F(CachedSetTest, addHolePunch_requiresOverlap) { + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); + CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); + + CachedSet cachedSet(layer1); + cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); + + cachedSet.addHolePunchLayerIfFeasible(layer3, true); + + ASSERT_EQ(nullptr, cachedSet.getHolePunchLayer()); +} + +TEST_F(CachedSetTest, addHolePunch_requiresOpaque) { + mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); + mTestLayers[0]->layerFECompositionState.isOpaque = false; + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); + CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); + + CachedSet cachedSet(layer1); + cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); + + cachedSet.addHolePunchLayerIfFeasible(layer3, false); + + ASSERT_EQ(nullptr, cachedSet.getHolePunchLayer()); +} + +TEST_F(CachedSetTest, addHolePunch_opaque) { + mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); + mTestLayers[0]->layerFECompositionState.isOpaque = true; + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); + CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); + + CachedSet cachedSet(layer1); + cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); + + cachedSet.addHolePunchLayerIfFeasible(layer3, false); + + ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer()); +} + +TEST_F(CachedSetTest, addHolePunch_firstLayer) { + mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); + mTestLayers[0]->layerFECompositionState.isOpaque = false; + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); + CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); + + CachedSet cachedSet(layer1); + cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); + + cachedSet.addHolePunchLayerIfFeasible(layer3, true); + + ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer()); +} + +TEST_F(CachedSetTest, addHolePunch) { + mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + sp<mock::LayerFE> layerFE1 = mTestLayers[0]->layerFE; + + CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); + sp<mock::LayerFE> layerFE2 = mTestLayers[1]->layerFE; + + CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); + sp<mock::LayerFE> layerFE3 = mTestLayers[2]->layerFE; + + CachedSet cachedSet(layer1); + cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); + + cachedSet.addHolePunchLayerIfFeasible(layer3, true); + + std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1; + clientCompList1.push_back({}); + std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2; + clientCompList2.push_back({}); + std::vector<compositionengine::LayerFE::LayerSettings> clientCompList3; + clientCompList3.push_back({}); + + clientCompList3[0].source.buffer.buffer = std::make_shared< + renderengine::ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine, + renderengine::ExternalTexture::READABLE); + + EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1)); + EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2)); + EXPECT_CALL(*layerFE3, prepareClientCompositionList(_)).WillOnce(Return(clientCompList3)); + + const auto drawLayers = [&](const renderengine::DisplaySettings&, + const std::vector<const renderengine::LayerSettings*>& layers, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd&&, base::unique_fd*) -> size_t { + // If the highlight layer is enabled, it will increase the size by 1. + // We're interested in the third layer either way. + EXPECT_GE(layers.size(), 3u); + const auto* holePunchSettings = layers[2]; + EXPECT_EQ(nullptr, holePunchSettings->source.buffer.buffer); + EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), holePunchSettings->source.solidColor); + EXPECT_TRUE(holePunchSettings->disableBlending); + EXPECT_EQ(0.0f, holePunchSettings->alpha); + + return NO_ERROR; + }; + + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Invoke(drawLayers)); + cachedSet.render(mRenderEngine, mOutputState); +} + +TEST_F(CachedSetTest, decompose_removesHolePunch) { + mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + CachedSet::Layer& layer2 = *mTestLayers[1]->cachedSetLayer.get(); + CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); + + CachedSet cachedSet(layer1); + cachedSet.addLayer(layer2.getState(), kStartTime + 10ms); + + cachedSet.addHolePunchLayerIfFeasible(layer3, true); + + ASSERT_EQ(&mTestLayers[2]->outputLayer, cachedSet.getHolePunchLayer()); + + std::vector<CachedSet> decomposed = cachedSet.decompose(); + EXPECT_EQ(2u, decomposed.size()); + for (const auto& set : decomposed) { + EXPECT_EQ(nullptr, set.getHolePunchLayer()); + } +} + } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index c5280877d5..71757f69ea 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -22,6 +22,8 @@ #include <compositionengine/mock/LayerFE.h> #include <compositionengine/mock/OutputLayer.h> #include <gtest/gtest.h> +#include <renderengine/ExternalTexture.h> +#include <renderengine/LayerSettings.h> #include <renderengine/mock/RenderEngine.h> namespace android::compositionengine { @@ -45,7 +47,7 @@ namespace { class FlattenerTest : public testing::Test { public: - FlattenerTest() : mFlattener(std::make_unique<Flattener>(mPredictor)) {} + FlattenerTest() : mFlattener(std::make_unique<Flattener>(mPredictor, true)) {} void SetUp() override; protected: @@ -275,11 +277,7 @@ TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsProjectionSpace) { mTime += 200ms; expectAllLayersFlattened(layers); - EXPECT_EQ(overrideDisplaySpace.bounds, - Rect(mOutputState.framebufferSpace.bounds.getWidth(), - mOutputState.framebufferSpace.bounds.getHeight())); - EXPECT_EQ(overrideDisplaySpace.content, Rect(0, 0, 2, 2)); - EXPECT_EQ(overrideDisplaySpace.orientation, mOutputState.framebufferSpace.orientation); + EXPECT_EQ(overrideDisplaySpace, mOutputState.framebufferSpace); } TEST_F(FlattenerTest, flattenLayers_FlattenedLayersSetsDamageRegions) { @@ -532,5 +530,123 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { EXPECT_EQ(overrideBuffer4, overrideBuffer5); } +// Tests for a PIP +TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) { + auto& layerState1 = mTestLayers[0]->layerState; + const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer; + + auto& layerState2 = mTestLayers[1]->layerState; + const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer; + + auto& layerState3 = mTestLayers[2]->layerState; + const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer; + + const std::vector<const LayerState*> layers = { + layerState1.get(), + layerState2.get(), + layerState3.get(), + }; + + initializeFlattener(layers); + + // 3 has a buffer update, so it will not be merged, but it has no round + // corners, so it is not a PIP. + mTime += 200ms; + layerState3->resetFramesSinceBufferUpdate(); + + initializeOverrideBuffer(layers); + EXPECT_EQ(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + + // This will render a CachedSet. + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + mFlattener->renderCachedSets(mRenderEngine, mOutputState); + + // We've rendered a CachedSet, but we haven't merged it in. + EXPECT_EQ(nullptr, overrideBuffer1); + EXPECT_EQ(nullptr, overrideBuffer2); + EXPECT_EQ(nullptr, overrideBuffer3); + + // This time we merge the CachedSet in, so we have a new hash, and we should + // only have two sets. + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0); + 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_pip) { + mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5); + auto& layerState1 = mTestLayers[0]->layerState; + const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer; + + auto& layerState2 = mTestLayers[1]->layerState; + const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer; + + auto& layerState3 = mTestLayers[2]->layerState; + const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer; + + EXPECT_CALL(*mTestLayers[2]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true)); + + std::vector<LayerFE::LayerSettings> clientCompositionList = { + LayerFE::LayerSettings{}, + }; + clientCompositionList[0].source.buffer.buffer = std::make_shared< + renderengine::ExternalTexture>(mTestLayers[2]->layerFECompositionState.buffer, + mRenderEngine, + renderengine::ExternalTexture::Usage::READABLE); + EXPECT_CALL(*mTestLayers[2]->layerFE, prepareClientCompositionList(_)) + .WillOnce(Return(clientCompositionList)); + + const std::vector<const LayerState*> layers = { + layerState1.get(), + layerState2.get(), + layerState3.get(), + }; + + initializeFlattener(layers); + + // 3 has a buffer update, so it will not be merged, and it has round + // corners, so it is a PIP. + mTime += 200ms; + layerState3->resetFramesSinceBufferUpdate(); + + initializeOverrideBuffer(layers); + EXPECT_EQ(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + + // This will render a CachedSet. + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).WillOnce(Return(NO_ERROR)); + mFlattener->renderCachedSets(mRenderEngine, mOutputState); + + // We've rendered a CachedSet, but we haven't merged it in. + EXPECT_EQ(nullptr, overrideBuffer1); + EXPECT_EQ(nullptr, overrideBuffer2); + EXPECT_EQ(nullptr, overrideBuffer3); + + // This time we merge the CachedSet in, so we have a new hash, and we should + // only have two sets. + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _, _)).Times(0); + 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); + + const auto* peekThroughLayer1 = + layerState1->getOutputLayer()->getState().overrideInfo.peekThroughLayer; + const auto* peekThroughLayer2 = + layerState2->getOutputLayer()->getState().overrideInfo.peekThroughLayer; + EXPECT_EQ(&mTestLayers[2]->outputLayer, peekThroughLayer1); + EXPECT_EQ(peekThroughLayer1, peekThroughLayer2); +} } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp index 0f4d0dd119..1b1a8cddc7 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp @@ -25,7 +25,6 @@ #include "ComposerHal.h" #include <composer-command-buffer/2.2/ComposerCommandBuffer.h> -#include <gui/BufferQueue.h> #include <hidl/HidlTransportSupport.h> #include <hidl/HidlTransportUtils.h> #include <log/log.h> @@ -378,15 +377,15 @@ Error Composer::acceptDisplayChanges(Display display) Error Composer::createLayer(Display display, Layer* outLayer) { Error error = kDefaultError; - mClient->createLayer(display, BufferQueue::NUM_BUFFER_SLOTS, - [&](const auto& tmpError, const auto& tmpLayer) { - error = tmpError; - if (error != Error::NONE) { - return; - } + mClient->createLayer(display, kMaxLayerBufferCount, + [&](const auto& tmpError, const auto& tmpLayer) { + error = tmpError; + if (error != Error::NONE) { + return; + } - *outLayer = tmpLayer; - }); + *outLayer = tmpLayer; + }); return error; } diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 9104414daf..7be856fb1a 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -32,6 +32,7 @@ #include <android/hardware/graphics/composer/2.4/IComposer.h> #include <android/hardware/graphics/composer/2.4/IComposerClient.h> #include <composer-command-buffer/2.4/ComposerCommandBuffer.h> +#include <gui/BufferQueue.h> #include <gui/HdrMetadata.h> #include <math/mat4.h> #include <ui/DisplayedFrameStats.h> @@ -522,6 +523,11 @@ private: // 64KiB minus a small space for metadata such as read/write pointers static constexpr size_t kWriterInitialSize = 64 * 1024 / sizeof(uint32_t) - 16; + // Max number of buffers that may be cached for a given layer + // We obtain this number by: + // 1. Tightly coupling this cache to the max size of BufferQueue + // 2. Adding an additional slot for the layer caching feature in SurfaceFlinger (see: Planner.h) + static const constexpr uint32_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1; CommandWriter mWriter; CommandReader mReader; }; diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp index 0bc2d3eb01..23db805d5b 100644 --- a/services/surfaceflinger/FpsReporter.cpp +++ b/services/surfaceflinger/FpsReporter.cpp @@ -26,10 +26,18 @@ namespace android { -FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger) - : mFrameTimeline(frameTimeline), mFlinger(flinger) {} +FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger, + std::unique_ptr<Clock> clock) + : mFrameTimeline(frameTimeline), mFlinger(flinger), mClock(std::move(clock)) { + LOG_ALWAYS_FATAL_IF(mClock == nullptr, "Passed in null clock when constructing FpsReporter!"); +} + +void FpsReporter::dispatchLayerFps() { + const auto now = mClock->now(); + if (now - mLastDispatch < kMinDispatchDuration) { + return; + } -void FpsReporter::dispatchLayerFps() const { std::vector<TrackedListener> localListeners; { std::scoped_lock lock(mMutex); @@ -71,6 +79,8 @@ void FpsReporter::dispatchLayerFps() const { listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds)); } + + mLastDispatch = now; } void FpsReporter::binderDied(const wp<IBinder>& who) { diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h index 1cec295a31..bd7b9a5222 100644 --- a/services/surfaceflinger/FpsReporter.h +++ b/services/surfaceflinger/FpsReporter.h @@ -22,6 +22,7 @@ #include <unordered_map> +#include "Clock.h" #include "FrameTimeline/FrameTimeline.h" namespace android { @@ -31,12 +32,13 @@ class SurfaceFlinger; class FpsReporter : public IBinder::DeathRecipient { public: - FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger); + FpsReporter(frametimeline::FrameTimeline& frameTimeline, SurfaceFlinger& flinger, + std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>()); // Dispatches updated layer fps values for the registered listeners // This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock // must be held when calling this method. - void dispatchLayerFps() const EXCLUDES(mMutex); + void dispatchLayerFps() EXCLUDES(mMutex); // Override for IBinder::DeathRecipient void binderDied(const wp<IBinder>&) override; @@ -61,6 +63,10 @@ private: frametimeline::FrameTimeline& mFrameTimeline; SurfaceFlinger& mFlinger; + static const constexpr std::chrono::steady_clock::duration kMinDispatchDuration = + std::chrono::milliseconds(500); + std::unique_ptr<Clock> mClock; + std::chrono::steady_clock::time_point mLastDispatch; std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex); }; diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index be552c6435..f19e2a7863 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -256,6 +256,10 @@ int32_t jankTypeBitmaskToProto(int32_t jankType) { protoJank |= FrameTimelineEvent::JANK_UNKNOWN; jankType &= ~JankType::Unknown; } + if (jankType & JankType::SurfaceFlingerStuffing) { + protoJank |= FrameTimelineEvent::JANK_SF_STUFFING; + jankType &= ~JankType::SurfaceFlingerStuffing; + } // jankType should be 0 if all types of jank were checked for. LOG_ALWAYS_FATAL_IF(jankType != 0, "Unrecognized jank type value 0x%x", jankType); @@ -726,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; } @@ -736,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), @@ -875,7 +869,8 @@ void FrameTimeline::DisplayFrame::setGpuFence(const std::shared_ptr<FenceTime>& mGpuFence = gpuFence; } -void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync) { +void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync, + nsecs_t previousPresentTime) { if (mPredictionState == PredictionState::Expired || mSurfaceFlingerActuals.presentTime == Fence::SIGNAL_TIME_INVALID) { // Cannot do jank classification with expired predictions or invalid signal times. Set the @@ -949,7 +944,15 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& mJankType = JankType::Unknown; } } else if (mFramePresentMetadata == FramePresentMetadata::LatePresent) { - if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) { + if (std::abs(mSurfaceFlingerPredictions.presentTime - previousPresentTime) <= + mJankClassificationThresholds.presentThreshold || + previousPresentTime > mSurfaceFlingerPredictions.presentTime) { + // The previous frame was either presented in the current frame's expected vsync or + // it was presented even later than the current frame's expected vsync. + mJankType = JankType::SurfaceFlingerStuffing; + } + if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish && + !(mJankType & JankType::SurfaceFlingerStuffing)) { // Finish on time, Present late if (deltaToVsync < mJankClassificationThresholds.presentThreshold || deltaToVsync >= (mRefreshRate.getPeriodNsecs() - @@ -963,11 +966,12 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& mJankType = JankType::PredictionError; } } else if (mFrameReadyMetadata == FrameReadyMetadata::LateFinish) { - if (mFrameStartMetadata == FrameStartMetadata::LateStart) { - // Late start, Late finish, Late Present - mJankType = JankType::SurfaceFlingerScheduling; - } else { - // OnTime start, Finish late, Present late + if (!(mJankType & JankType::SurfaceFlingerStuffing) || + mSurfaceFlingerActuals.presentTime - previousPresentTime > + mRefreshRate.getPeriodNsecs() + + mJankClassificationThresholds.presentThreshold) { + // Classify CPU vs GPU if SF wasn't stuffed or if SF was stuffed but this frame + // was presented more than a vsync late. if (mGpuFence != FenceTime::NO_FENCE && mSurfaceFlingerActuals.endTime - mSurfaceFlingerActuals.startTime < mRefreshRate.getPeriodNsecs()) { @@ -989,11 +993,11 @@ void FrameTimeline::DisplayFrame::classifyJank(nsecs_t& deadlineDelta, nsecs_t& } } -void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) { +void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime, nsecs_t previousPresentTime) { mSurfaceFlingerActuals.presentTime = signalTime; nsecs_t deadlineDelta = 0; nsecs_t deltaToVsync = 0; - classifyJank(deadlineDelta, deltaToVsync); + classifyJank(deadlineDelta, deltaToVsync, previousPresentTime); for (auto& surfaceFrame : mSurfaceFrames) { surfaceFrame->onPresent(signalTime, mJankType, mRefreshRate, deadlineDelta, deltaToVsync); @@ -1158,8 +1162,9 @@ void FrameTimeline::flushPendingPresentFences() { } } auto& displayFrame = pendingPresentFence.second; - displayFrame->onPresent(signalTime); + displayFrame->onPresent(signalTime, mPreviousPresentTime); displayFrame->trace(mSurfaceFlingerPid); + mPreviousPresentTime = signalTime; mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i)); --i; diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index 41f4978543..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 { @@ -370,7 +364,7 @@ public: void onSfWakeUp(int64_t token, Fps refreshRate, std::optional<TimelineItem> predictions, nsecs_t wakeUpTime); // Sets the appropriate metadata and classifies the jank. - void onPresent(nsecs_t signalTime); + void onPresent(nsecs_t signalTime, nsecs_t previousPresentTime); // Adds the provided SurfaceFrame to the current display frame. void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame); @@ -398,7 +392,8 @@ public: void dump(std::string& result, nsecs_t baseTime) const; void tracePredictions(pid_t surfaceFlingerPid) const; void traceActuals(pid_t surfaceFlingerPid) const; - void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync); + void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync, + nsecs_t previousPresentTime); int64_t mToken = FrameTimelineInfo::INVALID_VSYNC_ID; @@ -480,6 +475,7 @@ private: uint32_t mMaxDisplayFrames; std::shared_ptr<TimeStats> mTimeStats; const pid_t mSurfaceFlingerPid; + nsecs_t mPreviousPresentTime = 0; const JankClassificationThresholds mJankClassificationThresholds; static constexpr uint32_t kDefaultMaxDisplayFrames = 64; // The initial container size for the vector<SurfaceFrames> inside display frame. Although diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 41b7ecc740..b35f6ecc0e 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -639,8 +639,10 @@ std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCom if (!targetSettings.disableBlurs) { layerSettings.backgroundBlurRadius = getBackgroundBlurRadius(); layerSettings.blurRegions = getBlurRegions(); + 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; @@ -880,7 +882,7 @@ uint32_t Layer::doTransaction(uint32_t flags) { const State& s(getDrawingState()); State& c(getCurrentState()); - if (getActiveGeometry(c) != getActiveGeometry(s)) { + if (c.width != s.width || c.height != s.height || !(c.transform == s.transform)) { // invalidate and recompute the visible regions if needed flags |= Layer::eVisibleRegion; } @@ -955,20 +957,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); @@ -1087,6 +1087,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++; @@ -1165,8 +1166,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; @@ -1588,20 +1588,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) { @@ -2479,6 +2479,8 @@ InputWindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) { ui::Transform toPhysicalDisplay; if (display) { toPhysicalDisplay = display->getTransform(); + info.displayWidth = display->getWidth(); + info.displayHeight = display->getHeight(); } fillInputFrameInfo(info, toPhysicalDisplay); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 7118513717..7d43a59a01 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -274,6 +274,8 @@ public: // Stretch effect to apply to this layer StretchEffect stretchEffect; + + Rect bufferCrop; }; /* @@ -483,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; } @@ -890,6 +889,7 @@ public: bool setStretchEffect(const StretchEffect& effect); StretchEffect getStretchEffect() const; + virtual bool setBufferCrop(const Rect& /* bufferCrop */) { return false; } virtual std::atomic<int32_t>* getPendingBufferCounter() { return nullptr; } virtual std::string getPendingBufferCounterName() { return ""; } diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp index d659398394..16f041ae31 100644 --- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp +++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp @@ -15,7 +15,6 @@ */ #include "OneShotTimer.h" - #include <utils/Log.h> #include <utils/Timers.h> #include <chrono> @@ -40,14 +39,9 @@ void calculateTimeoutTime(std::chrono::nanoseconds timestamp, timespec* spec) { namespace android { namespace scheduler { -std::chrono::steady_clock::time_point OneShotTimer::Clock::now() const { - return std::chrono::steady_clock::now(); -} - OneShotTimer::OneShotTimer(std::string name, const Interval& interval, const ResetCallback& resetCallback, - const TimeoutCallback& timeoutCallback, - std::unique_ptr<OneShotTimer::Clock> clock) + const TimeoutCallback& timeoutCallback, std::unique_ptr<Clock> clock) : mClock(std::move(clock)), mName(std::move(name)), mInterval(interval), diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h index 7285427abb..09265bb947 100644 --- a/services/surfaceflinger/Scheduler/OneShotTimer.h +++ b/services/surfaceflinger/Scheduler/OneShotTimer.h @@ -20,6 +20,7 @@ #include <chrono> #include <condition_variable> #include <thread> +#include "../Clock.h" #include <android-base/thread_annotations.h> @@ -36,17 +37,9 @@ public: using ResetCallback = std::function<void()>; using TimeoutCallback = std::function<void()>; - class Clock { - public: - Clock() = default; - virtual ~Clock() = default; - - virtual std::chrono::steady_clock::time_point now() const; - }; - OneShotTimer(std::string name, const Interval& interval, const ResetCallback& resetCallback, const TimeoutCallback& timeoutCallback, - std::unique_ptr<OneShotTimer::Clock> = std::make_unique<OneShotTimer::Clock>()); + std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>()); ~OneShotTimer(); // Initializes and turns on the idle timer. diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 5279af341a..f825fae486 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -606,13 +606,9 @@ void Scheduler::registerLayer(Layer* layer) { scheduler::LayerHistory::LayerVoteType voteType; - if (layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) { + if (!mOptions.useContentDetection || + layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) { voteType = scheduler::LayerHistory::LayerVoteType::NoVote; - } else if (!mOptions.useContentDetection) { - // If the content detection feature is off, all layers are registered at Max. We still keep - // the layer history, since we use it for other features (like Frame Rate API), so layers - // still need to be registered. - voteType = scheduler::LayerHistory::LayerVoteType::Max; } else if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) { // Running Wallpaper at Min is considered as part of content detection. voteType = scheduler::LayerHistory::LayerVoteType::Min; @@ -620,6 +616,9 @@ void Scheduler::registerLayer(Layer* layer) { voteType = scheduler::LayerHistory::LayerVoteType::Heuristic; } + // If the content detection feature is off, we still keep the layer history, + // since we use it for other features (like Frame Rate API), so layers + // still need to be registered. mLayerHistory->registerLayer(layer, voteType); } diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 1cab62767e..6c3efee19e 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2107,12 +2107,6 @@ void SurfaceFlinger::updateFrameScheduler() NO_THREAD_SAFETY_ANALYSIS { } 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); @@ -2138,8 +2132,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 @@ -2155,6 +2147,13 @@ void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncT mScheduledPresentTime = expectedVSyncTime; updateFrameScheduler(); + 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. @@ -3841,7 +3840,7 @@ void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config, void SurfaceFlinger::commitTransaction() { commitTransactionLocked(); - signalSynchronousTransactions(); + signalSynchronousTransactions(CountDownLatch::eSyncTransaction); mAnimTransactionPending = false; } @@ -4277,7 +4276,9 @@ void SurfaceFlinger::queueTransaction(TransactionState& state) { // Generate a CountDownLatch pending state if this is a synchronous transaction. if ((state.flags & eSynchronous) || state.inputWindowCommands.syncInputWindows) { state.transactionCommittedSignal = std::make_shared<CountDownLatch>( - (state.inputWindowCommands.syncInputWindows ? 2 : 1)); + (state.inputWindowCommands.syncInputWindows + ? (CountDownLatch::eSyncInputWindows | CountDownLatch::eSyncTransaction) + : CountDownLatch::eSyncTransaction)); } mTransactionQueue.emplace(state); @@ -4302,10 +4303,10 @@ void SurfaceFlinger::waitForSynchronousTransaction( } } -void SurfaceFlinger::signalSynchronousTransactions() { +void SurfaceFlinger::signalSynchronousTransactions(const uint32_t flag) { for (auto it = mTransactionCommittedSignals.begin(); it != mTransactionCommittedSignals.end();) { - if ((*it)->countDown() == 0) { + if ((*it)->countDown(flag)) { it = mTransactionCommittedSignals.erase(it); } else { it++; @@ -4828,6 +4829,11 @@ uint32_t SurfaceFlinger::setClientStateLocked( flags |= eTraversalNeeded; } } + if (what & layer_state_t::eBufferCropChanged) { + if (layer->setBufferCrop(s.bufferCrop)) { + 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 @@ -6064,6 +6070,13 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co result.append("\n"); } + { + DumpArgs plannerArgs; + plannerArgs.add(); // first argument is ignored + plannerArgs.add(String16("--layers")); + dumpPlannerInfo(plannerArgs, result); + } + /* * Dump HWComposer state */ @@ -7414,7 +7427,7 @@ status_t SurfaceFlinger::renderScreenImplLocked( void SurfaceFlinger::setInputWindowsFinished() { Mutex::Autolock _l(mStateLock); - signalSynchronousTransactions(); + signalSynchronousTransactions(CountDownLatch::eSyncInputWindows); } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 87fe55f453..ec76de35eb 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -543,24 +543,31 @@ private: class CountDownLatch { public: - explicit CountDownLatch(int32_t count) : mCount(count) {} - - int32_t countDown() { + enum { + eSyncTransaction = 1 << 0, + eSyncInputWindows = 1 << 1, + }; + explicit CountDownLatch(uint32_t flags) : mFlags(flags) {} + + // True if there is no waiting condition after count down. + bool countDown(uint32_t flag) { std::unique_lock<std::mutex> lock(mMutex); - if (mCount == 0) { - return 0; + if (mFlags == 0) { + return true; } - if (--mCount == 0) { + mFlags &= ~flag; + if (mFlags == 0) { mCountDownComplete.notify_all(); + return true; } - return mCount; + return false; } // Return true if triggered. bool wait_until(const std::chrono::seconds& timeout) const { std::unique_lock<std::mutex> lock(mMutex); const auto untilTime = std::chrono::system_clock::now() + timeout; - while (mCount != 0) { + while (mFlags != 0) { // Conditional variables can be woken up sporadically, so we check count // to verify the wakeup was triggered by |countDown|. if (std::cv_status::timeout == mCountDownComplete.wait_until(lock, untilTime)) { @@ -571,7 +578,7 @@ private: } private: - int32_t mCount; + uint32_t mFlags; mutable std::condition_variable mCountDownComplete; mutable std::mutex mMutex; }; @@ -1256,7 +1263,7 @@ private: // Add transaction to the Transaction Queue void queueTransaction(TransactionState& state) EXCLUDES(mQueueLock); void waitForSynchronousTransaction(const CountDownLatch& transactionCommittedSignal); - void signalSynchronousTransactions(); + void signalSynchronousTransactions(const uint32_t flag); /* * Generic Layer Metadata 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 3d82afa43a..10d58a6087 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -427,8 +427,8 @@ bool TimeStats::recordReadyLocked(int32_t layerId, TimeRecord* timeRecord) { return true; } -static int32_t clampToSmallestBucket(Fps fps, size_t bucketWidth) { - return (fps.getIntValue() / bucketWidth) * bucketWidth; +static int32_t clampToNearestBucket(Fps fps, size_t bucketWidth) { + return std::round(fps.getValue() / bucketWidth) * bucketWidth; } void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate, @@ -441,10 +441,10 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayR TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord; std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords; const int32_t refreshRateBucket = - clampToSmallestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH); + clampToNearestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH); const int32_t renderRateBucket = - clampToSmallestBucket(renderRate ? *renderRate : displayRefreshRate, - RENDER_RATE_BUCKET_WIDTH); + clampToNearestBucket(renderRate ? *renderRate : displayRefreshRate, + RENDER_RATE_BUCKET_WIDTH); while (!timeRecords.empty()) { if (!recordReadyLocked(layerId, &timeRecords[0])) break; ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerId, @@ -799,10 +799,10 @@ void TimeStats::incrementJankyFrames(const JankyFramesInfo& info) { static const std::string kDefaultLayerName = "none"; const int32_t refreshRateBucket = - clampToSmallestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH); + clampToNearestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH); const int32_t renderRateBucket = - clampToSmallestBucket(info.renderRate ? *info.renderRate : info.refreshRate, - RENDER_RATE_BUCKET_WIDTH); + clampToNearestBucket(info.renderRate ? *info.renderRate : info.refreshRate, + RENDER_RATE_BUCKET_WIDTH); const TimeStatsHelper::TimelineStatsKey timelineKey = {refreshRateBucket, renderRateBucket}; if (!mTimeStats.stats.count(timelineKey)) { @@ -1021,6 +1021,7 @@ void TimeStats::disable() { void TimeStats::clearAll() { std::lock_guard<std::mutex> lock(mMutex); + mTimeStats.stats.clear(); clearGlobalLocked(); clearLayersLocked(); } diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp index 011ff70409..965aac301d 100644 --- a/services/surfaceflinger/tests/LayerCallback_test.cpp +++ b/services/surfaceflinger/tests/LayerCallback_test.cpp @@ -767,7 +767,8 @@ TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_SameStateC EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); } -TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame) { +// TODO (b/183181768): Fix & re-enable +TEST_F(LayerCallbackTest, DISABLED_MultipleTransactions_SingleFrame) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); @@ -938,7 +939,8 @@ TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) { EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true)); } -TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) { +// TODO (b/183181768): Fix & re-enable +TEST_F(LayerCallbackTest, DISABLED_DesiredPresentTime_OutOfOrder) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp index 7581cd3af4..c8eeac66e9 100644 --- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp @@ -1336,7 +1336,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipV_BufferState) { Color::GREEN, true /* filtered */); } -TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) { +// TODO (b/186543004): Fix & re-enable +TEST_P(LayerRenderTypeTransactionTest, DISABLED_SetFenceBasic_BufferState) { sp<SurfaceControl> layer; Transaction transaction; ASSERT_NO_FATAL_FAILURE( diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp index a2291b2762..dec0ff5df2 100644 --- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp +++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp @@ -28,6 +28,7 @@ #include "FpsReporter.h" #include "Layer.h" #include "TestableSurfaceFlinger.h" +#include "fake/FakeClock.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/MockEventThread.h" #include "mock/MockFrameTimeline.h" @@ -91,7 +92,9 @@ protected: sp<Layer> mUnrelated; sp<TestableFpsListener> mFpsListener; - sp<FpsReporter> mFpsReporter = new FpsReporter(mFrameTimeline, *(mFlinger.flinger())); + fake::FakeClock* mClock = new fake::FakeClock(); + sp<FpsReporter> mFpsReporter = + new FpsReporter(mFrameTimeline, *(mFlinger.flinger()), std::unique_ptr<Clock>(mClock)); }; FpsReporterTest::FpsReporterTest() { @@ -178,6 +181,7 @@ TEST_F(FpsReporterTest, callsListeners) { .WillOnce(Return(expectedFps)); mFpsReporter->addListener(mFpsListener, kTaskId); + mClock->advanceTime(600ms); mFpsReporter->dispatchLayerFps(); EXPECT_EQ(expectedFps, mFpsListener->lastReportedFps); mFpsReporter->removeListener(mFpsListener); @@ -187,5 +191,34 @@ TEST_F(FpsReporterTest, callsListeners) { mFpsReporter->dispatchLayerFps(); } +TEST_F(FpsReporterTest, rateLimits) { + const constexpr int32_t kTaskId = 12; + LayerMetadata targetMetadata; + targetMetadata.setInt32(METADATA_TASK_ID, kTaskId); + mTarget = createBufferStateLayer(targetMetadata); + mFlinger.mutableCurrentState().layersSortedByZ.add(mTarget); + + float firstFps = 44.0; + float secondFps = 53.0; + + EXPECT_CALL(mFrameTimeline, computeFps(UnorderedElementsAre(mTarget->getSequence()))) + .WillOnce(Return(firstFps)) + .WillOnce(Return(secondFps)); + + mFpsReporter->addListener(mFpsListener, kTaskId); + mClock->advanceTime(600ms); + mFpsReporter->dispatchLayerFps(); + EXPECT_EQ(firstFps, mFpsListener->lastReportedFps); + mClock->advanceTime(200ms); + mFpsReporter->dispatchLayerFps(); + EXPECT_EQ(firstFps, mFpsListener->lastReportedFps); + mClock->advanceTime(200ms); + mFpsReporter->dispatchLayerFps(); + EXPECT_EQ(firstFps, mFpsListener->lastReportedFps); + mClock->advanceTime(200ms); + mFpsReporter->dispatchLayerFps(); + EXPECT_EQ(secondFps, mFpsListener->lastReportedFps); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index 8a3f561487..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, @@ -1561,13 +1562,22 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) /* * Case 1 - cpu time > vsync period but combined time > deadline > deadline -> cpudeadlinemissed * Case 2 - cpu time < vsync period but combined time > deadline -> gpudeadlinemissed + * Case 3 - previous frame ran longer -> sf_stuffing + * Case 4 - Long cpu under SF stuffing -> cpudeadlinemissed */ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); auto gpuFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); auto gpuFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + auto gpuFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + auto gpuFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40}); int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 60, 60}); + int64_t sfToken3 = mTokenManager->generateTokenForPredictions({82, 90, 90}); + int64_t sfToken4 = mTokenManager->generateTokenForPredictions({112, 120, 120}); + // case 1 - cpu time = 33 - 12 = 21, vsync period = 11 mFrameTimeline->setSfWakeUp(sfToken1, 12, Fps::fromPeriodNsecs(11)); mFrameTimeline->setSfPresent(33, presentFence1, gpuFence1); @@ -1578,12 +1588,12 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) // Fences haven't been flushed yet, so it should be 0 EXPECT_EQ(displayFrame0->getActuals().presentTime, 0); - // case 2 - cpu time = 56 - 52 = 4, vsync period = 11 - mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11)); + // case 2 - cpu time = 56 - 52 = 4, vsync period = 30 + mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(30)); mFrameTimeline->setSfPresent(56, presentFence2, gpuFence2); auto displayFrame1 = getDisplayFrame(1); - gpuFence2->signalForTest(66); - presentFence2->signalForTest(71); + gpuFence2->signalForTest(76); + presentFence2->signalForTest(90); EXPECT_EQ(displayFrame1->getActuals().presentTime, 0); // Fences have flushed for first displayFrame, so the present timestamps should be updated @@ -1592,35 +1602,41 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) EXPECT_EQ(displayFrame0->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); EXPECT_EQ(displayFrame0->getJankType(), JankType::SurfaceFlingerCpuDeadlineMissed); - addEmptyDisplayFrame(); + // case 3 - cpu time = 86 - 82 = 4, vsync period = 30 + mFrameTimeline->setSfWakeUp(sfToken3, 106, Fps::fromPeriodNsecs(30)); + mFrameTimeline->setSfPresent(112, presentFence3, gpuFence3); + auto displayFrame2 = getDisplayFrame(2); + gpuFence3->signalForTest(116); + presentFence3->signalForTest(120); + EXPECT_EQ(displayFrame2->getActuals().presentTime, 0); // Fences have flushed for second displayFrame, so the present timestamps should be updated - EXPECT_EQ(displayFrame1->getActuals().presentTime, 71); + EXPECT_EQ(displayFrame1->getActuals().presentTime, 90); EXPECT_EQ(displayFrame1->getFramePresentMetadata(), FramePresentMetadata::LatePresent); EXPECT_EQ(displayFrame1->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); EXPECT_EQ(displayFrame1->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed); -} -TEST_F(FrameTimelineTest, jankClassification_displayFrameLateStartLateFinishLatePresent) { - auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); - int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40}); - mFrameTimeline->setSfWakeUp(sfToken1, 26, Fps::fromPeriodNsecs(11)); - mFrameTimeline->setSfPresent(36, presentFence1); - auto displayFrame = getDisplayFrame(0); - presentFence1->signalForTest(52); + // case 4 - cpu time = 86 - 82 = 4, vsync period = 30 + mFrameTimeline->setSfWakeUp(sfToken4, 120, Fps::fromPeriodNsecs(30)); + mFrameTimeline->setSfPresent(140, presentFence4, gpuFence4); + auto displayFrame3 = getDisplayFrame(3); + gpuFence4->signalForTest(156); + presentFence4->signalForTest(180); - // Fences haven't been flushed yet, so it should be 0 - EXPECT_EQ(displayFrame->getActuals().presentTime, 0); + EXPECT_EQ(displayFrame3->getActuals().presentTime, 0); + // Fences have flushed for third displayFrame, so the present timestamps should be updated + EXPECT_EQ(displayFrame2->getActuals().presentTime, 120); + EXPECT_EQ(displayFrame2->getFramePresentMetadata(), FramePresentMetadata::LatePresent); + EXPECT_EQ(displayFrame2->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); + EXPECT_EQ(displayFrame2->getJankType(), JankType::SurfaceFlingerStuffing); addEmptyDisplayFrame(); - displayFrame = getDisplayFrame(0); - // Fences have flushed, so the present timestamps should be updated - EXPECT_EQ(displayFrame->getActuals().presentTime, 52); - EXPECT_EQ(displayFrame->getFrameStartMetadata(), FrameStartMetadata::LateStart); - EXPECT_EQ(displayFrame->getFramePresentMetadata(), FramePresentMetadata::LatePresent); - EXPECT_EQ(displayFrame->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); - EXPECT_EQ(displayFrame->getJankType(), JankType::SurfaceFlingerScheduling); + // Fences have flushed for third displayFrame, so the present timestamps should be updated + EXPECT_EQ(displayFrame3->getActuals().presentTime, 180); + EXPECT_EQ(displayFrame3->getFramePresentMetadata(), FramePresentMetadata::LatePresent); + EXPECT_EQ(displayFrame3->getFrameReadyMetadata(), FrameReadyMetadata::LateFinish); + EXPECT_EQ(displayFrame3->getJankType(), JankType::SurfaceFlingerGpuDeadlineMissed); } TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) { diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp index a1f0588fc7..691676420c 100644 --- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp +++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp @@ -23,23 +23,13 @@ #include "AsyncCallRecorder.h" #include "Scheduler/OneShotTimer.h" +#include "fake/FakeClock.h" using namespace std::chrono_literals; namespace android { namespace scheduler { -class FakeClock : public OneShotTimer::Clock { -public: - virtual ~FakeClock() = default; - std::chrono::steady_clock::time_point now() const override { return mNow; } - - void advanceTime(std::chrono::nanoseconds delta) { mNow += delta; } - -private: - std::chrono::steady_clock::time_point mNow; -}; - class OneShotTimerTest : public testing::Test { protected: OneShotTimerTest() = default; @@ -58,17 +48,17 @@ protected: namespace { TEST_F(OneShotTimerTest, createAndDestroyTest) { - FakeClock* clock = new FakeClock(); + fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>( - "TestTimer", 3ms, [] {}, [] {}, std::unique_ptr<FakeClock>(clock)); + "TestTimer", 3ms, [] {}, [] {}, std::unique_ptr<fake::FakeClock>(clock)); } TEST_F(OneShotTimerTest, startStopTest) { - FakeClock* clock = new FakeClock(); + fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms, mResetTimerCallback.getInvocable(), mExpiredTimerCallback.getInvocable(), - std::unique_ptr<FakeClock>(clock)); + std::unique_ptr<fake::FakeClock>(clock)); mIdleTimer->start(); EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value()); @@ -83,11 +73,11 @@ TEST_F(OneShotTimerTest, startStopTest) { } TEST_F(OneShotTimerTest, resetTest) { - FakeClock* clock = new FakeClock(); + fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms, mResetTimerCallback.getInvocable(), mExpiredTimerCallback.getInvocable(), - std::unique_ptr<FakeClock>(clock)); + std::unique_ptr<fake::FakeClock>(clock)); mIdleTimer->start(); EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); @@ -105,11 +95,11 @@ TEST_F(OneShotTimerTest, resetTest) { } TEST_F(OneShotTimerTest, resetBackToBackTest) { - FakeClock* clock = new FakeClock(); + fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms, mResetTimerCallback.getInvocable(), mExpiredTimerCallback.getInvocable(), - std::unique_ptr<FakeClock>(clock)); + std::unique_ptr<fake::FakeClock>(clock)); mIdleTimer->start(); EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); @@ -139,11 +129,11 @@ TEST_F(OneShotTimerTest, resetBackToBackTest) { } TEST_F(OneShotTimerTest, startNotCalledTest) { - FakeClock* clock = new FakeClock(); + fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms, mResetTimerCallback.getInvocable(), mExpiredTimerCallback.getInvocable(), - std::unique_ptr<FakeClock>(clock)); + std::unique_ptr<fake::FakeClock>(clock)); // The start hasn't happened, so the callback does not happen. EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value()); EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value()); @@ -155,11 +145,11 @@ TEST_F(OneShotTimerTest, startNotCalledTest) { } TEST_F(OneShotTimerTest, idleTimerIdlesTest) { - FakeClock* clock = new FakeClock(); + fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms, mResetTimerCallback.getInvocable(), mExpiredTimerCallback.getInvocable(), - std::unique_ptr<FakeClock>(clock)); + std::unique_ptr<fake::FakeClock>(clock)); mIdleTimer->start(); EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); clock->advanceTime(2ms); @@ -180,11 +170,11 @@ TEST_F(OneShotTimerTest, idleTimerIdlesTest) { } TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) { - FakeClock* clock = new FakeClock(); + fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms, mResetTimerCallback.getInvocable(), mExpiredTimerCallback.getInvocable(), - std::unique_ptr<FakeClock>(clock)); + std::unique_ptr<fake::FakeClock>(clock)); mIdleTimer->start(); EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); @@ -197,11 +187,11 @@ TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) { } TEST_F(OneShotTimerTest, noCallbacksAfterStopAndResetTest) { - FakeClock* clock = new FakeClock(); + fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms, mResetTimerCallback.getInvocable(), mExpiredTimerCallback.getInvocable(), - std::unique_ptr<FakeClock>(clock)); + std::unique_ptr<fake::FakeClock>(clock)); mIdleTimer->start(); EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); clock->advanceTime(2ms); @@ -215,11 +205,11 @@ TEST_F(OneShotTimerTest, noCallbacksAfterStopAndResetTest) { } TEST_F(OneShotTimerTest, noCallbacksAfterStopTest) { - FakeClock* clock = new FakeClock(); + fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms, mResetTimerCallback.getInvocable(), mExpiredTimerCallback.getInvocable(), - std::unique_ptr<FakeClock>(clock)); + std::unique_ptr<fake::FakeClock>(clock)); mIdleTimer->start(); EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); EXPECT_FALSE(mExpiredTimerCallback.waitForUnexpectedCall().has_value()); diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp index 188ea758d4..ff53a7b09c 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -897,24 +897,24 @@ TEST_F(TimeStatsTest, canClearDumpOnlyTimeStats) { EXPECT_THAT(result, HasSubstr("compositionStrategyChanges = 0")); EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms")); EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms")); - std::string expectedResult = "totalTimelineFrames = " + std::to_string(0); - EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "jankyFrames = " + std::to_string(0); - EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "sfLongCpuJankyFrames = " + std::to_string(0); - EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "sfLongGpuJankyFrames = " + std::to_string(0); - EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "sfUnattributedJankyFrames = " + std::to_string(0); - EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "appUnattributedJankyFrames = " + std::to_string(0); - EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "sfSchedulingJankyFrames = " + std::to_string(0); - EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "sfPredictionErrorJankyFrames = " + std::to_string(0); - EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(0); - EXPECT_THAT(result, HasSubstr(expectedResult)); + std::string expectedResult = "totalTimelineFrames = "; + EXPECT_THAT(result, Not(HasSubstr(expectedResult))); + expectedResult = "jankyFrames = "; + EXPECT_THAT(result, Not(HasSubstr(expectedResult))); + expectedResult = "sfLongCpuJankyFrames = "; + EXPECT_THAT(result, Not(HasSubstr(expectedResult))); + expectedResult = "sfLongGpuJankyFrames = "; + EXPECT_THAT(result, Not(HasSubstr(expectedResult))); + expectedResult = "sfUnattributedJankyFrames = "; + EXPECT_THAT(result, Not(HasSubstr(expectedResult))); + expectedResult = "appUnattributedJankyFrames = "; + EXPECT_THAT(result, Not(HasSubstr(expectedResult))); + expectedResult = "sfSchedulingJankyFrames = "; + EXPECT_THAT(result, Not(HasSubstr(expectedResult))); + expectedResult = "sfPredictionErrorJankyFrames = "; + EXPECT_THAT(result, Not(HasSubstr(expectedResult))); + expectedResult = "appBufferStuffingJankyFrames = "; + EXPECT_THAT(result, Not(HasSubstr(expectedResult))); } TEST_F(TimeStatsTest, canDumpWithMaxLayers) { @@ -1348,6 +1348,30 @@ TEST_F(TimeStatsTest, canSurviveMonkey) { } } +TEST_F(TimeStatsTest, refreshRateIsClampedToNearestBucket) { + // this stat is not in the proto so verify by checking the string dump + const auto verifyRefreshRateBucket = [&](Fps fps, int32_t bucket) { + EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty()); + EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); + + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000); + mTimeStats->incrementJankyFrames( + {fps, std::nullopt, UID_0, genLayerName(LAYER_ID_0), JankType::None, 0, 0, 0}); + const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); + std::string expectedResult = "displayRefreshRate = " + std::to_string(bucket) + " fps"; + EXPECT_THAT(result, HasSubstr(expectedResult)) << "failed for " << fps; + }; + + verifyRefreshRateBucket(Fps(91.f), 90); + verifyRefreshRateBucket(Fps(89.f), 90); + + verifyRefreshRateBucket(Fps(61.f), 60); + verifyRefreshRateBucket(Fps(59.f), 60); + + verifyRefreshRateBucket(Fps(31.f), 30); + verifyRefreshRateBucket(Fps(29.f), 30); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/fake/FakeClock.h b/services/surfaceflinger/tests/unittests/fake/FakeClock.h new file mode 100644 index 0000000000..6d9c7646c8 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/fake/FakeClock.h @@ -0,0 +1,34 @@ +/* + * 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 "../../Clock.h" + +namespace android::fake { + +class FakeClock : public Clock { +public: + virtual ~FakeClock() = default; + std::chrono::steady_clock::time_point now() const override { return mNow; } + + void advanceTime(std::chrono::nanoseconds delta) { mNow += delta; } + +private: + std::chrono::steady_clock::time_point mNow; +}; + +} // namespace android::fake
\ No newline at end of file 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 diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp index 67cd8754a3..440c5b144a 100644 --- a/vulkan/libvulkan/Android.bp +++ b/vulkan/libvulkan/Android.bp @@ -29,17 +29,14 @@ ndk_library { unversioned_until: "current", } -llndk_library { - name: "libvulkan.llndk", - symbol_file: "libvulkan.map.txt", - export_llndk_headers: [ - "vulkan_headers_llndk", - ], -} - cc_library_shared { name: "libvulkan", - llndk_stubs: "libvulkan.llndk", + llndk: { + symbol_file: "libvulkan.map.txt", + export_llndk_headers: [ + "vulkan_headers", + ], + }, clang: true, sanitize: { misc_undefined: ["integer"], |