Merge "Move common device metrics code to InputDeviceMetricsSource" into main
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 376d57a..33f6d38 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -187,6 +187,7 @@
#define CGROUPFS_DIR "/sys/fs/cgroup"
#define SDK_EXT_INFO "/apex/com.android.sdkext/bin/derive_sdk"
#define DROPBOX_DIR "/data/system/dropbox"
+#define PRINT_FLAGS "/system/bin/printflags"
// TODO(narayan): Since this information has to be kept in sync
// with tombstoned, we should just put it in a common header.
@@ -1763,14 +1764,8 @@
DumpFile("PRODUCT BUILD-TIME RELEASE FLAGS", "/product/etc/build_flags.json");
DumpFile("VENDOR BUILD-TIME RELEASE FLAGS", "/vendor/etc/build_flags.json");
- DumpFile("SYSTEM BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime values)",
- "/system/etc/aconfig_flags.textproto");
- DumpFile("SYSTEM_EXT BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime"
- " values)", "/system_ext/etc/aconfig_flags.textproto");
- DumpFile("PRODUCT BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime values)",
- "/product/etc/aconfig_flags.textproto");
- DumpFile("VENDOR BUILD-TIME ACONFIG FLAGS (check dumpstate build_config for runtime values)",
- "/vendor/etc/aconfig_flags.textproto");
+ RunCommand("ACONFIG FLAGS", {PRINT_FLAGS},
+ CommandOptions::WithTimeout(10).Always().DropRoot().Build());
RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"});
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index b526a6c..ff6b558 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -25,6 +25,7 @@
#include <gui/IGraphicBufferProducer.h>
#include <gui/ISurfaceComposer.h>
#include <gui/LayerState.h>
+#include <gui/SchedulingPolicy.h>
#include <private/gui/ParcelUtils.h>
#include <stdint.h>
#include <sys/types.h>
@@ -201,6 +202,18 @@
isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
listenerCallbacks, transactionId, mergedTransactions);
}
+ case GET_SCHEDULING_POLICY: {
+ gui::SchedulingPolicy policy;
+ const auto status = gui::getSchedulingPolicy(&policy);
+ if (!status.isOk()) {
+ return status.exceptionCode();
+ }
+
+ SAFE_PARCEL(reply->writeInt32, policy.policy);
+ SAFE_PARCEL(reply->writeInt32, policy.priority);
+ return NO_ERROR;
+ }
+
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
index 9781ca9..008ef19 100644
--- a/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
+++ b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
@@ -18,6 +18,7 @@
import android.gui.BitTube;
import android.gui.ParcelableVsyncEventData;
+import android.gui.SchedulingPolicy;
/** @hide */
interface IDisplayEventConnection {
@@ -44,4 +45,9 @@
* getLatestVsyncEventData() gets the latest vsync event data.
*/
ParcelableVsyncEventData getLatestVsyncEventData();
+
+ /*
+ * getSchedulingPolicy() used in tests to validate the binder thread pririty
+ */
+ SchedulingPolicy getSchedulingPolicy();
}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index 1c604a1..507e086 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -46,6 +46,7 @@
import android.gui.OverlayProperties;
import android.gui.PullAtomData;
import android.gui.ARect;
+import android.gui.SchedulingPolicy;
import android.gui.StalledTransactionInfo;
import android.gui.StaticDisplayInfo;
import android.gui.WindowInfosListenerInfo;
@@ -545,4 +546,6 @@
* applied in SurfaceFlinger due to an unsignaled fence. Otherwise, null is returned.
*/
@nullable StalledTransactionInfo getStalledTransactionInfo(int pid);
+
+ SchedulingPolicy getSchedulingPolicy();
}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl b/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl
index 68781ce..920257c 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposerClient.aidl
@@ -19,6 +19,7 @@
import android.gui.CreateSurfaceResult;
import android.gui.FrameStats;
import android.gui.LayerMetadata;
+import android.gui.SchedulingPolicy;
/** @hide */
interface ISurfaceComposerClient {
@@ -60,4 +61,6 @@
CreateSurfaceResult mirrorSurface(IBinder mirrorFromHandle);
CreateSurfaceResult mirrorDisplay(long displayId);
+
+ SchedulingPolicy getSchedulingPolicy();
}
diff --git a/libs/gui/aidl/android/gui/SchedulingPolicy.aidl b/libs/gui/aidl/android/gui/SchedulingPolicy.aidl
new file mode 100644
index 0000000..4f7cf0a
--- /dev/null
+++ b/libs/gui/aidl/android/gui/SchedulingPolicy.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+/** @hide */
+parcelable SchedulingPolicy {
+ int policy;
+ int priority;
+}
diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h
index bdf8856..3142103 100644
--- a/libs/gui/fuzzer/libgui_fuzzer_utils.h
+++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h
@@ -166,6 +166,7 @@
MOCK_METHOD(binder::Status, getOverlaySupport, (gui::OverlayProperties*), (override));
MOCK_METHOD(binder::Status, getStalledTransactionInfo,
(int32_t, std::optional<gui::StalledTransactionInfo>*), (override));
+ MOCK_METHOD(binder::Status, getSchedulingPolicy, (gui::SchedulingPolicy*), (override));
};
class FakeBnSurfaceComposerClient : public gui::BnSurfaceComposerClient {
@@ -186,6 +187,8 @@
MOCK_METHOD(binder::Status, mirrorDisplay,
(int64_t displayId, gui::CreateSurfaceResult* outResult), (override));
+
+ MOCK_METHOD(binder::Status, getSchedulingPolicy, (gui::SchedulingPolicy*), (override));
};
class FakeDisplayEventDispatcher : public DisplayEventDispatcher {
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 3ff6735..a836f46 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -199,6 +199,7 @@
SET_BOOT_DISPLAY_MODE, // Deprecated. Autogenerated by .aidl now.
CLEAR_BOOT_DISPLAY_MODE, // Deprecated. Autogenerated by .aidl now.
SET_OVERRIDE_FRAME_RATE, // Deprecated. Autogenerated by .aidl now.
+ GET_SCHEDULING_POLICY,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/SchedulingPolicy.h b/libs/gui/include/gui/SchedulingPolicy.h
new file mode 100644
index 0000000..48c9f0c
--- /dev/null
+++ b/libs/gui/include/gui/SchedulingPolicy.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sched.h>
+
+#include <android/gui/SchedulingPolicy.h>
+#include <binder/Status.h>
+
+namespace android::gui {
+
+static binder::Status getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ outPolicy->policy = sched_getscheduler(0);
+ if (outPolicy->policy < 0) {
+ return binder::Status::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+
+ struct sched_param param;
+ if (sched_getparam(0, ¶m) < 0) {
+ return binder::Status::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ outPolicy->priority = param.sched_priority;
+ return binder::Status::ok();
+}
+
+} // namespace android::gui
\ No newline at end of file
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index daed764..9eee699 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -1036,6 +1036,10 @@
return binder::Status::ok();
}
+ binder::Status getSchedulingPolicy(gui::SchedulingPolicy*) override {
+ return binder::Status::ok();
+ }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 4251682..6226a19 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -6596,6 +6596,7 @@
}
}
if (changes.newFocus) {
+ resetNoFocusedWindowTimeoutLocked();
enqueueFocusEventLocked(changes.newFocus, /*hasFocus=*/true, changes.reason);
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index f0602df..703c3f7 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -73,6 +73,7 @@
static constexpr int32_t ACTION_HOVER_ENTER = AMOTION_EVENT_ACTION_HOVER_ENTER;
static constexpr int32_t ACTION_HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE;
static constexpr int32_t ACTION_HOVER_EXIT = AMOTION_EVENT_ACTION_HOVER_EXIT;
+static constexpr int32_t ACTION_SCROLL = AMOTION_EVENT_ACTION_SCROLL;
static constexpr int32_t ACTION_OUTSIDE = AMOTION_EVENT_ACTION_OUTSIDE;
static constexpr int32_t ACTION_CANCEL = AMOTION_EVENT_ACTION_CANCEL;
/**
@@ -2369,13 +2370,196 @@
}
/**
+ * Hover mouse over a window, and then send ACTION_SCROLL. Ensure both the hover and the scroll
+ * events are delivered to the window.
+ */
+TEST_F(InputDispatcherTest, HoverMoveAndScroll) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ // Start hovering in the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(120))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE));
+
+ // Scroll with the mouse
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_SCROLL, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(120))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_SCROLL));
+}
+
+using InputDispatcherMultiDeviceTest = InputDispatcherTest;
+
+/**
+ * One window. Stylus down on the window. Next, touch from another device goes down.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, StylusDownAndTouchDown) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t touchDeviceId = 4;
+ constexpr int32_t stylusDeviceId = 2;
+
+ // Stylus down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(110))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Touch down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
+ .build());
+ // Touch cancels stylus
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId),
+ WithCoords(100, 110)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId),
+ WithCoords(140, 145)));
+
+ // Touch move
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
+ WithCoords(141, 146)));
+
+ // Subsequent stylus movements are dropped
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
+ .build());
+ window->assertNoEvents();
+}
+
+/**
+ * One window and one spy window. Stylus down on the window. Next, touch from another device goes
+ * down.
+ * Similar test as above, but with added SPY window.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, StylusDownWithSpyAndTouchDown) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 200, 200));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t touchDeviceId = 4;
+ constexpr int32_t stylusDeviceId = 2;
+
+ // Stylus down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(110))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Touch down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
+ .build());
+
+ // Touch move
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
+ .build());
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ // Subsequent stylus movements are dropped
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
+ .build());
+
+ window->assertNoEvents();
+ spyWindow->assertNoEvents();
+}
+
+/**
+ * One window. Stylus hover on the window. Next, touch from another device goes down.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, StylusHoverAndTouchDown) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t touchDeviceId = 4;
+ constexpr int32_t stylusDeviceId = 2;
+
+ // Stylus down on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(110))
+ .build());
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+
+ // Touch down on window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
+ .build());
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ // Subsequent stylus movements are ignored
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
+ .build());
+ window->assertNoEvents();
+}
+
+/**
* Two windows: a window on the left and a window on the right.
* Mouse is clicked on the left window and remains down. Touch is touched on the right and remains
* down. Then, on the left window, also place second touch pointer down.
* This test tries to reproduce a crash.
* In the buggy implementation, second pointer down on the left window would cause a crash.
*/
-TEST_F(InputDispatcherTest, MultiDeviceSplitTouch) {
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceSplitTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
@@ -2425,8 +2609,8 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.build());
- leftWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
-
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(mouseDeviceId)));
rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
// Second touch pointer down on left window
@@ -2448,6 +2632,215 @@
}
/**
+ * Two windows: a window on the left and a window on the right.
+ * Mouse is hovered on the left window and stylus is hovered on the right window.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHover) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+ const int32_t stylusDeviceId = 3;
+ const int32_t mouseDeviceId = 6;
+
+ // Start hovering over the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
+
+ // Stylus hovered on right window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(300).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+
+ // Subsequent HOVER_MOVE events are dispatched correctly.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(120))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(310).y(110))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
+/**
+ * Three windows: a window on the left and a window on the right.
+ * And a spy window that's positioned above all of them.
+ * Stylus down on the left window and remains down. Touch goes down on the right and remains down.
+ * Check the stream that's received by the spy.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceWithSpy) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 400, 400));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spyWindow->getInfo(), *leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+ const int32_t stylusDeviceId = 1;
+ const int32_t touchDeviceId = 2;
+
+ // Stylus down on the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Touch down on the right window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+
+ // Stylus movements continue, but are ignored because the touch went down more recently.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(110).y(110))
+ .build());
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(310).y(110))
+ .build());
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ spyWindow->assertNoEvents();
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
+/**
+ * Three windows: a window on the left, a window on the right, and a spy window positioned above
+ * both.
+ * Check hover in left window and touch down in the right window.
+ * At first, spy should receive hover, but the touch down should cancel hovering inside spy.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverAndTouchWithSpy) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 400, 400));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spyWindow->getInfo(), *leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+ const int32_t stylusDeviceId = 1;
+ const int32_t touchDeviceId = 2;
+
+ // Stylus hover on the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+
+ // Touch down on the right window.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+
+ // Stylus movements continue, but are ignored because the touch is down.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(110).y(110))
+ .build());
+
+ // Touch movements continue. They should be delivered to the right window and to the spy
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(301).y(101))
+ .build());
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ spyWindow->assertNoEvents();
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
+/**
* On a single window, use two different devices: mouse and touch.
* Touch happens first, with two pointers going down, and then the first pointer leaving.
* Mouse is clicked next, which causes the touch stream to be aborted with ACTION_CANCEL.
@@ -2455,7 +2848,7 @@
* because the mouse is currently down, and a POINTER_DOWN event from the touchscreen does not
* represent a new gesture.
*/
-TEST_F(InputDispatcherTest, MixedTouchAndMouseWithPointerDown) {
+TEST_F(InputDispatcherMultiDeviceTest, MixedTouchAndMouseWithPointerDown) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2513,7 +2906,17 @@
.pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
.pointer(PointerBuilder(1, ToolType::FINGER).x(350).y(100))
.build());
- // The pointer_down event should be ignored
+ // Since we already canceled this touch gesture, it will be ignored until a completely new
+ // gesture is started. This is easier to implement than trying to keep track of the new pointer
+ // and generating an ACTION_DOWN instead of ACTION_POINTER_DOWN.
+ // However, mouse movements should continue to work.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(330).y(110))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(mouseDeviceId)));
+
window->assertNoEvents();
}
@@ -2521,7 +2924,7 @@
* Inject a touch down and then send a new event via 'notifyMotion'. Ensure the new event cancels
* the injected event.
*/
-TEST_F(InputDispatcherTest, UnfinishedInjectedEvent) {
+TEST_F(InputDispatcherMultiDeviceTest, UnfinishedInjectedEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2567,7 +2970,7 @@
* This test reproduces a crash where there is a mismatch between the downTime and eventTime.
* In the buggy implementation, second finger down on the left window would cause a crash.
*/
-TEST_F(InputDispatcherTest, HoverTapAndSplitTouch) {
+TEST_F(InputDispatcherMultiDeviceTest, HoverTapAndSplitTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
@@ -2643,7 +3046,7 @@
* While the touch is down, new hover events from the stylus device should be ignored. After the
* touch is gone, stylus hovering should start working again.
*/
-TEST_F(InputDispatcherTest, StylusHoverAndTouchTap) {
+TEST_F(InputDispatcherMultiDeviceTest, StylusHoverAndTouchTap) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2656,25 +3059,24 @@
// Start hovering with stylus
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
- MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
- AINPUT_SOURCE_STYLUS)
+ MotionEventBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
.build()));
- window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
// Finger down on the window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
- MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN)
+ MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
- window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
- window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(stylusDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
- // Try to continue hovering with stylus. Since we are already down, injection should fail
+ // Continue hovering with stylus. Injection will fail because touch is already down.
ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
@@ -2693,7 +3095,7 @@
.deviceId(touchDeviceId)
.pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
.build()));
- window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_UP));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(touchDeviceId)));
// Now that the touch is gone, stylus hovering should start working again
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2703,8 +3105,8 @@
.deviceId(stylusDeviceId)
.pointer(PointerBuilder(0, ToolType::STYLUS).x(70).y(70))
.build()));
- window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
- // No more events
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithDeviceId(stylusDeviceId)));
window->assertNoEvents();
}
@@ -3429,22 +3831,17 @@
window->setFrame(Rect(0, 0, 100, 100));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
-
- // Inject a hover_move from mouse.
- NotifyMotionArgs motionArgs =
- generateMotionArgs(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE,
- ADISPLAY_ID_DEFAULT, {{50, 50}});
- motionArgs.xCursorPosition = 50;
- motionArgs.yCursorPosition = 50;
- mDispatcher->notifyMotion(motionArgs);
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(50).y(50))
+ .build());
ASSERT_NO_FATAL_FAILURE(
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithSource(AINPUT_SOURCE_MOUSE))));
// Tap on the window
- mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {{10, 10}}));
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(10).y(10))
+ .build());
ASSERT_NO_FATAL_FAILURE(
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
WithSource(AINPUT_SOURCE_MOUSE))));
@@ -3453,8 +3850,9 @@
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
- mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {{10, 10}}));
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(10).y(10))
+ .build());
ASSERT_NO_FATAL_FAILURE(
window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
@@ -3655,12 +4053,12 @@
*/
TEST_F(InputDispatcherTest, ActionOutsideForOwnedWindowHasValidCoordinates) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "First Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
window->setFrame(Rect{0, 0, 100, 100});
sp<FakeWindowHandle> outsideWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Outside Window",
ADISPLAY_ID_DEFAULT);
outsideWindow->setFrame(Rect{100, 100, 200, 200});
outsideWindow->setWatchOutsideTouch(true);
@@ -5665,6 +6063,53 @@
rightDropTouchesWindow->assertNoEvents();
}
+/**
+ * A single window is on screen first. Touch is injected into that window. Next, a second window
+ * appears. Since the first window is slippery, touch will move from the first window to the second.
+ */
+TEST_F(InputDispatcherTest, InjectedTouchSlips) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> originalWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Original", ADISPLAY_ID_DEFAULT);
+ originalWindow->setFrame(Rect(0, 0, 200, 200));
+ originalWindow->setSlippery(true);
+
+ sp<FakeWindowHandle> appearingWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Appearing", ADISPLAY_ID_DEFAULT);
+ appearingWindow->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*originalWindow->getInfo()}, {}, 0, 0});
+
+ // Touch down on the original window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(100).y(100))
+ .build()));
+ originalWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Now, a new window appears. This could be, for example, a notification shade that appears
+ // after user starts to drag down on the launcher window.
+ mDispatcher->onWindowInfosChanged(
+ {{*appearingWindow->getInfo(), *originalWindow->getInfo()}, {}, 0, 0});
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(110).y(110))
+ .build()));
+ originalWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ appearingWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120))
+ .build()));
+ appearingWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+
+ originalWindow->assertNoEvents();
+ appearingWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, NotifiesDeviceInteractionsWithMotions) {
using Uid = gui::Uid;
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -6663,15 +7108,15 @@
mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
// Start hover in window 1
- mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_ENTER, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {{50, 50}}));
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
consumeMotionEvent(mWindow1, ACTION_HOVER_ENTER,
{getPointInWindow(mWindow1->getInfo(), PointF{50, 50})});
-
// Move hover to window 2.
- mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {{150, 150}}));
-
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(150))
+ .build());
consumeMotionEvent(mWindow1, ACTION_HOVER_EXIT, {{50, 50}});
consumeMotionEvent(mWindow1, ACTION_HOVER_ENTER,
{getPointInWindow(mWindow2->getInfo(), PointF{150, 150})});
@@ -7543,7 +7988,7 @@
TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) {
std::shared_ptr<FakeApplicationHandle> focusedApplication =
std::make_shared<FakeApplicationHandle>();
- focusedApplication->setDispatchingTimeout(200ms);
+ focusedApplication->setDispatchingTimeout(300ms);
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
// The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
mFocusedWindow->setFocusable(false);
@@ -7958,6 +8403,47 @@
mWindow->assertNoEvents();
}
+/**
+ * One window. Hover mouse in the window, and then start capture. Make sure that the relative
+ * mouse movements don't affect the previous mouse hovering state.
+ * When pointer capture is enabled, the incoming events are always ACTION_MOVE (there are no
+ * HOVER_MOVE events).
+ */
+TEST_F(InputDispatcherPointerCaptureTests, MouseHoverAndPointerCapture) {
+ // Mouse hover on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+ .build());
+
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER)));
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE)));
+
+ // Start pointer capture
+ requestAndVerifyPointerCapture(mWindow, true);
+
+ // Send some relative mouse movements and receive them in the window.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_MOUSE_RELATIVE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(10).y(11))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithCoords(10, 11),
+ WithSource(AINPUT_SOURCE_MOUSE_RELATIVE)));
+
+ // Stop pointer capture
+ requestAndVerifyPointerCapture(mWindow, false);
+
+ // Continue hovering on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(105).y(115))
+ .build());
+ mWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE)));
+
+ mWindow->assertNoEvents();
+}
+
class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest {
protected:
constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8;
@@ -9664,6 +10150,73 @@
spy->assertNoEvents();
}
+/**
+ * A window on the left and a window on the right. Also, a spy window that's above all of the
+ * windows, and spanning both left and right windows.
+ * Send simultaneous motion streams from two different devices, one to the left window, and another
+ * to the right window.
+ * Pilfer from spy window.
+ * Check that the pilfering only affects the pointers that are actually being received by the spy.
+ */
+TEST_F(InputDispatcherPilferPointersTest, MultiDevicePilfer) {
+ sp<FakeWindowHandle> spy = createSpy();
+ spy->setFrame(Rect(0, 0, 200, 200));
+ sp<FakeWindowHandle> leftWindow = createForeground();
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+
+ sp<FakeWindowHandle> rightWindow = createForeground();
+ rightWindow->setFrame(Rect(100, 0, 200, 100));
+
+ constexpr int32_t stylusDeviceId = 1;
+ constexpr int32_t touchDeviceId = 2;
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spy->getInfo(), *leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+ // Stylus down on left window and spy
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Finger down on right window and spy - but spy already has stylus
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .build());
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+
+ // Act: pilfer from spy. Spy is currently receiving touch events.
+ EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken()));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId)));
+
+ // Continue movements from both stylus and touch. Touch will be delivered to spy, but not stylus
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(52))
+ .build());
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(52))
+ .build());
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ spy->assertNoEvents();
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
class InputDispatcherStylusInterceptorTest : public InputDispatcherTest {
public:
std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupStylusOverlayScenario() {
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index bdbc79b..6b4215e 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -22,6 +22,7 @@
#include <private/android_filesystem_config.h>
#include <gui/AidlStatusUtil.h>
+#include <gui/SchedulingPolicy.h>
#include "Client.h"
#include "FrontEnd/LayerCreationArgs.h"
@@ -117,5 +118,9 @@
return binderStatusFromStatusT(status);
}
+binder::Status Client::getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ return gui::getSchedulingPolicy(outPolicy);
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index af410ea..77916e0 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -55,6 +55,8 @@
binder::Status mirrorDisplay(int64_t displayId, gui::CreateSurfaceResult* outResult) override;
+ binder::Status getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) override;
+
// constant
sp<SurfaceFlinger> mFlinger;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 1faf6a1..5b6591a 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -428,10 +428,12 @@
return;
}
- mHdrSdrRatioOverlay = std::make_unique<HdrSdrRatioOverlay>();
- mHdrSdrRatioOverlay->setLayerStack(getLayerStack());
- mHdrSdrRatioOverlay->setViewport(getSize());
- updateHdrSdrRatioOverlayRatio(mHdrSdrRatio);
+ mHdrSdrRatioOverlay = HdrSdrRatioOverlay::create();
+ if (mHdrSdrRatioOverlay) {
+ mHdrSdrRatioOverlay->setLayerStack(getLayerStack());
+ mHdrSdrRatioOverlay->setViewport(getSize());
+ updateHdrSdrRatioOverlayRatio(mHdrSdrRatio);
+ }
}
void DisplayDevice::updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio) {
@@ -468,11 +470,13 @@
// TODO(b/296636258) Update to use the render rate range in VRR mode.
const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
- mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features);
- mRefreshRateOverlay->setLayerStack(getLayerStack());
- mRefreshRateOverlay->setViewport(getSize());
- updateRefreshRateOverlayRate(getActiveMode().modePtr->getVsyncRate(), getActiveMode().fps,
- setByHwc);
+ mRefreshRateOverlay = RefreshRateOverlay::create(fpsRange, features);
+ if (mRefreshRateOverlay) {
+ mRefreshRateOverlay->setLayerStack(getLayerStack());
+ mRefreshRateOverlay->setViewport(getSize());
+ updateRefreshRateOverlayRate(getActiveMode().modePtr->getVsyncRate(), getActiveMode().fps,
+ setByHwc);
+ }
}
void DisplayDevice::updateRefreshRateOverlayRate(Fps vsyncRate, Fps renderFps, bool setByHwc) {
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 899d2de..f9c8e81 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -398,6 +398,15 @@
geomCrop = requested.crop;
}
+ if (forceUpdate || requested.what & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
+ const auto compatibility =
+ Layer::FrameRate::convertCompatibility(requested.defaultFrameRateCompatibility);
+ if (defaultFrameRateCompatibility != compatibility) {
+ clientChanges |= layer_state_t::eDefaultFrameRateCompatibilityChanged;
+ }
+ defaultFrameRateCompatibility = compatibility;
+ }
+
if (forceUpdate ||
requested.what &
(layer_state_t::eFlagsChanged | layer_state_t::eBufferChanged |
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index a5e9368..a1c72a9 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -86,6 +86,8 @@
gui::GameMode gameMode;
scheduler::LayerInfo::FrameRate frameRate;
scheduler::LayerInfo::FrameRateSelectionStrategy frameRateSelectionStrategy;
+ scheduler::FrameRateCompatibility defaultFrameRateCompatibility =
+ scheduler::FrameRateCompatibility::Default;
ui::Transform::RotationFlags fixedTransformHint;
std::optional<ui::Transform::RotationFlags> transformHint;
bool handleSkipScreenshotFlag = false;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 4c9fb06..55be398 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -666,7 +666,7 @@
return;
}
- using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
+ using FrameRateCompatibility = scheduler::FrameRateCompatibility;
if (snapshot.frameRate.isValid()) {
// we already have a valid framerate.
return;
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index dc8694c..168267b 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -122,8 +122,7 @@
isTrustedOverlay = false;
dropInputMode = gui::DropInputMode::NONE;
dimmingEnabled = true;
- defaultFrameRateCompatibility =
- static_cast<int8_t>(scheduler::LayerInfo::FrameRateCompatibility::Default);
+ defaultFrameRateCompatibility = static_cast<int8_t>(scheduler::FrameRateCompatibility::Default);
frameRateCategory = static_cast<int8_t>(FrameRateCategory::Default);
frameRateSelectionStrategy =
static_cast<int8_t>(scheduler::LayerInfo::FrameRateSelectionStrategy::Self);
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.cpp b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
index 186e878..dfb1c1e 100644
--- a/services/surfaceflinger/HdrSdrRatioOverlay.cpp
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
@@ -96,7 +96,18 @@
return buffer;
}
-HdrSdrRatioOverlay::HdrSdrRatioOverlay()
+std::unique_ptr<HdrSdrRatioOverlay> HdrSdrRatioOverlay::create() {
+ std::unique_ptr<HdrSdrRatioOverlay> overlay =
+ std::make_unique<HdrSdrRatioOverlay>(ConstructorTag{});
+ if (overlay->initCheck()) {
+ return overlay;
+ }
+
+ ALOGE("%s: Failed to create HdrSdrRatioOverlay", __func__);
+ return {};
+}
+
+HdrSdrRatioOverlay::HdrSdrRatioOverlay(ConstructorTag)
: mSurfaceControl(
SurfaceControlHolder::createSurfaceControlHolder(String8("HdrSdrRatioOverlay"))) {
if (!mSurfaceControl) {
@@ -109,6 +120,10 @@
.apply();
}
+bool HdrSdrRatioOverlay::initCheck() const {
+ return mSurfaceControl != nullptr;
+}
+
void HdrSdrRatioOverlay::changeHdrSdrRatio(float currentHdrSdrRatio) {
mCurrentHdrSdrRatio = currentHdrSdrRatio;
animate();
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.h b/services/surfaceflinger/HdrSdrRatioOverlay.h
index 69f95ec..72d401d 100644
--- a/services/surfaceflinger/HdrSdrRatioOverlay.h
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.h
@@ -25,15 +25,22 @@
namespace android {
class HdrSdrRatioOverlay {
+private:
+ // Effectively making the constructor private, while keeping std::make_unique work
+ struct ConstructorTag {};
+
public:
- HdrSdrRatioOverlay();
+ static std::unique_ptr<HdrSdrRatioOverlay> create();
+
void setLayerStack(ui::LayerStack);
void setViewport(ui::Size);
void animate();
void changeHdrSdrRatio(float currentRatio);
+ HdrSdrRatioOverlay(ConstructorTag);
+
private:
- float mCurrentHdrSdrRatio = 1.f;
+ bool initCheck() const;
static sp<GraphicBuffer> draw(float currentHdrSdrRatio, SkColor, ui::Transform::RotationFlags,
sp<GraphicBuffer>& ringBufer);
@@ -41,6 +48,7 @@
const sp<GraphicBuffer> getOrCreateBuffers(float currentHdrSdrRatio);
+ float mCurrentHdrSdrRatio = 1.f;
const std::unique_ptr<SurfaceControlHolder> mSurfaceControl;
size_t mIndex = 0;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5890050..5ae2999 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1155,12 +1155,12 @@
if (mDrawingState.defaultFrameRateCompatibility == compatibility) return false;
mDrawingState.defaultFrameRateCompatibility = compatibility;
mDrawingState.modified = true;
- mFlinger->mScheduler->setDefaultFrameRateCompatibility(this);
+ mFlinger->mScheduler->setDefaultFrameRateCompatibility(sequence, compatibility);
setTransactionFlags(eTransactionNeeded);
return true;
}
-scheduler::LayerInfo::FrameRateCompatibility Layer::getDefaultFrameRateCompatibility() const {
+scheduler::FrameRateCompatibility Layer::getDefaultFrameRateCompatibility() const {
return mDrawingState.defaultFrameRateCompatibility;
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 40882f4..78a3a7c 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -111,7 +111,7 @@
};
using FrameRate = scheduler::LayerInfo::FrameRate;
- using FrameRateCompatibility = scheduler::LayerInfo::FrameRateCompatibility;
+ using FrameRateCompatibility = scheduler::FrameRateCompatibility;
using FrameRateSelectionStrategy = scheduler::LayerInfo::FrameRateSelectionStrategy;
struct State {
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index be04c09..42676c6 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -138,7 +138,20 @@
SegmentDrawer::drawDigit(number % 10, left, color, canvas);
}
-RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, ftl::Flags<Features> features)
+std::unique_ptr<RefreshRateOverlay> RefreshRateOverlay::create(FpsRange range,
+ ftl::Flags<Features> features) {
+ std::unique_ptr<RefreshRateOverlay> overlay =
+ std::make_unique<RefreshRateOverlay>(ConstructorTag{}, range, features);
+ if (overlay->initCheck()) {
+ return overlay;
+ }
+
+ ALOGE("%s: Failed to create RefreshRateOverlay", __func__);
+ return {};
+}
+
+RefreshRateOverlay::RefreshRateOverlay(ConstructorTag, FpsRange fpsRange,
+ ftl::Flags<Features> features)
: mFpsRange(fpsRange),
mFeatures(features),
mSurfaceControl(
@@ -154,6 +167,10 @@
.apply();
}
+bool RefreshRateOverlay::initCheck() const {
+ return mSurfaceControl != nullptr;
+}
+
auto RefreshRateOverlay::getOrCreateBuffers(Fps vsyncRate, Fps renderFps) -> const Buffers& {
static const Buffers kNoBuffers;
if (!mSurfaceControl) return kNoBuffers;
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index ae334e5..0fec470 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -37,6 +37,10 @@
class SurfaceFlinger;
class RefreshRateOverlay {
+private:
+ // Effectively making the constructor private, while keeping std::make_unique work
+ struct ConstructorTag {};
+
public:
enum class Features {
Spinner = 1 << 0,
@@ -45,7 +49,7 @@
SetByHwc = 1 << 3,
};
- RefreshRateOverlay(FpsRange, ftl::Flags<Features>);
+ static std::unique_ptr<RefreshRateOverlay> create(FpsRange, ftl::Flags<Features>);
void setLayerStack(ui::LayerStack);
void setViewport(ui::Size);
@@ -54,7 +58,11 @@
void animate();
bool isSetByHwc() const { return mFeatures.test(RefreshRateOverlay::Features::SetByHwc); }
+ RefreshRateOverlay(ConstructorTag, FpsRange, ftl::Flags<Features>);
+
private:
+ bool initCheck() const;
+
using Buffers = std::vector<sp<GraphicBuffer>>;
static Buffers draw(int vsyncRate, int renderFps, SkColor, ui::Transform::RotationFlags,
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index edab7ec..9a55c94 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -38,6 +38,7 @@
#include <cutils/sched_policy.h>
#include <gui/DisplayEventReceiver.h>
+#include <gui/SchedulingPolicy.h>
#include <utils/Errors.h>
#include <utils/Trace.h>
@@ -218,6 +219,10 @@
return binder::Status::ok();
}
+binder::Status EventThreadConnection::getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ return gui::getSchedulingPolicy(outPolicy);
+}
+
status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
constexpr auto toStatus = [](ssize_t size) {
return size < 0 ? status_t(size) : status_t(NO_ERROR);
@@ -300,9 +305,14 @@
sp<EventThreadConnection> EventThread::createEventConnection(
EventRegistrationFlags eventRegistration) const {
- return sp<EventThreadConnection>::make(const_cast<EventThread*>(this),
- IPCThreadState::self()->getCallingUid(),
- eventRegistration);
+ auto connection = sp<EventThreadConnection>::make(const_cast<EventThread*>(this),
+ IPCThreadState::self()->getCallingUid(),
+ eventRegistration);
+ if (flags::misc1()) {
+ const int policy = SCHED_FIFO;
+ connection->setMinSchedulerPolicy(policy, sched_get_priority_min(policy));
+ }
+ return connection;
}
status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index a7c8b74..7842318 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -78,6 +78,7 @@
binder::Status setVsyncRate(int rate) override;
binder::Status requestNextVsync() override; // asynchronous
binder::Status getLatestVsyncEventData(ParcelableVsyncEventData* outVsyncEventData) override;
+ binder::Status getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) override;
VSyncRequest vsyncRequest = VSyncRequest::None;
const uid_t mOwnerUid;
diff --git a/services/surfaceflinger/Scheduler/FrameRateCompatibility.h b/services/surfaceflinger/Scheduler/FrameRateCompatibility.h
new file mode 100644
index 0000000..405c982
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/FrameRateCompatibility.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android::scheduler {
+// FrameRateCompatibility specifies how we should interpret the frame rate associated with
+// the layer.
+enum class FrameRateCompatibility {
+ Default, // Layer didn't specify any specific handling strategy
+
+ Min, // Layer needs the minimum frame rate.
+
+ Exact, // Layer needs the exact frame rate.
+
+ ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
+ // content properly. Any other value will result in a pull down.
+
+ NoVote, // Layer doesn't have any requirements for the refresh rate and
+ // should not be considered when the display refresh rate is determined.
+
+ ftl_last = NoVote
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 6b5327a..4e5659e 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -76,12 +76,12 @@
ALOGD("%s: %s @ %d Hz", __FUNCTION__, info.getName().c_str(), fps);
}
-LayerHistory::LayerVoteType getVoteType(LayerInfo::FrameRateCompatibility compatibility,
+LayerHistory::LayerVoteType getVoteType(FrameRateCompatibility compatibility,
bool contentDetectionEnabled) {
LayerHistory::LayerVoteType voteType;
- if (!contentDetectionEnabled || compatibility == LayerInfo::FrameRateCompatibility::NoVote) {
+ if (!contentDetectionEnabled || compatibility == FrameRateCompatibility::NoVote) {
voteType = LayerHistory::LayerVoteType::NoVote;
- } else if (compatibility == LayerInfo::FrameRateCompatibility::Min) {
+ } else if (compatibility == FrameRateCompatibility::Min) {
voteType = LayerHistory::LayerVoteType::Min;
} else {
voteType = LayerHistory::LayerVoteType::Heuristic;
@@ -141,20 +141,20 @@
}
}
-void LayerHistory::setDefaultFrameRateCompatibility(Layer* layer, bool contentDetectionEnabled) {
+void LayerHistory::setDefaultFrameRateCompatibility(int32_t id,
+ FrameRateCompatibility frameRateCompatibility,
+ bool contentDetectionEnabled) {
std::lock_guard lock(mLock);
- auto id = layer->getSequence();
auto [found, layerPair] = findLayer(id);
if (found == LayerStatus::NotFound) {
// Offscreen layer
- ALOGV("%s: %s not registered", __func__, layer->getName().c_str());
+ ALOGV("%s: %d not registered", __func__, id);
return;
}
const auto& info = layerPair->second;
- info->setDefaultLayerVote(
- getVoteType(layer->getDefaultFrameRateCompatibility(), contentDetectionEnabled));
+ info->setDefaultLayerVote(getVoteType(frameRateCompatibility, contentDetectionEnabled));
}
auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) -> Summary {
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 5750ea7..40bda83 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -29,6 +29,7 @@
#include "EventThread.h"
+#include "FrameRateCompatibility.h"
#include "RefreshRateSelector.h"
namespace android {
@@ -70,7 +71,8 @@
// Updates the default frame rate compatibility which takes effect when the app
// does not set a preference for refresh rate.
- void setDefaultFrameRateCompatibility(Layer*, bool contentDetectionEnabled);
+ void setDefaultFrameRateCompatibility(int32_t id, FrameRateCompatibility frameRateCompatibility,
+ bool contentDetectionEnabled);
using Summary = std::vector<RefreshRateSelector::LayerRequirement>;
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 875bdc8..dd96930 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -455,7 +455,7 @@
return consistent;
}
-LayerInfo::FrameRateCompatibility LayerInfo::FrameRate::convertCompatibility(int8_t compatibility) {
+FrameRateCompatibility LayerInfo::FrameRate::convertCompatibility(int8_t compatibility) {
switch (compatibility) {
case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT:
return FrameRateCompatibility::Default;
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 129b4c4..6286b28 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -28,6 +28,7 @@
#include <scheduler/Fps.h>
#include <scheduler/Seamlessness.h>
+#include "FrameRateCompatibility.h"
#include "LayerHistory.h"
#include "RefreshRateSelector.h"
@@ -78,24 +79,6 @@
using RefreshRateVotes = ftl::SmallVector<LayerInfo::LayerVote, 2>;
- // FrameRateCompatibility specifies how we should interpret the frame rate associated with
- // the layer.
- enum class FrameRateCompatibility {
- Default, // Layer didn't specify any specific handling strategy
-
- Min, // Layer needs the minimum frame rate.
-
- Exact, // Layer needs the exact frame rate.
-
- ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
- // content properly. Any other value will result in a pull down.
-
- NoVote, // Layer doesn't have any requirements for the refresh rate and
- // should not be considered when the display refresh rate is determined.
-
- ftl_last = NoVote
- };
-
enum class FrameRateSelectionStrategy {
Self,
OverrideChildren,
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 68e2ce9..76f1af9 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -635,8 +635,9 @@
mLayerHistory.setModeChangePending(pending);
}
-void Scheduler::setDefaultFrameRateCompatibility(Layer* layer) {
- mLayerHistory.setDefaultFrameRateCompatibility(layer,
+void Scheduler::setDefaultFrameRateCompatibility(
+ int32_t id, scheduler::FrameRateCompatibility frameRateCompatibility) {
+ mLayerHistory.setDefaultFrameRateCompatibility(id, frameRateCompatibility,
mFeatures.test(Feature::kContentDetection));
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index f652bb2..e6db654 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -232,7 +232,7 @@
void recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime,
LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock);
void setModeChangePending(bool pending);
- void setDefaultFrameRateCompatibility(Layer*);
+ void setDefaultFrameRateCompatibility(int32_t id, scheduler::FrameRateCompatibility);
void deregisterLayer(Layer*);
void onLayerDestroyed(Layer*) EXCLUDES(mChoreographerLock);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 12aacad..4d8dc94 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -113,6 +113,7 @@
#include <vector>
#include <gui/LayerStatePermissions.h>
+#include <gui/SchedulingPolicy.h>
#include <ui/DisplayIdentification.h>
#include "BackgroundExecutor.h"
#include "Client.h"
@@ -2204,8 +2205,12 @@
void SurfaceFlinger::updateLayerHistory(const frontend::LayerSnapshot& snapshot) {
using Changes = frontend::RequestedLayerState::Changes;
- if (snapshot.path.isClone() ||
- !snapshot.changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation)) {
+ if (snapshot.path.isClone()) {
+ return;
+ }
+
+ if (!snapshot.changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation) &&
+ (snapshot.clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) == 0) {
return;
}
@@ -2225,6 +2230,11 @@
it->second->recordLayerHistoryAnimationTx(layerProps);
}
+ if (snapshot.clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) {
+ mScheduler->setDefaultFrameRateCompatibility(snapshot.sequence,
+ snapshot.defaultFrameRateCompatibility);
+ }
+
if (snapshot.changes.test(Changes::FrameRate)) {
it->second->setFrameRateForLayerTree(snapshot.frameRate, layerProps);
}
@@ -6579,6 +6589,7 @@
case GET_ACTIVE_DISPLAY_MODE:
case GET_DISPLAY_COLOR_MODES:
case GET_DISPLAY_MODES:
+ case GET_SCHEDULING_POLICY:
// Calling setTransactionState is safe, because you need to have been
// granted a reference to Client* and Handle* to do anything with it.
case SET_TRANSACTION_STATE: {
@@ -9016,6 +9027,10 @@
const sp<Client> client = sp<Client>::make(mFlinger);
if (client->initCheck() == NO_ERROR) {
*outClient = client;
+ if (flags::misc1()) {
+ const int policy = SCHED_FIFO;
+ client->setMinSchedulerPolicy(policy, sched_get_priority_min(policy));
+ }
return binder::Status::ok();
} else {
*outClient = nullptr;
@@ -9797,6 +9812,10 @@
return binderStatusFromStatusT(status);
}
+binder::Status SurfaceComposerAIDL::getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ return gui::getSchedulingPolicy(outPolicy);
+}
+
status_t SurfaceComposerAIDL::checkAccessPermission(bool usePermissionCache) {
if (!mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
IPCThreadState* ipc = IPCThreadState::self();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 42825f7..a5a2341 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -1581,8 +1581,9 @@
gui::WindowInfosListenerInfo* outInfo) override;
binder::Status removeWindowInfosListener(
const sp<gui::IWindowInfosListener>& windowInfosListener) override;
- binder::Status getStalledTransactionInfo(int pid,
- std::optional<gui::StalledTransactionInfo>* outInfo);
+ binder::Status getStalledTransactionInfo(
+ int pid, std::optional<gui::StalledTransactionInfo>* outInfo) override;
+ binder::Status getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) override;
private:
static const constexpr bool kUsePermissionCache = true;
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
index ed8bb7f..d13189e 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzer.cpp
@@ -94,6 +94,7 @@
BnSurfaceComposer::REMOVE_TUNNEL_MODE_ENABLED_LISTENER,
BnSurfaceComposer::ADD_WINDOW_INFOS_LISTENER,
BnSurfaceComposer::REMOVE_WINDOW_INFOS_LISTENER,
+ BnSurfaceComposer::GET_SCHEDULING_POLICY,
};
static constexpr uint32_t kMinCode = 1000;
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index cf23169..9599452 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -149,6 +149,9 @@
// publish gui::ISurfaceComposer, the new AIDL interface
sp<SurfaceComposerAIDL> composerAIDL = sp<SurfaceComposerAIDL>::make(flinger);
+ if (flags::misc1()) {
+ composerAIDL->setMinSchedulerPolicy(SCHED_FIFO, newPriority);
+ }
sm->addService(String16("SurfaceFlingerAIDL"), composerAIDL, false,
IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index b5168b0..36b1972 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -30,6 +30,7 @@
test_suites: ["device-tests"],
srcs: [
"BootDisplayMode_test.cpp",
+ "Binder_test.cpp",
"BufferGenerator.cpp",
"CommonTypes_test.cpp",
"Credentials_test.cpp",
diff --git a/services/surfaceflinger/tests/Binder_test.cpp b/services/surfaceflinger/tests/Binder_test.cpp
new file mode 100644
index 0000000..3152973
--- /dev/null
+++ b/services/surfaceflinger/tests/Binder_test.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <sched.h>
+
+#include <android/gui/ISurfaceComposer.h>
+#include <android/gui/ISurfaceComposerClient.h>
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+#include <gtest/gtest.h>
+#include <gui/ISurfaceComposer.h>
+
+#include <com_android_graphics_surfaceflinger_flags.h>
+
+namespace android::test {
+using namespace com::android::graphics::surfaceflinger;
+
+class BinderTest : public ::testing::Test {
+protected:
+ BinderTest();
+
+ void SetUp() override;
+
+ void getSchedulingPolicy(gui::SchedulingPolicy* outPolicy);
+ void getNonAidlSchedulingPolicy(gui::SchedulingPolicy* outPolicy);
+ void getClientSchedulingPolicy(gui::SchedulingPolicy* outPolicy);
+ void getDisplayEventConnectionSchedulingPolicy(gui::SchedulingPolicy* outPolicy);
+
+private:
+ sp<gui::ISurfaceComposer> mISurfaceComposerAidl;
+ sp<ISurfaceComposer> mISurfaceComposer;
+ sp<gui::ISurfaceComposerClient> mISurfaceComposerClient;
+ sp<gui::IDisplayEventConnection> mConnection;
+};
+
+BinderTest::BinderTest() {
+ const String16 name("SurfaceFlingerAIDL");
+ mISurfaceComposerAidl = waitForService<gui::ISurfaceComposer>(String16("SurfaceFlingerAIDL"));
+ mISurfaceComposer = waitForService<ISurfaceComposer>(String16("SurfaceFlinger"));
+ mISurfaceComposerAidl->createConnection(&mISurfaceComposerClient);
+ mISurfaceComposerAidl
+ ->createDisplayEventConnection(gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
+ gui::ISurfaceComposer::EventRegistration(0), {},
+ &mConnection);
+}
+
+void BinderTest::SetUp() {
+ ASSERT_TRUE(mISurfaceComposerAidl);
+ ASSERT_TRUE(mISurfaceComposer);
+ ASSERT_TRUE(mISurfaceComposerClient);
+ ASSERT_TRUE(mConnection);
+}
+
+void BinderTest::getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ const auto status = mISurfaceComposerAidl->getSchedulingPolicy(outPolicy);
+ ASSERT_TRUE(status.isOk());
+}
+
+void BinderTest::getNonAidlSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ Parcel data, reply;
+ const status_t status =
+ IInterface::asBinder(mISurfaceComposer)
+ ->transact(BnSurfaceComposer::GET_SCHEDULING_POLICY, data, &reply);
+ ASSERT_EQ(OK, status);
+
+ outPolicy->policy = reply.readInt32();
+ outPolicy->priority = reply.readInt32();
+}
+
+void BinderTest::getClientSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ const auto status = mISurfaceComposerClient->getSchedulingPolicy(outPolicy);
+ ASSERT_TRUE(status.isOk());
+}
+
+void BinderTest::getDisplayEventConnectionSchedulingPolicy(gui::SchedulingPolicy* outPolicy) {
+ const auto status = mConnection->getSchedulingPolicy(outPolicy);
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(BinderTest, SchedulingPolicy) {
+ if (!flags::misc1()) GTEST_SKIP();
+
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+TEST_F(BinderTest, NonAidlSchedulingPolicy) {
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getNonAidlSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+TEST_F(BinderTest, ClientSchedulingPolicy) {
+ if (!flags::misc1()) GTEST_SKIP();
+
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getClientSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+TEST_F(BinderTest, DisplayEventConnectionSchedulingPolicy) {
+ if (!flags::misc1()) GTEST_SKIP();
+
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getDisplayEventConnectionSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+class BinderTestRtCaller : public BinderTest {
+protected:
+ void SetUp() override;
+ void TearDown() override;
+
+private:
+ int mOrigPolicy;
+ int mOrigPriority;
+};
+
+void BinderTestRtCaller::SetUp() {
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ mOrigPolicy = sched_getscheduler(0);
+ struct sched_param origSchedParam;
+ ASSERT_GE(0, sched_getparam(0, &origSchedParam)) << "errno: " << strerror(errno);
+ mOrigPriority = origSchedParam.sched_priority;
+
+ struct sched_param param;
+ param.sched_priority = priority;
+ ASSERT_GE(0, sched_setscheduler(0, policy, ¶m)) << "errno: " << strerror(errno);
+}
+
+void BinderTestRtCaller::TearDown() {
+ struct sched_param origSchedParam;
+ origSchedParam.sched_priority = mOrigPriority;
+ ASSERT_GE(0, sched_setscheduler(0, mOrigPolicy, &origSchedParam))
+ << "errno: " << strerror(errno);
+}
+
+TEST_F(BinderTestRtCaller, SchedulingPolicy) {
+ if (!flags::misc1()) GTEST_SKIP();
+
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+TEST_F(BinderTestRtCaller, NonAidlSchedulingPolicy) {
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getNonAidlSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+TEST_F(BinderTestRtCaller, ClientSchedulingPolicy) {
+ if (!flags::misc1()) GTEST_SKIP();
+
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getClientSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+TEST_F(BinderTestRtCaller, DisplayEventConnectionSchedulingPolicy) {
+ if (!flags::misc1()) GTEST_SKIP();
+
+ const int policy = SCHED_FIFO;
+ const int priority = sched_get_priority_min(policy);
+
+ gui::SchedulingPolicy sfPolicy;
+ ASSERT_NO_FATAL_FAILURE(getDisplayEventConnectionSchedulingPolicy(&sfPolicy));
+
+ ASSERT_EQ(policy, sfPolicy.policy & (~SCHED_RESET_ON_FORK));
+ ASSERT_EQ(priority, sfPolicy.priority);
+}
+
+} // namespace android::test
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 7e3e61f..549a362 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -152,7 +152,7 @@
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer, getDefaultFrameRateCompatibility())
- .WillOnce(Return(LayerInfo::FrameRateCompatibility::NoVote));
+ .WillOnce(Return(FrameRateCompatibility::NoVote));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -165,7 +165,10 @@
history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
LayerHistory::LayerUpdateType::Buffer);
- history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
+ history().setDefaultFrameRateCompatibility(layer->getSequence(),
+
+ layer->getDefaultFrameRateCompatibility(),
+ true /* contentDetectionEnabled */);
EXPECT_TRUE(summarizeLayerHistory(time).empty());
EXPECT_EQ(1, activeLayerCount());
@@ -176,7 +179,7 @@
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer, getDefaultFrameRateCompatibility())
- .WillOnce(Return(LayerInfo::FrameRateCompatibility::Min));
+ .WillOnce(Return(FrameRateCompatibility::Min));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -188,7 +191,9 @@
history().record(layer->getSequence(), layer->getLayerProps(), 0, time,
LayerHistory::LayerUpdateType::Buffer);
- history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */);
+ history().setDefaultFrameRateCompatibility(layer->getSequence(),
+ layer->getDefaultFrameRateCompatibility(),
+ true /* contentDetectionEnabled */);
auto summary = summarizeLayerHistory(time);
ASSERT_EQ(1, summarizeLayerHistory(time).size());
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 1a9233d..69316bf 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -315,14 +315,11 @@
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
EXPECT_EQ(getSnapshot(11)->frameRate.vote.rate.getIntValue(), 90);
- EXPECT_EQ(getSnapshot(11)->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Exact);
+ EXPECT_EQ(getSnapshot(11)->frameRate.vote.type, scheduler::FrameRateCompatibility::Exact);
EXPECT_EQ(getSnapshot(111)->frameRate.vote.rate.getIntValue(), 90);
- EXPECT_EQ(getSnapshot(111)->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Exact);
+ EXPECT_EQ(getSnapshot(111)->frameRate.vote.type, scheduler::FrameRateCompatibility::Exact);
EXPECT_EQ(getSnapshot(1)->frameRate.vote.rate.getIntValue(), 0);
- EXPECT_EQ(getSnapshot(1)->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ EXPECT_EQ(getSnapshot(1)->frameRate.vote.type, scheduler::FrameRateCompatibility::NoVote);
}
TEST_F(LayerSnapshotTest, CanCropTouchableRegion) {
@@ -550,20 +547,20 @@
// verify parent is gets no vote
EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ scheduler::FrameRateCompatibility::NoVote);
EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer and children get the requested votes
EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 11})->changes.test(RequestedLayerState::Changes::FrameRate));
EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 111})->changes.test(RequestedLayerState::Changes::FrameRate));
// reparent and verify the child gets the new parent's framerate
@@ -574,23 +571,23 @@
// verify parent is gets no vote
EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ scheduler::FrameRateCompatibility::NoVote);
// verify layer and children get the requested votes
EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
// reparent and verify the new parent gets no vote
@@ -601,30 +598,30 @@
// verify old parent has invalid framerate (default)
EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify new parent get no vote
EXPECT_FALSE(getSnapshot({.id = 2})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 2})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ scheduler::FrameRateCompatibility::NoVote);
EXPECT_TRUE(getSnapshot({.id = 2})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer and children get the requested votes (unchanged)
EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
}
TEST_F(LayerSnapshotTest, translateDataspace) {
@@ -653,33 +650,33 @@
// verify parent 1 gets no vote
EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ scheduler::FrameRateCompatibility::NoVote);
EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer 11 and children 111 get the requested votes
EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 11})->changes.test(RequestedLayerState::Changes::FrameRate));
EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 111})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify parent 12 gets no vote
EXPECT_FALSE(getSnapshot({.id = 12})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 12})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ scheduler::FrameRateCompatibility::NoVote);
EXPECT_TRUE(getSnapshot({.id = 12})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer 122 and children 1221 get the requested votes
EXPECT_FALSE(getSnapshot({.id = 122})->frameRate.vote.rate.isValid());
EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.isValid());
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.category, FrameRateCategory::Normal);
EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
EXPECT_TRUE(
@@ -688,7 +685,7 @@
EXPECT_FALSE(getSnapshot({.id = 1221})->frameRate.vote.rate.isValid());
EXPECT_TRUE(getSnapshot({.id = 1221})->frameRate.isValid());
EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.category, FrameRateCategory::Normal);
EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
EXPECT_TRUE(
@@ -713,32 +710,32 @@
// verify parent is gets no vote
EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ scheduler::FrameRateCompatibility::NoVote);
// verify layer 11 and children 111 get the requested votes
EXPECT_TRUE(getSnapshot({.id = 11})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 11})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_TRUE(getSnapshot({.id = 111})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.rate.getValue(), 244.f);
EXPECT_EQ(getSnapshot({.id = 111})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
// verify layer 122 and children 1221 get the requested category vote (unchanged from
// reparenting)
EXPECT_FALSE(getSnapshot({.id = 122})->frameRate.vote.rate.isValid());
EXPECT_TRUE(getSnapshot({.id = 122})->frameRate.isValid());
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_EQ(getSnapshot({.id = 122})->frameRate.category, FrameRateCategory::Normal);
EXPECT_TRUE(getSnapshot({.id = 122})->changes.test(RequestedLayerState::Changes::FrameRate));
EXPECT_FALSE(getSnapshot({.id = 1221})->frameRate.vote.rate.isValid());
EXPECT_TRUE(getSnapshot({.id = 1221})->frameRate.isValid());
EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::Default);
+ scheduler::FrameRateCompatibility::Default);
EXPECT_EQ(getSnapshot({.id = 1221})->frameRate.category, FrameRateCategory::Normal);
EXPECT_TRUE(getSnapshot({.id = 1221})->changes.test(RequestedLayerState::Changes::FrameRate));
}
@@ -762,7 +759,7 @@
// verify parent 1 gets no vote
EXPECT_FALSE(getSnapshot({.id = 1})->frameRate.vote.rate.isValid());
EXPECT_EQ(getSnapshot({.id = 1})->frameRate.vote.type,
- scheduler::LayerInfo::FrameRateCompatibility::NoVote);
+ scheduler::FrameRateCompatibility::NoVote);
EXPECT_TRUE(getSnapshot({.id = 1})->changes.test(RequestedLayerState::Changes::FrameRate));
// verify layer 12 and all descendants (121, 122, 1221) get the requested vote
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 50e07fc..4cc78fe 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -25,7 +25,7 @@
MockLayer(SurfaceFlinger* flinger, std::string name)
: Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {
EXPECT_CALL(*this, getDefaultFrameRateCompatibility())
- .WillOnce(testing::Return(scheduler::LayerInfo::FrameRateCompatibility::Default));
+ .WillOnce(testing::Return(scheduler::FrameRateCompatibility::Default));
}
explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
@@ -34,8 +34,7 @@
MOCK_CONST_METHOD0(isVisible, bool());
MOCK_METHOD1(createClone, sp<Layer>(uint32_t));
MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
- MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility,
- scheduler::LayerInfo::FrameRateCompatibility());
+ MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility, scheduler::FrameRateCompatibility());
MOCK_CONST_METHOD0(getOwnerUid, uid_t());
MOCK_CONST_METHOD0(getDataSpace, ui::Dataspace());
};