diff options
40 files changed, 1086 insertions, 334 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 9ba4819ee8..d4c161609c 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -305,8 +305,9 @@ static const CommandOptions AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot( /* * Returns a vector of dump fds under |dir_path| with a given |file_prefix|. - * The returned vector is sorted by the mtimes of the dumps. If |limit_by_mtime| - * is set, the vector only contains files that were written in the last 30 minutes. + * The returned vector is sorted by the mtimes of the dumps with descending + * order. If |limit_by_mtime| is set, the vector only contains files that + * were written in the last 30 minutes. */ static std::vector<DumpData> GetDumpFds(const std::string& dir_path, const std::string& file_prefix, @@ -353,6 +354,10 @@ static std::vector<DumpData> GetDumpFds(const std::string& dir_path, dump_data.emplace_back(DumpData{abs_path, std::move(fd), st.st_mtime}); } + if (!dump_data.empty()) { + std::sort(dump_data.begin(), dump_data.end(), + [](const auto& a, const auto& b) { return a.mtime > b.mtime; }); + } return dump_data; } @@ -1362,6 +1367,53 @@ static void DumpExternalFragmentationInfo() { printf("\n"); } +static void DumpstateArcOnly() { + // Trimmed-down version of dumpstate to only include a whitelisted + // set of logs (system log, event log, and system server / system app + // crashes, and ARC networking logs). See b/136273873 and b/138459828 + // for context. New sections must be first approved by Chrome OS Privacy + // and then added to server side cros monitoring PII scrubber before adding + // them here. See cl/312126645 for an example. + DurationReporter duration_reporter("DUMPSTATE"); + unsigned long timeout_ms; + // calculate timeout + timeout_ms = logcat_timeout({"main", "system", "crash"}); + RunCommand("SYSTEM LOG", + {"logcat", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, + CommandOptions::WithTimeoutInMs(timeout_ms).Build()); + timeout_ms = logcat_timeout({"events"}); + RunCommand( + "EVENT LOG", + {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, + CommandOptions::WithTimeoutInMs(timeout_ms).Build()); + + printf("========================================================\n"); + printf("== Networking Service\n"); + printf("========================================================\n"); + + // ARC networking service implements dumpsys by reusing the 'wifi' service name. + // The top-level handler is implemented in handleDump() in + // vendor/google_arc/libs/arc-services/src/com/android/server/arc/net/ArcNetworkService.java. + // It outputs a subset of Android system server state relevant for debugging ARC + // connectivity issues, in a PII-free manner. See b/147270970. + RunDumpsys("DUMPSYS NETWORK_SERVICE_LIMITED", {"wifi", "-a"}, + CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + + printf("========================================================\n"); + printf("== Dropbox crashes\n"); + printf("========================================================\n"); + + RunDumpsys("DROPBOX SYSTEM SERVER CRASHES", {"dropbox", "-p", "system_server_crash"}); + RunDumpsys("DROPBOX SYSTEM APP CRASHES", {"dropbox", "-p", "system_app_crash"}); + + printf("========================================================\n"); + printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(), + ds.progress_->GetMax(), ds.progress_->GetInitialMax()); + printf("========================================================\n"); + printf("== dumpstate: done (id %d)\n", ds.id_); + printf("========================================================\n"); +} + // Dumps various things. Returns early with status USER_CONSENT_DENIED if user denies consent // via the consent they are shown. Ignores other errors that occur while running various // commands. The consent checking is currently done around long running tasks, which happen to @@ -2048,7 +2100,7 @@ void Dumpstate::DumpstateBoard() { static void ShowUsage() { fprintf(stderr, "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-d] [-p] " - "[-z] [-s] [-S] [-q] [-P] [-R] [-V version]\n" + "[-z] [-s] [-S] [-q] [-P] [-R] [-A] [-V version]\n" " -h: display this help message\n" " -b: play sound file instead of vibrate, at beginning of job\n" " -e: play sound file instead of vibrate, at end of job\n" @@ -2061,6 +2113,7 @@ static void ShowUsage() { " -P: send broadcast when started and do progress updates\n" " -R: take bugreport in remote mode (requires -z and -d, shouldn't be used with -P)\n" " -w: start binder service and make it wait for a call to startBugreport\n" + " -A: output limited information that is safe for submission in ARC++ bugreports\n" " -v: prints the dumpstate header and exit\n"); } @@ -2306,13 +2359,12 @@ static void LogDumpOptions(const Dumpstate::DumpOptions& options) { "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_screenshot: %d " "is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d " "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s " - "args: %s\n", + "arc_only: %d args: %s\n", options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket, options.do_screenshot, options.is_remote_mode, options.show_header_only, - options.do_start_service, - options.telephony_only, options.wifi_only, options.do_progress_updates, - options.bugreport_fd.get(), options.bugreport_mode.c_str(), - toString(options.dumpstate_hal_mode).c_str(), options.args.c_str()); + options.do_start_service, options.telephony_only, options.wifi_only, + options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(), + toString(options.dumpstate_hal_mode).c_str(), options.arc_only, options.args.c_str()); } void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode, @@ -2334,7 +2386,7 @@ void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode, Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) { RunStatus status = RunStatus::OK; int c; - while ((c = getopt(argc, argv, "dho:svqzpPBRSV:w")) != -1) { + while ((c = getopt(argc, argv, "dho:svqzpAPBRSV:w")) != -1) { switch (c) { // clang-format off case 'd': do_add_date = true; break; @@ -2346,6 +2398,7 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) case 'p': do_screenshot = true; break; case 'P': do_progress_updates = true; break; case 'R': is_remote_mode = true; break; + case 'A': arc_only = true; break; case 'V': break; // compatibility no-op case 'w': // This was already processed @@ -2630,6 +2683,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // duration is logged into MYLOG instead. PrintHeader(); + // TODO(nandana) reduce code repetition in if branches if (options_->telephony_only) { MaybeTakeEarlyScreenshot(); onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package); @@ -2641,6 +2695,11 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package); MaybeCheckUserConsent(calling_uid, calling_package); DumpstateWifiOnly(); + } else if (options_->arc_only) { + MaybeTakeEarlyScreenshot(); + onUiIntensiveBugreportDumpsFinished(calling_uid, calling_package); + MaybeCheckUserConsent(calling_uid, calling_package); + DumpstateArcOnly(); } else { // Invoke critical dumpsys first to preserve system state, before doing anything else. RunDumpsysCritical(); diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 28d8936828..c8c9f452f0 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -377,6 +377,8 @@ class Dumpstate { bool do_start_service = false; bool telephony_only = false; bool wifi_only = false; + // Trimmed-down version of dumpstate to only include whitelisted logs. + bool arc_only = false; // Whether progress updates should be published. bool do_progress_updates = false; // The mode we'll use when calling IDumpstateDevice::dumpstateBoard. diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp index 68d373377c..f1342a5b53 100644 --- a/cmds/dumpstate/main.cpp +++ b/cmds/dumpstate/main.cpp @@ -30,7 +30,7 @@ bool ShouldStartServiceAndWait(int argc, char* argv[]) { bool do_wait = false; int c; // Keep flags in sync with Dumpstate::DumpOptions::Initialize. - while ((c = getopt(argc, argv, "wdho:svqzpPBRSV:")) != -1 && !do_wait) { + while ((c = getopt(argc, argv, "dho:svqzpAPBRSV:w")) != -1 && !do_wait) { switch (c) { case 'w': do_wait = true; diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index 9871a3bd6c..7078521b52 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -179,6 +179,7 @@ TEST_F(DumpOptionsTest, InitializeNone) { EXPECT_FALSE(options_.do_screenshot); EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); + EXPECT_FALSE(options_.arc_only); EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } @@ -206,6 +207,7 @@ TEST_F(DumpOptionsTest, InitializeAdbBugreport) { EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); + EXPECT_FALSE(options_.arc_only); EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } @@ -231,6 +233,7 @@ TEST_F(DumpOptionsTest, InitializeAdbShellBugreport) { EXPECT_FALSE(options_.do_screenshot); EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); + EXPECT_FALSE(options_.arc_only); EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } @@ -249,6 +252,7 @@ TEST_F(DumpOptionsTest, InitializeFullBugReport) { EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); EXPECT_FALSE(options_.do_start_service); + EXPECT_FALSE(options_.arc_only); } TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) { @@ -266,6 +270,7 @@ TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) { EXPECT_FALSE(options_.show_header_only); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); + EXPECT_FALSE(options_.arc_only); } TEST_F(DumpOptionsTest, InitializeRemoteBugReport) { @@ -282,6 +287,7 @@ TEST_F(DumpOptionsTest, InitializeRemoteBugReport) { EXPECT_FALSE(options_.show_header_only); EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.use_socket); + EXPECT_FALSE(options_.arc_only); } TEST_F(DumpOptionsTest, InitializeWearBugReport) { @@ -299,6 +305,7 @@ TEST_F(DumpOptionsTest, InitializeWearBugReport) { EXPECT_FALSE(options_.show_header_only); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); + EXPECT_FALSE(options_.arc_only); } TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) { @@ -316,6 +323,7 @@ TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) { EXPECT_FALSE(options_.show_header_only); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); + EXPECT_FALSE(options_.arc_only); } TEST_F(DumpOptionsTest, InitializeWifiBugReport) { @@ -333,6 +341,37 @@ TEST_F(DumpOptionsTest, InitializeWifiBugReport) { EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); + EXPECT_FALSE(options_.arc_only); +} + +TEST_F(DumpOptionsTest, InitializeArcOnlyBugreport) { + // clang-format off + char* argv[] = { + const_cast<char*>("dumpstatez"), + const_cast<char*>("-S"), + const_cast<char*>("-d"), + const_cast<char*>("-z"), + const_cast<char*>("-q"), + const_cast<char*>("-A") + }; + // clang-format on + + Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); + + EXPECT_EQ(status, Dumpstate::RunStatus::OK); + EXPECT_TRUE(options_.do_add_date); + EXPECT_TRUE(options_.do_zip_file); + EXPECT_TRUE(options_.use_control_socket); + EXPECT_FALSE(options_.do_vibrate); + EXPECT_TRUE(options_.arc_only); + + // Other options retain default values + EXPECT_FALSE(options_.show_header_only); + EXPECT_FALSE(options_.do_screenshot); + EXPECT_FALSE(options_.do_progress_updates); + EXPECT_FALSE(options_.is_remote_mode); + EXPECT_FALSE(options_.use_socket); + EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializeDefaultBugReport) { @@ -361,6 +400,7 @@ TEST_F(DumpOptionsTest, InitializeDefaultBugReport) { EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); EXPECT_FALSE(options_.wifi_only); + EXPECT_FALSE(options_.arc_only); } TEST_F(DumpOptionsTest, InitializePartial1) { @@ -390,6 +430,7 @@ TEST_F(DumpOptionsTest, InitializePartial1) { EXPECT_FALSE(options_.do_screenshot); EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); + EXPECT_FALSE(options_.arc_only); EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } @@ -419,6 +460,7 @@ TEST_F(DumpOptionsTest, InitializePartial2) { EXPECT_FALSE(options_.do_zip_file); EXPECT_FALSE(options_.use_socket); EXPECT_FALSE(options_.use_control_socket); + EXPECT_FALSE(options_.arc_only); EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h index 610062697c..2427a075a1 100644 --- a/include/input/DisplayViewport.h +++ b/include/input/DisplayViewport.h @@ -74,36 +74,40 @@ struct DisplayViewport { int32_t physicalBottom; int32_t deviceWidth; int32_t deviceHeight; + bool isActive; std::string uniqueId; // The actual (hardware) port that the associated display is connected to. // Not all viewports will have this specified. std::optional<uint8_t> physicalPort; ViewportType type; - DisplayViewport() : - displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0), - logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0), - physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0), - deviceWidth(0), deviceHeight(0), uniqueId(), physicalPort(std::nullopt), - type(ViewportType::VIEWPORT_INTERNAL) { - } + DisplayViewport() + : displayId(ADISPLAY_ID_NONE), + orientation(DISPLAY_ORIENTATION_0), + logicalLeft(0), + logicalTop(0), + logicalRight(0), + logicalBottom(0), + physicalLeft(0), + physicalTop(0), + physicalRight(0), + physicalBottom(0), + deviceWidth(0), + deviceHeight(0), + isActive(false), + uniqueId(), + physicalPort(std::nullopt), + type(ViewportType::VIEWPORT_INTERNAL) {} bool operator==(const DisplayViewport& other) const { - return displayId == other.displayId - && orientation == other.orientation - && logicalLeft == other.logicalLeft - && logicalTop == other.logicalTop - && logicalRight == other.logicalRight - && logicalBottom == other.logicalBottom - && physicalLeft == other.physicalLeft - && physicalTop == other.physicalTop - && physicalRight == other.physicalRight - && physicalBottom == other.physicalBottom - && deviceWidth == other.deviceWidth - && deviceHeight == other.deviceHeight - && uniqueId == other.uniqueId - && physicalPort == other.physicalPort - && type == other.type; + return displayId == other.displayId && orientation == other.orientation && + logicalLeft == other.logicalLeft && logicalTop == other.logicalTop && + logicalRight == other.logicalRight && logicalBottom == other.logicalBottom && + physicalLeft == other.physicalLeft && physicalTop == other.physicalTop && + physicalRight == other.physicalRight && physicalBottom == other.physicalBottom && + deviceWidth == other.deviceWidth && deviceHeight == other.deviceHeight && + isActive == other.isActive && uniqueId == other.uniqueId && + physicalPort == other.physicalPort && type == other.type; } bool operator!=(const DisplayViewport& other) const { @@ -127,6 +131,7 @@ struct DisplayViewport { physicalBottom = height; deviceWidth = width; deviceHeight = height; + isActive = false; uniqueId.clear(); physicalPort = std::nullopt; type = ViewportType::VIEWPORT_INTERNAL; @@ -134,18 +139,16 @@ struct DisplayViewport { std::string toString() const { return StringPrintf("Viewport %s: displayId=%d, uniqueId=%s, port=%s, orientation=%d, " - "logicalFrame=[%d, %d, %d, %d], " - "physicalFrame=[%d, %d, %d, %d], " - "deviceSize=[%d, %d]", - viewportTypeToString(type), displayId, - uniqueId.c_str(), - physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() : "<none>", - orientation, - logicalLeft, logicalTop, - logicalRight, logicalBottom, - physicalLeft, physicalTop, - physicalRight, physicalBottom, - deviceWidth, deviceHeight); + "logicalFrame=[%d, %d, %d, %d], " + "physicalFrame=[%d, %d, %d, %d], " + "deviceSize=[%d, %d], " + "isActive=[%d]", + viewportTypeToString(type), displayId, uniqueId.c_str(), + physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() + : "<none>", + orientation, logicalLeft, logicalTop, logicalRight, logicalBottom, + physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth, + deviceHeight, isActive); } }; diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h index 7f04611309..86de394a31 100644 --- a/include/input/InputApplication.h +++ b/include/input/InputApplication.h @@ -61,6 +61,11 @@ public: return mInfo.token ? mInfo.dispatchingTimeout : defaultValue; } + inline std::chrono::nanoseconds getDispatchingTimeout( + std::chrono::nanoseconds defaultValue) const { + return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue; + } + inline sp<IBinder> getApplicationToken() const { return mInfo.token; } diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index c5e56fd91f..2dac5b62a7 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -222,6 +222,11 @@ public: return mInfo.token ? mInfo.dispatchingTimeout : defaultValue; } + inline std::chrono::nanoseconds getDispatchingTimeout( + std::chrono::nanoseconds defaultValue) const { + return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue; + } + /** * Requests that the state of this object be updated to reflect * the most current available information about the application. diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h index 8d12754d79..1a7c2c946a 100644 --- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h +++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h @@ -431,6 +431,12 @@ static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayo static_cast<int64_t>( aidl::android::hardware::graphics::common::PlaneLayoutComponentType::A)}; +static const aidl::android::hardware::graphics::common::ExtendableType + PlaneLayoutComponentType_RAW = + {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE, + static_cast<int64_t>( + aidl::android::hardware::graphics::common::PlaneLayoutComponentType::RAW)}; + /*---------------------------------------------------------------------------------------------*/ /** diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h index 6366529a10..3afbabf1dc 100644 --- a/libs/gui/include/gui/ISurfaceComposerClient.h +++ b/libs/gui/include/gui/ISurfaceComposerClient.h @@ -33,7 +33,7 @@ public: DECLARE_META_INTERFACE(SurfaceComposerClient) // flags for createSurface() - enum { // (keep in sync with Surface.java) + enum { // (keep in sync with SurfaceControl.java) eHidden = 0x00000004, eDestroyBackbuffer = 0x00000020, eSecure = 0x00000080, @@ -42,6 +42,7 @@ public: eProtectedByApp = 0x00000800, eProtectedByDRM = 0x00001000, eCursorWindow = 0x00002000, + eNoColorFill = 0x00004000, eFXSurfaceBufferQueue = 0x00000000, eFXSurfaceEffect = 0x00020000, diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index 25130e2a03..36aad2eced 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -236,7 +236,11 @@ int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) __INTRODUCED_IN /** Compatibility value for ANativeWindow_setFrameRate. */ enum ANativeWindow_FrameRateCompatibility { /** - * There are no inherent restrictions on the frame rate of this window. + * There are no inherent restrictions on the frame rate of this window. When + * the system selects a frame rate other than what the app requested, the + * app will be able to run at the system frame rate without requiring pull + * down. This value should be used when displaying game content, UIs, and + * anything that isn't video. */ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT = 0, /** @@ -246,7 +250,7 @@ enum ANativeWindow_FrameRateCompatibility { * to do pull down or use some other technique to adapt to the system's * frame rate. The user experience is likely to be worse (e.g. more frame * stuttering) than it would be if the system had chosen the app's requested - * frame rate. + * frame rate. This value should be used for video content. */ ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1 }; diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index a1eb0079df..21c8ae165d 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -102,6 +102,12 @@ EventEntry::~EventEntry() { releaseInjectionState(); } +std::string EventEntry::getDescription() const { + std::string result; + appendDescription(result); + return result; +} + void EventEntry::release() { refCount -= 1; if (refCount == 0) { diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index ab481bd411..a135409365 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -83,6 +83,8 @@ struct EventEntry { virtual void appendDescription(std::string& msg) const = 0; + std::string getDescription() const; + protected: EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags); virtual ~EventEntry(); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index a239723652..e6e3347ae9 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -78,7 +78,7 @@ namespace android::inputdispatcher { // Default input dispatching timeout if there is no focused application or paused window // from which to determine an appropriate dispatching timeout. -constexpr nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec +constexpr std::chrono::nanoseconds DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5s; // Amount of time to allow for all pending events to be processed when an app switch // key is on the way. This is used to preempt input dispatch and drop input events @@ -620,6 +620,33 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } } +/** + * Return true if the events preceding this incoming motion event should be dropped + * Return false otherwise (the default behaviour) + */ +bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) { + bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN && + (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER); + if (isPointerDownEvent && + mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY && + mInputTargetWaitApplicationToken != nullptr) { + int32_t displayId = motionEntry.displayId; + int32_t x = static_cast<int32_t>( + motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); + int32_t y = static_cast<int32_t>( + motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); + sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y); + if (touchedWindowHandle != nullptr && + touchedWindowHandle->getApplicationToken() != mInputTargetWaitApplicationToken) { + // User touched a different application than the one we are waiting on. + // Flag the event, and start pruning the input queue. + ALOGI("Pruning input queue because user touched a different application"); + return true; + } + } + return false; +} + bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { bool needWake = mInboundQueue.empty(); mInboundQueue.push_back(entry); @@ -653,32 +680,18 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { // decides to touch a window in a different application. // If the application takes too long to catch up then we drop all events preceding // the touch into the other window. - MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); - if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN && - (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && - mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY && - mInputTargetWaitApplicationToken != nullptr) { - int32_t displayId = motionEntry->displayId; - int32_t x = - int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); - int32_t y = - int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); - sp<InputWindowHandle> touchedWindowHandle = - findTouchedWindowAtLocked(displayId, x, y); - if (touchedWindowHandle != nullptr && - touchedWindowHandle->getApplicationToken() != - mInputTargetWaitApplicationToken) { - // User touched a different application than the one we are waiting on. - // Flag the event, and start pruning the input queue. - mNextUnblockedEvent = motionEntry; - needWake = true; - } + if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(*entry))) { + mNextUnblockedEvent = entry; + needWake = true; } break; } - case EventEntry::Type::CONFIGURATION_CHANGED: - case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::FOCUS: { + LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked"); + break; + } + case EventEntry::Type::CONFIGURATION_CHANGED: + case EventEntry::Type::DEVICE_RESET: { // nothing to do break; } @@ -980,9 +993,24 @@ bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceReset } void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) { + if (mPendingEvent != nullptr) { + // Move the pending event to the front of the queue. This will give the chance + // for the pending event to get dispatched to the newly focused window + mInboundQueue.push_front(mPendingEvent); + mPendingEvent = nullptr; + } + FocusEntry* focusEntry = new FocusEntry(mIdGenerator.nextId(), now(), window.getToken(), hasFocus); - enqueueInboundEventLocked(focusEntry); + + // This event should go to the front of the queue, but behind all other focus events + // Find the last focus event, and insert right after it + std::deque<EventEntry*>::reverse_iterator it = + std::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(), + [](EventEntry* event) { return event->type == EventEntry::Type::FOCUS; }); + + // Maintain the order of focus events. Insert the entry after all other focus events. + mInboundQueue.insert(it.base(), focusEntry); } void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) { @@ -1267,11 +1295,9 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( } } else { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { - if (DEBUG_FOCUS) { - ALOGD("Waiting for application to become ready for input: %s. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); - } - nsecs_t timeout; + ALOGI("Waiting for application to become ready for input: %s. Reason: %s", + getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); + std::chrono::nanoseconds timeout; if (windowHandle != nullptr) { timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); } else if (applicationHandle != nullptr) { @@ -1283,7 +1309,7 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; mInputTargetWaitStartTime = currentTime; - mInputTargetWaitTimeoutTime = currentTime + timeout; + mInputTargetWaitTimeoutTime = currentTime + timeout.count(); mInputTargetWaitTimeoutExpired = false; mInputTargetWaitApplicationToken.clear(); @@ -1325,10 +1351,10 @@ void InputDispatcher::removeWindowByTokenLocked(const sp<IBinder>& token) { } void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked( - nsecs_t newTimeout, const sp<IBinder>& inputConnectionToken) { - if (newTimeout > 0) { + nsecs_t timeoutExtension, const sp<IBinder>& inputConnectionToken) { + if (timeoutExtension > 0) { // Extend the timeout. - mInputTargetWaitTimeoutTime = now() + newTimeout; + mInputTargetWaitTimeoutTime = now() + timeoutExtension; } else { // Give up. mInputTargetWaitTimeoutExpired = true; @@ -2108,15 +2134,12 @@ std::string InputDispatcher::getApplicationWindowLabel( const sp<InputWindowHandle>& windowHandle) { if (applicationHandle != nullptr) { if (windowHandle != nullptr) { - std::string label(applicationHandle->getName()); - label += " - "; - label += windowHandle->getName(); - return label; + return applicationHandle->getName() + " - " + windowHandle->getName(); } else { return applicationHandle->getName(); } } else if (windowHandle != nullptr) { - return windowHandle->getName(); + return windowHandle->getInfo()->applicationInfo.name + " - " + windowHandle->getName(); } else { return "<unknown application or window>"; } @@ -4023,11 +4046,12 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { const int32_t displayId = it.first; const sp<InputApplicationHandle>& applicationHandle = it.second; dump += StringPrintf(INDENT2 "displayId=%" PRId32 - ", name='%s', dispatchingTimeout=%0.3fms\n", + ", name='%s', dispatchingTimeout=%" PRId64 "ms\n", displayId, applicationHandle->getName().c_str(), - applicationHandle->getDispatchingTimeout( - DEFAULT_INPUT_DISPATCHING_TIMEOUT) / - 1000000.0); + ns2ms(applicationHandle + ->getDispatchingTimeout( + DEFAULT_INPUT_DISPATCHING_TIMEOUT) + .count())); } } else { dump += StringPrintf(INDENT "FocusedApplications: <none>\n"); @@ -4107,9 +4131,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { windowInfo->windowXScale, windowInfo->windowYScale); dumpRegion(dump, windowInfo->touchableRegion); dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures); - dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", + dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64 + "ms\n", windowInfo->ownerPid, windowInfo->ownerUid, - windowInfo->dispatchingTimeout / 1000000.0); + ns2ms(windowInfo->dispatchingTimeout)); } } else { dump += INDENT2 "Windows: <none>\n"; @@ -4142,7 +4167,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (EventEntry* entry : mRecentQueue) { dump += INDENT2; entry->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f); + dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime)); } } else { dump += INDENT "RecentQueue: <empty>\n"; @@ -4153,8 +4178,8 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT "PendingEvent:\n"; dump += INDENT2; mPendingEvent->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", - (currentTime - mPendingEvent->eventTime) * 0.000001f); + dump += StringPrintf(", age=%" PRId64 "ms\n", + ns2ms(currentTime - mPendingEvent->eventTime)); } else { dump += INDENT "PendingEvent: <none>\n"; } @@ -4165,7 +4190,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (EventEntry* entry : mInboundQueue) { dump += INDENT2; entry->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f); + dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime)); } } else { dump += INDENT "InboundQueue: <empty>\n"; @@ -4200,9 +4225,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (DispatchEntry* entry : connection->outboundQueue) { dump.append(INDENT4); entry->eventEntry->appendDescription(dump); - dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n", + dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64 + "ms\n", entry->targetFlags, entry->resolvedAction, - (currentTime - entry->eventEntry->eventTime) * 0.000001f); + ns2ms(currentTime - entry->eventEntry->eventTime)); } } else { dump += INDENT3 "OutboundQueue: <empty>\n"; @@ -4215,10 +4241,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT4; entry->eventEntry->appendDescription(dump); dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, " - "age=%0.1fms, wait=%0.1fms\n", + "age=%" PRId64 "ms, wait=%" PRId64 "ms\n", entry->targetFlags, entry->resolvedAction, - (currentTime - entry->eventEntry->eventTime) * 0.000001f, - (currentTime - entry->deliveryTime) * 0.000001f); + ns2ms(currentTime - entry->eventEntry->eventTime), + ns2ms(currentTime - entry->deliveryTime)); } } else { dump += INDENT3 "WaitQueue: <empty>\n"; @@ -4229,16 +4255,16 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { } if (isAppSwitchPendingLocked()) { - dump += StringPrintf(INDENT "AppSwitch: pending, due in %0.1fms\n", - (mAppSwitchDueTime - now()) / 1000000.0); + dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n", + ns2ms(mAppSwitchDueTime - now())); } else { dump += INDENT "AppSwitch: not pending\n"; } dump += INDENT "Configuration:\n"; - dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f); - dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %0.1fms\n", - mConfig.keyRepeatTimeout * 0.000001f); + dump += StringPrintf(INDENT2 "KeyRepeatDelay: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatDelay)); + dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n", + ns2ms(mConfig.keyRepeatTimeout)); } void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) { @@ -4340,8 +4366,7 @@ status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& i return BAD_VALUE; } - [[maybe_unused]] const bool removed = removeByValue(mConnectionsByFd, connection); - ALOG_ASSERT(removed); + removeConnectionLocked(connection); mInputChannelsByToken.erase(inputChannel->getConnectionToken()); if (connection->monitor) { @@ -4443,7 +4468,7 @@ std::optional<int32_t> InputDispatcher::findGestureMonitorDisplayByTokenLocked( return std::nullopt; } -sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) { +sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) const { if (inputConnectionToken == nullptr) { return nullptr; } @@ -4458,6 +4483,10 @@ sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConn return nullptr; } +void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) { + removeByValue(mConnectionsByFd, connection); +} + void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) { @@ -4562,12 +4591,12 @@ void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr; mLock.unlock(); - nsecs_t newTimeout = + const nsecs_t timeoutExtension = mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason); mLock.lock(); - resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, token); + resumeAfterTargetsNotReadyTimeoutLocked(timeoutExtension, token); } void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( @@ -4620,13 +4649,10 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c } DispatchEntry* dispatchEntry = *dispatchEntryIt; - nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; + const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { - std::string msg = - StringPrintf("Window '%s' spent %0.1fms processing the last input event: ", - connection->getWindowName().c_str(), eventDuration * 0.000001f); - dispatchEntry->eventEntry->appendDescription(msg); - ALOGI("%s", msg.c_str()); + ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(), + ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str()); } reportDispatchStatistics(std::chrono::nanoseconds(eventDuration), *connection, handled); @@ -4644,7 +4670,7 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c } // Dequeue the event and start the next cycle. - // Note that because the lock might have been released, it is possible that the + // Because the lock might have been released, it is possible that the // contents of the wait queue to have been drained, so we need to double-check // a few things. dispatchEntryIt = connection->findWaitQueueEntry(seq); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index d122282fbe..ff7be87609 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -197,6 +197,11 @@ private: // 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); + + 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()); @@ -209,7 +214,6 @@ private: std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock); - sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) REQUIRES(mLock); // Input channels that will receive a copy of all input events sent to the provided display. std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock); @@ -345,6 +349,8 @@ private: bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock); sp<IBinder> mInputTargetWaitApplicationToken GUARDED_BY(mLock); + bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock); + // Contains the last window which received a hover event. sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index f33cc65c2a..13e835427f 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -17,12 +17,14 @@ #include "../dispatcher/InputDispatcher.h" #include <android-base/stringprintf.h> +#include <android-base/thread_annotations.h> #include <binder/Binder.h> #include <input/Input.h> #include <gtest/gtest.h> #include <linux/input.h> #include <cinttypes> +#include <thread> #include <unordered_set> #include <vector> @@ -119,6 +121,33 @@ public: << "Expected onPointerDownOutsideFocus to not have been called"; } + // This function must be called soon after the expected ANR timer starts, + // because we are also checking how much time has passed. + void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout, + const sp<InputApplicationHandle>& expectedApplication, + const sp<IBinder>& expectedToken) { + const std::chrono::time_point start = std::chrono::steady_clock::now(); + std::unique_lock lock(mLock); + std::chrono::duration timeToWait = timeout + 100ms; // provide some slack + android::base::ScopedLockAssertion assumeLocked(mLock); + + // If there is an ANR, Dispatcher won't be idle because there are still events + // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle + // before checking if ANR was called. + // Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide + // it some time to act. 100ms seems reasonable. + mNotifyAnr.wait_for(lock, timeToWait, + [this]() REQUIRES(mLock) { return mNotifyAnrWasCalled; }); + const std::chrono::duration waited = std::chrono::steady_clock::now() - start; + ASSERT_TRUE(mNotifyAnrWasCalled); + // Ensure that the ANR didn't get raised too early. We can't be too strict here because + // the dispatcher started counting before this function was called + ASSERT_TRUE(timeout - 100ms < waited); // check (waited < timeout + 100ms) done by wait_for + mNotifyAnrWasCalled = false; + ASSERT_EQ(expectedApplication, mLastAnrApplication); + ASSERT_EQ(expectedToken, mLastAnrWindowToken); + } + void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) { mConfig.keyRepeatTimeout = timeout; mConfig.keyRepeatDelay = delay; @@ -131,14 +160,26 @@ private: sp<IBinder> mOnPointerDownToken GUARDED_BY(mLock); std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock); + // ANR handling + bool mNotifyAnrWasCalled GUARDED_BY(mLock) = false; + sp<InputApplicationHandle> mLastAnrApplication GUARDED_BY(mLock); + sp<IBinder> mLastAnrWindowToken GUARDED_BY(mLock); + std::condition_variable mNotifyAnr; + std::chrono::nanoseconds mAnrTimeout = 0ms; + virtual void notifyConfigurationChanged(nsecs_t when) override { std::scoped_lock lock(mLock); mConfigurationChangedTime = when; } - virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>&, const sp<IBinder>&, - const std::string&) override { - return 0; + virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& application, + const sp<IBinder>& windowToken, const std::string&) override { + std::scoped_lock lock(mLock); + mLastAnrApplication = application; + mLastAnrWindowToken = windowToken; + mNotifyAnrWasCalled = true; + mNotifyAnr.notify_all(); + return mAnrTimeout.count(); } virtual void notifyInputChannelBroken(const sp<IBinder>&) override {} @@ -309,6 +350,20 @@ protected: mFakePolicy.clear(); mDispatcher.clear(); } + + /** + * Used for debugging when writing the test + */ + void dumpDispatcherState() { + std::string dump; + mDispatcher->dump(dump); + std::stringstream ss(dump); + std::string to; + + while (std::getline(ss, to, '\n')) { + ALOGE("%s", to.c_str()); + } + } }; @@ -502,13 +557,20 @@ static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s; class FakeApplicationHandle : public InputApplicationHandle { public: - FakeApplicationHandle() {} + FakeApplicationHandle() { + mInfo.name = "Fake Application"; + mInfo.token = new BBinder(); + mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); + } virtual ~FakeApplicationHandle() {} virtual bool updateInfo() override { - mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); return true; } + + void setDispatchingTimeout(std::chrono::nanoseconds timeout) { + mInfo.dispatchingTimeout = timeout.count(); + } }; class FakeInputReceiver { @@ -519,6 +581,20 @@ public: } InputEvent* consume() { + InputEvent* event; + std::optional<uint32_t> consumeSeq = receiveEvent(&event); + if (!consumeSeq) { + return nullptr; + } + finishEvent(*consumeSeq); + return event; + } + + /** + * Receive an event without acknowledging it. + * Return the sequence number that could later be used to send finished signal. + */ + std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) { uint32_t consumeSeq; InputEvent* event; @@ -535,23 +611,29 @@ public: if (status == WOULD_BLOCK) { // Just means there's no event available. - return nullptr; + return std::nullopt; } if (status != OK) { ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK."; - return nullptr; + return std::nullopt; } if (event == nullptr) { ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer"; - return nullptr; + return std::nullopt; } - - status = mConsumer->sendFinishedSignal(consumeSeq, true); - if (status != OK) { - ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK."; + if (outEvent != nullptr) { + *outEvent = event; } - return event; + return consumeSeq; + } + + /** + * To be used together with "receiveEvent" to complete the consumption of an event. + */ + void finishEvent(uint32_t consumeSeq) { + const status_t status = mConsumer->sendFinishedSignal(consumeSeq, true); + ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK."; } void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId, @@ -668,6 +750,10 @@ public: void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; } + void setDispatchingTimeout(std::chrono::nanoseconds timeout) { + mInfo.dispatchingTimeout = timeout.count(); + } + void setFrame(const Rect& frame) { mInfo.frameLeft = frame.left; mInfo.frameTop = frame.top; @@ -740,6 +826,19 @@ public: expectedFlags); } + std::optional<uint32_t> receiveEvent() { + if (mInputReceiver == nullptr) { + ADD_FAILURE() << "Invalid receive event on window with no receiver"; + return std::nullopt; + } + return mInputReceiver->receiveEvent(); + } + + void finishEvent(uint32_t sequenceNum) { + ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver"; + mInputReceiver->finishEvent(sequenceNum); + } + InputEvent* consume() { if (mInputReceiver == nullptr) { return nullptr; @@ -765,16 +864,15 @@ private: std::atomic<int32_t> FakeWindowHandle::sId{1}; -static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher, - int32_t displayId = ADISPLAY_ID_NONE) { +static int32_t injectKey(const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount, + int32_t displayId = ADISPLAY_ID_NONE) { KeyEvent event; nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key down event. event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, - INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, - AMETA_NONE, - /* repeatCount */ 0, currentTime, currentTime); + INVALID_HMAC, action, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE, + repeatCount, currentTime, currentTime); // Inject event until dispatch out. return dispatcher->injectInputEvent( @@ -783,10 +881,16 @@ static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher, INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } -static int32_t injectMotionEvent(const sp<InputDispatcher>& dispatcher, int32_t action, - int32_t source, int32_t displayId, int32_t x, int32_t y, - int32_t xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION, - int32_t yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION) { +static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher, + int32_t displayId = ADISPLAY_ID_NONE) { + return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId); +} + +static int32_t injectMotionEvent( + const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId, + const PointF& position, + const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION}) { MotionEvent event; PointerProperties pointerProperties[1]; PointerCoords pointerCoords[1]; @@ -796,8 +900,8 @@ static int32_t injectMotionEvent(const sp<InputDispatcher>& dispatcher, int32_t pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; pointerCoords[0].clear(); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y); nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. @@ -806,7 +910,7 @@ static int32_t injectMotionEvent(const sp<InputDispatcher>& dispatcher, int32_t /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0, - /* xPrecision */ 0, /* yPrecision */ 0, xCursorPosition, yCursorPosition, + /* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); @@ -819,14 +923,12 @@ static int32_t injectMotionEvent(const sp<InputDispatcher>& dispatcher, int32_t static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source, int32_t displayId, const PointF& location = {100, 200}) { - return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location.x, - location.y); + return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location); } static int32_t injectMotionUp(const sp<InputDispatcher>& dispatcher, int32_t source, int32_t displayId, const PointF& location = {100, 200}) { - return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location.x, - location.y); + return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location); } static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { @@ -1051,7 +1153,7 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { // left window. This event should be dispatched to the left window. ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE, - ADISPLAY_ID_DEFAULT, 610, 400, 599, 400)); + ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400})); windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT); windowRight->assertNoEvents(); } @@ -2185,4 +2287,82 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithSc consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); } +class InputDispatcherSingleWindowAnr : public InputDispatcherTest { + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + + mApplication = new FakeApplicationHandle(); + mApplication->setDispatchingTimeout(20ms); + mWindow = + new FakeWindowHandle(mApplication, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + mWindow->setFrame(Rect(0, 0, 30, 30)); + mWindow->setDispatchingTimeout(10ms); + mWindow->setFocus(true); + // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this + // window. + mWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + + // Set focused application. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + mWindow->consumeFocusEvent(true); + } + + virtual void TearDown() override { + InputDispatcherTest::TearDown(); + mWindow.clear(); + } + +protected: + sp<FakeApplicationHandle> mApplication; + sp<FakeWindowHandle> mWindow; + static constexpr PointF WINDOW_LOCATION = {20, 20}; + + void tapOnWindow() { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + } +}; + +// Send an event to the app and have the app not respond right away. +// Make sure that ANR is raised +TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) { + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + + // Also, overwhelm the socket to make sure ANR starts + for (size_t i = 0; i < 100; i++) { + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {WINDOW_LOCATION.x, WINDOW_LOCATION.y + i}); + } + + std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN + ASSERT_TRUE(sequenceNum); + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken()); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + +// Send a key to the app and have the app not respond right away. +TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) { + // Inject a key, and don't respond - expect that ANR is called. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)); + std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); + ASSERT_TRUE(sequenceNum); + + // Start ANR process by sending a 2nd key, which would trigger the check for whether + // waitQueue is empty + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 1); + + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, mWindow->getToken()); + ASSERT_TRUE(mDispatcher->waitForIdle()); +} + } // namespace android::inputdispatcher diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index 45e67f74f2..8a282e2382 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -820,7 +820,7 @@ status_t SensorDevice::updateBatchParamsLocked(int handle, Info &info) { status_t err(NO_ERROR); // If the min period or min timeout has changed since the last batch call, call batch. - if (prevBestBatchParams != info.bestBatchParams) { + if (prevBestBatchParams != info.bestBatchParams && info.numActiveClients() > 0) { ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH 0x%08x %" PRId64 " %" PRId64, handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch); err = checkReturnAndGetStatus(mSensors->batch( @@ -890,14 +890,13 @@ void SensorDevice::setUidStateForConnection(void* ident, SensorService::UidState Info& info = mActivationCount.editValueAt(i); if (info.hasBatchParamsForIdent(ident)) { - if (updateBatchParamsLocked(handle, info) != NO_ERROR) { - bool enable = info.numActiveClients() == 0 && info.isActive; - bool disable = info.numActiveClients() > 0 && !info.isActive; + updateBatchParamsLocked(handle, info); + bool disable = info.numActiveClients() == 0 && info.isActive; + bool enable = info.numActiveClients() > 0 && !info.isActive; - if ((enable || disable) && - doActivateHardwareLocked(handle, enable) == NO_ERROR) { - info.isActive = enable; - } + if ((enable || disable) && + doActivateHardwareLocked(handle, enable) == NO_ERROR) { + info.isActive = enable; } } } diff --git a/services/sensorservice/SensorDeviceUtils.cpp b/services/sensorservice/SensorDeviceUtils.cpp index 0dcf8c0ea2..52213cff7a 100644 --- a/services/sensorservice/SensorDeviceUtils.cpp +++ b/services/sensorservice/SensorDeviceUtils.cpp @@ -40,22 +40,12 @@ void quantizeSensorEventValues(sensors_event_t *event, float resolution) { switch ((SensorTypeV2_1)event->type) { case SensorTypeV2_1::ACCELEROMETER: case SensorTypeV2_1::MAGNETIC_FIELD: - case SensorTypeV2_1::ORIENTATION: case SensorTypeV2_1::GYROSCOPE: - case SensorTypeV2_1::GRAVITY: - case SensorTypeV2_1::LINEAR_ACCELERATION: case SensorTypeV2_1::MAGNETIC_FIELD_UNCALIBRATED: case SensorTypeV2_1::GYROSCOPE_UNCALIBRATED: case SensorTypeV2_1::ACCELEROMETER_UNCALIBRATED: axes = 3; break; - case SensorTypeV2_1::GAME_ROTATION_VECTOR: - axes = 4; - break; - case SensorTypeV2_1::ROTATION_VECTOR: - case SensorTypeV2_1::GEOMAGNETIC_ROTATION_VECTOR: - axes = 5; - break; case SensorTypeV2_1::DEVICE_ORIENTATION: case SensorTypeV2_1::LIGHT: case SensorTypeV2_1::PRESSURE: @@ -77,11 +67,8 @@ void quantizeSensorEventValues(sensors_event_t *event, float resolution) { case SensorTypeV2_1::HINGE_ANGLE: axes = 1; break; - case SensorTypeV2_1::POSE_6DOF: - axes = 15; - break; default: - // No other sensors have data that needs to be rounded. + // No other sensors have data that needs to be quantized. break; } diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index e5b94e437f..07be7916ee 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -31,6 +31,7 @@ #include "SurfaceInterceptor.h" #include "FrameTracer/FrameTracer.h" +#include "Scheduler/LayerHistory.h" #include "TimeStats/TimeStats.h" namespace android { @@ -399,7 +400,8 @@ void BufferQueueLayer::onFrameAvailable(const BufferItem& item) { // Add this buffer from our internal queue tracker { // Autolock scope const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp; - mFlinger->mScheduler->recordLayerHistory(this, presentTime); + mFlinger->mScheduler->recordLayerHistory(this, presentTime, + LayerHistory::LayerUpdateType::Buffer); Mutex::Autolock lock(mQueueItemLock); // Reset the frame number tracker when we receive the first buffer after diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index a1ed6d7116..4f8fc41469 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -283,7 +283,8 @@ bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime; mCurrentState.desiredPresentTime = desiredPresentTime; - mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime); + mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime, + LayerHistory::LayerUpdateType::Buffer); addFrameEvent(acquireFence, postTime, desiredPresentTime); return true; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 5b9dbf2922..dcc213f943 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -119,6 +119,13 @@ Layer::Layer(const LayerCreationArgs& args) mCurrentState.treeHasFrameRateVote = false; mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID; + if (args.flags & ISurfaceComposerClient::eNoColorFill) { + // Set an invalid color so there is no color fill. + mCurrentState.color.r = -1.0_hf; + mCurrentState.color.g = -1.0_hf; + mCurrentState.color.b = -1.0_hf; + } + // drawing state & current state are identical mDrawingState = mCurrentState; @@ -848,11 +855,14 @@ bool Layer::applyPendingStates(State* stateToCommit) { } } - // If we still have pending updates, wake SurfaceFlinger back up and point - // it at this layer so we can process them + // If we still have pending updates, we need to ensure SurfaceFlinger + // will keep calling doTransaction, and so we set the transaction flags. + // However, our pending states won't clear until a frame is available, + // and so there is no need to specifically trigger a wakeup. Rather + // we set the flags and wait for something else to wake us up. if (!mPendingStates.empty()) { setTransactionFlags(eTransactionNeeded); - mFlinger->setTransactionFlags(eTraversalNeeded); + mFlinger->setTransactionFlagsNoWake(eTraversalNeeded); } mCurrentState.modified = false; @@ -1392,7 +1402,8 @@ bool Layer::setFrameRate(FrameRate frameRate) { } // Activate the layer in Scheduler's LayerHistory - mFlinger->mScheduler->recordLayerHistory(this, systemTime()); + mFlinger->mScheduler->recordLayerHistory(this, systemTime(), + LayerHistory::LayerUpdateType::SetFrameRate); mCurrentState.sequence++; mCurrentState.frameRate = frameRate; @@ -2111,7 +2122,9 @@ Layer::RoundedCornerState Layer::getRoundedCornerState() const { // but a transform matrix can define horizontal and vertical scales. // Let's take the average between both of them and pass into the shader, practically we // never do this type of transformation on windows anyway. - parentState.radius *= (t[0][0] + t[1][1]) / 2.0f; + auto scaleX = sqrtf(t[0][0] * t[0][0] + t[0][1] * t[0][1]); + auto scaleY = sqrtf(t[1][0] * t[1][0] + t[1][1] * t[1][1]); + parentState.radius *= (scaleX + scaleY) / 2.0f; return parentState; } } diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index 8958d9ae19..292510978d 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -79,7 +79,8 @@ void LayerHistory::registerLayer(Layer* layer, float lowRefreshRate, float highR mLayerInfos.emplace_back(layer, std::move(info)); } -void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now) { +void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now, + LayerUpdateType /*updateType*/) { std::lock_guard lock(mLock); const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(), diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h index d5bebf6d2b..acd76b0558 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.h +++ b/services/surfaceflinger/Scheduler/LayerHistory.h @@ -52,10 +52,18 @@ public: // Sets the display size. Client is responsible for synchronization. virtual void setDisplayArea(uint32_t displayArea) = 0; + // Sets whether a config change is pending to be applied virtual void setConfigChangePending(bool pending) = 0; + // Represents which layer activity is recorded + enum class LayerUpdateType { + Buffer, // a new buffer queued + AnimationTX, // a new transaction with eAnimation flag set + SetFrameRate, // setFrameRate API was called + }; + // Marks the layer as active, and records the given state to its history. - virtual void record(Layer*, nsecs_t presentTime, nsecs_t now) = 0; + virtual void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) = 0; using Summary = std::vector<RefreshRateConfigs::LayerRequirement>; @@ -83,7 +91,7 @@ public: void setConfigChangePending(bool /*pending*/) override {} // Marks the layer as active, and records the given state to its history. - void record(Layer*, nsecs_t presentTime, nsecs_t now) override; + void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override; // Rebuilds sets of active/inactive layers, and accumulates stats for active layers. android::scheduler::LayerHistory::Summary summarize(nsecs_t now) override; @@ -141,7 +149,7 @@ public: void setConfigChangePending(bool pending) override { mConfigChangePending = pending; } // Marks the layer as active, and records the given state to its history. - void record(Layer*, nsecs_t presentTime, nsecs_t now) override; + void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override; // Rebuilds sets of active/inactive layers, and accumulates stats for active layers. android::scheduler::LayerHistory::Summary summarize(nsecs_t /*now*/) override; diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp index afc8c4fcb2..316000c4d9 100644 --- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp @@ -62,13 +62,17 @@ void trace(const wp<Layer>& weak, LayerHistory::LayerVoteType type, int fps) { const auto layer = weak.promote(); if (!layer) return; - const auto& name = layer->getName(); - const auto noVoteTag = "LFPS NoVote " + name; - const auto heuristicVoteTag = "LFPS Heuristic " + name; - const auto explicitDefaultVoteTag = "LFPS ExplicitDefault" + name; - const auto explicitExactOrMultipleVoteTag = "LFPS ExplicitExactOrMultiple" + name; - const auto minVoteTag = "LFPS Min " + name; - const auto maxVoteTag = "LFPS Max " + name; + const auto makeTag = [layer](LayerHistory::LayerVoteType vote) { + return "LFPS " + RefreshRateConfigs::layerVoteTypeString(vote) + " " + layer->getName(); + }; + + const auto noVoteTag = makeTag(LayerHistory::LayerVoteType::NoVote); + const auto heuristicVoteTag = makeTag(LayerHistory::LayerVoteType::Heuristic); + const auto explicitDefaultVoteTag = makeTag(LayerHistory::LayerVoteType::ExplicitDefault); + const auto explicitExactOrMultipleVoteTag = + makeTag(LayerHistory::LayerVoteType::ExplicitExactOrMultiple); + const auto minVoteTag = makeTag(LayerHistory::LayerVoteType::Min); + const auto maxVoteTag = makeTag(LayerHistory::LayerVoteType::Max); ATRACE_INT(noVoteTag.c_str(), type == LayerHistory::LayerVoteType::NoVote ? 1 : 0); ATRACE_INT(heuristicVoteTag.c_str(), type == LayerHistory::LayerVoteType::Heuristic ? fps : 0); @@ -79,7 +83,7 @@ void trace(const wp<Layer>& weak, LayerHistory::LayerVoteType type, int fps) { ATRACE_INT(minVoteTag.c_str(), type == LayerHistory::LayerVoteType::Min ? 1 : 0); ATRACE_INT(maxVoteTag.c_str(), type == LayerHistory::LayerVoteType::Max ? 1 : 0); - ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps); + ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps); } } // namespace @@ -95,7 +99,8 @@ void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float mLayerInfos.emplace_back(layer, std::move(info)); } -void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now) { +void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now, + LayerUpdateType updateType) { std::lock_guard lock(mLock); const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(), @@ -103,7 +108,7 @@ void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now) { LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer); const auto& info = it->second; - info->setLastPresentTime(presentTime, now, mConfigChangePending); + info->setLastPresentTime(presentTime, now, updateType, mConfigChangePending); // Activate layer if inactive. if (const auto end = activeLayers().end(); it >= end) { @@ -188,7 +193,7 @@ void LayerHistoryV2::partitionLayers(nsecs_t now) { trace(weak, LayerHistory::LayerVoteType::NoVote, 0); } - info->clearHistory(); + info->onLayerInactive(now); std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]); } @@ -209,7 +214,7 @@ void LayerHistoryV2::clear() { std::lock_guard lock(mLock); for (const auto& [layer, info] : activeLayers()) { - info->clearHistory(); + info->clearHistory(systemTime()); } } } // namespace android::scheduler::impl diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp index 78f4433185..82da00785a 100644 --- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp @@ -35,43 +35,60 @@ LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod, mLayerVote({defaultVote, 0.0f}) {} void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, - bool pendingConfigChange) { + LayerUpdateType updateType, bool pendingConfigChange) { lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0)); mLastUpdatedTime = std::max(lastPresentTime, now); - - FrameTimeData frameTime = {.presetTime = lastPresentTime, - .queueTime = mLastUpdatedTime, - .pendingConfigChange = pendingConfigChange}; - - mFrameTimes.push_back(frameTime); - if (mFrameTimes.size() > HISTORY_SIZE) { - mFrameTimes.pop_front(); + switch (updateType) { + case LayerUpdateType::AnimationTX: + mLastAnimationTime = std::max(lastPresentTime, now); + break; + case LayerUpdateType::SetFrameRate: + case LayerUpdateType::Buffer: + FrameTimeData frameTime = {.presetTime = lastPresentTime, + .queueTime = mLastUpdatedTime, + .pendingConfigChange = pendingConfigChange}; + mFrameTimes.push_back(frameTime); + if (mFrameTimes.size() > HISTORY_SIZE) { + mFrameTimes.pop_front(); + } + break; } } -bool LayerInfoV2::isFrequent(nsecs_t now) { - mLastReportedIsFrequent = [&] { - for (auto it = mFrameTimes.crbegin(); it != mFrameTimes.crend(); ++it) { - if (now - it->queueTime >= MAX_FREQUENT_LAYER_PERIOD_NS.count()) { - ALOGV("%s infrequent (last frame is %.2fms ago)", mName.c_str(), - (now - mFrameTimes.back().queueTime) / 1e6f); - return false; - } +bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const { + return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>( + mFrameTimeValidSince.time_since_epoch()) + .count(); +} - const auto numFrames = std::distance(mFrameTimes.crbegin(), it + 1); - if (numFrames >= FREQUENT_LAYER_WINDOW_SIZE) { - ALOGV("%s frequent (burst of %zu frames)", mName.c_str(), numFrames); - return true; - } +bool LayerInfoV2::isFrequent(nsecs_t now) const { + // If we know nothing about this layer we consider it as frequent as it might be the start + // of an animation. + if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) { + return true; + } + + // Find the first active frame + auto it = mFrameTimes.begin(); + for (; it != mFrameTimes.end(); ++it) { + if (it->queueTime >= getActiveLayerThreshold(now)) { + break; } + } - ALOGV("%s %sfrequent (not enough frames %zu)", mName.c_str(), - mLastReportedIsFrequent ? "" : "in", mFrameTimes.size()); - return mLastReportedIsFrequent; - }(); + const auto numFrames = std::distance(it, mFrameTimes.end()); + if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) { + return false; + } - return mLastReportedIsFrequent; + // Layer is considered frequent if the average frame rate is higher than the threshold + const auto totalTime = mFrameTimes.back().queueTime - it->queueTime; + return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER; +} + +bool LayerInfoV2::isAnimating(nsecs_t now) const { + return mLastAnimationTime >= getActiveLayerThreshold(now); } bool LayerInfoV2::hasEnoughDataForHeuristic() const { @@ -80,6 +97,10 @@ bool LayerInfoV2::hasEnoughDataForHeuristic() const { return false; } + if (!isFrameTimeValid(mFrameTimes.front())) { + return false; + } + if (mFrameTimes.size() < HISTORY_SIZE && mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) { return false; @@ -179,6 +200,11 @@ std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_ return {mLayerVote.type, mLayerVote.fps}; } + if (isAnimating(now)) { + ALOGV("%s is animating", mName.c_str()); + return {LayerHistory::LayerVoteType::Max, 0}; + } + if (!isFrequent(now)) { ALOGV("%s is infrequent", mName.c_str()); return {LayerHistory::LayerVoteType::Min, 0}; @@ -190,7 +216,7 @@ std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_ return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()}; } - ALOGV("%s Max (can't resolve refresh rate", mName.c_str()); + ALOGV("%s Max (can't resolve refresh rate)", mName.c_str()); return {LayerHistory::LayerVoteType::Max, 0}; } diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h index 82da7e3dd7..9e6783f854 100644 --- a/services/surfaceflinger/Scheduler/LayerInfoV2.h +++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h @@ -43,11 +43,15 @@ constexpr nsecs_t getActiveLayerThreshold(nsecs_t now) { // Stores history of present times and refresh rates for a layer. class LayerInfoV2 { + using LayerUpdateType = LayerHistory::LayerUpdateType; + // Layer is considered frequent if the earliest value in the window of most recent present times // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in // favor of a low refresh rate. static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3; - static constexpr std::chrono::nanoseconds MAX_FREQUENT_LAYER_PERIOD_NS = 150ms; + static constexpr float MIN_FPS_FOR_FREQUENT_LAYER = 10.0f; + static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = + std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / MIN_FPS_FOR_FREQUENT_LAYER)) + 1ms; friend class LayerHistoryTestV2; @@ -61,7 +65,8 @@ public: // Records the last requested present time. It also stores information about when // the layer was last updated. If the present time is farther in the future than the // updated time, the updated time is the present time. - void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, bool pendingConfigChange); + void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType, + bool pendingConfigChange); // Sets an explicit layer vote. This usually comes directly from the application via // ANativeWindow_setFrameRate API @@ -81,11 +86,21 @@ public: // updated time, the updated time is the present time. nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; } - void clearHistory() { - mFrameTimes.clear(); + void onLayerInactive(nsecs_t now) { + // Mark mFrameTimeValidSince to now to ignore all previous frame times. + // We are not deleting the old frame to keep track of whether we should treat the first + // buffer as Max as we don't know anything about this layer or Min as this layer is + // posting infrequent updates. + const auto timePoint = std::chrono::nanoseconds(now); + mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint); mLastReportedRefreshRate = 0.0f; } + void clearHistory(nsecs_t now) { + onLayerInactive(now); + mFrameTimes.clear(); + } + private: // Used to store the layer timestamps struct FrameTimeData { @@ -94,11 +109,13 @@ private: bool pendingConfigChange; }; - bool isFrequent(nsecs_t now); + bool isFrequent(nsecs_t now) const; + bool isAnimating(nsecs_t now) const; bool hasEnoughDataForHeuristic() const; std::optional<float> calculateRefreshRateIfPossible(); std::pair<nsecs_t, bool> calculateAverageFrameTime() const; bool isRefreshRateStable(nsecs_t averageFrameTime, bool missingPresentTime) const; + bool isFrameTimeValid(const FrameTimeData&) const; const std::string mName; @@ -108,14 +125,9 @@ private: nsecs_t mLastUpdatedTime = 0; - float mLastReportedRefreshRate = 0.0f; + nsecs_t mLastAnimationTime = 0; - // Used to determine whether a layer should be considered frequent or - // not when we don't have enough frames. This member will not be cleared - // as part of clearHistory() to remember whether this layer was frequent - // or not before we processed touch boost (or anything else that would - // clear layer history). - bool mLastReportedIsFrequent = true; + float mLastReportedRefreshRate = 0.0f; // Holds information about the layer vote struct { @@ -124,6 +136,8 @@ private: } mLayerVote; std::deque<FrameTimeData> mFrameTimes; + std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince = + std::chrono::steady_clock::now(); static constexpr size_t HISTORY_SIZE = 90; static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s; }; diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index e181c1253c..3c8bd68145 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -31,6 +31,23 @@ namespace android::scheduler { using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType; using RefreshRate = RefreshRateConfigs::RefreshRate; +std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) { + switch (vote) { + case LayerVoteType::NoVote: + return "NoVote"; + case LayerVoteType::Min: + return "Min"; + case LayerVoteType::Max: + return "Max"; + case LayerVoteType::Heuristic: + return "Heuristic"; + case LayerVoteType::ExplicitDefault: + return "ExplicitDefault"; + case LayerVoteType::ExplicitExactOrMultiple: + return "ExplicitExactOrMultiple"; + } +} + const RefreshRate& RefreshRateConfigs::getRefreshRateForContent( const std::vector<LayerRequirement>& layers) const { std::lock_guard lock(mLock); @@ -146,6 +163,7 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( const bool primaryRangeIsSingleRate = policy->primaryRange.min == policy->primaryRange.max; if (!touchActive && idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) { + ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str()); return getMinRefreshRateByPolicyLocked(); } @@ -168,7 +186,8 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( } for (const auto& layer : layers) { - ALOGV("Calculating score for %s (type: %d)", layer.name.c_str(), layer.vote); + ALOGV("Calculating score for %s (%s, weight %.2f)", layer.name.c_str(), + layerVoteTypeString(layer.vote).c_str(), layer.weight); if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) { continue; } @@ -254,10 +273,8 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( return 1.0f / iter; }(); ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), - layer.vote == LayerVoteType::ExplicitExactOrMultiple - ? "ExplicitExactOrMultiple" - : "Heuristic", - weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), layerScore); + layerVoteTypeString(layer.vote).c_str(), weight, 1e9f / layerPeriod, + scores[i].first->name.c_str(), layerScore); scores[i].second += weight * layerScore; continue; } @@ -276,6 +293,8 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( // range instead of picking a random score from the app range. if (std::all_of(scores.begin(), scores.end(), [](std::pair<const RefreshRate*, float> p) { return p.second == 0; })) { + ALOGV("layers not scored - choose %s", + getMaxRefreshRateByPolicyLocked().getName().c_str()); return getMaxRefreshRateByPolicyLocked(); } else { return *bestRefreshRate; diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index 2657dee538..ff1eabdfc8 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -255,6 +255,9 @@ public: // Stores the current configId the device operates at void setCurrentConfigId(HwcConfigIndexType configId) EXCLUDES(mLock); + // Returns a string that represents the layer vote type + static std::string layerVoteTypeString(LayerVoteType vote); + RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs, HwcConfigIndexType currentConfigId); diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 7c2af23454..e250bbf064 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -62,7 +62,7 @@ namespace android { -std::unique_ptr<DispSync> createDispSync() { +std::unique_ptr<DispSync> createDispSync(bool supportKernelTimer) { // TODO (140302863) remove this and use the vsync_reactor system. if (property_get_bool("debug.sf.vsync_reactor", true)) { // TODO (144707443) tune Predictor tunables. @@ -90,7 +90,7 @@ std::unique_ptr<DispSync> createDispSync() { static constexpr size_t pendingFenceLimit = 20; return std::make_unique<scheduler::VSyncReactor>(std::make_unique<scheduler::SystemClock>(), std::move(dispatch), std::move(tracker), - pendingFenceLimit); + pendingFenceLimit, supportKernelTimer); } else { return std::make_unique<impl::DispSync>("SchedulerDispSync", sysprop::running_without_sync_framework(true)); @@ -101,9 +101,9 @@ Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function, const scheduler::RefreshRateConfigs& refreshRateConfig, ISchedulerCallback& schedulerCallback, bool useContentDetectionV2, bool useContentDetection) - : mPrimaryDispSync(createDispSync()), + : mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)), + mPrimaryDispSync(createDispSync(mSupportKernelTimer)), mEventControlThread(new impl::EventControlThread(std::move(function))), - mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)), mSchedulerCallback(schedulerCallback), mRefreshRateConfigs(refreshRateConfig), mUseContentDetection(useContentDetection), @@ -151,9 +151,9 @@ Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync, const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& schedulerCallback, bool useContentDetectionV2, bool useContentDetection) - : mPrimaryDispSync(std::move(primaryDispSync)), + : mSupportKernelTimer(false), + mPrimaryDispSync(std::move(primaryDispSync)), mEventControlThread(std::move(eventControlThread)), - mSupportKernelTimer(false), mSchedulerCallback(schedulerCallback), mRefreshRateConfigs(configs), mUseContentDetection(useContentDetection), @@ -417,9 +417,10 @@ void Scheduler::registerLayer(Layer* layer) { } } -void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime) { +void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, + LayerHistory::LayerUpdateType updateType) { if (mLayerHistory) { - mLayerHistory->record(layer, presentTime, systemTime()); + mLayerHistory->record(layer, presentTime, systemTime(), updateType); } } diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 066e9ca3f8..ebee9e3a8f 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -39,6 +39,7 @@ namespace android { using namespace std::chrono_literals; +using scheduler::LayerHistory; class DispSync; class FenceTime; @@ -118,7 +119,7 @@ public: // Layers are registered on creation, and unregistered when the weak reference expires. void registerLayer(Layer*); - void recordLayerHistory(Layer*, nsecs_t presentTime); + void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType); void setConfigChangePending(bool pending); // Detects content using layer history, and selects a matching refresh rate. @@ -207,14 +208,14 @@ private: std::atomic<nsecs_t> mLastResyncTime = 0; + // Whether to use idle timer callbacks that support the kernel timer. + const bool mSupportKernelTimer; + std::unique_ptr<DispSync> mPrimaryDispSync; std::unique_ptr<EventControlThread> mEventControlThread; // Used to choose refresh rate if content detection is enabled. - std::unique_ptr<scheduler::LayerHistory> mLayerHistory; - - // Whether to use idle timer callbacks that support the kernel timer. - const bool mSupportKernelTimer; + std::unique_ptr<LayerHistory> mLayerHistory; // Timer that records time between requests for next vsync. std::optional<scheduler::OneShotTimer> mIdleTimer; @@ -236,7 +237,7 @@ private: TimerState displayPowerTimer = TimerState::Expired; std::optional<HwcConfigIndexType> configId; - scheduler::LayerHistory::Summary contentRequirements; + LayerHistory::Summary contentRequirements; bool isDisplayPowerStateNormal = true; } mFeatures GUARDED_BY(mFeatureStateLock); diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index 5f0c9ce40c..16d102cac1 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -56,14 +56,16 @@ private: }; VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch, - std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit) + std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit, + bool supportKernelIdleTimer) : mClock(std::move(clock)), mTracker(std::move(tracker)), mDispatch(std::move(dispatch)), mPendingLimit(pendingFenceLimit), mPredictedVsyncTracer(property_get_bool("debug.sf.show_predicted_vsync", false) ? std::make_unique<PredictedVsyncTracer>(*mDispatch) - : nullptr) {} + : nullptr), + mSupportKernelIdleTimer(supportKernelIdleTimer) {} VSyncReactor::~VSyncReactor() = default; @@ -249,7 +251,8 @@ void VSyncReactor::setPeriod(nsecs_t period) { ATRACE_INT64("VSR-setPeriod", period); std::lock_guard lk(mMutex); mLastHwVsync.reset(); - if (period == getPeriod()) { + + if (!mSupportKernelIdleTimer && period == getPeriod()) { endPeriodTransition(); } else { startPeriodTransition(period); @@ -275,6 +278,11 @@ bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_ return false; } + if (mSupportKernelIdleTimer) { + // Clear out the Composer-provided period and use the allowance logic below + HwcVsyncPeriod = {}; + } + auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod(); static constexpr int allowancePercent = 10; static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio; diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h index 31ddf5a186..265d89c4b4 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.h +++ b/services/surfaceflinger/Scheduler/VSyncReactor.h @@ -36,7 +36,8 @@ class PredictedVsyncTracer; class VSyncReactor : public android::DispSync { public: VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch, - std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit); + std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit, + bool supportKernelIdleTimer); ~VSyncReactor(); bool addPresentFence(const std::shared_ptr<FenceTime>& fence) final; @@ -89,6 +90,7 @@ private: GUARDED_BY(mMutex); const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer; + const bool mSupportKernelIdleTimer = false; }; class SystemClock : public Clock { diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 2d25319e2a..2e2273594e 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -115,6 +115,7 @@ #include "Scheduler/DispSyncSource.h" #include "Scheduler/EventControlThread.h" #include "Scheduler/EventThread.h" +#include "Scheduler/LayerHistory.h" #include "Scheduler/MessageQueue.h" #include "Scheduler/PhaseOffsets.h" #include "Scheduler/Scheduler.h" @@ -3242,6 +3243,10 @@ uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, return old; } +uint32_t SurfaceFlinger::setTransactionFlagsNoWake(uint32_t flags) { + return mTransactionFlags.fetch_or(flags); +} + bool SurfaceFlinger::flushTransactionQueues() { // to prevent onHandleDestroyed from being called while the lock is held, // we must keep a copy of the transactions (specifically the composer @@ -3401,6 +3406,12 @@ void SurfaceFlinger::applyTransactionState( for (const ComposerState& state : states) { clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged, listenerCallbacksWithSurfaces); + if ((flags & eAnimation) && state.state.surface) { + if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) { + mScheduler->recordLayerHistory(layer.get(), desiredPresentTime, + LayerHistory::LayerUpdateType::AnimationTX); + } + } } for (const auto& listenerCallback : listenerCallbacksWithSurfaces) { @@ -3685,7 +3696,7 @@ uint32_t SurfaceFlinger::setClientStateLocked( if (layer->setCornerRadius(s.cornerRadius)) flags |= eTraversalNeeded; } - if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs) { + if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs && mSupportsBlur) { if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded; } if (what & layer_state_t::eLayerStackChanged) { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 4cfbdddc31..113b03557f 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -628,6 +628,12 @@ private: uint32_t peekTransactionFlags(); // Can only be called from the main thread or with mStateLock held uint32_t setTransactionFlags(uint32_t flags); + // Set the transaction flags, but don't trigger a wakeup! We use this cases where + // there are still pending transactions but we know they won't be ready until a frame + // arrives from a different layer. So we need to ensure we performTransaction from invalidate + // but there is no need to try and wake up immediately to do it. Rather we rely on + // onFrameAvailable to wake us up. + uint32_t setTransactionFlagsNoWake(uint32_t flags); uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart); void commitTransaction() REQUIRES(mStateLock); void commitOffscreenLayers(); diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp index 94ebe8949b..fe2af80c98 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -22,6 +22,7 @@ cc_test { "Credentials_test.cpp", "DereferenceSurfaceControl_test.cpp", "DisplayConfigs_test.cpp", + "EffectLayer_test.cpp", "InvalidHandles_test.cpp", "LayerCallback_test.cpp", "LayerRenderTypeTransaction_test.cpp", diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp new file mode 100644 index 0000000000..3dca3916e4 --- /dev/null +++ b/services/surfaceflinger/tests/EffectLayer_test.cpp @@ -0,0 +1,117 @@ +/* + * 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. + */ + +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" + +#include "LayerTransactionTest.h" + +namespace android { + +class EffectLayerTest : public LayerTransactionTest { +protected: + virtual void SetUp() { + LayerTransactionTest::SetUp(); + ASSERT_EQ(NO_ERROR, mClient->initCheck()); + + const auto display = SurfaceComposerClient::getInternalDisplayToken(); + ASSERT_FALSE(display == nullptr); + + mParentLayer = createColorLayer("Parent layer", Color::RED); + asTransaction([&](Transaction& t) { + t.setDisplayLayerStack(display, 0); + t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer); + t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque); + }); + } + + virtual void TearDown() { + LayerTransactionTest::TearDown(); + mParentLayer = 0; + } + + sp<SurfaceControl> mParentLayer; +}; + +TEST_F(EffectLayerTest, DefaultEffectLayerHasSolidBlackFill) { + sp<SurfaceControl> effectLayer = + mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */, + PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect, + mParentLayer.get()); + + EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl"; + asTransaction([&](Transaction& t) { + t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400)); + t.show(effectLayer); + }); + + { + SCOPED_TRACE("Default effect Layer has solid black fill"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 400, 400), Color::BLACK); + } +} + +TEST_F(EffectLayerTest, EffectLayerWithNoFill) { + sp<SurfaceControl> effectLayer = + mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */, + PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceEffect | + ISurfaceComposerClient::eNoColorFill, + mParentLayer.get()); + + EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl"; + asTransaction([&](Transaction& t) { + t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400)); + t.show(effectLayer); + }); + + { + SCOPED_TRACE("Effect layer with nofill option is transparent"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 400, 400), Color::RED); + } +} + +TEST_F(EffectLayerTest, EffectLayerCanSetColor) { + sp<SurfaceControl> effectLayer = + mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */, + PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceEffect | + ISurfaceComposerClient::eNoColorFill, + mParentLayer.get()); + + EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl"; + asTransaction([&](Transaction& t) { + t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400)); + t.setColor(effectLayer, + half3{Color::GREEN.r / 255.0f, Color::GREEN.g / 255.0f, + Color::GREEN.b / 255.0f}); + t.show(effectLayer); + }); + + { + SCOPED_TRACE("Effect Layer can set color"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 400, 400), Color::GREEN); + } +} + +} // namespace android + +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp index d666d7e01a..7d4314f4df 100644 --- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp @@ -220,6 +220,51 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadius) { shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK); shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK); shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::BLACK); + // Solid center + shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2, + size / 2 + testArea / 2, size / 2 + testArea / 2), + Color::RED); + } +} + +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusRotated) { + sp<SurfaceControl> parent; + sp<SurfaceControl> child; + const uint8_t size = 64; + const uint8_t testArea = 4; + const float cornerRadius = 20.0f; + ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", size, size)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, size, size)); + ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size)); + + auto transaction = Transaction() + .setCornerRadius(parent, cornerRadius) + .setCrop_legacy(parent, Rect(0, 0, size, size)) + .reparent(child, parent->getHandle()) + .setPosition(child, 0, size) + // Rotate by half PI + .setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f); + if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) { + transaction.setCrop_legacy(parent, Rect(0, 0, size, size)); + } else { + transaction.setFrame(parent, Rect(0, 0, size, size)); + } + transaction.apply(); + + { + const uint8_t bottom = size - 1; + const uint8_t right = size - 1; + auto shot = getScreenCapture(); + // Edges are transparent + shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK); + shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK); + shot->expectColor(Rect(0, bottom - testArea, testArea, bottom - testArea), Color::BLACK); + shot->expectColor(Rect(right - testArea, bottom - testArea, right, bottom), Color::BLACK); + // Solid center + shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2, + size / 2 + testArea / 2, size / 2 + testArea / 2), + Color::GREEN); } } diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index 71d17a9f80..cae317bd5d 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -96,14 +96,14 @@ TEST_F(LayerHistoryTest, oneLayer) { // no layers are returned if active layers have insufficient history. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) { - history().record(layer.get(), 0, mTime); + history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer); ASSERT_TRUE(history().summarize(mTime).empty()); EXPECT_EQ(1, activeLayerCount()); } // High FPS is returned once enough history has been recorded. for (int i = 0; i < 10; i++) { - history().record(layer.get(), 0, mTime); + history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, history().summarize(mTime).size()); EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime)[0].desiredRefreshRate); EXPECT_EQ(1, activeLayerCount()); @@ -121,7 +121,7 @@ TEST_F(LayerHistoryTest, explicitTimestamp) { nsecs_t time = mTime; for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer.get(), time, time); + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += LO_FPS_PERIOD; } @@ -155,7 +155,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { // layer1 is active but infrequent. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer1.get(), time, time); + history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); } @@ -166,12 +166,12 @@ TEST_F(LayerHistoryTest, multipleLayers) { // layer2 is frequent and has high refresh rate. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer2.get(), time, time); + history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; } // layer1 is still active but infrequent. - history().record(layer1.get(), time, time); + history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(2, history().summarize(time).size()); EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate); @@ -182,7 +182,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { // layer1 is no longer active. // layer2 is frequent and has low refresh rate. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer2.get(), time, time); + history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += LO_FPS_PERIOD; } @@ -196,10 +196,10 @@ TEST_F(LayerHistoryTest, multipleLayers) { constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD; for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) { if (i % RATIO == 0) { - history().record(layer2.get(), time, time); + history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); } - history().record(layer3.get(), time, time); + history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; } @@ -209,7 +209,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { EXPECT_EQ(2, frequentLayerCount(time)); // layer3 becomes recently active. - history().record(layer3.get(), time, time); + history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(2, history().summarize(time).size()); EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate); EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate); @@ -228,7 +228,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { // layer2 still has low refresh rate. // layer3 becomes inactive. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer2.get(), time, time); + history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += LO_FPS_PERIOD; } @@ -246,7 +246,7 @@ TEST_F(LayerHistoryTest, multipleLayers) { // layer3 becomes active and has high refresh rate. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer3.get(), time, time); + history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; } diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp index 09ef06a3e3..f376b4a3c1 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp @@ -17,6 +17,7 @@ #undef LOG_TAG #define LOG_TAG "LayerHistoryTestV2" +#include <Layer.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <log/log.h> @@ -37,6 +38,7 @@ protected: static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE; static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS; static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE; + static constexpr auto PRESENT_TIME_HISTORY_TIME = LayerInfoV2::HISTORY_TIME; static constexpr float LO_FPS = 30.f; static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS); @@ -59,6 +61,13 @@ protected: [now](const auto& pair) { return pair.second->isFrequent(now); }); } + auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS { + const auto& infos = history().mLayerInfos; + return std::count_if(infos.begin(), + infos.begin() + static_cast<long>(history().mActiveLayersEnd), + [now](const auto& pair) { return pair.second->isAnimating(now); }); + } + void setLayerInfoVote(Layer* layer, LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS { for (auto& [weak, info] : history().mLayerInfos) { @@ -71,6 +80,9 @@ protected: } auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); } + auto createLayer(std::string name) { + return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name))); + } Hwc2::mock::Display mDisplay; RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0) @@ -103,22 +115,20 @@ TEST_F(LayerHistoryTestV2, oneLayer) { EXPECT_TRUE(history().summarize(time).empty()); EXPECT_EQ(0, activeLayerCount()); - // The first few updates are considered frequent + // Max returned if active layers have insufficient history. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) { - history().record(layer.get(), 0, time); + history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, history().summarize(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); } // Max is returned since we have enough history but there is no timestamp votes. for (int i = 0; i < 10; i++) { - history().record(layer.get(), 0, time); + history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, history().summarize(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(1, frequentLayerCount(time)); } } @@ -132,10 +142,10 @@ TEST_F(LayerHistoryTestV2, oneInvisibleLayer) { nsecs_t time = systemTime(); - history().record(layer.get(), 0, time); + history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer); auto summary = history().summarize(time); ASSERT_EQ(1, history().summarize(time).size()); - // Layer is still considered active so we expect to get Max + // Layer is still considered inactive so we expect to get Min EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); @@ -156,7 +166,7 @@ TEST_F(LayerHistoryTestV2, explicitTimestamp) { nsecs_t time = systemTime(); for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer.get(), time, time); + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += LO_FPS_PERIOD; } @@ -179,7 +189,7 @@ TEST_F(LayerHistoryTestV2, oneLayerNoVote) { nsecs_t time = systemTime(); for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer.get(), time, time); + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; } @@ -206,7 +216,7 @@ TEST_F(LayerHistoryTestV2, oneLayerMinVote) { nsecs_t time = systemTime(); for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer.get(), time, time); + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; } @@ -234,7 +244,7 @@ TEST_F(LayerHistoryTestV2, oneLayerMaxVote) { nsecs_t time = systemTime(); for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer.get(), time, time); + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += LO_FPS_PERIOD; } @@ -262,7 +272,7 @@ TEST_F(LayerHistoryTestV2, oneLayerExplicitVote) { nsecs_t time = systemTime(); for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer.get(), time, time); + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; } @@ -294,7 +304,7 @@ TEST_F(LayerHistoryTestV2, oneLayerExplicitExactVote) { nsecs_t time = systemTime(); for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer.get(), time, time); + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; } @@ -338,7 +348,7 @@ TEST_F(LayerHistoryTestV2, multipleLayers) { // layer1 is active but infrequent. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer1.get(), time, time); + history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); } @@ -349,12 +359,12 @@ TEST_F(LayerHistoryTestV2, multipleLayers) { // layer2 is frequent and has high refresh rate. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer2.get(), time, time); + history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; } // layer1 is still active but infrequent. - history().record(layer1.get(), time, time); + history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(2, history().summarize(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); @@ -366,7 +376,7 @@ TEST_F(LayerHistoryTestV2, multipleLayers) { // layer1 is no longer active. // layer2 is frequent and has low refresh rate. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer2.get(), time, time); + history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += LO_FPS_PERIOD; } @@ -381,10 +391,10 @@ TEST_F(LayerHistoryTestV2, multipleLayers) { constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD; for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) { if (i % RATIO == 0) { - history().record(layer2.get(), time, time); + history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); } - history().record(layer3.get(), time, time); + history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; } @@ -396,7 +406,7 @@ TEST_F(LayerHistoryTestV2, multipleLayers) { EXPECT_EQ(2, frequentLayerCount(time)); // layer3 becomes recently active. - history().record(layer3.get(), time, time); + history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(2, history().summarize(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote); EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate); @@ -420,7 +430,7 @@ TEST_F(LayerHistoryTestV2, multipleLayers) { // layer2 still has low refresh rate. // layer3 becomes inactive. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer2.get(), time, time); + history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += LO_FPS_PERIOD; } @@ -439,7 +449,7 @@ TEST_F(LayerHistoryTestV2, multipleLayers) { // layer3 becomes active and has high refresh rate. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer3.get(), time, time); + history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; } @@ -466,21 +476,34 @@ TEST_F(LayerHistoryTestV2, inactiveLayers) { nsecs_t time = systemTime(); - // The first few updates are considered frequent - for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) { - history().record(layer.get(), 0, time); + // the very first updates makes the layer frequent + for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) { + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); + + EXPECT_EQ(1, layerCount()); ASSERT_EQ(1, history().summarize(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); } + // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); + + EXPECT_EQ(1, layerCount()); + ASSERT_EQ(1, history().summarize(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); + EXPECT_EQ(1, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + // advance the time for the previous frame to be inactive time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); // Now event if we post a quick few frame we should stay infrequent for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) { - history().record(layer.get(), time, time); + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; EXPECT_EQ(1, layerCount()); @@ -491,7 +514,7 @@ TEST_F(LayerHistoryTestV2, inactiveLayers) { } // More quick frames will get us to frequent again - history().record(layer.get(), time, time); + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; EXPECT_EQ(1, layerCount()); @@ -499,36 +522,6 @@ TEST_F(LayerHistoryTestV2, inactiveLayers) { EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, frequentLayerCount(time)); - - // advance the time for the previous frame to be inactive - time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); - - // Now event if we post a quick few frame we should stay infrequent - for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) { - history().record(layer.get(), time, time); - time += HI_FPS_PERIOD; - - EXPECT_EQ(1, layerCount()); - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - } - - // clear the history - history().clear(); - - // Now event if we post a quick few frame we should stay infrequent - for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) { - history().record(layer.get(), time, time); - time += HI_FPS_PERIOD; - - EXPECT_EQ(1, layerCount()); - ASSERT_EQ(1, history().summarize(time).size()); - EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); - EXPECT_EQ(1, activeLayerCount()); - EXPECT_EQ(0, frequentLayerCount(time)); - } } TEST_F(LayerHistoryTestV2, invisibleExplicitLayer) { @@ -547,11 +540,10 @@ TEST_F(LayerHistoryTestV2, invisibleExplicitLayer) { nsecs_t time = systemTime(); - // Post a few buffers to the layers to make them active - for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) { - history().record(explicitVisiblelayer.get(), time, time); - history().record(explicitInvisiblelayer.get(), time, time); - } + // Post a buffer to the layers to make them active + history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(explicitInvisiblelayer.get(), time, time, + LayerHistory::LayerUpdateType::Buffer); EXPECT_EQ(2, layerCount()); ASSERT_EQ(1, history().summarize(time).size()); @@ -562,5 +554,124 @@ TEST_F(LayerHistoryTestV2, invisibleExplicitLayer) { EXPECT_EQ(2, frequentLayerCount(time)); } +TEST_F(LayerHistoryTestV2, infrequentAnimatingLayer) { + auto layer = createLayer(); + + EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); + EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); + + nsecs_t time = systemTime(); + + EXPECT_EQ(1, layerCount()); + EXPECT_EQ(0, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(0, animatingLayerCount(time)); + + // layer is active but infrequent. + for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); + } + + ASSERT_EQ(1, history().summarize(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); + EXPECT_EQ(1, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(0, animatingLayerCount(time)); + + // another update with the same cadence keep in infrequent + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); + + ASSERT_EQ(1, history().summarize(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote); + EXPECT_EQ(1, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(0, animatingLayerCount(time)); + + // an update as animation will immediately vote for Max + history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX); + time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); + + ASSERT_EQ(1, history().summarize(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); + EXPECT_EQ(1, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(1, animatingLayerCount(time)); +} + +class LayerHistoryTestV2Parameterized + : public LayerHistoryTestV2, + public testing::WithParamInterface<std::chrono::nanoseconds> {}; + +TEST_P(LayerHistoryTestV2Parameterized, HeuristicLayerWithInfrequentLayer) { + std::chrono::nanoseconds infrequentUpdateDelta = GetParam(); + auto heuristicLayer = createLayer("HeuristicLayer"); + + EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true)); + EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree()) + .WillRepeatedly(Return(Layer::FrameRate())); + + auto infrequentLayer = createLayer("InfrequentLayer"); + EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true)); + EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree()) + .WillRepeatedly(Return(Layer::FrameRate())); + + const nsecs_t startTime = systemTime(); + + const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns; + history().record(heuristicLayer.get(), startTime, startTime, + LayerHistory::LayerUpdateType::Buffer); + history().record(infrequentLayer.get(), startTime, startTime, + LayerHistory::LayerUpdateType::Buffer); + + nsecs_t time = startTime; + nsecs_t lastInfrequentUpdate = startTime; + const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5; + int infrequentLayerUpdates = 0; + while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) { + time += heuristicUpdateDelta.count(); + history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + + if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) { + ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates, + totalInfrequentLayerUpdates); + lastInfrequentUpdate = time; + history().record(infrequentLayer.get(), time, time, + LayerHistory::LayerUpdateType::Buffer); + infrequentLayerUpdates++; + } + + if (time - startTime > PRESENT_TIME_HISTORY_TIME.count()) { + ASSERT_NE(0, history().summarize(time).size()); + ASSERT_GE(2, history().summarize(time).size()); + + bool max = false; + bool min = false; + float heuristic = 0; + for (const auto& layer : history().summarize(time)) { + if (layer.vote == LayerHistory::LayerVoteType::Heuristic) { + heuristic = layer.desiredRefreshRate; + } else if (layer.vote == LayerHistory::LayerVoteType::Max) { + max = true; + } else if (layer.vote == LayerHistory::LayerVoteType::Min) { + min = true; + } + } + + if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) { + EXPECT_FLOAT_EQ(24.0f, heuristic); + EXPECT_FALSE(max); + if (history().summarize(time).size() == 2) { + EXPECT_TRUE(min); + } + } + } + } +} + +INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestV2Parameterized, + ::testing::Values(1s, 2s, 3s, 4s, 5s)); + } // namespace } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index 3f14d652ce..ccbd17fa36 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -157,7 +157,8 @@ protected: mMockClock(std::make_shared<NiceMock<MockClock>>()), mReactor(std::make_unique<ClockWrapper>(mMockClock), std::make_unique<VSyncDispatchWrapper>(mMockDispatch), - std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit) { + std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit, + false /* supportKernelIdleTimer */) { ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow)); ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period)); } @@ -663,6 +664,28 @@ TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) { EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); } +TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) { + // Create a reactor which supports the kernel idle timer + auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock), + std::make_unique<VSyncDispatchWrapper>(mMockDispatch), + std::make_unique<VSyncTrackerWrapper>(mMockTracker), + kPendingLimit, true /* supportKernelIdleTimer */); + + bool periodFlushed = true; + EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2); + idleReactor.setIgnorePresentFences(true); + + nsecs_t const newPeriod = 5000; + idleReactor.setPeriod(newPeriod); + + EXPECT_TRUE(idleReactor.addResyncSample(0, 0, &periodFlushed)); + EXPECT_FALSE(periodFlushed); + EXPECT_FALSE(idleReactor.addResyncSample(newPeriod, 0, &periodFlushed)); + EXPECT_TRUE(periodFlushed); + + EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0))); +} + using VSyncReactorDeathTest = VSyncReactorTest; TEST_F(VSyncReactorDeathTest, invalidRemoval) { mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime); diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h index 119f58033c..078d8e07f7 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h +++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h @@ -24,8 +24,9 @@ namespace android::mock { class MockLayer : public Layer { public: - explicit MockLayer(SurfaceFlinger* flinger) - : Layer(LayerCreationArgs(flinger, nullptr, "TestLayer", 800, 600, 0, {})) {} + MockLayer(SurfaceFlinger* flinger, std::string name) + : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 800, 600, 0, {})) {} + explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {} MOCK_CONST_METHOD0(getType, const char*()); MOCK_METHOD0(getFrameSelectionPriority, int32_t()); |